├── .gitignore ├── tox.ini ├── LICENSE ├── README.md ├── applepy_curses.py ├── cycle_notes.txt ├── control.py ├── applepy.py ├── tests.py └── cpu6502.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore = E265,E501 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2001-2013 James Tauber and contributors 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to deal 5 | # in the Software without restriction, including without limitation the rights 6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | # copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | # THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ApplePy - an Apple ][ emulator in Python 2 | ======================================== 3 | 4 | by James Tauber / http://jtauber.com/ 5 | 6 | Originally written 2001, updated 2011 7 | 8 | Apple ][ ROM available from http://www.easy68k.com/paulrsm/6502/index.html 9 | 10 | 11 | Credits 12 | ------- 13 | 14 | Some 6502 code came from contributions from Christiaan Kelly in 2007. 15 | 16 | Greg Hewgill provided significant fixes and improvements to the 2011 version 17 | (see the commit log for details). 18 | 19 | The character generator bitmaps were entered by hand from visual inspection 20 | of http://www.sbprojects.com/projects/apple1/terminal.php 21 | 22 | 23 | Status 24 | ------ 25 | 26 | With original Apple ][ ROM it boots to the monitor, most monitor commands 27 | work and you can go into Integer BASIC (with E000G or Ctrl-B RETURN) and 28 | write and run programs. With an Apple ][+ ROM it boots to Applesoft Basic and 29 | runs all the programs I've tried so far. 30 | 31 | The only I/O supported is the keyboard and screen but 40-column text, LORES 32 | and HIRES graphics are all supported. 33 | 34 | ApplePy currently requires Pygame (although there is a minimal applepy_curses.py 35 | that uses curses to display text mode only) and numpy (just for an array for 36 | speaker sounds) 37 | 38 | Here's how I set up the dependencies (on Mac OS X 10.8): 39 | 40 | pip install numpy 41 | brew install sdl sdl_image sdl_mixer sdl_ttf portmidi hg 42 | pip install hg+http://bitbucket.org/pygame/pygame 43 | 44 | 45 | License 46 | ------- 47 | 48 | This code is made available under an MIT License. See LICENSE. 49 | -------------------------------------------------------------------------------- /applepy_curses.py: -------------------------------------------------------------------------------- 1 | # ApplePy - an Apple ][ emulator in Python 2 | # James Tauber / http://jtauber.com/ 3 | # originally written 2001, updated 2011 4 | 5 | 6 | import curses 7 | import socket 8 | import struct 9 | import subprocess 10 | import sys 11 | 12 | 13 | kbd = 0 14 | 15 | 16 | def write_screen(win, address, value): 17 | base = address - 0x400 18 | hi, lo = divmod(base, 0x80) 19 | row_group, column = divmod(lo, 0x28) 20 | row = hi + 8 * row_group 21 | 22 | # skip if writing to row group 3 23 | if row_group == 3: 24 | return 25 | 26 | c = chr(0x20 + ((value + 0x20) % 0x40)) 27 | 28 | if value < 0x40: 29 | attr = curses.A_DIM 30 | elif value < 0x80: 31 | attr = curses.A_REVERSE 32 | elif value < 0xA0: 33 | attr = curses.A_UNDERLINE 34 | else: 35 | attr = curses.A_DIM 36 | 37 | try: 38 | win.addch(row, column, c, attr) 39 | except curses.error: 40 | pass 41 | 42 | 43 | def read(addr, val): 44 | global kbd 45 | if addr == 0xC000: 46 | return kbd 47 | elif addr == 0xC010: 48 | kbd = kbd & 0x7F 49 | return 0x00 50 | 51 | 52 | def write(win, addr, val): 53 | if 0x400 <= addr <= 0x800: 54 | write_screen(win, addr, val) 55 | 56 | 57 | def run(win): 58 | global kbd 59 | 60 | listener = socket.socket() 61 | listener.bind(("127.0.0.1", 0)) 62 | listener.listen(0) 63 | 64 | args = [ 65 | sys.executable, 66 | "cpu6502.py", 67 | "--bus", str(listener.getsockname()[1]), 68 | "--rom", options.rom, 69 | ] 70 | 71 | subprocess.Popen(args) 72 | cpu, _ = listener.accept() 73 | 74 | win.clear() 75 | curses.noecho() 76 | win.nodelay(True) 77 | while True: 78 | op = cpu.recv(8) 79 | cycle, rw, addr, val = struct.unpack(">sys.stderr, "ApplePy - an Apple ][ emulator in Python" 102 | print >>sys.stderr, "James Tauber / http://jtauber.com/" 103 | print >>sys.stderr 104 | print >>sys.stderr, "Usage: applepy_curses.py [options]" 105 | print >>sys.stderr 106 | print >>sys.stderr, " -R, --rom ROM file to use (default A2ROM.BIN)" 107 | sys.exit(1) 108 | 109 | 110 | def get_options(): 111 | class Options: 112 | def __init__(self): 113 | self.rom = "A2ROM.BIN" 114 | 115 | options = Options() 116 | a = 1 117 | while a < len(sys.argv): 118 | if sys.argv[a].startswith("-"): 119 | if sys.argv[a] in ("-R", "--rom"): 120 | a += 1 121 | options.rom = sys.argv[a] 122 | else: 123 | usage() 124 | else: 125 | usage() 126 | a += 1 127 | 128 | return options 129 | 130 | 131 | if __name__ == "__main__": 132 | options = get_options() 133 | curses.wrapper(run) 134 | -------------------------------------------------------------------------------- /cycle_notes.txt: -------------------------------------------------------------------------------- 1 | There are two ways we could represent cycle information: 2 | 3 | 1. just have an array of cycles for each opcode + the adjustment for 4 | page boundary crossing (which seems opcode-specific, oddly) 5 | 6 | 2. add cycles in individual methods like those accessing memory and the 7 | operations themselves, to model *why* something takes the cycles it does. 8 | 9 | I prefer 2 on the grounds of it being more instructive but it assumes that 10 | the way we do things is closely aligned to the way the 6502 is doing them 11 | internally. Even if we end up having to do 1, I'd love to understand and 12 | document some of the "why". 13 | 14 | What follows is an attempt to "find the patterns" in the cycle times (as 15 | given on http://www.6502.org/tutorials/6502opcodes.html ) 16 | 17 | NOTE: there appears to be an error in AND and ORA zero page timings on that 18 | webpage given above. I've now corrected this below. 19 | 20 | There are 10 classes of instructions when it comes to cycle times: 21 | 22 | 23 | Class I 24 | (followed by ADC, AND, BIT, CMP, CPX, CPY, EOR, LDA, LDX, LDY, ORA, SBC, STA, 25 | STX, STY) 26 | 27 | immediate 2 28 | zero page 3 29 | zero page, x 4 30 | zero page, y 4 31 | absolute 4 32 | absolute, x 4 (+1 if page crossed or writing) 33 | absolute, y 4 (+1 if page crossed or writing) 34 | indirect, x 6 35 | indirect, y 5 (+1 if page crossed or writing) 36 | 37 | Note 1: the zero page indexed and x-index indirect don't have the page cross 38 | addition because they wrap. 39 | 40 | Note 2: writes to indexed non-zero-page memory (e.g. STA) have the +1 even 41 | if not page crossing. 42 | 43 | 44 | Class II 45 | (followed by ASL, DEC, INC, LSR, ROL, ROR) 46 | 47 | implied 2 48 | zero page 5 49 | zero page, x 6 50 | absolute 6 51 | absolute, x 7 52 | 53 | Note 3: these take 2 cycles longer than Class I because they involve a 54 | read-modify-write. Because the absolute, x is a write to an indexed 55 | non-zero-page memory location, there is an additional +1 even if not page 56 | crossing (see Note 2) 57 | 58 | 59 | Class IIIa 60 | (followed by CLC, CLD, CLI, CLV, DEX, DEY, INX, INY, NOP, SEC, SED, SEI, TAX, 61 | TAY, TSX, TXA, TXS, TYA) 62 | 63 | implied 2 64 | 65 | 66 | Class IIIb 67 | (followed by PHA, PHP) 68 | 69 | implied 3 70 | 71 | 72 | Class IIIc 73 | (followed by PLA, PLP) 74 | 75 | implied 4 76 | 77 | 78 | Class IIId 79 | (followed by RTI, RTS) 80 | 81 | implied 6 82 | 83 | 84 | Class IIIe 85 | (followed by BRK) 86 | 87 | implied 7 88 | 89 | 90 | Class IV 91 | (followed by BCC, BCS, BEQ, BMI, BNE, BPL, BVC, BVS) 92 | 93 | branch not taken 2 94 | branch taken 3 (+1 if page crossed) 95 | 96 | 97 | Class V 98 | (followed by JMP) 99 | 100 | absolute 3 101 | indirect 5 102 | 103 | 104 | Class VI 105 | (followed by JSR) 106 | 107 | absolute 6 108 | 109 | 110 | 111 | This seems a possible implementation (not yet considering page boundaries): 112 | 113 | 1. all instructions start with 2 114 | 2. absolute (including absolute indexed) adds 2 115 | 3. absolute indexed adds an additional 1 *if* instruction is of RMW type 116 | 4. zero page (including zero page indexed) adds 1 117 | 5. zero page indexed adds an addition 1 118 | 6. indirect (JMP) adds 4 119 | 7. indirect x adds 4 120 | 8. indirect y adds 3 plus an additional 1 *if* instruction is of RMW type 121 | 9. ASL, LSR, ROL, ROR add 2 cycles if not implied 122 | 10. JMP subtracts 1 cycle 123 | 11. JSR adds 2 124 | 12. RTS adds 4 125 | 13. branches add 1 if taken 126 | 14. DEC and INC add 2 cycles 127 | 15. PHA and PHP add 1 cycle 128 | 16. PLA and PLP add 2 cycles 129 | 17. BRK adds 5 cycles 130 | 18. RTI adds 4 cycles 131 | 132 | RMW instructions are the absolute,x of ASL, DEC, INC, LSR ROL, ROR and STA 133 | as well as indirect,y and absolute,y of STA 134 | 135 | 136 | It may be possible to simplify even further given the particular functions 137 | some of these share in command (where the cycle change could be placed 138 | instead) 139 | -------------------------------------------------------------------------------- /control.py: -------------------------------------------------------------------------------- 1 | import json 2 | import sys 3 | import urllib 4 | 5 | URL_PREFIX = "http://localhost:6502" 6 | 7 | 8 | def get(url): 9 | return json.loads(urllib.urlopen(URL_PREFIX + url).read()) 10 | 11 | 12 | def post(url, data=None): 13 | return urllib.urlopen(URL_PREFIX + url, json.dumps(data) if data is not None else "") 14 | 15 | 16 | def value(s): 17 | if s.startswith("$"): 18 | return int(s[1:], 16) 19 | if s.startswith("0x"): 20 | return int(s[2:], 16) 21 | return int(s) 22 | 23 | 24 | def format_disassemble(dis): 25 | r = "%04X- " % dis["address"] 26 | for i in range(3): 27 | if i < len(dis["bytes"]): 28 | r += "%02X " % dis["bytes"][i] 29 | else: 30 | r += " " 31 | r += " %s" % dis["mnemonic"] 32 | if "operand" in dis: 33 | r += " %-10s" % dis["operand"] 34 | if "memory" in dis: 35 | r += "[%04X] = %0*X" % tuple(dis["memory"]) 36 | return r 37 | 38 | 39 | def cmd_disassemble(a): 40 | """Disassemble""" 41 | if len(a) > 1: 42 | addr = value(a[1]) 43 | else: 44 | status = get("/status") 45 | addr = status["program_counter"] 46 | disasm = get("/disassemble/%d" % addr) 47 | for d in disasm: 48 | print format_disassemble(d) 49 | 50 | 51 | def cmd_dump(a): 52 | """Dump memory""" 53 | start = value(a[1]) 54 | if len(a) > 2: 55 | end = value(a[2]) 56 | else: 57 | end = start + 15 58 | data = get("/memory/%d-%d" % (start, end)) 59 | addr = start & ~0xF 60 | while addr <= end: 61 | s = "%04X-" % addr 62 | for i in range(16): 63 | if start <= addr + i <= end: 64 | s += " %02X" % data[addr + i - start] 65 | else: 66 | s += " " 67 | s += " " 68 | for i in range(16): 69 | if start <= addr + i <= end: 70 | c = data[addr + i - start] 71 | 72 | # adjust for apple character set 73 | c &= 0x3f 74 | if c < 0x20: 75 | c += 0x40 76 | 77 | if 0x20 <= c < 0x7f: 78 | s += chr(c) 79 | else: 80 | s += "." 81 | else: 82 | s += " " 83 | print s 84 | addr += 16 85 | 86 | 87 | def cmd_help(a): 88 | """Help commands""" 89 | if len(a) > 1: 90 | f = Commands.get(a[1]) 91 | if f is not None: 92 | print f.__doc__ 93 | else: 94 | print "Unknown command:", a[1] 95 | else: 96 | print "Commands:" 97 | for c in sorted(Commands): 98 | print " ", c 99 | 100 | 101 | def cmd_peek(a): 102 | """Peek memory location""" 103 | addr = value(a[1]) 104 | dump = get("/memory/%d" % addr) 105 | print "%04X: %02X" % (addr, dump[0]) 106 | 107 | 108 | def cmd_poke(a): 109 | """Poke memory location""" 110 | addr = value(a[1]) 111 | val = value(a[2]) 112 | post("/memory/%d" % addr, [val]) 113 | 114 | 115 | def cmd_status(a): 116 | """CPU status""" 117 | status = get("/status") 118 | print "A=%02X X=%02X Y=%02X S=%02X PC=%04X F=%c%c0%c%c%c%c%c" % ( 119 | status["accumulator"], 120 | status["x_index"], 121 | status["y_index"], 122 | status["stack_pointer"], 123 | status["program_counter"], 124 | "N" if status["sign_flag"] else "n", 125 | "V" if status["overflow_flag"] else "v", 126 | "B" if status["break_flag"] else "b", 127 | "D" if status["decimal_mode_flag"] else "d", 128 | "I" if status["interrupt_disable_flag"] else "i", 129 | "Z" if status["zero_flag"] else "z", 130 | "C" if status["carry_flag"] else "c", 131 | ) 132 | disasm = get("/disassemble/%d" % status["program_counter"]) 133 | print format_disassemble(disasm[0]) 134 | 135 | 136 | def cmd_quit(a): 137 | """Quit""" 138 | sys.exit(0) 139 | 140 | 141 | def cmd_reset(a): 142 | """Reset""" 143 | post("/reset") 144 | 145 | Commands = { 146 | "disassemble": cmd_disassemble, 147 | "dump": cmd_dump, 148 | "help": cmd_help, 149 | "peek": cmd_peek, 150 | "poke": cmd_poke, 151 | "status": cmd_status, 152 | "quit": cmd_quit, 153 | "reset": cmd_reset, 154 | } 155 | 156 | 157 | def main(): 158 | print "ApplePy control console" 159 | while True: 160 | s = raw_input("6502> ") 161 | a = s.strip().split() 162 | f = Commands.get(a[0]) 163 | if f is not None: 164 | f(a) 165 | else: 166 | print "Unknown command:", s 167 | 168 | if __name__ == "__main__": 169 | main() 170 | -------------------------------------------------------------------------------- /applepy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # ApplePy - an Apple ][ emulator in Python 4 | # James Tauber / http://jtauber.com/ 5 | # originally written 2001, updated 2011 6 | 7 | 8 | import numpy 9 | import pygame 10 | import select 11 | import socket 12 | import struct 13 | import subprocess 14 | import sys 15 | import time 16 | import wave 17 | 18 | 19 | class Display: 20 | 21 | characters = [ 22 | [0b00000, 0b01110, 0b10001, 0b10101, 0b10111, 0b10110, 0b10000, 0b01111], 23 | [0b00000, 0b00100, 0b01010, 0b10001, 0b10001, 0b11111, 0b10001, 0b10001], 24 | [0b00000, 0b11110, 0b10001, 0b10001, 0b11110, 0b10001, 0b10001, 0b11110], 25 | [0b00000, 0b01110, 0b10001, 0b10000, 0b10000, 0b10000, 0b10001, 0b01110], 26 | [0b00000, 0b11110, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b11110], 27 | [0b00000, 0b11111, 0b10000, 0b10000, 0b11110, 0b10000, 0b10000, 0b11111], 28 | [0b00000, 0b11111, 0b10000, 0b10000, 0b11110, 0b10000, 0b10000, 0b10000], 29 | [0b00000, 0b01111, 0b10000, 0b10000, 0b10000, 0b10011, 0b10001, 0b01111], 30 | [0b00000, 0b10001, 0b10001, 0b10001, 0b11111, 0b10001, 0b10001, 0b10001], 31 | [0b00000, 0b01110, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b01110], 32 | [0b00000, 0b00001, 0b00001, 0b00001, 0b00001, 0b00001, 0b10001, 0b01110], 33 | [0b00000, 0b10001, 0b10010, 0b10100, 0b11000, 0b10100, 0b10010, 0b10001], 34 | [0b00000, 0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b11111], 35 | [0b00000, 0b10001, 0b11011, 0b10101, 0b10101, 0b10001, 0b10001, 0b10001], 36 | [0b00000, 0b10001, 0b10001, 0b11001, 0b10101, 0b10011, 0b10001, 0b10001], 37 | [0b00000, 0b01110, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b01110], 38 | [0b00000, 0b11110, 0b10001, 0b10001, 0b11110, 0b10000, 0b10000, 0b10000], 39 | [0b00000, 0b01110, 0b10001, 0b10001, 0b10001, 0b10101, 0b10010, 0b01101], 40 | [0b00000, 0b11110, 0b10001, 0b10001, 0b11110, 0b10100, 0b10010, 0b10001], 41 | [0b00000, 0b01110, 0b10001, 0b10000, 0b01110, 0b00001, 0b10001, 0b01110], 42 | [0b00000, 0b11111, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100], 43 | [0b00000, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b01110], 44 | [0b00000, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b01010, 0b00100], 45 | [0b00000, 0b10001, 0b10001, 0b10001, 0b10101, 0b10101, 0b11011, 0b10001], 46 | [0b00000, 0b10001, 0b10001, 0b01010, 0b00100, 0b01010, 0b10001, 0b10001], 47 | [0b00000, 0b10001, 0b10001, 0b01010, 0b00100, 0b00100, 0b00100, 0b00100], 48 | [0b00000, 0b11111, 0b00001, 0b00010, 0b00100, 0b01000, 0b10000, 0b11111], 49 | [0b00000, 0b11111, 0b11000, 0b11000, 0b11000, 0b11000, 0b11000, 0b11111], 50 | [0b00000, 0b00000, 0b10000, 0b01000, 0b00100, 0b00010, 0b00001, 0b00000], 51 | [0b00000, 0b11111, 0b00011, 0b00011, 0b00011, 0b00011, 0b00011, 0b11111], 52 | [0b00000, 0b00000, 0b00000, 0b00100, 0b01010, 0b10001, 0b00000, 0b00000], 53 | [0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111], 54 | [0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000], 55 | [0b00000, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b00000, 0b00100], 56 | [0b00000, 0b01010, 0b01010, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000], 57 | [0b00000, 0b01010, 0b01010, 0b11111, 0b01010, 0b11111, 0b01010, 0b01010], 58 | [0b00000, 0b00100, 0b01111, 0b10100, 0b01110, 0b00101, 0b11110, 0b00100], 59 | [0b00000, 0b11000, 0b11001, 0b00010, 0b00100, 0b01000, 0b10011, 0b00011], 60 | [0b00000, 0b01000, 0b10100, 0b10100, 0b01000, 0b10101, 0b10010, 0b01101], 61 | [0b00000, 0b00100, 0b00100, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000], 62 | [0b00000, 0b00100, 0b01000, 0b10000, 0b10000, 0b10000, 0b01000, 0b00100], 63 | [0b00000, 0b00100, 0b00010, 0b00001, 0b00001, 0b00001, 0b00010, 0b00100], 64 | [0b00000, 0b00100, 0b10101, 0b01110, 0b00100, 0b01110, 0b10101, 0b00100], 65 | [0b00000, 0b00000, 0b00100, 0b00100, 0b11111, 0b00100, 0b00100, 0b00000], 66 | [0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00100, 0b00100, 0b01000], 67 | [0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b00000, 0b00000, 0b00000], 68 | [0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00100], 69 | [0b00000, 0b00000, 0b00001, 0b00010, 0b00100, 0b01000, 0b10000, 0b00000], 70 | [0b00000, 0b01110, 0b10001, 0b10011, 0b10101, 0b11001, 0b10001, 0b01110], 71 | [0b00000, 0b00100, 0b01100, 0b00100, 0b00100, 0b00100, 0b00100, 0b01110], 72 | [0b00000, 0b01110, 0b10001, 0b00001, 0b00110, 0b01000, 0b10000, 0b11111], 73 | [0b00000, 0b11111, 0b00001, 0b00010, 0b00110, 0b00001, 0b10001, 0b01110], 74 | [0b00000, 0b00010, 0b00110, 0b01010, 0b10010, 0b11111, 0b00010, 0b00010], 75 | [0b00000, 0b11111, 0b10000, 0b11110, 0b00001, 0b00001, 0b10001, 0b01110], 76 | [0b00000, 0b00111, 0b01000, 0b10000, 0b11110, 0b10001, 0b10001, 0b01110], 77 | [0b00000, 0b11111, 0b00001, 0b00010, 0b00100, 0b01000, 0b01000, 0b01000], 78 | [0b00000, 0b01110, 0b10001, 0b10001, 0b01110, 0b10001, 0b10001, 0b01110], 79 | [0b00000, 0b01110, 0b10001, 0b10001, 0b01111, 0b00001, 0b00010, 0b11100], 80 | [0b00000, 0b00000, 0b00000, 0b00100, 0b00000, 0b00100, 0b00000, 0b00000], 81 | [0b00000, 0b00000, 0b00000, 0b00100, 0b00000, 0b00100, 0b00100, 0b01000], 82 | [0b00000, 0b00010, 0b00100, 0b01000, 0b10000, 0b01000, 0b00100, 0b00010], 83 | [0b00000, 0b00000, 0b00000, 0b11111, 0b00000, 0b11111, 0b00000, 0b00000], 84 | [0b00000, 0b01000, 0b00100, 0b00010, 0b00001, 0b00010, 0b00100, 0b01000], 85 | [0b00000, 0b01110, 0b10001, 0b00010, 0b00100, 0b00100, 0b00000, 0b00100] 86 | ] 87 | 88 | lores_colours = [ 89 | (0, 0, 0), # black 90 | (208, 0, 48), # magenta / dark red 91 | (0, 0, 128), # dark blue 92 | (255, 0, 255), # purple / violet 93 | (0, 128, 0), # dark green 94 | (128, 128, 128), # gray 1 95 | (0, 0, 255), # medium blue / blue 96 | (96, 160, 255), # light blue 97 | (128, 80, 0), # brown / dark orange 98 | (255, 128, 0), # orange 99 | (192, 192, 192), # gray 2 100 | (255, 144, 128), # pink / light red 101 | (0, 255, 0), # light green / green 102 | (255, 255, 0), # yellow / light orange 103 | (64, 255, 144), # aquamarine / light green 104 | (255, 255, 255), # white 105 | ] 106 | 107 | def __init__(self): 108 | self.screen = pygame.display.set_mode((560, 384)) 109 | pygame.display.set_caption("ApplePy") 110 | self.mix = False 111 | self.flash_time = time.time() 112 | self.flash_on = False 113 | self.flash_chars = [[0] * 0x400] * 2 114 | 115 | self.page = 1 116 | self.text = True 117 | self.colour = False 118 | 119 | self.chargen = [] 120 | for c in self.characters: 121 | chars = [[pygame.Surface((14, 16)), pygame.Surface((14, 16))], 122 | [pygame.Surface((14, 16)), pygame.Surface((14, 16))]] 123 | for colour in (0, 1): 124 | hue = (255, 255, 255) if colour else (0, 200, 0) 125 | for inv in (0, 1): 126 | pixels = pygame.PixelArray(chars[colour][inv]) 127 | off = hue if inv else (0, 0, 0) 128 | on = (0, 0, 0) if inv else hue 129 | for row in range(8): 130 | b = c[row] << 1 131 | for col in range(7): 132 | bit = (b >> (6 - col)) & 1 133 | pixels[2 * col][2 * row] = on if bit else off 134 | pixels[2 * col + 1][2 * row] = on if bit else off 135 | del pixels 136 | self.chargen.append(chars) 137 | 138 | def txtclr(self): 139 | self.text = False 140 | 141 | def txtset(self): 142 | self.text = True 143 | self.colour = False 144 | 145 | def mixclr(self): 146 | self.mix = False 147 | 148 | def mixset(self): 149 | self.mix = True 150 | self.colour = True 151 | 152 | def lowscr(self): 153 | self.page = 1 154 | 155 | def hiscr(self): 156 | self.page = 2 157 | 158 | def lores(self): 159 | self.high_res = False 160 | 161 | def hires(self): 162 | self.high_res = True 163 | 164 | def update(self, address, value): 165 | if self.page == 1: 166 | start_text = 0x400 167 | start_hires = 0x2000 168 | elif self.page == 2: 169 | start_text = 0x800 170 | start_hires = 0x4000 171 | else: 172 | return 173 | 174 | if start_text <= address <= start_text + 0x3FF: 175 | base = address - start_text 176 | self.flash_chars[self.page - 1][base] = value 177 | hi, lo = divmod(base, 0x80) 178 | row_group, column = divmod(lo, 0x28) 179 | row = hi + 8 * row_group 180 | 181 | if row_group == 3: 182 | return 183 | 184 | if self.text or not self.mix or not row < 20: 185 | mode, ch = divmod(value, 0x40) 186 | 187 | if mode == 0: 188 | inv = True 189 | elif mode == 1: 190 | inv = self.flash_on 191 | else: 192 | inv = False 193 | 194 | self.screen.blit(self.chargen[ch][self.colour][inv], (2 * (column * 7), 2 * (row * 8))) 195 | else: 196 | pixels = pygame.PixelArray(self.screen) 197 | if not self.high_res: 198 | lower, upper = divmod(value, 0x10) 199 | 200 | for dx in range(14): 201 | for dy in range(8): 202 | x = column * 14 + dx 203 | y = row * 16 + dy 204 | pixels[x][y] = self.lores_colours[upper] 205 | for dy in range(8, 16): 206 | x = column * 14 + dx 207 | y = row * 16 + dy 208 | pixels[x][y] = self.lores_colours[lower] 209 | del pixels 210 | 211 | elif start_hires <= address <= start_hires + 0x1FFF: 212 | if self.high_res: 213 | base = address - start_hires 214 | row8, b = divmod(base, 0x400) 215 | hi, lo = divmod(b, 0x80) 216 | row_group, column = divmod(lo, 0x28) 217 | row = 8 * (hi + 8 * row_group) + row8 218 | 219 | if self.mix and row >= 160: 220 | return 221 | 222 | if row < 192 and column < 40: 223 | 224 | pixels = pygame.PixelArray(self.screen) 225 | msb = value // 0x80 226 | 227 | for b in range(7): 228 | c = value & (1 << b) 229 | xx = (column * 7 + b) 230 | x = 2 * xx 231 | y = 2 * row 232 | 233 | if msb: 234 | if xx % 2: 235 | pixels[x][y] = (0, 0, 0) 236 | # orange 237 | pixels[x][y] = (255, 192, 0) if c else (0, 0, 0) # @@@ 238 | pixels[x + 1][y] = (255, 192, 0) if c else (0, 0, 0) 239 | else: 240 | # blue 241 | pixels[x][y] = (0, 192, 255) if c else (0, 0, 0) 242 | pixels[x + 1][y] = (0, 0, 0) 243 | pixels[x + 1][y] = (0, 192, 255) if c else (0, 0, 0) # @@@ 244 | else: 245 | if xx % 2: 246 | pixels[x][y] = (0, 0, 0) 247 | # green 248 | pixels[x][y] = (0, 255, 0) if c else (0, 0, 0) # @@@ 249 | pixels[x + 1][y] = (0, 255, 0) if c else (0, 0, 0) 250 | else: 251 | # violet 252 | pixels[x][y] = (255, 0, 255) if c else (0, 0, 0) 253 | pixels[x + 1][y] = (0, 0, 0) 254 | pixels[x + 1][y] = (255, 0, 255) if c else (0, 0, 0) # @@@ 255 | 256 | pixels[x][y + 1] = (0, 0, 0) 257 | pixels[x + 1][y + 1] = (0, 0, 0) 258 | 259 | del pixels 260 | 261 | def flash(self): 262 | if time.time() - self.flash_time >= 0.5: 263 | self.flash_on = not self.flash_on 264 | for offset, char in enumerate(self.flash_chars[self.page - 1]): 265 | if (char & 0xC0) == 0x40: 266 | self.update(0x400 + offset, char) 267 | self.flash_time = time.time() 268 | 269 | 270 | class Speaker: 271 | 272 | CPU_CYCLES_PER_SAMPLE = 60 273 | CHECK_INTERVAL = 1000 274 | 275 | def __init__(self): 276 | pygame.mixer.pre_init(11025, -16, 1) 277 | pygame.init() 278 | self.reset() 279 | 280 | def toggle(self, cycle): 281 | if self.last_toggle is not None: 282 | l = (cycle - self.last_toggle) / Speaker.CPU_CYCLES_PER_SAMPLE 283 | self.buffer.extend([0, 26000] if self.polarity else [0, -2600]) 284 | self.buffer.extend((l - 2) * [16384] if self.polarity else [-16384]) 285 | self.polarity = not self.polarity 286 | self.last_toggle = cycle 287 | 288 | def reset(self): 289 | self.last_toggle = None 290 | self.buffer = [] 291 | self.polarity = False 292 | 293 | def play(self): 294 | sample_array = numpy.int16(self.buffer) 295 | sound = pygame.sndarray.make_sound(sample_array) 296 | sound.play() 297 | self.reset() 298 | 299 | def update(self, cycle): 300 | if self.buffer and (cycle - self.last_toggle) > self.CHECK_INTERVAL: 301 | self.play() 302 | 303 | 304 | class Cassette: 305 | 306 | def __init__(self, fn): 307 | wav = wave.open(fn, "r") 308 | self.raw = wav.readframes(wav.getnframes()) 309 | self.start_cycle = 0 310 | self.start_offset = 0 311 | 312 | for i, b in enumerate(self.raw): 313 | if ord(b) > 0xA0: 314 | self.start_offset = i 315 | break 316 | 317 | def read_byte(self, cycle): 318 | if self.start_cycle == 0: 319 | self.start_cycle = cycle 320 | offset = self.start_offset + (cycle - self.start_cycle) * 22000 / 1000000 321 | return ord(self.raw[offset]) if offset < len(self.raw) else 0x80 322 | 323 | 324 | class SoftSwitches: 325 | 326 | def __init__(self, display, speaker, cassette): 327 | self.kbd = 0x00 328 | self.display = display 329 | self.speaker = speaker 330 | self.cassette = cassette 331 | 332 | def read_byte(self, cycle, address): 333 | assert 0xC000 <= address <= 0xCFFF 334 | if address == 0xC000: 335 | return self.kbd 336 | elif address == 0xC010: 337 | self.kbd = self.kbd & 0x7F 338 | elif address == 0xC030: 339 | if self.speaker: 340 | self.speaker.toggle(cycle) 341 | elif address == 0xC050: 342 | self.display.txtclr() 343 | elif address == 0xC051: 344 | self.display.txtset() 345 | elif address == 0xC052: 346 | self.display.mixclr() 347 | elif address == 0xC053: 348 | self.display.mixset() 349 | elif address == 0xC054: 350 | self.display.lowscr() 351 | elif address == 0xC055: 352 | self.display.hiscr() 353 | elif address == 0xC056: 354 | self.display.lores() 355 | elif address == 0xC057: 356 | self.display.hires() 357 | elif address == 0xC060: 358 | if self.cassette: 359 | return self.cassette.read_byte(cycle) 360 | else: 361 | pass # print "%04X" % address 362 | return 0x00 363 | 364 | 365 | class Apple2: 366 | 367 | def __init__(self, options, display, speaker, cassette): 368 | self.display = display 369 | self.speaker = speaker 370 | self.softswitches = SoftSwitches(display, speaker, cassette) 371 | 372 | listener = socket.socket() 373 | listener.bind(("127.0.0.1", 0)) 374 | listener.listen(0) 375 | 376 | args = [ 377 | sys.executable, 378 | "cpu6502.py", 379 | "--bus", str(listener.getsockname()[1]), 380 | "--rom", options.rom, 381 | ] 382 | if options.ram: 383 | args.extend([ 384 | "--ram", options.ram, 385 | ]) 386 | if options.pc is not None: 387 | args.extend([ 388 | "--pc", str(options.pc), 389 | ]) 390 | self.core = subprocess.Popen(args) 391 | 392 | rs, _, _ = select.select([listener], [], [], 2) 393 | if not rs: 394 | print >>sys.stderr, "CPU module did not start" 395 | sys.exit(1) 396 | self.cpu, _ = listener.accept() 397 | 398 | def run(self): 399 | update_cycle = 0 400 | quit = False 401 | while not quit: 402 | op = self.cpu.recv(8) 403 | if len(op) == 0: 404 | break 405 | cycle, rw, addr, val = struct.unpack("= 1024: 430 | self.display.flash() 431 | pygame.display.flip() 432 | if self.speaker: 433 | self.speaker.update(cycle) 434 | update_cycle = 0 435 | 436 | 437 | def usage(): 438 | print >>sys.stderr, "ApplePy - an Apple ][ emulator in Python" 439 | print >>sys.stderr, "James Tauber / http://jtauber.com/" 440 | print >>sys.stderr 441 | print >>sys.stderr, "Usage: applepy.py [options]" 442 | print >>sys.stderr 443 | print >>sys.stderr, " -c, --cassette Cassette wav file to load" 444 | print >>sys.stderr, " -R, --rom ROM file to use (default A2ROM.BIN)" 445 | print >>sys.stderr, " -r, --ram RAM file to load (default none)" 446 | print >>sys.stderr, " -p, --pc Initial PC value" 447 | print >>sys.stderr, " -q, --quiet Quiet mode, no sounds (default sounds)" 448 | sys.exit(1) 449 | 450 | 451 | def get_options(): 452 | class Options: 453 | def __init__(self): 454 | self.cassette = None 455 | self.rom = "A2ROM.BIN" 456 | self.ram = None 457 | self.pc = None 458 | self.quiet = False 459 | 460 | options = Options() 461 | a = 1 462 | while a < len(sys.argv): 463 | if sys.argv[a].startswith("-"): 464 | if sys.argv[a] in ("-c", "--cassette"): 465 | a += 1 466 | options.cassette = sys.argv[a] 467 | elif sys.argv[a] in ("-R", "--rom"): 468 | a += 1 469 | options.rom = sys.argv[a] 470 | elif sys.argv[a] in ("-r", "--ram"): 471 | a += 1 472 | options.ram = sys.argv[a] 473 | elif sys.argv[a] in ("-p", "--pc"): 474 | a += 1 475 | options.pc = int(sys.argv[a]) 476 | elif sys.argv[a] in ("-q", "--quiet"): 477 | options.quiet = True 478 | else: 479 | usage() 480 | else: 481 | usage() 482 | a += 1 483 | 484 | return options 485 | 486 | 487 | if __name__ == "__main__": 488 | options = get_options() 489 | display = Display() 490 | speaker = None if options.quiet else Speaker() 491 | cassette = Cassette(options.cassette) if options.cassette else None 492 | 493 | apple = Apple2(options, display, speaker, cassette) 494 | apple.run() 495 | -------------------------------------------------------------------------------- /tests.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from cpu6502 import Memory, CPU 3 | 4 | 5 | class TestMemory(unittest.TestCase): 6 | 7 | def setUp(self): 8 | self.memory = Memory(use_bus=False) 9 | 10 | def test_load(self): 11 | self.memory.load(0x1000, [0x01, 0x02, 0x03]) 12 | self.assertEqual(self.memory.read_byte(None, 0x1000), 0x01) 13 | self.assertEqual(self.memory.read_byte(None, 0x1001), 0x02) 14 | self.assertEqual(self.memory.read_byte(None, 0x1002), 0x03) 15 | 16 | def test_write(self): 17 | self.memory.write_byte(None, 0x1000, 0x11) 18 | self.memory.write_byte(None, 0x1001, 0x12) 19 | self.memory.write_byte(None, 0x1002, 0x13) 20 | self.assertEqual(self.memory.read_byte(None, 0x1000), 0x11) 21 | self.assertEqual(self.memory.read_byte(None, 0x1001), 0x12) 22 | self.assertEqual(self.memory.read_byte(None, 0x1002), 0x13) 23 | 24 | 25 | class TestLoadStoreOperations(unittest.TestCase): 26 | 27 | def setUp(self): 28 | self.memory = Memory(use_bus=False) 29 | self.cpu = CPU(self.memory) 30 | self.memory.load(0x1000, [0x00, 0x01, 0x7F, 0x80, 0xFF]) 31 | 32 | def test_LDA(self): 33 | self.cpu.LDA(0x1000) 34 | self.assertEqual(self.cpu.accumulator, 0x00) 35 | self.assertEqual(self.cpu.sign_flag, 0) 36 | self.assertEqual(self.cpu.zero_flag, 1) 37 | self.cpu.LDA(0x1001) 38 | self.assertEqual(self.cpu.accumulator, 0x01) 39 | self.assertEqual(self.cpu.sign_flag, 0) 40 | self.assertEqual(self.cpu.zero_flag, 0) 41 | self.cpu.LDA(0x1002) 42 | self.assertEqual(self.cpu.accumulator, 0x7F) 43 | self.assertEqual(self.cpu.sign_flag, 0) 44 | self.assertEqual(self.cpu.zero_flag, 0) 45 | self.cpu.LDA(0x1003) 46 | self.assertEqual(self.cpu.accumulator, 0x80) 47 | self.assertEqual(self.cpu.sign_flag, 1) 48 | self.assertEqual(self.cpu.zero_flag, 0) 49 | self.cpu.LDA(0x1004) 50 | self.assertEqual(self.cpu.accumulator, 0xFF) 51 | self.assertEqual(self.cpu.sign_flag, 1) 52 | self.assertEqual(self.cpu.zero_flag, 0) 53 | 54 | def test_LDX(self): 55 | self.cpu.LDX(0x1000) 56 | self.assertEqual(self.cpu.x_index, 0x00) 57 | self.assertEqual(self.cpu.sign_flag, 0) 58 | self.assertEqual(self.cpu.zero_flag, 1) 59 | self.cpu.LDX(0x1001) 60 | self.assertEqual(self.cpu.x_index, 0x01) 61 | self.assertEqual(self.cpu.sign_flag, 0) 62 | self.assertEqual(self.cpu.zero_flag, 0) 63 | self.cpu.LDX(0x1002) 64 | self.assertEqual(self.cpu.x_index, 0x7F) 65 | self.assertEqual(self.cpu.sign_flag, 0) 66 | self.assertEqual(self.cpu.zero_flag, 0) 67 | self.cpu.LDX(0x1003) 68 | self.assertEqual(self.cpu.x_index, 0x80) 69 | self.assertEqual(self.cpu.sign_flag, 1) 70 | self.assertEqual(self.cpu.zero_flag, 0) 71 | self.cpu.LDX(0x1004) 72 | self.assertEqual(self.cpu.x_index, 0xFF) 73 | self.assertEqual(self.cpu.sign_flag, 1) 74 | self.assertEqual(self.cpu.zero_flag, 0) 75 | 76 | def test_LDY(self): 77 | self.cpu.LDY(0x1000) 78 | self.assertEqual(self.cpu.y_index, 0x00) 79 | self.assertEqual(self.cpu.sign_flag, 0) 80 | self.assertEqual(self.cpu.zero_flag, 1) 81 | self.cpu.LDY(0x1001) 82 | self.assertEqual(self.cpu.y_index, 0x01) 83 | self.assertEqual(self.cpu.sign_flag, 0) 84 | self.assertEqual(self.cpu.zero_flag, 0) 85 | self.cpu.LDY(0x1002) 86 | self.assertEqual(self.cpu.y_index, 0x7F) 87 | self.assertEqual(self.cpu.sign_flag, 0) 88 | self.assertEqual(self.cpu.zero_flag, 0) 89 | self.cpu.LDY(0x1003) 90 | self.assertEqual(self.cpu.y_index, 0x80) 91 | self.assertEqual(self.cpu.sign_flag, 1) 92 | self.assertEqual(self.cpu.zero_flag, 0) 93 | self.cpu.LDY(0x1004) 94 | self.assertEqual(self.cpu.y_index, 0xFF) 95 | self.assertEqual(self.cpu.sign_flag, 1) 96 | self.assertEqual(self.cpu.zero_flag, 0) 97 | 98 | def test_STA(self): 99 | self.cpu.accumulator = 0x37 100 | self.cpu.STA(0x2000) 101 | self.assertEqual(self.memory.read_byte(None, 0x2000), 0x37) 102 | 103 | def test_STX(self): 104 | self.cpu.x_index = 0x38 105 | self.cpu.STX(0x2000) 106 | self.assertEqual(self.memory.read_byte(None, 0x2000), 0x38) 107 | 108 | def test_STY(self): 109 | self.cpu.y_index = 0x39 110 | self.cpu.STY(0x2000) 111 | self.assertEqual(self.memory.read_byte(None, 0x2000), 0x39) 112 | 113 | 114 | class TestRegisterTransferOperations(unittest.TestCase): 115 | 116 | def setUp(self): 117 | self.memory = Memory(use_bus=False) 118 | self.cpu = CPU(self.memory) 119 | 120 | def test_TAX(self): 121 | self.cpu.accumulator = 0x00 122 | self.cpu.TAX() 123 | self.assertEqual(self.cpu.x_index, 0x00) 124 | self.assertEqual(self.cpu.sign_flag, 0) 125 | self.assertEqual(self.cpu.zero_flag, 1) 126 | self.cpu.accumulator = 0x01 127 | self.cpu.TAX() 128 | self.assertEqual(self.cpu.x_index, 0x01) 129 | self.assertEqual(self.cpu.sign_flag, 0) 130 | self.assertEqual(self.cpu.zero_flag, 0) 131 | self.cpu.accumulator = 0xFF 132 | self.cpu.TAX() 133 | self.assertEqual(self.cpu.x_index, 0xFF) 134 | self.assertEqual(self.cpu.sign_flag, 1) 135 | self.assertEqual(self.cpu.zero_flag, 0) 136 | 137 | def test_TAY(self): 138 | self.cpu.accumulator = 0x00 139 | self.cpu.TAY() 140 | self.assertEqual(self.cpu.y_index, 0x00) 141 | self.assertEqual(self.cpu.sign_flag, 0) 142 | self.assertEqual(self.cpu.zero_flag, 1) 143 | self.cpu.accumulator = 0x01 144 | self.cpu.TAY() 145 | self.assertEqual(self.cpu.y_index, 0x01) 146 | self.assertEqual(self.cpu.sign_flag, 0) 147 | self.assertEqual(self.cpu.zero_flag, 0) 148 | self.cpu.accumulator = 0xFF 149 | self.cpu.TAY() 150 | self.assertEqual(self.cpu.y_index, 0xFF) 151 | self.assertEqual(self.cpu.sign_flag, 1) 152 | self.assertEqual(self.cpu.zero_flag, 0) 153 | 154 | def test_TXA(self): 155 | self.cpu.x_index = 0x00 156 | self.cpu.TXA() 157 | self.assertEqual(self.cpu.accumulator, 0x00) 158 | self.assertEqual(self.cpu.sign_flag, 0) 159 | self.assertEqual(self.cpu.zero_flag, 1) 160 | self.cpu.x_index = 0x01 161 | self.cpu.TXA() 162 | self.assertEqual(self.cpu.accumulator, 0x01) 163 | self.assertEqual(self.cpu.sign_flag, 0) 164 | self.assertEqual(self.cpu.zero_flag, 0) 165 | self.cpu.x_index = 0xFF 166 | self.cpu.TXA() 167 | self.assertEqual(self.cpu.accumulator, 0xFF) 168 | self.assertEqual(self.cpu.sign_flag, 1) 169 | self.assertEqual(self.cpu.zero_flag, 0) 170 | 171 | def test_TYA(self): 172 | self.cpu.y_index = 0x00 173 | self.cpu.TYA() 174 | self.assertEqual(self.cpu.accumulator, 0x00) 175 | self.assertEqual(self.cpu.sign_flag, 0) 176 | self.assertEqual(self.cpu.zero_flag, 1) 177 | self.cpu.y_index = 0x01 178 | self.cpu.TYA() 179 | self.assertEqual(self.cpu.accumulator, 0x01) 180 | self.assertEqual(self.cpu.sign_flag, 0) 181 | self.assertEqual(self.cpu.zero_flag, 0) 182 | self.cpu.y_index = 0xFF 183 | self.cpu.TYA() 184 | self.assertEqual(self.cpu.accumulator, 0xFF) 185 | self.assertEqual(self.cpu.sign_flag, 1) 186 | self.assertEqual(self.cpu.zero_flag, 0) 187 | 188 | 189 | class TestStackOperations(unittest.TestCase): 190 | 191 | def setUp(self): 192 | self.memory = Memory(use_bus=False) 193 | self.cpu = CPU(self.memory) 194 | 195 | def test_TSX(self): 196 | s = self.cpu.stack_pointer 197 | self.cpu.TSX() 198 | self.assertEqual(self.cpu.x_index, s) 199 | # @@@ check NZ? 200 | 201 | def test_TXS(self): 202 | x = self.cpu.x_index 203 | self.cpu.TXS() 204 | self.assertEqual(self.cpu.stack_pointer, x) 205 | 206 | def test_PHA_and_PLA(self): 207 | self.cpu.accumulator = 0x00 208 | self.cpu.PHA() 209 | self.cpu.accumulator = 0x01 210 | self.cpu.PHA() 211 | self.cpu.accumulator = 0xFF 212 | self.cpu.PHA() 213 | self.assertEqual(self.cpu.accumulator, 0xFF) 214 | self.assertEqual(self.cpu.zero_flag, 0) 215 | self.assertEqual(self.cpu.sign_flag, 0) 216 | self.cpu.PLA() 217 | self.assertEqual(self.cpu.accumulator, 0xFF) 218 | self.assertEqual(self.cpu.zero_flag, 0) 219 | self.assertEqual(self.cpu.sign_flag, 1) 220 | self.cpu.PLA() 221 | self.assertEqual(self.cpu.accumulator, 0x01) 222 | self.assertEqual(self.cpu.zero_flag, 0) 223 | self.assertEqual(self.cpu.sign_flag, 0) 224 | self.cpu.PLA() 225 | self.assertEqual(self.cpu.accumulator, 0x00) 226 | self.assertEqual(self.cpu.zero_flag, 1) 227 | self.assertEqual(self.cpu.sign_flag, 0) 228 | 229 | def test_PHP_and_PLP(self): 230 | p = self.cpu.status_as_byte() 231 | self.cpu.PHP() 232 | self.cpu.status_from_byte(0xFF) 233 | self.cpu.PLP() 234 | self.assertEqual(self.cpu.status_as_byte(), p) 235 | 236 | 237 | class TestLogicalOperations(unittest.TestCase): 238 | 239 | def setUp(self): 240 | self.memory = Memory(use_bus=False) 241 | self.cpu = CPU(self.memory) 242 | 243 | def test_AND(self): 244 | self.memory.write_byte(None, 0x1000, 0x37) 245 | self.cpu.accumulator = 0x34 246 | self.cpu.AND(0x1000) 247 | self.assertEqual(self.cpu.accumulator, 0x34) 248 | self.assertEqual(self.cpu.zero_flag, 0) 249 | self.assertEqual(self.cpu.sign_flag, 0) 250 | self.cpu.accumulator = 0x40 251 | self.cpu.AND(0x1000) 252 | self.assertEqual(self.cpu.accumulator, 0x00) 253 | self.assertEqual(self.cpu.zero_flag, 1) 254 | self.assertEqual(self.cpu.sign_flag, 0) 255 | 256 | def test_EOR(self): 257 | self.memory.write_byte(None, 0x1000, 0x37) 258 | self.cpu.accumulator = 0x34 259 | self.cpu.EOR(0x1000) 260 | self.assertEqual(self.cpu.accumulator, 0x03) 261 | self.assertEqual(self.cpu.zero_flag, 0) 262 | self.assertEqual(self.cpu.sign_flag, 0) 263 | self.cpu.accumulator = 0x90 264 | self.cpu.EOR(0x1000) 265 | self.assertEqual(self.cpu.accumulator, 0xA7) 266 | self.assertEqual(self.cpu.zero_flag, 0) 267 | self.assertEqual(self.cpu.sign_flag, 1) 268 | self.cpu.accumulator = 0x37 269 | self.cpu.EOR(0x1000) 270 | self.assertEqual(self.cpu.accumulator, 0x00) 271 | self.assertEqual(self.cpu.zero_flag, 1) 272 | self.assertEqual(self.cpu.sign_flag, 0) 273 | 274 | def test_ORA(self): 275 | self.memory.write_byte(None, 0x1000, 0x37) 276 | self.cpu.accumulator = 0x34 277 | self.cpu.ORA(0x1000) 278 | self.assertEqual(self.cpu.accumulator, 0x37) 279 | self.assertEqual(self.cpu.zero_flag, 0) 280 | self.assertEqual(self.cpu.sign_flag, 0) 281 | self.cpu.accumulator = 0x90 282 | self.cpu.ORA(0x1000) 283 | self.assertEqual(self.cpu.accumulator, 0xB7) 284 | self.assertEqual(self.cpu.zero_flag, 0) 285 | self.assertEqual(self.cpu.sign_flag, 1) 286 | self.cpu.accumulator = 0x00 287 | self.cpu.ORA(0x1001) 288 | self.assertEqual(self.cpu.accumulator, 0x00) 289 | self.assertEqual(self.cpu.zero_flag, 1) 290 | self.assertEqual(self.cpu.sign_flag, 0) 291 | 292 | def test_BIT(self): 293 | self.memory.write_byte(None, 0x1000, 0x00) 294 | self.cpu.accumulator = 0x00 295 | self.cpu.BIT(0x1000) 296 | self.assertEqual(self.cpu.overflow_flag, 0) 297 | self.assertEqual(self.cpu.sign_flag, 0) 298 | self.assertEqual(self.cpu.zero_flag, 1) 299 | self.memory.write_byte(None, 0x1000, 0x40) 300 | self.cpu.accumulator = 0x00 301 | self.cpu.BIT(0x1000) 302 | self.assertEqual(self.cpu.overflow_flag, 1) 303 | self.assertEqual(self.cpu.sign_flag, 0) 304 | self.assertEqual(self.cpu.zero_flag, 1) 305 | self.memory.write_byte(None, 0x1000, 0x80) 306 | self.cpu.accumulator = 0x00 307 | self.cpu.BIT(0x1000) 308 | self.assertEqual(self.cpu.overflow_flag, 0) 309 | self.assertEqual(self.cpu.sign_flag, 1) 310 | self.assertEqual(self.cpu.zero_flag, 1) 311 | self.memory.write_byte(None, 0x1000, 0xC0) 312 | self.cpu.accumulator = 0x00 313 | self.cpu.BIT(0x1000) 314 | self.assertEqual(self.cpu.overflow_flag, 1) 315 | self.assertEqual(self.cpu.sign_flag, 1) 316 | self.assertEqual(self.cpu.zero_flag, 1) 317 | self.memory.write_byte(None, 0x1000, 0xC0) 318 | self.cpu.accumulator = 0xC0 319 | self.cpu.BIT(0x1000) 320 | self.assertEqual(self.cpu.overflow_flag, 1) 321 | self.assertEqual(self.cpu.sign_flag, 1) 322 | self.assertEqual(self.cpu.zero_flag, 0) 323 | 324 | 325 | class TestArithmeticOperations(unittest.TestCase): 326 | 327 | def setUp(self): 328 | self.memory = Memory(use_bus=False) 329 | self.cpu = CPU(self.memory) 330 | 331 | def test_ADC_without_BCD(self): 332 | 333 | ## test cases from http://www.6502.org/tutorials/vflag.html 334 | 335 | # 1 + 1 = 2 (C = 0; V = 0) 336 | self.cpu.carry_flag = 0 337 | self.cpu.accumulator = 0x01 338 | self.memory.write_byte(None, 0x1000, 0x01) 339 | self.cpu.ADC(0x1000) 340 | self.assertEqual(self.cpu.accumulator, 0x02) 341 | self.assertEqual(self.cpu.carry_flag, 0) 342 | self.assertEqual(self.cpu.overflow_flag, 0) 343 | 344 | # 1 + -1 = 0 (C = 1; V = 0) 345 | self.cpu.carry_flag = 0 346 | self.cpu.accumulator = 0x01 347 | self.memory.write_byte(None, 0x1000, 0xFF) 348 | self.cpu.ADC(0x1000) 349 | self.assertEqual(self.cpu.accumulator, 0x00) 350 | self.assertEqual(self.cpu.carry_flag, 1) 351 | self.assertEqual(self.cpu.overflow_flag, 0) 352 | 353 | # 127 + 1 = 128 (C = 0; V = 1) 354 | self.cpu.carry_flag = 0 355 | self.cpu.accumulator = 0x7F 356 | self.memory.write_byte(None, 0x1000, 0x01) 357 | self.cpu.ADC(0x1000) 358 | self.assertEqual(self.cpu.accumulator, 0x80) # @@@ 359 | self.assertEqual(self.cpu.carry_flag, 0) 360 | self.assertEqual(self.cpu.overflow_flag, 1) 361 | 362 | # -128 + -1 = -129 (C = 1; V = 1) 363 | self.cpu.carry_flag = 0 364 | self.cpu.accumulator = 0x80 365 | self.memory.write_byte(None, 0x1000, 0xFF) 366 | self.cpu.ADC(0x1000) 367 | self.assertEqual(self.cpu.accumulator, 0x7F) # @@@ 368 | self.assertEqual(self.cpu.carry_flag, 1) 369 | self.assertEqual(self.cpu.overflow_flag, 1) 370 | 371 | # 63 + 64 + 1 = 128 (C = 0; V = 1) 372 | self.cpu.carry_flag = 1 373 | self.cpu.accumulator = 0x3F 374 | self.memory.write_byte(None, 0x1000, 0x40) 375 | self.cpu.ADC(0x1000) 376 | self.assertEqual(self.cpu.accumulator, 0x80) 377 | self.assertEqual(self.cpu.carry_flag, 0) 378 | self.assertEqual(self.cpu.overflow_flag, 1) 379 | 380 | def test_SBC_without_BCD(self): 381 | self.cpu.accumulator = 0x02 382 | self.memory.write_byte(None, 0x1000, 0x01) 383 | self.cpu.SBC(0x1000) 384 | self.assertEqual(self.cpu.accumulator, 0x00) 385 | self.assertEqual(self.cpu.carry_flag, 1) 386 | self.assertEqual(self.cpu.overflow_flag, 0) 387 | 388 | self.cpu.accumulator = 0x01 389 | self.memory.write_byte(None, 0x1000, 0x02) 390 | self.cpu.SBC(0x1000) 391 | self.assertEqual(self.cpu.accumulator, 0xFF) 392 | self.assertEqual(self.cpu.carry_flag, 0) 393 | self.assertEqual(self.cpu.overflow_flag, 0) # @@@ 394 | 395 | ## test cases from http://www.6502.org/tutorials/vflag.html 396 | 397 | # 0 - 1 = -1 (V = 0) 398 | self.cpu.carry_flag = 1 399 | self.cpu.accumulator = 0x00 400 | self.memory.write_byte(None, 0x1000, 0x01) 401 | self.cpu.SBC(0x1000) 402 | self.assertEqual(self.cpu.accumulator, 0xFF) 403 | self.assertEqual(self.cpu.carry_flag, 0) 404 | self.assertEqual(self.cpu.overflow_flag, 0) # @@@ 405 | 406 | # -128 - 1 = -129 (V = 1) 407 | self.cpu.carry_flag = 1 408 | self.cpu.accumulator = 0x80 409 | self.memory.write_byte(None, 0x1000, 0x01) 410 | self.cpu.SBC(0x1000) 411 | self.assertEqual(self.cpu.accumulator, 0x7F) 412 | self.assertEqual(self.cpu.carry_flag, 1) 413 | self.assertEqual(self.cpu.overflow_flag, 1) 414 | 415 | # 127 - -1 = 128 (V = 1) 416 | self.cpu.carry_flag = 1 417 | self.cpu.accumulator = 0x7F 418 | self.memory.write_byte(None, 0x1000, 0xFF) 419 | self.cpu.SBC(0x1000) 420 | self.assertEqual(self.cpu.accumulator, 0x80) 421 | self.assertEqual(self.cpu.carry_flag, 0) 422 | self.assertEqual(self.cpu.overflow_flag, 1) 423 | 424 | # -64 -64 -1 = -129 (V = 1) 425 | self.cpu.carry_flag = 0 426 | self.cpu.accumulator = 0xC0 427 | self.memory.write_byte(None, 0x1000, 0x40) 428 | self.cpu.SBC(0x1000) 429 | self.assertEqual(self.cpu.accumulator, 0x7F) 430 | self.assertEqual(self.cpu.carry_flag, 1) 431 | self.assertEqual(self.cpu.overflow_flag, 1) # @@@ 432 | 433 | ## @@@ BCD versions still to do 434 | 435 | def test_CMP(self): 436 | self.cpu.accumulator = 0x0A 437 | self.memory.write_byte(None, 0x1000, 0x09) 438 | self.cpu.CMP(0x1000) 439 | self.assertEqual(self.cpu.sign_flag, 0) 440 | self.assertEqual(self.cpu.zero_flag, 0) 441 | self.assertEqual(self.cpu.carry_flag, 1) 442 | 443 | self.cpu.accumulator = 0x0A 444 | self.memory.write_byte(None, 0x1000, 0x0B) 445 | self.cpu.CMP(0x1000) 446 | self.assertEqual(self.cpu.sign_flag, 1) 447 | self.assertEqual(self.cpu.zero_flag, 0) 448 | self.assertEqual(self.cpu.carry_flag, 0) 449 | 450 | self.cpu.accumulator = 0x0A 451 | self.memory.write_byte(None, 0x1000, 0x0A) 452 | self.cpu.CMP(0x1000) 453 | self.assertEqual(self.cpu.sign_flag, 0) 454 | self.assertEqual(self.cpu.zero_flag, 1) 455 | self.assertEqual(self.cpu.carry_flag, 1) 456 | 457 | self.cpu.accumulator = 0xA0 458 | self.memory.write_byte(None, 0x1000, 0x0A) 459 | self.cpu.CMP(0x1000) 460 | self.assertEqual(self.cpu.sign_flag, 1) 461 | self.assertEqual(self.cpu.zero_flag, 0) 462 | self.assertEqual(self.cpu.carry_flag, 1) 463 | 464 | self.cpu.accumulator = 0x0A 465 | self.memory.write_byte(None, 0x1000, 0xA0) 466 | self.cpu.CMP(0x1000) 467 | self.assertEqual(self.cpu.sign_flag, 0) # @@@ 468 | self.assertEqual(self.cpu.zero_flag, 0) 469 | self.assertEqual(self.cpu.carry_flag, 0) 470 | 471 | def test_CPX(self): 472 | self.cpu.x_index = 0x0A 473 | self.memory.write_byte(None, 0x1000, 0x09) 474 | self.cpu.CPX(0x1000) 475 | self.assertEqual(self.cpu.sign_flag, 0) 476 | self.assertEqual(self.cpu.zero_flag, 0) 477 | self.assertEqual(self.cpu.carry_flag, 1) 478 | 479 | self.cpu.x_index = 0x0A 480 | self.memory.write_byte(None, 0x1000, 0x0B) 481 | self.cpu.CPX(0x1000) 482 | self.assertEqual(self.cpu.sign_flag, 1) 483 | self.assertEqual(self.cpu.zero_flag, 0) 484 | self.assertEqual(self.cpu.carry_flag, 0) 485 | 486 | self.cpu.x_index = 0x0A 487 | self.memory.write_byte(None, 0x1000, 0x0A) 488 | self.cpu.CPX(0x1000) 489 | self.assertEqual(self.cpu.sign_flag, 0) 490 | self.assertEqual(self.cpu.zero_flag, 1) 491 | self.assertEqual(self.cpu.carry_flag, 1) 492 | 493 | self.cpu.x_index = 0xA0 494 | self.memory.write_byte(None, 0x1000, 0x0A) 495 | self.cpu.CPX(0x1000) 496 | self.assertEqual(self.cpu.sign_flag, 1) 497 | self.assertEqual(self.cpu.zero_flag, 0) 498 | self.assertEqual(self.cpu.carry_flag, 1) 499 | 500 | self.cpu.x_index = 0x0A 501 | self.memory.write_byte(None, 0x1000, 0xA0) 502 | self.cpu.CPX(0x1000) 503 | self.assertEqual(self.cpu.sign_flag, 0) # @@@ 504 | self.assertEqual(self.cpu.zero_flag, 0) 505 | self.assertEqual(self.cpu.carry_flag, 0) 506 | 507 | def test_CPY(self): 508 | self.cpu.y_index = 0x0A 509 | self.memory.write_byte(None, 0x1000, 0x09) 510 | self.cpu.CPY(0x1000) 511 | self.assertEqual(self.cpu.sign_flag, 0) 512 | self.assertEqual(self.cpu.zero_flag, 0) 513 | self.assertEqual(self.cpu.carry_flag, 1) 514 | 515 | self.cpu.y_index = 0x0A 516 | self.memory.write_byte(None, 0x1000, 0x0B) 517 | self.cpu.CPY(0x1000) 518 | self.assertEqual(self.cpu.sign_flag, 1) 519 | self.assertEqual(self.cpu.zero_flag, 0) 520 | self.assertEqual(self.cpu.carry_flag, 0) 521 | 522 | self.cpu.y_index = 0x0A 523 | self.memory.write_byte(None, 0x1000, 0x0A) 524 | self.cpu.CPY(0x1000) 525 | self.assertEqual(self.cpu.sign_flag, 0) 526 | self.assertEqual(self.cpu.zero_flag, 1) 527 | self.assertEqual(self.cpu.carry_flag, 1) 528 | 529 | self.cpu.y_index = 0xA0 530 | self.memory.write_byte(None, 0x1000, 0x0A) 531 | self.cpu.CPY(0x1000) 532 | self.assertEqual(self.cpu.sign_flag, 1) 533 | self.assertEqual(self.cpu.zero_flag, 0) 534 | self.assertEqual(self.cpu.carry_flag, 1) 535 | 536 | self.cpu.y_index = 0x0A 537 | self.memory.write_byte(None, 0x1000, 0xA0) 538 | self.cpu.CPY(0x1000) 539 | self.assertEqual(self.cpu.sign_flag, 0) # @@@ 540 | self.assertEqual(self.cpu.zero_flag, 0) 541 | self.assertEqual(self.cpu.carry_flag, 0) 542 | 543 | 544 | class TestIncrementDecrementOperations(unittest.TestCase): 545 | 546 | def setUp(self): 547 | self.memory = Memory(use_bus=False) 548 | self.cpu = CPU(self.memory) 549 | 550 | def test_INC(self): 551 | self.memory.write_byte(None, 0x1000, 0x00) 552 | self.cpu.INC(0x1000) 553 | self.assertEqual(self.memory.read_byte(None, 0x1000), 0x01) 554 | self.assertEqual(self.cpu.sign_flag, 0) 555 | self.assertEqual(self.cpu.zero_flag, 0) 556 | self.memory.write_byte(None, 0x1000, 0x7F) 557 | self.cpu.INC(0x1000) 558 | self.assertEqual(self.memory.read_byte(None, 0x1000), 0x80) 559 | self.assertEqual(self.cpu.sign_flag, 1) 560 | self.assertEqual(self.cpu.zero_flag, 0) 561 | self.memory.write_byte(None, 0x1000, 0xFF) 562 | self.cpu.INC(0x1000) 563 | self.assertEqual(self.memory.read_byte(None, 0x1000), 0x00) 564 | self.assertEqual(self.cpu.sign_flag, 0) 565 | self.assertEqual(self.cpu.zero_flag, 1) 566 | 567 | def test_INX(self): 568 | self.cpu.x_index = 0x00 569 | self.cpu.INX() 570 | self.assertEqual(self.cpu.x_index, 0x01) 571 | self.assertEqual(self.cpu.sign_flag, 0) 572 | self.assertEqual(self.cpu.zero_flag, 0) 573 | self.cpu.x_index = 0x7F 574 | self.cpu.INX() 575 | self.assertEqual(self.cpu.x_index, 0x80) 576 | self.assertEqual(self.cpu.sign_flag, 1) 577 | self.assertEqual(self.cpu.zero_flag, 0) 578 | self.cpu.x_index = 0xFF 579 | self.cpu.INX() 580 | self.assertEqual(self.cpu.x_index, 0x00) 581 | self.assertEqual(self.cpu.sign_flag, 0) 582 | self.assertEqual(self.cpu.zero_flag, 1) 583 | 584 | def test_INY(self): 585 | self.cpu.y_index = 0x00 586 | self.cpu.INY() 587 | self.assertEqual(self.cpu.y_index, 0x01) 588 | self.assertEqual(self.cpu.sign_flag, 0) 589 | self.assertEqual(self.cpu.zero_flag, 0) 590 | self.cpu.y_index = 0x7F 591 | self.cpu.INY() 592 | self.assertEqual(self.cpu.y_index, 0x80) 593 | self.assertEqual(self.cpu.sign_flag, 1) 594 | self.assertEqual(self.cpu.zero_flag, 0) 595 | self.cpu.y_index = 0xFF 596 | self.cpu.INY() 597 | self.assertEqual(self.cpu.y_index, 0x00) 598 | self.assertEqual(self.cpu.sign_flag, 0) 599 | self.assertEqual(self.cpu.zero_flag, 1) 600 | 601 | def test_DEC(self): 602 | self.memory.write_byte(None, 0x1000, 0x01) 603 | self.cpu.DEC(0x1000) 604 | self.assertEqual(self.memory.read_byte(None, 0x1000), 0x00) 605 | self.assertEqual(self.cpu.sign_flag, 0) 606 | self.assertEqual(self.cpu.zero_flag, 1) 607 | self.memory.write_byte(None, 0x1000, 0x80) 608 | self.cpu.DEC(0x1000) 609 | self.assertEqual(self.memory.read_byte(None, 0x1000), 0x7F) 610 | self.assertEqual(self.cpu.sign_flag, 0) 611 | self.assertEqual(self.cpu.zero_flag, 0) 612 | self.memory.write_byte(None, 0x1000, 0x00) 613 | self.cpu.DEC(0x1000) 614 | self.assertEqual(self.memory.read_byte(None, 0x1000), 0xFF) 615 | self.assertEqual(self.cpu.sign_flag, 1) 616 | self.assertEqual(self.cpu.zero_flag, 0) 617 | 618 | def test_DEX(self): 619 | self.cpu.x_index = 0x01 620 | self.cpu.DEX() 621 | self.assertEqual(self.cpu.x_index, 0x00) 622 | self.assertEqual(self.cpu.sign_flag, 0) 623 | self.assertEqual(self.cpu.zero_flag, 1) 624 | self.cpu.x_index = 0x80 625 | self.cpu.DEX() 626 | self.assertEqual(self.cpu.x_index, 0x7F) 627 | self.assertEqual(self.cpu.sign_flag, 0) 628 | self.assertEqual(self.cpu.zero_flag, 0) 629 | self.cpu.x_index = 0x00 630 | self.cpu.DEX() 631 | self.assertEqual(self.cpu.x_index, 0xFF) 632 | self.assertEqual(self.cpu.sign_flag, 1) 633 | self.assertEqual(self.cpu.zero_flag, 0) 634 | 635 | def test_DEY(self): 636 | self.cpu.y_index = 0x01 637 | self.cpu.DEY() 638 | self.assertEqual(self.cpu.y_index, 0x00) 639 | self.assertEqual(self.cpu.sign_flag, 0) 640 | self.assertEqual(self.cpu.zero_flag, 1) 641 | self.cpu.y_index = 0x80 642 | self.cpu.DEY() 643 | self.assertEqual(self.cpu.y_index, 0x7F) 644 | self.assertEqual(self.cpu.sign_flag, 0) 645 | self.assertEqual(self.cpu.zero_flag, 0) 646 | self.cpu.y_index = 0x00 647 | self.cpu.DEY() 648 | self.assertEqual(self.cpu.y_index, 0xFF) 649 | self.assertEqual(self.cpu.sign_flag, 1) 650 | self.assertEqual(self.cpu.zero_flag, 0) 651 | 652 | 653 | class TestShiftOperations(unittest.TestCase): 654 | 655 | def setUp(self): 656 | self.memory = Memory(use_bus=False) 657 | self.cpu = CPU(self.memory) 658 | 659 | def test_ASL(self): 660 | self.cpu.accumulator = 0x01 661 | self.cpu.ASL() 662 | self.assertEqual(self.cpu.accumulator, 0x02) 663 | self.assertEqual(self.cpu.sign_flag, 0) 664 | self.assertEqual(self.cpu.zero_flag, 0) 665 | self.assertEqual(self.cpu.carry_flag, 0) 666 | self.memory.write_byte(None, 0x1000, 0x02) 667 | self.cpu.ASL(0x1000) 668 | self.assertEqual(self.memory.read_byte(None, 0x1000), 0x04) 669 | self.assertEqual(self.cpu.sign_flag, 0) 670 | self.assertEqual(self.cpu.zero_flag, 0) 671 | self.assertEqual(self.cpu.carry_flag, 0) 672 | self.cpu.accumulator = 0x80 673 | self.cpu.ASL() 674 | self.assertEqual(self.cpu.accumulator, 0x00) 675 | self.assertEqual(self.cpu.sign_flag, 0) 676 | self.assertEqual(self.cpu.zero_flag, 1) 677 | self.assertEqual(self.cpu.carry_flag, 1) 678 | 679 | def test_LSR(self): 680 | self.cpu.accumulator = 0x01 681 | self.cpu.LSR() 682 | self.assertEqual(self.cpu.accumulator, 0x00) 683 | self.assertEqual(self.cpu.sign_flag, 0) 684 | self.assertEqual(self.cpu.zero_flag, 1) 685 | self.assertEqual(self.cpu.carry_flag, 1) 686 | self.memory.write_byte(None, 0x1000, 0x01) 687 | self.cpu.LSR(0x1000) 688 | self.assertEqual(self.memory.read_byte(None, 0x1000), 0x00) 689 | self.assertEqual(self.cpu.sign_flag, 0) 690 | self.assertEqual(self.cpu.zero_flag, 1) 691 | self.assertEqual(self.cpu.carry_flag, 1) 692 | self.cpu.accumulator = 0x80 693 | self.cpu.LSR() 694 | self.assertEqual(self.cpu.accumulator, 0x40) 695 | self.assertEqual(self.cpu.sign_flag, 0) 696 | self.assertEqual(self.cpu.zero_flag, 0) 697 | self.assertEqual(self.cpu.carry_flag, 0) 698 | 699 | def test_ROL(self): 700 | self.cpu.carry_flag = 0 701 | self.cpu.accumulator = 0x80 702 | self.cpu.ROL() 703 | self.assertEqual(self.cpu.accumulator, 0x00) 704 | self.assertEqual(self.cpu.sign_flag, 0) 705 | self.assertEqual(self.cpu.zero_flag, 1) 706 | self.assertEqual(self.cpu.carry_flag, 1) 707 | self.cpu.carry_flag = 1 708 | self.cpu.accumulator = 0x80 709 | self.cpu.ROL() 710 | self.assertEqual(self.cpu.accumulator, 0x01) 711 | self.assertEqual(self.cpu.sign_flag, 0) 712 | self.assertEqual(self.cpu.zero_flag, 0) # @@@ 713 | self.assertEqual(self.cpu.carry_flag, 1) 714 | self.cpu.carry_flag = 0 715 | self.memory.write_byte(None, 0x1000, 0x80) 716 | self.cpu.ROL(0x1000) 717 | self.assertEqual(self.memory.read_byte(None, 0x1000), 0x00) 718 | self.assertEqual(self.cpu.sign_flag, 0) 719 | self.assertEqual(self.cpu.zero_flag, 1) # @@@ 720 | self.assertEqual(self.cpu.carry_flag, 1) 721 | self.cpu.carry_flag = 1 722 | self.memory.write_byte(None, 0x1000, 0x80) 723 | self.cpu.ROL(0x1000) 724 | self.assertEqual(self.memory.read_byte(None, 0x1000), 0x01) 725 | self.assertEqual(self.cpu.sign_flag, 0) 726 | self.assertEqual(self.cpu.zero_flag, 0) # @@@ 727 | self.assertEqual(self.cpu.carry_flag, 1) 728 | 729 | def test_ROR(self): 730 | self.cpu.carry_flag = 0 731 | self.cpu.accumulator = 0x01 732 | self.cpu.ROR() 733 | self.assertEqual(self.cpu.accumulator, 0x00) 734 | self.assertEqual(self.cpu.sign_flag, 0) 735 | self.assertEqual(self.cpu.zero_flag, 1) # @@@ 736 | self.assertEqual(self.cpu.carry_flag, 1) 737 | self.cpu.carry_flag = 1 738 | self.cpu.accumulator = 0x01 739 | self.cpu.ROR() 740 | self.assertEqual(self.cpu.accumulator, 0x80) 741 | self.assertEqual(self.cpu.sign_flag, 1) # @@@ 742 | self.assertEqual(self.cpu.zero_flag, 0) # @@@ 743 | self.assertEqual(self.cpu.carry_flag, 1) 744 | self.cpu.carry_flag = 0 745 | self.memory.write_byte(None, 0x1000, 0x01) 746 | self.cpu.ROR(0x1000) 747 | self.assertEqual(self.memory.read_byte(None, 0x1000), 0x00) 748 | self.assertEqual(self.cpu.sign_flag, 0) 749 | self.assertEqual(self.cpu.zero_flag, 1) # @@@ 750 | self.assertEqual(self.cpu.carry_flag, 1) 751 | self.cpu.carry_flag = 1 752 | self.memory.write_byte(None, 0x1000, 0x01) 753 | self.cpu.ROR(0x1000) 754 | self.assertEqual(self.memory.read_byte(None, 0x1000), 0x80) 755 | self.assertEqual(self.cpu.sign_flag, 1) # @@@ 756 | self.assertEqual(self.cpu.zero_flag, 0) # @@@ 757 | self.assertEqual(self.cpu.carry_flag, 1) 758 | 759 | 760 | class TestJumpCallOperations(unittest.TestCase): 761 | 762 | def setUp(self): 763 | self.memory = Memory(use_bus=False) 764 | self.cpu = CPU(self.memory) 765 | 766 | def test_JMP(self): 767 | self.cpu.JMP(0x1000) 768 | self.assertEqual(self.cpu.program_counter, 0x1000) 769 | 770 | def test_JSR(self): 771 | self.cpu.program_counter = 0x1000 772 | self.cpu.JSR(0x2000) 773 | self.assertEqual(self.cpu.program_counter, 0x2000) 774 | self.assertEqual(self.memory.read_byte(None, self.cpu.STACK_PAGE + self.cpu.stack_pointer + 1), 0xFF) 775 | self.assertEqual(self.memory.read_byte(None, self.cpu.STACK_PAGE + self.cpu.stack_pointer + 2), 0x0F) 776 | 777 | def test_RTS(self): 778 | self.memory.write_byte(None, self.cpu.STACK_PAGE + 0xFF, 0x12) 779 | self.memory.write_byte(None, self.cpu.STACK_PAGE + 0xFE, 0x33) 780 | self.cpu.stack_pointer = 0xFD 781 | self.cpu.RTS() 782 | self.assertEqual(self.cpu.program_counter, 0x1234) 783 | 784 | def test_JSR_and_RTS(self): 785 | self.cpu.program_counter = 0x1000 786 | self.cpu.JSR(0x2000) 787 | self.assertEqual(self.cpu.program_counter, 0x2000) 788 | self.cpu.RTS() 789 | self.assertEqual(self.cpu.program_counter, 0x1000) # @@@ 790 | 791 | 792 | class TestBranchOperations(unittest.TestCase): 793 | 794 | def setUp(self): 795 | self.memory = Memory(use_bus=False) 796 | self.cpu = CPU(self.memory) 797 | 798 | def test_BCC(self): 799 | self.cpu.program_counter = 0x1000 800 | self.cpu.carry_flag = 1 801 | self.cpu.BCC(0x2000) 802 | self.assertEqual(self.cpu.program_counter, 0x1000) 803 | self.cpu.program_counter = 0x1000 804 | self.cpu.carry_flag = 0 805 | self.cpu.BCC(0x2000) 806 | self.assertEqual(self.cpu.program_counter, 0x2000) 807 | 808 | def test_BCS(self): 809 | self.cpu.program_counter = 0x1000 810 | self.cpu.carry_flag = 0 811 | self.cpu.BCS(0x2000) 812 | self.assertEqual(self.cpu.program_counter, 0x1000) 813 | self.cpu.program_counter = 0x1000 814 | self.cpu.carry_flag = 1 815 | self.cpu.BCS(0x2000) 816 | self.assertEqual(self.cpu.program_counter, 0x2000) 817 | 818 | def test_BEQ(self): 819 | self.cpu.program_counter = 0x1000 820 | self.cpu.zero_flag = 0 821 | self.cpu.BEQ(0x2000) 822 | self.assertEqual(self.cpu.program_counter, 0x1000) 823 | self.cpu.program_counter = 0x1000 824 | self.cpu.zero_flag = 1 825 | self.cpu.BEQ(0x2000) 826 | self.assertEqual(self.cpu.program_counter, 0x2000) 827 | 828 | def test_BMI(self): 829 | self.cpu.program_counter = 0x1000 830 | self.cpu.sign_flag = 0 831 | self.cpu.BMI(0x2000) 832 | self.assertEqual(self.cpu.program_counter, 0x1000) 833 | self.cpu.program_counter = 0x1000 834 | self.cpu.sign_flag = 1 835 | self.cpu.BMI(0x2000) 836 | self.assertEqual(self.cpu.program_counter, 0x2000) 837 | 838 | def test_BNE(self): 839 | self.cpu.program_counter = 0x1000 840 | self.cpu.zero_flag = 1 841 | self.cpu.BNE(0x2000) 842 | self.assertEqual(self.cpu.program_counter, 0x1000) 843 | self.cpu.program_counter = 0x1000 844 | self.cpu.zero_flag = 0 845 | self.cpu.BNE(0x2000) 846 | self.assertEqual(self.cpu.program_counter, 0x2000) 847 | 848 | def test_BPL(self): 849 | self.cpu.program_counter = 0x1000 850 | self.cpu.sign_flag = 1 851 | self.cpu.BPL(0x2000) 852 | self.assertEqual(self.cpu.program_counter, 0x1000) 853 | self.cpu.program_counter = 0x1000 854 | self.cpu.sign_flag = 0 855 | self.cpu.BPL(0x2000) 856 | self.assertEqual(self.cpu.program_counter, 0x2000) 857 | 858 | def test_BVC(self): 859 | self.cpu.program_counter = 0x1000 860 | self.cpu.overflow_flag = 1 861 | self.cpu.BVC(0x2000) 862 | self.assertEqual(self.cpu.program_counter, 0x1000) 863 | self.cpu.program_counter = 0x1000 864 | self.cpu.overflow_flag = 0 865 | self.cpu.BVC(0x2000) 866 | self.assertEqual(self.cpu.program_counter, 0x2000) 867 | 868 | def test_BVS(self): 869 | self.cpu.program_counter = 0x1000 870 | self.cpu.overflow_flag = 0 871 | self.cpu.BVS(0x2000) 872 | self.assertEqual(self.cpu.program_counter, 0x1000) 873 | self.cpu.program_counter = 0x1000 874 | self.cpu.overflow_flag = 1 875 | self.cpu.BVS(0x2000) 876 | self.assertEqual(self.cpu.program_counter, 0x2000) 877 | 878 | 879 | class TestStatusFlagOperations(unittest.TestCase): 880 | 881 | def setUp(self): 882 | self.memory = Memory(use_bus=False) 883 | self.cpu = CPU(self.memory) 884 | 885 | def test_CLC(self): 886 | self.cpu.carry_flag = 1 887 | self.cpu.CLC() 888 | self.assertEqual(self.cpu.carry_flag, 0) 889 | 890 | def test_CLD(self): 891 | self.cpu.decimal_mode_flag = 1 892 | self.cpu.CLD() 893 | self.assertEqual(self.cpu.decimal_mode_flag, 0) 894 | 895 | def test_CLI(self): 896 | self.cpu.interrupt_disable_flag = 1 897 | self.cpu.CLI() 898 | self.assertEqual(self.cpu.interrupt_disable_flag, 0) 899 | 900 | def test_CLV(self): 901 | self.cpu.overflow_flag = 1 902 | self.cpu.CLV() 903 | self.assertEqual(self.cpu.overflow_flag, 0) 904 | 905 | def test_SEC(self): 906 | self.cpu.carry_flag = 0 907 | self.cpu.SEC() 908 | self.assertEqual(self.cpu.carry_flag, 1) 909 | 910 | def test_SED(self): 911 | self.cpu.decimal_mode_flag = 0 912 | self.cpu.SED() 913 | self.assertEqual(self.cpu.decimal_mode_flag, 1) 914 | 915 | def test_SEI(self): 916 | self.cpu.interrupt_disable_flag = 0 917 | self.cpu.SEI() 918 | self.assertEqual(self.cpu.interrupt_disable_flag, 1) 919 | 920 | 921 | class TestSystemFunctionOperations(unittest.TestCase): 922 | 923 | def setUp(self): 924 | self.memory = Memory(use_bus=False) 925 | self.cpu = CPU(self.memory) 926 | 927 | def test_BRK(self): 928 | self.cpu.program_counter = 0x1000 929 | self.memory.rom.load(0xFFFE, [0x00, 0x20]) 930 | status = self.cpu.status_as_byte() 931 | self.cpu.BRK() 932 | self.assertEqual(self.cpu.program_counter, 0x2000) 933 | self.assertEqual(self.cpu.break_flag, 1) 934 | self.assertEqual(self.memory.read_byte(None, self.cpu.STACK_PAGE + self.cpu.stack_pointer + 1), status) 935 | self.assertEqual(self.memory.read_byte(None, self.cpu.STACK_PAGE + self.cpu.stack_pointer + 2), 0x01) 936 | self.assertEqual(self.memory.read_byte(None, self.cpu.STACK_PAGE + self.cpu.stack_pointer + 3), 0x10) 937 | 938 | def test_RTI(self): 939 | self.memory.write_byte(None, self.cpu.STACK_PAGE + 0xFF, 0x12) 940 | self.memory.write_byte(None, self.cpu.STACK_PAGE + 0xFE, 0x33) 941 | self.memory.write_byte(None, self.cpu.STACK_PAGE + 0xFD, 0x20) 942 | self.cpu.stack_pointer = 0xFC 943 | self.cpu.RTI() 944 | self.assertEqual(self.cpu.program_counter, 0x1233) 945 | self.assertEqual(self.cpu.status_as_byte(), 0x20) 946 | 947 | def test_NOP(self): 948 | self.cpu.NOP() 949 | 950 | 951 | class Test6502Bugs(unittest.TestCase): 952 | 953 | def setUp(self): 954 | self.memory = Memory(use_bus=False) 955 | self.cpu = CPU(self.memory) 956 | 957 | def test_zero_page_x(self): 958 | self.cpu.x_index = 0x01 959 | self.memory.load(0x1000, [0x00, 0x7F, 0xFF]) 960 | self.cpu.program_counter = 0x1000 961 | self.assertEqual(self.cpu.zero_page_x_mode(), 0x01) 962 | self.assertEqual(self.cpu.zero_page_x_mode(), 0x80) 963 | self.assertEqual(self.cpu.zero_page_x_mode(), 0x00) 964 | 965 | def test_indirect(self): 966 | self.memory.load(0x20, [0x00, 0x20]) 967 | self.memory.load(0x00, [0x12]) 968 | self.memory.load(0xFF, [0x34]) 969 | self.memory.load(0x100, [0x56]) 970 | self.memory.load(0x1000, [0x20, 0x20, 0xFF, 0xFF, 0x00, 0x45, 0x23]) 971 | self.memory.load(0x2000, [0x05]) 972 | self.memory.load(0x1234, [0x05]) 973 | self.memory.load(0x2345, [0x00, 0xF0]) 974 | 975 | self.cpu.program_counter = 0x1000 976 | 977 | self.cpu.x_index = 0x00 978 | self.cpu.LDA(self.cpu.indirect_x_mode()) 979 | self.assertEqual(self.cpu.accumulator, 0x05) 980 | 981 | self.cpu.y_index = 0x00 982 | self.cpu.LDA(self.cpu.indirect_y_mode()) 983 | self.assertEqual(self.cpu.accumulator, 0x05) 984 | 985 | self.cpu.y_index = 0x00 986 | self.cpu.LDA(self.cpu.indirect_y_mode()) 987 | self.assertEqual(self.cpu.accumulator, 0x05) 988 | 989 | self.cpu.x_index = 0x00 990 | self.cpu.LDA(self.cpu.indirect_x_mode()) 991 | self.assertEqual(self.cpu.accumulator, 0x05) 992 | 993 | self.cpu.x_index = 0xFF 994 | self.cpu.LDA(self.cpu.indirect_x_mode()) 995 | self.assertEqual(self.cpu.accumulator, 0x05) 996 | 997 | self.assertEqual(self.cpu.indirect_mode(), 0xF000) 998 | 999 | 1000 | if __name__ == "__main__": 1001 | unittest.main() 1002 | -------------------------------------------------------------------------------- /cpu6502.py: -------------------------------------------------------------------------------- 1 | # ApplePy - an Apple ][ emulator in Python 2 | # James Tauber / http://jtauber.com/ 3 | # originally written 2001, updated 2011 4 | 5 | 6 | import BaseHTTPServer 7 | import json 8 | import re 9 | import select 10 | import socket 11 | import struct 12 | import sys 13 | 14 | 15 | bus = None # socket for bus I/O 16 | 17 | 18 | def signed(x): 19 | if x > 0x7F: 20 | x = x - 0x100 21 | return x 22 | 23 | 24 | class ROM: 25 | 26 | def __init__(self, start, size): 27 | self.start = start 28 | self.end = start + size - 1 29 | self._mem = [0x00] * size 30 | 31 | def load(self, address, data): 32 | for offset, datum in enumerate(data): 33 | self._mem[address - self.start + offset] = datum 34 | 35 | def load_file(self, address, filename): 36 | with open(filename, "rb") as f: 37 | for offset, datum in enumerate(f.read()): 38 | self._mem[address - self.start + offset] = ord(datum) 39 | 40 | def read_byte(self, address): 41 | assert self.start <= address <= self.end 42 | return self._mem[address - self.start] 43 | 44 | 45 | class RAM(ROM): 46 | 47 | def write_byte(self, address, value): 48 | self._mem[address] = value 49 | 50 | 51 | class Memory: 52 | 53 | def __init__(self, options=None, use_bus=True): 54 | self.use_bus = use_bus 55 | self.rom = ROM(0xD000, 0x3000) 56 | 57 | if options: 58 | self.rom.load_file(0xD000, options.rom) 59 | 60 | self.ram = RAM(0x0000, 0xC000) 61 | 62 | if options and options.ram: 63 | self.ram.load_file(0x0000, options.ram) 64 | 65 | def load(self, address, data): 66 | if address < 0xC000: 67 | self.ram.load(address, data) 68 | 69 | def read_byte(self, cycle, address): 70 | if address < 0xC000: 71 | return self.ram.read_byte(address) 72 | elif address < 0xD000: 73 | return self.bus_read(cycle, address) 74 | else: 75 | return self.rom.read_byte(address) 76 | 77 | def read_word(self, cycle, address): 78 | return self.read_byte(cycle, address) + (self.read_byte(cycle + 1, address + 1) << 8) 79 | 80 | def read_word_bug(self, cycle, address): 81 | if address % 0x100 == 0xFF: 82 | return self.read_byte(cycle, address) + (self.read_byte(cycle + 1, address & 0xFF00) << 8) 83 | else: 84 | return self.read_word(cycle, address) 85 | 86 | def write_byte(self, cycle, address, value): 87 | if address < 0xC000: 88 | self.ram.write_byte(address, value) 89 | if 0x400 <= address < 0x800 or 0x2000 <= address < 0x5FFF: 90 | self.bus_write(cycle, address, value) 91 | 92 | def bus_read(self, cycle, address): 93 | if not self.use_bus: 94 | return 0 95 | op = struct.pack(" 2: 364 | r.update(info[2](pc)) 365 | return r, info[0] 366 | 367 | 368 | class ControlHandler(BaseHTTPServer.BaseHTTPRequestHandler): 369 | 370 | def __init__(self, request, client_address, server, cpu): 371 | self.cpu = cpu 372 | self.disassemble = Disassemble(self.cpu, self.cpu.memory) 373 | 374 | self.get_urls = { 375 | r"/disassemble/(\d+)$": self.get_disassemble, 376 | r"/memory/(\d+)(-(\d+))?$": self.get_memory, 377 | r"/memory/(\d+)(-(\d+))?/raw$": self.get_memory_raw, 378 | r"/status$": self.get_status, 379 | } 380 | 381 | self.post_urls = { 382 | r"/memory/(\d+)(-(\d+))?$": self.post_memory, 383 | r"/memory/(\d+)(-(\d+))?/raw$": self.post_memory_raw, 384 | r"/quit$": self.post_quit, 385 | r"/reset$": self.post_reset, 386 | } 387 | 388 | BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, request, client_address, server) 389 | 390 | def log_request(self, code, size=0): 391 | pass 392 | 393 | def dispatch(self, urls): 394 | for r, f in urls.items(): 395 | m = re.match(r, self.path) 396 | if m is not None: 397 | f(m) 398 | break 399 | else: 400 | self.send_response(404) 401 | self.end_headers() 402 | 403 | def response(self, s): 404 | self.send_response(200) 405 | self.send_header("Content-Length", str(len(s))) 406 | self.end_headers() 407 | self.wfile.write(s) 408 | 409 | def do_GET(self): 410 | self.dispatch(self.get_urls) 411 | 412 | def do_POST(self): 413 | self.dispatch(self.post_urls) 414 | 415 | def get_disassemble(self, m): 416 | addr = int(m.group(1)) 417 | r = [] 418 | n = 20 419 | while n > 0: 420 | dis, length = self.disassemble.disasm(addr) 421 | r.append(dis) 422 | addr += length 423 | n -= 1 424 | self.response(json.dumps(r)) 425 | 426 | def get_memory_raw(self, m): 427 | addr = int(m.group(1)) 428 | e = m.group(3) 429 | if e is not None: 430 | end = int(e) 431 | else: 432 | end = addr 433 | self.response("".join([chr(self.cpu.read_byte(x)) for x in range(addr, end + 1)])) 434 | 435 | def get_memory(self, m): 436 | addr = int(m.group(1)) 437 | e = m.group(3) 438 | if e is not None: 439 | end = int(e) 440 | else: 441 | end = addr 442 | self.response(json.dumps(list(map(self.cpu.read_byte, range(addr, end + 1))))) 443 | 444 | def get_status(self, m): 445 | self.response(json.dumps(dict((x, getattr(self.cpu, x)) for x in ( 446 | "accumulator", 447 | "x_index", 448 | "y_index", 449 | "stack_pointer", 450 | "program_counter", 451 | "sign_flag", 452 | "overflow_flag", 453 | "break_flag", 454 | "decimal_mode_flag", 455 | "interrupt_disable_flag", 456 | "zero_flag", 457 | "carry_flag", 458 | )))) 459 | 460 | def post_memory(self, m): 461 | addr = int(m.group(1)) 462 | e = m.group(3) 463 | if e is not None: 464 | end = int(e) 465 | else: 466 | end = addr 467 | data = json.loads(self.rfile.read(int(self.headers["Content-Length"]))) 468 | for i, a in enumerate(range(addr, end + 1)): 469 | self.cpu.write_byte(a, data[i]) 470 | self.response("") 471 | 472 | def post_memory_raw(self, m): 473 | addr = int(m.group(1)) 474 | e = m.group(3) 475 | if e is not None: 476 | end = int(e) 477 | else: 478 | end = addr 479 | data = self.rfile.read(int(self.headers["Content-Length"])) 480 | for i, a in enumerate(range(addr, end + 1)): 481 | self.cpu.write_byte(a, data[i]) 482 | self.response("") 483 | 484 | def post_quit(self, m): 485 | self.cpu.quit = True 486 | self.response("") 487 | 488 | def post_reset(self, m): 489 | self.cpu.reset() 490 | self.cpu.running = True 491 | self.response("") 492 | 493 | 494 | class ControlHandlerFactory: 495 | 496 | def __init__(self, cpu): 497 | self.cpu = cpu 498 | 499 | def __call__(self, request, client_address, server): 500 | return ControlHandler(request, client_address, server, self.cpu) 501 | 502 | 503 | class CPU: 504 | 505 | STACK_PAGE = 0x100 506 | RESET_VECTOR = 0xFFFC 507 | 508 | def __init__(self, options, memory): 509 | self.memory = memory 510 | 511 | self.control_server = BaseHTTPServer.HTTPServer(("127.0.0.1", 6502), ControlHandlerFactory(self)) 512 | 513 | self.accumulator = 0x00 514 | self.x_index = 0x00 515 | self.y_index = 0x00 516 | 517 | self.carry_flag = 0 518 | self.zero_flag = 0 519 | self.interrupt_disable_flag = 0 520 | self.decimal_mode_flag = 0 521 | self.break_flag = 1 522 | self.overflow_flag = 0 523 | self.sign_flag = 0 524 | 525 | self.stack_pointer = 0xFF 526 | 527 | self.cycles = 0 528 | 529 | self.setup_ops() 530 | self.reset() 531 | if options.pc is not None: 532 | self.program_counter = options.pc 533 | self.running = True 534 | self.quit = False 535 | 536 | def setup_ops(self): 537 | self.ops = [None] * 0x100 538 | self.ops[0x00] = lambda: self.BRK() 539 | self.ops[0x01] = lambda: self.ORA(self.indirect_x_mode()) 540 | self.ops[0x05] = lambda: self.ORA(self.zero_page_mode()) 541 | self.ops[0x06] = lambda: self.ASL(self.zero_page_mode()) 542 | self.ops[0x08] = lambda: self.PHP() 543 | self.ops[0x09] = lambda: self.ORA(self.immediate_mode()) 544 | self.ops[0x0A] = lambda: self.ASL() 545 | self.ops[0x0D] = lambda: self.ORA(self.absolute_mode()) 546 | self.ops[0x0E] = lambda: self.ASL(self.absolute_mode()) 547 | self.ops[0x10] = lambda: self.BPL(self.relative_mode()) 548 | self.ops[0x11] = lambda: self.ORA(self.indirect_y_mode()) 549 | self.ops[0x15] = lambda: self.ORA(self.zero_page_x_mode()) 550 | self.ops[0x16] = lambda: self.ASL(self.zero_page_x_mode()) 551 | self.ops[0x18] = lambda: self.CLC() 552 | self.ops[0x19] = lambda: self.ORA(self.absolute_y_mode()) 553 | self.ops[0x1D] = lambda: self.ORA(self.absolute_x_mode()) 554 | self.ops[0x1E] = lambda: self.ASL(self.absolute_x_mode(rmw=True)) 555 | self.ops[0x20] = lambda: self.JSR(self.absolute_mode()) 556 | self.ops[0x21] = lambda: self.AND(self.indirect_x_mode()) 557 | self.ops[0x24] = lambda: self.BIT(self.zero_page_mode()) 558 | self.ops[0x25] = lambda: self.AND(self.zero_page_mode()) 559 | self.ops[0x26] = lambda: self.ROL(self.zero_page_mode()) 560 | self.ops[0x28] = lambda: self.PLP() 561 | self.ops[0x29] = lambda: self.AND(self.immediate_mode()) 562 | self.ops[0x2A] = lambda: self.ROL() 563 | self.ops[0x2C] = lambda: self.BIT(self.absolute_mode()) 564 | self.ops[0x2D] = lambda: self.AND(self.absolute_mode()) 565 | self.ops[0x2E] = lambda: self.ROL(self.absolute_mode()) 566 | self.ops[0x30] = lambda: self.BMI(self.relative_mode()) 567 | self.ops[0x31] = lambda: self.AND(self.indirect_y_mode()) 568 | self.ops[0x35] = lambda: self.AND(self.zero_page_x_mode()) 569 | self.ops[0x36] = lambda: self.ROL(self.zero_page_x_mode()) 570 | self.ops[0x38] = lambda: self.SEC() 571 | self.ops[0x39] = lambda: self.AND(self.absolute_y_mode()) 572 | self.ops[0x3D] = lambda: self.AND(self.absolute_x_mode()) 573 | self.ops[0x3E] = lambda: self.ROL(self.absolute_x_mode(rmw=True)) 574 | self.ops[0x40] = lambda: self.RTI() 575 | self.ops[0x41] = lambda: self.EOR(self.indirect_x_mode()) 576 | self.ops[0x45] = lambda: self.EOR(self.zero_page_mode()) 577 | self.ops[0x46] = lambda: self.LSR(self.zero_page_mode()) 578 | self.ops[0x48] = lambda: self.PHA() 579 | self.ops[0x49] = lambda: self.EOR(self.immediate_mode()) 580 | self.ops[0x4A] = lambda: self.LSR() 581 | self.ops[0x4C] = lambda: self.JMP(self.absolute_mode()) 582 | self.ops[0x4D] = lambda: self.EOR(self.absolute_mode()) 583 | self.ops[0x4E] = lambda: self.LSR(self.absolute_mode()) 584 | self.ops[0x50] = lambda: self.BVC(self.relative_mode()) 585 | self.ops[0x51] = lambda: self.EOR(self.indirect_y_mode()) 586 | self.ops[0x55] = lambda: self.EOR(self.zero_page_x_mode()) 587 | self.ops[0x56] = lambda: self.LSR(self.zero_page_x_mode()) 588 | self.ops[0x58] = lambda: self.CLI() 589 | self.ops[0x59] = lambda: self.EOR(self.absolute_y_mode()) 590 | self.ops[0x5D] = lambda: self.EOR(self.absolute_x_mode()) 591 | self.ops[0x5E] = lambda: self.LSR(self.absolute_x_mode(rmw=True)) 592 | self.ops[0x60] = lambda: self.RTS() 593 | self.ops[0x61] = lambda: self.ADC(self.indirect_x_mode()) 594 | self.ops[0x65] = lambda: self.ADC(self.zero_page_mode()) 595 | self.ops[0x66] = lambda: self.ROR(self.zero_page_mode()) 596 | self.ops[0x68] = lambda: self.PLA() 597 | self.ops[0x69] = lambda: self.ADC(self.immediate_mode()) 598 | self.ops[0x6A] = lambda: self.ROR() 599 | self.ops[0x6C] = lambda: self.JMP(self.indirect_mode()) 600 | self.ops[0x6D] = lambda: self.ADC(self.absolute_mode()) 601 | self.ops[0x6E] = lambda: self.ROR(self.absolute_mode()) 602 | self.ops[0x70] = lambda: self.BVS(self.relative_mode()) 603 | self.ops[0x71] = lambda: self.ADC(self.indirect_y_mode()) 604 | self.ops[0x75] = lambda: self.ADC(self.zero_page_x_mode()) 605 | self.ops[0x76] = lambda: self.ROR(self.zero_page_x_mode()) 606 | self.ops[0x78] = lambda: self.SEI() 607 | self.ops[0x79] = lambda: self.ADC(self.absolute_y_mode()) 608 | self.ops[0x7D] = lambda: self.ADC(self.absolute_x_mode()) 609 | self.ops[0x7E] = lambda: self.ROR(self.absolute_x_mode(rmw=True)) 610 | self.ops[0x81] = lambda: self.STA(self.indirect_x_mode()) 611 | self.ops[0x84] = lambda: self.STY(self.zero_page_mode()) 612 | self.ops[0x85] = lambda: self.STA(self.zero_page_mode()) 613 | self.ops[0x86] = lambda: self.STX(self.zero_page_mode()) 614 | self.ops[0x88] = lambda: self.DEY() 615 | self.ops[0x8A] = lambda: self.TXA() 616 | self.ops[0x8C] = lambda: self.STY(self.absolute_mode()) 617 | self.ops[0x8D] = lambda: self.STA(self.absolute_mode()) 618 | self.ops[0x8E] = lambda: self.STX(self.absolute_mode()) 619 | self.ops[0x90] = lambda: self.BCC(self.relative_mode()) 620 | self.ops[0x91] = lambda: self.STA(self.indirect_y_mode(rmw=True)) 621 | self.ops[0x94] = lambda: self.STY(self.zero_page_x_mode()) 622 | self.ops[0x95] = lambda: self.STA(self.zero_page_x_mode()) 623 | self.ops[0x96] = lambda: self.STX(self.zero_page_y_mode()) 624 | self.ops[0x98] = lambda: self.TYA() 625 | self.ops[0x99] = lambda: self.STA(self.absolute_y_mode(rmw=True)) 626 | self.ops[0x9A] = lambda: self.TXS() 627 | self.ops[0x9D] = lambda: self.STA(self.absolute_x_mode(rmw=True)) 628 | self.ops[0xA0] = lambda: self.LDY(self.immediate_mode()) 629 | self.ops[0xA1] = lambda: self.LDA(self.indirect_x_mode()) 630 | self.ops[0xA2] = lambda: self.LDX(self.immediate_mode()) 631 | self.ops[0xA4] = lambda: self.LDY(self.zero_page_mode()) 632 | self.ops[0xA5] = lambda: self.LDA(self.zero_page_mode()) 633 | self.ops[0xA6] = lambda: self.LDX(self.zero_page_mode()) 634 | self.ops[0xA8] = lambda: self.TAY() 635 | self.ops[0xA9] = lambda: self.LDA(self.immediate_mode()) 636 | self.ops[0xAA] = lambda: self.TAX() 637 | self.ops[0xAC] = lambda: self.LDY(self.absolute_mode()) 638 | self.ops[0xAD] = lambda: self.LDA(self.absolute_mode()) 639 | self.ops[0xAE] = lambda: self.LDX(self.absolute_mode()) 640 | self.ops[0xB0] = lambda: self.BCS(self.relative_mode()) 641 | self.ops[0xB1] = lambda: self.LDA(self.indirect_y_mode()) 642 | self.ops[0xB4] = lambda: self.LDY(self.zero_page_x_mode()) 643 | self.ops[0xB5] = lambda: self.LDA(self.zero_page_x_mode()) 644 | self.ops[0xB6] = lambda: self.LDX(self.zero_page_y_mode()) 645 | self.ops[0xB8] = lambda: self.CLV() 646 | self.ops[0xB9] = lambda: self.LDA(self.absolute_y_mode()) 647 | self.ops[0xBA] = lambda: self.TSX() 648 | self.ops[0xBC] = lambda: self.LDY(self.absolute_x_mode()) 649 | self.ops[0xBD] = lambda: self.LDA(self.absolute_x_mode()) 650 | self.ops[0xBE] = lambda: self.LDX(self.absolute_y_mode()) 651 | self.ops[0xC0] = lambda: self.CPY(self.immediate_mode()) 652 | self.ops[0xC1] = lambda: self.CMP(self.indirect_x_mode()) 653 | self.ops[0xC4] = lambda: self.CPY(self.zero_page_mode()) 654 | self.ops[0xC5] = lambda: self.CMP(self.zero_page_mode()) 655 | self.ops[0xC6] = lambda: self.DEC(self.zero_page_mode()) 656 | self.ops[0xC8] = lambda: self.INY() 657 | self.ops[0xC9] = lambda: self.CMP(self.immediate_mode()) 658 | self.ops[0xCA] = lambda: self.DEX() 659 | self.ops[0xCC] = lambda: self.CPY(self.absolute_mode()) 660 | self.ops[0xCD] = lambda: self.CMP(self.absolute_mode()) 661 | self.ops[0xCE] = lambda: self.DEC(self.absolute_mode()) 662 | self.ops[0xD0] = lambda: self.BNE(self.relative_mode()) 663 | self.ops[0xD1] = lambda: self.CMP(self.indirect_y_mode()) 664 | self.ops[0xD5] = lambda: self.CMP(self.zero_page_x_mode()) 665 | self.ops[0xD6] = lambda: self.DEC(self.zero_page_x_mode()) 666 | self.ops[0xD8] = lambda: self.CLD() 667 | self.ops[0xD9] = lambda: self.CMP(self.absolute_y_mode()) 668 | self.ops[0xDD] = lambda: self.CMP(self.absolute_x_mode()) 669 | self.ops[0xDE] = lambda: self.DEC(self.absolute_x_mode(rmw=True)) 670 | self.ops[0xE0] = lambda: self.CPX(self.immediate_mode()) 671 | self.ops[0xE1] = lambda: self.SBC(self.indirect_x_mode()) 672 | self.ops[0xE4] = lambda: self.CPX(self.zero_page_mode()) 673 | self.ops[0xE5] = lambda: self.SBC(self.zero_page_mode()) 674 | self.ops[0xE6] = lambda: self.INC(self.zero_page_mode()) 675 | self.ops[0xE8] = lambda: self.INX() 676 | self.ops[0xE9] = lambda: self.SBC(self.immediate_mode()) 677 | self.ops[0xEA] = lambda: self.NOP() 678 | self.ops[0xEC] = lambda: self.CPX(self.absolute_mode()) 679 | self.ops[0xED] = lambda: self.SBC(self.absolute_mode()) 680 | self.ops[0xEE] = lambda: self.INC(self.absolute_mode()) 681 | self.ops[0xF0] = lambda: self.BEQ(self.relative_mode()) 682 | self.ops[0xF1] = lambda: self.SBC(self.indirect_y_mode()) 683 | self.ops[0xF5] = lambda: self.SBC(self.zero_page_x_mode()) 684 | self.ops[0xF6] = lambda: self.INC(self.zero_page_x_mode()) 685 | self.ops[0xF8] = lambda: self.SED() 686 | self.ops[0xF9] = lambda: self.SBC(self.absolute_y_mode()) 687 | self.ops[0xFD] = lambda: self.SBC(self.absolute_x_mode()) 688 | self.ops[0xFE] = lambda: self.INC(self.absolute_x_mode(rmw=True)) 689 | 690 | def reset(self): 691 | self.program_counter = self.read_word(self.RESET_VECTOR) 692 | 693 | def run(self, bus_port): 694 | global bus 695 | bus = socket.socket() 696 | bus.connect(("127.0.0.1", bus_port)) 697 | 698 | while not self.quit: 699 | 700 | timeout = 0 701 | if not self.running: 702 | timeout = 1 703 | # Currently this handler blocks from the moment 704 | # a connection is accepted until the response 705 | # is sent. TODO: use an async HTTP server that 706 | # handles input data asynchronously. 707 | sockets = [self.control_server] 708 | rs, _, _ = select.select(sockets, [], [], timeout) 709 | for s in rs: 710 | if s is self.control_server: 711 | self.control_server._handle_request_noblock() 712 | else: 713 | pass 714 | 715 | count = 1000 716 | while count > 0 and self.running: 717 | self.cycles += 2 # all instructions take this as a minimum 718 | op = self.read_pc_byte() 719 | func = self.ops[op] 720 | if func is None: 721 | print "UNKNOWN OP" 722 | print hex(self.program_counter - 1) 723 | print hex(op) 724 | break 725 | else: 726 | self.ops[op]() 727 | count -= 1 728 | 729 | def test_run(self, start, end): 730 | self.program_counter = start 731 | while True: 732 | self.cycles += 2 # all instructions take this as a minimum 733 | if self.program_counter == end: 734 | break 735 | op = self.read_pc_byte() 736 | func = self.ops[op] 737 | if func is None: 738 | print "UNKNOWN OP" 739 | print hex(self.program_counter - 1) 740 | print hex(op) 741 | break 742 | else: 743 | self.ops[op]() 744 | 745 | #### 746 | 747 | def get_pc(self, inc=1): 748 | pc = self.program_counter 749 | self.program_counter += inc 750 | return pc 751 | 752 | def read_byte(self, address): 753 | return self.memory.read_byte(self.cycles, address) 754 | 755 | def read_word(self, address): 756 | return self.memory.read_word(self.cycles, address) 757 | 758 | def read_word_bug(self, address): 759 | return self.memory.read_word_bug(self.cycles, address) 760 | 761 | def read_pc_byte(self): 762 | return self.read_byte(self.get_pc()) 763 | 764 | def read_pc_word(self): 765 | return self.read_word(self.get_pc(2)) 766 | 767 | def write_byte(self, address, value): 768 | self.memory.write_byte(self.cycles, address, value) 769 | 770 | #### 771 | 772 | def status_from_byte(self, status): 773 | self.carry_flag = [0, 1][0 != status & 1] 774 | self.zero_flag = [0, 1][0 != status & 2] 775 | self.interrupt_disable_flag = [0, 1][0 != status & 4] 776 | self.decimal_mode_flag = [0, 1][0 != status & 8] 777 | self.break_flag = [0, 1][0 != status & 16] 778 | self.overflow_flag = [0, 1][0 != status & 64] 779 | self.sign_flag = [0, 1][0 != status & 128] 780 | 781 | def status_as_byte(self): 782 | 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 783 | 784 | #### 785 | 786 | def push_byte(self, byte): 787 | self.write_byte(self.STACK_PAGE + self.stack_pointer, byte) 788 | self.stack_pointer = (self.stack_pointer - 1) % 0x100 789 | 790 | def pull_byte(self): 791 | self.stack_pointer = (self.stack_pointer + 1) % 0x100 792 | return self.read_byte(self.STACK_PAGE + self.stack_pointer) 793 | 794 | def push_word(self, word): 795 | hi, lo = divmod(word, 0x100) 796 | self.push_byte(hi) 797 | self.push_byte(lo) 798 | 799 | def pull_word(self): 800 | s = self.STACK_PAGE + self.stack_pointer + 1 801 | self.stack_pointer += 2 802 | return self.read_word(s) 803 | 804 | #### 805 | 806 | def immediate_mode(self): 807 | return self.get_pc() 808 | 809 | def absolute_mode(self): 810 | self.cycles += 2 811 | return self.read_pc_word() 812 | 813 | def absolute_x_mode(self, rmw=False): 814 | if rmw: 815 | self.cycles += 1 816 | return self.absolute_mode() + self.x_index 817 | 818 | def absolute_y_mode(self, rmw=False): 819 | if rmw: 820 | self.cycles += 1 821 | return self.absolute_mode() + self.y_index 822 | 823 | def zero_page_mode(self): 824 | self.cycles += 1 825 | return self.read_pc_byte() 826 | 827 | def zero_page_x_mode(self): 828 | self.cycles += 1 829 | return (self.zero_page_mode() + self.x_index) % 0x100 830 | 831 | def zero_page_y_mode(self): 832 | self.cycles += 1 833 | return (self.zero_page_mode() + self.y_index) % 0x100 834 | 835 | def indirect_mode(self): 836 | self.cycles += 2 837 | return self.read_word_bug(self.absolute_mode()) 838 | 839 | def indirect_x_mode(self): 840 | self.cycles += 4 841 | return self.read_word_bug((self.read_pc_byte() + self.x_index) % 0x100) 842 | 843 | def indirect_y_mode(self, rmw=False): 844 | if rmw: 845 | self.cycles += 4 846 | else: 847 | self.cycles += 3 848 | return self.read_word_bug(self.read_pc_byte()) + self.y_index 849 | 850 | def relative_mode(self): 851 | pc = self.get_pc() 852 | return pc + 1 + signed(self.read_byte(pc)) 853 | 854 | #### 855 | 856 | def update_nz(self, value): 857 | value = value % 0x100 858 | self.zero_flag = [0, 1][(value == 0)] 859 | self.sign_flag = [0, 1][((value & 0x80) != 0)] 860 | return value 861 | 862 | def update_nzc(self, value): 863 | self.carry_flag = [0, 1][(value > 0xFF)] 864 | return self.update_nz(value) 865 | 866 | #### 867 | 868 | # LOAD / STORE 869 | 870 | def LDA(self, operand_address): 871 | self.accumulator = self.update_nz(self.read_byte(operand_address)) 872 | 873 | def LDX(self, operand_address): 874 | self.x_index = self.update_nz(self.read_byte(operand_address)) 875 | 876 | def LDY(self, operand_address): 877 | self.y_index = self.update_nz(self.read_byte(operand_address)) 878 | 879 | def STA(self, operand_address): 880 | self.write_byte(operand_address, self.accumulator) 881 | 882 | def STX(self, operand_address): 883 | self.write_byte(operand_address, self.x_index) 884 | 885 | def STY(self, operand_address): 886 | self.write_byte(operand_address, self.y_index) 887 | 888 | # TRANSFER 889 | 890 | def TAX(self): 891 | self.x_index = self.update_nz(self.accumulator) 892 | 893 | def TXA(self): 894 | self.accumulator = self.update_nz(self.x_index) 895 | 896 | def TAY(self): 897 | self.y_index = self.update_nz(self.accumulator) 898 | 899 | def TYA(self): 900 | self.accumulator = self.update_nz(self.y_index) 901 | 902 | def TSX(self): 903 | self.x_index = self.update_nz(self.stack_pointer) 904 | 905 | def TXS(self): 906 | self.stack_pointer = self.x_index 907 | 908 | # SHIFTS / ROTATES 909 | 910 | def ASL(self, operand_address=None): 911 | if operand_address is None: 912 | self.accumulator = self.update_nzc(self.accumulator << 1) 913 | else: 914 | self.cycles += 2 915 | self.write_byte(operand_address, self.update_nzc(self.read_byte(operand_address) << 1)) 916 | 917 | def ROL(self, operand_address=None): 918 | if operand_address is None: 919 | a = self.accumulator << 1 920 | if self.carry_flag: 921 | a = a | 0x01 922 | self.accumulator = self.update_nzc(a) 923 | else: 924 | self.cycles += 2 925 | m = self.read_byte(operand_address) << 1 926 | if self.carry_flag: 927 | m = m | 0x01 928 | self.write_byte(operand_address, self.update_nzc(m)) 929 | 930 | def ROR(self, operand_address=None): 931 | if operand_address is None: 932 | if self.carry_flag: 933 | self.accumulator = self.accumulator | 0x100 934 | self.carry_flag = self.accumulator % 2 935 | self.accumulator = self.update_nz(self.accumulator >> 1) 936 | else: 937 | self.cycles += 2 938 | m = self.read_byte(operand_address) 939 | if self.carry_flag: 940 | m = m | 0x100 941 | self.carry_flag = m % 2 942 | self.write_byte(operand_address, self.update_nz(m >> 1)) 943 | 944 | def LSR(self, operand_address=None): 945 | if operand_address is None: 946 | self.carry_flag = self.accumulator % 2 947 | self.accumulator = self.update_nz(self.accumulator >> 1) 948 | else: 949 | self.cycles += 2 950 | self.carry_flag = self.read_byte(operand_address) % 2 951 | self.write_byte(operand_address, self.update_nz(self.read_byte(operand_address) >> 1)) 952 | 953 | # JUMPS / RETURNS 954 | 955 | def JMP(self, operand_address): 956 | self.cycles -= 1 957 | self.program_counter = operand_address 958 | 959 | def JSR(self, operand_address): 960 | self.cycles += 2 961 | self.push_word(self.program_counter - 1) 962 | self.program_counter = operand_address 963 | 964 | def RTS(self): 965 | self.cycles += 4 966 | self.program_counter = self.pull_word() + 1 967 | 968 | # BRANCHES 969 | 970 | def BCC(self, operand_address): 971 | if not self.carry_flag: 972 | self.cycles += 1 973 | self.program_counter = operand_address 974 | 975 | def BCS(self, operand_address): 976 | if self.carry_flag: 977 | self.cycles += 1 978 | self.program_counter = operand_address 979 | 980 | def BEQ(self, operand_address): 981 | if self.zero_flag: 982 | self.cycles += 1 983 | self.program_counter = operand_address 984 | 985 | def BNE(self, operand_address): 986 | if not self.zero_flag: 987 | self.cycles += 1 988 | self.program_counter = operand_address 989 | 990 | def BMI(self, operand_address): 991 | if self.sign_flag: 992 | self.cycles += 1 993 | self.program_counter = operand_address 994 | 995 | def BPL(self, operand_address): 996 | if not self.sign_flag: 997 | self.cycles += 1 998 | self.program_counter = operand_address 999 | 1000 | def BVC(self, operand_address): 1001 | if not self.overflow_flag: 1002 | self.cycles += 1 1003 | self.program_counter = operand_address 1004 | 1005 | def BVS(self, operand_address): 1006 | if self.overflow_flag: 1007 | self.cycles += 1 1008 | self.program_counter = operand_address 1009 | 1010 | # SET / CLEAR FLAGS 1011 | 1012 | def CLC(self): 1013 | self.carry_flag = 0 1014 | 1015 | def CLD(self): 1016 | self.decimal_mode_flag = 0 1017 | 1018 | def CLI(self): 1019 | self.interrupt_disable_flag = 0 1020 | 1021 | def CLV(self): 1022 | self.overflow_flag = 0 1023 | 1024 | def SEC(self): 1025 | self.carry_flag = 1 1026 | 1027 | def SED(self): 1028 | self.decimal_mode_flag = 1 1029 | 1030 | def SEI(self): 1031 | self.interrupt_disable_flag = 1 1032 | 1033 | # INCREMENT / DECREMENT 1034 | 1035 | def DEC(self, operand_address): 1036 | self.cycles += 2 1037 | self.write_byte(operand_address, self.update_nz(self.read_byte(operand_address) - 1)) 1038 | 1039 | def DEX(self): 1040 | self.x_index = self.update_nz(self.x_index - 1) 1041 | 1042 | def DEY(self): 1043 | self.y_index = self.update_nz(self.y_index - 1) 1044 | 1045 | def INC(self, operand_address): 1046 | self.cycles += 2 1047 | self.write_byte(operand_address, self.update_nz(self.read_byte(operand_address) + 1)) 1048 | 1049 | def INX(self): 1050 | self.x_index = self.update_nz(self.x_index + 1) 1051 | 1052 | def INY(self): 1053 | self.y_index = self.update_nz(self.y_index + 1) 1054 | 1055 | # PUSH / PULL 1056 | 1057 | def PHA(self): 1058 | self.cycles += 1 1059 | self.push_byte(self.accumulator) 1060 | 1061 | def PHP(self): 1062 | self.cycles += 1 1063 | self.push_byte(self.status_as_byte()) 1064 | 1065 | def PLA(self): 1066 | self.cycles += 2 1067 | self.accumulator = self.update_nz(self.pull_byte()) 1068 | 1069 | def PLP(self): 1070 | self.cycles += 2 1071 | self.status_from_byte(self.pull_byte()) 1072 | 1073 | # LOGIC 1074 | 1075 | def AND(self, operand_address): 1076 | self.accumulator = self.update_nz(self.accumulator & self.read_byte(operand_address)) 1077 | 1078 | def ORA(self, operand_address): 1079 | self.accumulator = self.update_nz(self.accumulator | self.read_byte(operand_address)) 1080 | 1081 | def EOR(self, operand_address): 1082 | self.accumulator = self.update_nz(self.accumulator ^ self.read_byte(operand_address)) 1083 | 1084 | # ARITHMETIC 1085 | 1086 | def ADC(self, operand_address): 1087 | # @@@ doesn't handle BCD yet 1088 | assert not self.decimal_mode_flag 1089 | 1090 | a2 = self.accumulator 1091 | a1 = signed(a2) 1092 | m2 = self.read_byte(operand_address) 1093 | m1 = signed(m2) 1094 | 1095 | # twos complement addition 1096 | result1 = a1 + m1 + self.carry_flag 1097 | 1098 | # unsigned addition 1099 | result2 = a2 + m2 + self.carry_flag 1100 | 1101 | self.accumulator = self.update_nzc(result2) 1102 | 1103 | # perhaps this could be calculated from result2 but result1 is more intuitive 1104 | self.overflow_flag = [0, 1][(result1 > 127) | (result1 < -128)] 1105 | 1106 | def SBC(self, operand_address): 1107 | # @@@ doesn't handle BCD yet 1108 | assert not self.decimal_mode_flag 1109 | 1110 | a2 = self.accumulator 1111 | a1 = signed(a2) 1112 | m2 = self.read_byte(operand_address) 1113 | m1 = signed(m2) 1114 | 1115 | # twos complement subtraction 1116 | result1 = a1 - m1 - [1, 0][self.carry_flag] 1117 | 1118 | # unsigned subtraction 1119 | result2 = a2 - m2 - [1, 0][self.carry_flag] 1120 | 1121 | self.accumulator = self.update_nz(result2) 1122 | self.carry_flag = [0, 1][(result2 >= 0)] 1123 | 1124 | # perhaps this could be calculated from result2 but result1 is more intuitive 1125 | self.overflow_flag = [0, 1][(result1 > 127) | (result1 < -128)] 1126 | 1127 | # BIT 1128 | 1129 | def BIT(self, operand_address): 1130 | value = self.read_byte(operand_address) 1131 | self.sign_flag = ((value >> 7) % 2) # bit 7 1132 | self.overflow_flag = ((value >> 6) % 2) # bit 6 1133 | self.zero_flag = [0, 1][((self.accumulator & value) == 0)] 1134 | 1135 | # COMPARISON 1136 | 1137 | def CMP(self, operand_address): 1138 | result = self.accumulator - self.read_byte(operand_address) 1139 | self.carry_flag = [0, 1][(result >= 0)] 1140 | self.update_nz(result) 1141 | 1142 | def CPX(self, operand_address): 1143 | result = self.x_index - self.read_byte(operand_address) 1144 | self.carry_flag = [0, 1][(result >= 0)] 1145 | self.update_nz(result) 1146 | 1147 | def CPY(self, operand_address): 1148 | result = self.y_index - self.read_byte(operand_address) 1149 | self.carry_flag = [0, 1][(result >= 0)] 1150 | self.update_nz(result) 1151 | 1152 | # SYSTEM 1153 | 1154 | def NOP(self): 1155 | pass 1156 | 1157 | def BRK(self): 1158 | self.cycles += 5 1159 | self.push_word(self.program_counter + 1) 1160 | self.push_byte(self.status_as_byte()) 1161 | self.program_counter = self.read_word(0xFFFE) 1162 | self.break_flag = 1 1163 | 1164 | def RTI(self): 1165 | self.cycles += 4 1166 | self.status_from_byte(self.pull_byte()) 1167 | self.program_counter = self.pull_word() 1168 | 1169 | # @@@ IRQ 1170 | # @@@ NMI 1171 | 1172 | 1173 | def usage(): 1174 | print >>sys.stderr, "ApplePy - an Apple ][ emulator in Python" 1175 | print >>sys.stderr, "James Tauber / http://jtauber.com/" 1176 | print >>sys.stderr 1177 | print >>sys.stderr, "Usage: cpu6502.py [options]" 1178 | print >>sys.stderr 1179 | print >>sys.stderr, " -b, --bus Bus port number" 1180 | print >>sys.stderr, " -p, --pc Initial PC value" 1181 | print >>sys.stderr, " -R, --rom ROM file to use (default A2ROM.BIN)" 1182 | print >>sys.stderr, " -r, --ram RAM file to load (default none)" 1183 | sys.exit(1) 1184 | 1185 | 1186 | def get_options(): 1187 | class Options: 1188 | def __init__(self): 1189 | self.rom = "A2ROM.BIN" 1190 | self.ram = None 1191 | self.bus = None 1192 | self.pc = None 1193 | 1194 | options = Options() 1195 | a = 1 1196 | while a < len(sys.argv): 1197 | if sys.argv[a].startswith("-"): 1198 | if sys.argv[a] in ("-b", "--bus"): 1199 | a += 1 1200 | options.bus = int(sys.argv[a]) 1201 | elif sys.argv[a] in ("-p", "--pc"): 1202 | a += 1 1203 | options.pc = int(sys.argv[a]) 1204 | elif sys.argv[a] in ("-R", "--rom"): 1205 | a += 1 1206 | options.rom = sys.argv[a] 1207 | elif sys.argv[a] in ("-r", "--ram"): 1208 | a += 1 1209 | options.ram = sys.argv[a] 1210 | else: 1211 | usage() 1212 | else: 1213 | usage() 1214 | a += 1 1215 | 1216 | return options 1217 | 1218 | 1219 | if __name__ == "__main__": 1220 | options = get_options() 1221 | if options.bus is None: 1222 | print "ApplePy cpu core" 1223 | print "Run applepy.py instead" 1224 | sys.exit(0) 1225 | 1226 | mem = Memory(options) 1227 | 1228 | cpu = CPU(options, mem) 1229 | cpu.run(options.bus) 1230 | --------------------------------------------------------------------------------