1
0
Files
h8-536-decoder/h8536/emulator/runner.py
2026-05-25 20:42:45 +10:00

645 lines
25 KiB
Python

from __future__ import annotations
from dataclasses import dataclass, field
from ..decoder import H8536Decoder
from ..formatting import h16
from ..rom import DecodeError
from ..vectors import read_vectors_min
from .constants import (
FRT_TCR_OCIEA,
FRT_TCSR_OCFA,
FRT1_TCR,
FRT1_TCSR,
FRT2_TCR,
FRT2_TCSR,
IPRA,
IPRC,
IPRE,
SCI_SCR_TIE,
SCI_SSR_TDRE,
VECTOR_FRT1_OCIA,
VECTOR_FRT2_OCIA,
VECTOR_INTERVAL_TIMER,
VECTOR_SCI1_TXI,
)
from .cpu import CPUState, mask, s8, s16, sign_bit
from .errors import EmulatorError, UnsupportedInstruction
from .fast_paths import P9FastPath, P9FastPathConfig
from .memory import MemoryMap
from .sci import SCI1
@dataclass
class RunReport:
steps: int
cycles: int
pc: int
stopped_reason: str
tx_bytes: bytes
tx_frames: list[bytes]
heartbeat_seen: bool
unsupported: str | None = None
trace: list[str] = field(default_factory=list)
def summary_lines(self) -> list[str]:
lines = [
f"steps={self.steps}",
f"cycles={self.cycles}",
f"pc={h16(self.pc)}",
f"stopped={self.stopped_reason}",
"tx_bytes=" + self.tx_bytes.hex(" ").upper(),
"tx_frames=" + ", ".join(frame.hex(" ").upper() for frame in self.tx_frames),
f"heartbeat_seen={self.heartbeat_seen}",
]
if self.unsupported:
lines.append(f"unsupported={self.unsupported}")
lines.append(
"next_todo=implement the stopped opcode, then add interrupt scheduling for SCI1 TXI and interval/watchdog timer overflow"
)
return lines
class H8536Emulator:
def __init__(
self,
rom_bytes: bytes,
*,
interval_steps: int = 2048,
frt1_ocia_steps: int = 1024,
frt2_ocia_steps: int = 1024,
p9_fast_path: P9FastPath | None = None,
p9_fast_path_enabled: bool = False,
p9_fast_default_input_byte: int = 0xFF,
) -> None:
if not rom_bytes:
raise ValueError("ROM image is empty")
self.sci1 = SCI1()
self.memory = MemoryMap(rom_bytes, self.sci1)
self.p9_fast_path = p9_fast_path or P9FastPath(
P9FastPathConfig(enabled=p9_fast_path_enabled, default_input_byte=p9_fast_default_input_byte)
)
self.cpu = CPUState()
self.vectors = read_vectors_min(self.memory.rom)
self.interval_steps = max(1, interval_steps)
self.frt1_ocia_steps = max(1, frt1_ocia_steps)
self.frt2_ocia_steps = max(1, frt2_ocia_steps)
self._interval_counter = 0
self._frt1_ocia_counter = 0
self._frt2_ocia_counter = 0
self.reset()
def reset(self) -> None:
self.cpu = CPUState(pc=self.reset_vector())
def reset_vector(self) -> int:
if self.memory.rom.contains(0, 2):
return self.memory.rom.u16(0)
raise DecodeError("ROM does not contain a reset vector at H'0000")
def step(self) -> str:
pc = self.cpu.pc
if self.p9_fast_path.try_handle(self):
self._tick_peripherals()
return f"{h16(pc)}: {'<p9-fast-path>':<17} P9 fast-path"
decoder = H8536Decoder(self.memory.rom, br=self.cpu.br)
ins = decoder.decode(pc)
if not ins.valid:
raise UnsupportedInstruction(pc, ins.raw, ins.text)
next_pc = (pc + ins.size) & 0xFFFF
raw = ins.raw
text = ins.text
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)
self._set_logic_flags(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")
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(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:
next_pc = self._branch16(raw, pc, next_pc)
else:
raise UnsupportedInstruction(pc, raw, text)
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:
trace_lines: list[str] = []
stopped_reason = "max_steps"
unsupported: str | None = None
for _ in range(max_steps):
try:
line = self.step()
except UnsupportedInstruction as exc:
stopped_reason = "unsupported_instruction"
unsupported = str(exc)
break
if trace:
trace_lines.append(line)
if stop_on_heartbeat and self.sci1.saw_heartbeat():
stopped_reason = "heartbeat"
break
return RunReport(
steps=self.cpu.steps,
cycles=self.cpu.cycles,
pc=self.cpu.pc,
stopped_reason=stopped_reason,
tx_bytes=bytes(self.sci1.tx_bytes),
tx_frames=list(self.sci1.tx_frames),
heartbeat_seen=self.sci1.saw_heartbeat(),
unsupported=unsupported,
trace=trace_lines,
)
def _execute_general(self, pc: int, next_pc: int) -> int:
ea = self._decode_ea(pc)
op = self.memory.rom.u8(pc + int(ea["length"]))
size = int(ea["size"])
raw = self.memory.rom.slice(pc, self._general_length(pc, ea, op))
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:
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 base == 0xA8:
if size != 1:
raise UnsupportedInstruction(pc, raw, H8536Decoder(self.memory.rom, br=self.cpu.br).decode(pc).text)
result = (self._reg_read(rd, 1) * self._read_ea(ea, 1)) & 0xFFFF
self.cpu.regs[rd] = result
self._set_logic_flags(result, 2)
self.cpu.c = False
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 == 0x15:
result = (~self._read_ea(ea, size)) & mask(size)
self._write_ea(ea, result, size)
self._set_logic_flags(result, size)
elif op == 0x16:
self._set_logic_flags(self._read_ea(ea, size), size)
elif op in (0x10, 0x11, 0x12) and ea["mode"] == "reg" and size == 1:
reg = int(ea["reg"])
value = self.cpu.regs[reg] & 0xFFFF
if op == 0x10:
result = ((value & 0x00FF) << 8) | ((value >> 8) & 0x00FF)
elif op == 0x11:
result = s8(value & 0xFF) & 0xFFFF
else:
result = value & 0x00FF
self.cpu.regs[reg] = result
self._set_logic_flags(result, 2)
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._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):
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
def _branch8(self, raw: bytes, pc: int, next_pc: int) -> int:
cond = raw[0] & 0x0F
disp = s8(raw[1])
target = (pc + 2 + disp) & 0xFFFF
if self._branch_condition(cond):
return target
return next_pc
def _branch16(self, raw: bytes, pc: int, next_pc: int) -> int:
cond = raw[0] & 0x0F
disp = s16(int.from_bytes(raw[1:3], "big"))
target = (pc + 3 + disp) & 0xFFFF
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._frt1_ocia_counter += 1
self._frt2_ocia_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 self._frt1_ocia_pending():
target = self._vector_target(VECTOR_FRT1_OCIA)
if target is not None:
candidates.append((self._frt1_priority(), target, "frt1_ocia"))
if self._frt2_ocia_pending():
target = self._vector_target(VECTOR_FRT2_OCIA)
if target is not None:
candidates.append((self._frt2_priority(), target, "frt2_ocia"))
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
elif source == "frt1_ocia":
self._frt1_ocia_counter = 0
self.memory.write8(FRT1_TCSR, self.memory.read8(FRT1_TCSR) | FRT_TCSR_OCFA)
elif source == "frt2_ocia":
self._frt2_ocia_counter = 0
self.memory.write8(FRT2_TCSR, self.memory.read8(FRT2_TCSR) | FRT_TCSR_OCFA)
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 _frt1_ocia_pending(self) -> bool:
if self._frt1_ocia_counter < self.frt1_ocia_steps:
return False
return bool(self.memory.read8(FRT1_TCR) & FRT_TCR_OCIEA)
def _frt1_priority(self) -> int:
return (self.memory.read8(IPRC) >> 4) & 0x07
def _frt2_ocia_pending(self) -> bool:
if self._frt2_ocia_counter < self.frt2_ocia_steps:
return False
return bool(self.memory.read8(FRT2_TCR) & FRT_TCR_OCIEA)
def _frt2_priority(self) -> int:
# H8/536 IPRC assigns bits 6..4 to FRT1 and bits 2..0 to FRT2;
# the ROM's IPRC=H'66 therefore gives both timers priority 6.
return self.memory.read8(IPRC) & 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_value: int) -> None:
for reg in range(8):
if mask_value & (1 << reg):
self._push16(self.cpu.regs[reg])
def _pop_register_mask(self, mask_value: int) -> None:
for reg in reversed(range(8)):
if mask_value & (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:
max_value = mask(size)
sign = sign_bit(size)
result &= max_value
lhs &= max_value
rhs &= max_value
self.cpu.z = result == 0
self.cpu.n = bool(result & sign)
self.cpu.c = lhs + rhs > max_value
self.cpu.v = bool((~(lhs ^ rhs) & (lhs ^ result) & sign) != 0)
def _set_sub_flags(self, lhs: int, rhs: int, result: int, size: int) -> None:
max_value = mask(size)
sign = sign_bit(size)
result &= max_value
lhs &= max_value
rhs &= max_value
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
bit_mask = 1 << bit
self.cpu.z = not bool(value & bit_mask)
if op_base == 0xC0:
self._write_ea(ea, value | bit_mask, size)
elif op_base == 0xD0:
self._write_ea(ea, value & ~bit_mask, size)
elif op_base == 0xE0:
self._write_ea(ea, value ^ bit_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