mirror of
https://github.com/jtauber/applepy.git
synced 2024-09-27 19:57:56 +00:00
Merge branch 'cycles'
This commit is contained in:
commit
96787a2667
210
applepy.py
210
applepy.py
@ -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:
|
||||||
@ -341,6 +369,13 @@ class Memory:
|
|||||||
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:
|
||||||
def __init__(self, cpu, memory):
|
def __init__(self, cpu, memory):
|
||||||
@ -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
138
cycle_notes.txt
Normal 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)
|
52
tests.py
52
tests.py
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user