fn my_micro_assembler(code: &Vec<String>) -> Result<Vec<u8>, String> {
if code.len() != 2 {
return Err("Instruction should have a single argument.".to_string());
}
match code[0].as_str() {
"add" => Ok(vec![0x10, u8::from_str_radix(&code[1], 10).unwrap()]),
"set" => Ok(vec![0x11, u8::from_str_radix(&code[1], 10).unwrap()]),
_ => Err("Invalid instruction".to_string()),
}
}
fn my_implementation_specific_macros(code: &Vec<String>) -> Result<Option<String>, String> {
match code[0].as_str() {
"set0" => match code.len() {
1 => Ok(Some("set 0".to_string())),
_ => Err("Set 0 should have no arguments".to_string()),
},
_ => Ok(None),
}
}
const MY_RUNTIME_START: &str = "@macro set_10_plus 1
set 10
add $1
@end
@rawbytes \"11\"
@align \"4\"
@label start
";
const MY_RUNTIME_END: &str = "@align 3
@label end_code
";
const CODE_1: &str = "
@labref end_code
set0
add 10
set_10_plus 3
@labref start
";
fn make_assembler(code: &str) -> macro_asm_builder::Assembler<'_> {
let mut asm = macro_asm_builder::Assembler::from_text(code);
asm.add_text_before(MY_RUNTIME_START, "runtime");
asm.add_text_after(MY_RUNTIME_END, "runtime");
assert_eq!(asm.set_word_size(16), None);
asm.micro_assembly = &my_micro_assembler;
asm.implementation_macro = &my_implementation_specific_macros;
asm
}
#[test]
fn test_success() {
let mut asm = make_assembler(CODE_1);
match asm.assemble() {
Err(txt) => {
println!("{}", txt);
panic!("Test failed!");
},
Ok(data) => {
assert_eq!(data, vec![0x11, 0, 0, 0, 0x12, 0x00, 0x11, 0, 0x10, 10, 0x11, 10, 0x10, 3, 4, 0, 0, 0, ]);
},
}
}
const CODE_2: &str = "set0 bad_arg
bad_instruction arg
add too many args
";
#[test]
fn test_error_implementation() {
let mut asm = make_assembler(CODE_2);
match asm.assemble() {
Err(txt) => {
assert_eq!(txt, "Set 0 should have no arguments
At line 1 from file ./__asm_init: `set0 bad_arg`
--------------
Invalid instruction
At line 2 from file ./__asm_init: `bad_instruction arg`
--------------
Instruction should have a single argument.
At line 3 from file ./__asm_init: `add too many args`
--------------
" );
},
Ok(_) => {
panic!("Assembly should have failed.");
},
}
}
const CODE_3: &str = "
@end
@rawbytes 777
@align -1
@constant 100000000
";
#[test]
fn test_error_lib() {
let mut asm = make_assembler(CODE_3);
match asm.assemble() {
Err(txt) => {
assert_eq!(txt, "Error, @end should only be used to end macro definitions.
At line 2 from file ./__asm_init: `@end`
--------------
Error, unable to read byte 777 in @rawbyte directive.
At line 3 from file ./__asm_init: `@rawbytes 777`
--------------
Error, @align directive should take a single positive numeric argument.
At line 4 from file ./__asm_init: `@align -1`
--------------
Error, unable to read number.
At line 5 from file ./__asm_init: `@constant 100000000`
--------------
" );
},
Ok(_) => {
panic!("Assembly should have failed.");
},
}
}
#[test]
fn test_label_dump() {
const CODE: &str = "@label top
@pad-until 10
@label mid
@pad-until 25
@label bot";
let mut asm = macro_asm_builder::Assembler::from_text(CODE);
asm.start_address = 2;
let _ = asm.assemble().unwrap();
assert_eq!(asm.label_dump(), "top: 0x2\nmid: 0xA\nbot: 0x19\n".to_string());
}
#[test]
fn test_start_addr() {
const CODE: &str = "@labref start";
let mut asm = make_assembler(CODE);
asm.start_address = 10;
let data = asm.assemble().unwrap();
assert_eq!(data, vec![0x11, 0, 12, 0, 0]); }