From c9c609be1d5a6409322a3e0ab68aea99f43415cd Mon Sep 17 00:00:00 2001 From: Greg Hewgill Date: Sun, 14 Aug 2011 10:29:42 +1200 Subject: [PATCH] Separate CPU core and UI processes This is a first step toward separating the CPU core and UI. The UI program starts the CPU core as a subprocess and communicates through its standard input and output. The protocol is deliberately simple at this point. Each bus request from the core is exactly eight bytes: +-------------------------+ | cpu cycle counter high | +-------------------------+ | cpu cycle counter | +-------------------------+ | cpu cycle counter | +-------------------------+ | cpu cycle counter low | +-------------------------+ | 0x00=read / 0x01=write | +-------------------------+ | address high | +-------------------------+ | address low | +-------------------------+ | value (unused for read) | +-------------------------+ A single-byte response from the UI is required for a read request, and a response must not be sent for a write request. The above protocol is expected to change. For example: - the UI should tell the CPU core which address ranges are of interest - needs ability to send memory images to the core (both ROM and RAM) The stream communications is currently buggy because it expects that all eight bytes will be read when requested (that is, partial reads are not handled). In practice, this seems to work okay for the moment. To improve portability, it may be better to communicate over TCP sockets instead of stdin/stdout. --- .gitignore | 1 + applepy.py | 975 ++----------------------------------------- applepy_curses.py | 91 ++++ cpu6502.py | 1016 +++++++++++++++++++++++++++++++++++++++++++++ tests.py | 136 +++--- 5 files changed, 1213 insertions(+), 1006 deletions(-) create mode 100644 .gitignore create mode 100644 applepy_curses.py create mode 100644 cpu6502.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/applepy.py b/applepy.py index 6f6e69a..479de65 100644 --- a/applepy.py +++ b/applepy.py @@ -5,16 +5,12 @@ import numpy import pygame +import struct +import subprocess import sys import time -def signed(x): - if x > 0x7F: - x = x - 0x100 - return x - - class Display: characters = [ @@ -286,33 +282,10 @@ class Speaker: sound = pygame.sndarray.make_sound(sample_array) sound.play() self.reset() - - -class ROM: - def __init__(self, start, size): - self.start = start - self.end = start + size - 1 - self._mem = [0x00] * size - - def load(self, address, data): - for offset, datum in enumerate(data): - self._mem[address - self.start + offset] = datum - - def load_file(self, address, filename): - with open(filename) as f: - for offset, datum in enumerate(f.read()): - self._mem[address - self.start + offset] = ord(datum) - - def read_byte(self, address): - assert self.start <= address <= self.end - return self._mem[address - self.start] - - -class RAM(ROM): - - def write_byte(self, address, value): - self._mem[address] = value + def update(self, cycle): + if self.buffer and (cycle - self.last_toggle) > self.CHECK_INTERVAL: + self.play() class SoftSwitches: @@ -352,474 +325,41 @@ class SoftSwitches: return 0x00 -class Memory: - - def __init__(self, options=None, display=None, speaker=None): +class Apple2: + + def __init__(self, options, display, speaker): self.display = display self.speaker = speaker - self.rom = ROM(0xD000, 0x3000) - - if options: - self.rom.load_file(0xD000, options.rom) - - self.ram = RAM(0x0000, 0xC000) - - if options and options.ram: - self.ram.load_file(0x0000, options.ram) - self.softswitches = SoftSwitches(display, speaker) - - def load(self, address, data): - if address < 0xC000: - self.ram.load(address, data) - - def read_byte(self, cycle, address): - if address < 0xC000: - return self.ram.read_byte(address) - elif address < 0xD000: - return self.softswitches.read_byte(cycle, address) - else: - return self.rom.read_byte(address) - - def read_word(self, cycle, address): - return self.read_byte(cycle, address) + (self.read_byte(cycle + 1, address + 1) << 8) - - def read_word_bug(self, cycle, address): - if address % 0x100 == 0xFF: - return self.read_byte(cycle, address) + (self.read_byte(cycle + 1, address & 0xFF00) << 8) - else: - return self.read_word(cycle, address) - - def write_byte(self, address, value): - if address < 0xC000: - self.ram.write_byte(address, value) - if 0x400 <= address < 0x800 and self.display: - self.display.update(address, value) - if 0x2000 <= address < 0x5FFF and self.display: - self.display.update(address, value) - - def update(self, cycle): - if self.speaker and self.speaker.buffer and (cycle - self.speaker.last_toggle) > self.speaker.CHECK_INTERVAL: - self.speaker.play() + args = [ + sys.executable, + "cpu6502.py", + "--rom", options.rom, + ] + if options.ram: + args.extend([ + "--ram", options.ram, + ]) + self.core = subprocess.Popen( + args=args, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + ) -class Disassemble: - def __init__(self, cpu, memory): - self.cpu = cpu - self.memory = memory - - self.setup_ops() - - def setup_ops(self): - self.ops = [None] * 0x100 - self.ops[0x00] = ("BRK", ) - self.ops[0x01] = ("ORA", self.indirect_x_mode) - self.ops[0x05] = ("ORA", self.zero_page_mode) - self.ops[0x06] = ("ASL", self.zero_page_mode) - self.ops[0x08] = ("PHP", ) - self.ops[0x09] = ("ORA", self.immediate_mode) - self.ops[0x0A] = ("ASL", ) - self.ops[0x0D] = ("ORA", self.absolute_mode) - self.ops[0x0E] = ("ASL", self.absolute_mode) - self.ops[0x10] = ("BPL", self.relative_mode) - self.ops[0x11] = ("ORA", self.indirect_y_mode) - self.ops[0x15] = ("ORA", self.zero_page_x_mode) - self.ops[0x16] = ("ASL", self.zero_page_x_mode) - self.ops[0x18] = ("CLC", ) - self.ops[0x19] = ("ORA", self.absolute_y_mode) - self.ops[0x1D] = ("ORA", self.absolute_x_mode) - self.ops[0x1E] = ("ASL", self.absolute_x_mode) - self.ops[0x20] = ("JSR", self.absolute_mode) - self.ops[0x21] = ("AND", self.indirect_x_mode) - self.ops[0x24] = ("BIT", self.zero_page_mode) - self.ops[0x25] = ("AND", self.zero_page_mode) - self.ops[0x26] = ("ROL", self.zero_page_mode) - self.ops[0x28] = ("PLP", ) - self.ops[0x29] = ("AND", self.immediate_mode) - self.ops[0x2A] = ("ROL", ) - self.ops[0x2C] = ("BIT", self.absolute_mode) - self.ops[0x2D] = ("AND", self.absolute_mode) - self.ops[0x2E] = ("ROL", self.absolute_mode) - self.ops[0x30] = ("BMI", self.relative_mode) - self.ops[0x31] = ("AND", self.indirect_y_mode) - self.ops[0x35] = ("AND", self.zero_page_x_mode) - self.ops[0x36] = ("ROL", self.zero_page_x_mode) - self.ops[0x38] = ("SEC", ) - self.ops[0x39] = ("AND", self.absolute_y_mode) - self.ops[0x3D] = ("AND", self.absolute_x_mode) - self.ops[0x3E] = ("ROL", self.absolute_x_mode) - self.ops[0x40] = ("RTI", ) - self.ops[0x41] = ("EOR", self.indirect_x_mode) - self.ops[0x45] = ("EOR", self.zero_page_mode) - self.ops[0x46] = ("LSR", self.zero_page_mode) - self.ops[0x48] = ("PHA", ) - self.ops[0x49] = ("EOR", self.immediate_mode) - self.ops[0x4A] = ("LSR", ) - self.ops[0x4C] = ("JMP", self.absolute_mode) - self.ops[0x4D] = ("EOR", self.absolute_mode) - self.ops[0x4E] = ("LSR", self.absolute_mode) - self.ops[0x50] = ("BVC", self.relative_mode) - self.ops[0x51] = ("EOR", self.indirect_y_mode) - self.ops[0x55] = ("EOR", self.zero_page_x_mode) - self.ops[0x56] = ("LSR", self.zero_page_x_mode) - self.ops[0x58] = ("CLI", ) - self.ops[0x59] = ("EOR", self.absolute_y_mode) - self.ops[0x5D] = ("EOR", self.absolute_x_mode) - self.ops[0x5E] = ("LSR", self.absolute_x_mode) - self.ops[0x60] = ("RTS", ) - self.ops[0x61] = ("ADC", self.indirect_x_mode) - self.ops[0x65] = ("ADC", self.zero_page_mode) - self.ops[0x66] = ("ROR", self.zero_page_mode) - self.ops[0x68] = ("PLA", ) - self.ops[0x69] = ("ADC", self.immediate_mode) - self.ops[0x6A] = ("ROR", ) - self.ops[0x6C] = ("JMP", self.indirect_mode) - self.ops[0x6D] = ("ADC", self.absolute_mode) - self.ops[0x6E] = ("ROR", self.absolute_mode) - self.ops[0x70] = ("BVS", self.relative_mode) - self.ops[0x71] = ("ADC", self.indirect_y_mode) - self.ops[0x75] = ("ADC", self.zero_page_x_mode) - self.ops[0x76] = ("ROR", self.zero_page_x_mode) - self.ops[0x78] = ("SEI", ) - self.ops[0x79] = ("ADC", self.absolute_y_mode) - self.ops[0x7D] = ("ADC", self.absolute_x_mode) - self.ops[0x7E] = ("ROR", self.absolute_x_mode) - self.ops[0x81] = ("STA", self.indirect_x_mode) - self.ops[0x84] = ("STY", self.zero_page_mode) - self.ops[0x85] = ("STA", self.zero_page_mode) - self.ops[0x86] = ("STX", self.zero_page_mode) - self.ops[0x88] = ("DEY", ) - self.ops[0x8A] = ("TXA", ) - self.ops[0x8C] = ("STY", self.absolute_mode) - self.ops[0x8D] = ("STA", self.absolute_mode) - self.ops[0x8E] = ("STX", self.absolute_mode) - self.ops[0x90] = ("BCC", self.relative_mode) - self.ops[0x91] = ("STA", self.indirect_y_mode) - self.ops[0x94] = ("STY", self.zero_page_x_mode) - self.ops[0x95] = ("STA", self.zero_page_x_mode) - self.ops[0x96] = ("STX", self.zero_page_y_mode) - self.ops[0x98] = ("TYA", ) - self.ops[0x99] = ("STA", self.absolute_y_mode) - self.ops[0x9A] = ("TXS", ) - self.ops[0x9D] = ("STA", self.absolute_x_mode) - self.ops[0xA0] = ("LDY", self.immediate_mode) - self.ops[0xA1] = ("LDA", self.indirect_x_mode) - self.ops[0xA2] = ("LDX", self.immediate_mode) - self.ops[0xA4] = ("LDY", self.zero_page_mode) - self.ops[0xA5] = ("LDA", self.zero_page_mode) - self.ops[0xA6] = ("LDX", self.zero_page_mode) - self.ops[0xA8] = ("TAY", ) - self.ops[0xA9] = ("LDA", self.immediate_mode) - self.ops[0xAA] = ("TAX", ) - self.ops[0xAC] = ("LDY", self.absolute_mode) - self.ops[0xAD] = ("LDA", self.absolute_mode) - self.ops[0xAE] = ("LDX", self.absolute_mode) - self.ops[0xB0] = ("BCS", self.relative_mode) - self.ops[0xB1] = ("LDA", self.indirect_y_mode) - self.ops[0xB4] = ("LDY", self.zero_page_x_mode) - self.ops[0xB5] = ("LDA", self.zero_page_x_mode) - self.ops[0xB6] = ("LDX", self.zero_page_y_mode) - self.ops[0xB8] = ("CLV", ) - self.ops[0xB9] = ("LDA", self.absolute_y_mode) - self.ops[0xBA] = ("TSX", ) - self.ops[0xBC] = ("LDY", self.absolute_x_mode) - self.ops[0xBD] = ("LDA", self.absolute_x_mode) - self.ops[0xBE] = ("LDX", self.absolute_y_mode) - self.ops[0xC0] = ("CPY", self.immediate_mode) - self.ops[0xC1] = ("CMP", self.indirect_x_mode) - self.ops[0xC4] = ("CPY", self.zero_page_mode) - self.ops[0xC5] = ("CMP", self.zero_page_mode) - self.ops[0xC6] = ("DEC", self.zero_page_mode) - self.ops[0xC8] = ("INY", ) - self.ops[0xC9] = ("CMP", self.immediate_mode) - self.ops[0xCA] = ("DEX", ) - self.ops[0xCC] = ("CPY", self.absolute_mode) - self.ops[0xCD] = ("CMP", self.absolute_mode) - self.ops[0xCE] = ("DEC", self.absolute_mode) - self.ops[0xD0] = ("BNE", self.relative_mode) - self.ops[0xD1] = ("CMP", self.indirect_y_mode) - self.ops[0xD5] = ("CMP", self.zero_page_x_mode) - self.ops[0xD6] = ("DEC", self.zero_page_x_mode) - self.ops[0xD8] = ("CLD", ) - self.ops[0xD9] = ("CMP", self.absolute_y_mode) - self.ops[0xDD] = ("CMP", self.absolute_x_mode) - self.ops[0xDE] = ("DEC", self.absolute_x_mode) - self.ops[0xE0] = ("CPX", self.immediate_mode) - self.ops[0xE1] = ("SBC", self.indirect_x_mode) - self.ops[0xE4] = ("CPX", self.zero_page_mode) - self.ops[0xE5] = ("SBC", self.zero_page_mode) - self.ops[0xE6] = ("INC", self.zero_page_mode) - self.ops[0xE8] = ("INX", ) - self.ops[0xE9] = ("SBC", self.immediate_mode) - self.ops[0xEA] = ("NOP", ) - self.ops[0xEC] = ("CPX", self.absolute_mode) - self.ops[0xED] = ("SBC", self.absolute_mode) - self.ops[0xEE] = ("INC", self.absolute_mode) - self.ops[0xF0] = ("BEQ", self.relative_mode) - self.ops[0xF1] = ("SBC", self.indirect_y_mode) - self.ops[0xF5] = ("SBC", self.zero_page_x_mode) - self.ops[0xF6] = ("INC", self.zero_page_x_mode) - self.ops[0xF8] = ("SED", ) - self.ops[0xF9] = ("SBC", self.absolute_y_mode) - self.ops[0xFD] = ("SBC", self.absolute_x_mode) - self.ops[0xFE] = ("INC", self.absolute_x_mode) - - def absolute_mode(self, pc): - a = self.memory.read_word(pc + 1) - return "$%04X [%04X] = %02X" % (a, a, self.memory.read_word(a)) - - def absolute_x_mode(self, pc): - a = self.memory.read_word(pc + 1) - e = a + self.cpu.x_index - return "$%04X,X [%04X] = %02X" % (a, e, self.memory.read_byte(e)) - - def absolute_y_mode(self, pc): - a = self.memory.read_word(pc + 1) - e = a + self.cpu.y_index - return "$%04X,Y [%04X] = %02X" % (a, e, self.memory.read_byte(e)) - - def immediate_mode(self, pc): - return "#$%02X" % (self.memory.read_byte(pc + 1)) - - def indirect_mode(self, pc): - a = self.memory.read_word(pc + 1) - return "($%04X) [%04X] = %02X" % (a, a, self.memory.read_word(a)) - - def indirect_x_mode(self, pc): - z = self.memory.read_byte(pc + 1) - a = self.memory.read_word((z + self.cpu.x_index) % 0x100) - return "($%02X,X) [%04X] = %02X" % (z, a, self.memory.read_byte(a)) - - def indirect_y_mode(self, pc): - z = self.memory.read_byte(pc + 1) - a = self.memory.read_word(z) + self.cpu.y_index - return "($%02X),Y [%04X] = %02X" % (z, a, self.memory.read_byte(a)) - - def relative_mode(self, pc): - return "$%04X" % (pc + signed(self.memory.read_byte(pc + 1) + 2)) - - def zero_page_mode(self, pc): - a = self.memory.read_byte(pc + 1) - return "$%02X [%04X] = %02X" % (a, a, self.memory.read_byte(a)) - - def zero_page_x_mode(self, pc): - z = self.memory.read_byte(pc + 1) - a = (z + self.cpu.x_index) % 0x100 - return "$%02X,X [%04X] = %02X" % (z, a, self.memory.read_byte(a)) - - def zero_page_y_mode(self, pc): - z = self.memory.read_byte(pc + 1) - a = (z + self.cpu.y_index) % 0x100 - return "$%02X,Y [%04X] = %02X" % (z, a, self.memory.read_byte(a)) - - def disasm(self, pc): - op = self.memory.read_byte(pc) - info = self.ops[op] - s = "%02X %s" % (pc, info[0]) - if len(info) > 1: - s += " " + info[1](pc) - return s - - -class CPU: - - STACK_PAGE = 0x100 - RESET_VECTOR = 0xFFFC - - def __init__(self, memory): - self.memory = memory - self.disassemble = Disassemble(self, 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.cycles = 0 - - 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(rmw=True)) - 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(rmw=True)) - self.ops[0x40] = lambda: self.RTI() - self.ops[0x41] = lambda: self.EOR(self.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(rmw=True)) - 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(rmw=True)) - 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(rmw=True)) - 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_mode()) - self.ops[0x98] = lambda: self.TYA() - self.ops[0x99] = lambda: self.STA(self.absolute_y_mode(rmw=True)) - self.ops[0x9A] = lambda: self.TXS() - self.ops[0x9D] = lambda: self.STA(self.absolute_x_mode(rmw=True)) - 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_mode()) - 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(rmw=True)) - 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(rmw=True)) - - def reset(self): - self.program_counter = self.read_word(self.RESET_VECTOR) - def run(self): update_cycle = 0 quit = False while not quit: - self.cycles += 2 # all instructions take this as a minimum - 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 + op = self.core.stdout.read(8) + cycle, rw, addr, val = struct.unpack("= 1024: - self.memory.display.flash() + self.display.flash() pygame.display.flip() - self.memory.update(self.cycles) + if self.speaker: + self.speaker.update(cycle) 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): - pc = self.program_counter - self.program_counter += inc - 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): - return self.read_byte(self.get_pc()) - - def read_pc_word(self): - return self.read_word(self.get_pc(2)) - - #### - - def status_from_byte(self, status): - self.carry_flag = [0, 1][0 != status & 1] - self.zero_flag = [0, 1][0 != status & 2] - self.interrupt_disable_flag = [0, 1][0 != status & 4] - self.decimal_mode_flag = [0, 1][0 != status & 8] - self.break_flag = [0, 1][0 != status & 16] - self.overflow_flag = [0, 1][0 != status & 64] - self.sign_flag = [0, 1][0 != status & 128] - - def status_as_byte(self): - return 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 - - #### - - def push_byte(self, byte): - self.memory.write_byte(self.STACK_PAGE + self.stack_pointer, byte) - self.stack_pointer = (self.stack_pointer - 1) % 0x100 - - def pull_byte(self): - self.stack_pointer = (self.stack_pointer + 1) % 0x100 - return self.read_byte(self.STACK_PAGE + self.stack_pointer) - - def push_word(self, word): - hi, lo = divmod(word, 0x100) - self.push_byte(hi) - self.push_byte(lo) - - def pull_word(self): - s = self.STACK_PAGE + self.stack_pointer + 1 - self.stack_pointer += 2 - return self.read_word(s) - - #### - - def immediate_mode(self): - return self.get_pc() - - def absolute_mode(self): - self.cycles += 2 - return self.read_pc_word() - - def absolute_x_mode(self, rmw=False): - if rmw: - self.cycles += 1 - return self.absolute_mode() + self.x_index - - def absolute_y_mode(self, rmw=False): - if rmw: - self.cycles += 1 - return self.absolute_mode() + self.y_index - - def zero_page_mode(self): - self.cycles += 1 - return self.read_pc_byte() - - def zero_page_x_mode(self): - self.cycles += 1 - return (self.zero_page_mode() + self.x_index) % 0x100 - - def zero_page_y_mode(self): - self.cycles += 1 - return (self.zero_page_mode() + self.y_index) % 0x100 - - def indirect_mode(self): - self.cycles += 2 - return self.read_word_bug(self.absolute_mode()) - - def indirect_x_mode(self): - self.cycles += 4 - return self.read_word_bug((self.read_pc_byte() + self.x_index) % 0x100) - - def indirect_y_mode(self, rmw=False): - 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): - pc = self.get_pc() - return pc + 1 + signed(self.read_byte(pc)) - - #### - - def update_nz(self, value): - value = value % 0x100 - self.zero_flag = [0, 1][(value == 0)] - self.sign_flag = [0, 1][((value & 0x80) != 0)] - return value - - def update_nzc(self, value): - self.carry_flag = [0, 1][(value > 0xFF)] - return self.update_nz(value) - - #### - - # LOAD / STORE - - def LDA(self, operand_address): - self.accumulator = self.update_nz(self.read_byte(operand_address)) - - def LDX(self, operand_address): - self.x_index = self.update_nz(self.read_byte(operand_address)) - - def LDY(self, operand_address): - self.y_index = self.update_nz(self.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.update_nz(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.update_nzc(self.accumulator << 1) - else: - self.cycles += 2 - self.memory.write_byte(operand_address, self.update_nzc(self.read_byte(operand_address) << 1)) - - 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: - self.cycles += 2 - m = self.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, operand_address=None): - if operand_address is None: - if self.carry_flag: - self.accumulator = self.accumulator | 0x100 - self.carry_flag = self.accumulator % 2 - self.accumulator = self.update_nz(self.accumulator >> 1) - else: - self.cycles += 2 - m = self.read_byte(operand_address) - if self.carry_flag: - m = m | 0x100 - self.carry_flag = m % 2 - self.memory.write_byte(operand_address, self.update_nz(m >> 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.cycles += 2 - 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 - - def JMP(self, operand_address): - self.cycles -= 1 - self.program_counter = operand_address - - def JSR(self, operand_address): - self.cycles += 2 - self.push_word(self.program_counter - 1) - self.program_counter = operand_address - - def RTS(self): - self.cycles += 4 - self.program_counter = self.pull_word() + 1 - - # BRANCHES - - def BCC(self, operand_address): - if not self.carry_flag: - self.cycles += 1 - self.program_counter = operand_address - - def BCS(self, operand_address): - if self.carry_flag: - self.cycles += 1 - self.program_counter = operand_address - - def BEQ(self, operand_address): - if self.zero_flag: - self.cycles += 1 - self.program_counter = operand_address - - def BNE(self, operand_address): - if not self.zero_flag: - self.cycles += 1 - self.program_counter = operand_address - - def BMI(self, operand_address): - if self.sign_flag: - self.cycles += 1 - self.program_counter = operand_address - - def BPL(self, operand_address): - if not self.sign_flag: - self.cycles += 1 - self.program_counter = operand_address - - def BVC(self, operand_address): - if not self.overflow_flag: - self.cycles += 1 - self.program_counter = operand_address - - def BVS(self, operand_address): - if self.overflow_flag: - self.cycles += 1 - 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.cycles += 2 - self.memory.write_byte(operand_address, self.update_nz(self.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.cycles += 2 - self.memory.write_byte(operand_address, self.update_nz(self.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): - self.cycles += 1 - self.push_byte(self.accumulator) - - def PHP(self): - self.cycles += 1 - self.push_byte(self.status_as_byte()) - - def PLA(self): - self.cycles += 2 - self.accumulator = self.update_nz(self.pull_byte()) - - def PLP(self): - self.cycles += 2 - self.status_from_byte(self.pull_byte()) - - # LOGIC - - def AND(self, operand_address): - self.accumulator = self.update_nz(self.accumulator & self.read_byte(operand_address)) - - def ORA(self, operand_address): - self.accumulator = self.update_nz(self.accumulator | self.read_byte(operand_address)) - - def EOR(self, operand_address): - self.accumulator = self.update_nz(self.accumulator ^ self.read_byte(operand_address)) - - # ARITHMETIC - - def ADC(self, operand_address): - # @@@ doesn't handle BCD yet - assert not self.decimal_mode_flag - - a2 = self.accumulator - a1 = signed(a2) - m2 = self.read_byte(operand_address) - m1 = signed(m2) - - # twos complement addition - result1 = a1 + m1 + self.carry_flag - - # unsigned addition - result2 = a2 + m2 + self.carry_flag - - self.accumulator = self.update_nzc(result2) - - # perhaps this could be calculated from result2 but result1 is more intuitive - self.overflow_flag = [0, 1][(result1 > 127) | (result1 < -128)] - - def SBC(self, operand_address): - # @@@ doesn't handle BCD yet - assert not self.decimal_mode_flag - - a2 = self.accumulator - a1 = signed(a2) - m2 = self.read_byte(operand_address) - m1 = signed(m2) - - # twos complement subtraction - result1 = a1 - m1 - [1, 0][self.carry_flag] - - # unsigned subtraction - result2 = a2 - m2 - [1, 0][self.carry_flag] - - self.accumulator = self.update_nz(result2) - self.carry_flag = [0, 1][(result2 >= 0)] - - # perhaps this could be calculated from result2 but result1 is more intuitive - self.overflow_flag = [0, 1][(result1 > 127) | (result1 < -128)] - - # BIT - - def BIT(self, operand_address): - value = self.read_byte(operand_address) - self.sign_flag = ((value >> 7) % 2) # bit 7 - self.overflow_flag = ((value >> 6) % 2) # bit 6 - self.zero_flag = [0, 1][((self.accumulator & value) == 0)] - - # COMPARISON - - def CMP(self, operand_address): - result = self.accumulator - self.read_byte(operand_address) - self.carry_flag = [0, 1][(result >= 0)] - self.update_nz(result) - - def CPX(self, operand_address): - result = self.x_index - self.read_byte(operand_address) - self.carry_flag = [0, 1][(result >= 0)] - self.update_nz(result) - - def CPY(self, operand_address): - result = self.y_index - self.read_byte(operand_address) - self.carry_flag = [0, 1][(result >= 0)] - self.update_nz(result) - - # SYSTEM - - def NOP(self): - pass - - def BRK(self): - self.cycles += 5 - self.push_word(self.program_counter + 1) - self.push_byte(self.status_as_byte()) - self.program_counter = self.read_word(0xFFFE) - self.break_flag = 1 - - def RTI(self): - self.cycles += 4 - self.status_from_byte(self.pull_byte()) - self.program_counter = self.pull_word() - - - # @@@ IRQ - # @@@ NMI - def usage(): print >>sys.stderr, "ApplePy - an Apple ][ emulator in Python" @@ -1329,7 +429,6 @@ if __name__ == "__main__": options = get_options() display = Display() speaker = None if options.quiet else Speaker() - mem = Memory(options, display, speaker) - - cpu = CPU(mem) - cpu.run() + + apple = Apple2(options, display, speaker) + apple.run() diff --git a/applepy_curses.py b/applepy_curses.py new file mode 100644 index 0000000..8b234e6 --- /dev/null +++ b/applepy_curses.py @@ -0,0 +1,91 @@ +# ApplePy - an Apple ][ emulator in Python +# James Tauber / http://jtauber.com/ +# originally written 2001, updated 2011 + + +import curses +import struct +import subprocess +import sys + + +kbd = 0 + + +def write_screen(win, address, value): + base = address - 0x400 + hi, lo = divmod(base, 0x80) + row_group, column = divmod(lo, 0x28) + row = hi + 8 * row_group + + # skip if writing to row group 3 + if row_group == 3: + return + + 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 + + try: + win.addch(row, column, c, attr) + except curses.error: + pass + + +def read(addr, val): + global kbd + if addr == 0xC000: + return kbd + elif addr == 0xC010: + kbd = kbd & 0x7F + return 0x00 + + +def write(win, addr, val): + if 0x400 <= addr <= 0x800: + write_screen(win, addr, val) + + +def run(win): + global kbd + p = subprocess.Popen( + args=[sys.executable, "cpu6502.py"], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + ) + win.clear() + curses.noecho() + win.nodelay(True) + while True: + op = p.stdout.read(8) + cycle, rw, addr, val = struct.unpack(" 0x7F: + x = x - 0x100 + return x + + +class ROM: + + def __init__(self, start, size): + self.start = start + self.end = start + size - 1 + self._mem = [0x00] * size + + def load(self, address, data): + for offset, datum in enumerate(data): + self._mem[address - self.start + offset] = datum + + def load_file(self, address, filename): + with open(filename) as f: + for offset, datum in enumerate(f.read()): + self._mem[address - self.start + offset] = ord(datum) + + def read_byte(self, address): + assert self.start <= address <= self.end + return self._mem[address - self.start] + + +class RAM(ROM): + + def write_byte(self, address, value): + self._mem[address] = value + + +class Memory: + + def __init__(self, options=None, use_stdio=True): + self.use_stdio = use_stdio + self.rom = ROM(0xD000, 0x3000) + + if options: + self.rom.load_file(0xD000, options.rom) + + self.ram = RAM(0x0000, 0xC000) + + if options and options.ram: + self.ram.load_file(0x0000, options.ram) + + def load(self, address, data): + if address < 0xC000: + self.ram.load(address, data) + + def read_byte(self, cycle, address): + if address < 0xC000: + return self.ram.read_byte(address) + elif address < 0xD000: + return self.bus_read(cycle, address) + else: + return self.rom.read_byte(address) + + def read_word(self, cycle, address): + return self.read_byte(cycle, address) + (self.read_byte(cycle + 1, address + 1) << 8) + + def read_word_bug(self, cycle, address): + if address % 0x100 == 0xFF: + return self.read_byte(cycle, address) + (self.read_byte(cycle + 1, address & 0xFF00) << 8) + else: + return self.read_word(cycle, address) + + def write_byte(self, cycle, address, value): + if address < 0xC000: + self.ram.write_byte(address, value) + if 0x400 <= address < 0x800 or 0x2000 <= address < 0x5FFF: + self.bus_write(cycle, address, value) + + def bus_read(self, cycle, address): + if not self.use_stdio: + return 0 + op = struct.pack(" 1: + s += " " + info[1](pc) + return s + + +class CPU: + + STACK_PAGE = 0x100 + RESET_VECTOR = 0xFFFC + + def __init__(self, memory): + self.memory = memory + self.disassemble = Disassemble(self, 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.cycles = 0 + + 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(rmw=True)) + 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(rmw=True)) + self.ops[0x40] = lambda: self.RTI() + self.ops[0x41] = lambda: self.EOR(self.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(rmw=True)) + 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(rmw=True)) + 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(rmw=True)) + 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_mode()) + self.ops[0x98] = lambda: self.TYA() + self.ops[0x99] = lambda: self.STA(self.absolute_y_mode(rmw=True)) + self.ops[0x9A] = lambda: self.TXS() + self.ops[0x9D] = lambda: self.STA(self.absolute_x_mode(rmw=True)) + 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_mode()) + 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(rmw=True)) + 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(rmw=True)) + + def reset(self): + self.program_counter = self.read_word(self.RESET_VECTOR) + + def run(self): + while True: + self.cycles += 2 # all instructions take this as a minimum + 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 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): + pc = self.program_counter + self.program_counter += inc + 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): + return self.read_byte(self.get_pc()) + + def read_pc_word(self): + return self.read_word(self.get_pc(2)) + + def write_byte(self, address, value): + self.memory.write_byte(self.cycles, address, value) + + #### + + def status_from_byte(self, status): + self.carry_flag = [0, 1][0 != status & 1] + self.zero_flag = [0, 1][0 != status & 2] + self.interrupt_disable_flag = [0, 1][0 != status & 4] + self.decimal_mode_flag = [0, 1][0 != status & 8] + self.break_flag = [0, 1][0 != status & 16] + self.overflow_flag = [0, 1][0 != status & 64] + self.sign_flag = [0, 1][0 != status & 128] + + def status_as_byte(self): + return 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 + + #### + + def push_byte(self, byte): + self.write_byte(self.STACK_PAGE + self.stack_pointer, byte) + self.stack_pointer = (self.stack_pointer - 1) % 0x100 + + def pull_byte(self): + self.stack_pointer = (self.stack_pointer + 1) % 0x100 + return self.read_byte(self.STACK_PAGE + self.stack_pointer) + + def push_word(self, word): + hi, lo = divmod(word, 0x100) + self.push_byte(hi) + self.push_byte(lo) + + def pull_word(self): + s = self.STACK_PAGE + self.stack_pointer + 1 + self.stack_pointer += 2 + return self.read_word(s) + + #### + + def immediate_mode(self): + return self.get_pc() + + def absolute_mode(self): + self.cycles += 2 + return self.read_pc_word() + + def absolute_x_mode(self, rmw=False): + if rmw: + self.cycles += 1 + return self.absolute_mode() + self.x_index + + def absolute_y_mode(self, rmw=False): + if rmw: + self.cycles += 1 + return self.absolute_mode() + self.y_index + + def zero_page_mode(self): + self.cycles += 1 + return self.read_pc_byte() + + def zero_page_x_mode(self): + self.cycles += 1 + return (self.zero_page_mode() + self.x_index) % 0x100 + + def zero_page_y_mode(self): + self.cycles += 1 + return (self.zero_page_mode() + self.y_index) % 0x100 + + def indirect_mode(self): + self.cycles += 2 + return self.read_word_bug(self.absolute_mode()) + + def indirect_x_mode(self): + self.cycles += 4 + return self.read_word_bug((self.read_pc_byte() + self.x_index) % 0x100) + + def indirect_y_mode(self, rmw=False): + 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): + pc = self.get_pc() + return pc + 1 + signed(self.read_byte(pc)) + + #### + + def update_nz(self, value): + value = value % 0x100 + self.zero_flag = [0, 1][(value == 0)] + self.sign_flag = [0, 1][((value & 0x80) != 0)] + return value + + def update_nzc(self, value): + self.carry_flag = [0, 1][(value > 0xFF)] + return self.update_nz(value) + + #### + + # LOAD / STORE + + def LDA(self, operand_address): + self.accumulator = self.update_nz(self.read_byte(operand_address)) + + def LDX(self, operand_address): + self.x_index = self.update_nz(self.read_byte(operand_address)) + + def LDY(self, operand_address): + self.y_index = self.update_nz(self.read_byte(operand_address)) + + def STA(self, operand_address): + self.write_byte(operand_address, self.accumulator) + + def STX(self, operand_address): + self.write_byte(operand_address, self.x_index) + + def STY(self, operand_address): + self.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.update_nz(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.update_nzc(self.accumulator << 1) + else: + self.cycles += 2 + self.write_byte(operand_address, self.update_nzc(self.read_byte(operand_address) << 1)) + + 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: + self.cycles += 2 + m = self.read_byte(operand_address) << 1 + if self.carry_flag: + m = m | 0x01 + self.write_byte(operand_address, self.update_nzc(m)) + + def ROR(self, operand_address=None): + if operand_address is None: + if self.carry_flag: + self.accumulator = self.accumulator | 0x100 + self.carry_flag = self.accumulator % 2 + self.accumulator = self.update_nz(self.accumulator >> 1) + else: + self.cycles += 2 + m = self.read_byte(operand_address) + if self.carry_flag: + m = m | 0x100 + self.carry_flag = m % 2 + self.write_byte(operand_address, self.update_nz(m >> 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.cycles += 2 + self.carry_flag = self.read_byte(operand_address) % 2 + self.write_byte(operand_address, self.update_nz(self.read_byte(operand_address) >> 1)) + + # JUMPS / RETURNS + + def JMP(self, operand_address): + self.cycles -= 1 + self.program_counter = operand_address + + def JSR(self, operand_address): + self.cycles += 2 + self.push_word(self.program_counter - 1) + self.program_counter = operand_address + + def RTS(self): + self.cycles += 4 + self.program_counter = self.pull_word() + 1 + + # BRANCHES + + def BCC(self, operand_address): + if not self.carry_flag: + self.cycles += 1 + self.program_counter = operand_address + + def BCS(self, operand_address): + if self.carry_flag: + self.cycles += 1 + self.program_counter = operand_address + + def BEQ(self, operand_address): + if self.zero_flag: + self.cycles += 1 + self.program_counter = operand_address + + def BNE(self, operand_address): + if not self.zero_flag: + self.cycles += 1 + self.program_counter = operand_address + + def BMI(self, operand_address): + if self.sign_flag: + self.cycles += 1 + self.program_counter = operand_address + + def BPL(self, operand_address): + if not self.sign_flag: + self.cycles += 1 + self.program_counter = operand_address + + def BVC(self, operand_address): + if not self.overflow_flag: + self.cycles += 1 + self.program_counter = operand_address + + def BVS(self, operand_address): + if self.overflow_flag: + self.cycles += 1 + 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.cycles += 2 + self.write_byte(operand_address, self.update_nz(self.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.cycles += 2 + self.write_byte(operand_address, self.update_nz(self.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): + self.cycles += 1 + self.push_byte(self.accumulator) + + def PHP(self): + self.cycles += 1 + self.push_byte(self.status_as_byte()) + + def PLA(self): + self.cycles += 2 + self.accumulator = self.update_nz(self.pull_byte()) + + def PLP(self): + self.cycles += 2 + self.status_from_byte(self.pull_byte()) + + # LOGIC + + def AND(self, operand_address): + self.accumulator = self.update_nz(self.accumulator & self.read_byte(operand_address)) + + def ORA(self, operand_address): + self.accumulator = self.update_nz(self.accumulator | self.read_byte(operand_address)) + + def EOR(self, operand_address): + self.accumulator = self.update_nz(self.accumulator ^ self.read_byte(operand_address)) + + # ARITHMETIC + + def ADC(self, operand_address): + # @@@ doesn't handle BCD yet + assert not self.decimal_mode_flag + + a2 = self.accumulator + a1 = signed(a2) + m2 = self.read_byte(operand_address) + m1 = signed(m2) + + # twos complement addition + result1 = a1 + m1 + self.carry_flag + + # unsigned addition + result2 = a2 + m2 + self.carry_flag + + self.accumulator = self.update_nzc(result2) + + # perhaps this could be calculated from result2 but result1 is more intuitive + self.overflow_flag = [0, 1][(result1 > 127) | (result1 < -128)] + + def SBC(self, operand_address): + # @@@ doesn't handle BCD yet + assert not self.decimal_mode_flag + + a2 = self.accumulator + a1 = signed(a2) + m2 = self.read_byte(operand_address) + m1 = signed(m2) + + # twos complement subtraction + result1 = a1 - m1 - [1, 0][self.carry_flag] + + # unsigned subtraction + result2 = a2 - m2 - [1, 0][self.carry_flag] + + self.accumulator = self.update_nz(result2) + self.carry_flag = [0, 1][(result2 >= 0)] + + # perhaps this could be calculated from result2 but result1 is more intuitive + self.overflow_flag = [0, 1][(result1 > 127) | (result1 < -128)] + + # BIT + + def BIT(self, operand_address): + value = self.read_byte(operand_address) + self.sign_flag = ((value >> 7) % 2) # bit 7 + self.overflow_flag = ((value >> 6) % 2) # bit 6 + self.zero_flag = [0, 1][((self.accumulator & value) == 0)] + + # COMPARISON + + def CMP(self, operand_address): + result = self.accumulator - self.read_byte(operand_address) + self.carry_flag = [0, 1][(result >= 0)] + self.update_nz(result) + + def CPX(self, operand_address): + result = self.x_index - self.read_byte(operand_address) + self.carry_flag = [0, 1][(result >= 0)] + self.update_nz(result) + + def CPY(self, operand_address): + result = self.y_index - self.read_byte(operand_address) + self.carry_flag = [0, 1][(result >= 0)] + self.update_nz(result) + + # SYSTEM + + def NOP(self): + pass + + def BRK(self): + self.cycles += 5 + self.push_word(self.program_counter + 1) + self.push_byte(self.status_as_byte()) + self.program_counter = self.read_word(0xFFFE) + self.break_flag = 1 + + def RTI(self): + self.cycles += 4 + self.status_from_byte(self.pull_byte()) + self.program_counter = self.pull_word() + + + # @@@ IRQ + # @@@ NMI + + +def usage(): + print >>sys.stderr, "ApplePy - an Apple ][ emulator in Python" + print >>sys.stderr, "James Tauber / http://jtauber.com/" + print >>sys.stderr + print >>sys.stderr, "Usage: cpu6502.py [options]" + print >>sys.stderr + print >>sys.stderr, " -R, --rom ROM file to use (default A2ROM.BIN)" + print >>sys.stderr, " -r, --ram RAM file to load (default none)" + sys.exit(1) + + +def get_options(): + class Options: + def __init__(self): + self.rom = "A2ROM.BIN" + self.ram = None + + options = Options() + a = 1 + while a < len(sys.argv): + if sys.argv[a].startswith("-"): + if sys.argv[a] in ("-R", "--rom"): + a += 1 + options.rom = sys.argv[a] + elif sys.argv[a] in ("-r", "--ram"): + a += 1 + options.ram = sys.argv[a] + else: + usage() + else: + usage() + a += 1 + + return options + + +if __name__ == "__main__": + if sys.stdout.isatty(): + print "ApplePy cpu core" + print "Run applepy.py instead" + sys.exit(0) + + options = get_options() + mem = Memory(options) + + cpu = CPU(mem) + cpu.run() diff --git a/tests.py b/tests.py index 11fa2f4..3d8dedd 100644 --- a/tests.py +++ b/tests.py @@ -1,11 +1,11 @@ import unittest -from applepy import Memory, CPU +from cpu6502 import Memory, CPU class TestMemory(unittest.TestCase): def setUp(self): - self.memory = Memory() + self.memory = Memory(use_stdio=False) def test_load(self): self.memory.load(0x1000, [0x01, 0x02, 0x03]) @@ -14,9 +14,9 @@ class TestMemory(unittest.TestCase): self.assertEqual(self.memory.read_byte(None, 0x1002), 0x03) def test_write(self): - self.memory.write_byte(0x1000, 0x11) - self.memory.write_byte(0x1001, 0x12) - self.memory.write_byte(0x1002, 0x13) + self.memory.write_byte(None, 0x1000, 0x11) + self.memory.write_byte(None, 0x1001, 0x12) + self.memory.write_byte(None, 0x1002, 0x13) self.assertEqual(self.memory.read_byte(None, 0x1000), 0x11) self.assertEqual(self.memory.read_byte(None, 0x1001), 0x12) self.assertEqual(self.memory.read_byte(None, 0x1002), 0x13) @@ -25,7 +25,7 @@ class TestMemory(unittest.TestCase): class TestLoadStoreOperations(unittest.TestCase): def setUp(self): - self.memory = Memory() + self.memory = Memory(use_stdio=False) self.cpu = CPU(self.memory) self.memory.load(0x1000, [0x00, 0x01, 0x7F, 0x80, 0xFF]) @@ -114,7 +114,7 @@ class TestLoadStoreOperations(unittest.TestCase): class TestRegisterTransferOperations(unittest.TestCase): def setUp(self): - self.memory = Memory() + self.memory = Memory(use_stdio=False) self.cpu = CPU(self.memory) def test_TAX(self): @@ -189,7 +189,7 @@ class TestRegisterTransferOperations(unittest.TestCase): class TestStackOperations(unittest.TestCase): def setUp(self): - self.memory = Memory() + self.memory = Memory(use_stdio=False) self.cpu = CPU(self.memory) def test_TSX(self): @@ -237,11 +237,11 @@ class TestStackOperations(unittest.TestCase): class TestLogicalOperations(unittest.TestCase): def setUp(self): - self.memory = Memory() + self.memory = Memory(use_stdio=False) self.cpu = CPU(self.memory) def test_AND(self): - self.memory.write_byte(0x1000, 0x37) + self.memory.write_byte(None, 0x1000, 0x37) self.cpu.accumulator = 0x34 self.cpu.AND(0x1000) self.assertEqual(self.cpu.accumulator, 0x34) @@ -254,7 +254,7 @@ class TestLogicalOperations(unittest.TestCase): self.assertEqual(self.cpu.sign_flag, 0) def test_EOR(self): - self.memory.write_byte(0x1000, 0x37) + self.memory.write_byte(None, 0x1000, 0x37) self.cpu.accumulator = 0x34 self.cpu.EOR(0x1000) self.assertEqual(self.cpu.accumulator, 0x03) @@ -272,7 +272,7 @@ class TestLogicalOperations(unittest.TestCase): self.assertEqual(self.cpu.sign_flag, 0) def test_ORA(self): - self.memory.write_byte(0x1000, 0x37) + self.memory.write_byte(None, 0x1000, 0x37) self.cpu.accumulator = 0x34 self.cpu.ORA(0x1000) self.assertEqual(self.cpu.accumulator, 0x37) @@ -290,31 +290,31 @@ class TestLogicalOperations(unittest.TestCase): self.assertEqual(self.cpu.sign_flag, 0) def test_BIT(self): - self.memory.write_byte(0x1000, 0x00) + self.memory.write_byte(None, 0x1000, 0x00) self.cpu.accumulator = 0x00 self.cpu.BIT(0x1000) self.assertEqual(self.cpu.overflow_flag, 0) self.assertEqual(self.cpu.sign_flag, 0) self.assertEqual(self.cpu.zero_flag, 1) - self.memory.write_byte(0x1000, 0x40) + self.memory.write_byte(None, 0x1000, 0x40) self.cpu.accumulator = 0x00 self.cpu.BIT(0x1000) self.assertEqual(self.cpu.overflow_flag, 1) self.assertEqual(self.cpu.sign_flag, 0) self.assertEqual(self.cpu.zero_flag, 1) - self.memory.write_byte(0x1000, 0x80) + self.memory.write_byte(None, 0x1000, 0x80) self.cpu.accumulator = 0x00 self.cpu.BIT(0x1000) self.assertEqual(self.cpu.overflow_flag, 0) self.assertEqual(self.cpu.sign_flag, 1) self.assertEqual(self.cpu.zero_flag, 1) - self.memory.write_byte(0x1000, 0xC0) + self.memory.write_byte(None, 0x1000, 0xC0) self.cpu.accumulator = 0x00 self.cpu.BIT(0x1000) self.assertEqual(self.cpu.overflow_flag, 1) self.assertEqual(self.cpu.sign_flag, 1) self.assertEqual(self.cpu.zero_flag, 1) - self.memory.write_byte(0x1000, 0xC0) + self.memory.write_byte(None, 0x1000, 0xC0) self.cpu.accumulator = 0xC0 self.cpu.BIT(0x1000) self.assertEqual(self.cpu.overflow_flag, 1) @@ -325,7 +325,7 @@ class TestLogicalOperations(unittest.TestCase): class TestArithmeticOperations(unittest.TestCase): def setUp(self): - self.memory = Memory() + self.memory = Memory(use_stdio=False) self.cpu = CPU(self.memory) def test_ADC_without_BCD(self): @@ -335,7 +335,7 @@ class TestArithmeticOperations(unittest.TestCase): # 1 + 1 = 2 (C = 0; V = 0) self.cpu.carry_flag = 0 self.cpu.accumulator = 0x01 - self.memory.write_byte(0x1000, 0x01) + self.memory.write_byte(None, 0x1000, 0x01) self.cpu.ADC(0x1000) self.assertEqual(self.cpu.accumulator, 0x02) self.assertEqual(self.cpu.carry_flag, 0) @@ -344,7 +344,7 @@ class TestArithmeticOperations(unittest.TestCase): # 1 + -1 = 0 (C = 1; V = 0) self.cpu.carry_flag = 0 self.cpu.accumulator = 0x01 - self.memory.write_byte(0x1000, 0xFF) + self.memory.write_byte(None, 0x1000, 0xFF) self.cpu.ADC(0x1000) self.assertEqual(self.cpu.accumulator, 0x00) self.assertEqual(self.cpu.carry_flag, 1) @@ -353,7 +353,7 @@ class TestArithmeticOperations(unittest.TestCase): # 127 + 1 = 128 (C = 0; V = 1) self.cpu.carry_flag = 0 self.cpu.accumulator = 0x7F - self.memory.write_byte(0x1000, 0x01) + self.memory.write_byte(None, 0x1000, 0x01) self.cpu.ADC(0x1000) self.assertEqual(self.cpu.accumulator, 0x80) # @@@ self.assertEqual(self.cpu.carry_flag, 0) @@ -362,7 +362,7 @@ class TestArithmeticOperations(unittest.TestCase): # -128 + -1 = -129 (C = 1; V = 1) self.cpu.carry_flag = 0 self.cpu.accumulator = 0x80 - self.memory.write_byte(0x1000, 0xFF) + self.memory.write_byte(None, 0x1000, 0xFF) self.cpu.ADC(0x1000) self.assertEqual(self.cpu.accumulator, 0x7F) # @@@ self.assertEqual(self.cpu.carry_flag, 1) @@ -371,7 +371,7 @@ class TestArithmeticOperations(unittest.TestCase): # 63 + 64 + 1 = 128 (C = 0; V = 1) self.cpu.carry_flag = 1 self.cpu.accumulator = 0x3F - self.memory.write_byte(0x1000, 0x40) + self.memory.write_byte(None, 0x1000, 0x40) self.cpu.ADC(0x1000) self.assertEqual(self.cpu.accumulator, 0x80) self.assertEqual(self.cpu.carry_flag, 0) @@ -379,14 +379,14 @@ class TestArithmeticOperations(unittest.TestCase): def test_SBC_without_BCD(self): self.cpu.accumulator = 0x02 - self.memory.write_byte(0x1000, 0x01) + self.memory.write_byte(None, 0x1000, 0x01) self.cpu.SBC(0x1000) self.assertEqual(self.cpu.accumulator, 0x00) self.assertEqual(self.cpu.carry_flag, 1) self.assertEqual(self.cpu.overflow_flag, 0) self.cpu.accumulator = 0x01 - self.memory.write_byte(0x1000, 0x02) + self.memory.write_byte(None, 0x1000, 0x02) self.cpu.SBC(0x1000) self.assertEqual(self.cpu.accumulator, 0xFF) self.assertEqual(self.cpu.carry_flag, 0) @@ -397,7 +397,7 @@ class TestArithmeticOperations(unittest.TestCase): # 0 - 1 = -1 (V = 0) self.cpu.carry_flag = 1 self.cpu.accumulator = 0x00 - self.memory.write_byte(0x1000, 0x01) + self.memory.write_byte(None, 0x1000, 0x01) self.cpu.SBC(0x1000) self.assertEqual(self.cpu.accumulator, 0xFF) self.assertEqual(self.cpu.carry_flag, 0) @@ -406,7 +406,7 @@ class TestArithmeticOperations(unittest.TestCase): # -128 - 1 = -129 (V = 1) self.cpu.carry_flag = 1 self.cpu.accumulator = 0x80 - self.memory.write_byte(0x1000, 0x01) + self.memory.write_byte(None, 0x1000, 0x01) self.cpu.SBC(0x1000) self.assertEqual(self.cpu.accumulator, 0x7F) self.assertEqual(self.cpu.carry_flag, 1) @@ -415,7 +415,7 @@ class TestArithmeticOperations(unittest.TestCase): # 127 - -1 = 128 (V = 1) self.cpu.carry_flag = 1 self.cpu.accumulator = 0x7F - self.memory.write_byte(0x1000, 0xFF) + self.memory.write_byte(None, 0x1000, 0xFF) self.cpu.SBC(0x1000) self.assertEqual(self.cpu.accumulator, 0x80) self.assertEqual(self.cpu.carry_flag, 0) @@ -424,7 +424,7 @@ class TestArithmeticOperations(unittest.TestCase): # -64 -64 -1 = -129 (V = 1) self.cpu.carry_flag = 0 self.cpu.accumulator = 0xC0 - self.memory.write_byte(0x1000, 0x40) + self.memory.write_byte(None, 0x1000, 0x40) self.cpu.SBC(0x1000) self.assertEqual(self.cpu.accumulator, 0x7F) self.assertEqual(self.cpu.carry_flag, 1) @@ -434,35 +434,35 @@ class TestArithmeticOperations(unittest.TestCase): def test_CMP(self): self.cpu.accumulator = 0x0A - self.memory.write_byte(0x1000, 0x09) + self.memory.write_byte(None, 0x1000, 0x09) self.cpu.CMP(0x1000) self.assertEqual(self.cpu.sign_flag, 0) self.assertEqual(self.cpu.zero_flag, 0) self.assertEqual(self.cpu.carry_flag, 1) self.cpu.accumulator = 0x0A - self.memory.write_byte(0x1000, 0x0B) + self.memory.write_byte(None, 0x1000, 0x0B) self.cpu.CMP(0x1000) self.assertEqual(self.cpu.sign_flag, 1) self.assertEqual(self.cpu.zero_flag, 0) self.assertEqual(self.cpu.carry_flag, 0) self.cpu.accumulator = 0x0A - self.memory.write_byte(0x1000, 0x0A) + self.memory.write_byte(None, 0x1000, 0x0A) self.cpu.CMP(0x1000) self.assertEqual(self.cpu.sign_flag, 0) self.assertEqual(self.cpu.zero_flag, 1) self.assertEqual(self.cpu.carry_flag, 1) self.cpu.accumulator = 0xA0 - self.memory.write_byte(0x1000, 0x0A) + self.memory.write_byte(None, 0x1000, 0x0A) self.cpu.CMP(0x1000) self.assertEqual(self.cpu.sign_flag, 1) self.assertEqual(self.cpu.zero_flag, 0) self.assertEqual(self.cpu.carry_flag, 1) self.cpu.accumulator = 0x0A - self.memory.write_byte(0x1000, 0xA0) + self.memory.write_byte(None, 0x1000, 0xA0) self.cpu.CMP(0x1000) self.assertEqual(self.cpu.sign_flag, 0) # @@@ self.assertEqual(self.cpu.zero_flag, 0) @@ -470,35 +470,35 @@ class TestArithmeticOperations(unittest.TestCase): def test_CPX(self): self.cpu.x_index = 0x0A - self.memory.write_byte(0x1000, 0x09) + self.memory.write_byte(None, 0x1000, 0x09) self.cpu.CPX(0x1000) self.assertEqual(self.cpu.sign_flag, 0) self.assertEqual(self.cpu.zero_flag, 0) self.assertEqual(self.cpu.carry_flag, 1) self.cpu.x_index = 0x0A - self.memory.write_byte(0x1000, 0x0B) + self.memory.write_byte(None, 0x1000, 0x0B) self.cpu.CPX(0x1000) self.assertEqual(self.cpu.sign_flag, 1) self.assertEqual(self.cpu.zero_flag, 0) self.assertEqual(self.cpu.carry_flag, 0) self.cpu.x_index = 0x0A - self.memory.write_byte(0x1000, 0x0A) + self.memory.write_byte(None, 0x1000, 0x0A) self.cpu.CPX(0x1000) self.assertEqual(self.cpu.sign_flag, 0) self.assertEqual(self.cpu.zero_flag, 1) self.assertEqual(self.cpu.carry_flag, 1) self.cpu.x_index = 0xA0 - self.memory.write_byte(0x1000, 0x0A) + self.memory.write_byte(None, 0x1000, 0x0A) self.cpu.CPX(0x1000) self.assertEqual(self.cpu.sign_flag, 1) self.assertEqual(self.cpu.zero_flag, 0) self.assertEqual(self.cpu.carry_flag, 1) self.cpu.x_index = 0x0A - self.memory.write_byte(0x1000, 0xA0) + self.memory.write_byte(None, 0x1000, 0xA0) self.cpu.CPX(0x1000) self.assertEqual(self.cpu.sign_flag, 0) # @@@ self.assertEqual(self.cpu.zero_flag, 0) @@ -506,35 +506,35 @@ class TestArithmeticOperations(unittest.TestCase): def test_CPY(self): self.cpu.y_index = 0x0A - self.memory.write_byte(0x1000, 0x09) + self.memory.write_byte(None, 0x1000, 0x09) self.cpu.CPY(0x1000) self.assertEqual(self.cpu.sign_flag, 0) self.assertEqual(self.cpu.zero_flag, 0) self.assertEqual(self.cpu.carry_flag, 1) self.cpu.y_index = 0x0A - self.memory.write_byte(0x1000, 0x0B) + self.memory.write_byte(None, 0x1000, 0x0B) self.cpu.CPY(0x1000) self.assertEqual(self.cpu.sign_flag, 1) self.assertEqual(self.cpu.zero_flag, 0) self.assertEqual(self.cpu.carry_flag, 0) self.cpu.y_index = 0x0A - self.memory.write_byte(0x1000, 0x0A) + self.memory.write_byte(None, 0x1000, 0x0A) self.cpu.CPY(0x1000) self.assertEqual(self.cpu.sign_flag, 0) self.assertEqual(self.cpu.zero_flag, 1) self.assertEqual(self.cpu.carry_flag, 1) self.cpu.y_index = 0xA0 - self.memory.write_byte(0x1000, 0x0A) + self.memory.write_byte(None, 0x1000, 0x0A) self.cpu.CPY(0x1000) self.assertEqual(self.cpu.sign_flag, 1) self.assertEqual(self.cpu.zero_flag, 0) self.assertEqual(self.cpu.carry_flag, 1) self.cpu.y_index = 0x0A - self.memory.write_byte(0x1000, 0xA0) + self.memory.write_byte(None, 0x1000, 0xA0) self.cpu.CPY(0x1000) self.assertEqual(self.cpu.sign_flag, 0) # @@@ self.assertEqual(self.cpu.zero_flag, 0) @@ -544,21 +544,21 @@ class TestArithmeticOperations(unittest.TestCase): class TestIncrementDecrementOperations(unittest.TestCase): def setUp(self): - self.memory = Memory() + self.memory = Memory(use_stdio=False) self.cpu = CPU(self.memory) def test_INC(self): - self.memory.write_byte(0x1000, 0x00) + self.memory.write_byte(None, 0x1000, 0x00) self.cpu.INC(0x1000) self.assertEqual(self.memory.read_byte(None, 0x1000), 0x01) self.assertEqual(self.cpu.sign_flag, 0) self.assertEqual(self.cpu.zero_flag, 0) - self.memory.write_byte(0x1000, 0x7F) + self.memory.write_byte(None, 0x1000, 0x7F) self.cpu.INC(0x1000) self.assertEqual(self.memory.read_byte(None, 0x1000), 0x80) self.assertEqual(self.cpu.sign_flag, 1) self.assertEqual(self.cpu.zero_flag, 0) - self.memory.write_byte(0x1000, 0xFF) + self.memory.write_byte(None, 0x1000, 0xFF) self.cpu.INC(0x1000) self.assertEqual(self.memory.read_byte(None, 0x1000), 0x00) self.assertEqual(self.cpu.sign_flag, 0) @@ -599,17 +599,17 @@ class TestIncrementDecrementOperations(unittest.TestCase): self.assertEqual(self.cpu.zero_flag, 1) def test_DEC(self): - self.memory.write_byte(0x1000, 0x01) + self.memory.write_byte(None, 0x1000, 0x01) self.cpu.DEC(0x1000) self.assertEqual(self.memory.read_byte(None, 0x1000), 0x00) self.assertEqual(self.cpu.sign_flag, 0) self.assertEqual(self.cpu.zero_flag, 1) - self.memory.write_byte(0x1000, 0x80) + self.memory.write_byte(None, 0x1000, 0x80) self.cpu.DEC(0x1000) self.assertEqual(self.memory.read_byte(None, 0x1000), 0x7F) self.assertEqual(self.cpu.sign_flag, 0) self.assertEqual(self.cpu.zero_flag, 0) - self.memory.write_byte(0x1000, 0x00) + self.memory.write_byte(None, 0x1000, 0x00) self.cpu.DEC(0x1000) self.assertEqual(self.memory.read_byte(None, 0x1000), 0xFF) self.assertEqual(self.cpu.sign_flag, 1) @@ -653,7 +653,7 @@ class TestIncrementDecrementOperations(unittest.TestCase): class TestShiftOperations(unittest.TestCase): def setUp(self): - self.memory = Memory() + self.memory = Memory(use_stdio=False) self.cpu = CPU(self.memory) def test_ASL(self): @@ -663,7 +663,7 @@ class TestShiftOperations(unittest.TestCase): self.assertEqual(self.cpu.sign_flag, 0) self.assertEqual(self.cpu.zero_flag, 0) self.assertEqual(self.cpu.carry_flag, 0) - self.memory.write_byte(0x1000, 0x02) + self.memory.write_byte(None, 0x1000, 0x02) self.cpu.ASL(0x1000) self.assertEqual(self.memory.read_byte(None, 0x1000), 0x04) self.assertEqual(self.cpu.sign_flag, 0) @@ -683,7 +683,7 @@ class TestShiftOperations(unittest.TestCase): self.assertEqual(self.cpu.sign_flag, 0) self.assertEqual(self.cpu.zero_flag, 1) self.assertEqual(self.cpu.carry_flag, 1) - self.memory.write_byte(0x1000, 0x01) + self.memory.write_byte(None, 0x1000, 0x01) self.cpu.LSR(0x1000) self.assertEqual(self.memory.read_byte(None, 0x1000), 0x00) self.assertEqual(self.cpu.sign_flag, 0) @@ -712,14 +712,14 @@ class TestShiftOperations(unittest.TestCase): self.assertEqual(self.cpu.zero_flag, 0) # @@@ self.assertEqual(self.cpu.carry_flag, 1) self.cpu.carry_flag = 0 - self.memory.write_byte(0x1000, 0x80) + self.memory.write_byte(None, 0x1000, 0x80) self.cpu.ROL(0x1000) self.assertEqual(self.memory.read_byte(None, 0x1000), 0x00) self.assertEqual(self.cpu.sign_flag, 0) self.assertEqual(self.cpu.zero_flag, 1) # @@@ self.assertEqual(self.cpu.carry_flag, 1) self.cpu.carry_flag = 1 - self.memory.write_byte(0x1000, 0x80) + self.memory.write_byte(None, 0x1000, 0x80) self.cpu.ROL(0x1000) self.assertEqual(self.memory.read_byte(None, 0x1000), 0x01) self.assertEqual(self.cpu.sign_flag, 0) @@ -742,14 +742,14 @@ class TestShiftOperations(unittest.TestCase): self.assertEqual(self.cpu.zero_flag, 0) # @@@ self.assertEqual(self.cpu.carry_flag, 1) self.cpu.carry_flag = 0 - self.memory.write_byte(0x1000, 0x01) + self.memory.write_byte(None, 0x1000, 0x01) self.cpu.ROR(0x1000) self.assertEqual(self.memory.read_byte(None, 0x1000), 0x00) self.assertEqual(self.cpu.sign_flag, 0) self.assertEqual(self.cpu.zero_flag, 1) # @@@ self.assertEqual(self.cpu.carry_flag, 1) self.cpu.carry_flag = 1 - self.memory.write_byte(0x1000, 0x01) + self.memory.write_byte(None, 0x1000, 0x01) self.cpu.ROR(0x1000) self.assertEqual(self.memory.read_byte(None, 0x1000), 0x80) self.assertEqual(self.cpu.sign_flag, 1) # @@@ @@ -760,7 +760,7 @@ class TestShiftOperations(unittest.TestCase): class TestJumpCallOperations(unittest.TestCase): def setUp(self): - self.memory = Memory() + self.memory = Memory(use_stdio=False) self.cpu = CPU(self.memory) def test_JMP(self): @@ -775,8 +775,8 @@ class TestJumpCallOperations(unittest.TestCase): self.assertEqual(self.memory.read_byte(None, self.cpu.STACK_PAGE + self.cpu.stack_pointer + 2), 0x0F) def test_RTS(self): - self.memory.write_byte(self.cpu.STACK_PAGE + 0xFF, 0x12) - self.memory.write_byte(self.cpu.STACK_PAGE + 0xFE, 0x33) + self.memory.write_byte(None, self.cpu.STACK_PAGE + 0xFF, 0x12) + self.memory.write_byte(None, self.cpu.STACK_PAGE + 0xFE, 0x33) self.cpu.stack_pointer = 0xFD self.cpu.RTS() self.assertEqual(self.cpu.program_counter, 0x1234) @@ -792,7 +792,7 @@ class TestJumpCallOperations(unittest.TestCase): class TestBranchOperations(unittest.TestCase): def setUp(self): - self.memory = Memory() + self.memory = Memory(use_stdio=False) self.cpu = CPU(self.memory) def test_BCC(self): @@ -879,7 +879,7 @@ class TestBranchOperations(unittest.TestCase): class TestStatusFlagOperations(unittest.TestCase): def setUp(self): - self.memory = Memory() + self.memory = Memory(use_stdio=False) self.cpu = CPU(self.memory) def test_CLC(self): @@ -921,7 +921,7 @@ class TestStatusFlagOperations(unittest.TestCase): class TestSystemFunctionOperations(unittest.TestCase): def setUp(self): - self.memory = Memory() + self.memory = Memory(use_stdio=False) self.cpu = CPU(self.memory) def test_BRK(self): @@ -936,9 +936,9 @@ class TestSystemFunctionOperations(unittest.TestCase): self.assertEqual(self.memory.read_byte(None, self.cpu.STACK_PAGE + self.cpu.stack_pointer + 3), 0x10) def test_RTI(self): - self.memory.write_byte(self.cpu.STACK_PAGE + 0xFF, 0x12) - self.memory.write_byte(self.cpu.STACK_PAGE + 0xFE, 0x33) - self.memory.write_byte(self.cpu.STACK_PAGE + 0xFD, 0x20) + self.memory.write_byte(None, self.cpu.STACK_PAGE + 0xFF, 0x12) + self.memory.write_byte(None, self.cpu.STACK_PAGE + 0xFE, 0x33) + self.memory.write_byte(None, self.cpu.STACK_PAGE + 0xFD, 0x20) self.cpu.stack_pointer = 0xFC self.cpu.RTI() self.assertEqual(self.cpu.program_counter, 0x1233) @@ -951,7 +951,7 @@ class TestSystemFunctionOperations(unittest.TestCase): class Test6502Bugs(unittest.TestCase): def setUp(self): - self.memory = Memory() + self.memory = Memory(use_stdio=False) self.cpu = CPU(self.memory) def test_zero_page_x(self):