Merge branch 'cycles'

This commit is contained in:
James Tauber 2011-08-15 02:23:25 -04:00
commit 96787a2667
3 changed files with 320 additions and 80 deletions

View File

@ -3,6 +3,7 @@
# originally written 2001, updated 2011 # originally written 2001, updated 2011
import numpy
import pygame import pygame
import colorsys import colorsys
@ -238,6 +239,30 @@ class Display:
del pixels del pixels
class Speaker:
CPU_CYCLES_PER_SAMPLE = 70
CHECK_INTERVAL = 1000
def __init__(self):
pygame.mixer.pre_init(44100, -16, 1)
pygame.init()
self.reset()
def toggle(self, cycle):
if self.last_toggle is not None:
l = (cycle - self.last_toggle) / Speaker.CPU_CYCLES_PER_SAMPLE
self.buffer.extend([0, 0.8] if self.polarity else [0, -0.8])
self.buffer.extend(l * [0.5] if self.polarity else [-0.5])
self.polarity = not self.polarity
self.last_toggle = cycle
def reset(self):
self.last_toggle = None
self.buffer = []
self.polarity = False
class ROM: class ROM:
def __init__(self, start, size): def __init__(self, start, size):
@ -267,18 +292,20 @@ class RAM(ROM):
class SoftSwitches: class SoftSwitches:
def __init__(self, display): def __init__(self, display, speaker):
self.kbd = 0x00 self.kbd = 0x00
self.display = display self.display = display
self.speaker = speaker
def read_byte(self, address): def read_byte(self, cycle, address):
assert 0xC000 <= address <= 0xCFFF assert 0xC000 <= address <= 0xCFFF
if address == 0xC000: if address == 0xC000:
return self.kbd return self.kbd
elif address == 0xC010: elif address == 0xC010:
self.kbd = self.kbd & 0x7F self.kbd = self.kbd & 0x7F
elif address == 0xC030: elif address == 0xC030:
pass # toggle speaker if self.speaker:
self.speaker.toggle(cycle)
elif address == 0xC050: elif address == 0xC050:
self.display.txtclr() self.display.txtclr()
elif address == 0xC051: elif address == 0xC051:
@ -302,36 +329,37 @@ class SoftSwitches:
class Memory: class Memory:
def __init__(self, display=None): def __init__(self, display=None, speaker=None):
self.display = display self.display = display
self.speaker = speaker
self.rom = ROM(0xD000, 0x3000) self.rom = ROM(0xD000, 0x3000)
# available from http://www.easy68k.com/paulrsm/6502/index.html # available from http://www.easy68k.com/paulrsm/6502/index.html
self.rom.load_file(0xD000, "A2ROM.BIN") self.rom.load_file(0xD000, "A2ROM.BIN")
self.ram = RAM(0x0000, 0xC000) self.ram = RAM(0x0000, 0xC000)
self.softswitches = SoftSwitches(display) self.softswitches = SoftSwitches(display, speaker)
def load(self, address, data): def load(self, address, data):
if address < 0xC000: if address < 0xC000:
self.ram.load(address, data) self.ram.load(address, data)
def read_byte(self, address): def read_byte(self, cycle, address):
if address < 0xC000: if address < 0xC000:
return self.ram.read_byte(address) return self.ram.read_byte(address)
elif address < 0xD000: elif address < 0xD000:
return self.softswitches.read_byte(address) return self.softswitches.read_byte(cycle, address)
else: else:
return self.rom.read_byte(address) return self.rom.read_byte(address)
def read_word(self, address): def read_word(self, cycle, address):
return self.read_byte(address) + (self.read_byte(address + 1) << 8) return self.read_byte(cycle, address) + (self.read_byte(cycle + 1, address + 1) << 8)
def read_word_bug(self, address): def read_word_bug(self, cycle, address):
if address % 0x100 == 0xFF: if address % 0x100 == 0xFF:
return self.read_byte(address) + (self.read_byte(address & 0xFF00) << 8) return self.read_byte(cycle, address) + (self.read_byte(cycle + 1, address & 0xFF00) << 8)
else: else:
return self.read_word(address) return self.read_word(cycle, address)
def write_byte(self, address, value): def write_byte(self, address, value):
if address < 0xC000: if address < 0xC000:
@ -340,6 +368,13 @@ class Memory:
self.display.update(address, value) self.display.update(address, value)
if 0x2000 <= address < 0x5FFF and self.display: if 0x2000 <= address < 0x5FFF and self.display:
self.display.update(address, value) self.display.update(address, value)
def update(self, cycle):
if self.speaker.buffer and (cycle - self.speaker.last_toggle) > self.speaker.CHECK_INTERVAL:
sample_array = numpy.array(self.speaker.buffer)
sound = pygame.sndarray.make_sound(sample_array)
sound.play()
self.speaker.reset()
class Disassemble: class Disassemble:
@ -583,6 +618,8 @@ class CPU:
self.stack_pointer = 0xFF self.stack_pointer = 0xFF
self.cycles = 0
self.setup_ops() self.setup_ops()
self.reset() self.reset()
@ -604,7 +641,7 @@ class CPU:
self.ops[0x18] = lambda: self.CLC() self.ops[0x18] = lambda: self.CLC()
self.ops[0x19] = lambda: self.ORA(self.absolute_y_mode()) self.ops[0x19] = lambda: self.ORA(self.absolute_y_mode())
self.ops[0x1D] = lambda: self.ORA(self.absolute_x_mode()) self.ops[0x1D] = lambda: self.ORA(self.absolute_x_mode())
self.ops[0x1E] = lambda: self.ASL(self.absolute_x_mode()) self.ops[0x1E] = lambda: self.ASL(self.absolute_x_mode(rmw=True))
self.ops[0x20] = lambda: self.JSR(self.absolute_mode()) self.ops[0x20] = lambda: self.JSR(self.absolute_mode())
self.ops[0x21] = lambda: self.AND(self.indirect_x_mode()) self.ops[0x21] = lambda: self.AND(self.indirect_x_mode())
self.ops[0x24] = lambda: self.BIT(self.zero_page_mode()) self.ops[0x24] = lambda: self.BIT(self.zero_page_mode())
@ -623,7 +660,7 @@ class CPU:
self.ops[0x38] = lambda: self.SEC() self.ops[0x38] = lambda: self.SEC()
self.ops[0x39] = lambda: self.AND(self.absolute_y_mode()) self.ops[0x39] = lambda: self.AND(self.absolute_y_mode())
self.ops[0x3D] = lambda: self.AND(self.absolute_x_mode()) self.ops[0x3D] = lambda: self.AND(self.absolute_x_mode())
self.ops[0x3E] = lambda: self.ROL(self.absolute_x_mode()) self.ops[0x3E] = lambda: self.ROL(self.absolute_x_mode(rmw=True))
self.ops[0x40] = lambda: self.RTI() self.ops[0x40] = lambda: self.RTI()
self.ops[0x41] = lambda: self.EOR(self.indirect_x_mode()) self.ops[0x41] = lambda: self.EOR(self.indirect_x_mode())
self.ops[0x45] = lambda: self.EOR(self.zero_page_mode()) self.ops[0x45] = lambda: self.EOR(self.zero_page_mode())
@ -641,7 +678,7 @@ class CPU:
self.ops[0x58] = lambda: self.CLI() self.ops[0x58] = lambda: self.CLI()
self.ops[0x59] = lambda: self.EOR(self.absolute_y_mode()) self.ops[0x59] = lambda: self.EOR(self.absolute_y_mode())
self.ops[0x5D] = lambda: self.EOR(self.absolute_x_mode()) self.ops[0x5D] = lambda: self.EOR(self.absolute_x_mode())
self.ops[0x5E] = lambda: self.LSR(self.absolute_x_mode()) self.ops[0x5E] = lambda: self.LSR(self.absolute_x_mode(rmw=True))
self.ops[0x60] = lambda: self.RTS() self.ops[0x60] = lambda: self.RTS()
self.ops[0x61] = lambda: self.ADC(self.indirect_x_mode()) self.ops[0x61] = lambda: self.ADC(self.indirect_x_mode())
self.ops[0x65] = lambda: self.ADC(self.zero_page_mode()) self.ops[0x65] = lambda: self.ADC(self.zero_page_mode())
@ -659,7 +696,7 @@ class CPU:
self.ops[0x78] = lambda: self.SEI() self.ops[0x78] = lambda: self.SEI()
self.ops[0x79] = lambda: self.ADC(self.absolute_y_mode()) self.ops[0x79] = lambda: self.ADC(self.absolute_y_mode())
self.ops[0x7D] = lambda: self.ADC(self.absolute_x_mode()) self.ops[0x7D] = lambda: self.ADC(self.absolute_x_mode())
self.ops[0x7E] = lambda: self.ROR(self.absolute_x_mode()) self.ops[0x7E] = lambda: self.ROR(self.absolute_x_mode(rmw=True))
self.ops[0x81] = lambda: self.STA(self.indirect_x_mode()) self.ops[0x81] = lambda: self.STA(self.indirect_x_mode())
self.ops[0x84] = lambda: self.STY(self.zero_page_mode()) self.ops[0x84] = lambda: self.STY(self.zero_page_mode())
self.ops[0x85] = lambda: self.STA(self.zero_page_mode()) self.ops[0x85] = lambda: self.STA(self.zero_page_mode())
@ -670,14 +707,14 @@ class CPU:
self.ops[0x8D] = lambda: self.STA(self.absolute_mode()) self.ops[0x8D] = lambda: self.STA(self.absolute_mode())
self.ops[0x8E] = lambda: self.STX(self.absolute_mode()) self.ops[0x8E] = lambda: self.STX(self.absolute_mode())
self.ops[0x90] = lambda: self.BCC(self.relative_mode()) self.ops[0x90] = lambda: self.BCC(self.relative_mode())
self.ops[0x91] = lambda: self.STA(self.indirect_y_mode()) self.ops[0x91] = lambda: self.STA(self.indirect_y_mode(rmw=True))
self.ops[0x94] = lambda: self.STY(self.zero_page_x_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[0x95] = lambda: self.STA(self.zero_page_x_mode())
self.ops[0x96] = lambda: self.STX(self.zero_page_y_mode()) self.ops[0x96] = lambda: self.STX(self.zero_page_y_mode())
self.ops[0x98] = lambda: self.TYA() self.ops[0x98] = lambda: self.TYA()
self.ops[0x99] = lambda: self.STA(self.absolute_y_mode()) self.ops[0x99] = lambda: self.STA(self.absolute_y_mode(rmw=True))
self.ops[0x9A] = lambda: self.TXS() self.ops[0x9A] = lambda: self.TXS()
self.ops[0x9D] = lambda: self.STA(self.absolute_x_mode()) self.ops[0x9D] = lambda: self.STA(self.absolute_x_mode(rmw=True))
self.ops[0xA0] = lambda: self.LDY(self.immediate_mode()) self.ops[0xA0] = lambda: self.LDY(self.immediate_mode())
self.ops[0xA1] = lambda: self.LDA(self.indirect_x_mode()) self.ops[0xA1] = lambda: self.LDA(self.indirect_x_mode())
self.ops[0xA2] = lambda: self.LDX(self.immediate_mode()) self.ops[0xA2] = lambda: self.LDX(self.immediate_mode())
@ -719,7 +756,7 @@ class CPU:
self.ops[0xD8] = lambda: self.CLD() self.ops[0xD8] = lambda: self.CLD()
self.ops[0xD9] = lambda: self.CMP(self.absolute_y_mode()) self.ops[0xD9] = lambda: self.CMP(self.absolute_y_mode())
self.ops[0xDD] = lambda: self.CMP(self.absolute_x_mode()) self.ops[0xDD] = lambda: self.CMP(self.absolute_x_mode())
self.ops[0xDE] = lambda: self.DEC(self.absolute_x_mode()) self.ops[0xDE] = lambda: self.DEC(self.absolute_x_mode(rmw=True))
self.ops[0xE0] = lambda: self.CPX(self.immediate_mode()) self.ops[0xE0] = lambda: self.CPX(self.immediate_mode())
self.ops[0xE1] = lambda: self.SBC(self.indirect_x_mode()) self.ops[0xE1] = lambda: self.SBC(self.indirect_x_mode())
self.ops[0xE4] = lambda: self.CPX(self.zero_page_mode()) self.ops[0xE4] = lambda: self.CPX(self.zero_page_mode())
@ -738,15 +775,16 @@ class CPU:
self.ops[0xF8] = lambda: self.SED() self.ops[0xF8] = lambda: self.SED()
self.ops[0xF9] = lambda: self.SBC(self.absolute_y_mode()) self.ops[0xF9] = lambda: self.SBC(self.absolute_y_mode())
self.ops[0xFD] = lambda: self.SBC(self.absolute_x_mode()) self.ops[0xFD] = lambda: self.SBC(self.absolute_x_mode())
self.ops[0xFE] = lambda: self.INC(self.absolute_x_mode()) self.ops[0xFE] = lambda: self.INC(self.absolute_x_mode(rmw=True))
def reset(self): def reset(self):
self.program_counter = self.memory.read_word(self.RESET_VECTOR) self.program_counter = self.read_word(self.RESET_VECTOR)
def run(self): def run(self):
update_cycle = 0 update_cycle = 0
quit = False quit = False
while not quit: while not quit:
self.cycles += 2 # all instructions take this as a minimum
op = self.read_pc_byte() op = self.read_pc_byte()
func = self.ops[op] func = self.ops[op]
if func is None: if func is None:
@ -771,8 +809,25 @@ class CPU:
update_cycle += 1 update_cycle += 1
if update_cycle >= 1024: if update_cycle >= 1024:
pygame.display.flip() pygame.display.flip()
self.memory.update(self.cycles)
update_cycle = 0 update_cycle = 0
def test_run(self, start, end):
self.program_counter = start
while True:
self.cycles += 2 # all instructions take this as a minimum
if self.program_counter == end:
break
op = self.read_pc_byte()
func = self.ops[op]
if func is None:
print "UNKNOWN OP"
print hex(self.program_counter - 1)
print hex(op)
break
else:
self.ops[op]()
#### ####
def get_pc(self, inc=1): def get_pc(self, inc=1):
@ -780,11 +835,20 @@ class CPU:
self.program_counter += inc self.program_counter += inc
return pc return pc
def read_byte(self, address):
return self.memory.read_byte(self.cycles, address)
def read_word(self, address):
return self.memory.read_word(self.cycles, address)
def read_word_bug(self, address):
return self.memory.read_word_bug(self.cycles, address)
def read_pc_byte(self): def read_pc_byte(self):
return self.memory.read_byte(self.get_pc()) return self.read_byte(self.get_pc())
def read_pc_word(self): def read_pc_word(self):
return self.memory.read_word(self.get_pc(2)) return self.read_word(self.get_pc(2))
#### ####
@ -808,7 +872,7 @@ class CPU:
def pull_byte(self): def pull_byte(self):
self.stack_pointer = (self.stack_pointer + 1) % 0x100 self.stack_pointer = (self.stack_pointer + 1) % 0x100
return self.memory.read_byte(self.STACK_PAGE + self.stack_pointer) return self.read_byte(self.STACK_PAGE + self.stack_pointer)
def push_word(self, word): def push_word(self, word):
hi, lo = divmod(word, 0x100) hi, lo = divmod(word, 0x100)
@ -818,7 +882,7 @@ class CPU:
def pull_word(self): def pull_word(self):
s = self.STACK_PAGE + self.stack_pointer + 1 s = self.STACK_PAGE + self.stack_pointer + 1
self.stack_pointer += 2 self.stack_pointer += 2
return self.memory.read_word(s) return self.read_word(s)
#### ####
@ -826,35 +890,49 @@ class CPU:
return self.get_pc() return self.get_pc()
def absolute_mode(self): def absolute_mode(self):
self.cycles += 2
return self.read_pc_word() return self.read_pc_word()
def absolute_x_mode(self): def absolute_x_mode(self, rmw=False):
if rmw:
self.cycles += 1
return self.absolute_mode() + self.x_index return self.absolute_mode() + self.x_index
def absolute_y_mode(self): def absolute_y_mode(self, rmw=False):
if rmw:
self.cycles += 1
return self.absolute_mode() + self.y_index return self.absolute_mode() + self.y_index
def zero_page_mode(self): def zero_page_mode(self):
self.cycles += 1
return self.read_pc_byte() return self.read_pc_byte()
def zero_page_x_mode(self): def zero_page_x_mode(self):
self.cycles += 1
return (self.zero_page_mode() + self.x_index) % 0x100 return (self.zero_page_mode() + self.x_index) % 0x100
def zero_page_y_mode(self): def zero_page_y_mode(self):
self.cycles += 1
return (self.zero_page_mode() + self.y_index) % 0x100 return (self.zero_page_mode() + self.y_index) % 0x100
def indirect_mode(self): def indirect_mode(self):
return self.memory.read_word_bug(self.absolute_mode()) self.cycles += 2
return self.read_word_bug(self.absolute_mode())
def indirect_x_mode(self): def indirect_x_mode(self):
return self.memory.read_word_bug((self.read_pc_byte() + self.x_index) % 0x100) self.cycles += 4
return self.read_word_bug((self.read_pc_byte() + self.x_index) % 0x100)
def indirect_y_mode(self): def indirect_y_mode(self, rmw=False):
return self.memory.read_word_bug(self.read_pc_byte()) + self.y_index if rmw:
self.cycles += 4
else:
self.cycles += 3
return self.read_word_bug(self.read_pc_byte()) + self.y_index
def relative_mode(self): def relative_mode(self):
pc = self.get_pc() pc = self.get_pc()
return pc + 1 + signed(self.memory.read_byte(pc)) return pc + 1 + signed(self.read_byte(pc))
#### ####
@ -873,13 +951,13 @@ class CPU:
# LOAD / STORE # LOAD / STORE
def LDA(self, operand_address): def LDA(self, operand_address):
self.accumulator = self.update_nz(self.memory.read_byte(operand_address)) self.accumulator = self.update_nz(self.read_byte(operand_address))
def LDX(self, operand_address): def LDX(self, operand_address):
self.x_index = self.update_nz(self.memory.read_byte(operand_address)) self.x_index = self.update_nz(self.read_byte(operand_address))
def LDY(self, operand_address): def LDY(self, operand_address):
self.y_index = self.update_nz(self.memory.read_byte(operand_address)) self.y_index = self.update_nz(self.read_byte(operand_address))
def STA(self, operand_address): def STA(self, operand_address):
self.memory.write_byte(operand_address, self.accumulator) self.memory.write_byte(operand_address, self.accumulator)
@ -916,7 +994,8 @@ class CPU:
if operand_address is None: if operand_address is None:
self.accumulator = self.update_nzc(self.accumulator << 1) self.accumulator = self.update_nzc(self.accumulator << 1)
else: else:
self.memory.write_byte(operand_address, self.update_nzc(self.memory.read_byte(operand_address) << 1)) self.cycles += 2
self.memory.write_byte(operand_address, self.update_nzc(self.read_byte(operand_address) << 1))
def ROL(self, operand_address=None): def ROL(self, operand_address=None):
if operand_address is None: if operand_address is None:
@ -925,7 +1004,8 @@ class CPU:
a = a | 0x01 a = a | 0x01
self.accumulator = self.update_nzc(a) self.accumulator = self.update_nzc(a)
else: else:
m = self.memory.read_byte(operand_address) << 1 self.cycles += 2
m = self.read_byte(operand_address) << 1
if self.carry_flag: if self.carry_flag:
m = m | 0x01 m = m | 0x01
self.memory.write_byte(operand_address, self.update_nzc(m)) self.memory.write_byte(operand_address, self.update_nzc(m))
@ -937,7 +1017,8 @@ class CPU:
self.carry_flag = self.accumulator % 2 self.carry_flag = self.accumulator % 2
self.accumulator = self.update_nz(self.accumulator >> 1) self.accumulator = self.update_nz(self.accumulator >> 1)
else: else:
m = self.memory.read_byte(operand_address) self.cycles += 2
m = self.read_byte(operand_address)
if self.carry_flag: if self.carry_flag:
m = m | 0x100 m = m | 0x100
self.carry_flag = m % 2 self.carry_flag = m % 2
@ -948,53 +1029,65 @@ class CPU:
self.carry_flag = self.accumulator % 2 self.carry_flag = self.accumulator % 2
self.accumulator = self.update_nz(self.accumulator >> 1) self.accumulator = self.update_nz(self.accumulator >> 1)
else: else:
self.carry_flag = self.memory.read_byte(operand_address) % 2 self.cycles += 2
self.memory.write_byte(operand_address, self.update_nz(self.memory.read_byte(operand_address) >> 1)) self.carry_flag = self.read_byte(operand_address) % 2
self.memory.write_byte(operand_address, self.update_nz(self.read_byte(operand_address) >> 1))
# JUMPS / RETURNS # JUMPS / RETURNS
def JMP(self, operand_address): def JMP(self, operand_address):
self.cycles -= 1
self.program_counter = operand_address self.program_counter = operand_address
def JSR(self, operand_address): def JSR(self, operand_address):
self.cycles += 2
self.push_word(self.program_counter - 1) self.push_word(self.program_counter - 1)
self.program_counter = operand_address self.program_counter = operand_address
def RTS(self): def RTS(self):
self.cycles += 4
self.program_counter = self.pull_word() + 1 self.program_counter = self.pull_word() + 1
# BRANCHES # BRANCHES
def BCC(self, operand_address): def BCC(self, operand_address):
if not self.carry_flag: if not self.carry_flag:
self.cycles += 1
self.program_counter = operand_address self.program_counter = operand_address
def BCS(self, operand_address): def BCS(self, operand_address):
if self.carry_flag: if self.carry_flag:
self.cycles += 1
self.program_counter = operand_address self.program_counter = operand_address
def BEQ(self, operand_address): def BEQ(self, operand_address):
if self.zero_flag: if self.zero_flag:
self.cycles += 1
self.program_counter = operand_address self.program_counter = operand_address
def BNE(self, operand_address): def BNE(self, operand_address):
if not self.zero_flag: if not self.zero_flag:
self.cycles += 1
self.program_counter = operand_address self.program_counter = operand_address
def BMI(self, operand_address): def BMI(self, operand_address):
if self.sign_flag: if self.sign_flag:
self.cycles += 1
self.program_counter = operand_address self.program_counter = operand_address
def BPL(self, operand_address): def BPL(self, operand_address):
if not self.sign_flag: if not self.sign_flag:
self.cycles += 1
self.program_counter = operand_address self.program_counter = operand_address
def BVC(self, operand_address): def BVC(self, operand_address):
if not self.overflow_flag: if not self.overflow_flag:
self.cycles += 1
self.program_counter = operand_address self.program_counter = operand_address
def BVS(self, operand_address): def BVS(self, operand_address):
if self.overflow_flag: if self.overflow_flag:
self.cycles += 1
self.program_counter = operand_address self.program_counter = operand_address
# SET / CLEAR FLAGS # SET / CLEAR FLAGS
@ -1023,7 +1116,8 @@ class CPU:
# INCREMENT / DECREMENT # INCREMENT / DECREMENT
def DEC(self, operand_address): def DEC(self, operand_address):
self.memory.write_byte(operand_address, self.update_nz(self.memory.read_byte(operand_address) - 1)) self.cycles += 2
self.memory.write_byte(operand_address, self.update_nz(self.read_byte(operand_address) - 1))
def DEX(self): def DEX(self):
self.x_index = self.update_nz(self.x_index - 1) self.x_index = self.update_nz(self.x_index - 1)
@ -1032,7 +1126,8 @@ class CPU:
self.y_index = self.update_nz(self.y_index - 1) self.y_index = self.update_nz(self.y_index - 1)
def INC(self, operand_address): def INC(self, operand_address):
self.memory.write_byte(operand_address, self.update_nz(self.memory.read_byte(operand_address) + 1)) self.cycles += 2
self.memory.write_byte(operand_address, self.update_nz(self.read_byte(operand_address) + 1))
def INX(self): def INX(self):
self.x_index = self.update_nz(self.x_index + 1) self.x_index = self.update_nz(self.x_index + 1)
@ -1043,27 +1138,31 @@ class CPU:
# PUSH / PULL # PUSH / PULL
def PHA(self): def PHA(self):
self.cycles += 1
self.push_byte(self.accumulator) self.push_byte(self.accumulator)
def PHP(self): def PHP(self):
self.cycles += 1
self.push_byte(self.status_as_byte()) self.push_byte(self.status_as_byte())
def PLA(self): def PLA(self):
self.cycles += 2
self.accumulator = self.update_nz(self.pull_byte()) self.accumulator = self.update_nz(self.pull_byte())
def PLP(self): def PLP(self):
self.cycles += 2
self.status_from_byte(self.pull_byte()) self.status_from_byte(self.pull_byte())
# LOGIC # LOGIC
def AND(self, operand_address): def AND(self, operand_address):
self.accumulator = self.update_nz(self.accumulator & self.memory.read_byte(operand_address)) self.accumulator = self.update_nz(self.accumulator & self.read_byte(operand_address))
def ORA(self, operand_address): def ORA(self, operand_address):
self.accumulator = self.update_nz(self.accumulator | self.memory.read_byte(operand_address)) self.accumulator = self.update_nz(self.accumulator | self.read_byte(operand_address))
def EOR(self, operand_address): def EOR(self, operand_address):
self.accumulator = self.update_nz(self.accumulator ^ self.memory.read_byte(operand_address)) self.accumulator = self.update_nz(self.accumulator ^ self.read_byte(operand_address))
# ARITHMETIC # ARITHMETIC
@ -1073,7 +1172,7 @@ class CPU:
a2 = self.accumulator a2 = self.accumulator
a1 = signed(a2) a1 = signed(a2)
m2 = self.memory.read_byte(operand_address) m2 = self.read_byte(operand_address)
m1 = signed(m2) m1 = signed(m2)
# twos complement addition # twos complement addition
@ -1093,7 +1192,7 @@ class CPU:
a2 = self.accumulator a2 = self.accumulator
a1 = signed(a2) a1 = signed(a2)
m2 = self.memory.read_byte(operand_address) m2 = self.read_byte(operand_address)
m1 = signed(m2) m1 = signed(m2)
# twos complement subtraction # twos complement subtraction
@ -1111,7 +1210,7 @@ class CPU:
# BIT # BIT
def BIT(self, operand_address): def BIT(self, operand_address):
value = self.memory.read_byte(operand_address) value = self.read_byte(operand_address)
self.sign_flag = ((value >> 7) % 2) # bit 7 self.sign_flag = ((value >> 7) % 2) # bit 7
self.overflow_flag = ((value >> 6) % 2) # bit 6 self.overflow_flag = ((value >> 6) % 2) # bit 6
self.zero_flag = [0, 1][((self.accumulator & value) == 0)] self.zero_flag = [0, 1][((self.accumulator & value) == 0)]
@ -1119,17 +1218,17 @@ class CPU:
# COMPARISON # COMPARISON
def CMP(self, operand_address): def CMP(self, operand_address):
result = self.accumulator - self.memory.read_byte(operand_address) result = self.accumulator - self.read_byte(operand_address)
self.carry_flag = [0, 1][(result >= 0)] self.carry_flag = [0, 1][(result >= 0)]
self.update_nz(result) self.update_nz(result)
def CPX(self, operand_address): def CPX(self, operand_address):
result = self.x_index - self.memory.read_byte(operand_address) result = self.x_index - self.read_byte(operand_address)
self.carry_flag = [0, 1][(result >= 0)] self.carry_flag = [0, 1][(result >= 0)]
self.update_nz(result) self.update_nz(result)
def CPY(self, operand_address): def CPY(self, operand_address):
result = self.y_index - self.memory.read_byte(operand_address) result = self.y_index - self.read_byte(operand_address)
self.carry_flag = [0, 1][(result >= 0)] self.carry_flag = [0, 1][(result >= 0)]
self.update_nz(result) self.update_nz(result)
@ -1139,12 +1238,14 @@ class CPU:
pass pass
def BRK(self): def BRK(self):
self.cycles += 5
self.push_word(self.program_counter + 1) self.push_word(self.program_counter + 1)
self.push_byte(self.status_as_byte()) self.push_byte(self.status_as_byte())
self.program_counter = self.memory.read_word(0xFFFE) self.program_counter = self.read_word(0xFFFE)
self.break_flag = 1 self.break_flag = 1
def RTI(self): def RTI(self):
self.cycles += 4
self.status_from_byte(self.pull_byte()) self.status_from_byte(self.pull_byte())
self.program_counter = self.pull_word() self.program_counter = self.pull_word()
@ -1155,7 +1256,8 @@ class CPU:
if __name__ == "__main__": if __name__ == "__main__":
display = Display() display = Display()
mem = Memory(display) speaker = Speaker()
mem = Memory(display, speaker)
cpu = CPU(mem) cpu = CPU(mem)
cpu.run() cpu.run()

138
cycle_notes.txt Normal file
View File

@ -0,0 +1,138 @@
There are two ways we could represent cycle information:
1. just have an array of cycles for each opcode + the adjustment for
page boundary crossing (which seems opcode-specific, oddly)
2. add cycles in individual methods like those accessing memory and the
operations themselves, to model *why* something takes the cycles it does.
I prefer 2 on the grounds of it being more instructive but it assumes that
the way we do things is closely aligned to the way the 6502 is doing them
internally. Even if we end up having to do 1, I'd love to understand and
document some of the "why".
What follows is an attempt to "find the patterns" in the cycle times (as
given on http://www.6502.org/tutorials/6502opcodes.html )
NOTE: there appears to be an error in AND and ORA zero page timings on that
webpage given above. I've now corrected this below.
There are 10 classes of instructions when it comes to cycle times:
Class I
(followed by ADC, AND, BIT, CMP, CPX, CPY, EOR, LDA, LDX, LDY, ORA, SBC, STA,
STX, STY)
immediate 2
zero page 3
zero page, x 4
zero page, y 4
absolute 4
absolute, x 4 (+1 if page crossed or writing)
absolute, y 4 (+1 if page crossed or writing)
indirect, x 6
indirect, y 5 (+1 if page crossed or writing)
Note 1: the zero page indexed and x-index indirect don't have the page cross
addition because they wrap.
Note 2: writes to indexed non-zero-page memory (e.g. STA) have the +1 even
if not page crossing.
Class II
(followed by ASL, DEC, INC, LSR, ROL, ROR)
implied 2
zero page 5
zero page, x 6
absolute 6
absolute, x 7
Note 3: these take 2 cycles longer than Class I because they involve a
read-modify-write. Because the absolute, x is a write to an indexed
non-zero-page memory location, there is an additional +1 even if not page
crossing (see Note 2)
Class IIIa
(followed by CLC, CLD, CLI, CLV, DEX, DEY, INX, INY, NOP, SEC, SED, SEI, TAX,
TAY, TSX, TXA, TXS, TYA)
implied 2
Class IIIb
(followed by PHA, PHP)
implied 3
Class IIIc
(followed by PLA, PLP)
implied 4
Class IIId
(followed by RTI, RTS)
implied 6
Class IIIe
(followed by BRK)
implied 7
Class IV
(followed by BCC, BCS, BEQ, BMI, BNE, BPL, BVC, BVS)
branch not taken 2
branch taken 3 (+1 if page crossed)
Class V
(followed by JMP)
absolute 3
indirect 5
Class VI
(followed by JSR)
absolute 6
This seems a possible implementation (not yet considering page boundaries):
1. all instructions start with 2
2. absolute (including absolute indexed) adds 2
3. absolute indexed adds an additional 1 *if* instruction is of RMW type
4. zero page (including zero page indexed) adds 1
5. zero page indexed adds an addition 1
6. indirect (JMP) adds 4
7. indirect x adds 4
8. indirect y adds 3 plus an additional 1 *if* instruction is of RMW type
9. ASL, LSR, ROL, ROR add 2 cycles if not implied
10. JMP subtracts 1 cycle
11. JSR adds 2
12. RTS adds 4
13. branches add 1 if taken
14. DEC and INC add 2 cycles
15. PHA and PHP add 1 cycle
16. PLA and PLP add 2 cycles
17. BRK adds 5 cycles
18. RTI adds 4 cycles
RMW instructions are the absolute,x of ASL, DEC, INC, LSR ROL, ROR and STA
as well as indirect,y and absolute,y of STA
It may be possible to simplify even further given the particular functions
some of these share in command (where the cycle change could be placed
instead)

View File

@ -9,17 +9,17 @@ class TestMemory(unittest.TestCase):
def test_load(self): def test_load(self):
self.memory.load(0x1000, [0x01, 0x02, 0x03]) self.memory.load(0x1000, [0x01, 0x02, 0x03])
self.assertEqual(self.memory.read_byte(0x1000), 0x01) self.assertEqual(self.memory.read_byte(None, 0x1000), 0x01)
self.assertEqual(self.memory.read_byte(0x1001), 0x02) self.assertEqual(self.memory.read_byte(None, 0x1001), 0x02)
self.assertEqual(self.memory.read_byte(0x1002), 0x03) self.assertEqual(self.memory.read_byte(None, 0x1002), 0x03)
def test_write(self): def test_write(self):
self.memory.write_byte(0x1000, 0x11) self.memory.write_byte(0x1000, 0x11)
self.memory.write_byte(0x1001, 0x12) self.memory.write_byte(0x1001, 0x12)
self.memory.write_byte(0x1002, 0x13) self.memory.write_byte(0x1002, 0x13)
self.assertEqual(self.memory.read_byte(0x1000), 0x11) self.assertEqual(self.memory.read_byte(None, 0x1000), 0x11)
self.assertEqual(self.memory.read_byte(0x1001), 0x12) self.assertEqual(self.memory.read_byte(None, 0x1001), 0x12)
self.assertEqual(self.memory.read_byte(0x1002), 0x13) self.assertEqual(self.memory.read_byte(None, 0x1002), 0x13)
class TestLoadStoreOperations(unittest.TestCase): class TestLoadStoreOperations(unittest.TestCase):
@ -98,17 +98,17 @@ class TestLoadStoreOperations(unittest.TestCase):
def test_STA(self): def test_STA(self):
self.cpu.accumulator = 0x37 self.cpu.accumulator = 0x37
self.cpu.STA(0x2000) self.cpu.STA(0x2000)
self.assertEqual(self.memory.read_byte(0x2000), 0x37) self.assertEqual(self.memory.read_byte(None, 0x2000), 0x37)
def test_STX(self): def test_STX(self):
self.cpu.x_index = 0x38 self.cpu.x_index = 0x38
self.cpu.STX(0x2000) self.cpu.STX(0x2000)
self.assertEqual(self.memory.read_byte(0x2000), 0x38) self.assertEqual(self.memory.read_byte(None, 0x2000), 0x38)
def test_STY(self): def test_STY(self):
self.cpu.y_index = 0x39 self.cpu.y_index = 0x39
self.cpu.STY(0x2000) self.cpu.STY(0x2000)
self.assertEqual(self.memory.read_byte(0x2000), 0x39) self.assertEqual(self.memory.read_byte(None, 0x2000), 0x39)
class TestRegisterTransferOperations(unittest.TestCase): class TestRegisterTransferOperations(unittest.TestCase):
@ -550,17 +550,17 @@ class TestIncrementDecrementOperations(unittest.TestCase):
def test_INC(self): def test_INC(self):
self.memory.write_byte(0x1000, 0x00) self.memory.write_byte(0x1000, 0x00)
self.cpu.INC(0x1000) self.cpu.INC(0x1000)
self.assertEqual(self.memory.read_byte(0x1000), 0x01) self.assertEqual(self.memory.read_byte(None, 0x1000), 0x01)
self.assertEqual(self.cpu.sign_flag, 0) self.assertEqual(self.cpu.sign_flag, 0)
self.assertEqual(self.cpu.zero_flag, 0) self.assertEqual(self.cpu.zero_flag, 0)
self.memory.write_byte(0x1000, 0x7F) self.memory.write_byte(0x1000, 0x7F)
self.cpu.INC(0x1000) self.cpu.INC(0x1000)
self.assertEqual(self.memory.read_byte(0x1000), 0x80) self.assertEqual(self.memory.read_byte(None, 0x1000), 0x80)
self.assertEqual(self.cpu.sign_flag, 1) self.assertEqual(self.cpu.sign_flag, 1)
self.assertEqual(self.cpu.zero_flag, 0) self.assertEqual(self.cpu.zero_flag, 0)
self.memory.write_byte(0x1000, 0xFF) self.memory.write_byte(0x1000, 0xFF)
self.cpu.INC(0x1000) self.cpu.INC(0x1000)
self.assertEqual(self.memory.read_byte(0x1000), 0x00) self.assertEqual(self.memory.read_byte(None, 0x1000), 0x00)
self.assertEqual(self.cpu.sign_flag, 0) self.assertEqual(self.cpu.sign_flag, 0)
self.assertEqual(self.cpu.zero_flag, 1) self.assertEqual(self.cpu.zero_flag, 1)
@ -601,17 +601,17 @@ class TestIncrementDecrementOperations(unittest.TestCase):
def test_DEC(self): def test_DEC(self):
self.memory.write_byte(0x1000, 0x01) self.memory.write_byte(0x1000, 0x01)
self.cpu.DEC(0x1000) self.cpu.DEC(0x1000)
self.assertEqual(self.memory.read_byte(0x1000), 0x00) self.assertEqual(self.memory.read_byte(None, 0x1000), 0x00)
self.assertEqual(self.cpu.sign_flag, 0) self.assertEqual(self.cpu.sign_flag, 0)
self.assertEqual(self.cpu.zero_flag, 1) self.assertEqual(self.cpu.zero_flag, 1)
self.memory.write_byte(0x1000, 0x80) self.memory.write_byte(0x1000, 0x80)
self.cpu.DEC(0x1000) self.cpu.DEC(0x1000)
self.assertEqual(self.memory.read_byte(0x1000), 0x7F) self.assertEqual(self.memory.read_byte(None, 0x1000), 0x7F)
self.assertEqual(self.cpu.sign_flag, 0) self.assertEqual(self.cpu.sign_flag, 0)
self.assertEqual(self.cpu.zero_flag, 0) self.assertEqual(self.cpu.zero_flag, 0)
self.memory.write_byte(0x1000, 0x00) self.memory.write_byte(0x1000, 0x00)
self.cpu.DEC(0x1000) self.cpu.DEC(0x1000)
self.assertEqual(self.memory.read_byte(0x1000), 0xFF) self.assertEqual(self.memory.read_byte(None, 0x1000), 0xFF)
self.assertEqual(self.cpu.sign_flag, 1) self.assertEqual(self.cpu.sign_flag, 1)
self.assertEqual(self.cpu.zero_flag, 0) self.assertEqual(self.cpu.zero_flag, 0)
@ -665,7 +665,7 @@ class TestShiftOperations(unittest.TestCase):
self.assertEqual(self.cpu.carry_flag, 0) self.assertEqual(self.cpu.carry_flag, 0)
self.memory.write_byte(0x1000, 0x02) self.memory.write_byte(0x1000, 0x02)
self.cpu.ASL(0x1000) self.cpu.ASL(0x1000)
self.assertEqual(self.memory.read_byte(0x1000), 0x04) self.assertEqual(self.memory.read_byte(None, 0x1000), 0x04)
self.assertEqual(self.cpu.sign_flag, 0) self.assertEqual(self.cpu.sign_flag, 0)
self.assertEqual(self.cpu.zero_flag, 0) self.assertEqual(self.cpu.zero_flag, 0)
self.assertEqual(self.cpu.carry_flag, 0) self.assertEqual(self.cpu.carry_flag, 0)
@ -685,7 +685,7 @@ class TestShiftOperations(unittest.TestCase):
self.assertEqual(self.cpu.carry_flag, 1) self.assertEqual(self.cpu.carry_flag, 1)
self.memory.write_byte(0x1000, 0x01) self.memory.write_byte(0x1000, 0x01)
self.cpu.LSR(0x1000) self.cpu.LSR(0x1000)
self.assertEqual(self.memory.read_byte(0x1000), 0x00) self.assertEqual(self.memory.read_byte(None, 0x1000), 0x00)
self.assertEqual(self.cpu.sign_flag, 0) self.assertEqual(self.cpu.sign_flag, 0)
self.assertEqual(self.cpu.zero_flag, 1) self.assertEqual(self.cpu.zero_flag, 1)
self.assertEqual(self.cpu.carry_flag, 1) self.assertEqual(self.cpu.carry_flag, 1)
@ -714,14 +714,14 @@ class TestShiftOperations(unittest.TestCase):
self.cpu.carry_flag = 0 self.cpu.carry_flag = 0
self.memory.write_byte(0x1000, 0x80) self.memory.write_byte(0x1000, 0x80)
self.cpu.ROL(0x1000) self.cpu.ROL(0x1000)
self.assertEqual(self.memory.read_byte(0x1000), 0x00) self.assertEqual(self.memory.read_byte(None, 0x1000), 0x00)
self.assertEqual(self.cpu.sign_flag, 0) self.assertEqual(self.cpu.sign_flag, 0)
self.assertEqual(self.cpu.zero_flag, 1) # @@@ self.assertEqual(self.cpu.zero_flag, 1) # @@@
self.assertEqual(self.cpu.carry_flag, 1) self.assertEqual(self.cpu.carry_flag, 1)
self.cpu.carry_flag = 1 self.cpu.carry_flag = 1
self.memory.write_byte(0x1000, 0x80) self.memory.write_byte(0x1000, 0x80)
self.cpu.ROL(0x1000) self.cpu.ROL(0x1000)
self.assertEqual(self.memory.read_byte(0x1000), 0x01) self.assertEqual(self.memory.read_byte(None, 0x1000), 0x01)
self.assertEqual(self.cpu.sign_flag, 0) self.assertEqual(self.cpu.sign_flag, 0)
self.assertEqual(self.cpu.zero_flag, 0) # @@@ self.assertEqual(self.cpu.zero_flag, 0) # @@@
self.assertEqual(self.cpu.carry_flag, 1) self.assertEqual(self.cpu.carry_flag, 1)
@ -744,14 +744,14 @@ class TestShiftOperations(unittest.TestCase):
self.cpu.carry_flag = 0 self.cpu.carry_flag = 0
self.memory.write_byte(0x1000, 0x01) self.memory.write_byte(0x1000, 0x01)
self.cpu.ROR(0x1000) self.cpu.ROR(0x1000)
self.assertEqual(self.memory.read_byte(0x1000), 0x00) self.assertEqual(self.memory.read_byte(None, 0x1000), 0x00)
self.assertEqual(self.cpu.sign_flag, 0) self.assertEqual(self.cpu.sign_flag, 0)
self.assertEqual(self.cpu.zero_flag, 1) # @@@ self.assertEqual(self.cpu.zero_flag, 1) # @@@
self.assertEqual(self.cpu.carry_flag, 1) self.assertEqual(self.cpu.carry_flag, 1)
self.cpu.carry_flag = 1 self.cpu.carry_flag = 1
self.memory.write_byte(0x1000, 0x01) self.memory.write_byte(0x1000, 0x01)
self.cpu.ROR(0x1000) self.cpu.ROR(0x1000)
self.assertEqual(self.memory.read_byte(0x1000), 0x80) self.assertEqual(self.memory.read_byte(None, 0x1000), 0x80)
self.assertEqual(self.cpu.sign_flag, 1) # @@@ self.assertEqual(self.cpu.sign_flag, 1) # @@@
self.assertEqual(self.cpu.zero_flag, 0) # @@@ self.assertEqual(self.cpu.zero_flag, 0) # @@@
self.assertEqual(self.cpu.carry_flag, 1) self.assertEqual(self.cpu.carry_flag, 1)
@ -771,8 +771,8 @@ class TestJumpCallOperations(unittest.TestCase):
self.cpu.program_counter = 0x1000 self.cpu.program_counter = 0x1000
self.cpu.JSR(0x2000) self.cpu.JSR(0x2000)
self.assertEqual(self.cpu.program_counter, 0x2000) self.assertEqual(self.cpu.program_counter, 0x2000)
self.assertEqual(self.memory.read_byte(self.cpu.STACK_PAGE + self.cpu.stack_pointer + 1), 0xFF) self.assertEqual(self.memory.read_byte(None, self.cpu.STACK_PAGE + self.cpu.stack_pointer + 1), 0xFF)
self.assertEqual(self.memory.read_byte(self.cpu.STACK_PAGE + self.cpu.stack_pointer + 2), 0x0F) self.assertEqual(self.memory.read_byte(None, self.cpu.STACK_PAGE + self.cpu.stack_pointer + 2), 0x0F)
def test_RTS(self): def test_RTS(self):
self.memory.write_byte(self.cpu.STACK_PAGE + 0xFF, 0x12) self.memory.write_byte(self.cpu.STACK_PAGE + 0xFF, 0x12)
@ -931,9 +931,9 @@ class TestSystemFunctionOperations(unittest.TestCase):
self.cpu.BRK() self.cpu.BRK()
self.assertEqual(self.cpu.program_counter, 0x2000) self.assertEqual(self.cpu.program_counter, 0x2000)
self.assertEqual(self.cpu.break_flag, 1) self.assertEqual(self.cpu.break_flag, 1)
self.assertEqual(self.memory.read_byte(self.cpu.STACK_PAGE + self.cpu.stack_pointer + 1), status) self.assertEqual(self.memory.read_byte(None, self.cpu.STACK_PAGE + self.cpu.stack_pointer + 1), status)
self.assertEqual(self.memory.read_byte(self.cpu.STACK_PAGE + self.cpu.stack_pointer + 2), 0x01) self.assertEqual(self.memory.read_byte(None, self.cpu.STACK_PAGE + self.cpu.stack_pointer + 2), 0x01)
self.assertEqual(self.memory.read_byte(self.cpu.STACK_PAGE + self.cpu.stack_pointer + 3), 0x10) self.assertEqual(self.memory.read_byte(None, self.cpu.STACK_PAGE + self.cpu.stack_pointer + 3), 0x10)
def test_RTI(self): def test_RTI(self):
self.memory.write_byte(self.cpu.STACK_PAGE + 0xFF, 0x12) self.memory.write_byte(self.cpu.STACK_PAGE + 0xFF, 0x12)