Skip to content

Instantly share code, notes, and snippets.

@zserge
Created December 28, 2024 19:28
Show Gist options
  • Save zserge/e5a18c954ab18b5b07be7ff516b5387a to your computer and use it in GitHub Desktop.
Save zserge/e5a18c954ab18b5b07be7ff516b5387a to your computer and use it in GitHub Desktop.
tiny 6502 emulator in python
import base64
OPSZ = [1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 0, 3, 3, 0, 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, 3, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, 1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, 1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, 0, 2, 0, 0, 2, 2, 2, 0, 1, 0, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 0, 3, 0, 0, 2, 2, 2, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0]
WOZMON = base64.b64decode('2Figf4wS0KmnjRHQjRPQyd/wE8mb8APIEA+p3CDv/6mNIO//oAGIMPatEdAQ+60Q0JkAAiDv/8mN0NSg/6kAqgqFK8i5AALJjfDUya6Q9PDwybrw68nS8DuGKIYphCq5AAJJsMkKkAZpiMn6kBEKCgoKogQKJigmKcrQ+MjQ4MQq8JckK1AQpSiBJuYm0LXmJ0xE/2wkADArogK1J5UllSPK0PfQFKmNIO//pSUg3P+lJCDc/6m6IO//qaAg7/+hJCDc/4YrpSTFKKUl5SmwweYk0ALmJaUkKQcQyEhKSkpKIOX/aCkPCbDJupACaQYsEtAw+40S0GAAAAAPAP8AAA==')
WOZACI = base64.b64decode('qaog7/+pjSDv/6D/yK0R0BD7rRDQmQACIO//yZvw4cmN0Omi/6kAhSSFJYUmhSfovQACydLwVsnX8DXJrvAnyY3wIMmg8OhJsMkKkAZpiMn6kK0KCgoKoAQKJiQmJYjQ+PDMTBr/pSSFJqUlhSewv6lAIMzBiKIAoSaiEAog28HQ+iDxwaAekOymKLCYILzBqRYgzMEgvMGgHyC/wbD5IL/BoDqiCEggvMFoKqA5ytD1gSYg8cGgNZDqsM0gv8GIrYHAxSnw+IUpwIBghiigQiDgwdD5af6w9aAeIODBoCyI0P2QBaAviND9vADAoCnKYKUmxSSlJ+Ul5ibQAuYnYA==')
INTBASIC = base64.b64decode('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')
ABS, ABX, ABY, ACC, IMM, IND, IZX, IZY, REL, ZPG, ZPX, ZPY = range(12)
def u16(n): return n & 0xFFFF
def u8(n): return n & 0xFF
def to16(lo, hi): return lo + hi * 0x100
class MOS6502:
def __init__(self, bus):
self.bus = bus
self.a, self.x, self.y = 0, 0, 0
self.s, self.f, self.pc = 0xFD, 0x20, to16(bus[0xFFFC], bus[0xFFFD])
def fetch(self, offset): return self.bus[u16(self.pc + offset)]
def fetch16(self, offset): return to16(self.fetch(offset), self.fetch(u16(offset + 1)))
def push(self, value): self.bus[self.s | 0x100] = value; self.s = u8(self.s - 1)
def pop(self): self.s = u8(self.s + 1); return self.bus[self.s | 0x100]
def push16(self, value): self.push(u8(value >> 8)); self.push(u8(value))
def pop16(self): return to16(self.pop(), self.pop())
def zp16(self, addr): return to16(self.bus[addr], self.bus[u16(addr + 1)])
def addr(self, mode):
if mode == ABS: return to16(self.fetch(1), self.fetch(2))
elif mode == ABX: return u16(self.addr(ABS) + self.x)
elif mode == ABY: return u16(self.addr(ABS) + self.y)
elif mode == IMM: return u16(self.pc + 1)
elif mode == IZX: return self.zp16(u8(self.fetch(1) + self.x))
elif mode == IZY: return u16(self.zp16(self.fetch(1)) + self.y)
elif mode == REL: return (self.fetch(1) ^ 0x80) - 0x80
elif mode == ZPG: return self.fetch(1)
elif mode == ZPX: return u8(self.fetch(1) + self.x)
elif mode == ZPY: return u8(self.fetch(1) + self.y)
elif mode == IND:
addr = self.fetch16(1);
return to16(self.bus[addr], self.bus[(addr & 0xff00) | u8(addr + 1)])
def read(self, mode): return self.a if mode == ACC else self.bus[self.addr(mode)]
def write(self, mode, value):
if mode == ACC: self.a = value
else: self.bus[self.addr(mode)] = value
def nz(self, x):
self.f = (self.f & 0x7D) | ((int(x == 0)) << 1) | (x & 0x80)
return x
def flags(self, mode):
v = self.read(mode)
self.f = (self.f & 0x3D) | (0xC0 & v) | ((int((v & self.a) == 0)) << 1)
def inc(self, mode, n):
value = self.nz(u8(self.read(mode) + n))
self.write(mode, value)
def cmp(self, mode, n):
m = self.read(mode)
self.f = (self.f & 0xFE) | (int(n >= m))
self.nz(u8(n - m))
def bit(self, and_val, or_val, xor_val):
self.a = self.nz(u8(((self.a & and_val) | or_val) ^ xor_val))
def shift(self, mode, left, rot):
b = self.read(mode)
if left:
r = u8((b << 1) | ((self.f & 1) * rot))
f = (self.f & 0xFE) | (b >> 7)
else:
r = u8((b >> 1) | (((self.f & 1) << 7) * rot))
f = (self.f & 0xFE) | (b & 1)
self.f = f
self.nz(r)
self.write(mode, r)
def adc(self, mode, neg):
n = self.read(mode)
if self.f & 8 == 0:
r = self.a + u16(n ^ (0xFF * neg)) + (self.f & 1)
v = u16((0xFF * (1 - neg)) ^ (self.a ^ n)) & (self.a ^ r) & 0x80
self.f = self.f & 0xBE | ((r & 0x100) >> 8) | u8(v >> 1)
self.a = self.nz(u8(r))
else:
if neg:
nd = ((n >> 4) * 10) + (n & 0x0F) + (1 - (self.f & 1))
val = ((self.a >> 4) * 10) + (self.a & 0x0F) - nd
self.f = self.f | 1
if val < 0:
self.f = self.f & 0xFE
val = val + 100
self.a = u8(self.nz((int(val / 10) << 4) + (val % 10)))
else:
p1 = (self.a & 0xF) + (n & 0xF) + (self.f & 1)
p2 = int(p1 >= 10) + (self.a >> 4) + (n >> 4)
self.f = self.f & 0xFE
if p1 >= 10:
p1 = p1 - 10
if p2 >= 10:
self.f = self.f | 1
p2 = p2 - 10
self.a = u8(self.nz((p2 << 4) + p1))
def step(self):
op = self.fetch(0)
next_pc = self.pc + OPSZ[op]
branch = lambda cond: self.addr(REL) if cond else 0
if op == 0x00: # BRK,IMP,1,7,cziDBvn
self.push16(next_pc + 1)
self.push(self.f | 0x30)
self.f = self.f | 4
next_pc = to16(self.bus[0xFFFE], self.bus[0xFFFF])
elif op == 0x20: self.push16(u16(next_pc - 1)); next_pc = self.addr(ABS) # JSR,ABS,3,6,czidbvn
elif op == 0x69: self.adc(IMM, 0) # ADC,IMM,2,2,CZidbVN
elif op == 0x65: self.adc(ZPG, 0) # ADC,ZP,2,3,CZidbVN
elif op == 0x75: self.adc(ZPX, 0) # ADC,ZPX,2,4,CZidbVN
elif op == 0x6D: self.adc(ABS, 0) # ADC,ABS,3,4,CZidbVN
elif op == 0x7D: self.adc(ABX, 0) # ADC,AX,3,4,CZidbVN
elif op == 0x79: self.adc(ABY, 0) # ADC,AY,3,4,CZidbVN
elif op == 0x61: self.adc(IZX, 0) # ADC,ZIX,2,6,CZidbVN
elif op == 0x71: self.adc(IZY, 0) # ADC,ZIY,2,5,CZidbVN
elif op == 0xE9: self.adc(IMM, 1) # SBC,IMM,2,2,CZidbVN
elif op == 0xE5: self.adc(ZPG, 1) # SBC,ZP,2,3,CZidbVN
elif op == 0xF5: self.adc(ZPX, 1) # SBC,ZPX,2,4,CZidbVN
elif op == 0xED: self.adc(ABS, 1) # SBC,ABS,3,4,CZidbVN
elif op == 0xFD: self.adc(ABX, 1) # SBC,AX,3,4,CZidbVN
elif op == 0xF9: self.adc(ABY, 1) # SBC,AY,3,4,CZidbVN
elif op == 0xE1: self.adc(IZX, 1) # SBC,ZIX,2,6,CZidbVN
elif op == 0xF1: self.adc(IZY, 1) # SBC,ZIY,2,5,CZidbVN
elif op == 0x29: self.bit(self.read(IMM), 0, 0) # AND,IMM,2,2,cZidbvN
elif op == 0x25: self.bit(self.read(ZPG), 0, 0) # AND,ZP,2,3,cZidbvN
elif op == 0x35: self.bit(self.read(ZPX), 0, 0) # AND,ZPX,2,4,cZidbvN
elif op == 0x2D: self.bit(self.read(ABS), 0, 0) # AND,ABS,3,4,cZidbvN
elif op == 0x3D: self.bit(self.read(ABX), 0, 0) # AND,AX,3,4,cZidbvN
elif op == 0x39: self.bit(self.read(ABY), 0, 0) # AND,AY,3,4,cZidbvN
elif op == 0x21: self.bit(self.read(IZX), 0, 0) # AND,ZIX,2,6,cZidbvN
elif op == 0x31: self.bit(self.read(IZY), 0, 0) # AND,ZIY,2,5,cZidbvN
elif op == 0x09: self.bit(0xFF, self.read(IMM), 0) # ORA,IMM,2,2,cZidbvN
elif op == 0x05: self.bit(0xFF, self.read(ZPG), 0) # ORA,ZP,2,3,cZidbvN
elif op == 0x15: self.bit(0xFF, self.read(ZPX), 0) # ORA,ZPX,2,4,cZidbvN
elif op == 0x0D: self.bit(0xFF, self.read(ABS), 0) # ORA,ABS,3,4,cZidbvN
elif op == 0x1D: self.bit(0xFF, self.read(ABX), 0) # ORA,AX,3,4,cZidbvN
elif op == 0x19: self.bit(0xFF, self.read(ABY), 0) # ORA,AY,3,4,cZidbvN
elif op == 0x01: self.bit(0xFF, self.read(IZX), 0) # ORA,ZIX,2,6,cZidbvN
elif op == 0x11: self.bit(0xFF, self.read(IZY), 0) # ORA,ZIY,2,5,cZidbvN
elif op == 0x49: self.bit(0xFF, 0, self.read(IMM)) # EOR,IMM,2,2,cZidbvN
elif op == 0x45: self.bit(0xFF, 0, self.read(ZPG)) # EOR,ZP,2,3,cZidbvN
elif op == 0x55: self.bit(0xFF, 0, self.read(ZPX)) # EOR,ZPX,2,4,cZidbvN
elif op == 0x4D: self.bit(0xFF, 0, self.read(ABS)) # EOR,ABS,3,4,cZidbvN
elif op == 0x5D: self.bit(0xFF, 0, self.read(ABX)) # EOR,AX,3,4,cZidbvN
elif op == 0x59: self.bit(0xFF, 0, self.read(ABY)) # EOR,AY,3,4,cZidbvN
elif op == 0x41: self.bit(0xFF, 0, self.read(IZX)) # EOR,ZIX,2,6,cZidbvN
elif op == 0x51: self.bit(0xFF, 0, self.read(IZY)) # EOR,ZIY,2,5,cZidbvN
elif op == 0x0A: self.shift(ACC, True, 0) # ASL,ACC,1,2,CZidbvN
elif op == 0x06: self.shift(ZPG, True, 0) # ASL,ZP,2,5,CZidbvN
elif op == 0x16: self.shift(ZPX, True, 0) # ASL,ZPX,2,6,CZidbvN
elif op == 0x0E: self.shift(ABS, True, 0) # ASL,ABS,3,6,CZidbvN
elif op == 0x1E: self.shift(ABX, True, 0) # ASL,AX,3,6/7,CZidbvN
elif op == 0x4A: self.shift(ACC, False, 0) # LSR,ACC,1,2,cZidbvN
elif op == 0x46: self.shift(ZPG, False, 0) # LSR,ZP,2,5,cZidbvN
elif op == 0x56: self.shift(ZPX, False, 0) # LSR,ZPX,2,6,cZidbvN
elif op == 0x4E: self.shift(ABS, False, 0) # LSR,ABS,3,6,cZidbvN
elif op == 0x5E: self.shift(ABX, False, 0) # LSR,AX,3,6/7,cZidbvN
elif op == 0x2A: self.shift(ACC, True, 1) # ROL,ACC,1,2,CZidbvN
elif op == 0x26: self.shift(ZPG, True, 1) # ROL,ZP,2,5,CZidbvN
elif op == 0x36: self.shift(ZPX, True, 1) # ROL,ZPX,2,6,CZidbvN
elif op == 0x2E: self.shift(ABS, True, 1) # ROL,ABS,3,6,CZidbvN
elif op == 0x3E: self.shift(ABX, True, 1) # ROL,AX,3,6/7,CZidbvN
elif op == 0x6A: self.shift(ACC, False, 1) # ROR,ACC,1,2,CZidbvN
elif op == 0x66: self.shift(ZPG, False, 1) # ROR,ZP,2,5,CZidbvN
elif op == 0x76: self.shift(ZPX, False, 1) # ROR,ZPX,2,6,CZidbvN
elif op == 0x7E: self.shift(ABX, False, 1) # ROR,ABS,3,6,CZidbvN
elif op == 0x6E: self.shift(ABS, False, 1) # ROR,AX,3,6/7,CZidbvN
elif op == 0xCA: self.x = self.nz(u8(self.x - 1)) # DEX,IMP,1,2,cZidbvN
elif op == 0x88: self.y = self.nz(u8(self.y - 1)) # DEY,IMP,1,2,cZidbvN
elif op == 0xE8: self.x = self.nz(u8(self.x + 1)) # INX,IMP,1,2,cZidbvN
elif op == 0xC8: self.y = self.nz(u8(self.y + 1)) # INY,IMP,1,2,cZidbvN
elif op == 0xE6: self.inc(ZPG, 1) # INC,ZP,2,5,cZidbvN
elif op == 0xF6: self.inc(ZPX, 1) # INC,ZPX,2,6,cZidbvN
elif op == 0xEE: self.inc(ABS, 1) # INC,ABS,3,6,cZidbvN
elif op == 0xFE: self.inc(ABX, 1) # INC,AX,3,7,cZidbvN
elif op == 0xC6: self.inc(ZPG, 0xFF) # DEC,ZP,2,5,cZidbvN
elif op == 0xD6: self.inc(ZPX, 0xFF) # DEC,ZPX,2,6,cZidbvN
elif op == 0xCE: self.inc(ABS, 0xFF) # DEC,ABS,3,6,cZidbvN
elif op == 0xDE: self.inc(ABX, 0xFF) # DEC,AX,3,7,cZidbvN
elif op == 0xC9: self.cmp(IMM, self.a) # CMP,IMM,2,2,CZidbvN
elif op == 0xC5: self.cmp(ZPG, self.a) # CMP,ZP,2,3,CZidbvN
elif op == 0xD5: self.cmp(ZPX, self.a) # CMP,ZPX,2,4,CZidbvN
elif op == 0xCD: self.cmp(ABS, self.a) # CMP,ABS,3,4,CZidbvN
elif op == 0xDD: self.cmp(ABX, self.a) # CMP,AX,3,4,CZidbvN
elif op == 0xD9: self.cmp(ABY, self.a) # CMP,AY,3,4,CZidbvN
elif op == 0xC1: self.cmp(IZX, self.a) # CMP,ZIX,2,6,CZidbvN
elif op == 0xD1: self.cmp(IZY, self.a) # CMP,ZIY,2,5,CZidbvN
elif op == 0xE0: self.cmp(IMM, self.x) # CPX,IMM,2,2,CZidbvN
elif op == 0xE4: self.cmp(ZPG, self.x) # CPX,ZP,2,3,CZidbvN
elif op == 0xEC: self.cmp(ABS, self.x) # CPX,ABS,3,4,CZidbvN
elif op == 0xC0: self.cmp(IMM, self.y) # CPY,IMM,2,2,CZidbvN
elif op == 0xC4: self.cmp(ZPG, self.y) # CPY,ZP,2,3,CZidbvN
elif op == 0xCC: self.cmp(ABS, self.y) # CPY,ABS,3,4,CZidbvN
elif op == 0x18: self.f = self.f & 0xFE # CLC,IMP,1,2,Czidbvn
elif op == 0xD8: self.f = self.f & 0xF7 # CLD,IMP,1,2,cziDbvn
elif op == 0x58: self.f = self.f & 0xFB # CLI,IMP,1,2,czIdbvn
elif op == 0xB8: self.f = self.f & 0xBF # CLV,IMP,1,2,czidbVn
elif op == 0x38: self.f = self.f | 0x01 # SEC,IMP,1,2,Czidbvn
elif op == 0xF8: self.f = self.f | 0x08 # SED,IMP,1,2,cziDbvn
elif op == 0x78: self.f = self.f | 0x04 # SEI,IMP,1,2,czIdbvn
elif op == 0x24: self.flags(ZPG), # BIT,ZP,2,3,cZidbVN
elif op == 0x2C: self.flags(ABS), # BIT,ABS,3,4,cZidbVN
elif op == 0x90: next_pc += branch((self.f & 0x01) == 0) # BCC,REL,2,2/3,czidbvn
elif op == 0xB0: next_pc += branch((self.f & 0x01) != 0) # BCS,REL,2,2/3,czidbvn
elif op == 0xF0: next_pc += branch((self.f & 0x02) != 0) # BEQ,REL,2,2/3,czidbvn
elif op == 0x30: next_pc += branch((self.f & 0x80) != 0) # BMI,REL,2,2/3,czidbvn
elif op == 0xD0: next_pc += branch((self.f & 0x02) == 0) # BNE,REL,2,2/3,czidbvn
elif op == 0x10: next_pc += branch((self.f & 0x80) == 0) # BPL,REL,2,2/3,czidbvn
elif op == 0x50: next_pc += branch((self.f & 0x40) == 0) # BVC,REL,2,2/3,czidbvn
elif op == 0x70: next_pc += branch((self.f & 0x40) != 0) # BVS,REL,2,2/3,czidbvn
elif op == 0x4C: next_pc = self.addr(ABS) # JMP,ABS,3,3,czidbvn
elif op == 0x6C: next_pc = self.addr(IND) # JMP,IND,3,5,czidbvn
elif op == 0x48: self.push(self.a) # PHA,IMP,1,3,czidbvn
elif op == 0x68: n = self.pop(); self.a = self.nz(n) # PLA,IMP,1,4,cZidbvN
elif op == 0x08: self.push(self.f | 0x30) # PHP,IMP,1,3,czidbvn
elif op == 0x28: self.f = self.pop() | 0x20 # PLP,IMP,1,4,CZIDBVN
elif op == 0x40: self.f = self.pop() | 0x20; next_pc = self.pop16() # RTI,IMP,1,6,czidbvn
elif op == 0x60: next_pc = self.pop16() + 1 # RTS,IMP,1,6,czidbvn
elif op == 0xAA: self.x = self.nz(self.a) # TAX,IMP,1,2,cZidbvN
elif op == 0x8A: self.a = self.nz(self.x) # TXA,IMP,1,2,cZidbvN
elif op == 0xA8: self.y = self.nz(self.a) # TAY,IMP,1,2,cZidbvN
elif op == 0x98: self.a = self.nz(self.y) # TYA,IMP,1,2,cZidbvN
elif op == 0xBA: self.x = self.nz(self.s) # TSX,IMP,1,2,cZidbvN
elif op == 0x9A: self.s = self.x # TXS,IMP,1,2,czidbvn
elif op == 0xA9: self.a = self.nz(self.read(IMM)) # LDA,IMM,2,2,cZidbvN
elif op == 0xA5: self.a = self.nz(self.read(ZPG)) # LDA,ZP,2,3,cZidbvN
elif op == 0xB5: self.a = self.nz(self.read(ZPX)) # LDA,ZPX,2,4,cZidbvN
elif op == 0xAD: self.a = self.nz(self.read(ABS)) # LDA,ABS,3,4,cZidbvN
elif op == 0xBD: self.a = self.nz(self.read(ABX)) # LDA,AX,3,4,cZidbvN
elif op == 0xB9: self.a = self.nz(self.read(ABY)) # LDA,AY,3,4,cZidbvN
elif op == 0xA1: self.a = self.nz(self.read(IZX)) # LDA,ZIX,2,6,cZidbvN
elif op == 0xB1: self.a = self.nz(self.read(IZY)) # LDA,ZIY,2,5,cZidbvN
elif op == 0xA2: self.x = self.nz(self.read(IMM)) # LDX,IMM,2,2,cZidbvN
elif op == 0xA6: self.x = self.nz(self.read(ZPG)) # LDX,ZP,2,3,cZidbvN
elif op == 0xB6: self.x = self.nz(self.read(ZPY)) # LDX,ZPY,2,4,cZidbvN
elif op == 0xAE: self.x = self.nz(self.read(ABS)) # LDX,ABS,3,4,cZidbvN
elif op == 0xBE: self.x = self.nz(self.read(ABY)) # LDX,AY,3,4,cZidbvN
elif op == 0xA0: self.y = self.nz(self.read(IMM)) # LDY,IMM,2,2,cZidbvN
elif op == 0xA4: self.y = self.nz(self.read(ZPG)) # LDY,ZP,2,3,cZidbvN
elif op == 0xB4: self.y = self.nz(self.read(ZPX)) # LDY,ZPX,2,4,cZidbvN
elif op == 0xAC: self.y = self.nz(self.read(ABS)) # LDY,ABS,3,4,cZidbvN
elif op == 0xBC: self.y = self.nz(self.read(ABX)) # LDY,AX,3,4,cZidbvN
elif op == 0x85: self.write(ZPG, self.a) # STA,ZP,2,3,czidbvn
elif op == 0x95: self.write(ZPX, self.a) # STA,ZPX,2,4,czidbvn
elif op == 0x8D: self.write(ABS, self.a) # STA,ABS,3,4,czidbvn
elif op == 0x9D: self.write(ABX, self.a) # STA,AX,3,5,czidbvn
elif op == 0x99: self.write(ABY, self.a) # STA,AY,3,5,czidbvn
elif op == 0x81: self.write(IZX, self.a) # STA,ZIX,2,6,czidbvn
elif op == 0x91: self.write(IZY, self.a) # STA,ZIY,2,6,czidbvn
elif op == 0x86: self.write(ZPG, self.x) # STX,ZP,2,3,czidbvn
elif op == 0x96: self.write(ZPY, self.x) # STX,ZPY,2,4,czidbvn
elif op == 0x8E: self.write(ABS, self.x) # STX,ABS,3,4,czidbvn
elif op == 0x84: self.write(ZPG, self.y) # STY,ZP,2,3,czidbvn
elif op == 0x94: self.write(ZPX, self.y) # STY,ZPX,2,4,czidbvn
elif op == 0x8C: self.write(ABS, self.y) # STY,ABS,3,4,czidbvn
elif op == 0xEA: pass # NOP,IMP,1,2,czidbvn
else: print(op, self.pc); raise()
self.pc = u16(next_pc)
class AppleI:
def __init__(self):
self.keys = []
self.mem = bytearray(0x1000)
def __getitem__(self, i):
if i >= 0 and i < 0x1000: return self.mem[i] # 4K RAM
elif i >= 0xc100 and i <= 0xc1ff: return WOZACI[i - 0xc100]
elif i >= 0xff00 and i <= 0xffff: return WOZMON[i - 0xff00]
elif i >= 0xe000 and i <= 0xefff: return INTBASIC[i - 0xe000]
elif i == 0xd010: return self.keys.pop(0)|0x80 if len(self.keys) else 0x80
elif i == 0xd011: return 0x80 if len(self.keys) else 0
else: return 0
def __setitem__(self, i, n):
if i >= 0 and i < 0x0fff: self.mem[i] = n
elif i == 0xd012:
if n & 0x7f == 0x0d: print('')
elif n & 0x7f == 0x5f: print('\b', end = '', flush=True)
else: print(chr(n&0x7f), end='', flush=True)
def send_key(self, c):
self.keys.append(c)
import sys, termios, tty, select
a1 = AppleI()
cpu = MOS6502(a1)
attr = termios.tcgetattr(sys.stdin)
try:
tc = termios.tcgetattr(sys.stdin)
tc[3] = (tc[3] & ~termios.ICANON & ~termios.ECHO)
termios.tcsetattr(sys.stdin, termios.TCSAFLUSH, tc)
while True:
cpu.step()
if select.select([sys.stdin], [], [], 0.00001) == ([sys.stdin], [], []):
c = sys.stdin.read(1)
a1.send_key(13 if c == '\n' else ord(c))
finally:
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, attr)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment