560 lines
19 KiB
Python
560 lines
19 KiB
Python
#!/usr/bin/env python3
|
|
# Minimal 6502 CPU emulator -- just enough to run the Space Taxi
|
|
# level decompressor at $6283 against a captured 64KB RAM dump.
|
|
#
|
|
# Scope: implements the opcodes used by $6283 / $9656 / $964A / $96A3
|
|
# and their helpers. Not a general-purpose 6502 emulator; it has no
|
|
# decimal mode, no BRK handling, no NMI/IRQ -- the routine runs with
|
|
# SEI in effect and never returns control.
|
|
#
|
|
# Flags tracked: N Z C. V (overflow) is computed for ADC/SBC since
|
|
# those instructions set it, even if no branch tests it. D (decimal)
|
|
# is ignored.
|
|
|
|
class Cpu6502:
|
|
def __init__(self, ram):
|
|
# ram is a bytearray of 65536 bytes. Caller pre-loaded the dump.
|
|
self.ram = ram
|
|
self.a = 0
|
|
self.x = 0
|
|
self.y = 0
|
|
self.sp = 0xFF
|
|
self.pc = 0
|
|
self.n = self.z = self.c = self.v = False
|
|
# Stop tracking when RTS unwinds to this depth (set by run()).
|
|
self._depth_floor = 0
|
|
|
|
# ---- memory ----
|
|
def read8(self, addr):
|
|
return self.ram[addr & 0xFFFF]
|
|
|
|
def write8(self, addr, val):
|
|
self.ram[addr & 0xFFFF] = val & 0xFF
|
|
|
|
def read16(self, addr):
|
|
return self.ram[addr & 0xFFFF] | (self.ram[(addr + 1) & 0xFFFF] << 8)
|
|
|
|
# ---- stack ----
|
|
def push8(self, val):
|
|
self.ram[0x100 + self.sp] = val & 0xFF
|
|
self.sp = (self.sp - 1) & 0xFF
|
|
|
|
def pop8(self):
|
|
self.sp = (self.sp + 1) & 0xFF
|
|
return self.ram[0x100 + self.sp]
|
|
|
|
def push16(self, val):
|
|
self.push8((val >> 8) & 0xFF)
|
|
self.push8(val & 0xFF)
|
|
|
|
def pop16(self):
|
|
lo = self.pop8()
|
|
hi = self.pop8()
|
|
return (hi << 8) | lo
|
|
|
|
# ---- flag helpers ----
|
|
def set_nz(self, val):
|
|
val &= 0xFF
|
|
self.z = (val == 0)
|
|
self.n = (val & 0x80) != 0
|
|
return val
|
|
|
|
def pack_p(self):
|
|
# B and U are usually set when pushed by PHP/BRK.
|
|
p = 0x20
|
|
if self.n: p |= 0x80
|
|
if self.v: p |= 0x40
|
|
if self.z: p |= 0x02
|
|
if self.c: p |= 0x01
|
|
return p | 0x10 # B = 1 when pushed by PHP
|
|
|
|
def unpack_p(self, p):
|
|
self.n = (p & 0x80) != 0
|
|
self.v = (p & 0x40) != 0
|
|
self.z = (p & 0x02) != 0
|
|
self.c = (p & 0x01) != 0
|
|
|
|
# ---- addressing helpers (return effective address) ----
|
|
def addr_imm(self):
|
|
a = self.pc; self.pc = (self.pc + 1) & 0xFFFF; return a
|
|
|
|
def addr_zp(self):
|
|
a = self.read8(self.pc); self.pc = (self.pc + 1) & 0xFFFF; return a
|
|
|
|
def addr_zpx(self):
|
|
a = (self.read8(self.pc) + self.x) & 0xFF; self.pc = (self.pc + 1) & 0xFFFF; return a
|
|
|
|
def addr_abs(self):
|
|
a = self.read16(self.pc); self.pc = (self.pc + 2) & 0xFFFF; return a
|
|
|
|
def addr_absx(self):
|
|
a = (self.read16(self.pc) + self.x) & 0xFFFF; self.pc = (self.pc + 2) & 0xFFFF; return a
|
|
|
|
def addr_absy(self):
|
|
a = (self.read16(self.pc) + self.y) & 0xFFFF; self.pc = (self.pc + 2) & 0xFFFF; return a
|
|
|
|
def addr_indy(self):
|
|
zp = self.read8(self.pc); self.pc = (self.pc + 1) & 0xFFFF
|
|
base = self.ram[zp] | (self.ram[(zp + 1) & 0xFF] << 8)
|
|
return (base + self.y) & 0xFFFF
|
|
|
|
# ---- branch helper ----
|
|
def branch(self, cond):
|
|
off = self.read8(self.pc); self.pc = (self.pc + 1) & 0xFFFF
|
|
if off >= 0x80: off -= 0x100
|
|
if cond:
|
|
self.pc = (self.pc + off) & 0xFFFF
|
|
|
|
# ---- step ----
|
|
def step(self):
|
|
op = self.read8(self.pc)
|
|
self.pc = (self.pc + 1) & 0xFFFF
|
|
f = _DISPATCH.get(op)
|
|
if f is None:
|
|
raise NotImplementedError(f"opcode ${op:02X} at PC ${(self.pc-1)&0xFFFF:04X}")
|
|
f(self)
|
|
|
|
def call(self, target, max_steps=10_000_000):
|
|
"""Call subroutine at `target` as if via JSR. Returns when the
|
|
matching RTS pops the stack back below the original SP. Hard
|
|
cap on steps to avoid runaway."""
|
|
# Push a sentinel return address. When RTS pops to this, stop.
|
|
# Use $FFFF as sentinel: subsequent step would attempt to fetch
|
|
# from $FFFF; we detect via depth floor instead.
|
|
original_sp = self.sp
|
|
self.push16(0xFFFE) # RTS will pop this, then step will land at $FFFF
|
|
self.pc = target & 0xFFFF
|
|
for _ in range(max_steps):
|
|
if self.pc == 0xFFFF:
|
|
return
|
|
self.step()
|
|
raise RuntimeError(f"6502 call exceeded {max_steps} steps; PC=${self.pc:04X}")
|
|
|
|
|
|
# ---- opcode implementations ----
|
|
# Each takes the cpu and reads remaining operand bytes via cpu.addr_*.
|
|
|
|
def _NOP(cpu): pass
|
|
def _SEI(cpu): pass # we don't model interrupts
|
|
def _CLD(cpu): pass # no decimal mode
|
|
def _SEC(cpu): cpu.c = True
|
|
def _CLC(cpu): cpu.c = False
|
|
def _PHP(cpu): cpu.push8(cpu.pack_p())
|
|
def _PLP(cpu): cpu.unpack_p(cpu.pop8())
|
|
def _PHA(cpu): cpu.push8(cpu.a)
|
|
def _PLA(cpu): cpu.a = cpu.set_nz(cpu.pop8())
|
|
def _TXA(cpu): cpu.a = cpu.set_nz(cpu.x)
|
|
def _TAX(cpu): cpu.x = cpu.set_nz(cpu.a)
|
|
def _TYA(cpu): cpu.a = cpu.set_nz(cpu.y)
|
|
def _TAY(cpu): cpu.y = cpu.set_nz(cpu.a)
|
|
def _INX(cpu): cpu.x = cpu.set_nz((cpu.x + 1) & 0xFF)
|
|
def _INY(cpu): cpu.y = cpu.set_nz((cpu.y + 1) & 0xFF)
|
|
def _DEX(cpu): cpu.x = cpu.set_nz((cpu.x - 1) & 0xFF)
|
|
def _DEY(cpu): cpu.y = cpu.set_nz((cpu.y - 1) & 0xFF)
|
|
|
|
def _LDA_imm(cpu): cpu.a = cpu.set_nz(cpu.read8(cpu.addr_imm()))
|
|
def _LDA_zp(cpu): cpu.a = cpu.set_nz(cpu.read8(cpu.addr_zp()))
|
|
def _LDA_zpx(cpu): cpu.a = cpu.set_nz(cpu.read8(cpu.addr_zpx()))
|
|
def _LDA_abs(cpu): cpu.a = cpu.set_nz(cpu.read8(cpu.addr_abs()))
|
|
def _LDA_absx(cpu): cpu.a = cpu.set_nz(cpu.read8(cpu.addr_absx()))
|
|
def _LDA_absy(cpu): cpu.a = cpu.set_nz(cpu.read8(cpu.addr_absy()))
|
|
def _LDA_indy(cpu): cpu.a = cpu.set_nz(cpu.read8(cpu.addr_indy()))
|
|
|
|
def _LDX_imm(cpu): cpu.x = cpu.set_nz(cpu.read8(cpu.addr_imm()))
|
|
def _LDX_abs(cpu): cpu.x = cpu.set_nz(cpu.read8(cpu.addr_abs()))
|
|
def _LDX_absy(cpu): cpu.x = cpu.set_nz(cpu.read8(cpu.addr_absy()))
|
|
def _LDX_zp(cpu): cpu.x = cpu.set_nz(cpu.read8(cpu.addr_zp()))
|
|
|
|
def _LDY_imm(cpu): cpu.y = cpu.set_nz(cpu.read8(cpu.addr_imm()))
|
|
def _LDY_abs(cpu): cpu.y = cpu.set_nz(cpu.read8(cpu.addr_abs()))
|
|
def _LDY_absx(cpu): cpu.y = cpu.set_nz(cpu.read8(cpu.addr_absx()))
|
|
def _LDY_zp(cpu): cpu.y = cpu.set_nz(cpu.read8(cpu.addr_zp()))
|
|
|
|
def _STA_zp(cpu): cpu.write8(cpu.addr_zp(), cpu.a)
|
|
def _STA_zpx(cpu): cpu.write8(cpu.addr_zpx(), cpu.a)
|
|
def _STA_abs(cpu): cpu.write8(cpu.addr_abs(), cpu.a)
|
|
def _STA_absx(cpu): cpu.write8(cpu.addr_absx(), cpu.a)
|
|
def _STA_absy(cpu): cpu.write8(cpu.addr_absy(), cpu.a)
|
|
def _STA_indy(cpu): cpu.write8(cpu.addr_indy(), cpu.a)
|
|
|
|
def _STX_zp(cpu): cpu.write8(cpu.addr_zp(), cpu.x)
|
|
def _STX_abs(cpu): cpu.write8(cpu.addr_abs(), cpu.x)
|
|
|
|
def _STY_zp(cpu): cpu.write8(cpu.addr_zp(), cpu.y)
|
|
def _STY_abs(cpu): cpu.write8(cpu.addr_abs(), cpu.y)
|
|
|
|
def _AND_imm(cpu): cpu.a = cpu.set_nz(cpu.a & cpu.read8(cpu.addr_imm()))
|
|
def _AND_zp(cpu): cpu.a = cpu.set_nz(cpu.a & cpu.read8(cpu.addr_zp()))
|
|
def _AND_abs(cpu): cpu.a = cpu.set_nz(cpu.a & cpu.read8(cpu.addr_abs()))
|
|
def _ORA_imm(cpu): cpu.a = cpu.set_nz(cpu.a | cpu.read8(cpu.addr_imm()))
|
|
def _ORA_zp(cpu): cpu.a = cpu.set_nz(cpu.a | cpu.read8(cpu.addr_zp()))
|
|
def _EOR_imm(cpu): cpu.a = cpu.set_nz(cpu.a ^ cpu.read8(cpu.addr_imm()))
|
|
def _EOR_abs(cpu): cpu.a = cpu.set_nz(cpu.a ^ cpu.read8(cpu.addr_abs()))
|
|
|
|
def _adc(cpu, m):
|
|
r = cpu.a + m + (1 if cpu.c else 0)
|
|
cpu.c = r > 0xFF
|
|
cpu.v = ((cpu.a ^ r) & (m ^ r) & 0x80) != 0
|
|
cpu.a = cpu.set_nz(r & 0xFF)
|
|
|
|
def _ADC_imm(cpu): _adc(cpu, cpu.read8(cpu.addr_imm()))
|
|
def _ADC_zp(cpu): _adc(cpu, cpu.read8(cpu.addr_zp()))
|
|
def _ADC_abs(cpu): _adc(cpu, cpu.read8(cpu.addr_abs()))
|
|
def _ADC_absy(cpu): _adc(cpu, cpu.read8(cpu.addr_absy()))
|
|
|
|
def _sbc(cpu, m):
|
|
_adc(cpu, m ^ 0xFF)
|
|
|
|
def _SBC_imm(cpu): _sbc(cpu, cpu.read8(cpu.addr_imm()))
|
|
def _SBC_zp(cpu): _sbc(cpu, cpu.read8(cpu.addr_zp()))
|
|
def _SBC_abs(cpu): _sbc(cpu, cpu.read8(cpu.addr_abs()))
|
|
def _SBC_absx(cpu): _sbc(cpu, cpu.read8(cpu.addr_absx()))
|
|
def _SBC_absy(cpu): _sbc(cpu, cpu.read8(cpu.addr_absy()))
|
|
|
|
def _AND_absx(cpu): cpu.a = cpu.set_nz(cpu.a & cpu.read8(cpu.addr_absx()))
|
|
def _AND_absy(cpu): cpu.a = cpu.set_nz(cpu.a & cpu.read8(cpu.addr_absy()))
|
|
def _AND_indy(cpu): cpu.a = cpu.set_nz(cpu.a & cpu.read8(cpu.addr_indy()))
|
|
def _ORA_absx(cpu): cpu.a = cpu.set_nz(cpu.a | cpu.read8(cpu.addr_absx()))
|
|
def _ORA_abs(cpu): cpu.a = cpu.set_nz(cpu.a | cpu.read8(cpu.addr_abs()))
|
|
def _ORA_zpx(cpu): cpu.a = cpu.set_nz(cpu.a | cpu.read8(cpu.addr_zpx()))
|
|
def _ORA_indy(cpu): cpu.a = cpu.set_nz(cpu.a | cpu.read8(cpu.addr_indy()))
|
|
def _EOR_zp(cpu): cpu.a = cpu.set_nz(cpu.a ^ cpu.read8(cpu.addr_zp()))
|
|
def _EOR_zpx(cpu): cpu.a = cpu.set_nz(cpu.a ^ cpu.read8(cpu.addr_zpx()))
|
|
def _EOR_absx(cpu): cpu.a = cpu.set_nz(cpu.a ^ cpu.read8(cpu.addr_absx()))
|
|
def _EOR_absy(cpu): cpu.a = cpu.set_nz(cpu.a ^ cpu.read8(cpu.addr_absy()))
|
|
def _ADC_zpx(cpu): _adc(cpu, cpu.read8(cpu.addr_zpx()))
|
|
def _ADC_absx(cpu): _adc(cpu, cpu.read8(cpu.addr_absx()))
|
|
def _CMP_absx(cpu): _cmp_with(cpu, cpu.a, cpu.read8(cpu.addr_absx()))
|
|
def _CMP_absy(cpu): _cmp_with(cpu, cpu.a, cpu.read8(cpu.addr_absy()))
|
|
def _CMP_zpx(cpu): _cmp_with(cpu, cpu.a, cpu.read8(cpu.addr_zpx()))
|
|
|
|
def _JMP_ind(cpu):
|
|
addr = cpu.addr_abs()
|
|
cpu.pc = cpu.read8(addr) | (cpu.read8((addr & 0xFF00) | ((addr + 1) & 0xFF)) << 8)
|
|
|
|
def _BIT_zp(cpu):
|
|
v = cpu.read8(cpu.addr_zp())
|
|
cpu.z = (cpu.a & v) == 0
|
|
cpu.n = (v & 0x80) != 0
|
|
cpu.v = (v & 0x40) != 0
|
|
|
|
def _BIT_abs(cpu):
|
|
v = cpu.read8(cpu.addr_abs())
|
|
cpu.z = (cpu.a & v) == 0
|
|
cpu.n = (v & 0x80) != 0
|
|
cpu.v = (v & 0x40) != 0
|
|
|
|
def _ASL_zp(cpu):
|
|
addr = cpu.addr_zp()
|
|
v = cpu.read8(addr)
|
|
cpu.c = (v & 0x80) != 0
|
|
cpu.write8(addr, cpu.set_nz((v << 1) & 0xFF))
|
|
|
|
def _ASL_abs(cpu):
|
|
addr = cpu.addr_abs()
|
|
v = cpu.read8(addr)
|
|
cpu.c = (v & 0x80) != 0
|
|
cpu.write8(addr, cpu.set_nz((v << 1) & 0xFF))
|
|
|
|
def _LSR_abs(cpu):
|
|
addr = cpu.addr_abs()
|
|
v = cpu.read8(addr)
|
|
cpu.c = (v & 0x01) != 0
|
|
cpu.write8(addr, cpu.set_nz(v >> 1))
|
|
|
|
def _ROL_abs(cpu):
|
|
addr = cpu.addr_abs()
|
|
v = cpu.read8(addr)
|
|
new_c = (v & 0x80) != 0
|
|
v = ((v << 1) | (1 if cpu.c else 0)) & 0xFF
|
|
cpu.write8(addr, cpu.set_nz(v))
|
|
cpu.c = new_c
|
|
|
|
def _ROR_abs(cpu):
|
|
addr = cpu.addr_abs()
|
|
v = cpu.read8(addr)
|
|
new_c = (v & 0x01) != 0
|
|
v = ((v >> 1) | (0x80 if cpu.c else 0)) & 0xFF
|
|
cpu.write8(addr, cpu.set_nz(v))
|
|
cpu.c = new_c
|
|
|
|
def _ROR_zp(cpu):
|
|
addr = cpu.addr_zp()
|
|
v = cpu.read8(addr)
|
|
new_c = (v & 0x01) != 0
|
|
v = ((v >> 1) | (0x80 if cpu.c else 0)) & 0xFF
|
|
cpu.write8(addr, cpu.set_nz(v))
|
|
cpu.c = new_c
|
|
|
|
def _STA_zpx(cpu_): cpu_.write8(cpu_.addr_zpx(), cpu_.a)
|
|
def _TSX(cpu): cpu.x = cpu.set_nz(cpu.sp)
|
|
def _TXS(cpu): cpu.sp = cpu.x
|
|
def _CLI(cpu): pass
|
|
def _CLV(cpu): cpu.v = False
|
|
def _SED(cpu): pass # decimal mode ignored
|
|
def _STX_zpy(cpu):
|
|
a = (cpu.read8(cpu.pc) + cpu.y) & 0xFF; cpu.pc = (cpu.pc + 1) & 0xFFFF
|
|
cpu.write8(a, cpu.x)
|
|
def _STY_zpx(cpu): cpu.write8(cpu.addr_zpx(), cpu.y)
|
|
def _LDX_zpy(cpu):
|
|
a = (cpu.read8(cpu.pc) + cpu.y) & 0xFF; cpu.pc = (cpu.pc + 1) & 0xFFFF
|
|
cpu.x = cpu.set_nz(cpu.read8(a))
|
|
def _LDY_zpx(cpu): cpu.y = cpu.set_nz(cpu.read8(cpu.addr_zpx()))
|
|
|
|
def _cmp_with(cpu, lhs, m):
|
|
r = (lhs - m) & 0x1FF
|
|
cpu.c = lhs >= m
|
|
cpu.set_nz(r & 0xFF)
|
|
|
|
def _CMP_imm(cpu): _cmp_with(cpu, cpu.a, cpu.read8(cpu.addr_imm()))
|
|
def _CMP_zp(cpu): _cmp_with(cpu, cpu.a, cpu.read8(cpu.addr_zp()))
|
|
def _CMP_abs(cpu): _cmp_with(cpu, cpu.a, cpu.read8(cpu.addr_abs()))
|
|
def _CPX_imm(cpu): _cmp_with(cpu, cpu.x, cpu.read8(cpu.addr_imm()))
|
|
def _CPX_abs(cpu): _cmp_with(cpu, cpu.x, cpu.read8(cpu.addr_abs()))
|
|
def _CPY_imm(cpu): _cmp_with(cpu, cpu.y, cpu.read8(cpu.addr_imm()))
|
|
|
|
def _ASL_a(cpu):
|
|
cpu.c = (cpu.a & 0x80) != 0
|
|
cpu.a = cpu.set_nz((cpu.a << 1) & 0xFF)
|
|
|
|
def _LSR_a(cpu):
|
|
cpu.c = (cpu.a & 0x01) != 0
|
|
cpu.a = cpu.set_nz(cpu.a >> 1)
|
|
|
|
def _LSR_zp(cpu):
|
|
addr = cpu.addr_zp()
|
|
v = cpu.read8(addr)
|
|
cpu.c = (v & 0x01) != 0
|
|
cpu.write8(addr, cpu.set_nz(v >> 1))
|
|
|
|
def _ROL_a(cpu):
|
|
new_c = (cpu.a & 0x80) != 0
|
|
cpu.a = cpu.set_nz(((cpu.a << 1) | (1 if cpu.c else 0)) & 0xFF)
|
|
cpu.c = new_c
|
|
|
|
def _ROL_zp(cpu):
|
|
addr = cpu.addr_zp()
|
|
v = cpu.read8(addr)
|
|
new_c = (v & 0x80) != 0
|
|
v = ((v << 1) | (1 if cpu.c else 0)) & 0xFF
|
|
cpu.write8(addr, cpu.set_nz(v))
|
|
cpu.c = new_c
|
|
|
|
def _ROR_a(cpu):
|
|
new_c = (cpu.a & 0x01) != 0
|
|
cpu.a = cpu.set_nz(((cpu.a >> 1) | (0x80 if cpu.c else 0)) & 0xFF)
|
|
cpu.c = new_c
|
|
|
|
def _INC_abs(cpu):
|
|
addr = cpu.addr_abs()
|
|
cpu.write8(addr, cpu.set_nz((cpu.read8(addr) + 1) & 0xFF))
|
|
|
|
def _INC_absx(cpu):
|
|
addr = cpu.addr_absx()
|
|
cpu.write8(addr, cpu.set_nz((cpu.read8(addr) + 1) & 0xFF))
|
|
|
|
def _INC_zp(cpu):
|
|
addr = cpu.addr_zp()
|
|
cpu.write8(addr, cpu.set_nz((cpu.read8(addr) + 1) & 0xFF))
|
|
|
|
def _INC_zpx(cpu):
|
|
addr = cpu.addr_zpx()
|
|
cpu.write8(addr, cpu.set_nz((cpu.read8(addr) + 1) & 0xFF))
|
|
|
|
def _DEC_abs(cpu):
|
|
addr = cpu.addr_abs()
|
|
cpu.write8(addr, cpu.set_nz((cpu.read8(addr) - 1) & 0xFF))
|
|
|
|
def _DEC_absx(cpu):
|
|
addr = cpu.addr_absx()
|
|
cpu.write8(addr, cpu.set_nz((cpu.read8(addr) - 1) & 0xFF))
|
|
|
|
def _DEC_zp(cpu):
|
|
addr = cpu.addr_zp()
|
|
cpu.write8(addr, cpu.set_nz((cpu.read8(addr) - 1) & 0xFF))
|
|
|
|
def _DEC_zpx(cpu):
|
|
addr = cpu.addr_zpx()
|
|
cpu.write8(addr, cpu.set_nz((cpu.read8(addr) - 1) & 0xFF))
|
|
|
|
def _ASL_zpx(cpu):
|
|
addr = cpu.addr_zpx()
|
|
v = cpu.read8(addr)
|
|
cpu.c = (v & 0x80) != 0
|
|
cpu.write8(addr, cpu.set_nz((v << 1) & 0xFF))
|
|
|
|
def _ASL_absx(cpu):
|
|
addr = cpu.addr_absx()
|
|
v = cpu.read8(addr)
|
|
cpu.c = (v & 0x80) != 0
|
|
cpu.write8(addr, cpu.set_nz((v << 1) & 0xFF))
|
|
|
|
def _LSR_zpx(cpu):
|
|
addr = cpu.addr_zpx()
|
|
v = cpu.read8(addr)
|
|
cpu.c = (v & 0x01) != 0
|
|
cpu.write8(addr, cpu.set_nz(v >> 1))
|
|
|
|
def _LSR_absx(cpu):
|
|
addr = cpu.addr_absx()
|
|
v = cpu.read8(addr)
|
|
cpu.c = (v & 0x01) != 0
|
|
cpu.write8(addr, cpu.set_nz(v >> 1))
|
|
|
|
def _ROL_zpx(cpu):
|
|
addr = cpu.addr_zpx()
|
|
v = cpu.read8(addr)
|
|
new_c = (v & 0x80) != 0
|
|
v = ((v << 1) | (1 if cpu.c else 0)) & 0xFF
|
|
cpu.write8(addr, cpu.set_nz(v))
|
|
cpu.c = new_c
|
|
|
|
def _ROL_absx(cpu):
|
|
addr = cpu.addr_absx()
|
|
v = cpu.read8(addr)
|
|
new_c = (v & 0x80) != 0
|
|
v = ((v << 1) | (1 if cpu.c else 0)) & 0xFF
|
|
cpu.write8(addr, cpu.set_nz(v))
|
|
cpu.c = new_c
|
|
|
|
def _ROR_zpx(cpu):
|
|
addr = cpu.addr_zpx()
|
|
v = cpu.read8(addr)
|
|
new_c = (v & 0x01) != 0
|
|
v = ((v >> 1) | (0x80 if cpu.c else 0)) & 0xFF
|
|
cpu.write8(addr, cpu.set_nz(v))
|
|
cpu.c = new_c
|
|
|
|
def _ROR_absx(cpu):
|
|
addr = cpu.addr_absx()
|
|
v = cpu.read8(addr)
|
|
new_c = (v & 0x01) != 0
|
|
v = ((v >> 1) | (0x80 if cpu.c else 0)) & 0xFF
|
|
cpu.write8(addr, cpu.set_nz(v))
|
|
cpu.c = new_c
|
|
|
|
def _LDA_indx(cpu):
|
|
zp = cpu.read8(cpu.pc); cpu.pc = (cpu.pc + 1) & 0xFFFF
|
|
addr_ptr = (zp + cpu.x) & 0xFF
|
|
addr = cpu.ram[addr_ptr] | (cpu.ram[(addr_ptr + 1) & 0xFF] << 8)
|
|
cpu.a = cpu.set_nz(cpu.read8(addr))
|
|
|
|
def _STA_indx(cpu):
|
|
zp = cpu.read8(cpu.pc); cpu.pc = (cpu.pc + 1) & 0xFFFF
|
|
addr_ptr = (zp + cpu.x) & 0xFF
|
|
addr = cpu.ram[addr_ptr] | (cpu.ram[(addr_ptr + 1) & 0xFF] << 8)
|
|
cpu.write8(addr, cpu.a)
|
|
|
|
def _AND_indx(cpu):
|
|
zp = cpu.read8(cpu.pc); cpu.pc = (cpu.pc + 1) & 0xFFFF
|
|
addr_ptr = (zp + cpu.x) & 0xFF
|
|
addr = cpu.ram[addr_ptr] | (cpu.ram[(addr_ptr + 1) & 0xFF] << 8)
|
|
cpu.a = cpu.set_nz(cpu.a & cpu.read8(addr))
|
|
|
|
def _ORA_indx(cpu):
|
|
zp = cpu.read8(cpu.pc); cpu.pc = (cpu.pc + 1) & 0xFFFF
|
|
addr_ptr = (zp + cpu.x) & 0xFF
|
|
addr = cpu.ram[addr_ptr] | (cpu.ram[(addr_ptr + 1) & 0xFF] << 8)
|
|
cpu.a = cpu.set_nz(cpu.a | cpu.read8(addr))
|
|
|
|
def _EOR_indx(cpu):
|
|
zp = cpu.read8(cpu.pc); cpu.pc = (cpu.pc + 1) & 0xFFFF
|
|
addr_ptr = (zp + cpu.x) & 0xFF
|
|
addr = cpu.ram[addr_ptr] | (cpu.ram[(addr_ptr + 1) & 0xFF] << 8)
|
|
cpu.a = cpu.set_nz(cpu.a ^ cpu.read8(addr))
|
|
|
|
def _ADC_indx(cpu):
|
|
zp = cpu.read8(cpu.pc); cpu.pc = (cpu.pc + 1) & 0xFFFF
|
|
addr_ptr = (zp + cpu.x) & 0xFF
|
|
addr = cpu.ram[addr_ptr] | (cpu.ram[(addr_ptr + 1) & 0xFF] << 8)
|
|
_adc(cpu, cpu.read8(addr))
|
|
|
|
def _SBC_indx(cpu):
|
|
zp = cpu.read8(cpu.pc); cpu.pc = (cpu.pc + 1) & 0xFFFF
|
|
addr_ptr = (zp + cpu.x) & 0xFF
|
|
addr = cpu.ram[addr_ptr] | (cpu.ram[(addr_ptr + 1) & 0xFF] << 8)
|
|
_sbc(cpu, cpu.read8(addr))
|
|
|
|
def _CMP_indx(cpu):
|
|
zp = cpu.read8(cpu.pc); cpu.pc = (cpu.pc + 1) & 0xFFFF
|
|
addr_ptr = (zp + cpu.x) & 0xFF
|
|
addr = cpu.ram[addr_ptr] | (cpu.ram[(addr_ptr + 1) & 0xFF] << 8)
|
|
_cmp_with(cpu, cpu.a, cpu.read8(addr))
|
|
|
|
def _CPX_zp(cpu):
|
|
_cmp_with(cpu, cpu.x, cpu.read8(cpu.addr_zp()))
|
|
|
|
def _CPY_zp(cpu):
|
|
_cmp_with(cpu, cpu.y, cpu.read8(cpu.addr_zp()))
|
|
|
|
def _BPL(cpu): cpu.branch(not cpu.n)
|
|
def _BMI(cpu): cpu.branch(cpu.n)
|
|
def _BNE(cpu): cpu.branch(not cpu.z)
|
|
def _BEQ(cpu): cpu.branch(cpu.z)
|
|
def _BCC(cpu): cpu.branch(not cpu.c)
|
|
def _BCS(cpu): cpu.branch(cpu.c)
|
|
def _BVC(cpu): cpu.branch(not cpu.v)
|
|
def _BVS(cpu): cpu.branch(cpu.v)
|
|
|
|
def _JMP_abs(cpu): cpu.pc = cpu.addr_abs()
|
|
def _JSR(cpu):
|
|
target = cpu.addr_abs()
|
|
cpu.push16((cpu.pc - 1) & 0xFFFF)
|
|
cpu.pc = target
|
|
def _RTS(cpu):
|
|
cpu.pc = (cpu.pop16() + 1) & 0xFFFF
|
|
|
|
|
|
# Build dispatch table
|
|
_DISPATCH = {
|
|
0x00: _NOP, # treat BRK as NOP (we never expect it)
|
|
0x18: _CLC, 0x38: _SEC, 0x58: _NOP, 0x78: _SEI, 0xD8: _CLD,
|
|
0x08: _PHP, 0x28: _PLP, 0x48: _PHA, 0x68: _PLA,
|
|
0x8A: _TXA, 0xAA: _TAX, 0x98: _TYA, 0xA8: _TAY,
|
|
0xE8: _INX, 0xC8: _INY, 0xCA: _DEX, 0x88: _DEY,
|
|
0xA9: _LDA_imm, 0xA5: _LDA_zp, 0xB5: _LDA_zpx, 0xAD: _LDA_abs,
|
|
0xBD: _LDA_absx, 0xB9: _LDA_absy, 0xB1: _LDA_indy,
|
|
0xA2: _LDX_imm, 0xAE: _LDX_abs, 0xBE: _LDX_absy, 0xA6: _LDX_zp,
|
|
0xA0: _LDY_imm, 0xAC: _LDY_abs, 0xBC: _LDY_absx, 0xA4: _LDY_zp,
|
|
0x85: _STA_zp, 0x95: _STA_zpx, 0x8D: _STA_abs,
|
|
0x9D: _STA_absx, 0x99: _STA_absy, 0x91: _STA_indy,
|
|
0x86: _STX_zp, 0x8E: _STX_abs,
|
|
0x84: _STY_zp, 0x8C: _STY_abs,
|
|
0x29: _AND_imm, 0x25: _AND_zp, 0x2D: _AND_abs,
|
|
0x3D: _AND_absx, 0x39: _AND_absy, 0x31: _AND_indy,
|
|
0x09: _ORA_imm, 0x05: _ORA_zp, 0x0D: _ORA_abs, 0x15: _ORA_zpx,
|
|
0x1D: _ORA_absx, 0x11: _ORA_indy,
|
|
0x49: _EOR_imm, 0x4D: _EOR_abs, 0x45: _EOR_zp, 0x55: _EOR_zpx,
|
|
0x5D: _EOR_absx, 0x59: _EOR_absy,
|
|
0x69: _ADC_imm, 0x65: _ADC_zp, 0x6D: _ADC_abs, 0x79: _ADC_absy,
|
|
0x75: _ADC_zpx, 0x7D: _ADC_absx,
|
|
0xE9: _SBC_imm, 0xE5: _SBC_zp, 0xED: _SBC_abs, 0xFD: _SBC_absx, 0xF9: _SBC_absy,
|
|
0xC9: _CMP_imm, 0xC5: _CMP_zp, 0xCD: _CMP_abs, 0xD5: _CMP_zpx,
|
|
0xDD: _CMP_absx, 0xD9: _CMP_absy,
|
|
0xE0: _CPX_imm, 0xEC: _CPX_abs,
|
|
0xC0: _CPY_imm,
|
|
0x24: _BIT_zp, 0x2C: _BIT_abs,
|
|
0x0A: _ASL_a, 0x06: _ASL_zp, 0x0E: _ASL_abs,
|
|
0x4A: _LSR_a, 0x46: _LSR_zp, 0x4E: _LSR_abs,
|
|
0x2A: _ROL_a, 0x26: _ROL_zp, 0x2E: _ROL_abs,
|
|
0x6A: _ROR_a, 0x66: _ROR_zp, 0x6E: _ROR_abs,
|
|
0xEE: _INC_abs, 0xFE: _INC_absx, 0xE6: _INC_zp, 0xF6: _INC_zpx,
|
|
0xCE: _DEC_abs, 0xDE: _DEC_absx, 0xC6: _DEC_zp, 0xD6: _DEC_zpx,
|
|
0x16: _ASL_zpx, 0x1E: _ASL_absx,
|
|
0x56: _LSR_zpx, 0x5E: _LSR_absx,
|
|
0x36: _ROL_zpx, 0x3E: _ROL_absx,
|
|
0x76: _ROR_zpx, 0x7E: _ROR_absx,
|
|
0xA1: _LDA_indx, 0x81: _STA_indx, 0x21: _AND_indx,
|
|
0x01: _ORA_indx, 0x41: _EOR_indx,
|
|
0x61: _ADC_indx, 0xE1: _SBC_indx, 0xC1: _CMP_indx,
|
|
0xE4: _CPX_zp, 0xC4: _CPY_zp,
|
|
0x6C: _JMP_ind,
|
|
0x9A: _TXS, 0xBA: _TSX,
|
|
0x58: _CLI, 0xB8: _CLV, 0xF8: _SED,
|
|
0x96: _STX_zpy, 0x94: _STY_zpx,
|
|
0xB6: _LDX_zpy, 0xB4: _LDY_zpx,
|
|
0x10: _BPL, 0x30: _BMI, 0xD0: _BNE, 0xF0: _BEQ,
|
|
0x90: _BCC, 0xB0: _BCS, 0x50: _BVC, 0x70: _BVS,
|
|
0x4C: _JMP_abs, 0x20: _JSR, 0x60: _RTS,
|
|
0xEA: _NOP,
|
|
}
|