Created
December 28, 2024 19:28
-
-
Save zserge/e5a18c954ab18b5b07be7ff516b5387a to your computer and use it in GitHub Desktop.
tiny 6502 emulator in python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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