|
1 | 1 | go6502
|
2 | 2 | ======
|
3 | 3 |
|
4 |
| -The go6502 package implements a 6502 CPU emulator, assembler, disassembler, |
5 |
| -and debugger. |
6 |
| - |
7 |
| -See http://godoc.org/github.com/beevik/go6502 for the godoc-formatted API |
8 |
| -documentation. |
9 |
| - |
10 |
| -This project is currently under construction and is changing frequently. |
11 |
| - |
12 |
| -### Example |
13 |
| - |
14 |
| -Initialize a 64KB memory space and load some machine code from a byte |
15 |
| -slice. |
16 |
| -```go |
17 |
| -mem := go6502.NewFlatMemory() |
18 |
| - |
19 |
| -// Load a byte slice of machine code at address 0x600 |
20 |
| -mem.StoreBytes(0x600, []byte{0xa2, 0x05, 0xa1, 0x02, 0xa9, 0x08, 0x8d, |
21 |
| - 0x01, 0x02, 0x69, 0xfe, 0x8d, 0x00, 0x02, 0xa9, 0xff, 0xad, 0x00, |
22 |
| - 0x02, 0xa2, 0xee, 0x4c, 0x00, 0x06}) |
23 |
| -``` |
24 |
| - |
25 |
| -Create an emulated CMOS 65c02 CPU and initialize its program counter. |
26 |
| -```go |
27 |
| -cpu := go6502.NewCPU(go6502.CMOS, mem) |
28 |
| -cpu.SetPC(0x600) |
29 |
| -``` |
30 |
| - |
31 |
| -Use the `Step()` function to manually step the CPU one instruction at a time. |
32 |
| -```go |
33 |
| -for i := 0; i < 20; i++ { |
34 |
| - cpu.Step() |
35 |
| -} |
36 |
| -``` |
37 |
| - |
38 |
| -Use the `go6502/disasm` package to disassemble machine code while stepping |
39 |
| -the CPU. |
40 |
| -```go |
41 |
| -for i := 0; i < 20; i++ { |
42 |
| - pc := cpu.Reg.PC |
43 |
| - line, _, _ := disasm.Disassemble(cpu.Mem, pc) |
44 |
| - cpu.Step() |
45 |
| - fmt.Printf("%04X- %-12s A=%02X X=%02X Y=%02X PS=[%s] SP=%02X PC=%04X Cycles=%d\n", |
46 |
| - pc, line, |
47 |
| - cpu.Reg.A, cpu.Reg.X, cpu.Reg.Y, psString(&cpu.Reg), |
48 |
| - cpu.Reg.SP, cpu.Reg.PC, |
49 |
| - cpu.Cycles) |
50 |
| -} |
51 |
| -``` |
52 |
| - |
53 |
| -Output: |
54 |
| -``` |
55 |
| -0600- LDX #$05 A=00 X=05 Y=00 PS=[------] SP=FF PC=0602 Cycles=2 |
56 |
| -0602- LDA ($02,X) A=00 X=05 Y=00 PS=[-Z----] SP=FF PC=0604 Cycles=8 |
57 |
| -0604- LDA #$08 A=08 X=05 Y=00 PS=[------] SP=FF PC=0606 Cycles=10 |
58 |
| -0606- STA $0201 A=08 X=05 Y=00 PS=[------] SP=FF PC=0609 Cycles=14 |
59 |
| -0609- ADC #$FE A=06 X=05 Y=00 PS=[C-----] SP=FF PC=060B Cycles=16 |
60 |
| -060B- STA $0200 A=06 X=05 Y=00 PS=[C-----] SP=FF PC=060E Cycles=20 |
61 |
| -060E- LDA #$FF A=FF X=05 Y=00 PS=[C----N] SP=FF PC=0610 Cycles=22 |
62 |
| -0610- LDA $0200 A=06 X=05 Y=00 PS=[C-----] SP=FF PC=0613 Cycles=26 |
63 |
| -0613- LDX #$EE A=06 X=EE Y=00 PS=[C----N] SP=FF PC=0615 Cycles=28 |
64 |
| -0615- JMP $0600 A=06 X=EE Y=00 PS=[C----N] SP=FF PC=0600 Cycles=31 |
65 |
| -0600- LDX #$05 A=06 X=05 Y=00 PS=[C-----] SP=FF PC=0602 Cycles=33 |
66 |
| -0602- LDA ($02,X) A=00 X=05 Y=00 PS=[CZ----] SP=FF PC=0604 Cycles=39 |
67 |
| -0604- LDA #$08 A=08 X=05 Y=00 PS=[C-----] SP=FF PC=0606 Cycles=41 |
68 |
| -0606- STA $0201 A=08 X=05 Y=00 PS=[C-----] SP=FF PC=0609 Cycles=45 |
69 |
| -0609- ADC #$FE A=07 X=05 Y=00 PS=[C-----] SP=FF PC=060B Cycles=47 |
70 |
| -060B- STA $0200 A=07 X=05 Y=00 PS=[C-----] SP=FF PC=060E Cycles=51 |
71 |
| -060E- LDA #$FF A=FF X=05 Y=00 PS=[C----N] SP=FF PC=0610 Cycles=53 |
72 |
| -0610- LDA $0200 A=07 X=05 Y=00 PS=[C-----] SP=FF PC=0613 Cycles=57 |
73 |
| -0613- LDX #$EE A=07 X=EE Y=00 PS=[C----N] SP=FF PC=0615 Cycles=59 |
74 |
| -0615- JMP $0600 A=07 X=EE Y=00 PS=[C----N] SP=FF PC=0600 Cycles=62 |
75 |
| -``` |
76 |
| - |
77 |
| -Here is the implementation of the helper function `psString` used in the |
78 |
| -example: |
79 |
| -```go |
80 |
| -func psString(r *go6502.Registers) string { |
81 |
| - v := func(bit bool, ch byte) byte { |
82 |
| - if bit { |
83 |
| - return ch |
84 |
| - } |
85 |
| - return '-' |
86 |
| - } |
87 |
| - b := []byte{ |
88 |
| - v(r.Carry, 'C'), |
89 |
| - v(r.Zero, 'Z'), |
90 |
| - v(r.InterruptDisable, 'I'), |
91 |
| - v(r.Decimal, 'D'), |
92 |
| - v(r.Overflow, 'O'), |
93 |
| - v(r.Negative, 'N'), |
94 |
| - } |
95 |
| - return string(b) |
96 |
| -} |
| 4 | +_This project is currently under construction and is changing frequently._ |
| 5 | + |
| 6 | +go6502 is a collection of go packages used to emulate a 6502 or 65C02 CPU. It |
| 7 | +includes a CPU emulator, a cross-assembler, a disassembler, a debugger, and a |
| 8 | +host that wraps them all together. |
| 9 | + |
| 10 | +The go6502 application in the root directory wraps the host and provides an |
| 11 | +interactive interface to the go6502 packages' features. |
| 12 | + |
| 13 | +# Tutorial |
| 14 | + |
| 15 | +Let's start by considering the go6502 `sample.cmd` script: |
| 16 | + |
| 17 | +``` |
| 18 | +load monitor $F800 |
| 19 | +assemble sample |
| 20 | +load sample |
| 21 | +set PC START |
| 22 | +d . |
| 23 | +``` |
| 24 | + |
| 25 | +We'll describe these commands in further detail later, but for now just |
| 26 | +know that this script's commands do the following things: |
| 27 | +1. Load the `monitor.bin` binary file at memory address `F800`. |
| 28 | +2. Assemble the `sample.asm` file using the go6502 cross-assembler, generating |
| 29 | + a `sample.bin` binary file and a `sample.map` source map file. |
| 30 | +3. Load the `sample.bin` file at the origin address specified by the |
| 31 | + `sample.asm` file. |
| 32 | +4. Set the program counter to the `START` address, which was exported by |
| 33 | + `sample.asm`. |
| 34 | +5. Disassemble the first few lines of machine code starting from the program |
| 35 | + counter address. |
| 36 | + |
| 37 | +To run this script, build the go6502 application and type the following on the |
| 38 | +command line: |
| 39 | + |
| 40 | +``` |
| 41 | +go6502 sample.cmd |
| 42 | +``` |
| 43 | + |
| 44 | +You should then see: |
| 45 | + |
| 46 | +``` |
| 47 | +Loaded 'monitor.bin' to $F800..$FFFF |
| 48 | +Assembled 'sample.asm' to 'sample.bin'. |
| 49 | +Loaded 'sample.bin' to $1000..$10FF |
| 50 | +Loaded 'sample.map' source map |
| 51 | +Register PC set to $1000. |
| 52 | +1000- A2 EE LDX #$EE |
| 53 | +1002- 48 PHA |
| 54 | +1003- 20 18 10 JSR $1018 |
| 55 | +1006- 20 1B 10 JSR $101B |
| 56 | +1009- 20 35 10 JSR $1035 |
| 57 | +100C- 20 45 10 JSR $1045 |
| 58 | +100F- F0 06 BEQ $1017 |
| 59 | +1011- A0 3B LDY #$3B |
| 60 | +1013- A9 10 LDA #$10 |
| 61 | +1015- A2 55 LDX #$55 |
| 62 | +
|
| 63 | +1000- A2 EE LDX #$EE A=00 X=00 Y=00 PS=[------] SP=FF PC=1000 C=0 |
| 64 | +* |
| 65 | +``` |
| 66 | + |
| 67 | +The output shows the result of go6502 running each command in the sample |
| 68 | +script. Once the script is finished running, go6502 enters interactive mode |
| 69 | +and provides a `*` prompt for further input. |
| 70 | + |
| 71 | +Just before the prompt is a line starting with `1000-`. This line shows the |
| 72 | +disassembly of the instruction at the current program counter address and the |
| 73 | +state of the CPU registers. The `C` value indicates the number of CPU cycles |
| 74 | +that have elapsed since the application started. |
| 75 | + |
| 76 | +## Getting help |
| 77 | + |
| 78 | +Let's enter our first interactive command. Type `help` to see a list of all |
| 79 | +commands. |
| 80 | + |
| 81 | +``` |
| 82 | +* help |
| 83 | +go6502 commands: |
| 84 | + annotate Annotate an address |
| 85 | + assemble Assemble a file and save the binary |
| 86 | + breakpoint Breakpoint commands |
| 87 | + databreakpoint Data breakpoint commands |
| 88 | + disassemble Disassemble code |
| 89 | + evaluate Evaluate an expression |
| 90 | + exports List exported addresses |
| 91 | + load Load a binary file |
| 92 | + memory Memory commands |
| 93 | + quit Quit the program |
| 94 | + registers Display register contents |
| 95 | + run Run the CPU |
| 96 | + set Set a host setting |
| 97 | + step Step the debugger |
| 98 | +``` |
| 99 | + |
| 100 | +To get further help about a command, type `help <cmd>`. In some cases, you |
| 101 | +will be shown a list of subcommands that must be used with the command. Let's |
| 102 | +try `help step`. |
| 103 | + |
| 104 | +``` |
| 105 | +* help step |
| 106 | +Step commands: |
| 107 | + in Step in to routine |
| 108 | + over Step over a routine |
| 109 | +``` |
| 110 | + |
| 111 | +This output tells you that the `step` command requires an `in` or `over` |
| 112 | +subcommand. To step the debugger into a routine, for instance, you would |
| 113 | +type `step in`. |
| 114 | + |
| 115 | +## Stepping the debugger |
| 116 | + |
| 117 | +Let's use one of the `step` subcommands to step the debugger by a single CPU |
| 118 | +instruction. Type `step in`. |
| 119 | + |
| 120 | +``` |
| 121 | +1000- A2 EE LDX #$EE A=00 X=00 Y=00 PS=[------] SP=FF PC=1000 C=0 |
| 122 | +* step in |
| 123 | +1002- A9 05 LDA #$05 A=00 X=EE Y=00 PS=[N-----] SP=FF PC=1002 C=2 |
| 124 | +* |
| 125 | +``` |
| 126 | + |
| 127 | +By stepping into one instruction, the emulated CPU has executed the `LDX #$EE` |
| 128 | +instruction at address `1000`. This has advanced the program counter to |
| 129 | +`1002`, loaded the value `EE` into the X register, and advanced the CPU |
| 130 | +cycle counter by 2 cycles. |
| 131 | + |
| 132 | +Each time go6502 runs a command interactively, it disassembles the instruction |
| 133 | +to be executed next (i.e., the instruction at the current program counter |
| 134 | +address). It also displays the current contents of the CPU registers and the |
| 135 | +CPU cycle counter. |
| 136 | + |
| 137 | +Many go6502 commands have aliases. The alias for the `step in` command is |
| 138 | +`si`. Now, type `si 4` to step into the next 4 instructions: |
| 139 | + |
| 140 | +``` |
| 141 | +1002- A9 05 LDA #$05 A=00 X=EE Y=00 PS=[N-----] SP=FF PC=1002 C=2 |
| 142 | +* si 4 |
| 143 | +1004- 20 19 10 JSR $1019 A=05 X=EE Y=00 PS=[------] SP=FF PC=1004 C=4 |
| 144 | +1019- A9 FF LDA #$FF A=05 X=EE Y=00 PS=[------] SP=FD PC=1019 C=10 |
| 145 | +101B- 60 RTS A=FF X=EE Y=00 PS=[N-----] SP=FD PC=101B C=12 |
| 146 | +1007- 20 1C 10 JSR $101C A=FF X=EE Y=00 PS=[N-----] SP=FF PC=1007 C=18 |
| 147 | +* |
| 148 | +``` |
| 149 | + |
| 150 | +This output shows that the CPU has stepped into the next 4 instructions |
| 151 | +starting at address `1002`. Each executed instruction is disassembled and |
| 152 | +displayed along with the CPU's register values at the start of each |
| 153 | +instruction. In total, 18 CPU cycles have elapsed, and the program counter |
| 154 | +ends at address `1007`. The instruction at `1007` has not yet been |
| 155 | +executed. |
| 156 | + |
| 157 | +Note that the `step in` command stepped _into_ the `JSR $1019` subroutine call |
| 158 | +rather than stepping _over_ it. If you weren't interested in stepping through |
| 159 | +all the code inside the subroutine, you could have used the `step over` |
| 160 | +command instead. This would have caused the debugger to invisibly execute all |
| 161 | +instructions inside the subroutine, returning control to the application only |
| 162 | +after the `RTS` instruction was executed. |
| 163 | + |
| 164 | +Since the CPU is about to execute another `JSR` instruction, let's try the |
| 165 | +`step over` command (or `s` for short). |
| 166 | + |
| 167 | +``` |
| 168 | +1007- 20 1C 10 JSR $101C A=FF X=EE Y=00 PS=[N-----] SP=FF PC=1007 C=18 |
| 169 | +* s |
| 170 | +100A- 20 36 10 JSR $1036 A=00 X=EE Y=00 PS=[-Z----] SP=FF PC=100A C=70 |
| 171 | +* |
| 172 | +``` |
| 173 | + |
| 174 | +After stepping over the `JSR` call at address `1007`, all of the instructions |
| 175 | +inside the subroutine at `101C` have been executed, and control has returned |
| 176 | +to go6502 at address `100A` after 52 CPU cycles have elapsed. |
| 177 | + |
| 178 | +## Shortcut: Hit Enter! |
| 179 | + |
| 180 | +A useful shortcut you will probably use frequently is hitting the Enter key at |
| 181 | +the prompt. This causes go6502 to repeat the previously entered command. |
| 182 | + |
| 183 | +Let's try hitting Enter twice to repeat the `step over` command two more |
| 184 | +times. |
| 185 | + |
| 186 | +``` |
| 187 | +100A- 20 36 10 JSR $1036 A=00 X=EE Y=00 PS=[-Z----] SP=FF PC=100A C=70 |
| 188 | +* |
| 189 | +100D- 20 46 10 JSR $1046 A=00 X=00 Y=00 PS=[-Z----] SP=FF PC=100D C=103 |
| 190 | +* |
| 191 | +1010- F0 06 BEQ $1018 A=00 X=00 Y=00 PS=[-Z----] SP=FF PC=1010 C=136 |
| 192 | +* |
| 193 | +``` |
| 194 | + |
| 195 | +As you can see, go6502 has stepped "over" two more `JSR` instructions, |
| 196 | +elapsing another 66 CPU cycles and leaving the program counter at `1010`. |
| 197 | + |
| 198 | +## Disassembling code |
| 199 | + |
| 200 | +Now let's disassemble code at the current program counter address to get a |
| 201 | +preview of the code about to be executed: |
| 202 | + |
| 203 | +``` |
| 204 | +* d . |
| 205 | +1010- F0 06 BEQ $1018 |
| 206 | +1012- A0 3B LDY #$3B |
| 207 | +1014- A9 10 LDA #$10 |
| 208 | +1016- A2 56 LDX #$56 |
| 209 | +1018- 00 BRK |
| 210 | +1019- A9 FF LDA #$FF |
| 211 | +101B- 60 RTS |
| 212 | +101C- A9 20 LDA #$20 |
| 213 | +101E- A5 20 LDA $20 |
| 214 | +1020- B5 20 LDA $20,X |
| 215 | +* |
| 216 | +``` |
| 217 | + |
| 218 | +The `.` is shorthand for the current program counter address. You may also |
| 219 | +pass an address or mathematical expression to disassemble code starting at any |
| 220 | +address: |
| 221 | + |
| 222 | +``` |
| 223 | +* d START+2 |
| 224 | +1002- A9 05 LDA #$05 |
| 225 | +1004- 20 19 10 JSR $1019 |
| 226 | +1007- 20 1C 10 JSR $101C |
| 227 | +100A- 20 36 10 JSR $1036 |
| 228 | +100D- 20 46 10 JSR $1046 |
| 229 | +1010- F0 06 BEQ $1018 |
| 230 | +1012- A0 3B LDY #$3B |
| 231 | +1014- A9 10 LDA #$10 |
| 232 | +1016- A2 56 LDX #$56 |
| 233 | +1018- 00 BRK |
| 234 | +* |
| 235 | +``` |
| 236 | + |
| 237 | +By default, go6502 disassembles 10 instructions, but you can disassemble a |
| 238 | +different number of instructions by specifying a second argument: |
| 239 | + |
| 240 | +``` |
| 241 | +d . 3 |
| 242 | +1010- F0 06 BEQ $1018 |
| 243 | +1012- A0 3B LDY #$3B |
| 244 | +1014- A9 10 LDA #$10 |
| 245 | +* |
| 246 | +``` |
| 247 | + |
| 248 | +If you hit Enter after using a disassemble command, go6502 will continue |
| 249 | +disassembly from where it left off. |
| 250 | + |
| 251 | +``` |
| 252 | +* |
| 253 | +1016- A2 56 LDX #$56 |
| 254 | +1018- 00 BRK |
| 255 | +1019- A9 FF LDA #$FF |
| 256 | +* |
| 257 | +``` |
| 258 | + |
| 259 | +If you don't like the number of instructions that go6502 disassembles by |
| 260 | +default, you can change it with the `set` command: |
| 261 | + |
| 262 | +``` |
| 263 | +set DisasmLinesToDisplay 20 |
97 | 264 | ```
|
0 commit comments