diff options
| author | Jan Tuomi <jan@jantuomi.fi> | 2024-04-12 14:57:45 +0300 |
|---|---|---|
| committer | Jan Tuomi <jan@jantuomi.fi> | 2024-04-12 14:57:45 +0300 |
| commit | 4f78086920cd4e2cee6d970decaee7025875f2a9 (patch) | |
| tree | 1ef780401e5e26c20649c90e8713c02fc0fe90d6 | |
| parent | 0603e19435b4304fabd91f840325a153ce31cae2 (diff) | |
Implement signed multiplication
| -rw-r--r-- | atk16_asm/asm_ops.py | 13 | ||||
| -rw-r--r-- | test/e2e/call_stack/lib.atk16 | 60 | ||||
| -rw-r--r-- | test/e2e/call_stack/test_call_stack.py | 8 |
3 files changed, 70 insertions, 11 deletions
diff --git a/atk16_asm/asm_ops.py b/atk16_asm/asm_ops.py index 6d2b98a..96e0366 100644 --- a/atk16_asm/asm_ops.py +++ b/atk16_asm/asm_ops.py @@ -156,11 +156,12 @@ def expand_addi(left: str, imm: str, target: str) -> ExpandResult: def expand_subi(left: str, imm: str, target: str) -> ExpandResult: return [["ali", "al_minus", left, imm, target]] -def expand_not(reg: str, target: str) -> ExpandResult: - return [["alr", "al_xor", reg, "0xFFFF", target]] +# FIXME: 0xFFFF does not fit in 3 bits +# def expand_not(reg: str, target: str) -> ExpandResult: +# return [["alr", "al_xor", reg, "0xFFFF", target]] -def expand_noti(imm: str, target: str) -> ExpandResult: - return [["ali", "al_xor", imm, "0xFFFF", target]] +# def expand_noti(imm: str, target: str) -> ExpandResult: +# return [["ali", "al_xor", imm, "0xFFFF", target]] def expand_and(left: str, right: str, target: str) -> ExpandResult: return [["alr", "al_and", left, right, target]] @@ -293,8 +294,8 @@ expansions: OpExpansionDict = { "sub": expand_sub, "addi": expand_addi, "subi": expand_subi, - "not": expand_not, - "noti": expand_noti, + # "not": expand_not, + # "noti": expand_noti, "and": expand_and, "andi": expand_andi, "or": expand_or, diff --git a/test/e2e/call_stack/lib.atk16 b/test/e2e/call_stack/lib.atk16 index 39672e0..58b9cdb 100644 --- a/test/e2e/call_stack/lib.atk16 +++ b/test/e2e/call_stack/lib.atk16 @@ -1,12 +1,52 @@ +@label mul_sign_mask + ${2 ** 15} +@label mul_not_mask + 0xFFFF + @label mul - ; multiply a signed/unsigned (a) and an unsigned number (b) + ; multiply a signed (a) and a signed (b) ; parameters RA = a ; RB = b ; return RG = a * b - stack_stash RA RB - ; initialize return value in RG to 0 - ldi 0 RG + stack_stash RA RB RC RD + ldi 0 RG ; initialize return value in RG to 0 + + ldi mul_sign_mask RD + ldr RD RD ; RD := 2 ** 16 + and RA RD RC ; RC := 2 ** 16 if a is negative, 0 otherwise + and RB RD RD ; RD := 2 ** 16 if b is negative, 0 otherwise + xor RC RD RC ; RC := 2 ** 16 if exactly one of a or b is negative, 0 otherwise + + ldi mul_not_mask RD + ldr RD RD ; RD := 0xFFFF + + ; RA := abs(a) + subi RA 0 RA + bri sign mul_a_negative + jpi mul_a_negative_done +@label mul_a_negative + xor RA RD RA ; RA := ~a + inc RA ; negate twos complement a +@label mul_a_negative_done + + ; RB := abs(b) + subi RB 0 RB + bri sign mul_b_negative + jpi mul_b_negative_done +@label mul_b_negative + xor RB RD RB ; RB := ~b + inc RB ; negate twos complement b +@label mul_b_negative_done + + ; the algorithms iterates b times, adding a to RG each time + ; so b should be as small as possible + sub RB RA RD ; if a <= b, swap a and b + bri sign mul_loop + + mov RA RD + mov RB RA + mov RD RB @label mul_loop ; if b == 0, we are done subi RB 0 RB @@ -16,7 +56,17 @@ dec RB jpi mul_loop @label mul_done - stack_restore RA RB + ; set the most significant bit to the precomputed sign + subi RC 0 RC + bri sign mul_negate_result + jpi mul_return +@label mul_negate_result + ldi mul_not_mask RD + ldr RD RD ; RD := 0xFFFF + xor RG RD RG + inc RG +@label mul_return + stack_restore RA RB RC RD return ; Recursive impl of the factorial diff --git a/test/e2e/call_stack/test_call_stack.py b/test/e2e/call_stack/test_call_stack.py index 3895f5c..02897f9 100644 --- a/test/e2e/call_stack/test_call_stack.py +++ b/test/e2e/call_stack/test_call_stack.py @@ -21,6 +21,12 @@ def run_mul(a: int, b: int) -> int: return machine.rg.value +def test_mul_0(): + expected = 0 + received = run_mul(0, -100) + + assert expected == received + def test_mul_1(): expected = 30 received = run_mul(5, 6) @@ -32,6 +38,8 @@ def test_mul_2(): b = 200 expected = a * b received = run_mul(a, b) + # interpret received as a 16 bit signed integer, using powers of two in the math + received = received if received < 2**15 else received - 2**16 assert expected == received |
