#[derive(Debug, Clone)] struct Octo { tag: Option, 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 { 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::() { *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::().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())) } }