lc3asm 0.1.2

LC-3 assembly parser & assembler
Documentation
use super::*;
use console::Term;
use lazy_static::lazy_static;
use lc3::vm::VM;
use lc3dbg::helper::view_mem_entry;
use lc3dbg::symbol::TableEntry;

lazy_static! {
    static ref TEMPORARY_SYMBOL_TABLE: Vec<TableEntry> = vec![TableEntry::Unknown; 1 << 16];
}

macro_rules! asm_test {
    ($(#[$ignore:meta])? $name:ident, $code:literal, $input:literal, $output:literal
        $(,insert $in_addr:literal <- $in_value:literal)*
        $(,assert $out_addr:literal == $out_value:literal)* $(,)?) => {
        #[test]
        $(#[$ignore])?
        fn $name() -> Result<(), Error> {
            #![allow(unused_variables, unused_assignments, unused_mut)]
            let mut vm = VM::new();
            let obj = assemble($code)?.0;
            vm.load_u8(obj.as_ref());

            asm_test!(@insert_mem_values vm $(,$in_addr <- $in_value)*);

            eprintln!("");
            let stderr = Term::stderr();
            for delta in 0..(obj.len() / 2) {
                view_mem_entry(vm.pc as usize + delta, &vm, &TEMPORARY_SYMBOL_TABLE, &stderr)?;
            }

            let mut input_buf = $input.as_bytes();
            let mut output_buf: Vec<u8> = Vec::new();
            vm.run(&mut input_buf, &mut output_buf);

            assert_eq!(
                String::from_utf8_lossy(&output_buf),
                concat!($output, "\n\n--- halting the LC-3 ---\n\n")
            );

            let mut counter: usize = 1;

            asm_test!(@assert_mem_values vm, counter $(,$out_addr == $out_value)*);
            Ok(())
        }
    };

    (@insert_mem_values $vm:expr) => {};

    (@insert_mem_values $vm:expr, $addr:literal <- $value:literal $(,$($more:tt)+)*) => {
        $vm.mem[$addr] = $value;
        asm_test!(@insert_mem_values $vm $(,$($more)+)*);
    };

    (@assert_mem_values $vm:expr, $counter:expr) => {};

    (@assert_mem_values $vm:expr, $counter:expr, $addr:literal == $value:literal $(,$($more:tt)+)*) => {
        assert_eq!(
            $vm.mem[$addr],
            $value,
            "Memory test #{} failure",
            $counter,
        );
        $counter += 1;
        asm_test!(@assert_mem_values $vm, $counter $(,$($more)+)*);
    };
}

asm_test!(
    adder,
    r#"
.ORIG   x3000
        AND     r0, r0, #0
        ADD     r0, r0, #8
        ADD     r0, r0, #8
        OUT
        HALT
.END
    "#,
    "",
    "\u{10}",
);

asm_test!(
    string_print,
    r#"
.ORIG   x3000
        LEA     r0, STZ
        PUTS
        HALT
STZ     .STRINGZ "HELLO\n"
.END
    "#,
    "",
    "HELLO\n",
);

asm_test!(
    interger_division,
    r#"
.ORIG	x3000
        LDI	R0, NUM		; R0 = NUM
        LDI	R1, DIV
        NOT	R1, R1
        ADD	R1, R1, #1	; R1 = -DIV
        AND	R2, R2, #0	; R2 = 0 (count)
LOOP	ADD	R3, R0, R1
        BRn	OUTL		; DIVISION COMPLETE!
        ADD	R2, R2, #1
        ADD	R0, R3, #0
        BR	LOOP
OUTL	STI	R0, REM
        STI	R2, QTT
        HALT

NUM	    .FILL x4000
DIV	    .FILL x4001
QTT	    .FILL x4002
REM	    .FILL x4003
.END
    "#,
    "",
    "",
    insert 0x4000 <- 7,
    insert 0x4001 <- 3,
    assert 0x4002 == 2,
    assert 0x4003 == 1,
);