diff options
| -rw-r--r-- | atk16_asm/asm_ops.py | 16 | ||||
| -rw-r--r-- | atk16_emu/emu.py | 1 | ||||
| -rw-r--r-- | test/e2e/bootstrap.atk16 (renamed from test/e2e/emulation_speed/bootstrap.atk16) | 0 | ||||
| -rw-r--r-- | test/e2e/call_stack/lib.atk16 | 42 | ||||
| -rw-r--r-- | test/e2e/call_stack/run_fact.atk16 | 11 | ||||
| -rw-r--r-- | test/e2e/call_stack/run_mul.atk16 | 18 | ||||
| -rw-r--r-- | test/e2e/call_stack/test_call_stack.py | 65 | ||||
| -rw-r--r-- | test/e2e/emulation_speed/long_loop.atk16 | 2 | ||||
| -rw-r--r-- | test/e2e/fibo/bootstrap.atk16 | 78 | ||||
| -rw-r--r-- | test/e2e/fibo/fibo.atk16 | 2 |
10 files changed, 151 insertions, 84 deletions
diff --git a/atk16_asm/asm_ops.py b/atk16_asm/asm_ops.py index 6a08495..6d2b98a 100644 --- a/atk16_asm/asm_ops.py +++ b/atk16_asm/asm_ops.py @@ -227,7 +227,9 @@ def expand_callr(addr_reg: str) -> ExpandResult: ["lpc", "RG"], *expand_addi("RG", "4", "RG"), *expand_spu("RG"), - ["jpr", addr_reg] + ["jpr", addr_reg], + ["ldr", "SP", "RG"], + *expand_dec("SP"), ] def expand_calli(addr_imm: str) -> ExpandResult: @@ -236,11 +238,19 @@ def expand_calli(addr_imm: str) -> ExpandResult: ["ldi", ret_label, "RG"], *expand_spu("RG"), ["jpi", addr_imm], - ["@label", ret_label] + ["@label", ret_label], + ["ldr", "SP", "RG"], # return leaves SP pointing at return value, read to reg + *expand_dec("SP"), # decrement SP to fix stack ] def expand_return() -> ExpandResult: - return expand_spo("RG") + [["jpr", "RG"]] + return [ + ["str", "RG", "SP"], # store returned value on stack but don't increment SP + *expand_dec("SP"), # decrement SP to access the return address + ["ldr", "SP", "RG"], # load return address to RG + *expand_inc("SP"), # point SP to return value + ["jpr", "RG"] # jump back to call site + ] def stack_stash(*rs: str) -> ExpandResult: result: ExpandResult = [] diff --git a/atk16_emu/emu.py b/atk16_emu/emu.py index f0671bd..8277d00 100644 --- a/atk16_emu/emu.py +++ b/atk16_emu/emu.py @@ -341,7 +341,6 @@ class Machine: addr = self.get_nth_register(addr_reg).value value = self.get_nth_register(from_reg).value - print(f"==== addr: 0x{addr:>04x}, value: 0x{value:>04x}") self.mem_write(addr, value) case LDI(to_reg, imm): diff --git a/test/e2e/emulation_speed/bootstrap.atk16 b/test/e2e/bootstrap.atk16 index 9b024e6..9b024e6 100644 --- a/test/e2e/emulation_speed/bootstrap.atk16 +++ b/test/e2e/bootstrap.atk16 diff --git a/test/e2e/call_stack/lib.atk16 b/test/e2e/call_stack/lib.atk16 new file mode 100644 index 0000000..39672e0 --- /dev/null +++ b/test/e2e/call_stack/lib.atk16 @@ -0,0 +1,42 @@ +@label mul + ; multiply a signed/unsigned (a) and an unsigned number (b) + ; parameters RA = a + ; RB = b + ; return RG = a * b + + stack_stash RA RB + ; initialize return value in RG to 0 + ldi 0 RG +@label mul_loop + ; if b == 0, we are done + subi RB 0 RB + bri zero mul_done + ; RG += RA + add RA RG RG + dec RB + jpi mul_loop +@label mul_done + stack_restore RA RB + return + +; Recursive impl of the factorial +; Calling convention is that arguments are in registers RA..RG (max 7 arguments since RH = SP) +; and the return value is in RG (note: overwriting arg in RG) + +@label fact + ; parameters RA = n + ; return RG = factorial(n) + subi RA 0 RA + bri zero fact_basecase +@label fact_reccase + stack_stash RA RB + mov RA RB ; RB := n + subi RA 1 RA ; RA := n - 1 + calli fact ; RG := fact(n - 1) + mov RG RA ; RA := fact(n - 1) + calli mul ; RG := n * fact(n - 1) + stack_restore RA RB + return +@label fact_basecase + ldi 1 RG + return diff --git a/test/e2e/call_stack/run_fact.atk16 b/test/e2e/call_stack/run_fact.atk16 new file mode 100644 index 0000000..06ad59c --- /dev/null +++ b/test/e2e/call_stack/run_fact.atk16 @@ -0,0 +1,11 @@ +; bootstrap code (must be called to setup mandatory data) +; will jump to label main +@include ../bootstrap +@include lib + +@let INPUT %INPUT% ; value replaced by test driver code + +@label main + ldi INPUT RA + calli fact + hlt diff --git a/test/e2e/call_stack/run_mul.atk16 b/test/e2e/call_stack/run_mul.atk16 new file mode 100644 index 0000000..feaabe7 --- /dev/null +++ b/test/e2e/call_stack/run_mul.atk16 @@ -0,0 +1,18 @@ +; bootstrap code (must be called to setup mandatory data) +; will jump to label main +@include ../bootstrap +@include lib + +; values replaced by test driver code +@label INPUT_A + %INPUT_A% +@label INPUT_B + %INPUT_B% + +@label main + ldi INPUT_A RA + ldr RA RA + ldi INPUT_B RB + ldr RB RB + calli mul + hlt diff --git a/test/e2e/call_stack/test_call_stack.py b/test/e2e/call_stack/test_call_stack.py new file mode 100644 index 0000000..3895f5c --- /dev/null +++ b/test/e2e/call_stack/test_call_stack.py @@ -0,0 +1,65 @@ +import time +from atk16_asm import assemble +from atk16_emu import Machine +from test.utils import pad_bytearray + +def run_mul(a: int, b: int) -> int: + filename = "test/e2e/call_stack/run_mul.atk16" + with open(filename, "r") as f: + source = f.read() + + source = source.replace("%INPUT_A%", str(a)) + source = source.replace("%INPUT_B%", str(b)) + + obj = assemble(source, filename) + rom_image = pad_bytearray(obj.program) + + machine = Machine() + machine.load_rom_image(rom_image) + machine.reset() + machine.run_until_halted() + + return machine.rg.value + +def test_mul_1(): + expected = 30 + received = run_mul(5, 6) + + assert expected == received + +def test_mul_2(): + a = -100 + b = 200 + expected = a * b + received = run_mul(a, b) + + assert expected == received + +def run_fact(n: int) -> int: + filename = "test/e2e/call_stack/run_fact.atk16" + with open(filename, "r") as f: + source = f.read() + + source = source.replace("%INPUT%", str(n)) + obj = assemble(source, filename) + rom_image = pad_bytearray(obj.program) + + machine = Machine() + machine.load_rom_image(rom_image) + machine.reset() + machine.run_until_halted() + + return machine.rg.value + +def test_fact_0(): + expected = 1 + received = run_fact(0) + + assert expected == received + +def test_fact_6(): + expected = 720 + received = run_fact(6) + + assert expected == received + diff --git a/test/e2e/emulation_speed/long_loop.atk16 b/test/e2e/emulation_speed/long_loop.atk16 index 2303311..5447256 100644 --- a/test/e2e/emulation_speed/long_loop.atk16 +++ b/test/e2e/emulation_speed/long_loop.atk16 @@ -1,6 +1,6 @@ ; bootstrap code (must be called to setup mandatory data) ; will jump to label main -@include bootstrap +@include ../bootstrap @label iters 10000 diff --git a/test/e2e/fibo/bootstrap.atk16 b/test/e2e/fibo/bootstrap.atk16 deleted file mode 100644 index 9b024e6..0000000 --- a/test/e2e/fibo/bootstrap.atk16 +++ /dev/null @@ -1,78 +0,0 @@ -@let SP RH - -; vector table fields -@let vector_table 0x10 -@let vt_ISR0 0x10 ; ISR0 -@let vt_ISR1 0x11 ; ISR1 -@let vt_ISR2 0x12 ; ISR2 -@let vt_ISR3 0x13 ; ISR3 -@let vt_stack_addr 0x14 ; Stack address -@let vt_term_pp_addr 0x15 ; Terminal peripheral address -@let vt_kb_pp_addr 0x16 ; Keyboard peripheral address -@let vt_gr_mode_addr 0x17 ; Graphics mode setting address -@let vt_sprite_mem 0x18 ; Sprite memory address -@let vt_text_mem 0x19 ; Text memory buffer address - -; memory segments -@let stack_segment 0x8000 -@let mmio_segment 0xE000 -@let terminal_addr 0xE000 -@let keyboard_addr 0xE001 -@let gr_mode_addr 0xE002 -@let sprite_mem 0xE800 -@let text_mem 0xF800 ; to 0xFFFF -; note: sprite memory & text memory can use the same space -; since they are never used at the same time - -; graphics mode settings -@let gr_disabled_mode 0b00 -@let gr_text_mode 0b01 -@let gr_sprite_mode 0b10 - -@address vector_table - keyboard_isr ; 0x10 - hlt_isr ; 0x11 - hlt_isr ; 0x12 - hlt_isr ; 0x13 - stack_segment ; 0x14 - terminal_addr ; 0x15 - keyboard_addr ; 0x16 - gr_mode_addr ; 0x17 - sprite_mem ; 0x18 - text_mem ; 0x19 - -@label hlt_isr - ldi 0x55 RE - hlt - -@label keyboard_isr - spu RA - spu RB - ldi vt_kb_pp_addr RA ; RA := Keyboard peripheral address pointer - ldr RA RA ; RA := Keyboard peripheral address deref - ldr RA RA ; RA := <16-bit keyboard character code> from MMIO register - ldi vt_term_pp_addr RB ; RB := Terminal peripheral address pointer - ldr RB RB ; RB := Terminal peripheral address deref - str RA RB ; terminal <- character code (ASCII?) - mov RA RE ; RE := character code (for debugging) - spo RB - spo RA - rti -@label program_segment - -; define short prelude that sets up vital instructions -; NOTE: must be shorter than the vector table offset -@address 0x0 -; set up stack pointer to point to beginning of stack segment - ldi vt_stack_addr RA - ldr RA SP - jpi program_segment - -@address program_segment -; set graphics mode to disabled - ldi vt_gr_mode_addr RA - ldr RA RA - ldi gr_disabled_mode RB - str RB RA -; jump to main (user defined) - jpi main diff --git a/test/e2e/fibo/fibo.atk16 b/test/e2e/fibo/fibo.atk16 index 2cf3d20..2546f43 100644 --- a/test/e2e/fibo/fibo.atk16 +++ b/test/e2e/fibo/fibo.atk16 @@ -1,6 +1,6 @@ ; bootstrap code (must be called to setup mandatory data) ; will jump to label main -@include bootstrap +@include ../bootstrap @label iters 51 |
