From 0603e19435b4304fabd91f840325a153ce31cae2 Mon Sep 17 00:00:00 2001 From: Jan Tuomi Date: Fri, 12 Apr 2024 13:56:41 +0300 Subject: Finish calli/callr/return, add test code --- test/e2e/call_stack/lib.atk16 | 42 ++++++++++++++++++++++ test/e2e/call_stack/run_fact.atk16 | 11 ++++++ test/e2e/call_stack/run_mul.atk16 | 18 ++++++++++ test/e2e/call_stack/test_call_stack.py | 65 ++++++++++++++++++++++++++++++++++ 4 files changed, 136 insertions(+) create mode 100644 test/e2e/call_stack/lib.atk16 create mode 100644 test/e2e/call_stack/run_fact.atk16 create mode 100644 test/e2e/call_stack/run_mul.atk16 create mode 100644 test/e2e/call_stack/test_call_stack.py (limited to 'test/e2e/call_stack') 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 + -- cgit v1.3