diff options
| author | Jan Tuomi <jans.tuomi@gmail.com> | 2024-02-22 08:52:20 +0200 |
|---|---|---|
| committer | Jan Tuomi <jans.tuomi@gmail.com> | 2024-02-22 08:52:20 +0200 |
| commit | 416e896956d3b05793337da6609fe1544797c0a9 (patch) | |
| tree | 9fa0cf3b622d4c9c086e4c08c5011ab12fdd4a77 | |
| parent | aefe0cf3874c542669051b2b3cc21c9590aa970b (diff) | |
Add stuff, refactor
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | atk16_asm/__init__.py | 1 | ||||
| -rw-r--r-- | atk16_asm/asm_ops.py | 166 | ||||
| -rw-r--r-- | atk16_asm/asm_pass0.py | 6 | ||||
| -rw-r--r-- | atk16_asm/asm_pass1.py | 10 | ||||
| -rw-r--r-- | atk16_asm/asm_pass2.py | 6 | ||||
| -rw-r--r-- | atk16_asm/asm_pass3.py | 6 | ||||
| -rw-r--r-- | atk16_asm/asm_pass4.py | 6 | ||||
| -rwxr-xr-x | atk16_asm/assembler.py | 115 | ||||
| -rw-r--r-- | atk16_asm/optimizer.py | 2 | ||||
| -rw-r--r-- | atk16_emu/__init__.py | 1 | ||||
| -rw-r--r-- | atk16_emu/cli.py | 13 | ||||
| -rw-r--r-- | atk16_emu/emu.py | 16 | ||||
| -rw-r--r-- | atk16_emu/test/test_opcode_alr.py | 4 | ||||
| -rw-r--r-- | atk16_emu/test/test_opcode_hlt.py | 4 | ||||
| -rw-r--r-- | resources/asm/ext_std.py | 157 | ||||
| -rw-r--r-- | resources/asm/ext_string_io.py | 3 | ||||
| -rw-r--r-- | test/__init__.py | 0 | ||||
| -rw-r--r-- | test/e2e/fibo/bootstrap.atk16 | 78 | ||||
| -rw-r--r-- | test/e2e/fibo/fibo.atk16 | 53 | ||||
| -rw-r--r-- | test/e2e/fibo/test_fibo.py | 19 | ||||
| -rw-r--r-- | test/e2e/just_hlt/just_hlt.atk16 | 1 | ||||
| -rw-r--r-- | test/e2e/just_hlt/test_just_hlt.py | 19 | ||||
| -rw-r--r-- | test/e2e/sum/sum.atk16 | 31 | ||||
| -rw-r--r-- | test/e2e/sum/test_sum.py | 19 | ||||
| -rw-r--r-- | test/utils.py (renamed from atk16_emu/test/utils.py) | 4 |
26 files changed, 473 insertions, 269 deletions
@@ -1,4 +1,4 @@ -.PHONY: all +.PHONY: all test py = /usr/bin/env python3 digital_path = $(HOME)/.local/share/Digital/digital.jar diff --git a/atk16_asm/__init__.py b/atk16_asm/__init__.py new file mode 100644 index 0000000..abebbfb --- /dev/null +++ b/atk16_asm/__init__.py @@ -0,0 +1 @@ +from .assembler import assemble diff --git a/atk16_asm/asm_ops.py b/atk16_asm/asm_ops.py index 885c275..394a643 100644 --- a/atk16_asm/asm_ops.py +++ b/atk16_asm/asm_ops.py @@ -1,6 +1,10 @@ from typing import Callable -from asm_eval import * from dataclasses import dataclass +from .asm_eval import * + +ExpandResult = list[list[str]] +ExpandFn = Callable[..., ExpandResult] +OpExpansionDict = dict[str, ExpandFn] @dataclass class Meta: @@ -130,14 +134,131 @@ operations: OpWordDict = { "hlt": make_hlt, } -ExpandResult = list[list[str]] -ExpandFn = Callable[..., ExpandResult] -OpExpansionDict = dict[str, ExpandFn] - def expand_id(*parts: str) -> ExpandResult: return [list(parts)] -default_expansions: OpExpansionDict = { +def expand_add(left: str, right: str, target: str) -> ExpandResult: + return [["alr", "al_plus", left, right, target]] + +def expand_sub(left: str, right: str, target: str) -> ExpandResult: + return [["alr", "al_minus", left, right, target]] + +def expand_addi(left: str, imm: str, target: str) -> ExpandResult: + return [["ali", "al_plus", left, imm, target]] + +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]] + +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]] + +def expand_andi(left: str, imm: str, target: str) -> ExpandResult: + return [["ali", "al_and", left, imm, target]] + +def expand_or(left: str, right: str, target: str) -> ExpandResult: + return [["alr", "al_or", left, right, target]] + +def expand_ori(left: str, imm: str, target: str) -> ExpandResult: + return [["ali", "al_or", left, imm, target]] + +def expand_xor(left: str, imm: str, target: str) -> ExpandResult: + return [["alr", "al_xor", left, imm, target]] + +def expand_xori(left: str, right: str, target: str) -> ExpandResult: + return [["ali", "al_xor", left, right, target]] + +def expand_sll(left: str, right: str, target: str) -> ExpandResult: + return [["alr", "al_sll", left, right, target]] + +def expand_slr(left: str, right: str, target: str) -> ExpandResult: + return [["alr", "al_slr", left, right, target]] + +def expand_sar(left: str, right: str, target: str) -> ExpandResult: + return [["alr", "al_sar", left, right, target]] + +def expand_slli(left: str, imm: str, target: str) -> ExpandResult: + return [["ali", "al_sll", left, imm, target]] + +def expand_slri(left: str, imm: str, target: str) -> ExpandResult: + return [["ali", "al_slr", left, imm, target]] + +def expand_sari(left: str, imm: str, target: str) -> ExpandResult: + return [["ali", "al_sar", left, imm, target]] + +def expand_inc(reg: str) -> ExpandResult: + return [["ali", "al_plus", reg, "1", reg]] + +def expand_dec(reg: str) -> ExpandResult: + return [["ali", "al_minus", reg, "1", reg]] + +def expand_mov(from_reg: str, to_reg: str) -> ExpandResult: + return [["ali", "al_plus", from_reg, "0", to_reg]] + +def expand_nop() -> ExpandResult: + return [["ali", "al_plus", "RA", "0", "RA"]] + +def expand_spu(reg: str) -> ExpandResult: + return [["str", reg, "SP"]] + expand_inc("SP") + +def expand_spo(reg: str) -> ExpandResult: + return expand_dec("SP") + [["ldr", "SP", reg]] + +def expand_sinc(imm: str) -> ExpandResult: + return expand_addi("SP", imm, "SP") + +def expand_sdec(imm: str) -> ExpandResult: + return expand_subi("SP", imm, "SP") + +def expand_csr(addr_reg: str, stratch_reg: str) -> ExpandResult: + return [ + ["lpc", stratch_reg], + *expand_addi(stratch_reg, "4", stratch_reg), + *expand_spu(stratch_reg), + ["jpr", addr_reg] + ] + +def expand_csi(addr_imm: str, stratch_reg: str) -> ExpandResult: + return [ + ["lpc", stratch_reg], + *expand_addi(stratch_reg, "4", stratch_reg), + *expand_spu(stratch_reg), + ["jpi", addr_imm] + ] + +def expand_rsr(stratch_reg: str) -> ExpandResult: + return expand_spo(stratch_reg) + [["jpr", stratch_reg]] + + +def stack_stash(*rs: str) -> ExpandResult: + result: ExpandResult = [] + for r in rs: + result += expand_spu(r) + + return result + +def stack_restore(*rs: str) -> ExpandResult: + result: ExpandResult = [] + for r in rs: + prefix = expand_spu(r) + result = prefix + result + + return result + +def set_graphics_mode(mode: str) -> ExpandResult: + return [ + ["ldi", "vt_gr_mode_addr", "RA"], + ["ldr", "RA", "RA"], + ["ldi", mode, "RB"], + ["str", "RB", "RA"], + ] + +expansions: OpExpansionDict = { "alr": lambda *args: expand_id("alr", *args), "ali": lambda *args: expand_id("ali", *args), "ldr": lambda *args: expand_id("ldr", *args), @@ -150,5 +271,38 @@ default_expansions: OpExpansionDict = { "lpc": lambda *args: expand_id("lpc", *args), "rti": lambda *args: expand_id("rti", *args), "hlt": lambda *args: expand_id("hlt", *args), + + "add": expand_add, + "sub": expand_sub, + "addi": expand_addi, + "subi": expand_subi, + "not": expand_not, + "noti": expand_noti, + "and": expand_and, + "andi": expand_andi, + "or": expand_or, + "ori": expand_ori, + "xor": expand_xor, + "xori": expand_xori, + "sll": expand_sll, + "slr": expand_slr, + "sar": expand_sar, + "slli": expand_slli, + "slri": expand_slri, + "sari": expand_sari, + "inc": expand_inc, + "dec": expand_dec, + "mov": expand_mov, + "nop": expand_nop, + "spu": expand_spu, + "spo": expand_spo, + "sinc": expand_sinc, + "sdec": expand_sdec, + "csr": expand_csr, + "csi": expand_csi, + "rsr": expand_rsr, + "stack_stash": stack_stash, + "stack_restore": stack_restore, + "set_graphics_mode": set_graphics_mode, } diff --git a/atk16_asm/asm_pass0.py b/atk16_asm/asm_pass0.py index f5b6b0b..262e5c1 100644 --- a/atk16_asm/asm_pass0.py +++ b/atk16_asm/asm_pass0.py @@ -1,8 +1,8 @@ from dataclasses import dataclass import os.path -from asm_ops import * -from asm_eval import * -from tokenizer import * +from .asm_ops import * +from .asm_eval import * +from .tokenizer import * @dataclass class Result0Line: diff --git a/atk16_asm/asm_pass1.py b/atk16_asm/asm_pass1.py index 0b06738..4786afa 100644 --- a/atk16_asm/asm_pass1.py +++ b/atk16_asm/asm_pass1.py @@ -2,10 +2,10 @@ import importlib import sys import os.path from dataclasses import dataclass -from asm_ops import * -from asm_eval import * -from asm_pass0 import * -from tokenizer import tokenize +from .asm_ops import * +from .asm_eval import * +from .asm_pass0 import * +from .tokenizer import tokenize @dataclass class Result1Line: @@ -20,7 +20,7 @@ class Result1: def pass_1(result0: Result0) -> Result1: result_lines: list[Result1Line] = [] - operations: OpExpansionDict = default_expansions.copy() + operations: OpExpansionDict = expansions.copy() for line in result0.lines: keyword, *args = tokenize(line.line) diff --git a/atk16_asm/asm_pass2.py b/atk16_asm/asm_pass2.py index e2ad4ec..cf6b10d 100644 --- a/atk16_asm/asm_pass2.py +++ b/atk16_asm/asm_pass2.py @@ -1,7 +1,7 @@ from dataclasses import dataclass -from asm_ops import * -from asm_eval import * -from asm_pass1 import * +from .asm_ops import * +from .asm_eval import * +from .asm_pass1 import * @dataclass class Result2Line: diff --git a/atk16_asm/asm_pass3.py b/atk16_asm/asm_pass3.py index 9c8cb34..09f58fa 100644 --- a/atk16_asm/asm_pass3.py +++ b/atk16_asm/asm_pass3.py @@ -1,7 +1,7 @@ from dataclasses import dataclass -from asm_ops import * -from asm_eval import * -from asm_pass2 import * +from .asm_ops import * +from .asm_eval import * +from .asm_pass2 import * @dataclass class Result3Line: diff --git a/atk16_asm/asm_pass4.py b/atk16_asm/asm_pass4.py index 2fff870..7365416 100644 --- a/atk16_asm/asm_pass4.py +++ b/atk16_asm/asm_pass4.py @@ -1,7 +1,7 @@ from dataclasses import dataclass -from asm_ops import * -from asm_eval import * -from asm_pass3 import * +from .asm_ops import * +from .asm_eval import * +from .asm_pass3 import * @dataclass class Result4Line: diff --git a/atk16_asm/assembler.py b/atk16_asm/assembler.py index c678ba0..1451d47 100755 --- a/atk16_asm/assembler.py +++ b/atk16_asm/assembler.py @@ -2,81 +2,64 @@ # Assemble ATK16 assembly to bytecode import sys -from asm_ops import * -from asm_eval import * -from asm_pass0 import pass_0 -from asm_pass1 import pass_1 -from asm_pass2 import pass_2 -from asm_pass3 import pass_3 -from asm_pass4 import pass_4 +from .asm_ops import * +from .asm_eval import * +from .asm_pass0 import pass_0 +from .asm_pass1 import pass_1 +from .asm_pass2 import pass_2 +from .asm_pass3 import pass_3 +from .asm_pass4 import pass_4 -if len(sys.argv) != 3: - print("usage: assembler.py <infile> <outfile> # read from file") - print(" assembler.py - <outfile> # read from stdin") - sys.exit(1) +def assemble(source: str, file_name: str) -> bytearray: + src_lines = source.splitlines() -infile_path = sys.argv[1] -outfile_path = sys.argv[2] + result0 = pass_0(src_lines, file_name) + result1 = pass_1(result0) + result2 = pass_2(result1) + result3 = pass_3(result2) + result4 = pass_4(result3) -src = "" -if (infile_path == "-"): - for line in sys.stdin: - src += line -else: - with open(infile_path, "r") as f: - src = f.read() + nop = bytearray([0b1000_0000, 0]) + result = bytearray() + # initially one nop + result.extend(nop) -src_lines = src.splitlines() + for line in result4.lines: + for (symbol, symbol_value) in result4.symbols.items(): + if line.address == symbol_value: + print(f"{symbol}:") -### Utils + if len(result) < 2 * line.address + 1: + result.extend((2 * line.address + 1 - len(result)) * nop) -def parse(line: str) -> list[str]: - depth = 0 - result: list[str] = [] - acc: str = "" - for c in line: - if c.isspace() and depth == 0: - result.append(acc) - acc = "" - elif c == "(": - depth += 1 - acc += "(" - elif c == ")": - depth -= 1 - acc += ")" - else: - acc += c + out_line = f"{line.address:>08x} 0x{line.word:>04x} {line.text}" + out_spaces_n = (42 - len(out_line)) + out_spaces = out_spaces_n * " " if out_spaces_n > 0 else 4 * " " + print(f"{out_line}{out_spaces}{line.original_text}") + result[2 * line.address + 0] = ((line.word >> 8) & 0xff) + result[2 * line.address + 1] = ((line.word >> 0) & 0xff) - result.append(acc) - return list(filter(lambda x: len(x) > 0, result)) + return result -result0 = pass_0(src_lines, infile_path) -result1 = pass_1(result0) -result2 = pass_2(result1) -result3 = pass_3(result2) -result4 = pass_4(result3) +if __name__ == "__main__": + if len(sys.argv) != 3: + print("usage: assembler.py <infile> <outfile> # read from file") + print(" assembler.py - <outfile> # read from stdin") + sys.exit(1) -nop = bytearray([0b1000_0000, 0]) -result = bytearray() -# initially one nop -result.extend(nop) + infile_path = sys.argv[1] + outfile_path = sys.argv[2] -for line in result4.lines: - for (symbol, symbol_value) in result4.symbols.items(): - if line.address == symbol_value: - print(f"{symbol}:") + src: str = "" + if (infile_path == "-"): + for line in sys.stdin: + src += line + else: + with open(infile_path, "r") as f: + src = f.read() - if len(result) < 2 * line.address + 1: - result.extend((2 * line.address + 1 - len(result)) * nop) + result = assemble(src, infile_path) + with open(outfile_path, "wb") as f: + f.write(result) - out_line = f"{line.address:>08x} 0x{line.word:>04x} {line.text}" - out_spaces_n = (42 - len(out_line)) - out_spaces = out_spaces_n * " " if out_spaces_n > 0 else 4 * " " - print(f"{out_line}{out_spaces}{line.original_text}") - result[2 * line.address + 0] = ((line.word >> 8) & 0xff) - result[2 * line.address + 1] = ((line.word >> 0) & 0xff) - -with open(outfile_path, "wb") as f: - f.write(result) - -print(f"Wrote {len(result)} bytes to {outfile_path}") + print(f"Wrote {len(result)} bytes to {outfile_path}") diff --git a/atk16_asm/optimizer.py b/atk16_asm/optimizer.py index 0ee2837..f08bec8 100644 --- a/atk16_asm/optimizer.py +++ b/atk16_asm/optimizer.py @@ -1,4 +1,4 @@ -from tokenizer import tokenize +from .tokenizer import tokenize def format_asm_row(asm: str) -> str: if not (asm.startswith("@") or asm.startswith(";")) and not asm.startswith(" ") and len(asm) > 0: diff --git a/atk16_emu/__init__.py b/atk16_emu/__init__.py index e69de29..a314994 100644 --- a/atk16_emu/__init__.py +++ b/atk16_emu/__init__.py @@ -0,0 +1 @@ +from .emu import Machine
\ No newline at end of file diff --git a/atk16_emu/cli.py b/atk16_emu/cli.py index 113eb87..7aa4b6b 100644 --- a/atk16_emu/cli.py +++ b/atk16_emu/cli.py @@ -51,15 +51,4 @@ machine.run_until_halted() print("Machine halted.") print("===============") -for i in range(8): - reg_name = f"r{chr(ord('a') + i)}" - value = getattr(machine, reg_name).value - print(f"{reg_name.upper()}: 0x{value:>04x} ({value})") - -print(f"PC: 0x{machine.pc.value:>04x} ({machine.pc.value})") -print(f"FR: carry={machine.fr.carry}\n " - f"overflow={machine.fr.overflow}\n " - f"zero={machine.fr.zero}\n " - f"sign={machine.fr.sign}") -for i in range(8): - print(f"RAM[{i}]: 0x{machine.ram.read(i):>04x} ({machine.ram.read(i)})") +machine.print_state_summary() diff --git a/atk16_emu/emu.py b/atk16_emu/emu.py index 1596222..591eeb5 100644 --- a/atk16_emu/emu.py +++ b/atk16_emu/emu.py @@ -184,7 +184,7 @@ class Machine: self.running = True def run_until_halted(self): - "Run the machine until HLT instruction is encountered" + "Run the self until HLT instruction is encountered" self.running = True while self.running: self.step() @@ -324,3 +324,17 @@ class Machine: case 0b1111: return HLT() raise ValueError(f"Invalid instruction: {instr:>016b} ({instr:>04x})") + + def print_state_summary(self): + for i in range(8): + reg_name = f"r{chr(ord('a') + i)}" + value = getattr(self, reg_name).value + print(f"{reg_name.upper()}: 0x{value:>04x} ({value})") + + print(f"PC: 0x{self.pc.value:>04x} ({self.pc.value})") + print(f"FR: carry={self.fr.carry}\n " + f"overflow={self.fr.overflow}\n " + f"zero={self.fr.zero}\n " + f"sign={self.fr.sign}") + for i in range(8): + print(f"RAM[{i}]: 0x{self.ram.read(i):>04x} ({self.ram.read(i)})")
\ No newline at end of file diff --git a/atk16_emu/test/test_opcode_alr.py b/atk16_emu/test/test_opcode_alr.py index e7fe1a7..9a34845 100644 --- a/atk16_emu/test/test_opcode_alr.py +++ b/atk16_emu/test/test_opcode_alr.py @@ -1,5 +1,5 @@ -from ..emu import Machine -from .utils import make_rom +from atk16_emu import Machine +from test.utils import make_rom def test_alr_add_small_unsigned(): machine = Machine() diff --git a/atk16_emu/test/test_opcode_hlt.py b/atk16_emu/test/test_opcode_hlt.py index 6991042..4d3d5c8 100644 --- a/atk16_emu/test/test_opcode_hlt.py +++ b/atk16_emu/test/test_opcode_hlt.py @@ -1,5 +1,5 @@ -from ..emu import Machine -from .utils import make_rom +from atk16_emu import Machine +from test.utils import make_rom def test_hlt(): machine = Machine() diff --git a/resources/asm/ext_std.py b/resources/asm/ext_std.py deleted file mode 100644 index 247e605..0000000 --- a/resources/asm/ext_std.py +++ /dev/null @@ -1,157 +0,0 @@ -from asm_ops import * - -def expand_add(left: str, right: str, target: str) -> ExpandResult: - return [["alr", "al_plus", left, right, target]] - -def expand_sub(left: str, right: str, target: str) -> ExpandResult: - return [["alr", "al_minus", left, right, target]] - -def expand_addi(left: str, imm: str, target: str) -> ExpandResult: - return [["ali", "al_plus", left, imm, target]] - -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]] - -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]] - -def expand_andi(left: str, imm: str, target: str) -> ExpandResult: - return [["ali", "al_and", left, imm, target]] - -def expand_or(left: str, right: str, target: str) -> ExpandResult: - return [["alr", "al_or", left, right, target]] - -def expand_ori(left: str, imm: str, target: str) -> ExpandResult: - return [["ali", "al_or", left, imm, target]] - -def expand_xor(left: str, imm: str, target: str) -> ExpandResult: - return [["alr", "al_xor", left, imm, target]] - -def expand_xori(left: str, right: str, target: str) -> ExpandResult: - return [["ali", "al_xor", left, right, target]] - -def expand_sll(left: str, right: str, target: str) -> ExpandResult: - return [["alr", "al_sll", left, right, target]] - -def expand_slr(left: str, right: str, target: str) -> ExpandResult: - return [["alr", "al_slr", left, right, target]] - -def expand_sar(left: str, right: str, target: str) -> ExpandResult: - return [["alr", "al_sar", left, right, target]] - -def expand_slli(left: str, imm: str, target: str) -> ExpandResult: - return [["ali", "al_sll", left, imm, target]] - -def expand_slri(left: str, imm: str, target: str) -> ExpandResult: - return [["ali", "al_slr", left, imm, target]] - -def expand_sari(left: str, imm: str, target: str) -> ExpandResult: - return [["ali", "al_sar", left, imm, target]] - -def expand_inc(reg: str) -> ExpandResult: - return [["ali", "al_plus", reg, "1", reg]] - -def expand_dec(reg: str) -> ExpandResult: - return [["ali", "al_minus", reg, "1", reg]] - -def expand_mov(from_reg: str, to_reg: str) -> ExpandResult: - return [["ali", "al_plus", from_reg, "0", to_reg]] - -def expand_nop() -> ExpandResult: - return [["ali", "al_plus", "RA", "0", "RA"]] - -def expand_spu(reg: str) -> ExpandResult: - return [["str", reg, "SP"]] + expand_inc("SP") - -def expand_spo(reg: str) -> ExpandResult: - return expand_dec("SP") + [["ldr", "SP", reg]] - -def expand_sinc(imm: str) -> ExpandResult: - return expand_addi("SP", imm, "SP") - -def expand_sdec(imm: str) -> ExpandResult: - return expand_subi("SP", imm, "SP") - -def expand_csr(addr_reg: str, stratch_reg: str): - return [ - ["lpc", stratch_reg], - *expand_addi(stratch_reg, "4", stratch_reg), - *expand_spu(stratch_reg), - ["jpr", addr_reg] - ] - -def expand_csi(addr_imm: str, stratch_reg: str): - return [ - ["lpc", stratch_reg], - *expand_addi(stratch_reg, "4", stratch_reg), - *expand_spu(stratch_reg), - ["jpi", addr_imm] - ] - -def expand_rsr(stratch_reg: str): - return expand_spo(stratch_reg) + [["jpr", stratch_reg]] - - -def stack_stash(*rs: str): - result: ExpandResult = [] - for r in rs: - result += expand_spu(r) - - return result - -def stack_restore(*rs: str): - result: ExpandResult = [] - for r in rs: - prefix = expand_spu(r) - result = prefix + result - - return result - -def set_graphics_mode(mode: str): - return [ - ["ldi", "vt_gr_mode_addr", "RA"], - ["ldr", "RA", "RA"], - ["ldi", mode, "RB"], - ["str", "RB", "RA"], - ] - -expansions: OpExpansionDict = { - "add": expand_add, - "sub": expand_sub, - "addi": expand_addi, - "subi": expand_subi, - "not": expand_not, - "noti": expand_noti, - "and": expand_and, - "andi": expand_andi, - "or": expand_or, - "ori": expand_ori, - "xor": expand_xor, - "xori": expand_xori, - "sll": expand_sll, - "slr": expand_slr, - "sar": expand_sar, - "slli": expand_slli, - "slri": expand_slri, - "sari": expand_sari, - "inc": expand_inc, - "dec": expand_dec, - "mov": expand_mov, - "nop": expand_nop, - "spu": expand_spu, - "spo": expand_spo, - "sinc": expand_sinc, - "sdec": expand_sdec, - "csr": expand_csr, - "csi": expand_csi, - "rsr": expand_rsr, - "stack_stash": stack_stash, - "stack_restore": stack_restore, - "set_graphics_mode": set_graphics_mode, -} diff --git a/resources/asm/ext_string_io.py b/resources/asm/ext_string_io.py index 519077d..e00ac78 100644 --- a/resources/asm/ext_string_io.py +++ b/resources/asm/ext_string_io.py @@ -1,5 +1,4 @@ -from asm_ops import * -from ext_std import * +from atk16_asm.asm_ops import * def to_char_code(c: str): return ord(c) diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/__init__.py diff --git a/test/e2e/fibo/bootstrap.atk16 b/test/e2e/fibo/bootstrap.atk16 new file mode 100644 index 0000000..0fe2a9d --- /dev/null +++ b/test/e2e/fibo/bootstrap.atk16 @@ -0,0 +1,78 @@ +@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 new file mode 100644 index 0000000..608b4d1 --- /dev/null +++ b/test/e2e/fibo/fibo.atk16 @@ -0,0 +1,53 @@ +; bootstrap code (must be called to setup mandatory data) +; will jump to label main +@include bootstrap + +@label main +; call fibo subroutine with parameter 10 + ldi 10 RA + spush RA + call fibo + spop RA + hlt + +@label fibo +; % n -> % result + +; store RB, RC on stack + spush RB + spush RC +; if n < 2, return n + subi RA 2 RA + bri sign fibo_early +; store also RD on stack + spush RD +; a = 0 + ldi 0 RB +; b = 1 + ldi 1 RC +@label fibo_loop +; v = a + b + add RB RC RD +; a = b + mov RC RB +; b = v + mov RD RC +; n -= 1 + dec RA +; loop while n > 0 + bri zero fibo_done + jpi fibo_loop +@label fibo_early +; restore used registers + spop RC + spop RB +; return from subroutine + return +@label fibo_done + mov RD RA +; restore used registers + spop RD + spop RC + spop RB +; return from subroutine + return diff --git a/test/e2e/fibo/test_fibo.py b/test/e2e/fibo/test_fibo.py new file mode 100644 index 0000000..426435a --- /dev/null +++ b/test/e2e/fibo/test_fibo.py @@ -0,0 +1,19 @@ +from atk16_asm import assemble +from atk16_emu import Machine +from test.utils import pad_bytearray + +def test_fibo(): + filename = "test/e2e/fibo/fibo.atk16" + with open(filename, "r") as f: + source = f.read() + + obj = assemble(source, filename) + rom_image = pad_bytearray(obj) + + machine = Machine() + machine.load_rom_image(rom_image) + machine.reset() + machine.run_until_halted() + machine.print_state_summary() + + assert machine.ra.value == 34 diff --git a/test/e2e/just_hlt/just_hlt.atk16 b/test/e2e/just_hlt/just_hlt.atk16 new file mode 100644 index 0000000..88320b9 --- /dev/null +++ b/test/e2e/just_hlt/just_hlt.atk16 @@ -0,0 +1 @@ +hlt diff --git a/test/e2e/just_hlt/test_just_hlt.py b/test/e2e/just_hlt/test_just_hlt.py new file mode 100644 index 0000000..60c59b0 --- /dev/null +++ b/test/e2e/just_hlt/test_just_hlt.py @@ -0,0 +1,19 @@ +from atk16_asm import assemble +from atk16_emu import Machine +from test.utils import pad_bytearray + +def test_just_hlt(): + filename = "test/e2e/just_hlt/just_hlt.atk16" + with open(filename, "r") as f: + source = f.read() + + obj = assemble(source, filename) + rom_image = pad_bytearray(obj) + + machine = Machine() + machine.load_rom_image(rom_image) + machine.reset() + machine.run_until_halted() + machine.print_state_summary() + + assert machine.pc.value == 1 diff --git a/test/e2e/sum/sum.atk16 b/test/e2e/sum/sum.atk16 new file mode 100644 index 0000000..8399767 --- /dev/null +++ b/test/e2e/sum/sum.atk16 @@ -0,0 +1,31 @@ +; Program: sum two values and store the result in RAM + +; ROM (and program execution) starts at offset 0x0 +@address 0x0 + jpi program + +@label ram_offset + 0x8000 ; store ram offset for later memory access + +@label program + ldi 10 RA ; RA := 10 + ldi 20 RB ; RB := 20 + alr al_plus RA RB RC ; RC := RA + RB + ldi ram_offset RD ; store address of ram_offset in RD + ldr RD RD ; dereference ram_offset address +@label debug + str RC RD ; store RC in RAM + + ; Check that 10 + 20 = 30 + ali al_plus RC 0 RA ; RA := result of sum + ldi 30 RB ; RB := 30 + alr al_minus RA RB RC ; RC := RA - RB + bri zero success ; if result is zero, jump to success + + ldi 2 RA ; RA := 2 to signal failure + hlt + + ; Else +@label success + ldi 1 RA ; RA := 1 to signal success + hlt diff --git a/test/e2e/sum/test_sum.py b/test/e2e/sum/test_sum.py new file mode 100644 index 0000000..3484af5 --- /dev/null +++ b/test/e2e/sum/test_sum.py @@ -0,0 +1,19 @@ +from atk16_asm import assemble +from atk16_emu import Machine +from test.utils import pad_bytearray + +def test_sum(): + filename = "test/e2e/sum/sum.atk16" + with open(filename, "r") as f: + source = f.read() + + obj = assemble(source, filename) + rom_image = pad_bytearray(obj) + + machine = Machine() + machine.load_rom_image(rom_image) + machine.reset() + machine.run_until_halted() + machine.print_state_summary() + + assert machine.rb.value == 30 diff --git a/atk16_emu/test/utils.py b/test/utils.py index 0d40d54..873aa64 100644 --- a/atk16_emu/test/utils.py +++ b/test/utils.py @@ -1,4 +1,4 @@ -def pad_bytearray(to_length: int, ba: bytearray) -> bytearray: +def pad_bytearray(ba: bytearray, to_length: int = 64 * 1024) -> bytearray: """ Pads a bytearray with zeros until it reaches a specified length. @@ -22,4 +22,4 @@ def make_rom(words: list[int]) -> bytearray: for word in words: bytes.append((word >> 8) & 0xFF) bytes.append(word & 0xFF) - return pad_bytearray(64 * 1024, bytearray(bytes))
\ No newline at end of file + return pad_bytearray(bytearray(bytes)) |
