Emulator improvements
This commit is contained in:
@@ -18,6 +18,11 @@ SCI1_SCR = 0xFEDA
|
||||
SCI1_TDR = 0xFEDB
|
||||
SCI1_SSR = 0xFEDC
|
||||
SCI1_RDR = 0xFEDD
|
||||
P9DDR = 0xFEFE
|
||||
P9DR = 0xFEFF
|
||||
IPRA = 0xFF00
|
||||
IPRE = 0xFF04
|
||||
WDT_TCSR_R = 0xFEEC
|
||||
|
||||
SCI_SCR_TIE = 0x80
|
||||
SCI_SCR_RIE = 0x40
|
||||
@@ -35,6 +40,8 @@ REGISTER_FIELD_START = 0xFE80
|
||||
REGISTER_FIELD_END = 0xFFFF
|
||||
RAMCR = 0xFF11
|
||||
HEARTBEAT_FRAME = bytes([0x00, 0x00, 0x00, 0x00, 0x80, 0xDA])
|
||||
VECTOR_INTERVAL_TIMER = 0x0042
|
||||
VECTOR_SCI1_TXI = 0x0084
|
||||
|
||||
|
||||
class EmulatorError(Exception):
|
||||
@@ -80,6 +87,7 @@ class SCI1:
|
||||
tx_events: list[SciTxEvent] = field(default_factory=list)
|
||||
tx_frames: list[bytes] = field(default_factory=list)
|
||||
_frame_buffer: bytearray = field(default_factory=bytearray)
|
||||
tx_ready_delay: int = 0
|
||||
|
||||
def read(self, address: int) -> int:
|
||||
if address == SCI1_SMR:
|
||||
@@ -126,6 +134,7 @@ class SCI1:
|
||||
self.tx_frames.append(bytes(self._frame_buffer))
|
||||
self._frame_buffer.clear()
|
||||
self.ssr |= SCI_SSR_TDRE
|
||||
self.tx_ready_delay = 2
|
||||
self.tx_events.append(SciTxEvent(SCI1_TDR, value, self.scr, self.ssr, emitted))
|
||||
|
||||
def inject_rx(self, value: int) -> None:
|
||||
@@ -135,6 +144,12 @@ class SCI1:
|
||||
def saw_heartbeat(self) -> bool:
|
||||
return HEARTBEAT_FRAME in self.tx_frames
|
||||
|
||||
def tick(self) -> None:
|
||||
if self.tx_ready_delay:
|
||||
self.tx_ready_delay -= 1
|
||||
if self.tx_ready_delay == 0:
|
||||
self.ssr |= SCI_SSR_TDRE
|
||||
|
||||
|
||||
@dataclass
|
||||
class MemoryAccess:
|
||||
@@ -169,10 +184,22 @@ class MemoryMap:
|
||||
if address in (SCI1_SMR, SCI1_BRR, SCI1_SCR, SCI1_TDR, SCI1_SSR, SCI1_RDR):
|
||||
value = self.sci1.read(address)
|
||||
self._set_register(address, value)
|
||||
elif address in self.external:
|
||||
value = self.external[address]
|
||||
elif address in (0xF200, 0xF201):
|
||||
# LCD E-clock/status space. Default to ready/zero so boot can pass
|
||||
# busy-flag polling until a fuller external bus model exists.
|
||||
value = 0x00
|
||||
elif ON_CHIP_RAM_START <= address <= ON_CHIP_RAM_END:
|
||||
value = self.ram[address - ON_CHIP_RAM_START]
|
||||
elif REGISTER_FIELD_START <= address <= REGISTER_FIELD_END:
|
||||
value = self.registers[address - REGISTER_FIELD_START]
|
||||
if address == P9DR and not (self.registers[P9DDR - REGISTER_FIELD_START] & 0x80):
|
||||
# P97 is used as an input during the serial panel/camera-side
|
||||
# bit-bang handshake. With no external device modeled, hold the
|
||||
# input low so the firmware sees an idle/acknowledged bus rather
|
||||
# than reading back its previous output latch forever.
|
||||
value &= 0x7F
|
||||
elif self.rom.contains(address):
|
||||
value = self.rom.u8(address)
|
||||
else:
|
||||
@@ -196,9 +223,10 @@ class MemoryMap:
|
||||
elif REGISTER_FIELD_START <= address <= REGISTER_FIELD_END:
|
||||
self._set_register(address, value)
|
||||
elif self.rom.contains(address):
|
||||
# ROM writes are logged but ignored. This keeps the scaffold useful
|
||||
# for accidental writes without mutating the loaded image.
|
||||
pass
|
||||
# The ROM image spans the whole address space, but the H8/536 map
|
||||
# can place external RAM/peripherals in these ranges. Keep writes
|
||||
# as external overrides while leaving instruction fetch immutable.
|
||||
self.external[address] = value
|
||||
else:
|
||||
self.external[address] = value
|
||||
self._log("write", address, 1, value)
|
||||
@@ -223,6 +251,10 @@ class CPUState:
|
||||
cycles: int = 0
|
||||
steps: int = 0
|
||||
z: bool = False
|
||||
c: bool = False
|
||||
n: bool = False
|
||||
v: bool = False
|
||||
interrupt_depth: int = 0
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -254,13 +286,15 @@ class RunReport:
|
||||
|
||||
|
||||
class H8536Emulator:
|
||||
def __init__(self, rom_bytes: bytes) -> None:
|
||||
def __init__(self, rom_bytes: bytes, *, interval_steps: int = 2048) -> None:
|
||||
if not rom_bytes:
|
||||
raise ValueError("ROM image is empty")
|
||||
self.sci1 = SCI1()
|
||||
self.memory = MemoryMap(rom_bytes, self.sci1)
|
||||
self.cpu = CPUState()
|
||||
self.vectors = read_vectors_min(self.memory.rom)
|
||||
self.interval_steps = max(1, interval_steps)
|
||||
self._interval_counter = 0
|
||||
self.reset()
|
||||
|
||||
def reset(self) -> None:
|
||||
@@ -284,12 +318,35 @@ class H8536Emulator:
|
||||
|
||||
if raw[0] == 0x00:
|
||||
pass
|
||||
elif raw[0] == 0x02 and len(raw) == 2:
|
||||
self._pop_register_mask(raw[1])
|
||||
elif raw[0] == 0x12 and len(raw) == 2:
|
||||
self._push_register_mask(raw[1])
|
||||
elif raw[0] in (0x01, 0x06, 0x07) and len(raw) == 3 and 0xB8 <= raw[1] <= 0xBF:
|
||||
next_pc = self._scb(raw, pc, next_pc)
|
||||
elif raw[0] in (0x04, 0x0C):
|
||||
next_pc = self._execute_general(pc, next_pc)
|
||||
elif 0x40 <= raw[0] <= 0x47 and len(raw) == 2:
|
||||
reg = raw[0] & 0x07
|
||||
self._cmp(self._reg_read(reg, 1), raw[1], 1)
|
||||
elif 0x48 <= raw[0] <= 0x4F and len(raw) == 3:
|
||||
reg = raw[0] & 0x07
|
||||
self._cmp(self.cpu.regs[reg], int.from_bytes(raw[1:3], "big"), 2)
|
||||
elif 0x50 <= raw[0] <= 0x57 and len(raw) == 2:
|
||||
self._reg_write(raw[0] & 0x07, raw[1], 1)
|
||||
elif 0x58 <= raw[0] <= 0x5F and len(raw) == 3:
|
||||
self.cpu.regs[raw[0] & 0x07] = int.from_bytes(raw[1:3], "big")
|
||||
elif raw[:2] == bytes([0x0C, 0x07]) and len(raw) == 4:
|
||||
self.cpu.sr = int.from_bytes(raw[2:4], "big")
|
||||
self._set_logic_flags(self.cpu.regs[raw[0] & 0x07], 2)
|
||||
elif raw[0] in (0x0E, 0x1E, 0x18):
|
||||
next_pc = self._direct_call(raw, next_pc)
|
||||
elif raw[0] == 0x19:
|
||||
next_pc = self._pop16()
|
||||
elif raw[0] == 0x0A:
|
||||
next_pc = self._return_from_interrupt()
|
||||
elif raw[0] in (0x15, 0x1D) and len(raw) >= 4:
|
||||
next_pc = self._execute_general_abs(raw, pc, next_pc)
|
||||
next_pc = self._execute_general(pc, next_pc)
|
||||
elif raw[0] in range(0xA0, 0x100):
|
||||
next_pc = self._execute_general(pc, next_pc)
|
||||
elif raw[0] in range(0x20, 0x30) and len(raw) == 2:
|
||||
next_pc = self._branch8(raw, pc, next_pc)
|
||||
elif raw[0] in range(0x30, 0x40) and len(raw) == 3:
|
||||
@@ -300,6 +357,7 @@ class H8536Emulator:
|
||||
self.cpu.pc = next_pc
|
||||
self.cpu.steps += 1
|
||||
self.cpu.cycles += self._rough_cycles(raw)
|
||||
self._tick_peripherals()
|
||||
return f"{h16(pc)}: {' '.join(f'{byte:02X}' for byte in raw):<17} {text}"
|
||||
|
||||
def run(self, max_steps: int, trace: bool = False, stop_on_heartbeat: bool = False) -> RunReport:
|
||||
@@ -330,51 +388,134 @@ class H8536Emulator:
|
||||
trace=trace_lines,
|
||||
)
|
||||
|
||||
def _execute_general_abs(self, raw: bytes, pc: int, next_pc: int) -> int:
|
||||
size = "W" if raw[0] == 0x1D else "B"
|
||||
address = int.from_bytes(raw[1:3], "big")
|
||||
op = raw[3]
|
||||
def _execute_general(self, pc: int, next_pc: int) -> int:
|
||||
ea = self._decode_ea(pc)
|
||||
op = self.memory.rom.u8(pc + ea["length"])
|
||||
size = int(ea["size"])
|
||||
raw = self.memory.rom.slice(pc, self._general_length(pc, ea, op))
|
||||
|
||||
if op == 0x06:
|
||||
value = raw[4]
|
||||
self.memory.write8(address, value)
|
||||
elif op == 0x07:
|
||||
value = int.from_bytes(raw[4:6], "big")
|
||||
self.memory.write16(address, value)
|
||||
elif 0x90 <= op <= 0x97:
|
||||
reg = self.cpu.regs[op & 0x07]
|
||||
if size == "W":
|
||||
self.memory.write16(address, reg)
|
||||
if op == 0x00:
|
||||
ext = self.memory.rom.u8(pc + int(ea["length"]) + 1)
|
||||
ext_base = ext & 0xF8
|
||||
reg = ext & 0x07
|
||||
if ext_base == 0x80:
|
||||
value = self._read_ea(ea, 1)
|
||||
self._reg_write(reg, value, 1)
|
||||
self._set_logic_flags(value, 1)
|
||||
elif ext_base == 0x90:
|
||||
self._write_ea(ea, self._reg_read(reg, 1), 1)
|
||||
else:
|
||||
self.memory.write8(address, reg & 0xFF)
|
||||
elif 0x80 <= op <= 0x87:
|
||||
reg = op & 0x07
|
||||
self.cpu.regs[reg] = self.memory.read16(address) if size == "W" else self.memory.read8(address)
|
||||
elif 0xC0 <= op <= 0xCF:
|
||||
raise UnsupportedInstruction(pc, raw, H8536Decoder(self.memory.rom, br=self.cpu.br).decode(pc).text)
|
||||
return next_pc
|
||||
|
||||
if op in (0x06, 0x07):
|
||||
value = raw[-1] if op == 0x06 else int.from_bytes(raw[-2:], "big")
|
||||
self._write_ea(ea, value, 1 if op == 0x06 else 2)
|
||||
self._set_logic_flags(value, 1 if op == 0x06 else 2)
|
||||
return next_pc
|
||||
|
||||
base = op & 0xF8
|
||||
rd = op & 0x07
|
||||
if ea["mode"] == "imm" and base in (0x48, 0x58, 0x68):
|
||||
value = self._read_ea(ea, size)
|
||||
if base == 0x48:
|
||||
self.cpu.sr |= value
|
||||
elif base == 0x58:
|
||||
self.cpu.sr &= value
|
||||
else:
|
||||
self.cpu.sr ^= value
|
||||
elif base == 0x80:
|
||||
value = self._read_ea(ea, size)
|
||||
self._reg_write(rd, value, size)
|
||||
self._set_logic_flags(value, size)
|
||||
elif base == 0x90:
|
||||
self._write_ea(ea, self._reg_read(rd, size), size)
|
||||
elif base == 0x20:
|
||||
lhs = self._reg_read(rd, size)
|
||||
rhs = self._read_ea(ea, size)
|
||||
result = lhs + rhs
|
||||
self._reg_write(rd, result, size)
|
||||
self._set_add_flags(lhs, rhs, result, size)
|
||||
elif base == 0x30:
|
||||
lhs = self._reg_read(rd, size)
|
||||
rhs = self._read_ea(ea, size)
|
||||
result = lhs - rhs
|
||||
self._reg_write(rd, result, size)
|
||||
self._set_sub_flags(lhs, rhs, result, size)
|
||||
elif base == 0x50:
|
||||
result = self._reg_read(rd, size) & self._read_ea(ea, size)
|
||||
self._reg_write(rd, result, size)
|
||||
self._set_logic_flags(result, size)
|
||||
elif base == 0x40:
|
||||
result = self._reg_read(rd, size) | self._read_ea(ea, size)
|
||||
self._reg_write(rd, result, size)
|
||||
self._set_logic_flags(result, size)
|
||||
elif base == 0x60:
|
||||
result = self._reg_read(rd, size) ^ self._read_ea(ea, size)
|
||||
self._reg_write(rd, result, size)
|
||||
self._set_logic_flags(result, size)
|
||||
elif base == 0x70:
|
||||
self._cmp(self._reg_read(rd, size), self._read_ea(ea, size), size)
|
||||
elif op in (0x08, 0x09, 0x0C, 0x0D):
|
||||
delta = {0x08: 1, 0x09: 2, 0x0C: -1, 0x0D: -2}[op]
|
||||
old = self._read_ea(ea, size)
|
||||
result = old + delta
|
||||
self._write_ea(ea, result, size)
|
||||
if delta >= 0:
|
||||
self._set_add_flags(old, delta, result, size)
|
||||
else:
|
||||
self._set_sub_flags(old, -delta, result, size)
|
||||
elif op == 0x13:
|
||||
self._write_ea(ea, 0, size)
|
||||
self._set_logic_flags(0, size)
|
||||
elif op == 0x16:
|
||||
self._set_logic_flags(self._read_ea(ea, size), size)
|
||||
elif op == 0x10:
|
||||
value = self._read_ea(ea, size)
|
||||
result = ((value & 0xFF) << 8) | ((value >> 8) & 0xFF)
|
||||
self._write_ea(ea, result, 2)
|
||||
self._set_logic_flags(result, 2)
|
||||
elif op == 0x12:
|
||||
value = self._read_ea(ea, size) & 0xFF
|
||||
self._write_ea(ea, value, 2 if size == 2 else 1)
|
||||
self._set_logic_flags(value, size)
|
||||
elif op == 0x1A:
|
||||
value = self._read_ea(ea, size)
|
||||
result = (value << 1) & _mask(size)
|
||||
self.cpu.c = bool(value & _sign_bit(size))
|
||||
self._write_ea(ea, result, size)
|
||||
self._set_logic_flags(result, size)
|
||||
elif op == 0x1B:
|
||||
value = self._read_ea(ea, size)
|
||||
result = value >> 1
|
||||
self.cpu.c = bool(value & 1)
|
||||
self._write_ea(ea, result, size)
|
||||
self._set_logic_flags(result, size)
|
||||
elif 0xC0 <= op <= 0xFF:
|
||||
bit = op & 0x0F
|
||||
self.memory.write8(address, self.memory.read8(address) | (1 << bit))
|
||||
elif 0xD0 <= op <= 0xDF:
|
||||
bit = op & 0x0F
|
||||
self.memory.write8(address, self.memory.read8(address) & ~(1 << bit))
|
||||
elif 0xF0 <= op <= 0xFF:
|
||||
bit = op & 0x0F
|
||||
self.cpu.z = not bool(self.memory.read8(address) & (1 << bit))
|
||||
self._bit_operation(ea, size, op & 0xF0, bit)
|
||||
elif base in (0x48, 0x58, 0x68, 0x78):
|
||||
bit = self._reg_read(rd, 1) & 0x0F
|
||||
self._bit_operation(ea, size, base + 0x80, bit)
|
||||
elif base == 0x88:
|
||||
value = self._read_ea(ea, size)
|
||||
if size == 2 and rd == 0:
|
||||
self.cpu.sr = value & 0xFFFF
|
||||
elif size == 1 and rd == 1:
|
||||
self.cpu.br = value & 0xFF
|
||||
else:
|
||||
raise UnsupportedInstruction(pc, raw, H8536Decoder(self.memory.rom, br=self.cpu.br).decode(pc).text)
|
||||
elif base == 0x98:
|
||||
if size == 2 and rd == 0:
|
||||
self._write_ea(ea, self.cpu.sr, 2)
|
||||
elif size == 1 and rd == 1:
|
||||
self._write_ea(ea, self.cpu.br, 1)
|
||||
else:
|
||||
raise UnsupportedInstruction(pc, raw, H8536Decoder(self.memory.rom, br=self.cpu.br).decode(pc).text)
|
||||
elif op in (0x04, 0x05):
|
||||
if op == 0x04:
|
||||
value = raw[4]
|
||||
actual = self.memory.read8(address)
|
||||
else:
|
||||
value = int.from_bytes(raw[4:6], "big")
|
||||
actual = self.memory.read16(address)
|
||||
self.cpu.z = actual == value
|
||||
elif op == 0x08:
|
||||
value = self.memory.read16(address) if size == "W" else self.memory.read8(address)
|
||||
value = (value + 1) & (0xFFFF if size == "W" else 0xFF)
|
||||
if size == "W":
|
||||
self.memory.write16(address, value)
|
||||
else:
|
||||
self.memory.write8(address, value)
|
||||
self.cpu.z = value == 0
|
||||
compare_size = 1 if op == 0x04 else 2
|
||||
immediate = raw[-1] if op == 0x04 else int.from_bytes(raw[-2:], "big")
|
||||
self._cmp(self._read_ea(ea, compare_size), immediate, compare_size)
|
||||
else:
|
||||
raise UnsupportedInstruction(pc, raw, H8536Decoder(self.memory.rom, br=self.cpu.br).decode(pc).text)
|
||||
return next_pc
|
||||
@@ -383,11 +524,7 @@ class H8536Emulator:
|
||||
cond = raw[0] & 0x0F
|
||||
disp = _s8(raw[1])
|
||||
target = (pc + 2 + disp) & 0xFFFF
|
||||
if cond == 0x0:
|
||||
return target
|
||||
if cond == 0x7 and self.cpu.z:
|
||||
return target
|
||||
if cond == 0x6 and not self.cpu.z:
|
||||
if self._branch_condition(cond):
|
||||
return target
|
||||
return next_pc
|
||||
|
||||
@@ -395,21 +532,273 @@ class H8536Emulator:
|
||||
cond = raw[0] & 0x0F
|
||||
disp = _s16(int.from_bytes(raw[1:3], "big"))
|
||||
target = (pc + 3 + disp) & 0xFFFF
|
||||
if cond == 0x0:
|
||||
return target
|
||||
if cond == 0x7 and self.cpu.z:
|
||||
return target
|
||||
if cond == 0x6 and not self.cpu.z:
|
||||
if self._branch_condition(cond):
|
||||
return target
|
||||
return next_pc
|
||||
|
||||
def _rough_cycles(self, raw: bytes) -> int:
|
||||
if raw[0] in (0x15, 0x1D):
|
||||
return 9
|
||||
if raw[0] in (0x0E, 0x1E, 0x18):
|
||||
return 14
|
||||
if raw[0] in (0x19, 0x0A):
|
||||
return 12
|
||||
if raw[0] in range(0x20, 0x40):
|
||||
return 8 if (raw[0] & 0x0F) == 0 else 4
|
||||
return 3
|
||||
|
||||
def _direct_call(self, raw: bytes, next_pc: int) -> int:
|
||||
self._push16(next_pc)
|
||||
if raw[0] == 0x0E:
|
||||
return (next_pc + _s8(raw[1])) & 0xFFFF
|
||||
if raw[0] == 0x1E:
|
||||
return (next_pc + _s16(int.from_bytes(raw[1:3], "big"))) & 0xFFFF
|
||||
return int.from_bytes(raw[1:3], "big")
|
||||
|
||||
def _scb(self, raw: bytes, pc: int, next_pc: int) -> int:
|
||||
reg = raw[1] & 0x07
|
||||
condition = {0x01: False, 0x06: not self.cpu.z, 0x07: self.cpu.z}[raw[0]]
|
||||
if condition:
|
||||
return next_pc
|
||||
value = (self.cpu.regs[reg] - 1) & 0xFFFF
|
||||
self.cpu.regs[reg] = value
|
||||
self.cpu.z = value == 0
|
||||
if value != 0:
|
||||
return (pc + 3 + _s8(raw[2])) & 0xFFFF
|
||||
return next_pc
|
||||
|
||||
def _tick_peripherals(self) -> None:
|
||||
self.sci1.tick()
|
||||
self._interval_counter += 1
|
||||
self._service_pending_interrupt()
|
||||
|
||||
def _service_pending_interrupt(self) -> None:
|
||||
if self.cpu.interrupt_depth:
|
||||
return
|
||||
candidates: list[tuple[int, int, str]] = []
|
||||
if self.sci1.scr & SCI_SCR_TIE and self.sci1.ssr & SCI_SSR_TDRE:
|
||||
target = self._vector_target(VECTOR_SCI1_TXI)
|
||||
if target is not None:
|
||||
candidates.append((self._sci1_priority(), target, "sci1_txi"))
|
||||
if self._interval_counter >= self.interval_steps:
|
||||
target = self._vector_target(VECTOR_INTERVAL_TIMER)
|
||||
if target is not None:
|
||||
candidates.append((self._interval_priority(), target, "interval_timer"))
|
||||
|
||||
if not candidates:
|
||||
return
|
||||
priority, target, source = max(candidates, key=lambda item: item[0])
|
||||
if priority <= self._interrupt_mask():
|
||||
return
|
||||
if source == "interval_timer":
|
||||
self._interval_counter = 0
|
||||
self._enter_interrupt(target)
|
||||
|
||||
def _enter_interrupt(self, target: int) -> None:
|
||||
self._push16(self.cpu.sr)
|
||||
self._push16(self.cpu.pc)
|
||||
self.cpu.pc = target & 0xFFFF
|
||||
self.cpu.interrupt_depth += 1
|
||||
|
||||
def _return_from_interrupt(self) -> int:
|
||||
pc = self._pop16()
|
||||
self.cpu.sr = self._pop16()
|
||||
if self.cpu.interrupt_depth:
|
||||
self.cpu.interrupt_depth -= 1
|
||||
return pc
|
||||
|
||||
def _vector_target(self, vector_address: int) -> int | None:
|
||||
entry = self.vectors.get(vector_address)
|
||||
return entry[1] if entry else None
|
||||
|
||||
def _interrupt_mask(self) -> int:
|
||||
return (self.cpu.sr >> 8) & 0x07
|
||||
|
||||
def _sci1_priority(self) -> int:
|
||||
return (self.memory.read8(IPRE) >> 4) & 0x07
|
||||
|
||||
def _interval_priority(self) -> int:
|
||||
return (self.memory.read8(IPRA) >> 4) & 0x07
|
||||
|
||||
def _push16(self, value: int) -> None:
|
||||
sp = (self.cpu.regs[7] - 2) & 0xFFFF
|
||||
self.cpu.regs[7] = sp
|
||||
self.memory.write16(sp, value)
|
||||
|
||||
def _pop16(self) -> int:
|
||||
sp = self.cpu.regs[7] & 0xFFFF
|
||||
value = self.memory.read16(sp)
|
||||
self.cpu.regs[7] = (sp + 2) & 0xFFFF
|
||||
return value
|
||||
|
||||
def _push_register_mask(self, mask: int) -> None:
|
||||
for reg in range(8):
|
||||
if mask & (1 << reg):
|
||||
self._push16(self.cpu.regs[reg])
|
||||
|
||||
def _pop_register_mask(self, mask: int) -> None:
|
||||
for reg in reversed(range(8)):
|
||||
if mask & (1 << reg):
|
||||
self.cpu.regs[reg] = self._pop16()
|
||||
|
||||
def _decode_ea(self, pc: int) -> dict[str, int | str | None]:
|
||||
first = self.memory.rom.u8(pc)
|
||||
if 0xA0 <= first <= 0xAF:
|
||||
return {"mode": "reg", "reg": first & 0x07, "size": 2 if first & 0x08 else 1, "length": 1, "value": None}
|
||||
if 0xB0 <= first <= 0xBF:
|
||||
return {"mode": "predec", "reg": first & 0x07, "size": 2 if first & 0x08 else 1, "length": 1, "value": None}
|
||||
if 0xC0 <= first <= 0xCF:
|
||||
return {"mode": "postinc", "reg": first & 0x07, "size": 2 if first & 0x08 else 1, "length": 1, "value": None}
|
||||
if 0xD0 <= first <= 0xDF:
|
||||
return {"mode": "indirect", "reg": first & 0x07, "size": 2 if first & 0x08 else 1, "length": 1, "value": None}
|
||||
if 0xE0 <= first <= 0xEF:
|
||||
return {"mode": "disp", "reg": first & 0x07, "size": 2 if first & 0x08 else 1, "length": 2, "value": _s8(self.memory.rom.u8(pc + 1))}
|
||||
if 0xF0 <= first <= 0xFF:
|
||||
return {"mode": "disp", "reg": first & 0x07, "size": 2 if first & 0x08 else 1, "length": 3, "value": _s16(self.memory.rom.u16(pc + 1))}
|
||||
if first in (0x04, 0x0C):
|
||||
size = 2 if first == 0x0C else 1
|
||||
value = self.memory.rom.u16(pc + 1) if size == 2 else self.memory.rom.u8(pc + 1)
|
||||
return {"mode": "imm", "reg": None, "size": size, "length": 3 if size == 2 else 2, "value": value}
|
||||
if first in (0x15, 0x1D):
|
||||
return {"mode": "abs16", "reg": None, "size": 2 if first == 0x1D else 1, "length": 3, "value": self.memory.rom.u16(pc + 1)}
|
||||
raise UnsupportedInstruction(pc, self.memory.rom.slice(pc, 1), H8536Decoder(self.memory.rom, br=self.cpu.br).decode(pc).text)
|
||||
|
||||
def _general_length(self, pc: int, ea: dict[str, int | str | None], op: int) -> int:
|
||||
length = int(ea["length"]) + 1
|
||||
if op == 0x00:
|
||||
return length + 1
|
||||
if op == 0x06:
|
||||
return length + 1
|
||||
if op in (0x07, 0x05):
|
||||
return length + 2
|
||||
if op == 0x04:
|
||||
return length + 1
|
||||
return length
|
||||
|
||||
def _ea_address(self, ea: dict[str, int | str | None], size: int, *, write: bool = False) -> int:
|
||||
mode = ea["mode"]
|
||||
reg = ea.get("reg")
|
||||
if mode == "abs16":
|
||||
return int(ea["value"]) & 0xFFFF
|
||||
if mode == "indirect":
|
||||
return self.cpu.regs[int(reg)] & 0xFFFF
|
||||
if mode == "disp":
|
||||
return (self.cpu.regs[int(reg)] + int(ea["value"])) & 0xFFFF
|
||||
if mode == "predec":
|
||||
self.cpu.regs[int(reg)] = (self.cpu.regs[int(reg)] - size) & 0xFFFF
|
||||
return self.cpu.regs[int(reg)]
|
||||
if mode == "postinc":
|
||||
address = self.cpu.regs[int(reg)] & 0xFFFF
|
||||
if not write:
|
||||
self.cpu.regs[int(reg)] = (self.cpu.regs[int(reg)] + size) & 0xFFFF
|
||||
return address
|
||||
raise EmulatorError(f"EA mode {mode} does not have an address")
|
||||
|
||||
def _read_ea(self, ea: dict[str, int | str | None], size: int) -> int:
|
||||
mode = ea["mode"]
|
||||
if mode == "imm":
|
||||
return int(ea["value"]) & _mask(size)
|
||||
if mode == "reg":
|
||||
return self._reg_read(int(ea["reg"]), size)
|
||||
address = self._ea_address(ea, size)
|
||||
return self.memory.read16(address) if size == 2 else self.memory.read8(address)
|
||||
|
||||
def _write_ea(self, ea: dict[str, int | str | None], value: int, size: int) -> None:
|
||||
if ea["mode"] == "reg":
|
||||
self._reg_write(int(ea["reg"]), value, size)
|
||||
return
|
||||
address = self._ea_address(ea, size, write=True)
|
||||
if size == 2:
|
||||
self.memory.write16(address, value)
|
||||
else:
|
||||
self.memory.write8(address, value)
|
||||
|
||||
def _reg_read(self, reg: int, size: int) -> int:
|
||||
value = self.cpu.regs[reg] & 0xFFFF
|
||||
return value if size == 2 else value & 0xFF
|
||||
|
||||
def _reg_write(self, reg: int, value: int, size: int) -> None:
|
||||
if size == 2:
|
||||
self.cpu.regs[reg] = value & 0xFFFF
|
||||
else:
|
||||
self.cpu.regs[reg] = (self.cpu.regs[reg] & 0xFF00) | (value & 0xFF)
|
||||
|
||||
def _cmp(self, lhs: int, rhs: int, size: int) -> None:
|
||||
self._set_sub_flags(lhs, rhs, lhs - rhs, size)
|
||||
|
||||
def _set_logic_flags(self, value: int, size: int) -> None:
|
||||
value &= _mask(size)
|
||||
self.cpu.z = value == 0
|
||||
self.cpu.n = bool(value & _sign_bit(size))
|
||||
self.cpu.v = False
|
||||
|
||||
def _set_add_flags(self, lhs: int, rhs: int, result: int, size: int) -> None:
|
||||
mask = _mask(size)
|
||||
sign = _sign_bit(size)
|
||||
result &= mask
|
||||
lhs &= mask
|
||||
rhs &= mask
|
||||
self.cpu.z = result == 0
|
||||
self.cpu.n = bool(result & sign)
|
||||
self.cpu.c = lhs + rhs > mask
|
||||
self.cpu.v = bool((~(lhs ^ rhs) & (lhs ^ result) & sign) != 0)
|
||||
|
||||
def _set_sub_flags(self, lhs: int, rhs: int, result: int, size: int) -> None:
|
||||
mask = _mask(size)
|
||||
sign = _sign_bit(size)
|
||||
result &= mask
|
||||
lhs &= mask
|
||||
rhs &= mask
|
||||
self.cpu.z = result == 0
|
||||
self.cpu.n = bool(result & sign)
|
||||
self.cpu.c = lhs < rhs
|
||||
self.cpu.v = bool(((lhs ^ rhs) & (lhs ^ result) & sign) != 0)
|
||||
|
||||
def _bit_operation(self, ea: dict[str, int | str | None], size: int, op_base: int, bit: int) -> None:
|
||||
value = self._read_ea(ea, size)
|
||||
bit &= (15 if size == 2 else 7)
|
||||
mask = 1 << bit
|
||||
self.cpu.z = not bool(value & mask)
|
||||
if op_base == 0xC0:
|
||||
self._write_ea(ea, value | mask, size)
|
||||
elif op_base == 0xD0:
|
||||
self._write_ea(ea, value & ~mask, size)
|
||||
elif op_base == 0xE0:
|
||||
self._write_ea(ea, value ^ mask, size)
|
||||
|
||||
def _branch_condition(self, cond: int) -> bool:
|
||||
if cond == 0x0:
|
||||
return True
|
||||
if cond == 0x1:
|
||||
return False
|
||||
if cond == 0x2:
|
||||
return not self.cpu.c and not self.cpu.z
|
||||
if cond == 0x3:
|
||||
return self.cpu.c or self.cpu.z
|
||||
if cond == 0x4:
|
||||
return not self.cpu.c
|
||||
if cond == 0x5:
|
||||
return self.cpu.c
|
||||
if cond == 0x6:
|
||||
return not self.cpu.z
|
||||
if cond == 0x7:
|
||||
return self.cpu.z
|
||||
if cond == 0x8:
|
||||
return not self.cpu.v
|
||||
if cond == 0x9:
|
||||
return self.cpu.v
|
||||
if cond == 0xA:
|
||||
return not self.cpu.n
|
||||
if cond == 0xB:
|
||||
return self.cpu.n
|
||||
if cond == 0xC:
|
||||
return self.cpu.n == self.cpu.v
|
||||
if cond == 0xD:
|
||||
return self.cpu.n != self.cpu.v
|
||||
if cond == 0xE:
|
||||
return not self.cpu.z and self.cpu.n == self.cpu.v
|
||||
return self.cpu.z or self.cpu.n != self.cpu.v
|
||||
|
||||
|
||||
def _s8(value: int) -> int:
|
||||
return value - 0x100 if value & 0x80 else value
|
||||
@@ -419,6 +808,14 @@ def _s16(value: int) -> int:
|
||||
return value - 0x10000 if value & 0x8000 else value
|
||||
|
||||
|
||||
def _mask(size: int) -> int:
|
||||
return 0xFFFF if size == 2 else 0xFF
|
||||
|
||||
|
||||
def _sign_bit(size: int) -> int:
|
||||
return 0x8000 if size == 2 else 0x80
|
||||
|
||||
|
||||
def discover_rom_path(root: Path) -> Path | None:
|
||||
candidates = [
|
||||
root / "ROM" / "M27C512@DIP28_1.BIN",
|
||||
@@ -453,6 +850,7 @@ def build_arg_parser() -> argparse.ArgumentParser:
|
||||
parser.add_argument("--trace", action="store_true", help="print decoded/executed instruction trace")
|
||||
parser.add_argument("--stop-on-heartbeat", action="store_true", help="stop only when 00 00 00 00 80 DA is emitted through SCI1 TDR")
|
||||
parser.add_argument("--memory-map", action="store_true", help="print the scaffolded memory map before running")
|
||||
parser.add_argument("--interval-steps", type=int, default=2048, help="rough step period for the scaffolded timer interrupt")
|
||||
return parser
|
||||
|
||||
|
||||
@@ -464,7 +862,7 @@ def main(argv: list[str] | None = None) -> int:
|
||||
print(str(exc))
|
||||
return 2
|
||||
|
||||
emulator = H8536Emulator(rom_bytes)
|
||||
emulator = H8536Emulator(rom_bytes, interval_steps=args.interval_steps)
|
||||
print(f"rom={rom_path}")
|
||||
print(f"reset_vector={h16(emulator.reset_vector())}")
|
||||
if args.memory_map:
|
||||
|
||||
Reference in New Issue
Block a user