├── .gitignore ├── License.txt ├── README.txt └── src ├── Krakowicz_examples.asm ├── Krakowicz_examples.py ├── asm6502.py ├── debugger.py ├── dis6502.py ├── load_store.asm ├── memory_map.py ├── py6502_common.py ├── scrolltest.py ├── scrolltest.txt ├── sim6502.py ├── small_example.py ├── termbox_util.py ├── test6502.py ├── test_all_instructions.py ├── test_assemble_in_two_places.py ├── test_mpu6502.py ├── test_mpu65c02.py ├── test_shim.py └── test_show_all_opcodes.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2017, David Johnston 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | A python based 6502 assembler, disassembler and simulator 2 | David Johnston 3 | 4 | The Philosophy 5 | -------------- 6 | 7 | I enjoy old-school electronics and programming. I've learned over time that there is a benefit to combining old-school methods with modern tools. 8 | 9 | For example: 10 | 1) My day job is a chip designer, focusing on cryptgraphic circuits. For a particular project I either needed a big state machine or a small CPU. An old school 8 bit processor took me 4 hours to implement in synthesizable RTL and on a modern silicon process it runs blindingly fast in a tiny bit of silicon area. It came with lots of tools already since the instruction was standard. 11 | 12 | 2) I wrote a Point-of-Sale system for a family store, since the off-the-shelf solutions were expensive and none of them matched the needs of the store. Instead of going for some windows interface or touch driven app on a tablet, I wrote the user interface using the old-school curses library for a text based interface that was designed to minimize keystrokes at the checkout. The staff loved it since the key strokes took a couple of minutes to learn and I wrote it in Python, bringing the simplicity and power of modern programming tools. 13 | 14 | In both cases, the benefits of the efficiencies of old school methods with the power of modern methods yielded something better than either. 15 | 16 | This project is a proof-of-concept for this idea. I use it for hacking my Apple //e. It's a 6502 assembler, disassembler and simulator written in Python. Many old 6502 assemblers exist, but they suffer from inconsistent formats, directives and macro processors. In particular the macro processors tended to be horrible. 17 | 18 | The thing that makes it a little different is that instead of offering a 'better assembler language' or 'better macro language' I've stripped down the programs to the very basic functions but written them such that they are intended to be called from a python program that feeds it assembler and gets object code back. This then makes python the macro language. So you get the ability to write assembly code normally, or you can write python to automate the code generation or generate parameterized code, or unroll loops or any number of other things, but using a nice language that makes it easy rather than a set of confusing macro directive written in 1978. 19 | 20 | If you want to instrument and test of a bit of code, it's easy to assemble it and then write a program in python to iterate over your chosen input states and check the outputs and simulate the code repeatedly, calling the simulation of each instruction directly from python, coding in whatever analysis meets your needs. 21 | 22 | The simulator and disassembler works directly with the object code and symbol table from the assembler. 23 | 24 | An Simple Example: Sending Assembly to the Assembler From Python 25 | ---------------------------------------------------------------- 26 | This python assembles a few instructions 27 | 28 | from asm6502 import asm6502 29 | 30 | thecode = """ 31 | ORG $100 32 | start: 33 | LDA #$10 34 | LDX #$00 35 | loop: 36 | STA $1000,x 37 | INX 38 | SBC #$01 39 | BPL loop 40 | RTS 41 | """ 42 | 43 | lines = thecode.splitlines() 44 | 45 | a = asm6502() 46 | a.assemble(lines) 47 | 48 | The output looks like this: 49 | 50 | 65C02 Assembler 51 | LISTING 52 | 1 0000 : 53 | 2 0100 : ORG $100 54 | 3 0100 : start: 55 | 4 0100 : A9 10 LDA #$10 56 | 5 0102 : A2 00 LDX #$00 57 | 6 0104 : loop: 58 | 7 0104 : 9D 00 10 STA $1000,x 59 | 8 0107 : E8 INX 60 | 9 0108 : E9 01 SBC #$01 61 | 10 010A : 10 FA BPL loop 62 | 11 010C : 60 RTS 63 | 64 | SYMBOL TABLE 65 | start = $0100 66 | loop = $0104 67 | 68 | OBJECT CODE 69 | * 70 | 0100: A9 10 A2 00 9D 00 10 E8 E9 01 10 FA 60 71 | * 72 | 73 | a.object_code[256:268] 74 | 75 | The Object Code Map 76 | ------------------- 77 | 78 | If after running that, you typed this: 79 | >>> a.object_code[256:268] 80 | 81 | You would see the list of object code values, but in decimal, since that's how python displays by default: 82 | 83 | [169, 16, 162, 0, 157, 0, 16, 232, 233, 1, 16, 250, 96] 84 | >>> 85 | 86 | What's going on is the assembler keeps a complete map of the 64K memory space of the 6502 and populates the code and values into that map. The 'object_code' class variable is a list containing the map. Each untouched location is set to -1. Other values indicate the 8 bit value at that location. 87 | 88 | So after assembling the code into the map, it is possible to add in other things to the map by assigning to the object_code list. E.G. 89 | 90 | a.object_code[0xfffd] = 0x00 91 | a.object_code[0xfffc] = 0x10 92 | Which would set the reset vector to 0x1000. 93 | 94 | The Symbol Table 95 | ---------------- 96 | 97 | You can also see the symbol table as a dictionary after assembling: 98 | >>> a.symbols 99 | {'start': 256, 'loop': 260} 100 | 101 | 102 | Directives 103 | ---------- 104 | 105 | There are a small number of directives: 106 | 107 | ; Comment 108 | ORG address ; Sets the current aseembly location 109 | STR some_text ; Include text as ascii bytes 110 | DB comma_separated_list_of_bytes ; $ prefix for hex 111 | DW comma_separated_list_of_16_bit_numbers ; $ prefix for hex 112 | DDW comma_separated_list_of_32_bit_numbers ; $ prefix for hex 113 | DQW comma_separated_list_of_64_bit_numbers ; $ prefix for hex 114 | LE ; For multi word data (DW, DDW and DQW) sets the encoding to little endian 115 | BE ; For multi word data (DW, DDW and DQW) sets the encoding to big endian 116 | The assembler defaults to little endian. 117 | 118 | Prefixes 119 | -------- 120 | 121 | $ for hex. $10 = 16 122 | @ for octal. @10 = 8 123 | & for a label pointer. &labelname = the 16 bit address of the label, only works with DW. 124 | 125 | Labels 126 | ------ 127 | 128 | A word followed by a colon makes a label. It can be on it's own line, or in front of an instruction or directive. 129 | 130 | alabel: ; A label on it's own 131 | anotherlabel: STA #$10 ; A label with an instruction 132 | Any address or 16 bit data field can be replaced with a declared label and the label address will be inserted there. 133 | In a DW declaration you need to prefix a label with & to tell the assembler it's a label. This may change. I.E.: 134 | 135 | dw $1000, @2000, 123 ; Implicit numbers have a base prefix. 136 | ttable: dw &l1, &l2, &l3 ; labels in a DW prefixed with & 137 | org $1000 138 | l1: lda #$20 139 | jmp skip 140 | l2: lda #$30 141 | jmp skip 142 | l3: lda #$40 143 | skip: sta $10 144 | 145 | Assembling Into the Same Map 146 | ---------------------------- 147 | 148 | The assembler instance clears it's state before assembling, except for the object_code map. This enables you to assemble multiple pieces of code into different locations and they will be added to the map. 149 | The print_object_code() class method displays the current object code map 150 | E.G. The following code assembles a sequence, then modifies its origin, then reassembles it: 151 | from asm6502 import asm6502 152 | a = asm6502() 153 | lines = [' ORG $1000', ' NOP', ' LDA #$20', 'here: NOP', ' DB 10,11,12,13', ' RTS'] 154 | a.assemble(lines) 155 | lines[0] = ' ORG $2000' 156 | a.assemble(lines) 157 | a.print_object_code() 158 | 159 | This yields this memory map with the same code in two places. 160 | >>> a.print_object_code() 161 | OBJECT CODE 162 | * 163 | 1000: EA A9 20 EA 0A 0B 0C 0D 60 164 | * 165 | 2000: EA A9 20 EA 0A 0B 0C 0D 60 166 | * 167 | 168 | Getting IntelHex format data out 169 | -------------------------------- 170 | After assembling you can output the object code in intelhex format. 171 | calling the intelhex() method returns lines of intelhex as a list. 172 | 173 | >>> a.intelhex() 174 | [':10010000A000B90000990020B90001990021B900', ':1001100002990022B90003990023B90004990024', ':10012000B90005990025B90006B90026B9000799', ':0E0130000027B90008990028C8D0D04C59FF', ':10100000A000B90000990020C8D0D0A900850085', ':1010100002A9018501A9218503B2009202E602E6', ':1010200000D0F8E603E601A501C909D0EE4C59FF', ':00000001FF'] 175 | 176 | Calling the print_intelhex() method outputs intelhex format object code to stdout. 177 | >>> a.print_intelhex() 178 | :10010000A000B90000990020B90001990021B900 179 | :1001100002990022B90003990023B90004990024 180 | :10012000B90005990025B90006B90026B9000799 181 | :0E0130000027B90008990028C8D0D04C59FF 182 | :10100000A000B90000990020C8D0D0A900850085 183 | :1010100002A9018501A9218503B2009202E602E6 184 | :1010200000D0F8E603E601A501C909D0EE4C59FF 185 | :00000001FF 186 | >>> 187 | 188 | Getting SRecord format data out 189 | -------------------------------- 190 | After assembling you can output the object code in S19 Srecord format. 191 | calling the srecord() method returns lines of S19 S Record as a list. 192 | 193 | The parameters are (int:version, int:revision, str:module name, str:comment) 194 | 195 | >>> a.srecords(10,20,"Module Name,","Comment") 196 | ['S02e00000a144d6f64756c65204e616d652c436f6d6d656e74ad', 'S12e1000600D0C0B0AEA20A9EA96', 'S12e2000600D0C0B0AEA20A9EA86', 'S5030003f9', 'S9032008d4'] 197 | 198 | Calling the print_srecords() method outputs S19 format object code to stdout. 199 | >>> a.print_srecords(10,20,"Module Name,","Comment") 200 | S02e00000a144d6f64756c65204e616d652c436f6d6d656e74ad 201 | S12e1000600D0C0B0AEA20A9EA96 202 | S12e2000600D0C0B0AEA20A9EA86 203 | S5030003f9 204 | S9032008d4 205 | 206 | The Simulator and Disassembler 207 | ------------------------------ 208 | 209 | #The simulator is in sim6502.py, the disassembler is in dis6502.py 210 | #So start with all three.. 211 | import asm6502 212 | import sim6502 213 | import dis6502 214 | 215 | # The assembler code 216 | src = """ 217 | 218 | """ 219 | lines = src.splitlines() 220 | 221 | #The simulator must be given an object code map. You can get it from the assembler: 222 | 223 | # Assemble the code in a list called lines. 224 | a = asm6502.asm6502(debug=0) 225 | a.assemble(lines) 226 | object_code = a.object_code[:] 227 | 228 | # Then instantiate the simulator. 229 | # Also pass it the symbol table so it can know addresses 230 | s = sim6502.sim6502(object_code, symbols=a.symbols) 231 | 232 | # And instantiate the disassembler: 233 | d = dis6502.dis6502(object_code, symbols=a.symbols) 234 | 235 | # Now 236 | # s.reset() will reset the simulation 237 | # s.execute() will execute the current instruction 238 | # The 6502 state will be in 239 | # s.pc Program Counter 240 | # s.a Accumulator 241 | # s.x X registers 242 | # s.y Y register 243 | # s.sp stack pointer 244 | # s.cc Flags 245 | # d.disassemble(address) will disassemble the instruction as the address and 246 | # will return a text string of the disassembly 247 | # 248 | # E.G. 249 | 250 | s.reset() 251 | 252 | print 253 | print "SIMULATION START" 254 | print 255 | # Print a header for the simulator/disassembler output 256 | print ("LABEL " + "ADDR HEX INSTR").ljust(status_indent)+" PC A X Y SP Status" 257 | 258 | # Print the initial state 259 | print " ".ljust(status_indent) + " %04x %02x %02x %02x %04x %02x" % (s.pc,s.a,s.x,s.y,s.sp,s.cc) 260 | 261 | # Execute 200 instructions 262 | for i in xrange(200): 263 | # Disassemble the current instruction 264 | distxt = d.disassemble_line(s.pc) 265 | 266 | # Execute that instruction 267 | s.execute() 268 | 269 | # Print out the disassembled instruction followed by the simulator state 270 | print distxt.ljust(status_indent) + " %04x %02x %02x %02x %04x %02x" % (s.pc,s.a,s.x,s.y,s.sp,s.cc) 271 | 272 | # Each output line will then show the address, the hex, the instruction executed and the state of the 6502 after the execution. 273 | 274 | -------------------------------------------- 275 | Comments to dj@deadhat.com 276 | 277 | -------------------------------------------- 278 | TBD 1: Write a 65C02 simulator that runs from the object_code state generated by the assembler 279 | DONE! 280 | 281 | TBD 2: Write an output generator for more of the flash/prom/eeprom programming formats 282 | Added Srecords. Should also add ascii hex and binary. 283 | 284 | TBD 3: Give it decent error handling 285 | 286 | TBD 4: Set up a unit test bench to fuzz it with code and do directed tests. 287 | 288 | -------------------------------------------------------------------------------- /src/Krakowicz_examples.asm: -------------------------------------------------------------------------------- 1 | ORG $100 2 | LDY #$00 ;CLEAR Y-REGISTER 3 | LDA $00,Y ;GET A BYTE FROM 0+Y 4 | STA $2000,Y ;STORE AT 2000+Y 5 | LDA $0100,Y ;THEN FROM 100+Y 6 | STA $2100,Y ;TO 2100+Y 7 | LDA $0200,Y ;AND SO ON UNTIL 8 | STA $2200,Y ;WE HAVE COVERED 9 | LDA $0300,Y ;ALL THE MEMORY 10 | STA $2300,Y ;'PAGES' FROM 0 TO 8 11 | LDA $0400,Y ;AND STORED INTO 12 | STA $2400,Y ;PAGES 20 TO 28 13 | LDA $0500,Y 14 | STA $2500,Y 15 | LDA $0600,Y 16 | LDA $2600,Y 17 | LDA $0700,Y 18 | STA $2700,Y 19 | LDA $0800,Y 20 | STA $2800,Y 21 | INY ;THEN ADD 1 TO Y-REG 22 | BNE $FED0 ;AND REPEAT IF < 256 23 | JMP $FF59 ;WHEN WE'RE ALL DONE 24 | ;JUMP TO MONITOR START 25 | 26 | ORG $1000 ; A new location for a different code sequence 27 | LDY #$00 ;CLEAR Y-REGISTER 28 | LDA $00,Y ;XFER THE ZERO PAGE TO 29 | STA $2000,Y ;2000-20FF SO WE CAN USE 30 | INY ;THE ZERO PAGE MEMORY 31 | BNE $FED0 ;FOR THE OTHER MOVES 32 | LDA #$00 ;SET UP LOCNS 0 & 1 AS A 33 | STA $00 ;2-BYTE POINTER FOR THE 34 | STA $02 ;SOURCE ADDRESS, USE 2&3 35 | LDA #$01 ;AS 2-BYTE POINTER FOR 36 | STA $01 ;THE DESTINATION ADDRESS 37 | LDA #$21 ;STARTING AT $2100 38 | STA $03 39 | loophere: LDA ($00) ;GET A BYTE FROM 100-UP 40 | STA ($02) ;STORE AT 2100-UP 41 | INC $02 ;INCREMENT LO-ORDER BYTE 42 | INC $00 ;OF SOURCE & DESTINATION 43 | BNE loophere ;(BACK TO LDA ($00) IF 44 | ;LO-ORDER IS <256 45 | INC $03 ;IF LO-ORDER=0, INC THE 46 | INC $01 ;HI BYTE OF EACH 47 | LDA $01 ;CHECK TO SEE IF HI-BYTE 48 | CMP #$09 ;IS 9 -WE'RE THRU AT 8FF 49 | BNE loophere ;IF NOT, LOOP BACK TO 50 | ;THE LOAD/STORE UNTIL 51 | ;WE'RE ALL DONE 52 | JMP $FF59 ;EXIT THRU MONITOR 53 | 54 | ORG $fffa ; System vectors 55 | DW $0100,$0100,$0100 56 | 57 | 58 | -------------------------------------------------------------------------------- /src/Krakowicz_examples.py: -------------------------------------------------------------------------------- 1 | # Some code sequences from 2 | # http://www.textfiles.com/100/krckwczt.app 3 | 4 | # This shows how to simply feed in straight text 5 | # by passing it through splitlines() or reading from a file with readlines() 6 | 7 | from asm6502 import asm6502 8 | 9 | f = open("Krakowicz_examples.asm",'r') 10 | lines = f.readlines() 11 | 12 | a = asm6502() 13 | a.assemble(lines) 14 | 15 | -------------------------------------------------------------------------------- /src/debugger.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- encoding: utf-8 -*- 3 | 4 | import argparse 5 | import sys 6 | import os 7 | import binascii 8 | import copy 9 | import logging 10 | logging.basicConfig(filename='py6502_debugger.log',level=logging.DEBUG) 11 | logging.info('Staring Py6502 Debugger') 12 | 13 | 14 | # The assembler, disassembler and simulator libraries 15 | from asm6502 import asm6502 16 | from dis6502 import dis6502 17 | from sim6502 import sim6502 18 | 19 | # The termbox library for writing to the screen 20 | import termbox 21 | 22 | # temrbox_util adds a curses like interface to termbox 23 | from termbox_util import termbox_util 24 | from termbox_util import termbox_editableline 25 | from termbox_util import hex_validator 26 | from termbox_util import integer_validator 27 | from termbox_util import text_validator 28 | from termbox_util import decimal_validator 29 | 30 | # Also get the virtual viewplane support 31 | # Allows virtual text screen that can be displayed 32 | # on a real screen. Good for scrolling through lists. 33 | # Have a static list (like a memory view) and move 34 | # the viewport. 35 | from termbox_util import viewplane 36 | 37 | leftwidth = 40 38 | memory_selected = True 39 | disassembly_selected = False 40 | observer_selected = False 41 | 42 | current_pc_disassembly_line = 0 43 | disassembly_count = 0 44 | linelist=list() 45 | 46 | def el_validator(e): 47 | (type, ch, key, mod, w, h, x, y ) = e 48 | if type==termbox.EVENT_KEY and key == termbox.KEY_ENTER: 49 | return(7) 50 | else: 51 | return(ch) 52 | 53 | def draw_commands_view(vptu): 54 | vptu.clear() 55 | vptu.border() 56 | vptu.addstr(x=4,y=0,thestring="COMMANDS",bold=False) 57 | vptu.addstr(x=1, y=1, thestring="r: Reset, I: Irq, N:Nmi, s:Step, K:sKip, g: Goto Addr, o/l:page up/down", bold=False) 58 | vptu.addstr(x=1, y=2, thestring="ESC: exit B: set Breakpoint M:Modify Memory", bold=False) 59 | 60 | # 7 6 5 4 3 2 1 0 61 | # Negative Overflow (S) Break Decimal Interrupt Zero Carry 62 | # N V - B D I Z C 63 | # - - - - - - - - 64 | 65 | def draw_registers_view(vptu,pc,a,x,y,sp,cc): 66 | cc = cc 67 | ccstr = "" 68 | for i in range(8): 69 | if (cc & 128)==128: 70 | ccstr += "1" 71 | else: 72 | ccstr += "." 73 | cc = cc << 1 74 | 75 | vptu.clear() 76 | vptu.border() 77 | vptu.addstr(x=4,y=0,thestring="REGISTERS",bold=False) 78 | vptu.addstr(x=1, y=1, thestring="PC:%04x A:%02x X:%02x Y:%02x NVsBDIZC" % (pc,a,x,y), bold=False) 79 | vptu.addstr(x=1, y=2, thestring="SP:%04x Flags:%s" % (sp,ccstr), bold=False) 80 | 81 | def draw_observer_view(vptu,observer_selected,event): 82 | vptu.clear() 83 | vptu.border() 84 | 85 | # Read the NMI, IRQ and Reset vectors from memory 86 | ih = object_code[0xffff] 87 | il = object_code[0xfffe] 88 | rh = object_code[0xfffd] 89 | rl = object_code[0xfffc] 90 | nh = object_code[0xfffb] 91 | nl = object_code[0xfffa] 92 | 93 | # Turn them into 16 bit integers. 94 | resetvec = rl + (rh << 8) 95 | nmivec = nl + (nh << 8) 96 | irqvec = il + (ih << 8) 97 | 98 | # Print the vectors in the observer window 99 | vptu.addstr(x=4,y=0,thestring="OBSERVER",bold=observer_selected) 100 | vptu.addstr(x=1, y=1, thestring="ResetVec:%04x NMIVec:%04x IRQVec:%04x" % (resetvec,nmivec,irqvec), bold=False) 101 | #vptu.addstr(x=1, y=2, thestring="??:%04x Flags:%s" % (sp,ccstr), bold=False) 102 | 103 | # Debug output to show the character event last received. 104 | vptu.addstr(1,2,str(event)) 105 | 106 | def draw_action_view(vptu,actionstr): 107 | vptu.clear() 108 | vptu.border() 109 | vptu.addstr(x=4, y=0, thestring="ACTION", bold=False) 110 | vptu.addstr(x=1, y=1, thestring=actionstr, bold=False) 111 | 112 | def update_memory_inner_view(vptu,startaddr,object_code): 113 | vptu.clear() 114 | maxx,maxy = vptu.getmaxxy() 115 | 116 | startline = startaddr / 8 117 | endline = start + maxy 118 | for line in range(startline,endline+1): 119 | addr = line*8 120 | vptu.addstr(x=0,y=line,thestring="%04x"%addr,bold=False) 121 | for i in range(8): 122 | value = object_code[addr+i] 123 | if value >= 0: 124 | if (a.instruction_map[addr+i] == None): 125 | vptu.addstr(x=5+(i*3),y=line,thestring="%02x"%object_code[addr+i],bold=False) 126 | #vptu.addstr(x=5+(i*3),y=line,thestring="nn",bold=False) 127 | elif (a.instruction_map[addr+i] >= 0) and (a.instruction_map[addr+i] < 256): 128 | vptu.addstr(x=5+(i*3),y=line,thestring="%02x"%object_code[addr+i],bold=True) 129 | else: 130 | vptu.addstr(x=5+(i*3),y=line,thestring="%02x"%object_code[addr+i],bold=False) 131 | #vptu.addstr(x=5+(i*3),y=line,thestring="xx",bold=False) 132 | if (value > 31) and (value < 128): 133 | vptu.addstr(x=29+i,y=line,thestring=chr(object_code[addr+i]),bold=False) 134 | 135 | def draw_memory_inner_view(vptu,object_code): 136 | vptu.clear() 137 | for line in range(8192): 138 | addr = line*8 139 | vptu.addstr(x=0,y=line,thestring="%04x"%addr,bold=False) 140 | astr = "%04x" % addr 141 | for i in range(8): 142 | value = object_code[addr+i] 143 | if value >= 0: 144 | if (a.instruction_map[addr+i] == None): 145 | vptu.addstr(x=5+(i*3),y=line,thestring="%02x"%object_code[addr+i],bold=False) 146 | #vptu.addstr(x=5+(i*3),y=line,thestring="nn",bold=False) 147 | elif (a.instruction_map[addr+i] >= 0) and (a.instruction_map[addr+i] < 256): 148 | vptu.addstr(x=5+(i*3),y=line,thestring="%02x"%object_code[addr+i],bold=True) 149 | else: 150 | vptu.addstr(x=5+(i*3),y=line,thestring="%02x"%object_code[addr+i],bold=False) 151 | #vptu.addstr(x=5+(i*3),y=line,thestring="xx",bold=False) 152 | if (value > 31) and (value < 128): 153 | vptu.addstr(x=29+i,y=line,thestring=chr(object_code[addr+i]),bold=False) 154 | 155 | def draw_memory_inner_view_partial(vptu,startline,endline,object_code): 156 | for line in range(startline,endline+1): 157 | addr = line*8 158 | vptu.addstr(x=0,y=line,thestring="%04x"%addr,bold=False) 159 | astr = "%04x" % addr 160 | for i in range(8): 161 | value = object_code[addr+i] 162 | if value >= 0: 163 | if (a.instruction_map[addr+i] == None): 164 | vptu.addstr(x=5+(i*3),y=line,thestring="%02x"%object_code[addr+i],bold=False) 165 | #vptu.addstr(x=5+(i*3),y=line,thestring="nn",bold=False) 166 | elif (a.instruction_map[addr+i] >= 0) and (a.instruction_map[addr+i] < 256): 167 | vptu.addstr(x=5+(i*3),y=line,thestring="%02x"%object_code[addr+i],bold=True) 168 | else: 169 | vptu.addstr(x=5+(i*3),y=line,thestring="%02x"%object_code[addr+i],bold=False) 170 | #vptu.addstr(x=5+(i*3),y=line,thestring="xx",bold=False) 171 | if (value > 31) and (value < 128): 172 | vptu.addstr(x=29+i,y=line,thestring=chr(object_code[addr+i]),bold=False) 173 | 174 | def draw_memory_outer_view(vptu,memory_selected): 175 | vptu.clear() 176 | vptu.border() 177 | vptu.addstr(x=4,y=0,thestring="MEMORY",bold=memory_selected) 178 | 179 | def draw_disassembly_inner_view(vptu,object_code): 180 | vptu.clear() 181 | vptu.addstr(1,1,"wut?") 182 | # Start with PC. Disassemble a line and put it in a list of lines. 183 | # Then work backwards until we reach -49 lines or 0 184 | # Then work forwards until we reach +50 lines or ffff 185 | # Then we have a list of lines to display. 186 | # Show them in the window and highlight the instruction at pc. 187 | linelist = list() 188 | line, _ = dis.disassemble_line(s.pc) # The first line at pc 189 | linelist.append(line) 190 | 191 | # Now work backwards from pc 192 | reversecount = 0 193 | address = s.pc 194 | while (reversecount < 49) and (address > 0): 195 | if a.instruction_map[address-1] == -1: 196 | address = address -2 197 | thetype = "instruction" 198 | elif a.instruction_map[address-1]==-2: 199 | address = address -3 200 | thetype = "instruction" 201 | elif (a.instruction_map[address-1] >= 0) and (a.instruction_map[address-1] < 256): 202 | address = address -1 203 | thetype = "instruction" 204 | else: 205 | address = address -1 206 | thetype = "data" 207 | 208 | if thetype == "data": 209 | line = " %04x %02x DB $%02x" % (address,object_code[address],object_code[address]) 210 | else: 211 | line,_ = dis.disassemble_line(address) 212 | linelist.insert(0,line) 213 | reversecount += 1 214 | vptu.addstr(2,2,"reversecount="+str(reversecount).ljust(17)) 215 | # Now work forwards 216 | forwardcount = 0 217 | address = s.pc 218 | 219 | # Work out contents for the next 50 lines. 220 | while (forwardcount < 50) and (address < 0xffff): 221 | # Look forward for the next instruction. Work out if it's instruction or data 222 | 223 | # If it's not in the instruction map, it's data. 224 | if (a.instruction_map[address+1])==None: 225 | address = address+1 226 | thetype = "data" 227 | # The next byte is positive in the instruction map, so it must be an instruction. 228 | elif (a.instruction_map[address+1] >= 0) and (a.instruction_map[address+1] < 256): 229 | address = address +1 230 | thetype = "instruction" 231 | # An instruction with one operand followed by data 232 | elif (a.instruction_map[address+1]==-1) and (a.instruction_map[address+2] >= 0) and (a.instruction_map[address+2] < 256): 233 | address = address +2 234 | thetype = "instruction" 235 | # An instruction with a 2 byte operand. 236 | elif (a.instruction_map[address+1]==-1) and (a.instruction_map[address+3] >= 0) and (a.instruction_map[address+3] < 256): 237 | address = address +3 238 | thetype = "instruction" 239 | else: 240 | address = address +1 241 | thetype = "data" 242 | 243 | if thetype == "data": 244 | line = " %04x %02x DB $%02x" % (address,object_code[address],object_code[address]) 245 | else: 246 | line, _ = dis.disassemble_line(address) 247 | linelist.append(line) 248 | forwardcount += 1 249 | 250 | # Now put the lines into the inner view with pc somewhere near the middle 251 | 252 | # Add the reverse list 253 | for i in range(reversecount): 254 | if type(linelist[i]) != str: 255 | logging.debug(str(("linelist[%d] = " % i)+str(linelist[i]))) 256 | vptu.addstr(x=0,y=i,thestring=linelist[i],bold=False) 257 | 258 | # Add the current instruction highlighted 259 | vptu.addstr(x=0,y=reversecount,thestring=linelist[reversecount],bold=True) 260 | 261 | # Add the forward list 262 | for i in range(forwardcount): 263 | vptu.addstr(x=0,y=i+reversecount+1,thestring=linelist[i+1+reversecount],bold=False) 264 | 265 | current_pc_disassembly_line = reversecount 266 | disassembly_count = len(linelist) 267 | return (current_pc_disassembly_line,disassembly_count) 268 | 269 | def draw_disassembly_outer_view(vptu,disassembly_selected): 270 | vptu.clear() 271 | vptu.border() 272 | vptu.addstr(x=4,y=0,thestring="DISASSEMBLY",bold=disassembly_selected) 273 | 274 | # g was pressed so we bring up a window for the address or symbol to by typed into 275 | def draw_goto_view(tb,tbtu,thetext): 276 | #A viewplane in which to type 277 | tbtu.clear 278 | tbtu.border() 279 | tbtu.addstr(1,1,"Goto Addr(hex):"+str(thetext)) 280 | 281 | def dbg6502(object_code, symbol_table): 282 | 283 | memory_selected = True 284 | disassembly_selected = False 285 | observer_selected = False 286 | 287 | event = "no_event_yet" 288 | 289 | # Use a real termbox window tbinst. 290 | with termbox.Termbox() as tb: 291 | # The things we seen in the main view are composed 292 | # in virtual viewplanes which are placed within 293 | # the physical termbox view 294 | tbtu = termbox_util(tb) 295 | tbtu.clear() 296 | 297 | # Compute some positions 298 | maxx,maxy=tbtu.getmaxxy() 299 | memorywindowheight=maxy -12 300 | rightwidth = maxx-leftwidth 301 | 302 | # A viewplane to hold the command information 303 | vp_commands=viewplane(width=maxx+1,height=4) 304 | vptu_commands = termbox_util(vp_commands) 305 | draw_commands_view(vptu_commands) 306 | 307 | # A viewplane to hold the register state 308 | vp_registers=viewplane(width=leftwidth,height=4) 309 | vptu_registers = termbox_util(vp_registers) 310 | draw_registers_view(vptu_registers,0,0,0,0,0x100,0) 311 | 312 | # A viewplane to hold the action view 313 | vp_action=viewplane(width=leftwidth,height=4) 314 | vptu_action = termbox_util(vp_action) 315 | draw_action_view(vptu_action,"Nothing Yet") 316 | 317 | # A Viewplane to hold the goto input 318 | vp_goto=viewplane(width=28,height=3) 319 | vptu_goto=termbox_util(vp_goto) 320 | draw_goto_view(vp_goto, vptu_goto,"") 321 | 322 | # A viewplane to hold the register state 323 | vp_observer=viewplane(width=maxx+1-leftwidth,height=8) 324 | vptu_observer = termbox_util(vp_observer) 325 | draw_observer_view(vptu_observer,observer_selected,"no_event") 326 | 327 | # Since we have a sliding memory view but want a box around the 328 | # visible window, we will have a two deep hierarchy - the outer 329 | # with the border and the inner, set inside with the scrolling 330 | # view. 331 | 332 | # A viewplane to hold the inner memory view, 8 bytes per line * 8192 = 64k 333 | vp_memory_inner=viewplane(width=leftwidth-2,height=8192) 334 | vptu_memory_inner = termbox_util(vp_memory_inner) 335 | draw_memory_inner_view(vptu_memory_inner, object_code) 336 | 337 | # The outer to hold the border and the innner 338 | vp_memory_outer=viewplane(width=leftwidth,height=memorywindowheight) 339 | vptu_memory_outer = termbox_util(vp_memory_outer) 340 | draw_memory_outer_view(vptu_memory_outer,memory_selected) 341 | 342 | # Add the views as persistent views in the terminal view 343 | pvid_commands = tbtu.add_persistent_viewplane(vp_commands,0,0) 344 | pvid_registers = tbtu.add_persistent_viewplane(vp_registers,0,4) 345 | pvid_action = tbtu.add_persistent_viewplane(vp_action,0,8) 346 | pvid_observer = tbtu.add_persistent_viewplane(vp_observer,leftwidth,4) 347 | #pvid_goto = tbtu.add_persistent_viewplane(vp_goto,10,10) 348 | 349 | # Goto window is only visible when taking goto input. Hide it. 350 | #tbtu.deactivate_persistent_vp(pvid_goto) 351 | 352 | # The memory view is a window into the viewplane since the 353 | # viewplane has the whole of memory laid out in. 354 | # Parameters are : vp,width,height,srcx,srcy,viewx,viewy,active 355 | pvid_memory_outer = tbtu.add_persistent_viewplane(vp_memory_outer,0,12) 356 | 357 | # Put the inner memory view in the outer memory view 358 | pvid_memory_inner = vptu_memory_outer.add_persistent_viewplane_window(vp_memory_inner,leftwidth-2,memorywindowheight-2,0,0,1,1,True) 359 | 360 | # The disassembly view 361 | vp_disassembly_inner=viewplane(width=maxx+1-leftwidth-2,height=100) 362 | vptu_disassembly_inner = termbox_util(vp_disassembly_inner) 363 | s.pc = 0 364 | draw_disassembly_inner_view(vptu_disassembly_inner, object_code) 365 | 366 | # The outer to hold the border and the innner 367 | vp_disassembly_outer=viewplane(width=maxx+1-leftwidth,height=maxy-12) 368 | vptu_disassembly_outer = termbox_util(vp_disassembly_outer) 369 | draw_disassembly_outer_view(vptu_disassembly_outer,disassembly_selected) 370 | 371 | # The disassembly view is a window into the viewplane since the 372 | # viewplane has a lot of listing laid out in. 373 | # Parameters are : vp,width,height,srcx,srcy,viewx,viewy,active 374 | pvid_disassembly_outer = tbtu.add_persistent_viewplane(vp_disassembly_outer,leftwidth,12) 375 | 376 | # Put the inner memory view in the outer memory view 377 | pvid_disassembly_inner = vptu_disassembly_outer.add_persistent_viewplane_window(vp_disassembly_inner,(maxx-leftwidth-2),maxy-14,0,0,1,1,True) 378 | 379 | # The main loop 380 | # Listen for keypresses and do what is asked 381 | # Quit when esc is pressed 382 | 383 | memaddr = 0 384 | 385 | while True: 386 | #Refresh the screen 387 | (cpdl,dc) = draw_disassembly_inner_view(vptu_disassembly_inner,object_code) 388 | #draw_memory_inner_view(vptu_memory_inner, object_code) 389 | vptu_memory_outer.present() 390 | 391 | # Compute where to place the disassembly in memory 392 | new_srcx=0 393 | if cpdl > 0: 394 | new_srcy = cpdl -10 395 | if new_srcy < 0: 396 | new_srcy = 0 397 | else: 398 | new_srcy = 0 399 | 400 | vptu_disassembly_outer.move_persistent_viewplane_window(pvid_memory_inner,new_srcx,new_srcy) 401 | #vptu_disassembly_outer.addstr(20,0,"cpdl="+str(cpdl)+" len="+str(dc)+" ") 402 | vptu_disassembly_outer.present() 403 | draw_registers_view(vptu_registers,s.pc,s.a,s.x,s.y,s.sp,s.cc) 404 | draw_observer_view(vptu_observer,observer_selected,event) 405 | tbtu.present() 406 | 407 | # Get a keypress 408 | event = tb.poll_event() 409 | # untangle it's fields 410 | (type, ch, key, mod, w, h, x, y ) = event 411 | 412 | ## Go to an edit box if the user presses 'e' 413 | #if type==termbox.EVENT_KEY and ch=='e': 414 | # content=el.edit(el_validator,max_width=10) 415 | 416 | #vptu_observer.addstr(1,2,str(event)) 417 | # Exit when escape pressed. 418 | if type==termbox.EVENT_KEY and key == termbox.KEY_ESC: 419 | return event 420 | 421 | if type==termbox.EVENT_KEY and key == termbox_util.key_tab: 422 | 423 | # Rotate through selecting one of the three windows 424 | if memory_selected: 425 | memory_selected = False 426 | observer_selected = True 427 | disassembly_selected = False 428 | elif observer_selected: 429 | memory_selected = False 430 | observer_selected = False 431 | disassembly_selected = True 432 | elif disassembly_selected: 433 | memory_selected = True 434 | observer_selected = False 435 | disassembly_selected = False 436 | else: 437 | memory_selected = False 438 | observer_selected = True 439 | disassembly_selected = False 440 | 441 | draw_memory_outer_view(vptu_memory_outer,memory_selected) 442 | draw_disassembly_outer_view(vptu_disassembly_outer,disassembly_selected) 443 | draw_observer_view(vptu_observer,observer_selected,event) 444 | 445 | # When r is pressed, sent a reset to the simulator 446 | elif type==termbox.EVENT_KEY and (ch=='r' or ch=='r'): 447 | s.reset() 448 | vptu_action.addstr(1,1,"RESET ") 449 | draw_memory_inner_view(vptu_memory_inner, object_code) 450 | 451 | 452 | # When i is pressed, sent an irq to the simulator 453 | elif type==termbox.EVENT_KEY and (ch=='I' or ch=='i'): 454 | s.irq() 455 | vptu_action.addstr(1,1,"IRQ ") 456 | draw_memory_inner_view(vptu_memory_inner, object_code) 457 | 458 | # When n is pressed, sent an NMI to the simulator 459 | elif type==termbox.EVENT_KEY and (ch=='N' or ch=='n'): 460 | s.reset() 461 | vptu_action.addstr(1,1,"NMI ") 462 | draw_memory_inner_view(vptu_memory_inner, object_code) 463 | 464 | # When g is pressed, input a goto address 465 | elif type==termbox.EVENT_KEY and (ch=='G' or ch=='g'): 466 | #s.reset() 467 | 468 | tbtu.draw_viewplane(vp_goto,10,10) 469 | tb.present() 470 | #tbtu.activate_persistent_vp(pvid_goto) 471 | el = termbox_editableline(tb,tbtu, 26,11, 5) 472 | tb.present() 473 | 474 | thetext = el.edit(hex_validator, contents="", max_width=4) 475 | 476 | #tbtu.deactivate_persistent_vp(pvid_goto) 477 | 478 | if len(thetext) > 0: 479 | thetext = "0x"+thetext 480 | addr = int(eval(thetext)) 481 | if addr < 0x10000: 482 | s.pc = addr 483 | 484 | # When s is pressed, execute one instruction 485 | elif type==termbox.EVENT_KEY and ch=='s': 486 | distxt, _ = dis.disassemble_line(s.pc) 487 | (action, addr) = s.execute() 488 | vptu_action.addstr(1,1,distxt.ljust(leftwidth-2)) 489 | draw_registers_view(vptu_registers,s.pc,s.a,s.x,s.y,s.sp,s.cc) 490 | draw_disassembly_inner_view(vptu_disassembly_inner,object_code) 491 | 492 | # simulator indicated it tried to run uninitialized memory 493 | if action=="weeds": 494 | vptu_action.addstr(1,2,"In unitialized mem ".ljust(leftwidth-2)) 495 | 496 | # Memory changed so update that part of the memory view 497 | # Back up a bit in case we are in a multi byte stack op 498 | elif action=="w": 499 | startline = addr / 8 500 | startline -= 1 501 | endline = startline+2 502 | vptu_action.addstr(1,2,("modified %04x=%02x " % (addr,object_code[addr])).ljust(leftwidth-2)) 503 | draw_memory_inner_view_partial(vptu_memory_inner,startline,endline,object_code) 504 | vptu_memory_outer.present() 505 | elif action == "stack": 506 | startline = 0x100 507 | endline = 0x200 508 | vptu_action.addstr(1,2,"modified stack ".ljust(leftwidth-2)) 509 | draw_memory_inner_view_partial(vptu_memory_inner,startline,endline,object_code) 510 | vptu_memory_outer.present() 511 | elif action == "not_instruction": 512 | vptu_action.addstr(1,2,"Not an instruction ".ljust(leftwidth-2)) 513 | else: 514 | vptu_action.addstr(1,2," ".ljust(leftwidth-2)) 515 | 516 | # Move memory window with the mouse wheel 517 | elif type==termbox.EVENT_KEY and key==termbox_util.TB_KEY_ARROW_UP: 518 | #elif type==termbox.EVENT_KEY and ch=='i': 519 | memaddr -= 8 520 | if memaddr < 0: 521 | memaddr = 0 522 | 523 | new_srcy = memaddr/8 524 | new_srcx = 0 525 | vptu_memory_outer.move_persistent_viewplane_window(pvid_memory_inner,new_srcx,new_srcy) 526 | 527 | # Move memory window with the mouse wheel 528 | #elif type==termbox.EVENT_KEY and key==termbox_util.TB_KEY_ARROW_UP: 529 | elif type==termbox.EVENT_KEY and ch=='o': 530 | memaddr -= 8*32 531 | if memaddr < 0: 532 | memaddr = 0 533 | 534 | new_srcy = memaddr/8 535 | new_srcx = 0 536 | vptu_memory_outer.move_persistent_viewplane_window(pvid_memory_inner,new_srcx,new_srcy) 537 | 538 | elif type==termbox.EVENT_KEY and key==termbox_util.TB_KEY_ARROW_DOWN: 539 | #elif type==termbox.EVENT_KEY and ch=='k': 540 | memaddr += 8 541 | line = memaddr/8 542 | if (line+memorywindowheight-2) < 8192: 543 | memaddr = memaddr % 65536 544 | new_srcy = line 545 | new_srcx = 0 546 | vptu_memory_outer.move_persistent_viewplane_window(pvid_memory_inner,new_srcx,new_srcy) 547 | 548 | #elif type==termbox.EVENT_KEY and key==termbox_util.TB_KEY_MOUSE_WHEEL_DOWN: 549 | elif type==termbox.EVENT_KEY and ch=='l': 550 | memaddr += 8*32 551 | line = memaddr/8 552 | if (line+memorywindowheight-2) < 8192: 553 | memaddr = memaddr % 65536 554 | new_srcy = line 555 | new_srcx = 0 556 | vptu_memory_outer.move_persistent_viewplane_window(pvid_memory_inner,new_srcx,new_srcy) 557 | 558 | 559 | # Start with argument parsing 560 | 561 | parser = argparse.ArgumentParser(description='A 65C02 debugger built on the py6502 assembler, diassembler and simulator') 562 | 563 | parser.add_argument("-v","--verbose",action="store_true", dest="verbose", default=False, help="Print status messages to stderr") 564 | parser.add_argument("filename", help="read assembly from filename", nargs='?', metavar="FILE") 565 | 566 | options = parser.parse_args() 567 | 568 | filename = str(options.filename) 569 | if (options.filename == None): 570 | print("[py6502 Debugger] Error - A filename of an assembly file is required") 571 | quit() 572 | 573 | if (options.verbose==True): 574 | verbose = True 575 | else: 576 | verbose = False 577 | 578 | if (verbose): 579 | sys.stderr.write("filename:"+str(filename)+"\n") 580 | sys.stderr.write("verbose :"+str(verbose)+"\n") 581 | 582 | # open the assembly file 583 | f = open(str(options.filename)) 584 | 585 | # Read the file 586 | try: 587 | lines = f.readlines() 588 | except IOError: 589 | sys.stderr.write("ERROR: File Error") 590 | try: 591 | sys.stderr.close() 592 | except IOError: 593 | pass 594 | quit() 595 | 596 | # 597 | # Assemble the file 598 | # 599 | 600 | a = asm6502() 601 | a.assemble(lines) 602 | 603 | object_code = copy.deepcopy(a.object_code) 604 | symbol_table = copy.deepcopy(a.symbols) 605 | 606 | # Simulator instance 607 | s = sim6502(object_code,symbols=symbol_table) 608 | 609 | # Disassembler Instance 610 | dis = dis6502(object_code,symbols=symbol_table) 611 | 612 | # start the debugger 613 | d = dbg6502(object_code,symbol_table) 614 | #print d 615 | 616 | print("Exited 6502 Debugger") 617 | 618 | -------------------------------------------------------------------------------- /src/dis6502.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 4 | # The 65C02 Simulator 5 | # 6 | 7 | 8 | class dis6502: 9 | def __init__(self, object_code, symbols=None): 10 | 11 | self.object_code = object_code 12 | for i in range(len(self.object_code)): 13 | if self.object_code[i] < 0: 14 | self.object_code[i] = 0x00 15 | 16 | self.labels = {} 17 | if symbols == None: 18 | self.have_symbols = False 19 | else: 20 | self.have_symbols = True 21 | 22 | self.symbols = symbols 23 | for label, offset in self.symbols.items(): 24 | self.labels[offset] = label 25 | self.build_opcode_table() 26 | 27 | def build_opcode_table(self): 28 | self.hexcodes = dict() 29 | self.hexcodes[0x00] = ("brk", "implicit") 30 | self.hexcodes[0x10] = ("bpl", "relative") 31 | self.hexcodes[0x20] = ("jsr", "absolute") 32 | self.hexcodes[0x30] = ("bmi", "relative") 33 | self.hexcodes[0x40] = ("rti", "implicit") 34 | self.hexcodes[0x50] = ("bvc", "relative") 35 | self.hexcodes[0x60] = ("rts", "implicit") 36 | self.hexcodes[0x70] = ("bvs", "relative") 37 | self.hexcodes[0x80] = ("bra", "relative") 38 | self.hexcodes[0x90] = ("bcc", "relative") 39 | self.hexcodes[0xA0] = ("ldy", "immediate") 40 | self.hexcodes[0xB0] = ("bcs", "relative") 41 | self.hexcodes[0xC0] = ("cpy", "immediate") 42 | self.hexcodes[0xD0] = ("bne", "relative") 43 | self.hexcodes[0xE0] = ("cpx", "immediate") 44 | self.hexcodes[0xF0] = ("beq", "relative") 45 | 46 | self.hexcodes[0x01] = ("ora", "zeropageindexedindirectx") 47 | self.hexcodes[0x11] = ("ora", "zeropageindexedindirecty") 48 | self.hexcodes[0x21] = ("and", "zeropageindexedindirectx") 49 | self.hexcodes[0x31] = ("and", "zeropageindexedindirecty") 50 | self.hexcodes[0x41] = ("eor", "zeropageindexedindirectx") 51 | self.hexcodes[0x51] = ("eor", "zeropageindexedindirecty") 52 | self.hexcodes[0x61] = ("adc", "zeropageindexedindirectx") 53 | self.hexcodes[0x71] = ("adc", "zeropageindexedindirecty") 54 | self.hexcodes[0x81] = ("sta", "zeropageindexedindirectx") 55 | self.hexcodes[0x91] = ("sta", "zeropageindexedindirecty") 56 | self.hexcodes[0xA1] = ("lda", "zeropageindexedindirectx") 57 | self.hexcodes[0xB1] = ("lda", "zeropageindexedindirecty") 58 | self.hexcodes[0xC1] = ("cmp", "zeropageindexedindirectx") 59 | self.hexcodes[0xD1] = ("cmp", "zeropageindexedindirecty") 60 | self.hexcodes[0xE1] = ("sbc", "zeropageindexedindirectx") 61 | self.hexcodes[0xF1] = ("sbc", "zeropageindexedindirecty") 62 | 63 | self.hexcodes[0x02] = ("", "") 64 | self.hexcodes[0x12] = ("ora", "zeropageindirect") 65 | self.hexcodes[0x22] = ("", "") 66 | self.hexcodes[0x32] = ("and", "zeropageindirect") 67 | self.hexcodes[0x42] = ("", "") 68 | self.hexcodes[0x52] = ("eor", "zeropageindirect") 69 | self.hexcodes[0x62] = ("", "") 70 | self.hexcodes[0x72] = ("adc", "zeropageindirect") 71 | self.hexcodes[0x82] = ("", "") 72 | self.hexcodes[0x92] = ("sta", "zeropageindirect") 73 | self.hexcodes[0xA2] = ("ldx", "immediate") 74 | self.hexcodes[0xB2] = ("lda", "zeropageindirect") 75 | self.hexcodes[0xC2] = ("", "") 76 | self.hexcodes[0xD2] = ("cmp", "zeropageindirect") 77 | self.hexcodes[0xE2] = ("", "") 78 | self.hexcodes[0xF2] = ("sbc", "zeropageindirect") 79 | 80 | self.hexcodes[0x03] = ("", "") 81 | self.hexcodes[0x13] = ("", "") 82 | self.hexcodes[0x23] = ("", "") 83 | self.hexcodes[0x33] = ("", "") 84 | self.hexcodes[0x43] = ("", "") 85 | self.hexcodes[0x53] = ("", "") 86 | self.hexcodes[0x63] = ("", "") 87 | self.hexcodes[0x73] = ("", "") 88 | self.hexcodes[0x83] = ("", "") 89 | self.hexcodes[0x93] = ("", "") 90 | self.hexcodes[0xA3] = ("", "") 91 | self.hexcodes[0xB3] = ("", "") 92 | self.hexcodes[0xC3] = ("", "") 93 | self.hexcodes[0xD3] = ("", "") 94 | self.hexcodes[0xE3] = ("", "") 95 | self.hexcodes[0xF3] = ("", "") 96 | 97 | self.hexcodes[0x04] = ("tsb", "zeropage") 98 | self.hexcodes[0x14] = ("trb", "zeropage") 99 | self.hexcodes[0x24] = ("bit", "zeropage") 100 | self.hexcodes[0x34] = ("bit", "zeropagex") 101 | self.hexcodes[0x44] = ("", "") 102 | self.hexcodes[0x54] = ("", "") 103 | self.hexcodes[0x64] = ("stz", "zeropage") 104 | self.hexcodes[0x74] = ("stz", "zeropagex") 105 | self.hexcodes[0x84] = ("sty", "zeropage") 106 | self.hexcodes[0x94] = ("sty", "zeropagex") 107 | self.hexcodes[0xA4] = ("ldy", "zeropage") 108 | self.hexcodes[0xB4] = ("ldy", "zeropagex") 109 | self.hexcodes[0xC4] = ("cpy", "zeropage") 110 | self.hexcodes[0xD4] = ("", "") 111 | self.hexcodes[0xE4] = ("cpx", "zeropage") 112 | self.hexcodes[0xF4] = ("", "") 113 | 114 | self.hexcodes[0x05] = ("ora", "zeropage") 115 | self.hexcodes[0x15] = ("ora", "zeropagex") 116 | self.hexcodes[0x25] = ("and", "zeropage") 117 | self.hexcodes[0x35] = ("and", "zeropagex") 118 | self.hexcodes[0x45] = ("eor", "zeropage") 119 | self.hexcodes[0x55] = ("eor", "zeropagex") 120 | self.hexcodes[0x65] = ("adc", "zeropage") 121 | self.hexcodes[0x75] = ("adc", "zeropagex") 122 | self.hexcodes[0x85] = ("sta", "zeropage") 123 | self.hexcodes[0x95] = ("sta", "zeropagex") 124 | self.hexcodes[0xA5] = ("lda", "zeropage") 125 | self.hexcodes[0xB5] = ("lda", "zeropagex") 126 | self.hexcodes[0xC5] = ("cmp", "zeropage") 127 | self.hexcodes[0xD5] = ("cmp", "zeropagex") 128 | self.hexcodes[0xE5] = ("sbc", "zeropage") 129 | self.hexcodes[0xF5] = ("sbc", "zeropagex") 130 | 131 | self.hexcodes[0x06] = ("asl", "zeropage") 132 | self.hexcodes[0x16] = ("asl", "zeropagex") 133 | self.hexcodes[0x26] = ("rol", "zeropage") 134 | self.hexcodes[0x36] = ("rol", "zeropagex") 135 | self.hexcodes[0x46] = ("lsr", "zeropage") 136 | self.hexcodes[0x56] = ("lsr", "zeropagex") 137 | self.hexcodes[0x66] = ("ror", "zeropage") 138 | self.hexcodes[0x76] = ("ror", "zeropagex") 139 | self.hexcodes[0x86] = ("stx", "zeropage") 140 | self.hexcodes[0x96] = ("stx", "zeropagey") 141 | self.hexcodes[0xA6] = ("ldx", "zeropage") 142 | self.hexcodes[0xB6] = ("ldx", "zeropagey") 143 | self.hexcodes[0xC6] = ("dec", "zeropage") 144 | self.hexcodes[0xD6] = ("dec", "zeropagex") 145 | self.hexcodes[0xE6] = ("inc", "zeropage") 146 | self.hexcodes[0xF6] = ("inc", "zeropagex") 147 | 148 | self.hexcodes[0x07] = ("", "") 149 | self.hexcodes[0x17] = ("", "") 150 | self.hexcodes[0x27] = ("", "") 151 | self.hexcodes[0x37] = ("", "") 152 | self.hexcodes[0x47] = ("", "") 153 | self.hexcodes[0x57] = ("", "") 154 | self.hexcodes[0x67] = ("", "") 155 | self.hexcodes[0x77] = ("", "") 156 | self.hexcodes[0x87] = ("", "") 157 | self.hexcodes[0x97] = ("", "") 158 | self.hexcodes[0xA7] = ("", "") 159 | self.hexcodes[0xB7] = ("", "") 160 | self.hexcodes[0xC7] = ("", "") 161 | self.hexcodes[0xD7] = ("", "") 162 | self.hexcodes[0xE7] = ("", "") 163 | self.hexcodes[0xF7] = ("", "") 164 | 165 | self.hexcodes[0x08] = ("php", "implicit") 166 | self.hexcodes[0x18] = ("clc", "implicit") 167 | self.hexcodes[0x28] = ("plp", "implicit") 168 | self.hexcodes[0x38] = ("sec", "implicit") 169 | self.hexcodes[0x48] = ("pha", "implicit") 170 | self.hexcodes[0x58] = ("cli", "implicit") 171 | self.hexcodes[0x68] = ("pla", "implicit") 172 | self.hexcodes[0x78] = ("sei", "implicit") 173 | self.hexcodes[0x88] = ("dey", "implicit") 174 | self.hexcodes[0x98] = ("tya", "implicit") 175 | self.hexcodes[0xA8] = ("tay", "implicit") 176 | self.hexcodes[0xB8] = ("clv", "implicit") 177 | self.hexcodes[0xC8] = ("iny", "implicit") 178 | self.hexcodes[0xD8] = ("cld", "implicit") 179 | self.hexcodes[0xE8] = ("inx", "implicit") 180 | self.hexcodes[0xF8] = ("sed", "implicit") 181 | 182 | self.hexcodes[0x09] = ("ora", "immediate") 183 | self.hexcodes[0x19] = ("ora", "absolutey") 184 | self.hexcodes[0x29] = ("and", "immediate") 185 | self.hexcodes[0x39] = ("and", "absolutey") 186 | self.hexcodes[0x49] = ("eor", "immediate") 187 | self.hexcodes[0x59] = ("eor", "absolutey") 188 | self.hexcodes[0x69] = ("adc", "immediate") 189 | self.hexcodes[0x79] = ("adc", "absolutey") 190 | self.hexcodes[0x89] = ("bit", "immediate") 191 | self.hexcodes[0x99] = ("sta", "absolutey") 192 | self.hexcodes[0xA9] = ("lda", "immediate") 193 | self.hexcodes[0xB9] = ("lda", "absolutey") 194 | self.hexcodes[0xC9] = ("cmp", "immediate") 195 | self.hexcodes[0xD9] = ("cmp", "absolutey") 196 | self.hexcodes[0xE9] = ("sbc", "immediate") 197 | self.hexcodes[0xF9] = ("sbc", "absolutey") 198 | 199 | self.hexcodes[0x0A] = ("asl", "accumulator") 200 | self.hexcodes[0x1A] = ("ina", "accumulator") 201 | self.hexcodes[0x2A] = ("rol", "accumulator") 202 | self.hexcodes[0x3A] = ("dea", "accumulator") 203 | self.hexcodes[0x4A] = ("lsr", "accumulator") 204 | self.hexcodes[0x5A] = ("phy", "implicit") 205 | self.hexcodes[0x6A] = ("ror", "accumulator") 206 | self.hexcodes[0x7A] = ("ply", "implicit") 207 | self.hexcodes[0x8A] = ("txa", "implicit") 208 | self.hexcodes[0x9A] = ("txs", "implicit") 209 | self.hexcodes[0xAA] = ("tax", "implicit") 210 | self.hexcodes[0xBA] = ("tsx", "implicit") 211 | self.hexcodes[0xCA] = ("dex", "implicit") 212 | self.hexcodes[0xDA] = ("phx", "implicit") 213 | self.hexcodes[0xEA] = ("nop", "implicit") 214 | self.hexcodes[0xFA] = ("plx", "implicit") 215 | 216 | self.hexcodes[0x0B] = ("", "") 217 | self.hexcodes[0x1B] = ("", "") 218 | self.hexcodes[0x2B] = ("", "") 219 | self.hexcodes[0x3B] = ("", "") 220 | self.hexcodes[0x4B] = ("", "") 221 | self.hexcodes[0x5B] = ("", "") 222 | self.hexcodes[0x6B] = ("", "") 223 | self.hexcodes[0x7B] = ("", "") 224 | self.hexcodes[0x8B] = ("", "") 225 | self.hexcodes[0x9B] = ("", "") 226 | self.hexcodes[0xAB] = ("", "") 227 | self.hexcodes[0xBB] = ("", "") 228 | self.hexcodes[0xCB] = ("", "") 229 | self.hexcodes[0xDB] = ("", "") 230 | self.hexcodes[0xEB] = ("", "") 231 | self.hexcodes[0xFB] = ("", "") 232 | 233 | self.hexcodes[0x0C] = ("tsb", "absolute") 234 | self.hexcodes[0x1C] = ("trb", "absolute") 235 | self.hexcodes[0x2C] = ("bit", "absolute") 236 | self.hexcodes[0x3C] = ("bit", "absolutex") 237 | self.hexcodes[0x4C] = ("jmp", "absolute") 238 | self.hexcodes[0x5C] = ("", "") 239 | self.hexcodes[0x6C] = ("jmp", "absoluteindirect") 240 | self.hexcodes[0x7C] = ("jmp", "absoluteindexedindirect") 241 | self.hexcodes[0x8C] = ("sty", "absolute") 242 | self.hexcodes[0x9C] = ("stz", "absolute") 243 | self.hexcodes[0xAC] = ("ldy", "absolute") 244 | self.hexcodes[0xBC] = ("ldy", "absolutex") 245 | self.hexcodes[0xCC] = ("cpy", "absolute") 246 | self.hexcodes[0xDC] = ("", "") 247 | self.hexcodes[0xEC] = ("cpx", "absolute") 248 | self.hexcodes[0xFC] = ("", "") 249 | 250 | self.hexcodes[0x0D] = ("ora", "absolute") 251 | self.hexcodes[0x1D] = ("ora", "absolutex") 252 | self.hexcodes[0x2D] = ("and", "absolute") 253 | self.hexcodes[0x3D] = ("and", "absolutex") 254 | self.hexcodes[0x4D] = ("eor", "absolute") 255 | self.hexcodes[0x5D] = ("eor", "absolutex") 256 | self.hexcodes[0x6D] = ("adc", "absolute") 257 | self.hexcodes[0x7D] = ("adc", "absolutex") 258 | self.hexcodes[0x8D] = ("sta", "absolute") 259 | self.hexcodes[0x9D] = ("sta", "absolutex") 260 | self.hexcodes[0xAD] = ("lda", "absolute") 261 | self.hexcodes[0xBD] = ("lda", "absolutex") 262 | self.hexcodes[0xCD] = ("cmp", "absolute") 263 | self.hexcodes[0xDD] = ("cmp", "absolutex") 264 | self.hexcodes[0xED] = ("sbc", "absolute") 265 | self.hexcodes[0xFD] = ("sbc", "absolutex") 266 | 267 | self.hexcodes[0x0E] = ("asl", "absolute") 268 | self.hexcodes[0x1E] = ("asl", "absolutex") 269 | self.hexcodes[0x2E] = ("rol", "absolute") 270 | self.hexcodes[0x3E] = ("rol", "absolutex") 271 | self.hexcodes[0x4E] = ("lsr", "absolute") 272 | self.hexcodes[0x5E] = ("lsr", "absolutex") 273 | self.hexcodes[0x6E] = ("ror", "absolute") 274 | self.hexcodes[0x7E] = ("ror", "absolutex") 275 | self.hexcodes[0x8E] = ("stx", "absolute") 276 | self.hexcodes[0x9E] = ("stz", "absolutex") 277 | self.hexcodes[0xAE] = ("ldx", "absolute") 278 | self.hexcodes[0xBE] = ("ldx", "absolutey") 279 | self.hexcodes[0xCE] = ("dec", "absolute") 280 | self.hexcodes[0xDE] = ("dec", "absolutex") 281 | self.hexcodes[0xEE] = ("inc", "absolute") 282 | self.hexcodes[0xFE] = ("inc", "absolutex") 283 | 284 | self.hexcodes[0x0F] = ("", "") 285 | self.hexcodes[0x1F] = ("", "") 286 | self.hexcodes[0x2F] = ("", "") 287 | self.hexcodes[0x3F] = ("", "") 288 | self.hexcodes[0x4F] = ("", "") 289 | self.hexcodes[0x5F] = ("", "") 290 | self.hexcodes[0x6F] = ("", "") 291 | self.hexcodes[0x7F] = ("", "") 292 | self.hexcodes[0x8F] = ("", "") 293 | self.hexcodes[0x9F] = ("", "") 294 | self.hexcodes[0xAF] = ("", "") 295 | self.hexcodes[0xBF] = ("", "") 296 | self.hexcodes[0xCF] = ("", "") 297 | self.hexcodes[0xDF] = ("", "") 298 | self.hexcodes[0xEF] = ("", "") 299 | self.hexcodes[0xFF] = ("", "") 300 | 301 | def disassemble_line(self, address): 302 | # print "DISASSEMBLER ADDR: %04x" % address 303 | opcode_hex = self.object_code[address] 304 | operandl = self.object_code[(address + 1) % 65536] 305 | operandh = self.object_code[(address + 2) % 65536] 306 | 307 | operand8 = operandl 308 | operand16 = operandl + (operandh << 8) 309 | 310 | # print "OPCODE_HEX = %x" % opcode_hex 311 | opcode, addrmode = self.hexcodes[opcode_hex] 312 | # print "DISASSEMBLER OPCD: %02x" % opcode_hex 313 | # print "DISASSMBLER OPCD TXT:"+str(opcode)+" "+str(addrmode) 314 | if address in self.labels: 315 | label = (self.labels[address] + ":").ljust(10) 316 | else: 317 | label = " " * 10 318 | 319 | addr_text = "%04x " % address 320 | 321 | # Format the operand based on the addressmode 322 | length = 1 323 | if addrmode == "zeropageindexedindirectx": 324 | operandtext = "($%02x,x)" % operand8 325 | length = 2 326 | elif addrmode == "zeropageindexedindirecty": 327 | operandtext = "($%02x),y" % operand8 328 | length = 2 329 | elif addrmode == "zeropageindirect": 330 | operandtext = "($%02x)" % operand8 331 | length = 2 332 | elif addrmode == "zeropage": 333 | operandtext = "$%02x" % operand8 334 | length = 2 335 | elif addrmode == "zeropagex": 336 | operandtext = "$%02x,x" % operand8 337 | length = 2 338 | elif addrmode == "zeropagey": 339 | operandtext = "$%02x,y" % operand8 340 | length = 2 341 | elif addrmode == "immediate": 342 | operandtext = "#$%02x" % operand8 343 | length = 2 344 | elif addrmode == "absolutey": 345 | operandtext = "$%04x,y" % operand16 346 | length = 3 347 | elif addrmode == "absolute": 348 | operandtext = "$%04x" % operand16 349 | length = 3 350 | elif addrmode == "absoluteindirect": 351 | operandtext = "($%04x)" % operand16 352 | length = 3 353 | elif addrmode == "absoluteindexedindirect": 354 | operandtext = "($%04x,x)" % operand16 355 | length = 3 356 | elif addrmode == "absolutex": 357 | operandtext = "$%04x,x" % operand16 358 | length = 3 359 | elif addrmode == "indirect": 360 | operandtext = "($%04x)" % operand16 361 | length = 3 362 | elif addrmode == "relative": 363 | if operand8 < 128: 364 | operandtext = "+$%02x" % operand8 365 | else: 366 | offset = (operand8 & 0x7f) - 128 367 | 368 | offset = -offset 369 | operandtext = "-$%02x" % offset 370 | length = 2 371 | elif addrmode == "accumulator": 372 | operandtext = "A" 373 | length = 1 374 | elif addrmode == "implicit": 375 | operandtext = "" 376 | length = 1 377 | elif addrmode == "": 378 | operandtext = "" 379 | length = 1 380 | elif addrmode == None: 381 | operandtext = "" 382 | length = 1 383 | else: 384 | print("ERROR: Disassembler: Address mode %s not found" % addrmode) 385 | exit() 386 | 387 | if length == 1: 388 | binary_text = "%02x " % opcode_hex 389 | elif length == 2: 390 | binary_text = "%02x %02x " % (opcode_hex, operandl) 391 | else: 392 | binary_text = "%02x %02x %02x " % (opcode_hex, operandl, operandh) 393 | 394 | the_text = label + " " + addr_text + binary_text 395 | the_text += (opcode.ljust(5)) 396 | the_text += operandtext 397 | return (the_text, length) 398 | 399 | def disassemble_region(self, address, region_length): 400 | current_address = address 401 | while current_address < address + region_length: 402 | (line, length) = self.disassemble_line(current_address) 403 | yield line 404 | current_address += length 405 | -------------------------------------------------------------------------------- /src/load_store.asm: -------------------------------------------------------------------------------- 1 | ORG $100 2 | start: 3 | LDA #$aa 4 | STA $10 5 | ADC #$54 ; comment 6 | ADC #$01 7 | ADC #$01 ;comment 8 | LDA #$55 9 | STA $11 10 | LDA #$40 11 | STA $12 12 | LDX #$00 13 | 14 | loop: 15 | STA $10,x 16 | INX 17 | SBC #$01 18 | ;CMP #$00 19 | BNE loop 20 | NOP 21 | NOP 22 | BRA start 23 | ORG $fffa 24 | DW $0100,$0100,$0100 ; Reset Vector 25 | -------------------------------------------------------------------------------- /src/memory_map.py: -------------------------------------------------------------------------------- 1 | """Memory map for 6502 address space.""" 2 | 3 | # Memory access modes 4 | MODE_READ = 0 5 | MODE_WRITE = 1 6 | MODE_EXECUTE = 2 7 | 8 | class TrapException(Exception): 9 | """May be raised by an interceptor on access to a memory address.""" 10 | def __init__(self, address, access_mode): 11 | self.address = address 12 | self.access_mode = access_mode 13 | 14 | def __str__(self): 15 | return "Trap when accessing memory location $%X with mode %d" % ( 16 | self.address, self.access_mode) 17 | 18 | 19 | class MemoryMap(object): 20 | # Don't intercept accesses to uninitialized memory 21 | NONE_INTERCEPTOR = 0 22 | # Raise TrapException on read/execute access to uninitialized memory 23 | TRAP_INTERCEPTOR = 1 24 | 25 | def __init__(self, cpu, default_interceptor=NONE_INTERCEPTOR): 26 | # Pointer back to sim6502 object 27 | self.cpu = cpu 28 | 29 | # -1 represents an uninitialized memory address, which will optionally trap if accessed. 30 | self._memory_map = [-1] * 65536 31 | 32 | self.interceptors = {} 33 | 34 | if default_interceptor == self.NONE_INTERCEPTOR: 35 | self.default_interceptor = None 36 | elif default_interceptor == self.TRAP_INTERCEPTOR: 37 | self.default_interceptor = self.TrapInterceptor 38 | else: 39 | self.default_interceptor = default_interceptor 40 | 41 | def TrapInterceptor(self, address, access_mode, _): 42 | if self._memory_map[address] == -1 and (access_mode == MODE_READ or access_mode == MODE_EXECUTE): 43 | 44 | print(self.cpu.show_state()) 45 | print(self.Dump(self.cpu.pc, 0x3)) 46 | raise TrapException(address, access_mode) 47 | 48 | def InitializeMemory(self, address, data, interceptor=None): 49 | for idx, value in enumerate(data): 50 | # Bug: https://github.com/dj-on-github/py6502/issues/6 51 | # Fix this by choosing to skip assigning data from object_code if it is untouched. 52 | #if value < 0 or value > 255: 53 | # raise ValueError 54 | if (value >= 0 and value < 256): 55 | self._memory_map[address + idx] = value 56 | if interceptor: 57 | self.Intercept(address + idx, interceptor) 58 | 59 | def Intercept(self, address, interceptor): 60 | """Register interceptor for access to a memory address""" 61 | self.interceptors[address] = interceptor 62 | 63 | def Dump(self, address=0x0, length=0x10000): 64 | lines = [] 65 | line = [] 66 | for i, value in enumerate(self._memory_map[address:address+length]): 67 | if i % 16 == 0: 68 | line.append('$%04X :' % (address + i)) 69 | if value == -1: 70 | line.append('--') 71 | else: 72 | line.append('%02X' % value) 73 | if (i + 1) % 16 == 0: 74 | lines.append(' '.join(line)) 75 | line = [] 76 | 77 | if line: 78 | lines.append(' '.join(line)) 79 | 80 | return '\n'.join(lines) 81 | 82 | def _MaybeIntercept(self, address, access_mode): 83 | try: 84 | interceptor = self.interceptors[address] 85 | except KeyError: 86 | interceptor = self.default_interceptor 87 | 88 | # May raise TrapException 89 | if interceptor: 90 | if access_mode == MODE_WRITE: 91 | value = self._memory_map[address] 92 | else: 93 | value = None 94 | interceptor(address, access_mode, value) 95 | 96 | # TODO: record access trace records 97 | 98 | def Read(self, address, trace=True): 99 | self._MaybeIntercept(address, MODE_READ) 100 | return self._memory_map[address] 101 | 102 | def Write(self, address, value, trace=True): 103 | self._memory_map[address] = value 104 | self._MaybeIntercept(address, MODE_WRITE) 105 | 106 | def Execute(self, address, trace=True): 107 | self._MaybeIntercept(address, MODE_EXECUTE) 108 | return self._memory_map[address] 109 | 110 | 111 | -------------------------------------------------------------------------------- /src/py6502_common.py: -------------------------------------------------------------------------------- 1 | # Address mode format name applied 2 | # implicit ~ "implicit" 3 | # immediate #num ~ "immediate" 4 | # accumulator A ~ "accumulator" 5 | # absolute $2000 ~ "absolute" 6 | # zero page $20 ~ "zeropage" 7 | # absolute indexed x $5000,X ~ "absolutex" 8 | # absolute indexed y $5000,y ~ "absolutey" 9 | # zeropage indexed x $20,X ~ "zeropagex" 10 | # zeropage indexed y $20,Y ~ "zeropagey" 11 | # relative +10 (or label) ~ "relative" 12 | # zeropage indexed indirect x ($20,X) ~ "zeropageindexedindirectx" 13 | # zeropage indexed indirect y ($20),Y ~ "zeropageindexedindirecty" 14 | # absolute indexed indirect ($5000,X) - only JMP ~ "absoluteindexedindirect" 15 | # zeropage indirect ($20) ~ "zeropageindirect" 16 | # absolute indirect ($5000) - only JMP ~ "absoluteindirect" 17 | 18 | class py6502_common(object): 19 | def __init__(self): 20 | 21 | # Build the opcode type tables 22 | 23 | self.modeswithlowbytevalue = { 24 | "immediate", "absolute", "zeropage", "absolutex", "absolutey", 25 | "zeropagex", "zeropagey", "zeropageindexedindirectx", "zeropageindexedindirecty", 26 | "absoluteindexedindirect", "zeropageindirect", 27 | "absoluteindirect" 28 | } 29 | self.modeswithhighbytevalue = { 30 | "absolute", "absolutex", "absolutey", 31 | "absoluteindexedindirect", "absoluteindirect" 32 | } 33 | 34 | self.validdirectives = { 35 | "db", "dw", "ddw", "dqw", "str", "org", "le", "be" 36 | } 37 | 38 | # TODO: construct this as the union of all of the others. 39 | self.validopcodes = { 40 | "adc", "and", "asl", "bcc", "bcs", "beq", "bit", "bmi", "bne", 41 | "bpl", "bra", "brk", "bvc", "bvs", "clc", "cld", "cli", "clv", 42 | "cmp", "cpx", "cpy", "dea", "dec", "dex", "dey", "eor", "inc", "ina", "inx", 43 | "iny", "jmp", "jsr", "lda", "ldx", "ldy", "lsr", "nop", "ora", 44 | "pha", "php", "phx", "phy", "pla", "plp", "plx", "ply", "rol", 45 | "ror", "rti", "rts", "sbc", "sec", "sed", "sei", "sta", "stx", 46 | "sty", "stz", "tax", "tay", "trb", "tsb", "tsx", "txa", "txs", 47 | "tya" 48 | } 49 | 50 | self.implicitopcodes = { 51 | "brk", "clc", "cld", "cli", "clv", "dex", "dey", "inx", "iny", "nop", 52 | "pha", "php", "phx", "phy", "pla", "plp", "plx", "ply", "rti", "rts", 53 | "sec", "sed", "sei", "tax", "tay", "trb", "tsb", "tsx", "txa", "txs", 54 | "tya" 55 | } 56 | 57 | self.immediateopcodes = { 58 | "adc", "and", "bit", "cmp", "cpx", "cpy", "eor", "lda", "ldx", 59 | "ldy", "ora", "sbc" 60 | } 61 | 62 | self.accumulatoropcodes = { 63 | "asl", "dea", "dec", "ina", "inc", "lsr", "rol", "ror" 64 | } 65 | 66 | self.zeropageopcodes = { 67 | "adc", "and", "asl", "bit", "cmp", "cpx", "cpy", "dec", "eor", "inc", 68 | "lda", "ldx", "ldy", "lsr", "ora", "rol", "ror", "sbc", "sta", "stx", 69 | "sty", "stz", "trb", "tsb" 70 | } 71 | 72 | self.absoluteopcodes = { 73 | "adc", "and", "asl", "bit", "cmp", "cpx", "cpy", "dec", "eor", "inc", 74 | "jmp", "jsr", "lda", "ldx", "ldy", "lsr", "ora", "rol", "ror", "sbc", 75 | "sta", "stx", "sty", "stz", "trb", "tsb" 76 | } 77 | 78 | self.absolutexopcodes = { 79 | "adc", "and", "asl", "bit", "cmp", "dec", "eor", "inc", 80 | "lda", "lsr", "ora", "rol", "ror", "sbc", 81 | "sta", "stz", "ldy" 82 | } 83 | 84 | self.absoluteyopcodes = { 85 | "adc", "and", "cmp", "eor", 86 | "lda", "ldx", "ora", "sbc", "sta" 87 | } 88 | 89 | self.zeropagexopcodes = { 90 | "adc", "and", "cmp", "eor", "lda", "dec", "bit", "asl", "ldy", 91 | "ora", "sbc", "sta", "sty", "ror", "rol", "lsr", "inc", "stz" 92 | } 93 | 94 | self.zeropageyopcodes = {"ldx", "stx"} 95 | 96 | self.relativeopcodes = {"bmi", "bne", "bpl", "bra", "bvc", "bvs", "bcc", "bcs", "beq"} 97 | 98 | self.zeropageindexedindirectxopcodes = { 99 | "adc", "and", "cmp", "eor", "lda", "ora", "sbc", "sta" 100 | } 101 | 102 | self.zeropageindexedindirectyopcodes = { 103 | "adc", "and", "cmp", "eor", "lda", "ora", "sbc", "sta" 104 | } 105 | 106 | self.zeropageindirectopcodes = { 107 | "adc", "and", "cmp", "eor", "lda", "ora", "sbc", "sta" 108 | } 109 | 110 | # Build a map of opcodes to list of modes the opcode supports. 111 | self.map = dict() 112 | 113 | for opcode in self.validopcodes: 114 | self.map[opcode] = list() 115 | if opcode in self.implicitopcodes: 116 | self.map[opcode].append("implicit") 117 | if opcode in self.immediateopcodes: 118 | self.map[opcode].append("immediate") 119 | if opcode in self.accumulatoropcodes: 120 | self.map[opcode].append("accumulator") 121 | if opcode in self.zeropageopcodes: 122 | self.map[opcode].append("zeropage") 123 | if opcode in self.absoluteopcodes: 124 | self.map[opcode].append("absolute") 125 | if opcode in self.absolutexopcodes: 126 | self.map[opcode].append("absolutex") 127 | if opcode in self.absoluteyopcodes: 128 | self.map[opcode].append("absolutey") 129 | if opcode in self.zeropagexopcodes: 130 | self.map[opcode].append("zeropagex") 131 | if opcode in self.zeropageyopcodes: 132 | self.map[opcode].append("zeropagey") 133 | if opcode in self.relativeopcodes: 134 | self.map[opcode].append("relative") 135 | if opcode in self.zeropageindexedindirectxopcodes: 136 | self.map[opcode].append("zeropageindexedindirectx") 137 | if opcode in self.zeropageindexedindirectyopcodes: 138 | self.map[opcode].append("zeropageindexedindirecty") 139 | if opcode in self.zeropageindirectopcodes: 140 | self.map[opcode].append("zeropageindirect") 141 | 142 | # Build the opcode value to opcode name/address mode dictionary 143 | # TODO: this implies all of the above 144 | self.hexcodes = dict() 145 | self.hexcodes[0x00] = ("brk", "implicit") 146 | self.hexcodes[0x10] = ("bpl", "relative") 147 | self.hexcodes[0x20] = ("jsr", "absolute") 148 | self.hexcodes[0x30] = ("bmi", "relative") 149 | self.hexcodes[0x40] = ("rti", "implicit") 150 | self.hexcodes[0x50] = ("bvc", "relative") 151 | self.hexcodes[0x60] = ("rts", "implicit") 152 | self.hexcodes[0x70] = ("bvs", "relative") 153 | self.hexcodes[0x80] = ("bra", "relative") 154 | self.hexcodes[0x90] = ("bcc", "relative") 155 | self.hexcodes[0xA0] = ("ldy", "immediate") 156 | self.hexcodes[0xB0] = ("bcs", "relative") 157 | self.hexcodes[0xC0] = ("cpy", "immediate") 158 | self.hexcodes[0xD0] = ("bne", "relative") 159 | self.hexcodes[0xE0] = ("cpx", "immediate") 160 | self.hexcodes[0xF0] = ("beq", "relative") 161 | 162 | self.hexcodes[0x01] = ("ora", "zeropageindexedindirectx") 163 | self.hexcodes[0x11] = ("ora", "zeropageindexedindirecty") 164 | self.hexcodes[0x21] = ("and", "zeropageindexedindirectx") 165 | self.hexcodes[0x31] = ("and", "zeropageindexedindirecty") 166 | self.hexcodes[0x41] = ("eor", "zeropageindexedindirectx") 167 | self.hexcodes[0x51] = ("eor", "zeropageindexedindirecty") 168 | self.hexcodes[0x61] = ("adc", "zeropageindexedindirectx") 169 | self.hexcodes[0x71] = ("adc", "zeropageindexedindirecty") 170 | self.hexcodes[0x81] = ("sta", "zeropageindexedindirectx") 171 | self.hexcodes[0x91] = ("sta", "zeropageindexedindirecty") 172 | self.hexcodes[0xA1] = ("lda", "zeropageindexedindirectx") 173 | self.hexcodes[0xB1] = ("lda", "zeropageindexedindirecty") 174 | self.hexcodes[0xC1] = ("cmp", "zeropageindexedindirectx") 175 | self.hexcodes[0xD1] = ("cmp", "zeropageindexedindirecty") 176 | self.hexcodes[0xE1] = ("sbc", "zeropageindexedindirectx") 177 | self.hexcodes[0xF1] = ("sbc", "zeropageindexedindirecty") 178 | 179 | self.hexcodes[0x02] = ("", "") 180 | self.hexcodes[0x12] = ("ora", "zeropageindirect") 181 | self.hexcodes[0x22] = ("", "") 182 | self.hexcodes[0x32] = ("and", "zeropageindirect") 183 | self.hexcodes[0x42] = ("", "") 184 | self.hexcodes[0x52] = ("eor", "zeropageindirect") 185 | self.hexcodes[0x62] = ("", "") 186 | self.hexcodes[0x72] = ("adc", "zeropageindirect") 187 | self.hexcodes[0x82] = ("", "") 188 | self.hexcodes[0x92] = ("sta", "zeropageindirect") 189 | self.hexcodes[0xA2] = ("ldx", "immediate") 190 | self.hexcodes[0xB2] = ("lda", "zeropageindirect") 191 | self.hexcodes[0xC2] = ("", "") 192 | self.hexcodes[0xD2] = ("cmp", "zeropageindirect") 193 | self.hexcodes[0xE2] = ("", "") 194 | self.hexcodes[0xF2] = ("sbc", "zeropageindirect") 195 | 196 | self.hexcodes[0x03] = ("", "") 197 | self.hexcodes[0x13] = ("", "") 198 | self.hexcodes[0x23] = ("", "") 199 | self.hexcodes[0x33] = ("", "") 200 | self.hexcodes[0x43] = ("", "") 201 | self.hexcodes[0x53] = ("", "") 202 | self.hexcodes[0x63] = ("", "") 203 | self.hexcodes[0x73] = ("", "") 204 | self.hexcodes[0x83] = ("", "") 205 | self.hexcodes[0x93] = ("", "") 206 | self.hexcodes[0xA3] = ("", "") 207 | self.hexcodes[0xB3] = ("", "") 208 | self.hexcodes[0xC3] = ("", "") 209 | self.hexcodes[0xD3] = ("", "") 210 | self.hexcodes[0xE3] = ("", "") 211 | self.hexcodes[0xF3] = ("", "") 212 | 213 | self.hexcodes[0x04] = ("tsb", "zeropage") 214 | self.hexcodes[0x14] = ("trb", "zeropage") 215 | self.hexcodes[0x24] = ("bit", "zeropage") 216 | self.hexcodes[0x34] = ("bit", "zeropagex") 217 | self.hexcodes[0x44] = ("", "") 218 | self.hexcodes[0x54] = ("", "") 219 | self.hexcodes[0x64] = ("stz", "zeropage") 220 | self.hexcodes[0x74] = ("stz", "zeropagex") 221 | self.hexcodes[0x84] = ("sty", "zeropage") 222 | self.hexcodes[0x94] = ("sty", "zeropagex") 223 | self.hexcodes[0xA4] = ("ldy", "zeropage") 224 | self.hexcodes[0xB4] = ("ldy", "zeropagex") 225 | self.hexcodes[0xC4] = ("cpy", "zeropage") 226 | self.hexcodes[0xD4] = ("", "") 227 | self.hexcodes[0xE4] = ("cpx", "zeropage") 228 | self.hexcodes[0xF4] = ("", "") 229 | 230 | self.hexcodes[0x05] = ("ora", "zeropage") 231 | self.hexcodes[0x15] = ("ora", "zeropagex") 232 | self.hexcodes[0x25] = ("and", "zeropage") 233 | self.hexcodes[0x35] = ("and", "zeropagex") 234 | self.hexcodes[0x45] = ("eor", "zeropage") 235 | self.hexcodes[0x55] = ("eor", "zeropagex") 236 | self.hexcodes[0x65] = ("adc", "zeropage") 237 | self.hexcodes[0x75] = ("adc", "zeropagex") 238 | self.hexcodes[0x85] = ("sta", "zeropage") 239 | self.hexcodes[0x95] = ("sta", "zeropagex") 240 | self.hexcodes[0xA5] = ("lda", "zeropage") 241 | self.hexcodes[0xB5] = ("lda", "zeropagex") 242 | self.hexcodes[0xC5] = ("cmp", "zeropage") 243 | self.hexcodes[0xD5] = ("cmp", "zeropagex") 244 | self.hexcodes[0xE5] = ("sbc", "zeropage") 245 | self.hexcodes[0xF5] = ("sbc", "zeropagex") 246 | 247 | self.hexcodes[0x06] = ("asl", "zeropage") 248 | self.hexcodes[0x16] = ("asl", "zeropagex") 249 | self.hexcodes[0x26] = ("rol", "zeropage") 250 | self.hexcodes[0x36] = ("rol", "zeropagex") 251 | self.hexcodes[0x46] = ("lsr", "zeropage") 252 | self.hexcodes[0x56] = ("lsr", "zeropagex") 253 | self.hexcodes[0x66] = ("ror", "zeropage") 254 | self.hexcodes[0x76] = ("ror", "zeropagex") 255 | self.hexcodes[0x86] = ("stx", "zeropage") 256 | self.hexcodes[0x96] = ("stx", "zeropagey") 257 | self.hexcodes[0xA6] = ("ldx", "zeropage") 258 | self.hexcodes[0xB6] = ("ldx", "zeropagey") 259 | self.hexcodes[0xC6] = ("dec", "zeropage") 260 | self.hexcodes[0xD6] = ("dec", "zeropagex") 261 | self.hexcodes[0xE6] = ("inc", "zeropage") 262 | self.hexcodes[0xF6] = ("inc", "zeropagex") 263 | 264 | self.hexcodes[0x07] = ("", "") 265 | self.hexcodes[0x17] = ("", "") 266 | self.hexcodes[0x27] = ("", "") 267 | self.hexcodes[0x37] = ("", "") 268 | self.hexcodes[0x47] = ("", "") 269 | self.hexcodes[0x57] = ("", "") 270 | self.hexcodes[0x67] = ("", "") 271 | self.hexcodes[0x77] = ("", "") 272 | self.hexcodes[0x87] = ("", "") 273 | self.hexcodes[0x97] = ("", "") 274 | self.hexcodes[0xA7] = ("", "") 275 | self.hexcodes[0xB7] = ("", "") 276 | self.hexcodes[0xC7] = ("", "") 277 | self.hexcodes[0xD7] = ("", "") 278 | self.hexcodes[0xE7] = ("", "") 279 | self.hexcodes[0xF7] = ("", "") 280 | 281 | self.hexcodes[0x08] = ("php", "implicit") 282 | self.hexcodes[0x18] = ("clc", "implicit") 283 | self.hexcodes[0x28] = ("plp", "implicit") 284 | self.hexcodes[0x38] = ("sec", "implicit") 285 | self.hexcodes[0x48] = ("pha", "implicit") 286 | self.hexcodes[0x58] = ("cli", "implicit") 287 | self.hexcodes[0x68] = ("pla", "implicit") 288 | self.hexcodes[0x78] = ("sei", "implicit") 289 | self.hexcodes[0x88] = ("dey", "implicit") 290 | self.hexcodes[0x98] = ("tya", "implicit") 291 | self.hexcodes[0xA8] = ("tay", "implicit") 292 | self.hexcodes[0xB8] = ("clv", "implicit") 293 | self.hexcodes[0xC8] = ("iny", "implicit") 294 | self.hexcodes[0xD8] = ("cld", "implicit") 295 | self.hexcodes[0xE8] = ("inx", "implicit") 296 | self.hexcodes[0xF8] = ("sed", "implicit") 297 | 298 | self.hexcodes[0x09] = ("ora", "immediate") 299 | self.hexcodes[0x19] = ("ora", "absolutey") 300 | self.hexcodes[0x29] = ("and", "immediate") 301 | self.hexcodes[0x39] = ("and", "absolutey") 302 | self.hexcodes[0x49] = ("eor", "immediate") 303 | self.hexcodes[0x59] = ("eor", "absolutey") 304 | self.hexcodes[0x69] = ("adc", "immediate") 305 | self.hexcodes[0x79] = ("adc", "absolutey") 306 | self.hexcodes[0x89] = ("bit", "immediate") 307 | self.hexcodes[0x99] = ("sta", "absolutey") 308 | self.hexcodes[0xA9] = ("lda", "immediate") 309 | self.hexcodes[0xB9] = ("lda", "absolutey") 310 | self.hexcodes[0xC9] = ("cmp", "immediate") 311 | self.hexcodes[0xD9] = ("cmp", "absolutey") 312 | self.hexcodes[0xE9] = ("sbc", "immediate") 313 | self.hexcodes[0xF9] = ("sbc", "absolutey") 314 | 315 | self.hexcodes[0x0A] = ("asl", "accumulator") 316 | self.hexcodes[0x1A] = ("ina", "accumulator") 317 | self.hexcodes[0x2A] = ("rol", "accumulator") 318 | self.hexcodes[0x3A] = ("dea", "accumulator") 319 | self.hexcodes[0x4A] = ("lsr", "accumulator") 320 | self.hexcodes[0x5A] = ("phy", "implicit") 321 | self.hexcodes[0x6A] = ("ror", "accumulator") 322 | self.hexcodes[0x7A] = ("ply", "implicit") 323 | self.hexcodes[0x8A] = ("txa", "implicit") 324 | self.hexcodes[0x9A] = ("txs", "implicit") 325 | self.hexcodes[0xAA] = ("tax", "implicit") 326 | self.hexcodes[0xBA] = ("tsx", "implicit") 327 | self.hexcodes[0xCA] = ("dex", "implicit") 328 | self.hexcodes[0xDA] = ("phx", "implicit") 329 | self.hexcodes[0xEA] = ("nop", "implicit") 330 | self.hexcodes[0xFA] = ("plx", "implicit") 331 | 332 | self.hexcodes[0x0B] = ("", "") 333 | self.hexcodes[0x1B] = ("", "") 334 | self.hexcodes[0x2B] = ("", "") 335 | self.hexcodes[0x3B] = ("", "") 336 | self.hexcodes[0x4B] = ("", "") 337 | self.hexcodes[0x5B] = ("", "") 338 | self.hexcodes[0x6B] = ("", "") 339 | self.hexcodes[0x7B] = ("", "") 340 | self.hexcodes[0x8B] = ("", "") 341 | self.hexcodes[0x9B] = ("", "") 342 | self.hexcodes[0xAB] = ("", "") 343 | self.hexcodes[0xBB] = ("", "") 344 | self.hexcodes[0xCB] = ("", "") 345 | self.hexcodes[0xDB] = ("", "") 346 | self.hexcodes[0xEB] = ("", "") 347 | self.hexcodes[0xFB] = ("", "") 348 | 349 | self.hexcodes[0x0C] = ("tsb", "absolute") 350 | self.hexcodes[0x1C] = ("trb", "absolute") 351 | self.hexcodes[0x2C] = ("bit", "absolute") 352 | self.hexcodes[0x3C] = ("bit", "absolutex") 353 | self.hexcodes[0x4C] = ("jmp", "absolute") 354 | self.hexcodes[0x5C] = ("", "") 355 | self.hexcodes[0x6C] = ("jmp", "absoluteindirect") 356 | self.hexcodes[0x7C] = ("jmp", "absoluteindexedindirect") 357 | self.hexcodes[0x8C] = ("sty", "absolute") 358 | self.hexcodes[0x9C] = ("stz", "absolute") 359 | self.hexcodes[0xAC] = ("ldy", "absolute") 360 | self.hexcodes[0xBC] = ("ldy", "absolutex") 361 | self.hexcodes[0xCC] = ("cpy", "absolute") 362 | self.hexcodes[0xDC] = ("", "") 363 | self.hexcodes[0xEC] = ("cpx", "absolute") 364 | self.hexcodes[0xFC] = ("", "") 365 | 366 | self.hexcodes[0x0D] = ("ora", "absolute") 367 | self.hexcodes[0x1D] = ("ora", "absolutex") 368 | self.hexcodes[0x2D] = ("and", "absolute") 369 | self.hexcodes[0x3D] = ("and", "absolutex") 370 | self.hexcodes[0x4D] = ("eor", "absolute") 371 | self.hexcodes[0x5D] = ("eor", "absolutex") 372 | self.hexcodes[0x6D] = ("adc", "absolute") 373 | self.hexcodes[0x7D] = ("adc", "absolutex") 374 | self.hexcodes[0x8D] = ("sta", "absolute") 375 | self.hexcodes[0x9D] = ("sta", "absolutex") 376 | self.hexcodes[0xAD] = ("lda", "absolute") 377 | self.hexcodes[0xBD] = ("lda", "absolutex") 378 | self.hexcodes[0xCD] = ("cmp", "absolute") 379 | self.hexcodes[0xDD] = ("cmp", "absolutex") 380 | self.hexcodes[0xED] = ("sbc", "absolute") 381 | self.hexcodes[0xFD] = ("sbc", "absolutex") 382 | 383 | self.hexcodes[0x0E] = ("asl", "absolute") 384 | self.hexcodes[0x1E] = ("asl", "absolutex") 385 | self.hexcodes[0x2E] = ("rol", "absolute") 386 | self.hexcodes[0x3E] = ("rol", "absolutex") 387 | self.hexcodes[0x4E] = ("lsr", "absolute") 388 | self.hexcodes[0x5E] = ("lsr", "absolutex") 389 | self.hexcodes[0x6E] = ("ror", "absolute") 390 | self.hexcodes[0x7E] = ("ror", "absolutex") 391 | self.hexcodes[0x8E] = ("stx", "absolute") 392 | self.hexcodes[0x9E] = ("stz", "absolutex") 393 | self.hexcodes[0xAE] = ("ldx", "absolute") 394 | self.hexcodes[0xBE] = ("ldx", "absolutey") 395 | self.hexcodes[0xCE] = ("dec", "absolute") 396 | self.hexcodes[0xDE] = ("dec", "absolutex") 397 | self.hexcodes[0xEE] = ("inc", "absolute") 398 | self.hexcodes[0xFE] = ("inc", "absolutex") 399 | 400 | self.hexcodes[0x0F] = ("", "") 401 | self.hexcodes[0x1F] = ("", "") 402 | self.hexcodes[0x2F] = ("", "") 403 | self.hexcodes[0x3F] = ("", "") 404 | self.hexcodes[0x4F] = ("", "") 405 | self.hexcodes[0x5F] = ("", "") 406 | self.hexcodes[0x6F] = ("", "") 407 | self.hexcodes[0x7F] = ("", "") 408 | self.hexcodes[0x8F] = ("", "") 409 | self.hexcodes[0x9F] = ("", "") 410 | self.hexcodes[0xAF] = ("", "") 411 | self.hexcodes[0xBF] = ("", "") 412 | self.hexcodes[0xCF] = ("", "") 413 | self.hexcodes[0xDF] = ("", "") 414 | self.hexcodes[0xEF] = ("", "") 415 | self.hexcodes[0xFF] = ("", "") 416 | 417 | # Make another list for synonyms 418 | self.otherhexcodes = dict() 419 | for hexval in range(256): 420 | self.otherhexcodes[hexval] = ("", "") 421 | self.otherhexcodes[0x1A] = ("inc", "accumulator") 422 | self.otherhexcodes[0x3A] = ("dec", "accumulator") 423 | self.otherhexcodes[0x90] = ("blt", "relative") 424 | self.otherhexcodes[0xB0] = ("bge", "relative") 425 | 426 | # Make a dictionary to map opcode+address mode to the opcode value. 427 | self.hexmap = dict() 428 | for hexval in range(256): 429 | op, mode = self.hexcodes[hexval] 430 | astring = op + mode 431 | if len(astring) > 1: 432 | self.hexmap[astring] = hexval 433 | 434 | op, mode = self.otherhexcodes[hexval] 435 | astring = op + mode 436 | if len(astring) > 1: 437 | self.hexmap[astring] = hexval 438 | 439 | # implicit ~ "implicit" 440 | # immediate #num ~ "immediate" 441 | # accumulator A ~ "accumulator" 442 | # absolute $2000 ~ "absolute" 443 | # zero page $20 ~ "zeropage" 444 | # absolute indexed x $5000,X ~ "absolutex" 445 | # absolute indexed y $5000,y ~ "absolutey" 446 | # zeropage indexed x $20,X ~ "zeropagex" 447 | # zeropage indexed y $20,Y ~ "zeropagey" 448 | # relative +10 (or label) ~ "relative" 449 | # zeropage indexed indirect x ($20,X) ~ "zeropageindexedindirectx" 450 | # zeropage indexed indirect y ($20),Y ~ "zeropageindexedindirecty" 451 | # absolute indexed indirect ($5000,X) - only JMP ~ "absoluteindexedindirect" 452 | # zeropage indirect ($20) ~ "zeropageindirect" 453 | # absolute indirect ($5000) - only JMP ~ "absoluteindirect" 454 | def addrmode_length(self, addrmode): 455 | return { 456 | 'implicit': 0, 457 | 'immediate': 1, 458 | 'accumulator': 0, 459 | 'absolute': 2, 460 | 'zeropage': 1, 461 | 'absolutex': 2, 462 | 'absolutey': 2, 463 | 'zeropagex': 1, 464 | 'zeropagey': 1, 465 | 'relative': 1, 466 | 'zeropageindexedindirectx': 1, 467 | 'zeropageindexedindirecty': 1, 468 | 'absoluteindexedindirect': 2, 469 | 'zeropageindirect': 1, 470 | 'absoluteindirect': 2 471 | }[addrmode] 472 | 473 | def firstpasstext(self, thetuple): 474 | (offset, linenumber, labelstring, opcode_val, lowbyte, highbyte, opcode, operand, addressmode, value, comment, 475 | extrabytes) = thetuple 476 | a = ("%d" % linenumber).ljust(4) 477 | if (labelstring != None): 478 | b = (": %s" % labelstring).ljust(10) 479 | else: 480 | b = " " 481 | 482 | if (opcode_val == None): 483 | c = " " 484 | else: 485 | if (opcode_val > -1): 486 | c = "%02X " % opcode_val 487 | else: 488 | c = "?? " 489 | 490 | if (lowbyte == None): 491 | d = " " 492 | else: 493 | if (lowbyte > -1): 494 | d = "%02X " % lowbyte 495 | else: 496 | d = "?? " 497 | 498 | if (highbyte == None): 499 | e = " " 500 | else: 501 | if (highbyte > -1): 502 | e = "%02X " % highbyte 503 | else: 504 | e = "?? " 505 | 506 | # Print the opcode in 4 spaces 507 | if (opcode == None): 508 | f = " " 509 | else: 510 | f = opcode.ljust(4) 511 | 512 | # Either print the operand in 10 spaces or print 10 spaces 513 | # when there is no operand 514 | if (operand == None): 515 | g = " " 516 | else: 517 | if (len(operand) > 0): 518 | g = operand.ljust(10) 519 | else: 520 | g = " " 521 | 522 | h = comment 523 | astring = a + b + c + d + e + f + g + h 524 | self.debug(1, astring) 525 | return astring 526 | 527 | def secondpasstext(self, thetuple): 528 | (offset, linenumber, labelstring, opcode_val, lowbyte, highbyte, opcode, operand, addressmode, value, comment, 529 | extrabytes) = thetuple 530 | a = ("%d " % linenumber).ljust(5) 531 | aa = ("%04X " % offset) 532 | 533 | if (labelstring != None) and (labelstring != ""): 534 | b = (": %s:" % labelstring).ljust(10) 535 | else: 536 | b = ": " 537 | 538 | if (opcode_val == None): 539 | c = " " 540 | else: 541 | if (opcode_val > -1): 542 | c = "%02X " % opcode_val 543 | else: 544 | c = "?? " 545 | 546 | if (lowbyte == None): 547 | d = " " 548 | else: 549 | if (lowbyte > -1): 550 | d = "%02X " % lowbyte 551 | else: 552 | d = "?? " 553 | 554 | if (highbyte == None): 555 | e = " " 556 | else: 557 | if (highbyte > -1): 558 | e = "%02X " % highbyte 559 | else: 560 | e = "?? " 561 | 562 | # Print the opcode in 4 spaces 563 | if (opcode == None): 564 | f = " " 565 | else: 566 | f = opcode.ljust(4) 567 | 568 | if (operand == None): 569 | g = " " 570 | else: 571 | if (len(operand) > 0): 572 | g = operand.ljust(10) 573 | else: 574 | g = " " 575 | 576 | h = comment 577 | 578 | astring = a + aa + b + c + d + e + f + g + h 579 | self.debug(1, astring) 580 | self.debug(2, thetuple) 581 | 582 | # If there are extra bytes from a db, dw, dq, do or text operator, 583 | # print the resulting hex bytes on the next line. 584 | if (extrabytes != None) and (len(extrabytes) > 1): 585 | hexchars = "" 586 | index = 0 587 | for index in range(0, len(extrabytes) - 1): 588 | hexchars = hexchars + "%02X " % extrabytes[index] 589 | 590 | hexchars = hexchars + "%02X" % extrabytes[len(extrabytes) - 1] 591 | bytestring = a + aa + ": " + hexchars 592 | self.debug(1, bytestring) 593 | return astring + "\n" + bytestring 594 | return astring 595 | 596 | # Separate out the label, opcode, operand and comment fields. 597 | # Identify the address mode as we go along 598 | # The results end up in self.allstuff in a tuple per entry 599 | # -1 in fields indicates a value not known yet 600 | # None in a field indicates that it doesn't exist 601 | def parse_line(self, thestring): 602 | linenumber = self.line 603 | self.line += 1 604 | thetext = "LINE #" + ("%d" % linenumber).ljust(5) + (": %s" % thestring) 605 | self.debug(2, thetext) 606 | mystring, comment = self.strip_comments(thestring) 607 | labelstring, mystring = self.strip_label(mystring, linenumber) 608 | opcode_anycase, operand = self.strip_opcode(mystring, linenumber) 609 | opcode = self.check_opcode(opcode_anycase, linenumber) 610 | premode, value = self.identify_addressmodeformat(operand, linenumber) 611 | addressmode = self.identify_addressmode(opcode, premode, value, linenumber) 612 | self.debug(3, "PARSE LINE: opcode=%s addressmode=%s" % (str(opcode), addressmode)) 613 | if (opcode != None) and (addressmode != "UNDECIDED"): 614 | astring = opcode + addressmode 615 | self.debug(3, "PARSE LINE 2 astring=%s" % astring) 616 | if astring in self.hexmap: 617 | self.debug(3, "PARSE LINE 3 astring=%s self.hexmap[astring]=0x%x" % (astring, self.hexmap[astring])) 618 | opcode_val = self.hexmap[astring] 619 | else: 620 | opcode_val = None 621 | else: 622 | opcode_val = None 623 | astring = "" 624 | 625 | if (self.addrmode_length(addressmode) == 0): 626 | lowbyte = None 627 | highbyte = None 628 | elif (self.addrmode_length(addressmode) == 1) and (self.decode_value(value) != -1): 629 | lowbyte = self.decode_value(value) & 0x00FF 630 | highbyte = None 631 | elif (self.addrmode_length(addressmode) == 2) and (self.decode_value(value) != -1): 632 | lowbyte = self.decode_value(value) & 0x00FF 633 | highbyte = ((self.decode_value(value) & 0xFF00) >> 8) & 0x00FF 634 | elif (self.addrmode_length(addressmode) == 1) and (self.decode_value(value) == -1): 635 | lowbyte = -1 636 | highbyte = None 637 | elif (self.addrmode_length(addressmode) == 2) and (self.decode_value(value) == -1): 638 | lowbyte = -1 639 | highbyte = -1 640 | else: 641 | lowbyte = None 642 | highbyte = None 643 | offset = -1 644 | 645 | # Handle switches between little endian and big endian 646 | if (opcode == "le"): 647 | self.littleendian = True 648 | if (opcode == "be"): 649 | self.littleendian = False 650 | 651 | # interpret extra bytes from the db, dw, ddw, dqw directives. 652 | extrabytes = list() 653 | if (opcode == "db") and (operand != None) and (len(operand) > 0): 654 | extrabytes = self.decode_extrabytes(linenumber, thestring, operand) 655 | elif (opcode == "dw") and (operand != None) and (len(operand) > 0): 656 | extrabytes = self.decode_extrawords(linenumber, thestring, operand) 657 | elif (opcode == "ddw") and (operand != None) and (len(operand) > 0): 658 | extrabytes = self.decode_extradoublewords(linenumber, thestring, operand) 659 | elif (opcode == "dqw") and (operand != None) and (len(operand) > 0): 660 | extrabytes = self.decode_extraquadwords(linenumber, thestring, operand) 661 | 662 | thetuple = ( 663 | offset, linenumber, labelstring, opcode_val, lowbyte, highbyte, opcode, operand, addressmode, value, 664 | comment, 665 | extrabytes) 666 | self.allstuff.append(thetuple) 667 | self.firstpasstext(thetuple) 668 | 669 | self.debug(2, "addressmode = %s" % addressmode) 670 | self.debug(2, str(self.allstuff[linenumber - 1])) 671 | self.debug(2, "-----------------------") 672 | 673 | # Perform the three passes of the assembly 674 | def assemble(self, lines): 675 | self.clear_state() 676 | 677 | # First pass, parse each line for label, opcode, operand and comments 678 | self.debug(1, "First Pass") 679 | for line in lines: 680 | self.parse_line(line) 681 | 682 | # Second pass, compute the offsets and populate the symbol table 683 | self.debug(1, "Second Pass") 684 | self.symbols = dict() 685 | 686 | # Default to 0x0000. ORG directive overrides 687 | self.address = 0x0000 688 | 689 | # Add the offset to each line by counting the opcodes and operands 690 | for i in range(len(self.allstuff)): 691 | tuple = self.allstuff[i] 692 | (offset, linenumber, labelstring, opcode_val, lowbyte, highbyte, opcode, operand, addressmode, value, 693 | comment, extrabytes) = tuple 694 | # Handle ORG directive 695 | if (opcode == "org"): 696 | newaddr = self.decode_value(value) 697 | if (newaddr != -1): 698 | self.address = newaddr & 0x00ffff 699 | offset = self.address 700 | 701 | if (opcode_val != None): 702 | self.address += 1 703 | if (lowbyte != None): 704 | self.address += 1 705 | if (highbyte != None): 706 | self.address += 1 707 | self.address += len(extrabytes) 708 | 709 | # If there is a label, we now know its address. So store it in the symbol table 710 | if (labelstring != None) and (labelstring != ""): 711 | self.symbols[labelstring] = offset 712 | tuple = ( 713 | offset, linenumber, labelstring, opcode_val, lowbyte, highbyte, opcode, operand, addressmode, value, 714 | comment, extrabytes) 715 | self.allstuff[i] = tuple 716 | self.secondpasstext(tuple) 717 | 718 | # Print out the symbol table 719 | self.debug(1, "Symbol Table") 720 | for label in self.symbols: 721 | offset = self.symbols[label] 722 | astring = (("%s" % label).ljust(10)) + (" = " + "$%04X" % offset) 723 | self.debug(1, astring) 724 | 725 | # Third pass 726 | # Go through filling in the unknown values from the symbol table 727 | self.debug(1, "Third Pass") 728 | self.listing = list() 729 | for i in range(len(self.allstuff)): 730 | tuple = self.allstuff[i] 731 | (offset, linenumber, labelstring, opcode_val, lowbyte, highbyte, opcode, operand, addressmode, value, 732 | comment, extrabytes) = tuple 733 | 734 | if (lowbyte == -1) and (addressmode == "relative"): 735 | destination = self.symbols[value] 736 | start = offset 737 | delta = destination - start 738 | lowbyte = delta & 0x00ff 739 | if (delta > 127) or (delta < -128): 740 | self.warning(linenumber, "", "branch can't reach destination, delta is %d" % delta) 741 | elif (lowbyte == -1) and ( 742 | (addressmode in self.modeswithlowbytevalue) or (addressmode in self.modeswithhighbytevalue)): 743 | if (value in self.symbols): 744 | newvalue = self.symbols[value] 745 | lowbyte = newvalue & 0x00ff 746 | if (highbyte == -1) and (addressmode in self.modeswithhighbytevalue): 747 | if (value in self.symbols): 748 | newvalue = self.symbols[value] 749 | highbyte = ((newvalue & 0xff00) >> 8) & 0x00ff 750 | 751 | tuple = ( 752 | offset, linenumber, labelstring, opcode_val, lowbyte, highbyte, opcode, operand, addressmode, value, 753 | comment, extrabytes) 754 | self.allstuff[i] = tuple 755 | line = self.secondpasstext(tuple) 756 | self.listing.append(line) 757 | 758 | # write generated bytes to object code map 759 | addr = offset 760 | if (opcode_val != None) and (opcode_val != -1): 761 | self.object_code[addr] = opcode_val 762 | addr = addr + 1 763 | if (lowbyte != None): 764 | self.object_code[addr] = lowbyte 765 | addr = addr + 1 766 | if (highbyte != None): 767 | self.object_code[addr] = highbyte 768 | addr = addr + 1 769 | if (extrabytes != None): 770 | for i in extrabytes: 771 | self.object_code[addr] = i 772 | addr = addr + 1 773 | 774 | print("LISTING") 775 | for i in self.listing: 776 | print(i) 777 | 778 | print() 779 | print("SYMBOL TABLE") 780 | for label in self.symbols: 781 | offset = self.symbols[label] 782 | astring = (("%s" % label).ljust(10)) + (" = " + "$%04X" % offset) 783 | print(astring) 784 | 785 | print() 786 | self.print_object_code() 787 | 788 | def print_object_code(self): 789 | print("OBJECT CODE") 790 | 791 | # Insert a star when there are empty spots in the memory map 792 | i = 0 793 | astring = "" 794 | printed_a_star = 0 795 | while (i < 65536): 796 | if self.object_code[i] != -1: 797 | printed_a_star = 0 798 | astring = "%04X: %02X" % (i, self.object_code[i]) 799 | localrun = 1 800 | i = i + 1 801 | if (i < 65536): 802 | nextval = self.object_code[i] 803 | while (nextval != -1) and (localrun < 16): 804 | astring = astring + " %02X" % self.object_code[i] 805 | i = i + 1 806 | localrun = localrun + 1 807 | if (i < 65536): 808 | nextval = self.object_code[i] 809 | else: 810 | nextval = -1 811 | print(astring) 812 | else: 813 | print(astring) 814 | else: 815 | if (printed_a_star == 0): 816 | print("*") 817 | printed_a_star = 1 818 | i = i + 1 819 | 820 | 821 | def go(debug=0): 822 | lines = list() 823 | lines.append(" ADC #$55 ") 824 | lines.append(" ADC $20 ") 825 | lines.append(" ADC $20,X ") 826 | lines.append(" ADC $2233 ") 827 | lines.append(" ADC $2233,X ") 828 | lines.append(" ADC $2233,Y ") 829 | lines.append(" ADC ($20,X) ") 830 | lines.append(" ADC ($20),Y ") 831 | lines.append(" ADC ($20) ") 832 | lines.append(" AND #$55 ") 833 | lines.append(" AND $20 ") 834 | lines.append(" AND $20,X ") 835 | lines.append(" AND $2233 ") 836 | lines.append(" AND $2233,X ") 837 | lines.append(" AND $2233,Y ") 838 | lines.append(" AND ($20,X) ") 839 | lines.append(" AND ($20),Y ") 840 | lines.append(" AND ($20) ") 841 | lines.append(" ASL A ") 842 | lines.append(" ASL $20 ") 843 | lines.append(" ASL $20,X ") 844 | lines.append(" ASL $2233 ") 845 | lines.append(" ASL $2233,X ") 846 | lines.append(" BCC $55 ") 847 | lines.append(" BCS $55 ") 848 | lines.append(" BEQ $55 ") 849 | lines.append(" BIT #$55 ") 850 | lines.append(" BIT $20 ") 851 | lines.append(" BIT $20,X ") 852 | lines.append(" BIT $2233 ") 853 | lines.append(" BIT $2233,X ") 854 | lines.append(" BMI $55 ") 855 | lines.append(" BNE $55 ") 856 | lines.append(" BPL $55 ") 857 | lines.append(" BRA $55 ") 858 | lines.append(" BRK ") 859 | lines.append(" BVC $55 ") 860 | lines.append(" BVS $55 ") 861 | lines.append(" CLC ") 862 | lines.append(" CLD ") 863 | lines.append(" CLI ") 864 | lines.append(" CLV ") 865 | lines.append(" CMP #$55 ") 866 | lines.append(" CMP $20 ") 867 | lines.append(" CMP $20 ") 868 | lines.append(" CMP $2233 ") 869 | lines.append(" CMP $2233,X ") 870 | lines.append(" CMP $2233,Y ") 871 | lines.append(" CMP ($20,X) ") 872 | lines.append(" CMP ($20),Y ") 873 | lines.append(" CMP ($20) ") 874 | lines.append(" CPX #$55 ") 875 | lines.append(" CPX $20 ") 876 | lines.append(" CPX $2233 ") 877 | lines.append(" CPY #$55 ") 878 | lines.append(" CPY $20 ") 879 | lines.append(" CPY $2233 ") 880 | lines.append(" DEA ") 881 | lines.append(" DEC A ") 882 | lines.append(" DEC $20 ") 883 | lines.append(" DEC $20,X ") 884 | lines.append(" DEC $2233 ") 885 | lines.append(" DEC $2233,X ") 886 | lines.append(" DEX ") 887 | lines.append(" DEY ") 888 | lines.append(" EOR #$55 ") 889 | lines.append(" EOR $20 ") 890 | lines.append(" EOR $20,X ") 891 | lines.append(" EOR $2233 ") 892 | lines.append(" EOR $2233,X ") 893 | lines.append(" EOR $2233,Y ") 894 | lines.append(" EOR ($20,X) ") 895 | lines.append(" EOR ($20),Y ") 896 | lines.append(" EOR ($20) ") 897 | lines.append(" INA") 898 | lines.append(" INC A ") 899 | lines.append(" INC $20 ") 900 | lines.append(" INC $20,X ") 901 | lines.append(" INC $2233 ") 902 | lines.append(" INC $2233,X ") 903 | lines.append(" INX ") 904 | lines.append(" INY ") 905 | lines.append(" JMP $2233 ") 906 | lines.append(" JMP ($2233) ") 907 | lines.append(" JMP ($2233,X) ") 908 | lines.append(" JSR $2233 ") 909 | lines.append(" LDA #$55 ") 910 | lines.append(" LDA $20 ") 911 | lines.append(" LDA $20,X ") 912 | lines.append(" LDA $2233 ") 913 | lines.append(" LDA $2233,X ") 914 | lines.append(" LDA $2233,Y ") 915 | lines.append(" LDA ($20,X) ") 916 | lines.append(" LDA ($20),Y ") 917 | lines.append(" LDA ($20) ") 918 | lines.append(" LDX #$55 ") 919 | lines.append(" LDX $20 ") 920 | lines.append(" LDX $20,Y ") 921 | lines.append(" LDX $2233 ") 922 | lines.append(" LDX $2233,Y ") 923 | lines.append(" LDY #$55 ") 924 | lines.append(" LDY $20 ") 925 | lines.append(" LDY $20,X ") 926 | lines.append(" LDY $2233 ") 927 | lines.append(" LDY $2233,X ") 928 | lines.append(" LSR A ") 929 | lines.append(" LSR $20 ") 930 | lines.append(" LSR $20,X ") 931 | lines.append(" LSR $2233 ") 932 | lines.append(" LSR $2233,X ") 933 | lines.append(" NOP ") 934 | lines.append(" ORA #$55 ") 935 | lines.append(" ORA $20 ") 936 | lines.append(" ORA $20,X ") 937 | lines.append(" ORA $2233 ") 938 | lines.append(" ORA $2233,X ") 939 | lines.append(" ORA $2233,Y ") 940 | lines.append(" ORA ($20,X) ") 941 | lines.append(" ORA ($20),Y ") 942 | lines.append(" ORA ($20) ") 943 | lines.append(" PHA ") 944 | lines.append(" PHX ") 945 | lines.append(" PHY ") 946 | lines.append(" PLA ") 947 | lines.append(" PLX ") 948 | lines.append(" PLY ") 949 | lines.append(" ROL A ") 950 | lines.append(" ROL $20 ") 951 | lines.append(" ROL $20,X ") 952 | lines.append(" ROL $2233 ") 953 | lines.append(" ROL $2233,X ") 954 | lines.append(" ROR A ") 955 | lines.append(" ROR $20 ") 956 | lines.append(" ROR $20,X ") 957 | lines.append(" ROR $2233 ") 958 | lines.append(" ROR $2233,X ") 959 | lines.append(" RTI ") 960 | lines.append(" RTS ") 961 | lines.append(" SBC #$55 ") 962 | lines.append(" SBC $20 ") 963 | lines.append(" SBC $20,X ") 964 | lines.append(" SBC $2233 ") 965 | lines.append(" SBC $2233,X ") 966 | lines.append(" SBC $2233,Y ") 967 | lines.append(" SBC ($20,X) ") 968 | lines.append(" SBC ($20),Y ") 969 | lines.append(" SBC ($20) ") 970 | lines.append(" SEC ") 971 | lines.append(" SED ") 972 | lines.append(" SEI ") 973 | lines.append(" STA $20 ") 974 | lines.append(" STA $20,X ") 975 | lines.append(" STA $2233 ") 976 | lines.append(" STA $2233,X ") 977 | lines.append(" STA $2233,Y ") 978 | lines.append(" STA ($20,X) ") 979 | lines.append(" STA ($20),Y ") 980 | lines.append(" STA ($20) ") 981 | lines.append(" STX $20 ") 982 | lines.append(" STX $20,Y ") 983 | lines.append(" STX $2233 ") 984 | lines.append(" STY $20 ") 985 | lines.append(" STY $20,X ") 986 | lines.append(" STY $2233 ") 987 | lines.append(" STZ $20 ") 988 | lines.append(" STZ $20,X ") 989 | lines.append(" STZ $2233 ") 990 | lines.append(" STZ $2233,X ") 991 | lines.append(" TAX ") 992 | lines.append(" TAY ") 993 | lines.append(" TRB $20 ") 994 | lines.append(" TRB $2233 ") 995 | lines.append(" TSB $20 ") 996 | lines.append(" TSB $2233 ") 997 | lines.append(" TSX ") 998 | lines.append(" TXA ") 999 | lines.append(" TXS ") 1000 | lines.append(" TYA") 1001 | lines.append("; A remark") 1002 | lines.append(" org $1000") 1003 | lines.append("start: lda #$50") 1004 | lines.append(" sta $5000 ; blah") 1005 | lines.append(" sta $25") 1006 | lines.append(" clc") 1007 | lines.append(" ROR A") 1008 | lines.append(" adc #%10011010") 1009 | lines.append(" sta %0101101000111100") 1010 | lines.append(" sta %00111100") 1011 | lines.append(" lda ($20)") 1012 | lines.append(" adc $10,x") 1013 | lines.append("middle:ldx $20,y") 1014 | lines.append(" adc $3000,x") 1015 | lines.append(" adc $3000,y") 1016 | lines.append(" adc ($40,x) ") 1017 | lines.append(" adc ($40),y") 1018 | lines.append(" nop") 1019 | lines.append(" nop") 1020 | lines.append("label:") 1021 | lines.append(" nop") 1022 | lines.append(" org $3000") 1023 | lines.append("vals: db @10,$aa,8,$cc,$dd") 1024 | lines.append(" be") 1025 | lines.append(" dw $1020,$3040") 1026 | lines.append(" le") 1027 | lines.append(" dw $1020,$3040") 1028 | lines.append(" ddw $1020,$3040") 1029 | lines.append(" dqw $1020,$3040") 1030 | lines.append(" adc start") 1031 | lines.append(" adc ($40)") 1032 | lines.append("end: bpl vals") 1033 | lines.append(" db $aa,$bb,$cc,$dd") 1034 | lines.append(" nop") 1035 | 1036 | a = asm6502(debug=debug) 1037 | a.assemble(lines) 1038 | -------------------------------------------------------------------------------- /src/scrolltest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- encoding: utf-8 -*- 3 | import sys 4 | import time 5 | import termbox 6 | 7 | eventdict = dict() 8 | with termbox.Termbox() as tb: 9 | finish = False 10 | while finish==False: 11 | event = tb.poll_event() 12 | (type, ch, key, mod, w, h, x, y ) = event 13 | if type==termbox.EVENT_KEY and ch=='X': 14 | break 15 | 16 | if event in eventdict: 17 | eventdict[event] += 1 18 | else: 19 | eventdict[event] = 1 20 | time.sleep(0.01) # Remove this delay to make it not fail. 21 | 22 | for e in eventdict: 23 | print(e,":",eventdict[e]) 24 | 25 | -------------------------------------------------------------------------------- /src/scrolltest.txt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- encoding: utf-8 -*- 3 | import sys 4 | import time 5 | import termbox 6 | 7 | eventdict = dict() 8 | with termbox.Termbox() as tb: 9 | finish = False 10 | while finish==False: 11 | event = tb.poll_event() 12 | (type, ch, key, mod, w, h, x, y ) = event 13 | if type==termbox.EVENT_KEY and ch=='X': 14 | break 15 | 16 | if event in eventdict: 17 | eventdict[event] += 1 18 | else: 19 | eventdict[event] = 1 20 | time.sleep(0.01) # Remove this delay to make it not fail. 21 | 22 | for e in eventdict: 23 | print(e,":",eventdict[e]) 24 | 25 | -------------------------------------------------------------------------------- /src/sim6502.py: -------------------------------------------------------------------------------- 1 | import memory_map 2 | 3 | class Flags(object): 4 | # processor flags 5 | NEGATIVE = 128 6 | OVERFLOW = 64 7 | UNUSED = 32 8 | BREAK = 16 9 | DECIMAL = 8 10 | INTERRUPT = 4 11 | ZERO = 2 12 | CARRY = 1 13 | 14 | # TODO: check for other cases of % on negative numbers leading to negative underflow 15 | 16 | # 17 | # The 65C02 Simulator 18 | # 19 | class sim6502(object): 20 | def __init__(self, object_code=None, address=0x0, symbols=None): 21 | self.pc = 0x0000 22 | self.a = 0x00 23 | self.x = 0x00 24 | self.y = 0x00 25 | self.sp = 0xff 26 | self.cc = 0x00 27 | 28 | self.memory_map = memory_map.MemoryMap(self) 29 | if object_code: 30 | self.memory_map.InitializeMemory(address, object_code) 31 | 32 | self.build_opcode_table() 33 | 34 | if symbols == None: 35 | self.have_symbols = False 36 | else: 37 | self.have_symbols = True 38 | 39 | self.symbols = symbols 40 | self.labels = dict() 41 | for label in self.symbols: 42 | offset = self.symbols[label] 43 | self.labels[offset] = label 44 | 45 | # TODO: factor out to common code 46 | def build_opcode_table(self): 47 | self.hexcodes = dict() 48 | self.hexcodes[0x00] = ("brk", "implicit") 49 | self.hexcodes[0x10] = ("bpl", "relative") 50 | self.hexcodes[0x20] = ("jsr", "absolute") 51 | self.hexcodes[0x30] = ("bmi", "relative") 52 | self.hexcodes[0x40] = ("rti", "implicit") 53 | self.hexcodes[0x50] = ("bvc", "relative") 54 | self.hexcodes[0x60] = ("rts", "implicit") 55 | self.hexcodes[0x70] = ("bvs", "relative") 56 | self.hexcodes[0x80] = ("bra", "relative") 57 | self.hexcodes[0x90] = ("bcc", "relative") 58 | self.hexcodes[0xA0] = ("ldy", "immediate") 59 | self.hexcodes[0xB0] = ("bcs", "relative") 60 | self.hexcodes[0xC0] = ("cpy", "immediate") 61 | self.hexcodes[0xD0] = ("bne", "relative") 62 | self.hexcodes[0xE0] = ("cpx", "immediate") 63 | self.hexcodes[0xF0] = ("beq", "relative") 64 | 65 | self.hexcodes[0x01] = ("ora", "zeropageindexedindirectx") 66 | self.hexcodes[0x11] = ("ora", "zeropageindexedindirecty") 67 | self.hexcodes[0x21] = ("and", "zeropageindexedindirectx") 68 | self.hexcodes[0x31] = ("and", "zeropageindexedindirecty") 69 | self.hexcodes[0x41] = ("eor", "zeropageindexedindirectx") 70 | self.hexcodes[0x51] = ("eor", "zeropageindexedindirecty") 71 | self.hexcodes[0x61] = ("adc", "zeropageindexedindirectx") 72 | self.hexcodes[0x71] = ("adc", "zeropageindexedindirecty") 73 | self.hexcodes[0x81] = ("sta", "zeropageindexedindirectx") 74 | self.hexcodes[0x91] = ("sta", "zeropageindexedindirecty") 75 | self.hexcodes[0xA1] = ("lda", "zeropageindexedindirectx") 76 | self.hexcodes[0xB1] = ("lda", "zeropageindexedindirecty") 77 | self.hexcodes[0xC1] = ("cmp", "zeropageindexedindirectx") 78 | self.hexcodes[0xD1] = ("cmp", "zeropageindexedindirecty") 79 | self.hexcodes[0xE1] = ("sbc", "zeropageindexedindirectx") 80 | self.hexcodes[0xF1] = ("sbc", "zeropageindexedindirecty") 81 | 82 | self.hexcodes[0x02] = ("", "") 83 | self.hexcodes[0x12] = ("ora", "zeropageindirect") 84 | self.hexcodes[0x22] = ("", "") 85 | self.hexcodes[0x32] = ("and", "zeropageindirect") 86 | self.hexcodes[0x42] = ("", "") 87 | self.hexcodes[0x52] = ("eor", "zeropageindirect") 88 | self.hexcodes[0x62] = ("", "") 89 | self.hexcodes[0x72] = ("adc", "zeropageindirect") 90 | self.hexcodes[0x82] = ("", "") 91 | self.hexcodes[0x92] = ("sta", "zeropageindirect") 92 | self.hexcodes[0xA2] = ("ldx", "immediate") 93 | self.hexcodes[0xB2] = ("lda", "zeropageindirect") 94 | self.hexcodes[0xC2] = ("", "") 95 | self.hexcodes[0xD2] = ("cmp", "zeropageindirect") 96 | self.hexcodes[0xE2] = ("", "") 97 | self.hexcodes[0xF2] = ("sbc", "zeropageindirect") 98 | 99 | self.hexcodes[0x03] = ("", "") 100 | self.hexcodes[0x13] = ("", "") 101 | self.hexcodes[0x23] = ("", "") 102 | self.hexcodes[0x33] = ("", "") 103 | self.hexcodes[0x43] = ("", "") 104 | self.hexcodes[0x53] = ("", "") 105 | self.hexcodes[0x63] = ("", "") 106 | self.hexcodes[0x73] = ("", "") 107 | self.hexcodes[0x83] = ("", "") 108 | self.hexcodes[0x93] = ("", "") 109 | self.hexcodes[0xA3] = ("", "") 110 | self.hexcodes[0xB3] = ("", "") 111 | self.hexcodes[0xC3] = ("", "") 112 | self.hexcodes[0xD3] = ("", "") 113 | self.hexcodes[0xE3] = ("", "") 114 | self.hexcodes[0xF3] = ("", "") 115 | 116 | self.hexcodes[0x04] = ("tsb", "zeropage") 117 | self.hexcodes[0x14] = ("trb", "zeropage") 118 | self.hexcodes[0x24] = ("bit", "zeropage") 119 | self.hexcodes[0x34] = ("bit", "zeropagex") 120 | self.hexcodes[0x44] = ("", "") 121 | self.hexcodes[0x54] = ("", "") 122 | self.hexcodes[0x64] = ("stz", "zeropage") 123 | self.hexcodes[0x74] = ("stz", "zeropagex") 124 | self.hexcodes[0x84] = ("sty", "zeropage") 125 | self.hexcodes[0x94] = ("sty", "zeropagex") 126 | self.hexcodes[0xA4] = ("ldy", "zeropage") 127 | self.hexcodes[0xB4] = ("ldy", "zeropagex") 128 | self.hexcodes[0xC4] = ("cpy", "zeropage") 129 | self.hexcodes[0xD4] = ("", "") 130 | self.hexcodes[0xE4] = ("cpx", "zeropage") 131 | self.hexcodes[0xF4] = ("", "") 132 | 133 | self.hexcodes[0x05] = ("ora", "zeropage") 134 | self.hexcodes[0x15] = ("ora", "zeropagex") 135 | self.hexcodes[0x25] = ("and", "zeropage") 136 | self.hexcodes[0x35] = ("and", "zeropagex") 137 | self.hexcodes[0x45] = ("eor", "zeropage") 138 | self.hexcodes[0x55] = ("eor", "zeropagex") 139 | self.hexcodes[0x65] = ("adc", "zeropage") 140 | self.hexcodes[0x75] = ("adc", "zeropagex") 141 | self.hexcodes[0x85] = ("sta", "zeropage") 142 | self.hexcodes[0x95] = ("sta", "zeropagex") 143 | self.hexcodes[0xA5] = ("lda", "zeropage") 144 | self.hexcodes[0xB5] = ("lda", "zeropagex") 145 | self.hexcodes[0xC5] = ("cmp", "zeropage") 146 | self.hexcodes[0xD5] = ("cmp", "zeropagex") 147 | self.hexcodes[0xE5] = ("sbc", "zeropage") 148 | self.hexcodes[0xF5] = ("sbc", "zeropagex") 149 | 150 | self.hexcodes[0x06] = ("asl", "zeropage") 151 | self.hexcodes[0x16] = ("asl", "zeropagex") 152 | self.hexcodes[0x26] = ("rol", "zeropage") 153 | self.hexcodes[0x36] = ("rol", "zeropagex") 154 | self.hexcodes[0x46] = ("lsr", "zeropage") 155 | self.hexcodes[0x56] = ("lsr", "zeropagex") 156 | self.hexcodes[0x66] = ("ror", "zeropage") 157 | self.hexcodes[0x76] = ("ror", "zeropagex") 158 | self.hexcodes[0x86] = ("stx", "zeropage") 159 | self.hexcodes[0x96] = ("stx", "zeropagey") 160 | self.hexcodes[0xA6] = ("ldx", "zeropage") 161 | self.hexcodes[0xB6] = ("ldx", "zeropagey") 162 | self.hexcodes[0xC6] = ("dec", "zeropage") 163 | self.hexcodes[0xD6] = ("dec", "zeropagex") 164 | self.hexcodes[0xE6] = ("inc", "zeropage") 165 | self.hexcodes[0xF6] = ("inc", "zeropagex") 166 | 167 | self.hexcodes[0x07] = ("", "") 168 | self.hexcodes[0x17] = ("", "") 169 | self.hexcodes[0x27] = ("", "") 170 | self.hexcodes[0x37] = ("", "") 171 | self.hexcodes[0x47] = ("", "") 172 | self.hexcodes[0x57] = ("", "") 173 | self.hexcodes[0x67] = ("", "") 174 | self.hexcodes[0x77] = ("", "") 175 | self.hexcodes[0x87] = ("", "") 176 | self.hexcodes[0x97] = ("", "") 177 | self.hexcodes[0xA7] = ("", "") 178 | self.hexcodes[0xB7] = ("", "") 179 | self.hexcodes[0xC7] = ("", "") 180 | self.hexcodes[0xD7] = ("", "") 181 | self.hexcodes[0xE7] = ("", "") 182 | self.hexcodes[0xF7] = ("", "") 183 | 184 | self.hexcodes[0x08] = ("php", "implicit") 185 | self.hexcodes[0x18] = ("clc", "implicit") 186 | self.hexcodes[0x28] = ("plp", "implicit") 187 | self.hexcodes[0x38] = ("sec", "implicit") 188 | self.hexcodes[0x48] = ("pha", "implicit") 189 | self.hexcodes[0x58] = ("cli", "implicit") 190 | self.hexcodes[0x68] = ("pla", "implicit") 191 | self.hexcodes[0x78] = ("sei", "implicit") 192 | self.hexcodes[0x88] = ("dey", "implicit") 193 | self.hexcodes[0x98] = ("tya", "implicit") 194 | self.hexcodes[0xA8] = ("tay", "implicit") 195 | self.hexcodes[0xB8] = ("clv", "implicit") 196 | self.hexcodes[0xC8] = ("iny", "implicit") 197 | self.hexcodes[0xD8] = ("cld", "implicit") 198 | self.hexcodes[0xE8] = ("inx", "implicit") 199 | self.hexcodes[0xF8] = ("sed", "implicit") 200 | 201 | self.hexcodes[0x09] = ("ora", "immediate") 202 | self.hexcodes[0x19] = ("ora", "absolutey") 203 | self.hexcodes[0x29] = ("and", "immediate") 204 | self.hexcodes[0x39] = ("and", "absolutey") 205 | self.hexcodes[0x49] = ("eor", "immediate") 206 | self.hexcodes[0x59] = ("eor", "absolutey") 207 | self.hexcodes[0x69] = ("adc", "immediate") 208 | self.hexcodes[0x79] = ("adc", "absolutey") 209 | self.hexcodes[0x89] = ("bit", "immediate") 210 | self.hexcodes[0x99] = ("sta", "absolutey") 211 | self.hexcodes[0xA9] = ("lda", "immediate") 212 | self.hexcodes[0xB9] = ("lda", "absolutey") 213 | self.hexcodes[0xC9] = ("cmp", "immediate") 214 | self.hexcodes[0xD9] = ("cmp", "absolutey") 215 | self.hexcodes[0xE9] = ("sbc", "immediate") 216 | self.hexcodes[0xF9] = ("sbc", "absolutey") 217 | 218 | self.hexcodes[0x0A] = ("asl", "accumulator") 219 | self.hexcodes[0x1A] = ("ina", "accumulator") 220 | self.hexcodes[0x2A] = ("rol", "accumulator") 221 | self.hexcodes[0x3A] = ("dea", "accumulator") 222 | self.hexcodes[0x4A] = ("lsr", "accumulator") 223 | self.hexcodes[0x5A] = ("phy", "implicit") 224 | self.hexcodes[0x6A] = ("ror", "accumulator") 225 | self.hexcodes[0x7A] = ("ply", "implicit") 226 | self.hexcodes[0x8A] = ("txa", "implicit") 227 | self.hexcodes[0x9A] = ("txs", "implicit") 228 | self.hexcodes[0xAA] = ("tax", "implicit") 229 | self.hexcodes[0xBA] = ("tsx", "implicit") 230 | self.hexcodes[0xCA] = ("dex", "implicit") 231 | self.hexcodes[0xDA] = ("phx", "implicit") 232 | self.hexcodes[0xEA] = ("nop", "implicit") 233 | self.hexcodes[0xFA] = ("plx", "implicit") 234 | 235 | self.hexcodes[0x0B] = ("", "") 236 | self.hexcodes[0x1B] = ("", "") 237 | self.hexcodes[0x2B] = ("", "") 238 | self.hexcodes[0x3B] = ("", "") 239 | self.hexcodes[0x4B] = ("", "") 240 | self.hexcodes[0x5B] = ("", "") 241 | self.hexcodes[0x6B] = ("", "") 242 | self.hexcodes[0x7B] = ("", "") 243 | self.hexcodes[0x8B] = ("", "") 244 | self.hexcodes[0x9B] = ("", "") 245 | self.hexcodes[0xAB] = ("", "") 246 | self.hexcodes[0xBB] = ("", "") 247 | self.hexcodes[0xCB] = ("", "") 248 | self.hexcodes[0xDB] = ("", "") 249 | self.hexcodes[0xEB] = ("", "") 250 | self.hexcodes[0xFB] = ("", "") 251 | 252 | self.hexcodes[0x0C] = ("tsb", "absolute") 253 | self.hexcodes[0x1C] = ("trb", "absolute") 254 | self.hexcodes[0x2C] = ("bit", "absolute") 255 | self.hexcodes[0x3C] = ("bit", "absolutex") 256 | self.hexcodes[0x4C] = ("jmp", "absolute") 257 | self.hexcodes[0x5C] = ("", "") 258 | self.hexcodes[0x6C] = ("jmp", "absoluteindirect") 259 | self.hexcodes[0x7C] = ("jmp", "absoluteindexedindirect") 260 | self.hexcodes[0x8C] = ("sty", "absolute") 261 | self.hexcodes[0x9C] = ("stz", "absolute") 262 | self.hexcodes[0xAC] = ("ldy", "absolute") 263 | self.hexcodes[0xBC] = ("ldy", "absolutex") 264 | self.hexcodes[0xCC] = ("cpy", "absolute") 265 | self.hexcodes[0xDC] = ("", "") 266 | self.hexcodes[0xEC] = ("cpx", "absolute") 267 | self.hexcodes[0xFC] = ("", "") 268 | 269 | self.hexcodes[0x0D] = ("ora", "absolute") 270 | self.hexcodes[0x1D] = ("ora", "absolutex") 271 | self.hexcodes[0x2D] = ("and", "absolute") 272 | self.hexcodes[0x3D] = ("and", "absolutex") 273 | self.hexcodes[0x4D] = ("eor", "absolute") 274 | self.hexcodes[0x5D] = ("eor", "absolutex") 275 | self.hexcodes[0x6D] = ("adc", "absolute") 276 | self.hexcodes[0x7D] = ("adc", "absolutex") 277 | self.hexcodes[0x8D] = ("sta", "absolute") 278 | self.hexcodes[0x9D] = ("sta", "absolutex") 279 | self.hexcodes[0xAD] = ("lda", "absolute") 280 | self.hexcodes[0xBD] = ("lda", "absolutex") 281 | self.hexcodes[0xCD] = ("cmp", "absolute") 282 | self.hexcodes[0xDD] = ("cmp", "absolutex") 283 | self.hexcodes[0xED] = ("sbc", "absolute") 284 | self.hexcodes[0xFD] = ("sbc", "absolutex") 285 | 286 | self.hexcodes[0x0E] = ("asl", "absolute") 287 | self.hexcodes[0x1E] = ("asl", "absolutex") 288 | self.hexcodes[0x2E] = ("rol", "absolute") 289 | self.hexcodes[0x3E] = ("rol", "absolutex") 290 | self.hexcodes[0x4E] = ("lsr", "absolute") 291 | self.hexcodes[0x5E] = ("lsr", "absolutex") 292 | self.hexcodes[0x6E] = ("ror", "absolute") 293 | self.hexcodes[0x7E] = ("ror", "absolutex") 294 | self.hexcodes[0x8E] = ("stx", "absolute") 295 | self.hexcodes[0x9E] = ("stz", "absolutex") 296 | self.hexcodes[0xAE] = ("ldx", "absolute") 297 | self.hexcodes[0xBE] = ("ldx", "absolutey") 298 | self.hexcodes[0xCE] = ("dec", "absolute") 299 | self.hexcodes[0xDE] = ("dec", "absolutex") 300 | self.hexcodes[0xEE] = ("inc", "absolute") 301 | self.hexcodes[0xFE] = ("inc", "absolutex") 302 | 303 | self.hexcodes[0x0F] = ("", "") 304 | self.hexcodes[0x1F] = ("", "") 305 | self.hexcodes[0x2F] = ("", "") 306 | self.hexcodes[0x3F] = ("", "") 307 | self.hexcodes[0x4F] = ("", "") 308 | self.hexcodes[0x5F] = ("", "") 309 | self.hexcodes[0x6F] = ("", "") 310 | self.hexcodes[0x7F] = ("", "") 311 | self.hexcodes[0x8F] = ("", "") 312 | self.hexcodes[0x9F] = ("", "") 313 | self.hexcodes[0xAF] = ("", "") 314 | self.hexcodes[0xBF] = ("", "") 315 | self.hexcodes[0xCF] = ("", "") 316 | self.hexcodes[0xDF] = ("", "") 317 | self.hexcodes[0xEF] = ("", "") 318 | self.hexcodes[0xFF] = ("", "") 319 | 320 | def reset(self): 321 | self.a = 0x00 322 | self.x = 0x00 323 | self.y = 0x00 324 | self.sp = 0xff 325 | self.cc = Flags.BREAK | Flags.UNUSED 326 | lowaddr = self.memory_map.Read(0xfffc) 327 | highaddr = self.memory_map.Read(0xfffd) 328 | if (lowaddr != None) and (lowaddr > -1) and (highaddr != None) and (highaddr > -1): 329 | address = (lowaddr & 0xff) | ((highaddr << 8) & 0xff00) 330 | self.pc = address 331 | return True 332 | else: 333 | print("ERROR: Bad reset vector 0x" + str(self.memory_map.Read(0xfffc)) + ",0x" + str(self.memory_map.Read(0xfffd))) 334 | return False 335 | 336 | def nmi(self): 337 | # Read the NMI vector 338 | lowaddr = self.memory_map.Read(0xfffa) 339 | highaddr = self.memory_map.Read(0xfffb) 340 | if (lowaddr != None) and (lowaddr > -1) and (highaddr != None) and (highaddr > -1): 341 | address = (lowaddr & 0xff) | ((highaddr << 8) & 0xff00) 342 | else: 343 | return False 344 | 345 | # push PC and status on stack 346 | self.memory_map.Write(self.sp, (self.pc >> 8) & 0xff) 347 | self.memory_map.Write(self.sp - 1, self.pc & 0xff) 348 | # TODO: does this actually set this flag before pushing to the stack? 349 | self.memory_map.Write(self.sp - 2, self.cc | Flags.UNUSED) 350 | self.sp -= 3 351 | 352 | # Set PC to the NMI vector 353 | self.pc = address 354 | return True 355 | 356 | def irq(self): 357 | # Read the IRQ vector 358 | lowaddr = self.memory_map.Read(0xfffa) 359 | highaddr = self.memory_map.Read(0xfffb) 360 | if (lowaddr != None) and (lowaddr > -1) and (highaddr != None) and (highaddr > -1): 361 | address = (lowaddr & 0xff) | ((highaddr << 8) & 0xff00) 362 | else: 363 | return False 364 | 365 | # push PC and status on stack 366 | self.memory_map.Write(self.sp, (self.pc >> 8) & 0xff) 367 | self.memory_map.Write(self.sp - 1, self.pc & 0xff) 368 | self.memory_map.Write(self.sp - 2, self.cc) 369 | self.sp -= 3 370 | 371 | # Set PC to the NMI vector 372 | self.pc = address 373 | return True 374 | 375 | def make_flags_nz(self, result): 376 | self.set_n(int(result) & 0x80) 377 | self.set_z(int(result) == 0) 378 | 379 | def make_flags_v(self, acc, operand, carryin, result, carryout): 380 | # V Flag, bit 6 381 | self.set_v(((int(acc) ^ int(result)) & (int(operand) ^ int(result)) & 0x80) == 0x80) 382 | 383 | def get_operand(self, addrmode, opcode, operand8, operand16): 384 | # Get the operand based on the address mode 385 | # print get operand addrmode="+str(addrmode)+" opcode:"+str(opcode)+" op8:"+str(operand8) 386 | if addrmode == "zeropageindexedindirectx": 387 | # 6502 bug/feature: indirecting by x wraps within the zero page 388 | indirectaddr = (operand8 + self.x) & 0xff 389 | addr = (self.memory_map.Read(indirectaddr + 1) << 8) + self.memory_map.Read(indirectaddr) 390 | operand = self.memory_map.Read(addr) 391 | length = 2 392 | elif addrmode == "zeropageindexedindirecty": 393 | indirectaddr = operand8 394 | # 6502 bug when ($FF),y 395 | addr = (self.memory_map.Read((indirectaddr + 1) & 0xff) << 8) + self.memory_map.Read(indirectaddr) 396 | addr = addr + self.y 397 | operand = self.memory_map.Read(addr) 398 | length = 2 399 | elif addrmode == "zeropageindirect": 400 | indirectaddr = operand8 401 | addr = (self.memory_map.Read(indirectaddr + 1) << 8) + self.memory_map.Read(indirectaddr) 402 | operand = self.memory_map.Read(addr) 403 | length = 2 404 | elif addrmode == "zeropage": 405 | addr = operand8 406 | operand = self.memory_map.Read(addr) 407 | length = 2 408 | elif addrmode == "zeropagex": 409 | addr = (operand8 + self.x) & 0xff 410 | operand = self.memory_map.Read(addr) 411 | length = 2 412 | elif addrmode == "zeropagey": 413 | addr = (operand8 + self.y) & 0xff 414 | operand = self.memory_map.Read(addr) 415 | length = 2 416 | elif addrmode == "immediate": 417 | addr = None 418 | operand = operand8 419 | length = 2 420 | elif addrmode == "absolutey": 421 | addr = operand16 + self.y 422 | operand = self.memory_map.Read(addr) 423 | length = 3 424 | elif addrmode == "absolute": 425 | addr = operand16 426 | operand = self.memory_map.Read(addr) 427 | length = 3 428 | elif addrmode == "absolutex": 429 | addr = operand16 + self.x 430 | operand = self.memory_map.Read(addr) 431 | length = 3 432 | elif addrmode == "indirect": 433 | indirectaddr = operand16 434 | addr = (self.memory_map.Read(indirectaddr + 1) << 8) + self.memory_map.Read(indirectaddr) 435 | operand = (self.memory_map.Read(addr + 1) << 8) + self.memory_map.Read(addr) 436 | length = 3 437 | elif addrmode == "accumulator": 438 | addr = None 439 | operand = self.a 440 | length = 1 441 | elif addrmode == "implicit": 442 | addr = None 443 | operand = operand8 444 | length = 1 445 | else: 446 | print("ERROR: Address mode %s not found" % addrmode) 447 | print(" : PC = 0x%04x" % self.pc) 448 | exit() 449 | return (operand, addr, length) 450 | 451 | def get_operand16(self, addrmode, opcode, operand8, operand16): 452 | # Get the operand based on the address mode 453 | if addrmode == "absolute": 454 | addr = operand16 455 | length = 3 456 | elif addrmode == "indirect": 457 | indirectaddr = operand16 458 | addr = (self.memory_map.Read(indirectaddr + 1) << 8) + self.memory_map.Read(indirectaddr) 459 | length = 3 460 | elif addrmode == "absoluteindexedindirect": 461 | indirectaddr = operand16 + self.x 462 | addr = (self.memory_map.Read(indirectaddr + 1) << 8) + self.memory_map.Read(indirectaddr) 463 | length = 3 464 | elif addrmode == "absoluteindirect": 465 | indirectaddr = operand16 466 | addr = (self.memory_map.Read(indirectaddr + 1) << 8) + self.memory_map.Read(indirectaddr) 467 | length = 3 468 | else: 469 | print("ERROR: Address mode %s not found for JMP or JSR" % addrmode) 470 | print(" : PC = 0x%04x" % self.pc) 471 | exit() 472 | operand = self.memory_map.Read(addr) 473 | return (operand, addr, length) 474 | 475 | # Execute the instruction at the current program counter location. 476 | # Converts hex opcode to instruction three letter name and address mode 477 | # turns instruction name into a method - e.g. instr_lda() 478 | # Then calls the method and passes in the operands 479 | 480 | def execute(self, address=None): 481 | if address == None: 482 | address = self.pc 483 | # Pre-increment PC on instruction fetch 484 | self.pc += 1 485 | opcode = self.memory_map.Execute(address) 486 | # TODO: we should increment self.pc here by the opcode argument length instead 487 | # of doing it manually in every opcode handler and potentially introducing bugs 488 | # TODO: only fetch the number of operand bytes appropriate for the instruction 489 | # to avoid the extra memory accesses 490 | operand8 = self.memory_map.Execute((address + 1) % 65536) 491 | hi = self.memory_map.Execute((address + 2) % 65536) 492 | operand16 = operand8 + ((hi << 8) & 0xff00) 493 | 494 | if (opcode >= 0) and (opcode < 256): 495 | instruction, addrmode = self.hexcodes[opcode] 496 | if (instruction != ""): 497 | # TODO: construct a method dispatch table once instead of every time 498 | methodname = "instr_" + instruction 499 | # print "METHODNAME:"+methodname 500 | method = getattr(self, methodname, lambda: "nothing") 501 | thing = method(addrmode, opcode, operand8, operand16) 502 | if thing == None: 503 | return (None, None) 504 | return thing 505 | else: 506 | # TODO: raise exception here 507 | return ("not_instruction", self.pc) 508 | else: 509 | # TODO: raise exception here 510 | #print "ERROR: Out in the weeds. Opcode = %d" % opcode 511 | return ("weeds", self.pc) 512 | 513 | 514 | def none_or_byte(self, thebyte): 515 | if thebyte == None: 516 | thestr = "None" 517 | else: 518 | thestr = "0x%02x" % thebyte 519 | return thestr 520 | 521 | def show_state(self): 522 | str_pc = self.none_or_byte(self.pc) 523 | str_a = self.none_or_byte(self.a) 524 | str_x = self.none_or_byte(self.x) 525 | str_y = self.none_or_byte(self.y) 526 | str_sp = self.none_or_byte(self.sp) 527 | str_cc = self.none_or_byte(self.cc) 528 | if (self.have_symbols) and (self.pc in self.labels): 529 | label = self.labels[self.pc] 530 | label = label.ljust(10) 531 | print(label + " PC:" + str_pc + " A:" + str_a + " X:" + str_x + " Y:" + str_y + " SP:" + str_sp + " STATUS:" + str_cc) 532 | else: 533 | print(" PC:" + str_pc + " A:" + str_a + " X:" + str_x + " Y:" + str_y + " SP:" + str_sp + " STATUS:" + str_cc) 534 | 535 | # Utility routines to change the flags 536 | # So you don't need to remember the bit positions 537 | # 538 | 539 | # 7 6 5 4 3 2 1 0 540 | # Negative Overflow (S) Break Decimal Interrupt Zero Carry 541 | # N V - B D I Z C 542 | # - - - - - - - - 543 | 544 | def set_c(self, truth): 545 | if truth: 546 | self.cc = self.cc | Flags.CARRY 547 | else: 548 | self.cc = self.cc & (0xff ^ Flags.CARRY) 549 | 550 | def set_z(self, truth): 551 | if truth: 552 | self.cc = self.cc | Flags.ZERO 553 | else: 554 | self.cc = self.cc & (0xff ^ Flags.ZERO) 555 | 556 | def set_i(self, truth): 557 | if truth: 558 | self.cc = self.cc | Flags.INTERRUPT 559 | else: 560 | self.cc = self.cc & (0xff ^ Flags.INTERRUPT) 561 | 562 | def set_d(self, truth): 563 | if truth: 564 | self.cc = self.cc | Flags.DECIMAL 565 | else: 566 | self.cc = self.cc & (0xff ^ Flags.DECIMAL) 567 | 568 | def set_b(self, truth): 569 | if truth: 570 | self.cc = self.cc | Flags.BREAK 571 | else: 572 | self.cc = self.cc & (0xff ^ Flags.BREAK) 573 | 574 | def set_s(self, truth): 575 | if truth: 576 | self.cc = self.cc | Flags.UNUSED 577 | else: 578 | self.cc = self.cc & (0xff ^ Flags.UNUSED) 579 | 580 | def set_v(self, truth): 581 | if truth: 582 | self.cc = self.cc | Flags.OVERFLOW 583 | else: 584 | self.cc = self.cc & (0xff ^ Flags.OVERFLOW) 585 | 586 | def set_n(self, truth): 587 | if truth: 588 | self.cc = self.cc | Flags.NEGATIVE 589 | else: 590 | self.cc = self.cc & (0xff ^ Flags.NEGATIVE) 591 | 592 | def push(self, value): 593 | self.memory_map.Write(0x100 + self.sp, value) 594 | self.sp -= 1 595 | 596 | def pushaddr(self, addr): 597 | low = addr & 0xff 598 | high = (addr & 0xff00) >> 8 599 | self.memory_map.Write(0x100 + self.sp, high) 600 | self.sp -= 1 601 | self.memory_map.Write(0x100 + self.sp, low) 602 | self.sp -= 1 603 | 604 | def pull(self): 605 | self.sp += 1 606 | value = self.memory_map.Read(0x100 + self.sp) 607 | return value 608 | 609 | def pulladdr(self): 610 | self.sp += 1 611 | low = self.memory_map.Read(0x100 + self.sp) 612 | self.sp += 1 613 | high = self.memory_map.Read(0x100 + self.sp) 614 | 615 | addr = low + (high << 8) 616 | return addr 617 | 618 | def relative_address(self, operand8, addr): 619 | if operand8 & 0x80: 620 | offset = ((operand8 & 0xff) ^ 0xff) + 1 # invert and +1 to change neg to pos 621 | new_addr = addr - offset 622 | else: 623 | offset = operand8 624 | new_addr = addr + offset 625 | return new_addr 626 | 627 | # Instruction ADC 628 | # 69 55 adc #$55 629 | # 65 20 adc $20 630 | # 75 20 adc $20,X 631 | # 6D 33 22 adc $2233 632 | # 7D 33 22 adc $2233,X 633 | # 79 33 22 adc $2233,Y 634 | # 61 20 adc ($20,X) 635 | # 71 20 adc ($20),Y 636 | # 72 20 adc ($20) 637 | def instr_adc(self, addrmode, opcode, operand8, operand16): 638 | # TODO: support non-BCD arguments in DECIMAL mode 639 | 640 | carryin = self.cc & Flags.CARRY 641 | 642 | # Get the operand based on the address mode 643 | operand, addr, length = self.get_operand(addrmode, opcode, operand8, operand16) 644 | 645 | # Do the add 646 | # Compute the carry 647 | # Put the result in A 648 | # Compute the flags 649 | 650 | if self.cc & Flags.DECIMAL: 651 | a_10s = ((self.a & 0xf0) >> 4) * 10 652 | a_1s = (self.a & 0xf) 653 | operand_10s = ((operand & 0xf0) >> 4) * 10 654 | operand_1s = (operand & 0xf) 655 | if (a_10s >= 100 or a_1s >= 10 or operand_10s >= 100 or operand_1s >= 10): 656 | raise ValueError("Invalid BCD argument not supported") 657 | sum = (a_10s + a_1s + operand_10s + operand_1s + carryin) 658 | self.set_c(sum > 100) 659 | 660 | sum_1s = sum % 10 661 | sum_10s = (sum % 100 - sum_1s)/10 662 | result = int(sum_10s) << 4 + sum_1s 663 | else: 664 | result = (self.a + operand + carryin) 665 | self.set_c(result > 255) 666 | result = result % 256 667 | 668 | acc = self.a 669 | self.a = result 670 | self.make_flags_nz(result) 671 | # self.make_flags_v(self.a, operand, carryin, result, carryout) 672 | self.set_v(((acc ^ result) & (operand ^ result) & 0x80) == 0x80) 673 | self.pc += length - 1 674 | return None 675 | 676 | # Instruction AND 677 | # 29 55 and #$55 678 | # 25 20 and $20 679 | # 35 20 and $20,X 680 | # 2D 33 22 and $2233 681 | # 3D 33 22 and $2233,X 682 | # 39 33 22 and $2233,Y 683 | # 21 20 and ($20,X) 684 | # 31 20 and ($20),Y 685 | # 32 20 and ($20) 686 | 687 | def instr_and(self, addrmode, opcode, operand8, operand16): 688 | # Get the operand based on the address mode 689 | operand, addr, length = self.get_operand(addrmode, opcode, operand8, operand16) 690 | 691 | # Do the an 692 | # Put the result in A 693 | # Compute the flags 694 | 695 | result = (self.a & operand) 696 | 697 | self.a = result 698 | self.make_flags_nz(result) 699 | self.pc += length - 1 700 | 701 | return None 702 | 703 | # Instruction ASL 704 | # 0A asl A 705 | # 06 20 asl $20 706 | # 16 20 asl $20,X 707 | # 0E 33 22 asl $2233 708 | # 1E 33 22 asl $2233,X 709 | def instr_asl(self, addrmode, opcode, operand8, operand16): 710 | if addrmode == "accumulator": 711 | self.set_c(self.a & 0x80) 712 | result = (self.a & 0x7f) << 1 713 | self.a = result 714 | self.make_flags_nz(result) 715 | return None 716 | else: 717 | # Get the operand based on the address mode 718 | operand, addr, length = self.get_operand(addrmode, opcode, operand8, operand16) 719 | self.set_c(operand & 0x80) 720 | result = (operand & 0x7f) << 1 721 | 722 | self.memory_map.Write(addr, result) 723 | self.pc += length - 1 724 | self.make_flags_nz(result) 725 | return ("w", addr) 726 | 727 | # Instruction BCC 728 | # 90 55 bcc $55 729 | def instr_bcc(self, addrmode, opcode, operand8, operand16): 730 | self.pc += 1 731 | if not self.cc & Flags.CARRY: 732 | self.pc = self.relative_address(operand8, self.pc) 733 | 734 | return None 735 | 736 | # Instruction BCS 737 | # B0 55 bcs $55 738 | def instr_bcs(self, addrmode, opcode, operand8, operand16): 739 | self.pc += 1 740 | if self.cc & Flags.CARRY: 741 | self.pc = self.relative_address(operand8, self.pc) 742 | 743 | return None 744 | 745 | # Instruction BEQ 746 | # F0 55 beq $55 747 | def instr_beq(self, addrmode, opcode, operand8, operand16): 748 | self.pc += 1 749 | if self.cc & Flags.ZERO: 750 | self.pc = self.relative_address(operand8, self.pc) 751 | 752 | return None 753 | 754 | # Instruction BIT 755 | # 89 55 bit #$55 756 | # 24 20 bit $20 757 | # 34 20 bit $20,X 758 | # 2C 33 22 bit $2233 759 | # 3C 33 22 bit $2233,X 760 | def instr_bit(self, addrmode, opcode, operand8, operand16): 761 | # Get the operand, immediate or from memory 762 | if addrmode == "immediate": 763 | operand = operand8 764 | length = 2 765 | else: 766 | operand, addr, length = self.get_operand(addrmode, opcode, operand8, operand16) 767 | 768 | # Do the test. 769 | test = self.a & operand 770 | self.set_z(test == 0x00) 771 | 772 | # N is set to bit 7 of the operand 773 | self.set_n(operand & 0x80) 774 | 775 | # V is set to bit 6 of the operand 776 | self.set_v(operand & 0x40) 777 | self.pc += length - 1 778 | 779 | return None 780 | 781 | # Instruction BMI 782 | # 30 55 bmi $55 783 | def instr_bmi(self, addrmode, opcode, operand8, operand16): 784 | self.pc += 1 785 | if self.cc & Flags.NEGATIVE: 786 | self.pc = self.relative_address(operand8, self.pc) 787 | 788 | return None 789 | 790 | # Instruction BNE 791 | # D0 55 bne $55 792 | def instr_bne(self, addrmode, opcode, operand8, operand16): 793 | self.pc += 1 794 | if not self.cc & Flags.ZERO: 795 | self.pc = self.relative_address(operand8, self.pc) 796 | 797 | return None 798 | 799 | # Instruction BPL 800 | # 10 55 bpl $55 801 | def instr_bpl(self, addrmode, opcode, operand8, operand16): 802 | self.pc += 1 803 | if not self.cc & Flags.NEGATIVE: 804 | self.pc = self.relative_address(operand8, self.pc) 805 | return None 806 | 807 | # Instruction BRA 808 | # 80 55 bra $55 809 | def instr_bra(self, addrmode, opcode, operand8, operand16): 810 | self.pc += 1 811 | self.pc = self.relative_address(operand8, self.pc) 812 | return None 813 | 814 | # Instruction BRK 815 | # 00 brk 816 | def instr_brk(self, addrmode, opcode, operand8, operand16): 817 | # PC is pre-incremented on instruction fetch 818 | self.pushaddr(self.pc + 1) 819 | self.set_s(True) 820 | self.set_b(True) 821 | self.push(self.cc) 822 | low = self.memory_map.Read(0xfffe) 823 | high = self.memory_map.Read(0xffff) 824 | self.pc = low + (high << 8) 825 | self.set_i(True) 826 | # 65C02 827 | self.set_d(False) 828 | return None 829 | 830 | # Instruction BVC 831 | # 50 55 bvc $55 832 | def instr_bvc(self, addrmode, opcode, operand8, operand16): 833 | self.pc += 1 834 | if not self.cc & Flags.OVERFLOW: 835 | self.pc = self.relative_address(operand8, self.pc) 836 | return None 837 | 838 | # Instruction BVS 839 | # 70 55 bvs $55 840 | def instr_bvs(self, addrmode, opcode, operand8, operand16): 841 | self.pc += 1 842 | if self.cc & Flags.OVERFLOW: 843 | self.pc = self.relative_address(operand8, self.pc) 844 | return None 845 | 846 | # Instruction CLC 847 | # 18 clc 848 | def instr_clc(self, addrmode, opcode, operand8, operand16): 849 | self.set_c(False) 850 | return None 851 | 852 | # Instruction CLD 853 | # D8 cld 854 | def instr_cld(self, addrmode, opcode, operand8, operand16): 855 | self.set_d(False) 856 | return None 857 | 858 | # Instruction CLI 859 | # 58 cli 860 | def instr_cli(self, addrmode, opcode, operand8, operand16): 861 | self.set_i(False) 862 | return None 863 | 864 | # Instruction CLV 865 | # 57 clv 866 | def instr_clv(self, addrmode, opcode, operand8, operand16): 867 | self.set_v(False) 868 | return None 869 | 870 | # Instruction CMP 871 | # C9 55 cmp #$55 872 | # C5 20 cmp $20 873 | # CD 33 22 cmp $2233 874 | # DD 33 22 cmp $2233,X 875 | # D9 33 22 cmp $2233,Y 876 | # C1 20 cmp ($20,X) 877 | # D1 20 cmp ($20),Y 878 | # D2 20 cmp ($20) 879 | def instr_cmp(self, addrmode, opcode, operand8, operand16): 880 | operand, addr, length = self.get_operand(addrmode, opcode, operand8, operand16) 881 | test = self.a - operand 882 | # TODO: add test case 883 | if test < 0: 884 | test += 256 885 | self.make_flags_nz(test) 886 | self.pc += length - 1 887 | return None 888 | 889 | # Instruction CMP 890 | # E0 55 cpx #$55 891 | # E4 20 cpx $20 892 | # EC 33 22 cpx $2233 893 | # C0 55 cpy #$55 894 | # C4 20 cpy $20 895 | # CC 33 22 cpy $2233 896 | def instr_cpx(self, addrmode, opcode, operand8, operand16): 897 | operand, addr, length = self.get_operand(addrmode, opcode, operand8, operand16) 898 | test = self.a - operand 899 | # TODO: add test case 900 | if test < 0: 901 | test += 256 902 | self.make_flags_nz(test) 903 | self.pc += length 904 | return None 905 | 906 | # Instruction CPY 907 | # C0 55 cpy #$55 908 | # C4 20 cpy $20 909 | # CC 33 22 cpy $2233 910 | def instr_cpy(self, addrmode, opcode, operand8, operand16): 911 | operand, addr, length = self.get_operand(addrmode, opcode, operand8, operand16) 912 | test = self.y - operand 913 | # TODO: add test case 914 | if test < 0: 915 | test += 256 916 | self.make_flags_nz(test) 917 | self.pc += length - 1 918 | return None 919 | 920 | # Instruction DEA aka DEC A 921 | # 3A dea 922 | def instr_dea(self, addrmode, opcode, operand8, operand16): 923 | operand, addr, length = self.get_operand(addrmode, opcode, operand8, operand16) 924 | # TODO: add test case 925 | if self.a: 926 | self.a -= 1 927 | else: 928 | self.a = 0xff 929 | self.make_flags_nz(self.a) 930 | self.pc += length - 1 931 | return None 932 | 933 | # Instruction DEC 934 | # C6 20 dec $20 935 | # D6 20 dec $20,X 936 | # CE 33 22 dec $2233 937 | # DE 33 22 dec $2233,X 938 | def instr_dec(self, addrmode, opcode, operand8, operand16): 939 | operand, addr, length = self.get_operand(addrmode, opcode, operand8, operand16) 940 | # TODO: add test case 941 | if operand: 942 | result = operand - 1 943 | else: 944 | result = 0xff 945 | self.make_flags_nz(result) 946 | self.memory_map.Write(addr, result) 947 | self.pc += length - 1 948 | return ("w", addr) 949 | 950 | # Instruction DEX 951 | # CA dex 952 | def instr_dex(self, addrmode, opcode, operand8, operand16): 953 | operand, addr, length = self.get_operand(addrmode, opcode, operand8, operand16) 954 | # TODO: add test case 955 | if self.x: 956 | result = self.x - 1 957 | else: 958 | result = 0xff 959 | self.make_flags_nz(result) 960 | self.x = result 961 | self.pc += length - 1 962 | return None 963 | 964 | # Instruction DEY 965 | # 88 dey 966 | def instr_dey(self, addrmode, opcode, operand8, operand16): 967 | operand, addr, length = self.get_operand(addrmode, opcode, operand8, operand16) 968 | # TODO: add test case 969 | if self.y: 970 | result = self.y - 1 971 | else: 972 | result = 0xff 973 | self.make_flags_nz(result) 974 | self.y = result 975 | self.pc += length - 1 976 | return None 977 | 978 | # Instruction EOR 979 | # 49 55 eor #$55 980 | # 45 20 eor $20 981 | # 55 20 eor $20,X 982 | # 4D 33 22 eor $2233 983 | # 5D 33 22 eor $2233,X 984 | # 59 33 22 eor $2233,Y 985 | # 41 20 eor ($20,X) 986 | # 51 20 eor ($20),Y 987 | # 52 20 eor ($20) 988 | def instr_eor(self, addrmode, opcode, operand8, operand16): 989 | # Get the operand based on the address mode 990 | operand, addr, length = self.get_operand(addrmode, opcode, operand8, operand16) 991 | 992 | # Do the an 993 | # Put the result in A 994 | # Compute the flags 995 | 996 | result = (self.a ^ operand) 997 | 998 | self.a = result 999 | self.make_flags_nz(result) 1000 | self.pc += length - 1 1001 | return None 1002 | 1003 | # Instruction INA aka INC A 1004 | # 1A ina 1005 | def instr_ina(self, addrmode, opcode, operand8, operand16): 1006 | operand, addr, length = self.get_operand(addrmode, opcode, operand8, operand16) 1007 | self.a = (self.a + 1) % 256 1008 | self.make_flags_nz(self.a) 1009 | self.pc += length - 1 1010 | return None 1011 | 1012 | # Instruction INC 1013 | # E6 20 inc $20 1014 | # F6 20 inc $20,X 1015 | # EE 33 22 inc $2233 1016 | # FE 33 22 inc $2233,X 1017 | def instr_inc(self, addrmode, opcode, operand8, operand16): 1018 | operand, addr, length = self.get_operand(addrmode, opcode, operand8, operand16) 1019 | result = (operand + 1) % 256 1020 | self.make_flags_nz(result) 1021 | self.pc += length - 1 1022 | self.memory_map.Write(addr, result) 1023 | return None 1024 | 1025 | # Instruction INX 1026 | # E8 inx 1027 | def instr_inx(self, addrmode, opcode, operand8, operand16): 1028 | operand, addr, length = self.get_operand(addrmode, opcode, operand8, operand16) 1029 | result = (self.x + 1) % 256 1030 | self.make_flags_nz(result) 1031 | self.x = result 1032 | self.pc += length - 1 1033 | 1034 | # Instruction INY 1035 | # C8 iny 1036 | def instr_iny(self, addrmode, opcode, operand8, operand16): 1037 | operand, addr, length = self.get_operand(addrmode, opcode, operand8, operand16) 1038 | result = (self.y + 1) % 256 1039 | self.make_flags_nz(result) 1040 | self.y = result 1041 | self.pc += length - 1 1042 | return None 1043 | 1044 | # Instruction JMP 1045 | # 4C 33 22 jmp $2233 1046 | # 6C 33 22 jmp ($2233) 1047 | # 7C 33 22 jmp ($2233,X) 1048 | def instr_jmp(self, addrmode, opcode, operand8, operand16): 1049 | # print "INSTR_JMP CALLED addrmode = %s opcode=%02x operand8=%02x operand16=%04x" % (addrmode,opcode,operand8,operand16) 1050 | operand, addr, length = self.get_operand16(addrmode, opcode, operand8, operand16) 1051 | # print "INSTR_JMP operand = %04x addr=%04x length=%d" % (operand,addr, length) 1052 | # print "INSTR_JMP operand16 = %04x " % operand16 1053 | self.pc = addr 1054 | return None 1055 | 1056 | # Instruction JSR 1057 | 1058 | # 20 33 22 jsr $2233 1059 | def instr_jsr(self, addrmode, opcode, operand8, operand16): 1060 | operand, addr, length = self.get_operand16(addrmode, opcode, operand8, operand16) 1061 | self.pc += length - 1 1062 | # Pushes the address - 1 of the next operation to be executed, so that when RTS is 1063 | # called and the next opcode pre-increments PC, it will be pointed at the right place. 1064 | self.pushaddr(self.pc - 1) 1065 | self.pc = addr 1066 | return ("stack", self.sp) 1067 | 1068 | # Instruction LDA 1069 | 1070 | # A9 55 lda #$55 1071 | # A5 20 lda $20 1072 | # B5 20 lda $20,X 1073 | # AD 33 22 lda $2233 1074 | # BD 33 22 lda $2233,X 1075 | # B9 33 22 lda $2233,Y 1076 | # A1 20 lda ($20,X) 1077 | # B1 20 lda ($20),Y 1078 | # B2 20 lda ($20) 1079 | def instr_lda(self, addrmode, opcode, operand8, operand16): 1080 | operand, addr, length = self.get_operand(addrmode, opcode, operand8, operand16) 1081 | # print "LDA : addrmode:"+str(addrmode)+" operand:"+str(operand)+" operand8 "+str(operand8) 1082 | self.a = operand 1083 | self.make_flags_nz(operand) 1084 | self.pc += length - 1 1085 | return None 1086 | 1087 | # Instruction LDX 1088 | # A9 55 lda #$55 1089 | # A2 55 ldx #$55 1090 | # A6 20 ldx $20 1091 | # B6 20 ldx $20,Y 1092 | # AE 33 22 ldx $2233 1093 | # BE 33 22 ldx $2233,Y 1094 | def instr_ldx(self, addrmode, opcode, operand8, operand16): 1095 | operand, addr, length = self.get_operand(addrmode, opcode, operand8, operand16) 1096 | self.x = operand 1097 | self.make_flags_nz(operand) 1098 | self.pc += length - 1 1099 | return None 1100 | 1101 | # Instruction LDY 1102 | # A0 55 ldy #$55 1103 | # A4 20 ldy $20 1104 | # B4 20 ldy $20,X 1105 | # AC 33 22 ldy $2233 1106 | # BC 33 22 ldy $2233,X 1107 | def instr_ldy(self, addrmode, opcode, operand8, operand16): 1108 | operand, addr, length = self.get_operand(addrmode, opcode, operand8, operand16) 1109 | self.y = operand 1110 | self.make_flags_nz(operand) 1111 | self.pc += length - 1 1112 | return None 1113 | 1114 | # Instruction LSR 1115 | # 4A lsr A 1116 | # 46 20 lsr $20 1117 | # 56 20 lsr $20,X 1118 | # 4E 33 22 lsr $2233 1119 | # 5E 33 22 lsr $2233,X 1120 | def instr_lsr(self, addrmode, opcode, operand8, operand16): 1121 | if (addrmode == "accumulator"): 1122 | self.set_c(self.a & 0x01) 1123 | 1124 | result = self.a >> 1 1125 | self.a = result 1126 | self.make_flags_nz(result) 1127 | return None 1128 | else: 1129 | operand, addr, length = self.get_operand(addrmode, opcode, operand8, operand16) 1130 | self.set_c(operand & 0x01) 1131 | 1132 | result = (operand >> 1) & 0xff 1133 | self.pc += length - 1 1134 | self.make_flags_nz(result) 1135 | self.memory_map.Write(addr, result) 1136 | return ("w", addr) 1137 | 1138 | # Instruction NOP 1139 | # EA nop 1140 | def instr_nop(self, addrmode, opcode, operand8, operand16): 1141 | return None 1142 | 1143 | # Instruction ORA 1144 | # 09 55 ora #$55 1145 | # 05 20 ora $20 1146 | # 15 20 ora $20,X 1147 | # 0D 33 22 ora $2233 1148 | # 1D 33 22 ora $2233,X 1149 | # 19 33 22 ora $2233,Y 1150 | # 01 20 ora ($20,X) 1151 | # 11 20 ora ($20),Y 1152 | # 12 20 ora ($20) 1153 | def instr_ora(self, addrmode, opcode, operand8, operand16): 1154 | operand, addr, length = self.get_operand(addrmode, opcode, operand8, operand16) 1155 | result = (operand | self.a) 1156 | self.a = result 1157 | self.make_flags_nz(result) 1158 | self.pc += length - 1 1159 | return None 1160 | 1161 | # Instruction PHA and other Pxx stack instructions 1162 | # 08 php 1163 | # 28 plp 1164 | # 48 pha 1165 | # DA phx 1166 | # 5A phy 1167 | # 68 pla 1168 | # FA plx 1169 | # 7A ply 1170 | def instr_php(self, addrmode, opcode, operand8, operand16): 1171 | self.memory_map.Write(0x100 + self.sp, self.cc) 1172 | if self.sp: 1173 | self.sp = self.sp - 1 1174 | else: 1175 | self.sp = 0xff 1176 | return ("stack", self.sp) 1177 | 1178 | def instr_pha(self, addrmode, opcode, operand8, operand16): 1179 | self.memory_map.Write(0x100 + self.sp, self.a) 1180 | if self.sp: 1181 | self.sp = self.sp - 1 1182 | else: 1183 | self.sp = 0xff 1184 | return ("stack", self.sp) 1185 | 1186 | def instr_phx(self, addrmode, opcode, operand8, operand16): 1187 | self.memory_map.Write(0x100 + self.sp, self.x) 1188 | if self.sp: 1189 | self.sp = self.sp - 1 1190 | else: 1191 | self.sp = 0xff 1192 | return ("stack", self.sp) 1193 | 1194 | def instr_phy(self, addrmode, opcode, operand8, operand16): 1195 | self.memory_map.Write(0x100 + self.sp, self.y) 1196 | if self.sp: 1197 | self.sp = self.sp - 1 1198 | else: 1199 | self.sp = 0xff 1200 | return ("stack", self.sp) 1201 | 1202 | def instr_plp(self, addrmode, opcode, operand8, operand16): 1203 | self.sp = (self.sp + 1) % 256 1204 | self.cc = self.memory_map.Read(0x100 + self.sp) 1205 | return ("stack", self.sp) 1206 | 1207 | def instr_pla(self, addrmode, opcode, operand8, operand16): 1208 | self.sp = (self.sp + 1) % 256 1209 | self.a = self.memory_map.Read(0x100 + self.sp) 1210 | return ("stack", self.sp) 1211 | 1212 | def instr_plx(self, addrmode, opcode, operand8, operand16): 1213 | self.sp = (self.sp + 1) % 256 1214 | self.x = self.memory_map.Read(0x100 + self.sp) 1215 | return ("stack", self.sp) 1216 | 1217 | def instr_ply(self, addrmode, opcode, operand8, operand16): 1218 | self.sp = (self.sp + 1) % 256 1219 | self.y = self.memory_map.Read(0x100 + self.sp) 1220 | return ("stack", self.sp) 1221 | 1222 | # Instruction ROL 1223 | # 2A rol A 1224 | # 26 20 rol $20 1225 | # 36 20 rol $20,X 1226 | # 2E 33 22 rol $2233 1227 | # 3E 33 22 rol $2233,X 1228 | def instr_rol(self, addrmode, opcode, operand8, operand16): 1229 | if (addrmode == "accumulator"): 1230 | carryout = self.a & 0x80 1231 | carryin = self.cc & Flags.CARRY 1232 | 1233 | result = ((self.a << 1) & 0xff) | carryin 1234 | self.a = result 1235 | self.set_c(carryout) 1236 | self.make_flags_nz(result) 1237 | return None 1238 | else: 1239 | operand, addr, length = self.get_operand(addrmode, opcode, operand8, operand16) 1240 | 1241 | carryout = (operand & 0x80) 1242 | carryin = self.cc & Flags.CARRY 1243 | 1244 | result = ((operand << 1) & 0xff) | carryin 1245 | self.set_c(carryout) 1246 | self.memory_map.Write(addr, result) 1247 | self.pc += length - 1 1248 | self.make_flags_nz(result) 1249 | return ("w", addr) 1250 | 1251 | # Instruction ROR 1252 | # 6A ror A 1253 | # 66 20 ror $20 1254 | # 76 20 ror $20,X 1255 | # 6E 33 22 ror $2233 1256 | # 7E 33 22 ror $2233,X 1257 | def instr_ror(self, addrmode, opcode, operand8, operand16): 1258 | if (addrmode == "accumulator"): 1259 | if self.cc & Flags.CARRY: 1260 | carry = 0x80 1261 | else: 1262 | carry = 0 1263 | carryout = self.a & 0x01 1264 | 1265 | result = (self.a >> 1) | carry 1266 | self.a = result 1267 | self.set_c(carryout) 1268 | self.make_flags_nz(result) 1269 | return None 1270 | else: 1271 | operand, addr, length = self.get_operand(addrmode, opcode, operand8, operand16) 1272 | if self.cc & Flags.CARRY: 1273 | carry = 0x80 1274 | else: 1275 | carry = 0 1276 | carryout = operand & 0x01 1277 | 1278 | result = ((operand >> 1) % 256) | carry 1279 | self.memory_map.Write(addr, result) 1280 | self.set_c(carryout) 1281 | self.pc += length - 1 1282 | self.make_flags_nz(result) 1283 | return ("w", addr) 1284 | 1285 | # Instruction RTI 1286 | 1287 | # 40 rti 1288 | def instr_rti(self, addrmode, opcode, operand8, operand16): 1289 | self.cc = self.pull() 1290 | self.pc = self.pulladdr() 1291 | self.set_b(True) 1292 | self.set_s(True) 1293 | return ("stack", self.sp) 1294 | 1295 | # Instruction RTS 1296 | # 60 rts 1297 | def instr_rts(self, addrmode, opcode, operand8, operand16): 1298 | self.pc = (self.pulladdr() + 1) % 0x10000 1299 | return ("stack", self.sp) 1300 | 1301 | # Instruction SBC 1302 | 1303 | # E9 55 sbc #$55 1304 | # E5 20 sbc $20 1305 | # F5 20 sbc $20,X 1306 | # ED 33 22 sbc $2233 1307 | # FD 33 22 sbc $2233,X 1308 | # F9 33 22 sbc $2233,Y 1309 | # E1 20 sbc ($20,X) 1310 | # F1 20 sbc ($20),Y 1311 | # F2 20 sbc ($20) 1312 | def instr_sbc(self, addrmode, opcode, operand8, operand16): 1313 | carryin = not (self.cc & Flags.CARRY) 1314 | 1315 | # Get the operand based on the address mode 1316 | operand, addr, length = self.get_operand(addrmode, opcode, operand8, operand16) 1317 | 1318 | # Do the subtract 1319 | # Compute the carry 1320 | # Put the result in A 1321 | # Compute the flags 1322 | 1323 | if self.cc & Flags.DECIMAL: 1324 | a_10s = ((self.a & 0xf0) >> 4) * 10 1325 | a_1s = (self.a & 0xf) 1326 | operand_10s = ((operand & 0xf0) >> 4) * 10 1327 | operand_1s = (operand & 0xf) 1328 | 1329 | if (a_10s >= 100 or a_1s >= 10 or operand_10s >= 100 or operand_1s >= 10): 1330 | raise ValueError("Invalid BCD argument not supported") 1331 | diff = (a_10s + a_1s - operand_10s - operand_1s - carryin) 1332 | carryout = diff < 0 1333 | 1334 | diff_1s = diff % 10 1335 | diff_10s = (diff % 100 - diff_1s)/10 1336 | 1337 | result = diff_10s * 16 + diff_1s 1338 | else: 1339 | result = (self.a - operand - carryin) 1340 | carryout = result < 0 1341 | result = result % 256 1342 | 1343 | self.set_c(not carryout) 1344 | 1345 | self.a = result 1346 | self.make_flags_nz(result) 1347 | self.make_flags_v(self.a, operand, carryin, result, carryout) 1348 | self.pc += length - 1 1349 | return None 1350 | 1351 | # Instruction SEC 1352 | # 38 sec 1353 | def instr_sec(self, addrmode, opcode, operand8, operand16): 1354 | self.set_c(True) 1355 | return None 1356 | 1357 | # Instruction SED 1358 | # F8 sed 1359 | def instr_sed(self, addrmode, opcode, operand8, operand16): 1360 | self.set_d(True) 1361 | return None 1362 | 1363 | # Instruction SEI 1364 | # 78 sei 1365 | def instr_sei(self, addrmode, opcode, operand8, operand16): 1366 | self.set_i(True) 1367 | return None 1368 | 1369 | # Instruction STA 1370 | # 85 20 sta $20 1371 | # 95 20 sta $20,X 1372 | # 8D 33 22 sta $2233 1373 | # 9D 33 22 sta $2233,X 1374 | # 99 33 22 sta $2233,Y 1375 | # 81 20 sta ($20,X) 1376 | # 91 20 sta ($20),Y 1377 | # 92 20 sta ($20) 1378 | def instr_sta(self, addrmode, opcode, operand8, operand16): 1379 | operand, addr, length = self.get_operand(addrmode, opcode, operand8, operand16) 1380 | self.memory_map.Write(addr, self.a) 1381 | self.pc += length - 1 1382 | return ("w", addr) 1383 | 1384 | # Instruction STX 1385 | # 86 20 stx $20 1386 | # 96 20 stx $20,Y 1387 | # 8E 33 22 stx $2233 1388 | def instr_stx(self, addrmode, opcode, operand8, operand16): 1389 | operand, addr, length = self.get_operand(addrmode, opcode, operand8, operand16) 1390 | self.memory_map.Write(addr, self.x) 1391 | self.pc += length - 1 1392 | return ("w", addr) 1393 | 1394 | # Instruction STY 1395 | # 84 20 sty $20 1396 | # 94 20 sty $20,X 1397 | # 8C 33 22 sty $2233 1398 | def instr_sty(self, addrmode, opcode, operand8, operand16): 1399 | operand, addr, length = self.get_operand(addrmode, opcode, operand8, operand16) 1400 | self.memory_map.Write(addr, self.y) 1401 | self.pc += length - 1 1402 | return ("w", addr) 1403 | 1404 | # Instruction STZ 1405 | # 64 20 stz $20 1406 | # 74 20 stz $20,X 1407 | # 9C 33 22 stz $2233 1408 | # 9E 33 22 stz $2233,X 1409 | def instr_stz(self, addrmode, opcode, operand8, operand16): 1410 | operand, addr, length = self.get_operand(addrmode, opcode, operand8, operand16) 1411 | self.memory_map.Write(addr, 0x00) 1412 | self.pc += length - 1 1413 | return ("w", addr) 1414 | 1415 | # Instruction TAX 1416 | # AA tax 1417 | def instr_tax(self, addrmode, opcode, operand8, operand16): 1418 | self.x = self.a 1419 | self.make_flags_nz(self.a) 1420 | return None 1421 | 1422 | # Instruction TAY 1423 | # A8 tay 1424 | def instr_tay(self, addrmode, opcode, operand8, operand16): 1425 | self.y = self.a 1426 | self.make_flags_nz(self.a) 1427 | return None 1428 | 1429 | # Instruction TRB 1430 | # 14 20 trb $20 1431 | # 1C 33 22 trb $2233 1432 | def instr_trb(self, addrmode, opcode, operand8, operand16): 1433 | operand, addr, length = self.get_operand(addrmode, opcode, operand8, operand16) 1434 | result = operand & (self.a ^ 0xff) 1435 | self.memory_map.Write(addr, result) 1436 | self.set_z((operand & self.a) == 0x00) 1437 | self.pc += length - 1 1438 | return ("w", addr) 1439 | 1440 | # Instruction TSB 1441 | # 04 20 tsb $20 1442 | # 0C 33 22 tsb $2233 1443 | def instr_tsb(self, addrmode, opcode, operand8, operand16): 1444 | operand, addr, length = self.get_operand(addrmode, opcode, operand8, operand16) 1445 | result = operand | self.a 1446 | self.memory_map.Write(addr, result) 1447 | self.set_z((operand & self.a) == 0x00) 1448 | self.pc += length - 1 1449 | return ("w", addr) 1450 | 1451 | # BA tsx 1452 | def instr_tsx(self, addrmode, opcode, operand8, operand16): 1453 | self.x = self.sp 1454 | self.make_flags_nz(self.sp) 1455 | return None 1456 | 1457 | # 8A txa 1458 | 1459 | def instr_txa(self, addrmode, opcode, operand8, operand16): 1460 | self.a = self.x 1461 | self.make_flags_nz(self.x) 1462 | return None 1463 | 1464 | # 9A txs 1465 | def instr_txs(self, addrmode, opcode, operand8, operand16): 1466 | self.sp = self.x 1467 | return None 1468 | 1469 | # 98 tya 1470 | def instr_tya(self, addrmode, opcode, operand8, operand16): 1471 | self.a = self.y 1472 | self.make_flags_nz(self.y) 1473 | return None 1474 | -------------------------------------------------------------------------------- /src/small_example.py: -------------------------------------------------------------------------------- 1 | # This shows how to simply feed in straight text 2 | # by passing it through splitlines() 3 | 4 | from asm6502 import asm6502 5 | 6 | thecode = """ 7 | ORG $100 8 | start: 9 | LDA #$10 10 | LDX #$00 11 | loop: 12 | STA $1000,x 13 | INX 14 | SBC #$01 15 | BPL loop 16 | RTS 17 | """ 18 | 19 | lines = thecode.splitlines() 20 | 21 | a = asm6502() 22 | (l,s) = a.assemble(lines) 23 | for line in l: 24 | print(line) 25 | print() 26 | for line in s: 27 | print(line) 28 | 29 | -------------------------------------------------------------------------------- /src/termbox_util.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- encoding: utf-8 -*- 3 | 4 | import termbox 5 | import inspect 6 | from inspect import currentframe, getframeinfo 7 | 8 | cf = currentframe() 9 | filename = getframeinfo(cf).filename 10 | 11 | import logging 12 | 13 | # Implements a virtual screen of arbitary size. 14 | # A rectangular view or views of a chosen size can be displayed on 15 | # the real view. 16 | # Handy for scrolling lists and things like that. Just draw the whole 17 | # list then move the view window around. 18 | # Implements the same drawing functions. Viewplanes in viewplanes are allowed. 19 | # Just call a_viewplane.draw_viewplane(sub_viewplane,...) 20 | # a_termbox_inst.draw_viewplane(a_viewplane,...) 21 | # 22 | # It doesn't implement getevent. 23 | # It has a resize function that termbox doesn't. 24 | # 25 | # Copyright (c) 2015 26 | # David Johnston 27 | # dj@deadhat.com 28 | 29 | class viewplane(object): 30 | def __init__(self,width,height,fg=termbox.WHITE,bg=termbox.BLACK): 31 | self.iwidth = width 32 | self.iheight = height 33 | 34 | self.mk_blanklines(fg=fg,bg=bg) 35 | #self.persistent_vp_list=list() 36 | 37 | def width(self): 38 | return self.iwidth 39 | 40 | def height(self): 41 | return self.iheight 42 | 43 | def mk_blanklines(self,fg=termbox.WHITE,bg=termbox.BLACK): 44 | self.chars = list() 45 | self.fgs = list() 46 | self.bgs = list() 47 | self.fgline=list() 48 | self.bgline=list() 49 | 50 | self.blankline = list() 51 | for c in range(self.iwidth): 52 | self.blankline.append(ord(u' ')) 53 | self.fgline.append(fg) 54 | self.bgline.append(bg) 55 | 56 | for c in range(self.iheight): 57 | self.chars.append(self.blankline[:]) 58 | self.fgs.append(self.fgline[:]) 59 | self.bgs.append(self.bgline[:]) 60 | 61 | def getmaxxy(self): 62 | return (self.iwidth-1, self.iheight-1) 63 | 64 | def getmaxyx(self): 65 | return (self.iheight-1, self.iwidth-1) 66 | 67 | def change_cell(self,x,y,ch,fg=termbox.WHITE,bg=termbox.BLACK): 68 | if (x > -1) and (x < self.iwidth) and (y > -1) and (y < self.iheight): 69 | self.chars[y][x]=ch 70 | self.bgs[y][x]=bg 71 | self.fgs[y][x]=fg 72 | 73 | def clear(self,fg=termbox.WHITE,bg=termbox.BLACK): 74 | self.mk_blanklines(fg=fg,bg=bg) 75 | 76 | def resize(self,width,height): 77 | if height < 1 or width < 1: 78 | return 79 | 80 | if height < self.iheight: 81 | self.chars=self.chars[:height] 82 | self.fgs=self.fgs[:height] 83 | self.bgs=self.bgs[:height] 84 | self.iheight = height 85 | 86 | if height > self.iheight: 87 | for i in range(height-self.iheight): 88 | self.chars.append(self.blankline) 89 | self.fgs.append(self.fgline) 90 | self.bgs.append(self.bgline) 91 | 92 | self.iheight = height 93 | 94 | # Trim off the ends if shrinking width. 95 | if width < self.iwidth: 96 | self.fgline = self.fgline[:width] 97 | self.bgline = self.bgline[:width] 98 | for i in range(self.iheight): 99 | self.chars[i]=self.chars[i][:width] 100 | self.fgs[i] = self.fgs[i][:width] 101 | self.bgs[i] = self.bgs[i][:width] 102 | self.iwidth=width 103 | 104 | # Add blanks to ends of lines if increasing width 105 | if width > self.width: 106 | # First increase the length of the blank lines 107 | self.fgline=list() 108 | self.bgline=list() 109 | self.blankline = list() 110 | for c in range(width): 111 | self.blankline.append(ord(u' ')) 112 | self.fgline.append(fg) 113 | self.bgline.append(bg) 114 | 115 | # Then tack blanks on the ends. 116 | for i in range(self.iheight): 117 | self.chars[i].join(self.blankline[self.iwidth:width]) 118 | self.fgs[i].join(self.fgline[self.iwidth:width]) 119 | self.bgs[i].join(self.bgline[self.iwidth:width]) 120 | 121 | self.iwidth = width 122 | 123 | #def present(self): 124 | # for pvp in self.persistent_vp_list: 125 | # (vp,width,height,srcx,srcy,viewx,viewy,active) = pvp 126 | # if active: 127 | # self.draw_viewplane_window(vp,width,height,srcx,srcy,viewx,viewy) 128 | 129 | # The utility functions that operate over a termbox or viewplane 130 | # 131 | class termbox_util(): 132 | key_up = 1 133 | key_down = 2 134 | key_left = 3 135 | key_right = 4 136 | key_escape = 5 137 | key_return = 6 138 | key_delete = 7 139 | key_backspace = 8 140 | key_tab = 9 141 | TB_KEY_F1 = (0xFFFF-0) 142 | TB_KEY_F2 = (0xFFFF-1) 143 | TB_KEY_F3 = (0xFFFF-2) 144 | TB_KEY_F4 = (0xFFFF-3) 145 | TB_KEY_F5 = (0xFFFF-4) 146 | TB_KEY_F6 = (0xFFFF-5) 147 | TB_KEY_F7 = (0xFFFF-6) 148 | TB_KEY_F8 = (0xFFFF-7) 149 | TB_KEY_F9 = (0xFFFF-8) 150 | TB_KEY_F10 = (0xFFFF-9) 151 | TB_KEY_F11 = (0xFFFF-10) 152 | TB_KEY_F12 = (0xFFFF-11) 153 | TB_KEY_INSERT = (0xFFFF-12) 154 | TB_KEY_DELETE = (0xFFFF-13) 155 | TB_KEY_HOME = (0xFFFF-14) 156 | TB_KEY_END = (0xFFFF-15) 157 | TB_KEY_PGUP = (0xFFFF-16) 158 | TB_KEY_PGDN = (0xFFFF-17) 159 | TB_KEY_ARROW_UP = (0xFFFF-18) 160 | TB_KEY_ARROW_DOWN = (0xFFFF-19) 161 | TB_KEY_ARROW_LEFT = (0xFFFF-20) 162 | TB_KEY_ARROW_RIGHT = (0xFFFF-21) 163 | TB_KEY_MOUSE_LEFT = (0xFFFF-22) 164 | TB_KEY_MOUSE_RIGHT = (0xFFFF-23) 165 | TB_KEY_MOUSE_MIDDLE = (0xFFFF-24) 166 | TB_KEY_MOUSE_RELEASE = (0xFFFF-25) 167 | TB_KEY_MOUSE_WHEEL_UP = (0xFFFF-26) 168 | TB_KEY_MOUSE_WHEEL_DOWN = (0xFFFF-27) 169 | 170 | def __init__(self,tb): 171 | self.tb = tb 172 | self.fg = termbox.WHITE 173 | self.bg = termbox.BLACK 174 | self.persistent_vp_list=list() # for self displaying viewplanes 175 | 176 | #self.can_input = hasattr(tb, poll_event) and inspect.ismethod(getattr(tb, poll_event)) 177 | 178 | def getmaxyx(self): 179 | x = self.tb.width()-1 180 | y = self.tb.height()-1 181 | self.maxx = x 182 | self.maxy = y 183 | return((y,x)) 184 | 185 | def getmaxxy(self): 186 | x = self.tb.width()-1 187 | y = self.tb.height()-1 188 | self.maxx = x 189 | self.maxy = y 190 | return((x,y)) 191 | 192 | def clear(self): 193 | self.tb.clear() 194 | #self.tb.present() 195 | 196 | def outside(self, x,y): 197 | maxy,maxx = self.getmaxyx() 198 | if ((x < 0) or (y < 0)): 199 | return True 200 | if x > maxx: 201 | return True 202 | if y > maxy: 203 | return True 204 | return False 205 | 206 | def addstr(self, x, y, thestring, bold=False): 207 | maxx,maxy = self.getmaxxy() 208 | 209 | # ignore attempts to place strings outside window boundary 210 | if y < 0 or y > maxy: 211 | return 212 | 213 | if x+len(thestring) < 1: 214 | return 215 | 216 | #truncate string so it doesn't run off edge of screen 217 | maxstringlen = maxx-x+1 218 | if (len(thestring)) > maxstringlen: 219 | thestring = thestring[:maxstringlen] 220 | 221 | # Then shave off the front if it isn't in the screen 222 | if x < 0: 223 | thestring = thestring[-x:] 224 | x = 0 225 | 226 | for i in range(len(thestring)): 227 | if bold: 228 | self.tb.change_cell(x+i, y, ord(thestring[i]), self.bg,self.fg) 229 | else: 230 | if (type(thestring) != str): 231 | cf = currentframe() 232 | lineno = cf.f_lineno 233 | fn = getframeinfo(cf).filename 234 | logging.debug(str("File: %s line: %d , Expecting sting, got %s" % (fn, lineno, thestring))) 235 | self.tb.change_cell(x+i, y, ord(thestring[i]), self.fg,self.bg) 236 | 237 | def hline(self,x1,y1,x2): 238 | # draw a horizontal line from x1,y1 to x2,y1 239 | 240 | #if self.outside(x1,y1): 241 | # return 242 | #if self.outside(x2,y1): 243 | # return 244 | 245 | if x1 > x2: 246 | (x1,x2) = (x2,x1) 247 | for x in range(x1,x2+1): 248 | self.tb.change_cell(x,y1,ord(u'─'),self.fg, self.bg) 249 | 250 | def vline(self,x1,y1,y2): 251 | # draw a vertical line from x1,y1 to x1,y2 252 | 253 | #if self.outside(x1,y1): 254 | # return 255 | #if self.outside(x1,y2): 256 | # return 257 | 258 | if y1 > y2: 259 | (y1,y2) = (y2,y1) 260 | for y in range(y1,y2+1): 261 | self.tb.change_cell(x1,y,ord(u'│'),self.fg, self.bg) 262 | 263 | def fill_area(self,ch,x1=0,y1=0,x2='maxx',y2='maxy',fg='fg', bg='bg'): 264 | # draw a filled rectangle from x1,y1 to x2,y2 265 | (maxx,maxy)=self.getmaxxy() 266 | if x2=='maxx': 267 | x2 = maxx 268 | if y2=='maxy': 269 | y2 = maxy 270 | if fg=='fg': 271 | fg=self.fg 272 | if bg=='bg': 273 | bg=self.bg 274 | 275 | ## Don't draw outside the screen 276 | #if self.outside(x1,y1): 277 | # return 278 | #if self.outside(x2,y2): 279 | # return 280 | 281 | # Switch corners so x1,y1 is top right 282 | if x1 > x2: 283 | (x1,y1,x2,y2) = (x2,y1,x1,y2) 284 | if y1 > x2: 285 | (x1,y1,x2,y2) = (x1,y2,x2,y1) 286 | 287 | width = 1+x2-x1 288 | height = 1+y2-y1 289 | 290 | #FillArea(x, y, w, h) 291 | for y in range(height): 292 | for x in range(width): 293 | self.tb.change_cell(x1+x, y1+y, ord(ch), fg, bg) 294 | 295 | def box(self,x1=0,y1=0,x2='maxx',y2='maxy'): 296 | # draw a box from x1,y1 to x2,y2 297 | (maxx,maxy)=self.getmaxxy() 298 | if x2=='maxx': 299 | x2 = maxx 300 | if y2=='maxy': 301 | y2 = maxy 302 | # Don't draw outside the screen 303 | #if self.outside(x1,y1): 304 | # return 305 | #if self.outside(x2,y2): 306 | # return 307 | 308 | # Switch corners so x1,y1 is top right 309 | if x1 > x2: 310 | (x1,y1,x2,y2) = (x2,y1,x1,y2) 311 | if y1 > x2: 312 | (x1,y1,x2,y2) = (x1,y2,x2,y1) 313 | 314 | #FillArea(x, y, w, h) 315 | self.hline(x1+1, y1, x2-1) # top 316 | self.hline(x1+1, y2, x2-1) # bottom 317 | self.vline(x1, y1+1, y2-1) # left 318 | self.vline(x2, y1+1, y2-1) # right 319 | self.tb.change_cell(x1, y1, ord(u'┌'), self.fg, self.bg) # top-left corner 320 | self.tb.change_cell(x2, y1, ord(u'┐'), self.fg, self.bg) # top-right corner 321 | self.tb.change_cell(x1, y2, ord(u'└'), self.fg, self.bg) #bottom-left corner 322 | self.tb.change_cell(x2, y2, ord(u'┘'), self.fg, self.bg) # bottom-right corner 323 | 324 | # Just draws a box around the full termbox 325 | # Avoids needing to put in the parameters to box. 326 | def border(self): 327 | #maxx,maxy = self.getmaxxy() 328 | self.box() 329 | 330 | 331 | def draw_viewplane(self,vp,viewx,viewy): 332 | (width,height)=vp.getmaxxy() 333 | width=width+1 334 | height=height+1 335 | 336 | for y in range(height): 337 | for x in range(width): 338 | ch = vp.chars[y][x] 339 | fg = vp.fgs[y][x] 340 | bg = vp.bgs[y][x] 341 | self.tb.change_cell(x+viewx,y+viewy, ch, fg, bg) 342 | 343 | def draw_viewplane_window(self,vp,width,height,srcx,srcy,viewx,viewy): 344 | if width < 1 or height < 1: 345 | return 346 | if width+srcx > vp.width(): 347 | return 348 | if height+srcy > vp.height(): 349 | return 350 | 351 | for line in range(height): 352 | for i in range(width): 353 | ch = vp.chars[line+srcy][i+srcx] 354 | fg = vp.fgs[line+srcy][i+srcx] 355 | bg = vp.bgs[line+srcy][i+srcx] 356 | self.tb.change_cell(viewx+i, viewy+line, ch, fg, bg) 357 | #print str(vp.chars[line+srcy]) 358 | 359 | # The class holds onto a list of viewplane parameters and 360 | # present() displays them automatically. An active field is also added 361 | # which tells present() whether or not to display it. 362 | # So you can set up your viewplanes and work solely within 363 | # viewplanes and activate and deactivate them as needed. 364 | # The add_ method returns a pvid (persistent viewplane id) so it can be 365 | # referenced later. 366 | 367 | def add_persistent_viewplane(self,vp,viewx,viewy,active=True): 368 | (width,height)=vp.getmaxxy() 369 | width=width+1 370 | height=height+1 371 | 372 | pid = len(self.persistent_vp_list) 373 | self.persistent_vp_list.append((vp,width,height,0,0,viewx,viewy,active)) 374 | return pid 375 | 376 | def add_persistent_viewplane_window(self,vp,width,height,srcx,srcy,viewx,viewy,active=True): 377 | abort = 0 378 | if width < 1 or height < 1: 379 | return None 380 | if width+srcx > vp.width(): 381 | return None 382 | if height+srcy > vp.height(): 383 | return None 384 | 385 | pid = len(self.persistent_vp_list) 386 | self.persistent_vp_list.append((vp,width,height,srcx,srcy,viewx,viewy, active)) 387 | return pid 388 | 389 | def move_persistent_viewplane_window(self,pid,new_srcx,new_srcy): 390 | (vp,width,height,srcx,srcy,viewx,viewy, active) = self.persistent_vp_list[pid] 391 | self.persistent_vp_list[pid] = (vp,width,height,new_srcx,new_srcy,viewx,viewy,True) 392 | 393 | def activate_persistent_vp(self,pid): 394 | (vp,width,height,srcx,srcy,viewx,viewy, active) = self.persistent_vp_list[pid] 395 | self.persistent_vp_list[pid] = (vp,width,height,srcx,srcy,viewx,viewy,True) 396 | 397 | def deactivate_persistent_vp(self,pid): 398 | (vp,width,height,srcx,srcy,viewx,viewy, active) = self.persistent_vp_list[pid] 399 | self.persistent_vp_list[pid] = (vp,width,height,srcx,srcy,viewx,viewy,False) 400 | 401 | # Calls the underlaying present() but also draws any active persistent 402 | # viewplanes in the persistent_vp_list() 403 | 404 | def present(self): 405 | for pvp in self.persistent_vp_list: 406 | (vp,width,height,srcx,srcy,viewx,viewy,active) = pvp 407 | if active: 408 | self.draw_viewplane_window(vp,width,height,srcx,srcy,viewx,viewy) 409 | present_method = getattr(self.tb, "present", None) 410 | if callable(present_method): 411 | self.tb.present() 412 | 413 | # Asks the user to press a few keys, to build a dictionary of a key map 414 | 415 | def keymapper(self): 416 | keymap = dict() 417 | eventmap = dict() 418 | 419 | keys = ['key_up','key_down','key_left','key_right','key_escape', 420 | 'key_return','key_delete','key_tab'] 421 | 422 | 423 | self.clear() 424 | self.box() 425 | self.addstr(2,2,"Key Mapper") 426 | 427 | for name in keys: 428 | self.addstr(2,4,"Press %s " % name) 429 | self.tb.present() 430 | event = self.tb.poll_event() 431 | keymap[name] = event 432 | eventmap[event] = name 433 | 434 | i = 0 435 | for akey in keymap: 436 | self.addstr(2,6+i,str(akey)+" = "+str(keymap[akey])) 437 | i = i+1 438 | 439 | return (keymap,eventmap) 440 | 441 | # Implements a line of text that can be edited. 442 | class termbox_editableline(): 443 | def __init__(self,tbinst,tbutil, x,y, width): 444 | self.tbutil = tbutil 445 | self.tbinst = tbinst 446 | self.x = x 447 | self.y = y 448 | self.width = width 449 | self.contents="" 450 | 451 | # Highlights the edited textbox 452 | # Puts a cursor there 453 | # Takes in left-right for moving cursor 454 | # Adds ascii visibles at cursor position 455 | # Delete removes to the left of the cursor 456 | # Calls the validator (provided by caller) to handle the 457 | # inputs. Validator returns: 458 | # 7 (CR) to finish editing. 459 | # Ascii codes for typed characters 460 | # Specials like escape need to be handled by the validator 461 | # E.G. return a 7 and mark that esc was pressed to the 462 | # program can know what to do next. 463 | # 464 | def edit(self,validator, contents="", max_width=None,presenter=None): 465 | original_contents=contents[:] 466 | blankstr = " "*self.width 467 | cursorpos=0 468 | ttype = 0 469 | tkey = 0 470 | windowstart=0 471 | event=" " 472 | if max_width==None: 473 | max_width=self.width 474 | 475 | # 476 | # Keep handling keystrokes until esc or return is pressed. 477 | # 478 | while True: 479 | # Erase what's there 480 | self.tbutil.addstr(self.x,self.y,blankstr,bold=True) 481 | 482 | # Work out what to display 483 | # Taking into account where in the string the window starts 484 | if len(contents)==0: 485 | displaystr=" " 486 | else: 487 | displaystr = contents[windowstart:] 488 | displaystr = displaystr[:self.width] 489 | #self.tbutil.addstr(self.x,self.y,displaystr,bold=True) 490 | 491 | # Then work out where to put the cursor 492 | if (cursorpos == windowstart): 493 | self.tbutil.addstr(self.x,self.y,displaystr[0],bold=False) 494 | self.tbutil.addstr(self.x+1,self.y,displaystr[1:],bold=True) 495 | elif (cursorpos == len(contents)): 496 | self.tbutil.addstr(self.x,self.y,displaystr,bold=True) 497 | self.tbutil.addstr(self.x+len(displaystr),self.y," ",bold=False) 498 | else: 499 | cpos = cursorpos-windowstart 500 | self.tbutil.addstr(self.x,self.y,displaystr[:cpos],bold=True) 501 | try: 502 | self.tbutil.addstr(self.x+cpos,self.y,displaystr[cpos],bold=False) 503 | except: 504 | pass 505 | try: 506 | self.tbutil.addstr(self.x+cpos+1,self.y,displaystr[cpos+1:],bold=True) 507 | except: 508 | pass 509 | try: 510 | self.tbutil.addstr(self.x+len(displaystr),self.y," ",bold=False) 511 | except: 512 | pass 513 | # Diagnostic output 514 | #self.tbutil.addstr(self.x,self.y+26,displaystr[:cpos]+" ",bold=True) 515 | #self.tbutil.addstr(self.x,self.y+27,displaystr[cpos]+" ",bold=False) 516 | #self.tbutil.addstr(self.x,self.y+28,displaystr[cpos+1:]+" ",bold=True) 517 | #self.tbutil.addstr(self.x,self.y+29,str(cpos)+" ",bold=True) 518 | 519 | # More Diagnostic Output 520 | #self.tbutil.addstr(self.x,self.y+1," len(contents)="+str(len(contents))+" cursorpos="+str(cursorpos)+" windstrt="+str(windowstart)+" ") 521 | #self.tbutil.addstr(self.x,self.y+24," contents ="+contents+" ") 522 | #self.tbutil.addstr(self.x,self.y+25," displaystr="+displaystr+" ") 523 | 524 | # Show the screen then wait for input 525 | # 526 | if presenter==None: 527 | self.tbinst.present() 528 | event = self.tbinst.poll_event() 529 | else: 530 | presenter.present() 531 | event = presenter.poll_event() 532 | (ttype, ch, tkey, mod, w, h, x, y ) = event 533 | if event != None: 534 | c = validator(event,contents) 535 | # 536 | # Handle esc, return, delete, up, down, left, right and input characters. 537 | # 538 | 539 | # Return Pressed 540 | if c == 7: 541 | return (contents) 542 | elif (ttype==1) and (tkey == 27): # ESC pressed 543 | return (original_contents) 544 | elif (tkey == self.tbutil.key_left) or (tkey == self.tbutil.TB_KEY_ARROW_LEFT): 545 | #self.tbutil.addstr(self.x,self.y+2," LEFT ") 546 | if cursorpos > 0: 547 | cursorpos = cursorpos - 1 548 | elif (tkey == self.tbutil.key_right) or (tkey == self.tbutil.TB_KEY_ARROW_RIGHT): 549 | #self.tbutil.addstr(self.x,self.y+2," RIGHT ") 550 | if cursorpos < (len(contents)): 551 | cursorpos = cursorpos + 1 552 | elif (ttype==1) and (tkey==127): # delete pressed 553 | if (cursorpos >= len(contents)) and (cursorpos > 0): # cursor is at end. Take one off the end 554 | #self.tbutil.addstr(self.x,self.y+2," DELETE END ") 555 | contents = contents[:-1] 556 | cursorpos -= 1 557 | elif (cursorpos > 0) and (cursorpos < len(contents)): # cursor somewhere in the middle. 558 | #self.tbutil.addstr(self.x,self.y+2," DELETE MID ") 559 | contents = contents[:cursorpos-1] + contents[cursorpos:] 560 | cursorpos -= 1 561 | elif (ttype==1) and (tkey==32): # space pressed 562 | contents = contents[:cursorpos] + " " + contents[cursorpos:] 563 | cursorpos += 1 564 | #self.tbutil.addstr(self.x,self.y+2," ADD SPACE ") 565 | else: 566 | if c != None: 567 | if len(contents) < max_width: 568 | contents = contents[:cursorpos] + str(c) + contents[cursorpos:] 569 | cursorpos += 1 570 | contents = contents[:max_width] 571 | #self.tbutil.addstr(self.x,self.y+2," ADD CHAR ") 572 | 573 | # Move window view 574 | if (cursorpos >= self.width-1): 575 | windowstart = cursorpos+1-self.width 576 | if (cursorpos < windowstart): 577 | windowstart = cursorpos 578 | if windowstart < 0: 579 | windowstart = 0 580 | 581 | def text_validator(e,contents): 582 | (type, ch, key, mod, w, h, x, y ) = e 583 | if type==termbox.EVENT_KEY and key == termbox.KEY_ENTER: 584 | return(7) 585 | else: 586 | return(ch) 587 | 588 | def integer_validator(e,contents): 589 | (type, ch, key, mod, w, h, x, y ) = e 590 | if (type==1 and (ch in "0123456789")): 591 | return(ch) 592 | elif type==termbox.EVENT_KEY and key == termbox.KEY_ENTER: 593 | return(7) 594 | else: 595 | return(ch) 596 | 597 | def hex_validator(e,contents): 598 | (type, ch, key, mod, w, h, x, y ) = e 599 | if (ch != None): 600 | if (type==1 and (ch in "ABCDEFabcdef0123456789")): 601 | return(ch) 602 | elif type==termbox.EVENT_KEY and key == termbox.KEY_ENTER: 603 | return(7) 604 | else: 605 | return(ch) 606 | 607 | def decimal_validator(e,contents): 608 | (type, ch, key, mod, w, h, x, y ) = e 609 | if type==termbox.EVENT_KEY and key == termbox.KEY_ENTER: 610 | return(7) 611 | else: 612 | return(ch) 613 | 614 | 615 | -------------------------------------------------------------------------------- /src/test6502.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import asm6502 4 | import dis6502 5 | import sim6502 6 | 7 | 8 | def main(debug=0): 9 | assembly_src = """ 10 | org $0000 11 | JMP overthere 12 | bhaddr: dw &brkhandler,$0102,&another,&labels ; insert address for brkhandler 13 | l8addr: dw &land8 14 | overthere: 15 | LDA bhaddr 16 | LDX 1 17 | STA $FFFE 18 | LDA bhaddr,x 19 | STA $FFFF 20 | LDA #$10 21 | ADC #$55 22 | ADC $20 23 | ADC $20,X 24 | ADC $0002 25 | ADC $0010,X 26 | ADC $0008,Y 27 | ADC ($20,X) 28 | ADC ($20),Y 29 | another: ADC ($20) 30 | AND #$55 31 | AND $20 32 | AND $20,X 33 | AND $0004 34 | labels: 35 | AND $0010,X 36 | AND $0012,Y 37 | AND ($20,X) 38 | AND ($20),Y 39 | AND ($20) 40 | ASL A 41 | ASL $20 42 | ASL $20,X 43 | ASL $2233 44 | ASL $2233,X 45 | CLC 46 | BCC land1 47 | CLC 48 | land1: 49 | SEC 50 | BCS land2 51 | land2: 52 | BEQ land3 53 | land3: 54 | BIT #$55 55 | BIT $20 56 | BIT $20,X 57 | BIT $2233 58 | BIT $2233,X 59 | BMI land4 60 | land4: 61 | BNE land5 62 | land5: 63 | BPL land6 64 | land6: 65 | BRA land7 66 | land7: 67 | LDA l8addr 68 | STA $fffe 69 | LDX #$01 70 | LDA l8addr,x 71 | STA $ffff 72 | BRK 73 | BVC land8 74 | land8: 75 | BVS land9 76 | land9: 77 | CLC 78 | CLD 79 | CLI 80 | CLV 81 | CMP #$55 82 | CMP $20 83 | CMP $20 84 | CMP $2233 85 | CMP $2233,X 86 | CMP $2233,Y 87 | CMP ($20,X) 88 | CMP ($20),Y 89 | CMP ($20) 90 | CPX #$55 91 | CPX $20 92 | CPX $2233 93 | CPY #$55 94 | CPY $20 95 | CPY $2233 96 | DEA 97 | DEC A 98 | DEC $20 99 | DEC $20,X 100 | DEC $2233 101 | DEC $2233,X 102 | DEX 103 | DEY 104 | EOR #$55 105 | EOR $20 106 | EOR $20,X 107 | EOR $2233 108 | EOR $2233,X 109 | EOR $2233,Y 110 | EOR ($20,X) 111 | EOR ($20),Y 112 | EOR ($20) 113 | INA 114 | INC A 115 | INC $20 116 | INC $20,X 117 | INC $2233 118 | INC $2233,X 119 | INX 120 | INY 121 | JMP jmp1 122 | jmpa: dw &jmp2 123 | dw &jmp3 124 | jmp1: 125 | JMP (jmpa) 126 | jmp2: 127 | ldx #$02 128 | JMP (jmpa,X) 129 | jmp3: 130 | JSR jsrhandler 131 | LDA #$55 132 | LDA $20 133 | LDA $20,X 134 | LDA $2233 135 | LDA $2233,X 136 | LDA $2233,Y 137 | LDA ($20,X) 138 | LDA ($20),Y 139 | LDA ($20) 140 | LDX #$55 141 | LDX $20 142 | LDX $20,Y 143 | LDX $2233 144 | LDX $2233,Y 145 | LDY #$55 146 | LDY $20 147 | LDY $20,X 148 | LDY $2233 149 | LDY $2233,X 150 | LSR A 151 | LSR $20 152 | LSR $20,X 153 | LSR $2233 154 | LSR $2233,X 155 | NOP 156 | ORA #$55 157 | ORA $20 158 | ORA $20,X 159 | ORA $2233 160 | ORA $2233,X 161 | ORA $2233,Y 162 | ORA ($20,X) 163 | ORA ($20),Y 164 | ORA ($20) 165 | PHA 166 | PHP 167 | PLP 168 | PLX 169 | PLY 170 | ROL A 171 | ROL $20 172 | ROL $20,X 173 | ROL $2233 174 | ROL $2233,X 175 | ROR A 176 | ROR $20 177 | ROR $20,X 178 | ROR $2233 179 | ROR $2233,X 180 | SBC #$55 181 | SBC $20 182 | SBC $20,X 183 | SBC $2233 184 | SBC $2233,X 185 | SBC $2233,Y 186 | SBC ($20,X) 187 | SBC ($20),Y 188 | SBC ($20) 189 | SEC 190 | SED 191 | SEI 192 | STA $20 193 | STA $20,X 194 | STA $2233 195 | STA $2233,X 196 | STA $2233,Y 197 | STA ($20,X) 198 | STA ($20),Y 199 | STA ($20) 200 | STX $20 201 | STX $20,Y 202 | STX $2233 203 | STY $20 204 | STY $20,X 205 | STY $2233 206 | STZ $20 207 | STZ $20,X 208 | STZ $2233 209 | STZ $2233,X 210 | TAX 211 | TAY 212 | TRB $20 213 | TRB $2233 214 | TSB $20 215 | TSB $2233 216 | TSX 217 | TXA 218 | TXS 219 | TYA 220 | ; A remark 221 | JMP $1000 222 | jsrhandler: 223 | nop 224 | nop 225 | rts 226 | brkhandler: 227 | NOP 228 | NOP 229 | RTI 230 | 231 | ORG $1000 232 | start: lda #$50 233 | sta $5000 ; blah 234 | sta $25 235 | clc 236 | ROR A 237 | adc #%10011010 238 | sta %0101101000111100 239 | sta %00111100 240 | lda ($20) 241 | adc $10,x 242 | middle:ldx $20,y 243 | adc $3000,x 244 | adc $3000,y 245 | adc ($40,x) 246 | adc ($40),y 247 | jmp $2000 248 | nop 249 | nop 250 | label: 251 | nop 252 | org $2000 253 | vals: db @10,$aa,8,$cc,$dd 254 | be 255 | dw $1020,$3040 256 | le 257 | dw $1020,$3040 258 | ddw $1020,$3040 259 | dqw $1020,$3040 260 | adc start 261 | adc ($40) 262 | end: bpl vals 263 | db $aa,$bb,$cc,$dd 264 | nop 265 | org $fffc 266 | db $00,$00,$00,$00 267 | """ 268 | lines = assembly_src.splitlines() 269 | 270 | # Instantiate the assembler, assemble the code, grab the object_code 271 | a = asm6502.asm6502(debug=debug) 272 | (listing, symbols) = a.assemble(lines) 273 | 274 | for line in listing: 275 | print(line) 276 | print() 277 | for line in symbols: 278 | print(line) 279 | print() 280 | 281 | object_code = a.object_code[:] 282 | 283 | print() 284 | # Output IntelHex 285 | print("IntelHex") 286 | a.print_intelhex() 287 | 288 | # Output Srecords 289 | print() 290 | print("SRECORDS") 291 | a.print_srecords(1, 1, "ModuleName", "Comment") 292 | print() 293 | 294 | # Instantiate the simulator 295 | s = sim6502.sim6502(object_code, symbols=a.symbols) 296 | 297 | # Instantiate the disassembler 298 | d = dis6502.dis6502(object_code, symbols=a.symbols) 299 | 300 | # How much space to accomodate the disassembly 301 | status_indent = 39 302 | 303 | # Reset the state of the simulator 304 | s.reset() 305 | 306 | print() 307 | print("SIMULATION START") 308 | print() 309 | # Print a header for the simulator/disassembler output 310 | print("LABEL ADDR HEX INSTR".ljust(status_indent) + " PC A X Y SP Status") 311 | 312 | # Print the initial state 313 | print(" ".ljust(status_indent) + " %04x %02x %02x %02x %04x %02x" % (s.pc, s.a, s.x, s.y, s.sp, s.cc)) 314 | 315 | # Execute 200 instructions 316 | for i in range(200): 317 | # Disassemble the current instruction 318 | distxt, _ = d.disassemble_line(s.pc) 319 | 320 | # Execute that instruction 321 | s.execute() 322 | 323 | # Print out the disassembled instruction followed by the simulator state 324 | print(distxt.ljust(status_indent) + " %04x %02x %02x %02x %04x %02x" % (s.pc, s.a, s.x, s.y, s.sp, s.cc)) 325 | 326 | print(s.memory_map.Dump()) 327 | 328 | if __name__ == "__main__": 329 | main() -------------------------------------------------------------------------------- /src/test_all_instructions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from asm6502 import asm6502 4 | 5 | def go(debug=0): 6 | lines = list() 7 | lines.append(" ORG $100") 8 | lines.append(" ADC #$55 ") 9 | lines.append(" ADC $20 ") 10 | lines.append(" ADC $20,X ") 11 | lines.append(" ADC $2233 ") 12 | lines.append(" ADC $2233,X ") 13 | lines.append(" ADC $2233,Y ") 14 | lines.append(" ADC ($20,X) ") 15 | lines.append(" ADC ($20),Y ") 16 | lines.append(" ADC ($20) ") 17 | lines.append(" AND #$55 ") 18 | lines.append(" AND $20 ") 19 | lines.append(" AND $20,X ") 20 | lines.append(" AND $2233 ") 21 | lines.append(" AND $2233,X ") 22 | lines.append(" AND $2233,Y ") 23 | lines.append(" AND ($20,X) ") 24 | lines.append(" AND ($20),Y ") 25 | lines.append(" AND ($20) ") 26 | lines.append(" ASL A ") 27 | lines.append(" ASL $20 ") 28 | lines.append(" ASL $20,X ") 29 | lines.append(" ASL $2233 ") 30 | lines.append(" ASL $2233,X ") 31 | lines.append(" BCC $55 ") 32 | lines.append(" BCS $55 ") 33 | lines.append(" BEQ $55 ") 34 | lines.append(" BIT #$55 ") 35 | lines.append(" BIT $20 ") 36 | lines.append(" BIT $20,X ") 37 | lines.append(" BIT $2233 ") 38 | lines.append(" BIT $2233,X ") 39 | lines.append(" BMI $55 ") 40 | lines.append(" BNE $55 ") 41 | lines.append(" BPL $55 ") 42 | lines.append(" BRA $55 ") 43 | lines.append(" BRK ") 44 | lines.append(" BVC $55 ") 45 | lines.append(" BVS $55 ") 46 | lines.append(" CLC ") 47 | lines.append(" CLD ") 48 | lines.append(" CLI ") 49 | lines.append(" CLV ") 50 | lines.append(" CMP #$55 ") 51 | lines.append(" CMP $20 ") 52 | lines.append(" CMP $20 ") 53 | lines.append(" CMP $2233 ") 54 | lines.append(" CMP $2233,X ") 55 | lines.append(" CMP $2233,Y ") 56 | lines.append(" CMP ($20,X) ") 57 | lines.append(" CMP ($20),Y ") 58 | lines.append(" CMP ($20) ") 59 | lines.append(" CPX #$55 ") 60 | lines.append(" CPX $20 ") 61 | lines.append(" CPX $2233 ") 62 | lines.append(" CPY #$55 ") 63 | lines.append(" CPY $20 ") 64 | lines.append(" CPY $2233 ") 65 | lines.append(" DEA ") 66 | lines.append(" DEC A ") 67 | lines.append(" DEC $20 ") 68 | lines.append(" DEC $20,X ") 69 | lines.append(" DEC $2233 ") 70 | lines.append(" DEC $2233,X ") 71 | lines.append(" DEX ") 72 | lines.append(" DEY ") 73 | lines.append(" EOR #$55 ") 74 | lines.append(" EOR $20 ") 75 | lines.append(" EOR $20,X ") 76 | lines.append(" EOR $2233 ") 77 | lines.append(" EOR $2233,X ") 78 | lines.append(" EOR $2233,Y ") 79 | lines.append(" EOR ($20,X) ") 80 | lines.append(" EOR ($20),Y ") 81 | lines.append(" EOR ($20) ") 82 | lines.append(" INA") 83 | lines.append(" INC A ") 84 | lines.append(" INC $20 ") 85 | lines.append(" INC $20,X ") 86 | lines.append(" INC $2233 ") 87 | lines.append(" INC $2233,X ") 88 | lines.append(" INX ") 89 | lines.append(" INY ") 90 | lines.append(" JMP $2233 ") 91 | lines.append(" JMP ($2233) ") 92 | lines.append(" JMP ($2233,X) ") 93 | lines.append(" JSR $2233 ") 94 | lines.append(" LDA #$55 ") 95 | lines.append(" LDA $20 ") 96 | lines.append(" LDA $20,X ") 97 | lines.append(" LDA $2233 ") 98 | lines.append(" LDA $2233,X ") 99 | lines.append(" LDA $2233,Y ") 100 | lines.append(" LDA ($20,X) ") 101 | lines.append(" LDA ($20),Y ") 102 | lines.append(" LDA ($20) ") 103 | lines.append(" LDX #$55 ") 104 | lines.append(" LDX $20 ") 105 | lines.append(" LDX $20,Y ") 106 | lines.append(" LDX $2233 ") 107 | lines.append(" LDX $2233,Y ") 108 | lines.append(" LDY #$55 ") 109 | lines.append(" LDY $20 ") 110 | lines.append(" LDY $20,X ") 111 | lines.append(" LDY $2233 ") 112 | lines.append(" LDY $2233,X ") 113 | lines.append(" LSR A ") 114 | lines.append(" LSR $20 ") 115 | lines.append(" LSR $20,X ") 116 | lines.append(" LSR $2233 ") 117 | lines.append(" LSR $2233,X ") 118 | lines.append(" NOP ") 119 | lines.append(" ORA #$55 ") 120 | lines.append(" ORA $20 ") 121 | lines.append(" ORA $20,X ") 122 | lines.append(" ORA $2233 ") 123 | lines.append(" ORA $2233,X ") 124 | lines.append(" ORA $2233,Y ") 125 | lines.append(" ORA ($20,X) ") 126 | lines.append(" ORA ($20),Y ") 127 | lines.append(" ORA ($20) ") 128 | lines.append(" PHA ") 129 | lines.append(" PHX ") 130 | lines.append(" PHY ") 131 | lines.append(" PLA ") 132 | lines.append(" PLX ") 133 | lines.append(" PLY ") 134 | lines.append(" ROL A ") 135 | lines.append(" ROL $20 ") 136 | lines.append(" ROL $20,X ") 137 | lines.append(" ROL $2233 ") 138 | lines.append(" ROL $2233,X ") 139 | lines.append(" ROR A ") 140 | lines.append(" ROR $20 ") 141 | lines.append(" ROR $20,X ") 142 | lines.append(" ROR $2233 ") 143 | lines.append(" ROR $2233,X ") 144 | lines.append(" RTI ") 145 | lines.append(" RTS ") 146 | lines.append(" SBC #$55 ") 147 | lines.append(" SBC $20 ") 148 | lines.append(" SBC $20,X ") 149 | lines.append(" SBC $2233 ") 150 | lines.append(" SBC $2233,X ") 151 | lines.append(" SBC $2233,Y ") 152 | lines.append(" SBC ($20,X) ") 153 | lines.append(" SBC ($20),Y ") 154 | lines.append(" SBC ($20) ") 155 | lines.append(" SEC ") 156 | lines.append(" SED ") 157 | lines.append(" SEI ") 158 | lines.append(" STA $20 ") 159 | lines.append(" STA $20,X ") 160 | lines.append(" STA $2233 ") 161 | lines.append(" STA $2233,X ") 162 | lines.append(" STA $2233,Y ") 163 | lines.append(" STA ($20,X) ") 164 | lines.append(" STA ($20),Y ") 165 | lines.append(" STA ($20) ") 166 | lines.append(" STX $20 ") 167 | lines.append(" STX $20,Y ") 168 | lines.append(" STX $2233 ") 169 | lines.append(" STY $20 ") 170 | lines.append(" STY $20,X ") 171 | lines.append(" STY $2233 ") 172 | lines.append(" STZ $20 ") 173 | lines.append(" STZ $20,X ") 174 | lines.append(" STZ $2233 ") 175 | lines.append(" STZ $2233,X ") 176 | lines.append(" TAX ") 177 | lines.append(" TAY ") 178 | lines.append(" TRB $20 ") 179 | lines.append(" TRB $2233 ") 180 | lines.append(" TSB $20 ") 181 | lines.append(" TSB $2233 ") 182 | lines.append(" TSX ") 183 | lines.append(" TXA ") 184 | lines.append(" TXS ") 185 | lines.append(" TYA") 186 | lines.append("; A remark") 187 | lines.append(" org $1000") 188 | lines.append("start: lda #$50") 189 | lines.append(" sta $5000 ; blah") 190 | lines.append(" sta $25") 191 | lines.append(" clc") 192 | lines.append(" ROR A") 193 | lines.append(" adc #%10011010") 194 | lines.append(" sta %0101101000111100") 195 | lines.append(" sta %00111100") 196 | lines.append(" lda ($20)") 197 | lines.append(" adc $10,x") 198 | lines.append("middle:ldx $20,y") 199 | lines.append(" adc $3000,x") 200 | lines.append(" adc $3000,y") 201 | lines.append(" adc ($40,x) ") 202 | lines.append(" adc ($40),y") 203 | lines.append(" nop") 204 | lines.append(" nop") 205 | lines.append("label:") 206 | lines.append(" nop") 207 | lines.append(" org $3000") 208 | lines.append("vals: db @10,$aa,8,$cc,$dd") 209 | lines.append(" be") 210 | lines.append(" dw $1020,$3040") 211 | lines.append(" le") 212 | lines.append(" dw $1020,$3040") 213 | lines.append(" ddw $1020,$3040") 214 | lines.append(" dqw $1020,$3040") 215 | lines.append(" adc start") 216 | lines.append(" adc ($40)") 217 | lines.append("end: bpl vals") 218 | lines.append(" db $aa,$bb,$cc,$dd") 219 | lines.append(" nop") 220 | 221 | a = asm6502(debug=debug) 222 | (listingtext,symboltext) = a.assemble(lines) 223 | 224 | for line in listingtext: 225 | print(line) 226 | print() 227 | for line in symboltext: 228 | print(line) 229 | 230 | a.print_object_code() 231 | go() 232 | -------------------------------------------------------------------------------- /src/test_assemble_in_two_places.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from asm6502 import asm6502 3 | a = asm6502(debug=0) 4 | lines = [' ORG $1000', ' NOP', ' LDA #$20', 'here: NOP', ' DB 10,11,12,13', ' RTS'] 5 | (listingtext,symboltext)=a.assemble(lines) 6 | lines[0] = ' ORG $2000' 7 | (listingtext2,symboltext2) = a.assemble(lines) 8 | 9 | for line in listingtext: 10 | print(line) 11 | print() 12 | 13 | for line in symboltext: 14 | print(line) 15 | 16 | print() 17 | print("Second assemble at $2000") 18 | for line in listingtext2: 19 | print(line) 20 | print() 21 | 22 | for line in symboltext2: 23 | print(line) 24 | 25 | a.print_object_code() 26 | 27 | #results = a.assemble(lines) 28 | #if results != None: 29 | # (listing_report,object_code_report,symbol_table_report) = results 30 | -------------------------------------------------------------------------------- /src/test_shim.py: -------------------------------------------------------------------------------- 1 | import sim6502 2 | 3 | class Shim6502(object): 4 | """Shim between the API expected by py65's MPU class and sim6502 class""" 5 | 6 | # processor flags 7 | NEGATIVE = 0x80 8 | OVERFLOW = 0x40 9 | UNUSED = 0x20 10 | BREAK = 0x10 11 | DECIMAL = 0x8 12 | INTERRUPT = 0x4 13 | ZERO = 0x2 14 | CARRY = 0x1 15 | 16 | def __init__(self): 17 | self.mpu = sim6502.sim6502() 18 | 19 | def step(self): 20 | self.mpu.execute() 21 | 22 | def reset(self): 23 | self.mpu.reset() 24 | 25 | @property 26 | def memory(self): 27 | return self._memory 28 | 29 | @memory.setter 30 | def memory(self, value): 31 | self.mpu.memory_map.InitializeMemory(0x0, value) 32 | self._memory = MemoryShim(self.mpu.memory_map) 33 | 34 | @property 35 | def p(self): 36 | return self.mpu.cc 37 | 38 | @p.setter 39 | def p(self, value): 40 | self.mpu.cc = value 41 | 42 | @property 43 | def x(self): 44 | return self.mpu.x 45 | 46 | @x.setter 47 | def x(self, value): 48 | self.mpu.x = value 49 | 50 | @property 51 | def y(self): 52 | return self.mpu.y 53 | 54 | @y.setter 55 | def y(self, value): 56 | self.mpu.y = value 57 | 58 | @property 59 | def sp(self): 60 | return self.mpu.sp 61 | 62 | @sp.setter 63 | def sp(self, value): 64 | self.mpu.sp = value 65 | 66 | @property 67 | def a(self): 68 | return self.mpu.a 69 | 70 | @a.setter 71 | def a(self, value): 72 | self.mpu.a = value 73 | 74 | @property 75 | def pc(self): 76 | return self.mpu.pc 77 | 78 | @pc.setter 79 | def pc(self, value): 80 | self.mpu.pc = value 81 | 82 | 83 | class MemoryShim(object): 84 | def __init__(self, memory_map): 85 | self.memory_map = memory_map 86 | 87 | def __getitem__(self, item): 88 | return self.memory_map.Read(item) 89 | 90 | def __setitem__(self, item, value): 91 | # TODO: hack, bypasses tracing 92 | return self.memory_map._memory_map.__setitem__(item, value) 93 | 94 | def __len__(self): 95 | return len(self.memory_map._memory_map) 96 | 97 | -------------------------------------------------------------------------------- /src/test_show_all_opcodes.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import asm6502 4 | 5 | a = asm6502.asm6502() 6 | 7 | print("All the opcodes known by asm6502") 8 | 9 | for b in a.hexcodes: 10 | if (a.hexcodes[b][0]==""): 11 | pass 12 | else: 13 | print("OPCODE=%02X %s %s" % (b,a.hexcodes[b][0], a.hexcodes[b][1])) 14 | 15 | print() 16 | print("Alternative Opcode Names") 17 | for b in a.otherhexcodes: 18 | if (a.otherhexcodes[b][0]==""): 19 | pass 20 | else: 21 | print("OPCODE=%02X %s %s" % (b,a.otherhexcodes[b][0], a.otherhexcodes[b][1])) 22 | --------------------------------------------------------------------------------