Files
semi-libc/tools/compile-iconv.rs
2026-07-18 21:20:39 +08:00

148 lines
4.3 KiB
Rust

#[derive(Debug, Clone)]
struct Octo {
tag: Option<String>,
content: [u8; 8],
reloc: Vec<(usize, String)>,
}
fn main() {
let mut args = std::env::args().skip(1);
let src = args.next().expect("please specify source file");
let dst = args.next().expect("please specify output file");
let mut octos = Vec::new();
let src_content = std::fs::read_to_string(&src).expect("cannot read source file");
let mut current_tag = None;
for (nline, line) in src_content.lines().enumerate() {
let line = line.trim();
if line.is_empty() || line.starts_with("//") {
continue;
}
if let Some(tag) = line.strip_suffix(":") {
current_tag = Some(tag);
continue;
}
let Some((op, arglist)) = line.split_once(' ') else {
error(nline, "operation without arglist is currently disallowed");
};
let mut content = [0u8; 8];
content[0] = do_op(op).unwrap_or_else(|| error(nline, "unrecognized op"));
let mut cursor = 1;
if content[0] == 0 {
cursor = 0;
}
let reloc = do_arglist(arglist, &mut content, &mut cursor);
octos.push(Octo {
tag: current_tag.take().map(ToString::to_string),
content,
reloc,
});
}
let mut tags = std::collections::HashMap::new();
for (nocto, octo) in octos.iter().enumerate() {
if let Some(tag) = &octo.tag {
tags.insert(tag.to_string(), nocto * 8);
}
}
let mut content = Vec::new();
for octo in octos {
let mut current = octo.content.to_vec();
for (offset, tag) in octo.reloc {
let tag_addr = *tags.get(&tag).expect(&format!("tag {tag} undefined")) as u32;
current[offset..offset + 4].copy_from_slice(&tag_addr.to_be_bytes()[..]);
}
content.append(&mut current);
}
std::fs::write(dst, &content).expect("failed to write dst file");
}
fn error(line: usize, reason: &str) -> ! {
eprintln!("error: {reason} (source:{})", line + 1);
std::process::exit(1);
}
fn do_op(s: &str) -> Option<u8> {
match s {
"place64" => Some(0),
"input8" => Some(1),
"input16" => Some(2),
"input32" => Some(3),
"input64" => Some(4),
"output8" => Some(5),
"output16" => Some(6),
"output32" => Some(7),
"output64" => Some(8),
"xor" => Some(9),
"and" => Some(10),
"or" => Some(11),
"add" => Some(13),
"sub" => Some(14),
"smul" => Some(15),
"umul" => Some(17),
"eq" => Some(19),
"mt" => Some(20),
"lt" => Some(21),
"shl" => Some(29),
"shr" => Some(30),
"not" => Some(12),
"jnz" => Some(22),
"jmp" => Some(23),
"load8" => Some(24),
"load16" => Some(25),
"load32" => Some(26),
"load64" => Some(27),
"store64" => Some(28),
"ret" => Some(31),
"ptrload8" => Some(33),
"ptrload16" => Some(34),
"ptrload32" => Some(35),
"ptrload64" => Some(36),
_ => None,
}
}
fn do_arglist(s: &str, content: &mut [u8; 8], cursor: &mut usize) -> Vec<(usize, String)> {
let mut reloc = Vec::new();
for arg in s.split(',') {
let arg = arg.trim();
if let Some(x) = do_arg(arg, content, cursor) {
reloc.push(x);
}
}
reloc
}
fn do_arg(s: &str, content: &mut [u8; 8], cursor: &mut usize) -> Option<(usize, String)> {
if let Ok(n) = s.parse::<u64>() {
*cursor = 8;
*content = n.to_be_bytes();
None
} else if let Some(digit) = s.strip_prefix('0') {
let radix = match digit.as_bytes()[0] {
b'x' => 16,
b'o' => 8,
b'b' => 2,
_ => panic!("unknown radix"),
};
*cursor = 8;
*content = u64::from_str_radix(&digit[1..], radix)
.unwrap()
.to_be_bytes();
None
} else if let Some(reg_id) = s.strip_prefix("&r") {
content[*cursor] = reg_id.parse::<u8>().unwrap();
*cursor += 1;
None
} else if s == "@sicv_magic_v1" {
*content = [b'S', b'I', b'C', b'V', 0, 0, 0, 1];
*cursor = 8;
None
} else {
*cursor += 4;
Some((*cursor - 4, s.into()))
}
}