aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Tuomi <jans.tuomi@gmail.com>2024-02-22 08:52:20 +0200
committerJan Tuomi <jans.tuomi@gmail.com>2024-02-22 08:52:20 +0200
commit416e896956d3b05793337da6609fe1544797c0a9 (patch)
tree9fa0cf3b622d4c9c086e4c08c5011ab12fdd4a77
parentaefe0cf3874c542669051b2b3cc21c9590aa970b (diff)
Add stuff, refactor
-rw-r--r--Makefile2
-rw-r--r--atk16_asm/__init__.py1
-rw-r--r--atk16_asm/asm_ops.py166
-rw-r--r--atk16_asm/asm_pass0.py6
-rw-r--r--atk16_asm/asm_pass1.py10
-rw-r--r--atk16_asm/asm_pass2.py6
-rw-r--r--atk16_asm/asm_pass3.py6
-rw-r--r--atk16_asm/asm_pass4.py6
-rwxr-xr-xatk16_asm/assembler.py115
-rw-r--r--atk16_asm/optimizer.py2
-rw-r--r--atk16_emu/__init__.py1
-rw-r--r--atk16_emu/cli.py13
-rw-r--r--atk16_emu/emu.py16
-rw-r--r--atk16_emu/test/test_opcode_alr.py4
-rw-r--r--atk16_emu/test/test_opcode_hlt.py4
-rw-r--r--resources/asm/ext_std.py157
-rw-r--r--resources/asm/ext_string_io.py3
-rw-r--r--test/__init__.py0
-rw-r--r--test/e2e/fibo/bootstrap.atk1678
-rw-r--r--test/e2e/fibo/fibo.atk1653
-rw-r--r--test/e2e/fibo/test_fibo.py19
-rw-r--r--test/e2e/just_hlt/just_hlt.atk161
-rw-r--r--test/e2e/just_hlt/test_just_hlt.py19
-rw-r--r--test/e2e/sum/sum.atk1631
-rw-r--r--test/e2e/sum/test_sum.py19
-rw-r--r--test/utils.py (renamed from atk16_emu/test/utils.py)4
26 files changed, 473 insertions, 269 deletions
diff --git a/Makefile b/Makefile
index 3c3ae26..3e7a8af 100644
--- a/Makefile
+++ b/Makefile
@@ -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))