mirror of
https://github.com/jtauber/applepy.git
synced 2024-10-31 13:08:22 +00:00
665 lines
25 KiB
Python
665 lines
25 KiB
Python
# ApplePy - an Apple ][ emulator in Python
|
|
# James Tauber / http://jtauber.com/
|
|
# originally written 2001, updated 2011
|
|
|
|
|
|
import curses
|
|
|
|
def signed(x):
|
|
if x > 0x7F:
|
|
x = x - 0x100
|
|
return x
|
|
|
|
|
|
class Memory:
|
|
def __init__(self, size):
|
|
self.__mem = [0x00] * size
|
|
|
|
def load(self, filename, offset):
|
|
with open(filename) as f:
|
|
address = offset
|
|
while True:
|
|
ch = f.read(1)
|
|
if ch == "":
|
|
break
|
|
self.__mem[address] = ord(ch)
|
|
address += 1
|
|
|
|
def read_byte(self, address):
|
|
assert address <= 0xFFFF
|
|
if 0xC000 <= address <= 0xCFFF:
|
|
if address == 0xC010:
|
|
self.__mem[0xC000] = self.__mem[0xC000] & 0x7F # clear keyboard
|
|
return self.__mem[address]
|
|
|
|
def write_byte(self, address, value):
|
|
if 0x400 <= address < 0x800:
|
|
self.write_screen(address, value)
|
|
self.__mem[address] = value
|
|
|
|
def read_word(self, address):
|
|
return self.read_byte(address) + (self.read_byte(address + 1) << 8)
|
|
|
|
def write_screen(self, address, value):
|
|
address_hi, address_lo = divmod(address, 0x100)
|
|
|
|
if address_lo < 0x28:
|
|
a = 0x01
|
|
b = 0x00
|
|
elif address_lo < 0x50:
|
|
a = 0x09
|
|
b = 0x28
|
|
elif address_lo < 0x80:
|
|
a = 0x11
|
|
b = 0x50
|
|
elif address_lo < 0xA8:
|
|
a = 0x02
|
|
b = 0x80
|
|
elif address_lo < 0xD0:
|
|
a = 0x0A
|
|
b = 0xA8
|
|
else:
|
|
a = 0x12
|
|
b = 0xD0
|
|
|
|
x = address_lo - b
|
|
y = 2 * (address_hi - 0x04) + a
|
|
|
|
c = chr(0x20 + ((value + 0x20) % 0x40))
|
|
|
|
if value < 0x40:
|
|
attr = curses.A_DIM
|
|
elif value < 0x80:
|
|
attr = curses.A_REVERSE
|
|
elif value < 0xA0:
|
|
attr = curses.A_UNDERLINE
|
|
else:
|
|
attr = curses.A_DIM
|
|
|
|
if x < 0x28:
|
|
self.win.addch(y, x, c, attr)
|
|
|
|
|
|
class CPU:
|
|
|
|
STACK_PAGE = 0x100
|
|
RESET_VECTOR = 0xFFFC
|
|
|
|
def __init__(self, memory):
|
|
self.memory = memory
|
|
|
|
self.accumulator = 0x00
|
|
self.x_index = 0x00
|
|
self.y_index = 0x00
|
|
|
|
self.carry_flag = 0
|
|
self.zero_flag = 0
|
|
self.interrupt_disable_flag = 0
|
|
self.decimal_mode_flag = 0
|
|
self.break_flag = 1
|
|
self.overflow_flag = 0
|
|
self.sign_flag = 0
|
|
|
|
self.stack_pointer = 0xFF
|
|
|
|
self.setup_ops()
|
|
self.reset()
|
|
|
|
def setup_ops(self):
|
|
self.ops = [None] * 0x100
|
|
self.ops[0x00] = lambda: self.BRK()
|
|
self.ops[0x01] = lambda: self.ORA(self.indirect_x_mode())
|
|
self.ops[0x05] = lambda: self.ORA(self.zero_page_mode())
|
|
self.ops[0x06] = lambda: self.ASL(self.zero_page_mode())
|
|
self.ops[0x08] = lambda: self.PHP()
|
|
self.ops[0x09] = lambda: self.ORA(self.immediate_mode())
|
|
self.ops[0x0A] = lambda: self.ASL()
|
|
self.ops[0x0D] = lambda: self.ORA(self.absolute_mode())
|
|
self.ops[0x0E] = lambda: self.ASL(self.absolute_mode())
|
|
self.ops[0x10] = lambda: self.BPL(self.relative_mode())
|
|
self.ops[0x11] = lambda: self.ORA(self.indirect_y_mode())
|
|
self.ops[0x15] = lambda: self.ORA(self.zero_page_x_mode())
|
|
self.ops[0x16] = lambda: self.ASL(self.zero_page_x_mode())
|
|
self.ops[0x18] = lambda: self.CLC()
|
|
self.ops[0x19] = lambda: self.ORA(self.absolute_y_mode())
|
|
self.ops[0x1D] = lambda: self.ORA(self.absolute_x_mode())
|
|
self.ops[0x1E] = lambda: self.ASL(self.absolute_x_mode())
|
|
self.ops[0x20] = lambda: self.JSR(self.absolute_mode())
|
|
self.ops[0x21] = lambda: self.AND(self.indirect_x_mode())
|
|
self.ops[0x24] = lambda: self.BIT(self.zero_page_mode())
|
|
self.ops[0x25] = lambda: self.AND(self.zero_page_mode())
|
|
self.ops[0x26] = lambda: self.ROL(self.zero_page_mode())
|
|
self.ops[0x28] = lambda: self.PLP()
|
|
self.ops[0x29] = lambda: self.AND(self.immediate_mode())
|
|
self.ops[0x2A] = lambda: self.ROL()
|
|
self.ops[0x2C] = lambda: self.BIT(self.absolute_mode())
|
|
self.ops[0x2D] = lambda: self.AND(self.absolute_mode())
|
|
self.ops[0x2E] = lambda: self.ROL(self.absolute_mode())
|
|
self.ops[0x30] = lambda: self.BMI(self.relative_mode())
|
|
self.ops[0x31] = lambda: self.AND(self.indirect_y_mode())
|
|
self.ops[0x35] = lambda: self.AND(self.zero_page_x_mode())
|
|
self.ops[0x36] = lambda: self.ROL(self.zero_page_x_mode())
|
|
self.ops[0x38] = lambda: self.SEC()
|
|
self.ops[0x39] = lambda: self.AND(self.absolute_y_mode())
|
|
self.ops[0x3D] = lambda: self.AND(self.absolute_x_mode())
|
|
self.ops[0x3E] = lambda: self.ROL(self.absolute_x_mode())
|
|
self.ops[0x40] = lambda: self.RTI()
|
|
self.ops[0x41] = lambda: self.EOR(x.indirect_x_mode())
|
|
self.ops[0x45] = lambda: self.EOR(self.zero_page_mode())
|
|
self.ops[0x46] = lambda: self.LSR(self.zero_page_mode())
|
|
self.ops[0x48] = lambda: self.PHA()
|
|
self.ops[0x49] = lambda: self.EOR(self.immediate_mode())
|
|
self.ops[0x4A] = lambda: self.LSR()
|
|
self.ops[0x4C] = lambda: self.JMP(self.absolute_mode())
|
|
self.ops[0x4D] = lambda: self.EOR(self.absolute_mode())
|
|
self.ops[0x4E] = lambda: self.LSR(self.absolute_mode())
|
|
self.ops[0x50] = lambda: self.BVC(self.relative_mode())
|
|
self.ops[0x51] = lambda: self.EOR(self.indirect_y_mode())
|
|
self.ops[0x55] = lambda: self.EOR(self.zero_page_x_mode())
|
|
self.ops[0x56] = lambda: self.LSR(self.zero_page_x_mode())
|
|
self.ops[0x58] = lambda: self.CLI()
|
|
self.ops[0x59] = lambda: self.EOR(self.absolute_y_mode())
|
|
self.ops[0x5D] = lambda: self.EOR(self.absolute_x_mode())
|
|
self.ops[0x5E] = lambda: self.LSR(self.absolute_x_mode())
|
|
self.ops[0x60] = lambda: self.RTS()
|
|
self.ops[0x61] = lambda: self.ADC(self.indirect_x_mode())
|
|
self.ops[0x65] = lambda: self.ADC(self.zero_page_mode())
|
|
self.ops[0x66] = lambda: self.ROR(self.zero_page_mode())
|
|
self.ops[0x68] = lambda: self.PLA()
|
|
self.ops[0x69] = lambda: self.ADC(self.immediate_mode())
|
|
self.ops[0x6A] = lambda: self.ROR()
|
|
self.ops[0x6C] = lambda: self.JMP(self.indirect_mode())
|
|
self.ops[0x6D] = lambda: self.ADC(self.absolute_mode())
|
|
self.ops[0x6E] = lambda: self.ROR(self.absolute_mode())
|
|
self.ops[0x70] = lambda: self.BVS(self.relative_mode())
|
|
self.ops[0x71] = lambda: self.ADC(self.indirect_y_mode())
|
|
self.ops[0x75] = lambda: self.ADC(self.zero_page_x_mode())
|
|
self.ops[0x76] = lambda: self.ROR(self.zero_page_x_mode())
|
|
self.ops[0x78] = lambda: self.SEI()
|
|
self.ops[0x79] = lambda: self.ADC(self.absolute_y_mode())
|
|
self.ops[0x7D] = lambda: self.ADC(self.absolute_x_mode())
|
|
self.ops[0x7E] = lambda: self.ROR(self.absolute_x_mode())
|
|
self.ops[0x81] = lambda: self.STA(self.indirect_x_mode())
|
|
self.ops[0x84] = lambda: self.STY(self.zero_page_mode())
|
|
self.ops[0x85] = lambda: self.STA(self.zero_page_mode())
|
|
self.ops[0x86] = lambda: self.STX(self.zero_page_mode())
|
|
self.ops[0x88] = lambda: self.DEY()
|
|
self.ops[0x8A] = lambda: self.TXA()
|
|
self.ops[0x8C] = lambda: self.STY(self.absolute_mode())
|
|
self.ops[0x8D] = lambda: self.STA(self.absolute_mode())
|
|
self.ops[0x8E] = lambda: self.STX(self.absolute_mode())
|
|
self.ops[0x90] = lambda: self.BCC(self.relative_mode())
|
|
self.ops[0x91] = lambda: self.STA(self.indirect_y_mode())
|
|
self.ops[0x94] = lambda: self.STY(self.zero_page_x_mode())
|
|
self.ops[0x95] = lambda: self.STA(self.zero_page_x_mode())
|
|
self.ops[0x96] = lambda: self.STX(self.zero_page_y())
|
|
self.ops[0x98] = lambda: self.TYA()
|
|
self.ops[0x99] = lambda: self.STA(self.absolute_y_mode())
|
|
self.ops[0x9A] = lambda: self.TXS()
|
|
self.ops[0x9D] = lambda: self.STA(self.absolute_x_mode())
|
|
self.ops[0xA0] = lambda: self.LDY(self.immediate_mode())
|
|
self.ops[0xA1] = lambda: self.LDA(self.indirect_x_mode())
|
|
self.ops[0xA2] = lambda: self.LDX(self.immediate_mode())
|
|
self.ops[0xA4] = lambda: self.LDY(self.zero_page_mode())
|
|
self.ops[0xA5] = lambda: self.LDA(self.zero_page_mode())
|
|
self.ops[0xA6] = lambda: self.LDX(self.zero_page_mode())
|
|
self.ops[0xA8] = lambda: self.TAY()
|
|
self.ops[0xA9] = lambda: self.LDA(self.immediate_mode())
|
|
self.ops[0xAA] = lambda: self.TAX()
|
|
self.ops[0xAC] = lambda: self.LDY(self.absolute_mode())
|
|
self.ops[0xAD] = lambda: self.LDA(self.absolute_mode())
|
|
self.ops[0xAE] = lambda: self.LDX(self.absolute_mode())
|
|
self.ops[0xB0] = lambda: self.BCS(self.relative_mode())
|
|
self.ops[0xB1] = lambda: self.LDA(self.indirect_y_mode())
|
|
self.ops[0xB4] = lambda: self.LDY(self.zero_page_x_mode())
|
|
self.ops[0xB5] = lambda: self.LDA(self.zero_page_x_mode())
|
|
self.ops[0xB6] = lambda: self.LDX(self.zero_page_y())
|
|
self.ops[0xB8] = lambda: self.CLV()
|
|
self.ops[0xB9] = lambda: self.LDA(self.absolute_y_mode())
|
|
self.ops[0xBA] = lambda: self.TSX()
|
|
self.ops[0xBC] = lambda: self.LDY(self.absolute_x_mode())
|
|
self.ops[0xBD] = lambda: self.LDA(self.absolute_x_mode())
|
|
self.ops[0xBE] = lambda: self.LDX(self.absolute_y_mode())
|
|
self.ops[0xC0] = lambda: self.CPY(self.immediate_mode())
|
|
self.ops[0xC1] = lambda: self.CMP(self.indirect_x_mode())
|
|
self.ops[0xC4] = lambda: self.CPY(self.zero_page_mode())
|
|
self.ops[0xC5] = lambda: self.CMP(self.zero_page_mode())
|
|
self.ops[0xC6] = lambda: self.DEC(self.zero_page_mode())
|
|
self.ops[0xC8] = lambda: self.INY()
|
|
self.ops[0xC9] = lambda: self.CMP(self.immediate_mode())
|
|
self.ops[0xCA] = lambda: self.DEX()
|
|
self.ops[0xCC] = lambda: self.CPY(self.absolute_mode())
|
|
self.ops[0xCD] = lambda: self.CMP(self.absolute_mode())
|
|
self.ops[0xCE] = lambda: self.DEC(self.absolute_mode())
|
|
self.ops[0xD0] = lambda: self.BNE(self.relative_mode())
|
|
self.ops[0xD1] = lambda: self.CMP(self.indirect_y_mode())
|
|
self.ops[0xD5] = lambda: self.CMP(self.zero_page_x_mode())
|
|
self.ops[0xD6] = lambda: self.DEC(self.zero_page_x_mode())
|
|
self.ops[0xD8] = lambda: self.CLD()
|
|
self.ops[0xD9] = lambda: self.CMP(self.absolute_y_mode())
|
|
self.ops[0xDD] = lambda: self.CMP(self.absolute_x_mode())
|
|
self.ops[0xDE] = lambda: self.DEC(self.absolute_x_mode())
|
|
self.ops[0xE0] = lambda: self.CPX(self.immediate_mode())
|
|
self.ops[0xE1] = lambda: self.SBC(self.indirect_x_mode())
|
|
self.ops[0xE4] = lambda: self.CPX(self.zero_page_mode())
|
|
self.ops[0xE5] = lambda: self.SBC(self.zero_page_mode())
|
|
self.ops[0xE6] = lambda: self.INC(self.zero_page_mode())
|
|
self.ops[0xE8] = lambda: self.INX()
|
|
self.ops[0xE9] = lambda: self.SBC(self.immediate_mode())
|
|
self.ops[0xEA] = lambda: self.NOP()
|
|
self.ops[0xEC] = lambda: self.CPX(self.absolute_mode())
|
|
self.ops[0xED] = lambda: self.SBC(self.absolute_mode())
|
|
self.ops[0xEE] = lambda: self.INC(self.absolute_mode())
|
|
self.ops[0xF0] = lambda: self.BEQ(self.relative_mode())
|
|
self.ops[0xF1] = lambda: self.SBC(self.indirect_y_mode())
|
|
self.ops[0xF5] = lambda: self.SBC(self.zero_page_x_mode())
|
|
self.ops[0xF6] = lambda: self.INC(self.zero_page_x_mode())
|
|
self.ops[0xF8] = lambda: self.SED()
|
|
self.ops[0xF9] = lambda: self.SBC(self.absolute_y_mode())
|
|
self.ops[0xFD] = lambda: self.SBC(self.absolute_x_mode())
|
|
self.ops[0xFE] = lambda: self.INC(self.absolute_x_mode())
|
|
|
|
def reset(self):
|
|
self.program_counter = self.memory.read_word(self.RESET_VECTOR)
|
|
|
|
def dump(self, win):
|
|
win.addstr(10, 50, "%04X got %02X" % (self.program_counter, op))
|
|
win.addstr(14, 50, "BUFFER:" +
|
|
" ".join("%02X" % self.memory.read_byte(m) for m in range(0x200, 0x210))
|
|
)
|
|
win.addstr(11, 50, "A=%02X X=%02X Y=%02X S=%02X V=%02X B=%02X D=%02X I=%02X Z=%02X C=%02X PC=%04X S=%02X" % (
|
|
self.accumulator,
|
|
self.x_index,
|
|
self.y_index,
|
|
self.sign_flag,
|
|
self.overflow_flag,
|
|
self.break_flag,
|
|
self.decimal_mode_flag,
|
|
self.interrupt_disable_flag,
|
|
self.zero_flag,
|
|
self.carry_flag,
|
|
self.program_counter,
|
|
self.stack_pointer))
|
|
win.addstr(12, 50, "STACK:" +
|
|
" ".join("%02X" % self.memory.read_byte(self.STACK_PAGE + i) for i in range(255, self.stack_pointer, -1))
|
|
)
|
|
|
|
def run(self, win):
|
|
self.memory.win = win
|
|
win.clear()
|
|
curses.noecho()
|
|
win.nodelay(True)
|
|
while True:
|
|
op = self.memory.read_byte(self.program_counter)
|
|
# self.dump(win)
|
|
self.program_counter += 1
|
|
func = self.ops[op]
|
|
if func is None:
|
|
curses.endwin()
|
|
print "UNKNOWN OP"
|
|
print hex(self.program_counter - 1)
|
|
print hex(op)
|
|
break
|
|
else:
|
|
self.ops[op]()
|
|
|
|
try:
|
|
key = ord(win.getkey())
|
|
if key == 0xA:
|
|
key = 0xD
|
|
elif key == 0x7F:
|
|
key = 0x8
|
|
# win.addstr(15, 50, hex(key))
|
|
self.memory.write_byte(0xC000, 0x80 + key)
|
|
except curses.error:
|
|
pass
|
|
except TypeError:
|
|
pass
|
|
|
|
####
|
|
|
|
def immediate_mode(self):
|
|
address = self.program_counter
|
|
self.program_counter += 1
|
|
return address
|
|
|
|
def absolute_mode(self):
|
|
address = self.memory.read_word(self.program_counter)
|
|
self.program_counter += 2
|
|
return address
|
|
|
|
def absolute_x_mode(self):
|
|
return self.absolute_mode() + signed(self.x_index)
|
|
|
|
def absolute_y_mode(self):
|
|
return self.absolute_mode() + signed(self.y_index)
|
|
|
|
def zero_page_mode(self):
|
|
address = self.memory.read_byte(self.program_counter)
|
|
self.program_counter += 1
|
|
return address
|
|
|
|
def zero_page_x_mode(self):
|
|
return self.zero_page_mode() + signed(self.x_index)
|
|
|
|
def indirect_mode(self):
|
|
return self.memory.read_word(self.absolute_mode())
|
|
|
|
def indirect_x_mode(self):
|
|
address = self.memory.read_word((self.memory.read_byte(self.program_counter) + signed(self.x_index)) % 0x100)
|
|
self.program_counter += 1
|
|
return address
|
|
|
|
def indirect_y_mode(self):
|
|
address = self.memory.read_word(self.memory.read_byte(self.program_counter)) + signed(self.y_index)
|
|
self.program_counter += 1
|
|
return address
|
|
|
|
def relative_mode(self):
|
|
pc = self.program_counter
|
|
self.program_counter += 1
|
|
address = pc + 1 + signed(self.memory.read_byte(pc))
|
|
return address
|
|
|
|
####
|
|
|
|
def update_nz(self, value):
|
|
self.zero_flag = (value % 0x100 == 0)
|
|
self.sign_flag = (value > 0x7F) or (value < 0x00)
|
|
return value % 0x100
|
|
|
|
def update_nzc(self, value):
|
|
self.zero_flag = (value % 0x100 == 0)
|
|
self.sign_flag = (value > 0x7F) or (value < 0x00)
|
|
self.carry_flag = (value > 0xFF)
|
|
return value % 0x100
|
|
|
|
####
|
|
|
|
# NOP
|
|
|
|
def NOP(self):
|
|
pass
|
|
|
|
# LOAD / STORE
|
|
|
|
def LDA(self, operand_address):
|
|
self.accumulator = self.update_nz(self.memory.read_byte(operand_address))
|
|
|
|
def LDX(self, operand_address):
|
|
self.x_index = self.update_nz(self.memory.read_byte(operand_address))
|
|
|
|
def LDY(self, operand_address):
|
|
self.y_index = self.update_nz(self.memory.read_byte(operand_address))
|
|
|
|
def STA(self, operand_address):
|
|
self.memory.write_byte(operand_address, self.accumulator)
|
|
|
|
def STX(self, operand_address):
|
|
self.memory.write_byte(operand_address, self.x_index)
|
|
|
|
def STY(self, operand_address):
|
|
self.memory.write_byte(operand_address, self.y_index)
|
|
|
|
# TRANSFER
|
|
|
|
def TAX(self):
|
|
self.x_index = self.update_nz(self.accumulator)
|
|
|
|
def TXA(self):
|
|
self.accumulator = self.update_nz(self.x_index)
|
|
|
|
def TAY(self):
|
|
self.y_index = self.update_nz(self.accumulator)
|
|
|
|
def TYA(self):
|
|
self.accumulator = self.update_nz(self.y_index)
|
|
|
|
def TSX(self):
|
|
self.x_index = self.stack_pointer
|
|
|
|
def TXS(self):
|
|
self.stack_pointer = self.x_index
|
|
|
|
# SHIFTS / ROTATES
|
|
|
|
def ASL(self, operand_address=None):
|
|
if operand_address is None:
|
|
self.accumulator = self.accumulator << 1
|
|
self.carry_flag = (self.accumulator > 0xFF)
|
|
self.accumulator = self.update_nz(self.accumulator)
|
|
else:
|
|
m = self.memory.read_byte(operand_address) << 1
|
|
self.memory.write_byte(operand_address, self.update_nzc(m))
|
|
|
|
def ROL(self, operand_address=None):
|
|
if operand_address is None:
|
|
a = self.accumulator << 1
|
|
if self.carry_flag:
|
|
a = a | 0x01
|
|
self.accumulator = self.update_nzc(a)
|
|
else:
|
|
m = self.memory.read_byte(operand_address) << 1
|
|
if self.carry_flag:
|
|
m = m | 0x01
|
|
self.memory.write_byte(operand_address,self.update_nzc(m))
|
|
|
|
def ROR(self):
|
|
if self.carry_flag:
|
|
self.accumulator = self.accumulator | 0x100
|
|
self.carry_flag = self.accumulator % 2
|
|
self.accumulator = self.update_nz(self.accumulator >> 1)
|
|
|
|
def LSR(self, operand_address=None):
|
|
if operand_address is None:
|
|
self.carry_flag = self.accumulator % 2
|
|
self.accumulator = self.update_nz(self.accumulator >> 1)
|
|
else:
|
|
self.carry_flag = self.memory.read_byte(operand_address) % 2
|
|
self.memory.write_byte(operand_address, self.update_nz(self.memory.read_byte(operand_address) >> 1))
|
|
|
|
# JUMPS / RETURNS
|
|
|
|
def JMP(self, operand_address):
|
|
self.program_counter = operand_address
|
|
|
|
def JSR(self, operand_address):
|
|
hi, lo = divmod(self.program_counter - 1, 0x100)
|
|
s = self.STACK_PAGE + self.stack_pointer
|
|
self.stack_pointer = (self.stack_pointer - 1)
|
|
self.memory.write_byte(s, hi)
|
|
s = self.STACK_PAGE + self.stack_pointer
|
|
self.stack_pointer = (self.stack_pointer - 1)
|
|
self.memory.write_byte(s, lo)
|
|
self.program_counter = operand_address
|
|
|
|
def RTS(self):
|
|
s = self.STACK_PAGE + self.stack_pointer + 1
|
|
self.program_counter = self.memory.read_word(s) + 1
|
|
self.stack_pointer = self.stack_pointer + 2 # TODO: what to do when stack is empty?
|
|
|
|
# BRANCHES
|
|
|
|
def BCC(self, operand_address):
|
|
if not self.carry_flag:
|
|
self.program_counter = operand_address
|
|
|
|
def BCS(self, operand_address):
|
|
if self.carry_flag:
|
|
self.program_counter = operand_address
|
|
|
|
def BEQ(self, operand_address):
|
|
if self.zero_flag:
|
|
self.program_counter = operand_address
|
|
|
|
def BNE(self, operand_address):
|
|
if not self.zero_flag:
|
|
self.program_counter = operand_address
|
|
|
|
def BMI(self, operand_address):
|
|
if self.sign_flag:
|
|
self.program_counter = operand_address
|
|
|
|
def BPL(self, operand_address):
|
|
if not self.sign_flag:
|
|
self.program_counter = operand_address
|
|
|
|
def BVC(self, operand_address):
|
|
if not self.overflow_flag:
|
|
self.program_counter = operand_address
|
|
|
|
def BVS(self, operand_address):
|
|
if not self.overflow_flag:
|
|
self.program_counter = operand_address
|
|
|
|
# SET / CLEAR FLAGS
|
|
|
|
def CLC(self):
|
|
self.carry_flag = 0
|
|
|
|
def CLD(self):
|
|
self.decimal_mode_flag = 0
|
|
|
|
def CLI(self):
|
|
self.interrupt_disable_flag = 0
|
|
|
|
def CLV(self):
|
|
self.overflow_flag = 0
|
|
|
|
def SEC(self):
|
|
self.carry_flag = 1
|
|
|
|
def SED(self):
|
|
self.decimal_mode_flag = 1
|
|
|
|
def SEI(self):
|
|
self.interrupt_disable_flag = 1
|
|
|
|
# INCREMENT / DECREMENT
|
|
|
|
def DEC(self, operand_address):
|
|
self.memory.write_byte(operand_address, self.update_nz(self.memory.read_byte(operand_address) - 1))
|
|
|
|
def DEX(self):
|
|
self.x_index = self.update_nz(self.x_index - 1)
|
|
|
|
def DEY(self):
|
|
self.y_index = self.update_nz(self.y_index - 1)
|
|
|
|
def INC(self, operand_address):
|
|
self.memory.write_byte(operand_address, self.update_nz(self.memory.read_byte(operand_address) + 1))
|
|
|
|
def INX(self):
|
|
self.x_index = self.update_nz(self.x_index + 1)
|
|
|
|
def INY(self):
|
|
self.y_index = self.update_nz(self.y_index + 1)
|
|
|
|
# PUSH / PULL
|
|
|
|
def PHA(self):
|
|
s = self.STACK_PAGE + self.stack_pointer
|
|
self.stack_pointer -= 1
|
|
self.memory.write_byte(s, self.accumulator)
|
|
|
|
def PHP(self):
|
|
status = self.carry_flag | self.zero_flag << 1 | self.interrupt_disable_flag << 2 | self.decimal_mode_flag << 3 | self.break_flag << 4 | 1 << 5 | self.overflow_flag << 6 | self.sign_flag << 7
|
|
|
|
s = self.STACK_PAGE + self.stack_pointer
|
|
self.stack_pointer = (self.stack_pointer - 1) % 0x100
|
|
self.memory.write_byte(s, status)
|
|
|
|
def PLA(self):
|
|
self.stack_pointer += 1
|
|
self.accumulator = self.update_nz(self.memory.read_byte(self.STACK_PAGE + self.stack_pointer))
|
|
|
|
def PLP(self):
|
|
self.stack_pointer = (self.stack_pointer + 1) % 0x100
|
|
s = self.STACK_PAGE + self.stack_pointer
|
|
status = self.memory.read_byte(s)
|
|
self.carry_flag = 0 != status & 1
|
|
self.zero_flag = 0 != status & 2
|
|
self.interrupt_disable_flag = 0 != status & 4
|
|
self.decimal_mode_flag = 0 != status & 8
|
|
self.break_flag = 0 != status & 16
|
|
self.overflow_flag = 0 != status & 64
|
|
self.sign_flag = 0 != status & 128
|
|
|
|
# LOGIC
|
|
|
|
def AND(self, operand_address):
|
|
self.accumulator = self.update_nz(self.accumulator & self.memory.read_byte(operand_address))
|
|
|
|
def ORA(self, operand_address):
|
|
self.accumulator = self.update_nz(self.accumulator | self.memory.read_byte(operand_address))
|
|
|
|
def EOR(self, operand_address):
|
|
self.accumulator = self.update_nz(self.accumulator ^ self.memory.read_byte(operand_address))
|
|
|
|
# ARITHMETIC
|
|
|
|
def ADC(self, operand_address):
|
|
# @@@ doesn't handle BCD yet
|
|
assert not self.decimal_mode_flag
|
|
a1 = self.accumulator
|
|
a2 = self.memory.read_byte(operand_address)
|
|
result = a1 + a2 + self.carry_flag
|
|
self.accumulator = self.update_nzc(result)
|
|
self.overflow_flag = self.carry_flag ^ self.sign_flag
|
|
|
|
def SBC(self, operand_address):
|
|
# @@@ doesn't handle BCD yet
|
|
assert not self.decimal_mode_flag
|
|
s1 = self.accumulator
|
|
s2 = self.memory.read_byte(operand_address)
|
|
result = s1 - s2
|
|
if not self.carry_flag:
|
|
result = result - 1
|
|
self.accumulator = self.update_nz(result) # @@@ carry flag?
|
|
self.overflow_flag = self.carry_flag ^ self.sign_flag
|
|
|
|
# BIT
|
|
|
|
def BIT(self, operand_address):
|
|
value = self.memory.read_byte(operand_address)
|
|
if value > 0x7F:
|
|
self.sign_flag = 1
|
|
else:
|
|
self.sign_flag = 0
|
|
self.overflow_flag = ((value >> 6) % 2) # bit 6
|
|
self.zero_flag = ((self.accumulator & value) == 0) # @@@ is this right?
|
|
|
|
# COMPARISON
|
|
|
|
def CMP(self, operand_address):
|
|
value = self.memory.read_byte(operand_address)
|
|
self.carry_flag = (self.accumulator >= value)
|
|
self.zero_flag = (self.accumulator == value)
|
|
self.sign_flag = (self.accumulator < 0x80) # @@@ is this right?
|
|
|
|
def CPX(self, operand_address):
|
|
value = self.memory.read_byte(operand_address)
|
|
self.carry_flag = (self.x_index >= value)
|
|
self.zero_flag = (self.x_index == value)
|
|
self.sign_flag = (self.x_index < 0x80) # TODO: is this right?
|
|
|
|
def CPY(self, operand_address):
|
|
value = self.memory.read_byte(operand_address)
|
|
self.carry_flag = (self.y_index >= value)
|
|
self.zero_flag = (self.y_index == value)
|
|
self.sign_flag = (self.y_index < 0x80) # @@@ is this right?
|
|
|
|
# BRK
|
|
# RTI
|
|
|
|
# @@@ IRQ
|
|
# @@@ NMI
|
|
|
|
mem = Memory(0x100000)
|
|
|
|
# available from http://www.easy68k.com/paulrsm/6502/index.html
|
|
mem.load("A2ROM.BIN", 0xD000)
|
|
|
|
cpu = CPU(mem)
|
|
curses.wrapper(cpu.run)
|