aboutsummaryrefslogtreecommitdiffstats
path: root/atk16_emu
diff options
context:
space:
mode:
Diffstat (limited to 'atk16_emu')
-rw-r--r--atk16_emu/cli.py21
-rw-r--r--atk16_emu/debugger.py98
-rw-r--r--atk16_emu/emu.py8
3 files changed, 119 insertions, 8 deletions
diff --git a/atk16_emu/cli.py b/atk16_emu/cli.py
index 7aa4b6b..d6bd46b 100644
--- a/atk16_emu/cli.py
+++ b/atk16_emu/cli.py
@@ -1,6 +1,7 @@
import sys
from dataclasses import dataclass
from .emu import Machine
+from .debugger import Debugger
@dataclass
class Options:
@@ -43,12 +44,18 @@ def load_rom_image_from_path(path: str) -> bytearray:
rom_image = load_rom_image_from_path(options.rom_image_path)
-machine = Machine()
-machine.load_rom_image(rom_image)
-machine.reset()
-machine.run_until_halted()
+if not options.debugger_enabled:
+ machine = Machine()
+ machine.load_rom_image(rom_image)
+ machine.reset()
+ machine.run_until_halted()
-print("Machine halted.")
-print("===============")
+ print("Machine halted.")
+ print("===============")
-machine.print_state_summary()
+ machine.print_state_summary()
+
+else:
+ debugger = Debugger()
+ debugger.load_rom_image(rom_image)
+ debugger.activate()
diff --git a/atk16_emu/debugger.py b/atk16_emu/debugger.py
new file mode 100644
index 0000000..f887ed0
--- /dev/null
+++ b/atk16_emu/debugger.py
@@ -0,0 +1,98 @@
+from getch import getche
+from .emu import Machine
+
+class Debugger:
+ def __init__(self):
+ self.rom_image = None
+ self.breakpoints: set[int] = set()
+ self.machine = Machine()
+ self.machine.reset()
+ self.machine.run()
+
+ def load_rom_image(self, rom_image: bytearray) -> None:
+ self.rom_image = rom_image
+ self.machine.load_rom_image(rom_image)
+
+ print("Loaded rom image.")
+
+ def print_pc_context(self):
+ print("=== Program context")
+ for i in range(-4, 5):
+ addr = self.machine.pc.value + i
+ if addr < 0 or addr >= 64 * 2 ** 16:
+ continue
+
+ if i == 0:
+ print(f"> 0x{addr:>04x}: 0x{self.machine.mem_read(addr):>04x}")
+ else:
+ print(f" 0x{addr:>04x}: 0x{self.machine.mem_read(addr):>04x}")
+
+ print()
+
+ def activate(self):
+ print("=== ATK16 debugger ===")
+ print("Press ? to show command help. Press q to quit.")
+ print()
+
+ while True:
+ self.print_pc_context()
+ print("dbg> ", end="", flush=True)
+ cmd = getche()
+ print()
+
+ if cmd == "q":
+ break
+ elif cmd == "?":
+ self.print_help()
+ elif cmd == "r":
+ while self.machine.running:
+ self.machine.step()
+ if self.machine.pc.value in self.breakpoints:
+ print(f"Breakpoint hit at 0x{self.machine.pc.value:>04x}")
+ break
+
+ if not self.machine.running:
+ print("Machine halted.")
+
+ elif cmd == "b":
+ print("Set breakpoints:")
+ for addr in self.breakpoints:
+ print(f" 0x{addr:>04x}")
+ if len(self.breakpoints) == 0:
+ print("<no breakpoints>")
+ print()
+
+ try:
+ addr = eval(input("Breakpoint address: "), {})
+ except:
+ print("Cancelled")
+ continue
+ if type(addr) != int or addr < 0 or addr >= 64 * 2 ** 16:
+ print("Invalid address")
+
+ if addr in self.breakpoints:
+ self.breakpoints.remove(addr)
+ print(f"Removed breakpoint at 0x{addr:>04x}")
+ else:
+ self.breakpoints.add(addr)
+ elif cmd == "n":
+ self.machine.step()
+ elif cmd == "s":
+ self.machine.print_state_summary()
+ elif cmd == "0":
+ self.machine.reset()
+ print("Machine reset.")
+ else:
+ print(f"Unknown command: {cmd}")
+
+ def print_help(self):
+ print("Debugger commands:")
+ print(" r run until next breakpoint or until halted")
+ print(" n step forward")
+ print(" b step backward")
+ print(" b set or remove breakpoint")
+ print(" s show state summary")
+ print(" 0 reset the machine state")
+ print(" q quit")
+ print(" ? show this help")
+ print()
diff --git a/atk16_emu/emu.py b/atk16_emu/emu.py
index 591eeb5..8eeb6b1 100644
--- a/atk16_emu/emu.py
+++ b/atk16_emu/emu.py
@@ -208,6 +208,8 @@ class Machine:
instr = self.mem_read(pc_addr)
instruction = self.decode(instr)
+ #print(f"Executing instruction 0b{instr:>016b} (0x{instr:>04x}) at address 0x{pc_addr:>04x}")
+
try:
match instruction:
case ALR(target, left, right, alu_code):
@@ -254,6 +256,8 @@ class Machine:
self.pc.value = addr
case JPI(imm):
+ # convert imm from signed (twos complement) 9-bit to a python int
+ imm = (imm & (0b011111111)) - (imm & 0b100000000)
self.pc.value = (self.pc.value + imm) & 0xFFFF
case BRR(flag, addr_reg):
@@ -262,6 +266,8 @@ class Machine:
self.pc.value = addr
case BRI(flag, addr_imm):
+ # convert imm from signed (twos complement) 9-bit to a python int
+ addr_imm = (addr_imm & (0b011111111)) - (addr_imm & 0b100000000)
if self.check_nth_flag(flag):
self.pc.value = (self.pc.value + addr_imm) & 0xFFFF
@@ -288,7 +294,7 @@ class Machine:
self.running = False
except:
- print(f"Error while executing instruction {instr:>016b} ({instr:>04x}) at address {pc_addr:>04x}", file=sys.stderr)
+ print(f"Error while executing instruction 0b{instr:>016b} (0x{instr:>04x}) at address 0x{pc_addr:>04x}", file=sys.stderr)
raise
def decode(self, instr: int):