├── .editorconfig ├── .gitignore ├── LICENSE ├── README.md ├── asm ├── asm.go ├── asm_test.go ├── expr.go ├── fstring.go ├── sourcemap.go └── util.go ├── cpu ├── README.md ├── cpu.go ├── cpu_test.go ├── debugger.go ├── instructions.go ├── memory.go └── register.go ├── disasm └── disasm.go ├── go.mod ├── go.sum ├── host ├── .gitignore ├── cmds.go ├── expr.go ├── host.go ├── settings.go └── util.go ├── main.go ├── monitor.bin ├── monitor.map ├── sample.asm ├── sample.cmd └── term ├── LICENSE ├── PATENTS ├── README.md ├── term.go ├── term_plan9.go ├── term_unix.go ├── term_unix_bsd.go ├── term_unix_other.go ├── term_unsupported.go ├── term_windows.go └── terminal.go /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | indent_size = 4 3 | indent_style = space 4 | insert_final_newline = true 5 | trim_trailing_whitespace = true 6 | 7 | [*.asm] 8 | indent_size = 8 9 | indent_style = tab 10 | 11 | [*.go] 12 | indent_size = 4 13 | indent_style = tab 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | debug 2 | debug.test 3 | go6502 4 | go6502.exe 5 | todo.txt 6 | sample.bin 7 | sample.map 8 | .vscode/ 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2014-2018 Brett Vickers. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions 5 | are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER ``AS IS'' AND ANY 15 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR 18 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 | OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![GoDoc](https://godoc.org/github.com/beevik/go6502?status.svg)](https://godoc.org/github.com/beevik/go6502) 2 | 3 | go6502 4 | ====== 5 | 6 | go6502 is a collection of go packages that 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 interactive go6502 console application in the root directory provides 11 | access to all of these features. 12 | 13 | 14 | # Building the application 15 | 16 | Install go 1.21 or later, and then run `go build` to build the application. 17 | 18 | 19 | # Tutorial 20 | 21 | Start by considering the go6502 `sample.cmd` script: 22 | 23 | ``` 24 | load monitor.bin $F800 25 | assemble file sample.asm 26 | load sample.bin 27 | reg PC START 28 | d . 29 | ``` 30 | 31 | We'll describe what each of these commands does in greater detail later, but 32 | for now know that they do the following things: 33 | 1. Load the `monitor.bin` binary file at memory address `F800`. 34 | 2. Assemble the `sample.asm` file using the go6502 cross-assembler, generating 35 | a `sample.bin` binary file and a `sample.map` source map file. 36 | 3. Load the `sample.bin` binary file and its corresponding `sample.map` source 37 | map file. The binary data is loaded into memory at the origin address 38 | exported during assembly into the `sample.map` file. 39 | 4. Set the program counter register to value of the `START` address, which 40 | was exported during assembly into the `sample.map` file. 41 | 5. Disassemble the first few lines of machine code starting from the program 42 | counter address. 43 | 44 | To run this script, type the following on the command line: 45 | 46 | ``` 47 | go6502 sample.cmd 48 | ``` 49 | 50 | You should then see: 51 | 52 | ``` 53 | Loaded 'monitor.bin' to $F800..$FFFF. 54 | Assembled 'sample.asm' to 'sample.bin'. 55 | Loaded source map from 'sample.bin'. 56 | Loaded 'sample.bin' to $1000..$10FF. 57 | Register PC set to $1000. 58 | Breakpoint added at $1020. 59 | 1000- A2 EE LDX #$EE 60 | 1002- A9 05 LDA #$05 61 | 1004- 20 19 10 JSR $1019 62 | 1007- 20 1C 10 JSR $101C 63 | 100A- 20 36 10 JSR $1036 64 | 100D- 20 46 10 JSR $1046 65 | 1010- F0 06 BEQ $1018 66 | 1012- A0 3B LDY #$3B 67 | 1014- A9 10 LDA #$10 68 | 1016- A2 56 LDX #$56 69 | 70 | 1000- A2 EE LDX #$EE A=00 X=00 Y=00 PS=[------] SP=FF PC=1000 C=0 71 | * 72 | ``` 73 | 74 | The output shows the result of running each sample script command. Once the 75 | script has finished running, go6502 enters interactive mode and displays a `*` 76 | prompt for further input. 77 | 78 | Just before the prompt is a line starting with `1000-`. This line displays the 79 | disassembly of the instruction at the current program counter address and the 80 | state of the CPU registers. The `C` value indicates the number of CPU cycles 81 | that have elapsed since the application started. 82 | 83 | ## Getting help 84 | 85 | Let's enter our first interactive command. Type `help` to see a list of all 86 | commands. 87 | 88 | ``` 89 | go6502 commands: 90 | annotate Annotate an address 91 | assemble Assemble commands 92 | breakpoint Breakpoint commands 93 | databreakpoint Data breakpoint commands 94 | disassemble Disassemble code 95 | evaluate Evaluate an expression 96 | execute Execute a go6502 script file 97 | exports List exported addresses 98 | load Load a binary file 99 | memory Memory commands 100 | quit Quit the program 101 | register View or change register values 102 | run Run the CPU 103 | set Set a configuration variable 104 | step Step the debugger 105 | 106 | * 107 | ``` 108 | 109 | To get more information about a command, type `help` followed by the command 110 | name. In some cases, you will be shown a list of subcommands that must be used 111 | with the command. Let's try `help step`. 112 | 113 | ``` 114 | * help step 115 | Step commands: 116 | in Step into next instruction 117 | over Step over next instruction 118 | 119 | * 120 | ``` 121 | 122 | This response indicates that the `step` command has two possible subcommands. 123 | For example, if you wanted to step the CPU into the next instruction, you 124 | would type `step in`. 125 | 126 | Now let's get help on the `step in` command. 127 | 128 | ``` 129 | * help step in 130 | Usage: step in [] 131 | 132 | Description: 133 | Step the CPU by a single instruction. If the instruction is a subroutine 134 | call, step into the subroutine. The number of steps may be specified as an 135 | option. 136 | 137 | Shortcut: si 138 | 139 | * 140 | ``` 141 | 142 | Every command has help text like this. Included in the help text is a 143 | description of the command, a list of shortcuts that can be used to invoke the 144 | command, and a usage hint indicating the arguments accepted by the command. 145 | Usage arguments appear inside ``. Optional usage arguments 146 | appear inside square `[]`. 147 | 148 | ## Abbreviating commands 149 | 150 | The go6502 application uses a "shortest unambiguous match" parser to process 151 | commands. This means that when entering a command, you need only type the 152 | smallest number of characters that uniquely identify it. For instance, 153 | instead of typing `quit`, you can type `q` since no other commands start with 154 | the letter Q. 155 | 156 | Most commands also have shortcuts. To discover a command's shortcuts, use 157 | `help`. 158 | 159 | ## Stepping the CPU 160 | 161 | Let's use one of the `step` commands to step the CPU by a single instruction. 162 | Type `step in`. 163 | 164 | ``` 165 | 1000- A2 EE LDX #$EE A=00 X=00 Y=00 PS=[------] SP=FF PC=1000 C=0 166 | * step in 167 | 1002- A9 05 LDA #$05 A=00 X=EE Y=00 PS=[N-----] SP=FF PC=1002 C=2 168 | * 169 | ``` 170 | 171 | By typing `step in`, you are telling the emulated CPU to execute the `LDX #$EE` 172 | instruction at address `1000`. This advances the program counter to `1002`, 173 | loads the value `EE` into the X register, and increases the CPU cycle counter 174 | by 2 cycles. 175 | 176 | Each time go6502 advances the program counter interactively, it disassembles 177 | and displays the instruction to be executed next. It also displays the current 178 | values of the CPU registers and cycle counter. 179 | 180 | The shortcut for the `step in` command is `si`. Let's type `si 4` to step the 181 | CPU by 4 instructions: 182 | 183 | ``` 184 | 1002- A9 05 LDA #$05 A=00 X=EE Y=00 PS=[N-----] SP=FF PC=1002 C=2 185 | * si 4 186 | 1004- 20 19 10 JSR $1019 A=05 X=EE Y=00 PS=[------] SP=FF PC=1004 C=4 187 | 1019- A9 FF LDA #$FF A=05 X=EE Y=00 PS=[------] SP=FD PC=1019 C=10 188 | 101B- 60 RTS A=FF X=EE Y=00 PS=[N-----] SP=FD PC=101B C=12 189 | 1007- 20 1C 10 JSR $101C A=FF X=EE Y=00 PS=[N-----] SP=FF PC=1007 C=18 190 | * 191 | ``` 192 | 193 | This output shows that the CPU has stepped the next 4 instructions starting at 194 | address `1002`. Each executed instruction is disassembled and displayed along 195 | with the CPU's register values at the start of each instruction. In this 196 | example, a total of 18 CPU cycles have elapsed, and the program counter ends 197 | at address `1007`. The instruction at `1007` is waiting to be executed. 198 | 199 | Note that the `step in` command stepped _into_ the `JSR $1019` subroutine call 200 | rather than stepping _over_ it. If you weren't interested in stepping through 201 | all the code inside the subroutine, you could have used the `step over` 202 | command instead. This would have caused the debugger to invisibly execute all 203 | instructions inside the subroutine, returning the prompt only after the `RTS` 204 | instruction has executed. 205 | 206 | Since the CPU is about to execute another `JSR` instruction, let's try the 207 | `step over` command (or `s` for short). 208 | 209 | ``` 210 | 1007- 20 1C 10 JSR $101C A=FF X=EE Y=00 PS=[N-----] SP=FF PC=1007 C=18 211 | * s 212 | 100A- 20 36 10 JSR $1036 A=00 X=EE Y=00 PS=[-Z----] SP=FF PC=100A C=70 213 | * 214 | ``` 215 | 216 | After stepping over the `JSR` call at address `1007`, all of the instructions 217 | inside the subroutine at `101C` have been executed, and control has returned 218 | at address `100A` after 52 additional CPU cycles have elapsed. 219 | 220 | ## Another shortcut: Hit Enter! 221 | 222 | One shortcut you will probably use frequently is the blank-line short cut. 223 | Whenever you hit the Enter key instead of typing a command, the go6502 224 | application repeats the previously entered command. 225 | 226 | Let's try hitting enter twice to repeat the `step over` command two more 227 | times. 228 | 229 | ``` 230 | 100A- 20 36 10 JSR $1036 A=00 X=EE Y=00 PS=[-Z----] SP=FF PC=100A C=70 231 | * 232 | 100D- 20 46 10 JSR $1046 A=00 X=00 Y=00 PS=[-Z----] SP=FF PC=100D C=103 233 | * 234 | 1010- F0 06 BEQ $1018 A=00 X=00 Y=00 PS=[-Z----] SP=FF PC=1010 C=136 235 | * 236 | ``` 237 | 238 | go6502 has stepped over two more `JSR` instructions, elapsing another 66 CPU 239 | cycles and leaving the program counter at `1010`. 240 | 241 | ## Disassembling code 242 | 243 | Now let's disassemble some code at the current program counter address to get 244 | a preview of the code about to be executed. To do this, use the `disassemble` 245 | command or its shortcut `d`. 246 | 247 | ``` 248 | * d . 249 | 1010- F0 06 BEQ $1018 250 | 1012- A0 3B LDY #$3B 251 | 1014- A9 10 LDA #$10 252 | 1016- A2 56 LDX #$56 253 | 1018- 00 BRK 254 | 1019- A9 FF LDA #$FF 255 | 101B- 60 RTS 256 | 101C- A9 20 LDA #$20 257 | 101E- A5 20 LDA $20 258 | 1020- B5 20 LDA $20,X 259 | * 260 | ``` 261 | 262 | Note the `.` after the `d` command. This is shorthand for the current program 263 | counter address. You may also pass an address or mathematical expression to 264 | disassemble code starting from any address: 265 | 266 | ``` 267 | * d START+2 268 | 1002- A9 05 LDA #$05 269 | 1004- 20 19 10 JSR $1019 270 | 1007- 20 1C 10 JSR $101C 271 | 100A- 20 36 10 JSR $1036 272 | 100D- 20 46 10 JSR $1046 273 | 1010- F0 06 BEQ $1018 274 | 1012- A0 3B LDY #$3B 275 | 1014- A9 10 LDA #$10 276 | 1016- A2 56 LDX #$56 277 | 1018- 00 BRK 278 | * 279 | ``` 280 | 281 | By default, go6502 disassembles 10 instructions, but you can disassemble a 282 | different number of instructions by specifying a second argument to the 283 | command. 284 | 285 | ``` 286 | * d . 3 287 | 1010- F0 06 BEQ $1018 288 | 1012- A0 3B LDY #$3B 289 | 1014- A9 10 LDA #$10 290 | * 291 | ``` 292 | 293 | If you hit the Enter key after using a disassemble command, go6502 will 294 | continue disassembling code from where it left off. 295 | 296 | ``` 297 | * 298 | 1016- A2 56 LDX #$56 299 | 1018- 00 BRK 300 | 1019- A9 FF LDA #$FF 301 | * 302 | ``` 303 | 304 | If you don't like the number of instructions that go6502 is configured to 305 | disassemble by default, you can change it with the `set` command: 306 | 307 | ``` 308 | * set DisasmLines 20 309 | ``` 310 | 311 | ## Annotating code 312 | 313 | It's often useful to annotate a line of code with a comment. I use annotations 314 | to leave notes to myself when I'm trying to understand how some piece of 315 | machine code works. 316 | 317 | Let's consider again the code loaded by the sample script. 318 | 319 | ``` 320 | * d $1000 321 | 1000- A2 EE LDX #$EE 322 | 1002- A9 05 LDA #$05 323 | 1004- 20 19 10 JSR $1019 324 | 1007- 20 1C 10 JSR $101C 325 | 100A- 20 36 10 JSR $1036 326 | 100D- 20 46 10 JSR $1046 327 | 1010- F0 06 BEQ $1018 328 | 1012- A0 3B LDY #$3B 329 | 1014- A9 10 LDA #$10 330 | 1016- A2 56 LDX #$56 331 | * 332 | ``` 333 | 334 | The `JSR` instruction at address `1007` calls a subroutine that uses all 335 | the addressing mode variants of the `LDA` command. Let's add an annotation 336 | to that line of code to remind ourselves later what its purpose is. 337 | 338 | ``` 339 | * annotate $1007 Use different forms of the LDA command 340 | * 341 | ``` 342 | 343 | Now whenever we disassemble code that includes the instruction at address 344 | `1007`, we will see our annotation. 345 | 346 | ``` 347 | * d $1000 348 | 1000- A2 EE LDX #$EE 349 | 1002- A9 05 LDA #$05 350 | 1004- 20 19 10 JSR $1019 351 | 1007- 20 1C 10 JSR $101C ; Use different forms of the LDA command 352 | 100A- 20 36 10 JSR $1036 353 | 100D- 20 46 10 JSR $1046 354 | 1010- F0 06 BEQ $1018 355 | 1012- A0 3B LDY #$3B 356 | 1014- A9 10 LDA #$10 357 | 1016- A2 56 LDX #$56 358 | * 359 | ``` 360 | 361 | To remove an annotation, use the `annotate` command with an address but 362 | without a description. 363 | 364 | ## Dumping memory 365 | 366 | Another common task is dumping the contents of memory. To do this, use the 367 | `memory dump` command, or `m` for short. 368 | 369 | ``` 370 | * m $1000 371 | 1000- A2 EE A9 05 20 19 10 20 "n). .. 372 | 1008- 1C 10 20 36 10 20 46 10 .. 6. F. 373 | 1010- F0 06 A0 3B A9 10 A2 56 p. ;)."V 374 | 1018- 00 A9 FF 60 A9 20 A5 20 .).`) % 375 | 1020- B5 20 A1 20 B1 20 AD 00 5 ! 1 -. 376 | 1028- 02 AD 20 00 BD 00 02 B9 .- .=..9 377 | 1030- 00 02 8D 00 03 60 A2 20 .....`" 378 | 1038- A6 20 B6 20 AE 00 02 AE & 6 .... 379 | * 380 | ``` 381 | 382 | Memory dumps include hexadecimal and ASCII representations of the dumped 383 | memory, starting from the address you specified. By default, the memory 384 | dump shows 64 bytes, but you can specify a different number of bytes to 385 | dump with a second argument. 386 | 387 | ``` 388 | * m $1000 16 389 | 1000- A2 EE A9 05 20 19 10 20 "n). .. 390 | 1008- 1C 10 20 36 10 20 46 10 .. 6. F. 391 | * 392 | ``` 393 | 394 | As with the `disassemble` command, you can enter a blank line to continue 395 | dumping memory from where you left off: 396 | 397 | ``` 398 | * 399 | 1010- F0 06 A0 3B A9 10 A2 56 p. ;)."V 400 | 1018- 00 A9 FF 60 A9 20 A5 20 .).`) % 401 | * 402 | 1020- B5 20 A1 20 B1 20 AD 00 5 ! 1 -. 403 | 1028- 02 AD 20 00 BD 00 02 B9 .- .=..9 404 | * 405 | ``` 406 | 407 | To change the default number of bytes that are dumped by a `memory dump` 408 | command, use the `set` command: 409 | 410 | ``` 411 | * set MemDumpBytes 128 412 | ``` 413 | 414 | ## Modifying memory 415 | 416 | To change the contents of memory, use the `memory set` command, or `ms` for 417 | short. 418 | 419 | ``` 420 | * ms 0x800 $5A $59 $58 $57 421 | * m 0x800 4 422 | 0800- 5A 59 58 57 ZYXW 423 | * 424 | ``` 425 | 426 | A sequence of memory values must be separated by spaces and may include 427 | simple hexadecimal values like shown in the example above, or mathematical 428 | expressions like in the following: 429 | 430 | ``` 431 | * ms 0x800 12*2 'A' 1<<4 $0F^$05 432 | * m 0x800 4 433 | 0800- 18 41 10 0A .A.. 434 | * 435 | ``` 436 | 437 | ## Aside: Number formats 438 | 439 | go6502 accepts numbers in multiple formats. In most of the examples we've seen 440 | so far, addresses and byte values have been specified in base-16 hexadecimal 441 | format using the `$` prefix. 442 | 443 | The following table lists the number-formatting options understood by go6502: 444 | 445 | Prefix | Format | Base | Example | Comment 446 | ----------|-------------|:----:|-------------|------------------------- 447 | _(none)_ | Decimal | 10 | -151 | See note about hex mode. 448 | `$` | Hexadecimal | 16 | `$FDED` | 449 | `0x` | Hexadecimal | 16 | `0xfded` | 450 | `%` | Binary | 2 | `%01011010` | 451 | `0b` | Binary | 2 | `0b01011010`| 452 | `0d` | Decimal | 10 | `0d128` | Useful in hex mode. 453 | 454 | If you prefer to work primarily with hexadecimal numbers, you can change the 455 | "hex mode" setting using the `set` command. 456 | 457 | ``` 458 | * set HexMode true 459 | ``` 460 | 461 | In hex mode, numeric values entered without a prefix are interpreted as 462 | hexadecimal values. However, because hexadecimal numbers include the letters 463 | `A` through `F`, the interpreter is unable to distinguish between a number and 464 | an identifier. So identifiers are not allowed when interpreting expressions in 465 | hex mode. 466 | 467 | 468 | ## Inspecting and changing registers 469 | 470 | The 6502 registers can be inspected using the `register` command, or `r` 471 | for short. 472 | 473 | ``` 474 | * r 475 | 1000- A2 EE LDX #$EE A=00 X=00 Y=00 PS=[------] SP=FF PC=1000 C=0 476 | * 477 | ``` 478 | 479 | If you wish to change a register value, simply add additional arguments. 480 | 481 | ``` 482 | * r A $80 483 | Register A set to $80. 484 | 1000- A2 EE LDX #$EE A=80 X=00 Y=00 PS=[------] SP=FF PC=1000 C=0 485 | * 486 | ``` 487 | 488 | Registers you can change this way include `A`, `X`, `Y`, `PC` and `SP`. 489 | 490 | You can also change the CPU's status flags. Simply provide one of the flag 491 | names (`N`, `Z`, `C`, `I`, `D` or `V`) instead of a register name. 492 | 493 | ``` 494 | * r Z 1 495 | Status flag ZERO set to true. 496 | 1000- A2 EE LDX #$EE A=80 X=00 Y=00 PS=[-Z----] SP=FF PC=1000 C=0 497 | * r Z 0 498 | Status flag ZERO set to false. 499 | 1000- A2 EE LDX #$EE A=80 X=00 Y=00 PS=[------] SP=FF PC=1000 C=0 500 | * 501 | ``` 502 | 503 | Further info about the `register` command can be found by typing 504 | `help register`. 505 | 506 | 507 | ## Evaluating expressions 508 | 509 | Sometimes it's useful to have a calculator on hand to compute the result of a 510 | simple math expression. go6502 has a built-in expression evaluator in the form 511 | of the `evaluate` command, or `e` for short. The evaluator understands most C 512 | expression operators. 513 | 514 | ``` 515 | * e 1<<4 516 | $0010 517 | * e ($FF ^ $AA) | $0100 518 | $0155 519 | * e ('A' + 0x20) | 0x80 520 | $00E1 521 | * e 0b11100101 522 | $00E5 523 | * e -151 524 | $FF69 525 | ``` 526 | 527 | Because go6502 is written for an 8-bit CPU with a 16-bit address space, the 528 | results of all evaluations are displayed as 16-bit values. 529 | 530 | 531 | ## Assembling source code 532 | 533 | go6502 has a built-in cross-assembler. To assemble a file on disk into a raw 534 | binary file containing 6502 machine code, use the `assemble file` command (or 535 | `a` for short). 536 | 537 | ``` 538 | * a sample.asm 539 | Assembled 'sample.asm' to 'sample.bin'. 540 | ``` 541 | 542 | The `assemble file` command loads the specified source file, assembles it, and 543 | if successful outputs a raw `.bin` file containing the machine code into the 544 | same directory. It also produces a `.map` source map file, which is used to 545 | store (1) the "origin" memory address the machine code should be loaded at, 546 | (2) a list of exported address identifiers, and (3) a mapping between source 547 | code lines and memory addresses. 548 | 549 | Once assembled, the binary file and its associated source map can be loaded 550 | into memory using the `load` command. 551 | 552 | ``` 553 | * load sample.bin 554 | Loaded source map from 'sample.map'. 555 | Loaded 'sample.bin' to $1000..$10FF. 556 | ``` 557 | 558 | 559 | _To be continued..._ 560 | -------------------------------------------------------------------------------- /asm/asm_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2018 Brett Vickers. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package asm 6 | 7 | import ( 8 | "bytes" 9 | "os" 10 | "strings" 11 | "testing" 12 | ) 13 | 14 | func assemble(code string) ([]byte, error) { 15 | r := bytes.NewReader([]byte(code)) 16 | assembly, _, err := Assemble(r, "test", 0x1000, os.Stdout, 0) 17 | if err != nil { 18 | return []byte{}, err 19 | } 20 | return assembly.Code, nil 21 | } 22 | 23 | func checkASM(t *testing.T, asm string, expected string) { 24 | code, err := assemble(asm) 25 | if err != nil { 26 | t.Error(err) 27 | return 28 | } 29 | 30 | b := make([]byte, len(code)*2) 31 | for i, j := 0, 0; i < len(code); i, j = i+1, j+2 { 32 | v := code[i] 33 | b[j+0] = hex[v>>4] 34 | b[j+1] = hex[v&0x0f] 35 | } 36 | s := string(b) 37 | 38 | if s != expected { 39 | t.Error("code doesn't match expected") 40 | t.Errorf("got: %s\n", s) 41 | t.Errorf("exp: %s\n", expected) 42 | } 43 | } 44 | 45 | func checkASMError(t *testing.T, asm string, errString string) { 46 | _, err := assemble(asm) 47 | if err == nil { 48 | t.Errorf("Expected error on %s, didn't get one\n", asm) 49 | return 50 | } 51 | if errString != err.Error() { 52 | t.Errorf("Expected '%s', got '%v'\n", errString, err) 53 | } 54 | } 55 | 56 | func TestAddressingIMM(t *testing.T) { 57 | asm := ` 58 | LDA #$20 59 | LDX #$20 60 | LDY #$20 61 | ADC #$20 62 | SBC #$20 63 | CMP #$20 64 | CPX #$20 65 | CPY #$20 66 | AND #$20 67 | ORA #$20 68 | EOR #$20` 69 | 70 | checkASM(t, asm, "A920A220A0206920E920C920E020C020292009204920") 71 | } 72 | 73 | func TestAddressingABS(t *testing.T) { 74 | asm := ` 75 | LDA $2000 76 | LDX $2000 77 | LDY $2000 78 | STA $2000 79 | STX $2000 80 | STY $2000 81 | ADC $2000 82 | SBC $2000 83 | CMP $2000 84 | CPX $2000 85 | CPY $2000 86 | BIT $2000 87 | AND $2000 88 | ORA $2000 89 | EOR $2000 90 | INC $2000 91 | DEC $2000 92 | JMP $2000 93 | JSR $2000 94 | ASL $2000 95 | LSR $2000 96 | ROL $2000 97 | ROR $2000 98 | LDA A:$20 99 | LDA ABS:$20` 100 | 101 | checkASM(t, asm, "AD0020AE0020AC00208D00208E00208C00206D0020ED0020CD0020"+ 102 | "EC0020CC00202C00202D00200D00204D0020EE0020CE00204C00202000200E0020"+ 103 | "4E00202E00206E0020AD2000AD2000") 104 | } 105 | 106 | func TestAddressingABX(t *testing.T) { 107 | asm := ` 108 | LDA $2000,X 109 | LDY $2000,X 110 | STA $2000,X 111 | ADC $2000,X 112 | SBC $2000,X 113 | CMP $2000,X 114 | AND $2000,X 115 | ORA $2000,X 116 | EOR $2000,X 117 | INC $2000,X 118 | DEC $2000,X 119 | ASL $2000,X 120 | LSR $2000,X 121 | ROL $2000,X 122 | ROR $2000,X` 123 | 124 | checkASM(t, asm, "BD0020BC00209D00207D0020FD0020DD00203D00201D00205D0020"+ 125 | "FE0020DE00201E00205E00203E00207E0020") 126 | } 127 | 128 | func TestAddressingABY(t *testing.T) { 129 | asm := ` 130 | LDA $2000,Y 131 | LDX $2000,Y 132 | STA $2000,Y 133 | ADC $2000,Y 134 | SBC $2000,Y 135 | CMP $2000,Y 136 | AND $2000,Y 137 | ORA $2000,Y 138 | EOR $2000,Y` 139 | 140 | checkASM(t, asm, "B90020BE0020990020790020F90020D90020390020190020590020") 141 | } 142 | 143 | func TestAddressingZPG(t *testing.T) { 144 | asm := ` 145 | LDA $20 146 | LDX $20 147 | LDY $20 148 | STA $20 149 | STX $20 150 | STY $20 151 | ADC $20 152 | SBC $20 153 | CMP $20 154 | CPX $20 155 | CPY $20 156 | BIT $20 157 | AND $20 158 | ORA $20 159 | EOR $20 160 | INC $20 161 | DEC $20 162 | ASL $20 163 | LSR $20 164 | ROL $20 165 | ROR $20` 166 | 167 | checkASM(t, asm, "A520A620A4208520862084206520E520C520E420C42024202520"+ 168 | "05204520E620C6200620462026206620") 169 | } 170 | 171 | func TestAddressingIND(t *testing.T) { 172 | asm := ` 173 | JMP ($20) 174 | JMP ($2000)` 175 | 176 | checkASM(t, asm, "6C20006C0020") 177 | } 178 | 179 | func TestDataBytes(t *testing.T) { 180 | asm := ` 181 | .DB "AB", $00 182 | .DB 'f', 'f' 183 | .DB $ABCD 184 | .DB $ABCD >> 8 185 | .DB $0102 186 | .DB $03040506 187 | .DB 1+2+3+4 188 | .DB -1 189 | .DB -129 190 | .DB 0b0101010101010101 191 | .DB 0b01010101` 192 | 193 | checkASM(t, asm, "4142006666CDAB02060AFF7F5555") 194 | } 195 | 196 | func TestDataWords(t *testing.T) { 197 | asm := ` 198 | .DW "AB", $00 199 | .DW 'f', 'f' 200 | .DW $ABCD 201 | .DW $ABCD >> 8 202 | .DW $0102 203 | .DW $03040506 204 | .DW 1+2+3+4 205 | .DW -1 206 | .DW -129 207 | .DW 0b01010101 208 | .DW 0b0101010101010101` 209 | 210 | checkASM(t, asm, "4142000066006600CDABAB00020106050A00FFFF7FFF55005555") 211 | } 212 | 213 | func TestDataDwords(t *testing.T) { 214 | asm := ` 215 | .DD "AB", $00 216 | .DD 'f', 'f' 217 | .DD $ABCD 218 | .DD $ABCD >> 8 219 | .DD $0102 220 | .DD $03040506 221 | .DD 1+2+3+4 222 | .DD -1 223 | .DD -129 224 | .DD 0b01010101 225 | .DD 0b0101010101010101` 226 | 227 | checkASM(t, asm, "4142000000006600000066000000CDAB0000AB000000020100000"+ 228 | "60504030A000000FFFFFFFF7FFFFFFF5500000055550000") 229 | } 230 | 231 | func TestDataHexStrings(t *testing.T) { 232 | asm := ` 233 | .DH 0102030405060708 234 | .DH aabbcc 235 | .DH dd 236 | .DH ee 237 | .DH ff` 238 | 239 | checkASM(t, asm, "0102030405060708AABBCCDDEEFF") 240 | } 241 | 242 | func TestDataTermStrings(t *testing.T) { 243 | asm := ` 244 | .DS "AAA" 245 | .DS "a", 0 246 | .DS ""` 247 | 248 | checkASM(t, asm, "4141C1E100") 249 | } 250 | 251 | func TestAlign(t *testing.T) { 252 | asm := ` 253 | .ALIGN 4 254 | .DB $ff 255 | .ALIGN 2 256 | .DB $ff 257 | .ALIGN 8 258 | .DB $ff 259 | .ALIGN 1 260 | .DB $ff` 261 | 262 | checkASM(t, asm, "FF00FF0000000000FFFF") 263 | } 264 | 265 | func TestHereExpression1(t *testing.T) { 266 | asm := ` 267 | .OR $0600 268 | X .EQ FOO 269 | BIT X 270 | FOO .EQ $` 271 | 272 | checkASM(t, asm, "2C0306") 273 | } 274 | 275 | func TestHereExpression2(t *testing.T) { 276 | asm := ` 277 | .OR $0600 278 | X .EQ $ - 1 279 | BIT X` 280 | 281 | checkASM(t, asm, "2CFF05") 282 | } 283 | 284 | func TestHereExpression3(t *testing.T) { 285 | asm := ` 286 | .OR $0600 287 | BIT X 288 | X .EQ $ - 1` 289 | 290 | checkASM(t, asm, "2C0206") 291 | } 292 | 293 | var asm65c02 = ` PHX 294 | PHY 295 | PLX 296 | PLY 297 | BRA $1000 298 | STZ $01 299 | STZ $1234 300 | STZ ABS:$01 301 | STZ $01,X 302 | STZ $1234,X 303 | INC 304 | DEC 305 | JMP $1234,X 306 | BIT #$12 307 | BIT $12,X 308 | BIT $1234,X 309 | TRB $01 310 | TRB $1234 311 | TSB $01 312 | TSB $1234 313 | ADC ($01) 314 | SBC ($01) 315 | CMP ($01) 316 | AND ($01) 317 | ORA ($01) 318 | EOR ($01) 319 | LDA ($01) 320 | STA ($01)` 321 | 322 | func Test65c02(t *testing.T) { 323 | prefix := ` 324 | .ARCH 65c02 325 | .ORG $1000 326 | ` 327 | checkASM(t, prefix+asm65c02, "DA5AFA7A80FA64019C34129C010074019E3412"+ 328 | "1A3A7C3412891234123C341214011C341204010C34127201F201D201320112015201B2019201") 329 | } 330 | 331 | func Test65c02FailOn6502(t *testing.T) { 332 | lines := strings.Split(asm65c02, "\n") 333 | prefix := ` 334 | .ARCH 6502 335 | .ORG $1000 336 | ` 337 | for _, line := range lines { 338 | checkASMError(t, prefix+line, "parse error") 339 | } 340 | } 341 | -------------------------------------------------------------------------------- /asm/expr.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2018 Brett Vickers. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package asm 6 | 7 | import ( 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | // 13 | // exprOp 14 | // 15 | 16 | type exprOp byte 17 | 18 | const ( 19 | // operators in descending order of precedence 20 | 21 | // unary operations (0..5) 22 | // opUnaryMinus 23 | // opUnaryPlus 24 | // opUnaryLessThan 25 | // opUnaryGreaterThan 26 | // opUnarySlash 27 | // opBitwiseNEG 28 | 29 | // binary operations (6..15) 30 | // opMultiply 31 | // opDivide 32 | // opModulo 33 | // opAdd 34 | // opSubstract 35 | // opShiftLeft 36 | // opShiftRight 37 | // opBitwiseAND 38 | // opBitwiseXOR 39 | // opBitwiseOR 40 | 41 | // value "operations" (16..19) 42 | opNumber exprOp = iota + 16 43 | opString 44 | opIdentifier 45 | opHere 46 | 47 | // pseudo-ops (20..21) (used only during parsing but not stored in expr's) 48 | opLeftParen 49 | opRightParen 50 | ) 51 | 52 | type opdata struct { 53 | precedence byte 54 | children int 55 | leftAssociative bool 56 | symbol string 57 | eval func(a, b int) int 58 | } 59 | 60 | func (o *opdata) isBinary() bool { 61 | return o.children == 2 62 | } 63 | 64 | func (o *opdata) isUnary() bool { 65 | return o.children == 1 66 | } 67 | 68 | // One entry per exprOp value (order must match) 69 | var ops = []opdata{ 70 | // unary operations 71 | {7, 1, false, "-", func(a, b int) int { return -a }}, // uminus 72 | {7, 1, false, "+", func(a, b int) int { return a }}, // uplus 73 | {7, 1, false, "<", func(a, b int) int { return a & 0xff }}, // ulessthan 74 | {7, 1, false, ">", func(a, b int) int { return (a >> 8) & 0xff }}, // ugreaterthan 75 | {7, 1, false, "/", func(a, b int) int { return (a >> 8) & 0xff }}, // uslash 76 | {7, 1, false, "~", func(a, b int) int { return 0xffffffff ^ a }}, // bitneg 77 | 78 | // binary operations 79 | {6, 2, true, "*", func(a, b int) int { return a * b }}, // multiply 80 | {6, 2, true, "/", func(a, b int) int { return a / b }}, // divide 81 | {6, 2, true, "%", func(a, b int) int { return a % b }}, // modulo 82 | {5, 2, true, "+", func(a, b int) int { return a + b }}, // add 83 | {5, 2, true, "-", func(a, b int) int { return a - b }}, // subtract 84 | {4, 2, true, "<<", func(a, b int) int { return a << uint32(b) }}, // shift_left 85 | {4, 2, true, ">>", func(a, b int) int { return a >> uint32(b) }}, // shift_right 86 | {3, 2, true, "&", func(a, b int) int { return a & b }}, // and 87 | {2, 2, true, "^", func(a, b int) int { return a ^ b }}, // xor 88 | {1, 2, true, "|", func(a, b int) int { return a | b }}, // or 89 | 90 | // value "operations" 91 | {0, 0, false, "", nil}, // numeric literal 92 | {0, 0, false, "", nil}, // string literal 93 | {0, 0, false, "", nil}, // identifier 94 | {0, 0, false, "", nil}, // here 95 | 96 | // pseudo-operations 97 | {0, 0, false, "", nil}, // lparen 98 | {0, 0, false, "", nil}, // rparen 99 | } 100 | 101 | func (op exprOp) isBinary() bool { 102 | return ops[op].isBinary() 103 | } 104 | 105 | func (op exprOp) eval(a, b int) int { 106 | return ops[op].eval(a, b) 107 | } 108 | 109 | func (op exprOp) symbol() string { 110 | return ops[op].symbol 111 | } 112 | 113 | func (op exprOp) isCollapsible() bool { 114 | return ops[op].precedence > 0 115 | } 116 | 117 | // Compare the precendence and associativity of 'op' to 'other'. 118 | // Return true if the shunting yard algorithm should cause an 119 | // expression node collapse. 120 | func (op exprOp) collapses(other exprOp) bool { 121 | if ops[op].leftAssociative { 122 | return ops[op].precedence <= ops[other].precedence 123 | } 124 | return ops[op].precedence < ops[other].precedence 125 | } 126 | 127 | // 128 | // expr 129 | // 130 | 131 | type parseFlags uint32 132 | 133 | const ( 134 | allowParentheses parseFlags = 1 << iota 135 | allowStrings 136 | ) 137 | 138 | // An expr represents a single node in a binary expression tree. 139 | // The root node represents an entire expression. 140 | type expr struct { 141 | line fstring // start of expression line 142 | op exprOp // type of expression 143 | value int // resolved value 144 | bytes int // number of bytes to hold the value 145 | address bool // true if value is an address 146 | evaluated bool // true if value has been evaluated 147 | isString bool // true if expr is a string literal (not a value) 148 | stringLiteral fstring // if op == opString 149 | identifier fstring // if op == opIdentifier 150 | scopeLabel fstring // active scope label when parsing began 151 | child0 *expr // first child in expression tree 152 | child1 *expr // second child in expression tree (parent must be binary op) 153 | } 154 | 155 | // Return the expression as a postfix notation string. 156 | func (e *expr) String() string { 157 | switch { 158 | case e.op == opNumber: 159 | return fmt.Sprintf("%d", e.value) 160 | case e.op == opString: 161 | return e.stringLiteral.str 162 | case e.op == opIdentifier: 163 | if e.address && (e.identifier.startsWithChar('.') || e.identifier.startsWithChar('@')) { 164 | return "~" + e.scopeLabel.str + e.identifier.str 165 | } 166 | return e.identifier.str 167 | case e.op == opHere: 168 | return "$" 169 | case e.op.isBinary(): 170 | return fmt.Sprintf("%s %s %s", e.child0.String(), e.child1.String(), e.op.symbol()) 171 | case !e.op.isBinary(): 172 | return fmt.Sprintf("%s [%s]", e.child0.String(), e.op.symbol()) 173 | default: 174 | return "" 175 | } 176 | } 177 | 178 | // Evaluate the expression tree. 179 | func (e *expr) eval(addr int, constants map[string]*expr, labels map[string]int) bool { 180 | if !e.evaluated { 181 | switch { 182 | case e.op == opNumber: 183 | e.evaluated = true 184 | 185 | case e.op == opString: 186 | e.evaluated = true 187 | 188 | case e.op == opIdentifier: 189 | var ident string 190 | switch { 191 | case e.identifier.startsWithChar('.') || e.identifier.startsWithChar('@'): 192 | ident = "~" + e.scopeLabel.str + e.identifier.str 193 | default: 194 | ident = e.identifier.str 195 | } 196 | if m, ok := constants[ident]; ok { 197 | e.bytes = maxInt(e.bytes, m.bytes) 198 | if m.address { 199 | e.address = true 200 | } 201 | if m.evaluated { 202 | e.value, e.bytes, e.evaluated = m.value, m.bytes, true 203 | } 204 | } 205 | if _, ok := labels[ident]; ok { 206 | e.address, e.bytes = true, 2 207 | } 208 | 209 | case e.op == opHere: 210 | if addr != -1 { 211 | e.value, e.bytes, e.address, e.evaluated = addr, 2, true, true 212 | } 213 | 214 | case e.op.isBinary(): 215 | e.child0.eval(addr, constants, labels) 216 | e.child1.eval(addr, constants, labels) 217 | if e.child0.evaluated && e.child1.evaluated { 218 | e.value = e.op.eval(e.child0.value, e.child1.value) 219 | e.bytes = maxInt(e.child0.bytes, e.child1.bytes) 220 | e.evaluated = true 221 | } 222 | if e.child0.address || e.child1.address { 223 | e.address, e.bytes = true, 2 224 | } 225 | 226 | default: 227 | e.child0.eval(addr, constants, labels) 228 | if e.child0.evaluated { 229 | e.value = e.op.eval(e.child0.value, 0) 230 | e.bytes = e.child0.bytes 231 | e.evaluated = true 232 | } 233 | if e.child0.address { 234 | e.address, e.bytes = true, 2 235 | } 236 | } 237 | } 238 | return e.evaluated 239 | } 240 | 241 | // 242 | // token 243 | // 244 | 245 | type tokentype byte 246 | 247 | const ( 248 | tokenNil tokentype = iota 249 | tokenOp 250 | tokenNumber 251 | tokenString 252 | tokenIdentifier 253 | tokenHere 254 | tokenLeftParen 255 | tokenRightParen 256 | ) 257 | 258 | func (tt tokentype) isValue() bool { 259 | return tt == tokenNumber || tt == tokenIdentifier || tt == tokenHere 260 | } 261 | 262 | func (tt tokentype) canPrecedeUnaryOp() bool { 263 | return tt == tokenOp || tt == tokenLeftParen || tt == tokenNil 264 | } 265 | 266 | type token struct { 267 | typ tokentype 268 | value int 269 | bytes int 270 | stringLiteral fstring 271 | identifier fstring 272 | op exprOp 273 | } 274 | 275 | // 276 | // exprParser 277 | // 278 | 279 | type exprParser struct { 280 | operandStack stack[*expr] 281 | operatorStack stack[exprOp] 282 | parenCounter int 283 | flags parseFlags 284 | prevTokenType tokentype 285 | errors []asmerror 286 | } 287 | 288 | // Parse an expression from the line until it is exhausted. 289 | func (p *exprParser) parse(line, scopeLabel fstring, flags parseFlags) (e *expr, remain fstring, err error) { 290 | p.errors = nil 291 | p.flags = flags 292 | p.prevTokenType = tokenNil 293 | 294 | orig := line 295 | 296 | // Process expression using Dijkstra's shunting-yard algorithm 297 | for err == nil { 298 | 299 | // Parse the next expression token 300 | var token token 301 | token, remain, err = p.parseToken(line) 302 | if err != nil { 303 | break 304 | } 305 | 306 | // We're done when the token parser returns the nil token 307 | if token.typ == tokenNil { 308 | break 309 | } 310 | 311 | // Handle each possible token type 312 | switch token.typ { 313 | 314 | case tokenNumber: 315 | e := &expr{ 316 | op: opNumber, 317 | value: token.value, 318 | bytes: token.bytes, 319 | evaluated: true, 320 | } 321 | p.operandStack.push(e) 322 | 323 | case tokenString: 324 | e := &expr{ 325 | op: opString, 326 | stringLiteral: token.stringLiteral, 327 | isString: true, 328 | bytes: token.bytes, 329 | evaluated: true, 330 | } 331 | p.operandStack.push(e) 332 | 333 | case tokenIdentifier: 334 | e := &expr{ 335 | op: opIdentifier, 336 | identifier: token.identifier, 337 | scopeLabel: scopeLabel, 338 | } 339 | p.operandStack.push(e) 340 | 341 | case tokenHere: 342 | e := &expr{ 343 | op: opHere, 344 | bytes: 2, 345 | address: true, 346 | } 347 | p.operandStack.push(e) 348 | 349 | case tokenOp: 350 | for err == nil && !p.operatorStack.empty() && token.op.collapses(p.operatorStack.peek()) { 351 | err = collapse(&p.operandStack, p.operatorStack.pop()) 352 | if err != nil { 353 | p.addError(line, "invalid expression") 354 | } 355 | } 356 | p.operatorStack.push(token.op) 357 | 358 | case tokenLeftParen: 359 | p.operatorStack.push(opLeftParen) 360 | 361 | case tokenRightParen: 362 | for err == nil { 363 | if p.operatorStack.empty() { 364 | p.addError(line, "mismatched parentheses") 365 | err = errParse 366 | break 367 | } 368 | op := p.operatorStack.pop() 369 | if op == opLeftParen { 370 | break 371 | } 372 | err = collapse(&p.operandStack, op) 373 | if err != nil { 374 | p.addError(line, "invalid expression") 375 | } 376 | } 377 | 378 | } 379 | line = remain 380 | } 381 | 382 | // Collapse any operators (and operands) remaining on the stack 383 | for err == nil && !p.operatorStack.empty() { 384 | err = collapse(&p.operandStack, p.operatorStack.pop()) 385 | if err != nil { 386 | p.addError(line, "invalid expression") 387 | err = errParse 388 | } 389 | } 390 | 391 | if err == nil { 392 | e = p.operandStack.peek() 393 | e.line = orig 394 | } 395 | 396 | p.reset() 397 | return e, remain, err 398 | } 399 | 400 | // Collapse one or more expression nodes on the top of the 401 | // stack into a combined expression node, and push the combined 402 | // node back onto the stack. 403 | func collapse(s *stack[*expr], op exprOp) error { 404 | switch { 405 | case !op.isCollapsible(): 406 | return errParse 407 | 408 | case op.isBinary(): 409 | if len(s.data) < 2 { 410 | return errParse 411 | } 412 | e := &expr{ 413 | op: op, 414 | child1: s.pop(), 415 | child0: s.pop(), 416 | } 417 | s.push(e) 418 | return nil 419 | 420 | default: 421 | if s.empty() { 422 | return errParse 423 | } 424 | e := &expr{ 425 | op: op, 426 | child0: s.pop(), 427 | } 428 | s.push(e) 429 | return nil 430 | } 431 | } 432 | 433 | // Attempt to parse the next token from the line. 434 | func (p *exprParser) parseToken(line fstring) (t token, remain fstring, err error) { 435 | if line.isEmpty() { 436 | return token{typ: tokenNil}, line, nil 437 | } 438 | 439 | switch { 440 | case line.startsWithChar('$') && (len(line.str) == 1 || !hexadecimal(line.str[1])): 441 | remain = line.consume(1) 442 | t.typ = tokenHere 443 | t.bytes = 2 444 | 445 | case line.startsWith(decimal) || line.startsWithChar('$') || line.startsWithChar('%'): 446 | t.value, t.bytes, remain, err = p.parseNumber(line) 447 | t.typ = tokenNumber 448 | if p.prevTokenType.isValue() || p.prevTokenType == tokenRightParen { 449 | p.addError(line, "invalid numeric literal") 450 | err = errParse 451 | } 452 | 453 | case line.startsWithChar('\''): 454 | t.value, remain, err = p.parseCharLiteral(line) 455 | t.bytes = 1 456 | t.typ = tokenNumber 457 | if p.prevTokenType.isValue() || p.prevTokenType == tokenRightParen { 458 | p.addError(line, "invalid character literal") 459 | err = errParse 460 | } 461 | 462 | case line.startsWith(stringQuote) && (p.flags&allowStrings) != 0: 463 | t.stringLiteral, remain, err = p.parseStringLiteral(line) 464 | t.bytes = len(t.stringLiteral.str) 465 | t.typ = tokenString 466 | 467 | case line.startsWithChar('(') && (p.flags&allowParentheses) != 0: 468 | p.parenCounter++ 469 | t.typ, t.op = tokenLeftParen, opLeftParen 470 | remain = line.consume(1) 471 | 472 | case line.startsWithChar(')') && (p.flags&allowParentheses) != 0: 473 | if p.parenCounter == 0 { 474 | p.addError(line, "mismatched parentheses") 475 | err = errParse 476 | remain = line.consume(1) 477 | } else { 478 | p.parenCounter-- 479 | t.typ, t.op, remain = tokenRightParen, opRightParen, line.consume(1) 480 | } 481 | 482 | case line.startsWith(identifierStartChar): 483 | t.typ = tokenIdentifier 484 | t.identifier, remain = line.consumeWhile(identifierChar) 485 | if p.prevTokenType.isValue() || p.prevTokenType == tokenRightParen { 486 | p.addError(line, "invalid identifier") 487 | err = errParse 488 | } 489 | 490 | default: 491 | for i, o := range ops { 492 | if o.children > 0 && line.startsWithString(o.symbol) { 493 | if o.isBinary() || (o.isUnary() && p.prevTokenType.canPrecedeUnaryOp()) { 494 | t.typ, t.op, remain = tokenOp, exprOp(i), line.consume(len(o.symbol)) 495 | break 496 | } 497 | } 498 | } 499 | if t.typ != tokenOp { 500 | p.addError(line, "invalid token") 501 | err = errParse 502 | } 503 | } 504 | 505 | p.prevTokenType = t.typ 506 | remain = remain.consumeWhitespace() 507 | return t, remain, err 508 | } 509 | 510 | // Parse a number from the line. The following numeric formats are allowed: 511 | // 512 | // [0-9]+ Decimal number 513 | // $[0-9a-fA-F]+ Hexadecimal number 514 | // 0x[0-9a-fA-F]+ Hexadecimal number 515 | // 0b[01]+ Binary number 516 | // 517 | // The function returns the parsed value, the number of bytes used to 518 | // hold the value, the remainder of the line, and any parsing error 519 | // encountered. The number of bytes used to hold the value will be 1, 2 520 | // or 4. 521 | // 522 | // If a hexadecimal or binary value is parsed, the length of the parsed 523 | // string is used to determine how many bytes are required to hold the 524 | // value. For example, if the parsed string is "0x0020", the number of bytes 525 | // required to hold the value is 2, while if the parse string is "0x20", the 526 | // number of bytes required is 1. 527 | // 528 | // If a decimal number if parsed, the length of the parsed string is ignored, 529 | // and the minimum number of bytes required to hold the value is returned. 530 | func (p *exprParser) parseNumber(line fstring) (value, bytes int, remain fstring, err error) { 531 | // Select decimal, hexadecimal or binary depending on the prefix. 532 | base, fn, bitsPerChar, negative := 10, decimal, 0, false 533 | if line.startsWithChar('-') { 534 | negative = true 535 | line = line.consume(1) 536 | } 537 | 538 | switch { 539 | case line.startsWithChar('$'): 540 | line = line.consume(1) 541 | base, fn, bitsPerChar = 16, hexadecimal, 4 542 | case line.startsWithString("0x"): 543 | line = line.consume(2) 544 | base, fn, bitsPerChar = 16, hexadecimal, 4 545 | case line.startsWithChar('%'): 546 | line = line.consume(1) 547 | base, fn, bitsPerChar = 2, binarynum, 1 548 | case line.startsWithString("0b"): 549 | line = line.consume(2) 550 | base, fn, bitsPerChar = 2, binarynum, 1 551 | } 552 | 553 | numstr, remain := line.consumeWhile(fn) 554 | 555 | num64, converr := strconv.ParseInt(numstr.str, base, 32) 556 | if converr != nil { 557 | p.addError(numstr, "invalid numeric literal") 558 | err = errParse 559 | } 560 | 561 | value = int(num64) 562 | 563 | if base == 10 { 564 | switch negative { 565 | case true: 566 | switch { 567 | case value <= 0x80: 568 | return 0x100 - value, 1, remain, err 569 | case value <= 0x8000: 570 | return 0x10000 - value, 2, remain, err 571 | default: 572 | return 0x100000000 - value, 4, remain, err 573 | } 574 | case false: 575 | switch { 576 | case value <= 0xff: 577 | return value, 1, remain, err 578 | case value <= 0xffff: 579 | return value, 2, remain, err 580 | default: 581 | return value, 4, remain, err 582 | } 583 | } 584 | } 585 | 586 | bytes = (len(numstr.str)*bitsPerChar + 7) / 8 587 | if bytes > 2 { 588 | bytes = 4 589 | } 590 | 591 | if negative { 592 | value = -value 593 | } 594 | 595 | return value, bytes, remain, err 596 | } 597 | 598 | func (p *exprParser) parseStringLiteral(line fstring) (s, remain fstring, err error) { 599 | quote := line.str[0] 600 | remain = line.consume(1) 601 | 602 | s, remain = remain.consumeUntilChar(quote) 603 | if remain.isEmpty() { 604 | p.addError(remain, "string literal missing closing quote") 605 | return fstring{}, remain, errParse 606 | } 607 | 608 | remain = remain.consume(1) 609 | return s, remain, nil 610 | } 611 | 612 | func (p *exprParser) parseCharLiteral(line fstring) (value int, remain fstring, err error) { 613 | if len(line.str) < 3 || line.str[2] != '\'' { 614 | p.addError(line, "invalid character literal") 615 | return 0, fstring{}, errParse 616 | } 617 | 618 | value = int(line.str[1]) 619 | remain = line.consume(3) 620 | return value, remain, nil 621 | } 622 | 623 | func (p *exprParser) addError(line fstring, msg string) { 624 | p.errors = append(p.errors, asmerror{line, msg}) 625 | } 626 | 627 | func (p *exprParser) reset() { 628 | p.operandStack.data, p.operatorStack.data = nil, nil 629 | p.parenCounter = 0 630 | } 631 | 632 | // 633 | // stack 634 | // 635 | 636 | type stack[T any] struct { 637 | data []T 638 | } 639 | 640 | func (s *stack[T]) push(value T) { 641 | s.data = append(s.data, value) 642 | } 643 | 644 | func (s *stack[T]) pop() T { 645 | i := len(s.data) - 1 646 | value := s.data[i] 647 | s.data = s.data[:i] 648 | return value 649 | } 650 | 651 | func (s *stack[T]) empty() bool { 652 | return len(s.data) == 0 653 | } 654 | 655 | func (s *stack[T]) peek() T { 656 | return s.data[len(s.data)-1] 657 | } 658 | -------------------------------------------------------------------------------- /asm/fstring.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2018 Brett Vickers. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package asm 6 | 7 | // An fstring is a string that keeps track of its position within the 8 | // file from which it was read. 9 | type fstring struct { 10 | fileIndex int // index of file in the assembly 11 | row int // 1-based line number of substring 12 | column int // 0-based column of start of substring 13 | str string // the actual substring of interest 14 | full string // the full line as originally read from the file 15 | } 16 | 17 | func newFstring(fileIndex, row int, str string) fstring { 18 | return fstring{fileIndex, row, 0, str, str} 19 | } 20 | 21 | func (l *fstring) String() string { 22 | return l.str 23 | } 24 | 25 | func (l *fstring) advanceColumn(n int) int { 26 | c := l.column 27 | for i := 0; i < n; i++ { 28 | if l.str[i] == '\t' { 29 | c += 8 - (c % 8) 30 | } else { 31 | c++ 32 | } 33 | } 34 | return c 35 | } 36 | 37 | func (l fstring) consume(n int) fstring { 38 | col := l.advanceColumn(n) 39 | return fstring{l.fileIndex, l.row, col, l.str[n:], l.full} 40 | } 41 | 42 | func (l fstring) trunc(n int) fstring { 43 | return fstring{l.fileIndex, l.row, l.column, l.str[:n], l.full} 44 | } 45 | 46 | func (l *fstring) isEmpty() bool { 47 | return len(l.str) == 0 48 | } 49 | 50 | func (l *fstring) startsWith(fn func(c byte) bool) bool { 51 | return len(l.str) > 0 && fn(l.str[0]) 52 | } 53 | 54 | func (l *fstring) startsWithChar(c byte) bool { 55 | return len(l.str) > 0 && l.str[0] == c 56 | } 57 | 58 | func (l *fstring) startsWithString(s string) bool { 59 | return len(l.str) >= len(s) && l.str[:len(s)] == s 60 | } 61 | 62 | func (l fstring) consumeWhitespace() fstring { 63 | return l.consume(l.scanWhile(whitespace)) 64 | } 65 | 66 | func (l *fstring) scanWhile(fn func(c byte) bool) int { 67 | i := 0 68 | for ; i < len(l.str) && fn(l.str[i]); i++ { 69 | } 70 | return i 71 | } 72 | 73 | func (l *fstring) scanUntil(fn func(c byte) bool) int { 74 | i := 0 75 | for ; i < len(l.str) && !fn(l.str[i]); i++ { 76 | } 77 | return i 78 | } 79 | 80 | func (l *fstring) scanUntilChar(c byte) int { 81 | i := 0 82 | for ; i < len(l.str) && l.str[i] != c; i++ { 83 | } 84 | return i 85 | } 86 | 87 | func (l *fstring) consumeWhile(fn func(c byte) bool) (consumed, remain fstring) { 88 | i := l.scanWhile(fn) 89 | consumed, remain = l.trunc(i), l.consume(i) 90 | return 91 | } 92 | 93 | func (l *fstring) consumeUntil(fn func(c byte) bool) (consumed, remain fstring) { 94 | i := l.scanUntil(fn) 95 | consumed, remain = l.trunc(i), l.consume(i) 96 | return 97 | } 98 | 99 | func (l *fstring) consumeUntilChar(c byte) (consumed, remain fstring) { 100 | i := l.scanUntilChar(c) 101 | consumed, remain = l.trunc(i), l.consume(i) 102 | return 103 | } 104 | 105 | func (l *fstring) consumeUntilUnquotedChar(c byte) (consumed, remain fstring) { 106 | var quote byte 107 | i := 0 108 | for ; i < len(l.str); i++ { 109 | if quote == 0 { 110 | if l.str[i] == c { 111 | break 112 | } 113 | if l.str[i] == '\'' || l.str[i] == '"' { 114 | quote = l.str[i] 115 | } 116 | } else { 117 | if l.str[i] == quote { 118 | quote = 0 119 | } 120 | } 121 | } 122 | consumed, remain = l.trunc(i), l.consume(i) 123 | return 124 | } 125 | 126 | func (l fstring) stripTrailingComment() fstring { 127 | lastNonWS := 0 128 | for i := 0; i < len(l.str); i++ { 129 | if comment(l.str[i]) { 130 | break 131 | } 132 | if stringQuote(l.str[i]) { 133 | q := l.str[i] 134 | i++ 135 | for ; i < len(l.str) && l.str[i] != q; i++ { 136 | } 137 | lastNonWS = i 138 | if i == len(l.str) { 139 | break 140 | } 141 | } 142 | if !whitespace(l.str[i]) { 143 | lastNonWS = i + 1 144 | } 145 | } 146 | return l.trunc(lastNonWS) 147 | } 148 | 149 | // 150 | // character helper functions 151 | // 152 | 153 | func whitespace(c byte) bool { 154 | return c == ' ' || c == '\t' 155 | } 156 | 157 | func wordChar(c byte) bool { 158 | return c != ' ' && c != '\t' 159 | } 160 | 161 | func alpha(c byte) bool { 162 | return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') 163 | } 164 | 165 | func decimal(c byte) bool { 166 | return (c >= '0' && c <= '9') 167 | } 168 | 169 | func comment(c byte) bool { 170 | return c == ';' 171 | } 172 | 173 | func hexadecimal(c byte) bool { 174 | return decimal(c) || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f') 175 | } 176 | 177 | func binarynum(c byte) bool { 178 | return c == '0' || c == '1' 179 | } 180 | 181 | func labelStartChar(c byte) bool { 182 | return alpha(c) || c == '_' || c == '.' || c == '@' 183 | } 184 | 185 | func labelChar(c byte) bool { 186 | return alpha(c) || decimal(c) || c == '_' || c == '.' || c == '@' 187 | } 188 | 189 | func identifierStartChar(c byte) bool { 190 | return alpha(c) || c == '_' || c == '.' || c == '@' 191 | } 192 | 193 | func identifierChar(c byte) bool { 194 | return alpha(c) || decimal(c) || c == '_' || c == '.' || c == '@' || c == ':' 195 | } 196 | 197 | func stringQuote(c byte) bool { 198 | return c == '"' || c == '\'' 199 | } 200 | -------------------------------------------------------------------------------- /asm/sourcemap.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2018 Brett Vickers. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package asm 6 | 7 | import ( 8 | "bufio" 9 | "bytes" 10 | "cmp" 11 | "encoding/binary" 12 | "errors" 13 | "fmt" 14 | "io" 15 | "slices" 16 | "sort" 17 | ) 18 | 19 | // A SourceMap describes the mapping between source code line numbers and 20 | // assembly code addresses. 21 | type SourceMap struct { 22 | Origin uint16 23 | Size uint32 24 | CRC uint32 25 | Files []string 26 | Lines []SourceLine 27 | Exports []Export 28 | } 29 | 30 | // A SourceLine represents a mapping between a machine code address and 31 | // the source code file and line number used to generate it. 32 | type SourceLine struct { 33 | Address int // Machine code address 34 | FileIndex int // Source code file index 35 | Line int // Source code line number 36 | } 37 | 38 | // Encoding flags 39 | const ( 40 | continued byte = 1 << 7 41 | negative byte = 1 << 6 42 | fileIndexChanged byte = 1 << 5 43 | ) 44 | 45 | // NewSourceMap creates an empty source map. 46 | func NewSourceMap() *SourceMap { 47 | return &SourceMap{ 48 | Files: []string{}, 49 | Lines: []SourceLine{}, 50 | Exports: []Export{}, 51 | } 52 | } 53 | 54 | // Find searches the source map for a source code line corresponding to the 55 | // requested address. 56 | func (s *SourceMap) Find(addr int) (filename string, line int, err error) { 57 | i := sort.Search(len(s.Lines), func(i int) bool { 58 | return s.Lines[i].Address >= addr 59 | }) 60 | if i < len(s.Lines) && s.Lines[i].Address == addr { 61 | return s.Files[s.Lines[i].FileIndex], s.Lines[i].Line, nil 62 | } 63 | return "", 0, fmt.Errorf("address $%04X not found in source file", addr) 64 | } 65 | 66 | // ClearRange clears portions of the source map that reference the 67 | // address range between `origin` and `origin+size`. 68 | func (s *SourceMap) ClearRange(origin, size int) { 69 | min := uint16(origin) 70 | max := uint16(origin + size) 71 | 72 | // Filter out original exports covered by the new map's address range. 73 | exports := make([]Export, 0, len(s.Exports)) 74 | for _, e := range s.Exports { 75 | if e.Address < min || e.Address > max { 76 | exports = append(exports, e) 77 | } 78 | } 79 | 80 | // Filter out original source lines covered by the new map's address 81 | // range. Track only the files that remain referenced. 82 | fileCount := 0 83 | fileMap := make(map[string]int) // filename -> file index 84 | lines := make([]SourceLine, 0, len(s.Lines)) 85 | for _, l := range s.Lines { 86 | if uint16(l.Address) < min || uint16(l.Address) >= max { 87 | filename := s.Files[l.FileIndex] 88 | if fileIndex, ok := fileMap[filename]; ok { 89 | l.FileIndex = fileIndex 90 | } else { 91 | fileIndex = fileCount 92 | fileMap[filename] = fileIndex 93 | l.FileIndex = fileIndex 94 | fileCount++ 95 | } 96 | lines = append(lines, l) 97 | } 98 | } 99 | 100 | // Build the files array from the file map. 101 | files := make([]string, len(fileMap)) 102 | for f, i := range fileMap { 103 | files[i] = f 104 | } 105 | 106 | s.Files = files 107 | s.Lines = lines 108 | s.Exports = exports 109 | } 110 | 111 | // Merge merges another source map (s2) into this source map. 112 | func (s *SourceMap) Merge(s2 *SourceMap) { 113 | // Clear the portion of the original source map that references addresses 114 | // in the new map's range. 115 | s.ClearRange(int(s2.Origin), int(s2.Size)) 116 | 117 | // Add exports from the new map. 118 | s.Exports = sortExports(append(s.Exports, s2.Exports...)) 119 | 120 | // Build a mapping from filename to file index. 121 | fileCount := 0 122 | fileMap := make(map[string]int) 123 | for i, f := range s.Files { 124 | fileMap[f] = i 125 | fileCount++ 126 | } 127 | 128 | // Add source lines from the new map. 129 | for _, l := range s2.Lines { 130 | filename := s2.Files[l.FileIndex] 131 | if fileIndex, ok := fileMap[filename]; ok { 132 | l.FileIndex = fileIndex 133 | } else { 134 | fileIndex = fileCount 135 | fileMap[filename] = fileIndex 136 | l.FileIndex = fileIndex 137 | fileCount++ 138 | } 139 | s.Lines = append(s.Lines, l) 140 | } 141 | s.Lines = sortLines(s.Lines) 142 | 143 | // Build the files array from the file map. 144 | s.Files = make([]string, len(fileMap)) 145 | for f, i := range fileMap { 146 | s.Files[i] = f 147 | } 148 | } 149 | 150 | // ReadFrom reads the contents of an assembly source map. 151 | func (s *SourceMap) ReadFrom(r io.Reader) (n int64, err error) { 152 | rr := bufio.NewReader(r) 153 | 154 | b := make([]byte, 26) 155 | nn, err := io.ReadFull(rr, b) 156 | n += int64(nn) 157 | if err != nil { 158 | return n, err 159 | } 160 | 161 | if len(b) < 16 || !bytes.Equal(b[0:4], []byte(sourceMapSignature)) { 162 | return n, errors.New("invalid source map format") 163 | } 164 | if b[4] != versionMajor || b[5] != versionMinor { 165 | return n, errors.New("invalid source map version") 166 | } 167 | 168 | s.Origin = binary.LittleEndian.Uint16(b[6:8]) 169 | s.Size = binary.LittleEndian.Uint32(b[8:12]) 170 | s.CRC = binary.LittleEndian.Uint32(b[12:16]) 171 | fileCount := int(binary.LittleEndian.Uint16(b[16:18])) 172 | lineCount := int(binary.LittleEndian.Uint32(b[18:22])) 173 | exportCount := int(binary.LittleEndian.Uint32(b[22:26])) 174 | 175 | s.Files = make([]string, fileCount) 176 | for i := 0; i < fileCount; i++ { 177 | file, err := rr.ReadString(0) 178 | n += int64(len(file)) 179 | if err != nil { 180 | return n, err 181 | } 182 | s.Files[i] = file[:len(file)-1] 183 | } 184 | 185 | s.Lines = make([]SourceLine, 0, lineCount) 186 | if lineCount > 0 { 187 | var line SourceLine 188 | for i := 0; i < lineCount; i++ { 189 | var nn int 190 | line, nn, err = decodeSourceLine(rr, line) 191 | n += int64(nn) 192 | if err != nil { 193 | return n, err 194 | } 195 | s.Lines = append(s.Lines, line) 196 | } 197 | } 198 | 199 | s.Exports = make([]Export, exportCount) 200 | for i := 0; i < exportCount; i++ { 201 | label, err := rr.ReadString(0) 202 | n += int64(len(label)) 203 | if err != nil { 204 | return n, err 205 | } 206 | s.Exports[i].Label = label[:len(label)-1] 207 | 208 | nn, err = io.ReadFull(rr, b[:2]) 209 | n += int64(nn) 210 | if err != nil { 211 | return n, err 212 | } 213 | s.Exports[i].Address = binary.LittleEndian.Uint16(b[0:2]) 214 | } 215 | 216 | return n, nil 217 | } 218 | 219 | // WriteTo writes the contents of an assembly source map to an output 220 | // stream. 221 | func (s *SourceMap) WriteTo(w io.Writer) (n int64, err error) { 222 | fileCount := uint16(len(s.Files)) 223 | lineCount := uint32(len(s.Lines)) 224 | exportCount := uint32(len(s.Exports)) 225 | 226 | ww := bufio.NewWriter(w) 227 | 228 | var hdr [26]byte 229 | copy(hdr[:], []byte(sourceMapSignature)) 230 | hdr[4] = versionMajor 231 | hdr[5] = versionMinor 232 | binary.LittleEndian.PutUint16(hdr[6:8], s.Origin) 233 | binary.LittleEndian.PutUint32(hdr[8:12], s.Size) 234 | binary.LittleEndian.PutUint32(hdr[12:16], s.CRC) 235 | binary.LittleEndian.PutUint16(hdr[16:18], fileCount) 236 | binary.LittleEndian.PutUint32(hdr[18:22], lineCount) 237 | binary.LittleEndian.PutUint32(hdr[22:26], exportCount) 238 | nn, err := ww.Write(hdr[:]) 239 | n += int64(nn) 240 | if err != nil { 241 | return n, err 242 | } 243 | 244 | for _, f := range s.Files { 245 | nn, err = ww.WriteString(f) 246 | n += int64(nn) 247 | if err != nil { 248 | return n, err 249 | } 250 | err = ww.WriteByte(0) 251 | if err != nil { 252 | return 0, err 253 | } 254 | n++ 255 | } 256 | 257 | if len(s.Lines) > 0 { 258 | var prev SourceLine 259 | for _, line := range s.Lines { 260 | nn, err = encodeSourceLine(ww, prev, line) 261 | n += int64(nn) 262 | if err != nil { 263 | return n, err 264 | } 265 | prev = line 266 | } 267 | } 268 | 269 | for _, e := range s.Exports { 270 | nn, err = ww.WriteString(e.Label) 271 | n += int64(nn) 272 | if err != nil { 273 | return n, err 274 | } 275 | ww.WriteByte(0) 276 | n++ 277 | 278 | var b [2]byte 279 | binary.LittleEndian.PutUint16(b[:], e.Address) 280 | nn, err = ww.Write(b[:]) 281 | n += int64(nn) 282 | if err != nil { 283 | return n, err 284 | } 285 | } 286 | 287 | ww.Flush() 288 | 289 | return n, nil 290 | } 291 | 292 | func decodeSourceLine(r *bufio.Reader, prev SourceLine) (line SourceLine, n int, err error) { 293 | da, nn, err := decode67(r) 294 | n += nn 295 | if err != nil { 296 | return line, n, err 297 | } 298 | 299 | var dl int 300 | var f bool 301 | dl, f, nn, err = decode57(r) 302 | n += nn 303 | if err != nil { 304 | return line, n, err 305 | } 306 | 307 | var df int 308 | if f { 309 | df, nn, err = decode67(r) 310 | n += nn 311 | if err != nil { 312 | return line, n, err 313 | } 314 | } 315 | 316 | line.Address = prev.Address + da 317 | line.FileIndex = prev.FileIndex + df 318 | line.Line = prev.Line + dl 319 | return line, n, nil 320 | } 321 | 322 | func decode7(r *bufio.Reader) (value int, n int, err error) { 323 | var shift uint 324 | for { 325 | var b byte 326 | b, err = r.ReadByte() 327 | if err != nil { 328 | return 0, n, err 329 | } 330 | n++ 331 | 332 | value |= (int(b&0x7f) << shift) 333 | shift += 7 334 | 335 | if (b & continued) == 0 { 336 | break 337 | } 338 | } 339 | return value, n, nil 340 | } 341 | 342 | func decode57(r *bufio.Reader) (value int, f bool, n int, err error) { 343 | var b byte 344 | b, err = r.ReadByte() 345 | if err != nil { 346 | return 0, f, n, err 347 | } 348 | n++ 349 | 350 | value = int(b & 0x1f) 351 | 352 | f = (b & fileIndexChanged) != 0 353 | 354 | neg := (b & negative) != 0 355 | 356 | if (b & continued) != 0 { 357 | var vl, nn int 358 | vl, nn, err = decode7(r) 359 | n += nn 360 | if err != nil { 361 | return 0, f, n, err 362 | } 363 | 364 | value |= vl << 5 365 | } 366 | 367 | if neg { 368 | value = -value 369 | } 370 | 371 | return value, f, n, nil 372 | } 373 | 374 | func decode67(r *bufio.Reader) (value int, n int, err error) { 375 | var b byte 376 | b, err = r.ReadByte() 377 | if err != nil { 378 | return 0, n, err 379 | } 380 | n++ 381 | 382 | value = int(b & 0x3f) 383 | 384 | neg := (b & negative) != 0 385 | 386 | if (b & continued) != 0 { 387 | var vl, nn int 388 | vl, nn, err = decode7(r) 389 | n += nn 390 | if err != nil { 391 | return 0, n, err 392 | } 393 | 394 | value |= vl << 6 395 | } 396 | 397 | if neg { 398 | value = -value 399 | } 400 | 401 | return value, n, nil 402 | } 403 | 404 | func encodeSourceLine(w *bufio.Writer, l0, l1 SourceLine) (n int, err error) { 405 | da := l1.Address - l0.Address 406 | df := l1.FileIndex - l0.FileIndex 407 | dl := l1.Line - l0.Line 408 | 409 | nn, err := encode67(w, da) 410 | n += nn 411 | if err != nil { 412 | return n, err 413 | } 414 | 415 | nn, err = encode57(w, dl, df != 0) 416 | n += nn 417 | if err != nil { 418 | return n, err 419 | } 420 | 421 | if df != 0 { 422 | nn, err = encode67(w, df) 423 | n += nn 424 | } 425 | return n, err 426 | } 427 | 428 | func encode7(w *bufio.Writer, v int) (n int, err error) { 429 | for v != 0 { 430 | var b byte 431 | if v >= 0x80 { 432 | b |= continued 433 | } 434 | b |= (byte(v) & 0x7f) 435 | 436 | err = w.WriteByte(b) 437 | if err != nil { 438 | return n, err 439 | } 440 | n++ 441 | 442 | v >>= 7 443 | } 444 | return n, nil 445 | } 446 | 447 | func encode57(w *bufio.Writer, v int, f bool) (n int, err error) { 448 | var b byte 449 | if f { 450 | b |= fileIndexChanged 451 | } 452 | if v < 0 { 453 | b |= negative 454 | v = -v 455 | } 456 | if v >= 0x20 { 457 | b |= continued 458 | } 459 | 460 | b |= (byte(v) & 0x1f) 461 | err = w.WriteByte(b) 462 | if err != nil { 463 | return n, err 464 | } 465 | n++ 466 | v >>= 5 467 | 468 | nn, err := encode7(w, v) 469 | n += nn 470 | return n, err 471 | } 472 | 473 | func encode67(w *bufio.Writer, v int) (n int, err error) { 474 | var b byte 475 | if v < 0 { 476 | b |= negative 477 | v = -v 478 | } 479 | if v >= 0x40 { 480 | b |= continued 481 | } 482 | 483 | b |= (byte(v) & 0x3f) 484 | err = w.WriteByte(b) 485 | if err != nil { 486 | return n, err 487 | } 488 | n++ 489 | v >>= 6 490 | 491 | nn, err := encode7(w, v) 492 | n += nn 493 | return n, err 494 | } 495 | 496 | func sortLines(lines []SourceLine) []SourceLine { 497 | cmp := func(a, b SourceLine) int { 498 | return cmp.Compare(a.Address, b.Address) 499 | } 500 | slices.SortFunc(lines, cmp) 501 | return lines 502 | } 503 | 504 | func sortExports(exports []Export) []Export { 505 | cmp := func(a, b Export) int { 506 | return cmp.Compare(a.Address, b.Address) 507 | } 508 | slices.SortFunc(exports, cmp) 509 | return exports 510 | } 511 | -------------------------------------------------------------------------------- /asm/util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2018 Brett Vickers. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package asm 6 | 7 | var hex = "0123456789ABCDEF" 8 | 9 | func maxInt(a, b int) int { 10 | if a > b { 11 | return a 12 | } 13 | return b 14 | } 15 | 16 | func hexchar(c byte) byte { 17 | switch { 18 | case c >= '0' && c <= '9': 19 | return c - '0' 20 | case c >= 'a' && c <= 'f': 21 | return c - 'a' + 10 22 | case c >= 'A' && c <= 'F': 23 | return c - 'A' + 10 24 | default: 25 | return 0 26 | } 27 | } 28 | 29 | func hexToByte(s string) byte { 30 | return hexchar(s[0])<<4 | hexchar(s[1]) 31 | } 32 | 33 | // Return a little-endian representation of the value using the requested 34 | // number of bytes. 35 | func toBytes(bytes, value int) []byte { 36 | switch bytes { 37 | case 1: 38 | return []byte{byte(value)} 39 | case 2: 40 | return []byte{byte(value), byte(value >> 8)} 41 | default: 42 | return []byte{byte(value), byte(value >> 8), byte(value >> 16), byte(value >> 24)} 43 | } 44 | } 45 | 46 | // Return a hexadecimal string representation of a byte slice. 47 | func byteString(b []byte) string { 48 | if len(b) < 1 { 49 | return "" 50 | } 51 | 52 | s := make([]byte, len(b)*3-1) 53 | i, j := 0, 0 54 | for n := len(b) - 1; i < n; i, j = i+1, j+3 { 55 | s[j+0] = hex[(b[i] >> 4)] 56 | s[j+1] = hex[(b[i] & 0x0f)] 57 | s[j+2] = ' ' 58 | } 59 | s[j+0] = hex[(b[i] >> 4)] 60 | s[j+1] = hex[(b[i] & 0x0f)] 61 | return string(s) 62 | } 63 | -------------------------------------------------------------------------------- /cpu/README.md: -------------------------------------------------------------------------------- 1 | go6502/cpu 2 | ========== 3 | 4 | Initialize a 64KB memory space and load some machine code from a byte 5 | slice. 6 | ```go 7 | mem := go6502.NewFlatMemory() 8 | 9 | // Load a byte slice of machine code at address 0x600 10 | mem.StoreBytes(0x600, []byte{0xa2, 0x05, 0xa1, 0x02, 0xa9, 0x08, 0x8d, 11 | 0x01, 0x02, 0x69, 0xfe, 0x8d, 0x00, 0x02, 0xa9, 0xff, 0xad, 0x00, 12 | 0x02, 0xa2, 0xee, 0x4c, 0x00, 0x06}) 13 | ``` 14 | 15 | Create an emulated CMOS 65c02 CPU and initialize its program counter. 16 | ```go 17 | cpu := go6502.NewCPU(go6502.CMOS, mem) 18 | cpu.SetPC(0x600) 19 | ``` 20 | 21 | Use the `Step()` function to manually step the CPU one instruction at a time. 22 | ```go 23 | for i := 0; i < 20; i++ { 24 | cpu.Step() 25 | } 26 | ``` 27 | 28 | Use the `go6502/disasm` package to disassemble machine code while stepping 29 | the CPU. 30 | ```go 31 | for i := 0; i < 20; i++ { 32 | pc := cpu.Reg.PC 33 | line, _, _ := disasm.Disassemble(cpu.Mem, pc) 34 | cpu.Step() 35 | fmt.Printf("%04X- %-12s A=%02X X=%02X Y=%02X PS=[%s] SP=%02X PC=%04X Cycles=%d\n", 36 | pc, line, 37 | cpu.Reg.A, cpu.Reg.X, cpu.Reg.Y, psString(&cpu.Reg), 38 | cpu.Reg.SP, cpu.Reg.PC, 39 | cpu.Cycles) 40 | } 41 | ``` 42 | 43 | Output: 44 | ``` 45 | 0600- LDX #$05 A=00 X=05 Y=00 PS=[------] SP=FF PC=0602 Cycles=2 46 | 0602- LDA ($02,X) A=00 X=05 Y=00 PS=[-Z----] SP=FF PC=0604 Cycles=8 47 | 0604- LDA #$08 A=08 X=05 Y=00 PS=[------] SP=FF PC=0606 Cycles=10 48 | 0606- STA $0201 A=08 X=05 Y=00 PS=[------] SP=FF PC=0609 Cycles=14 49 | 0609- ADC #$FE A=06 X=05 Y=00 PS=[C-----] SP=FF PC=060B Cycles=16 50 | 060B- STA $0200 A=06 X=05 Y=00 PS=[C-----] SP=FF PC=060E Cycles=20 51 | 060E- LDA #$FF A=FF X=05 Y=00 PS=[C----N] SP=FF PC=0610 Cycles=22 52 | 0610- LDA $0200 A=06 X=05 Y=00 PS=[C-----] SP=FF PC=0613 Cycles=26 53 | 0613- LDX #$EE A=06 X=EE Y=00 PS=[C----N] SP=FF PC=0615 Cycles=28 54 | 0615- JMP $0600 A=06 X=EE Y=00 PS=[C----N] SP=FF PC=0600 Cycles=31 55 | 0600- LDX #$05 A=06 X=05 Y=00 PS=[C-----] SP=FF PC=0602 Cycles=33 56 | 0602- LDA ($02,X) A=00 X=05 Y=00 PS=[CZ----] SP=FF PC=0604 Cycles=39 57 | 0604- LDA #$08 A=08 X=05 Y=00 PS=[C-----] SP=FF PC=0606 Cycles=41 58 | 0606- STA $0201 A=08 X=05 Y=00 PS=[C-----] SP=FF PC=0609 Cycles=45 59 | 0609- ADC #$FE A=07 X=05 Y=00 PS=[C-----] SP=FF PC=060B Cycles=47 60 | 060B- STA $0200 A=07 X=05 Y=00 PS=[C-----] SP=FF PC=060E Cycles=51 61 | 060E- LDA #$FF A=FF X=05 Y=00 PS=[C----N] SP=FF PC=0610 Cycles=53 62 | 0610- LDA $0200 A=07 X=05 Y=00 PS=[C-----] SP=FF PC=0613 Cycles=57 63 | 0613- LDX #$EE A=07 X=EE Y=00 PS=[C----N] SP=FF PC=0615 Cycles=59 64 | 0615- JMP $0600 A=07 X=EE Y=00 PS=[C----N] SP=FF PC=0600 Cycles=62 65 | ``` 66 | 67 | Here is the implementation of the helper function `psString` used in the 68 | example: 69 | ```go 70 | func psString(r *go6502.Registers) string { 71 | v := func(bit bool, ch byte) byte { 72 | if bit { 73 | return ch 74 | } 75 | return '-' 76 | } 77 | b := []byte{ 78 | v(r.Carry, 'C'), 79 | v(r.Zero, 'Z'), 80 | v(r.InterruptDisable, 'I'), 81 | v(r.Decimal, 'D'), 82 | v(r.Overflow, 'O'), 83 | v(r.Negative, 'N'), 84 | } 85 | return string(b) 86 | } 87 | ``` 88 | -------------------------------------------------------------------------------- /cpu/cpu.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2018 Brett Vickers. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package cpu implements a 6502 CPU instruction 6 | // set and emulator. 7 | package cpu 8 | 9 | // Architecture selects the CPU chip: 6502 or 65c02 10 | type Architecture byte 11 | 12 | const ( 13 | // NMOS 6502 CPU 14 | NMOS Architecture = iota 15 | 16 | // CMOS 65c02 CPU 17 | CMOS 18 | ) 19 | 20 | // BrkHandler is an interface implemented by types that wish to be notified 21 | // when a BRK instruction is about to be executed. 22 | type BrkHandler interface { 23 | OnBrk(cpu *CPU) 24 | } 25 | 26 | // CPU represents a single 6502 CPU. It contains a pointer to the 27 | // memory associated with the CPU. 28 | type CPU struct { 29 | Arch Architecture // CPU architecture 30 | Reg Registers // CPU registers 31 | Mem Memory // assigned memory 32 | Cycles uint64 // total executed CPU cycles 33 | LastPC uint16 // Previous program counter 34 | InstSet *InstructionSet // Instruction set used by the CPU 35 | pageCrossed bool 36 | deltaCycles int8 37 | debugger *Debugger 38 | brkHandler BrkHandler 39 | storeByte func(cpu *CPU, addr uint16, v byte) 40 | } 41 | 42 | // Interrupt vectors 43 | const ( 44 | vectorNMI = 0xfffa 45 | vectorReset = 0xfffc 46 | vectorIRQ = 0xfffe 47 | vectorBRK = 0xfffe 48 | ) 49 | 50 | // NewCPU creates an emulated 6502 CPU bound to the specified memory. 51 | func NewCPU(arch Architecture, m Memory) *CPU { 52 | cpu := &CPU{ 53 | Arch: arch, 54 | Mem: m, 55 | InstSet: GetInstructionSet(arch), 56 | storeByte: (*CPU).storeByteNormal, 57 | } 58 | 59 | cpu.Reg.Init() 60 | return cpu 61 | } 62 | 63 | // SetPC updates the CPU program counter to 'addr'. 64 | func (cpu *CPU) SetPC(addr uint16) { 65 | cpu.Reg.PC = addr 66 | } 67 | 68 | // GetInstruction returns the instruction opcode at the requested address. 69 | func (cpu *CPU) GetInstruction(addr uint16) *Instruction { 70 | opcode := cpu.Mem.LoadByte(addr) 71 | return cpu.InstSet.Lookup(opcode) 72 | } 73 | 74 | // NextAddr returns the address of the next instruction following the 75 | // instruction at addr. 76 | func (cpu *CPU) NextAddr(addr uint16) uint16 { 77 | opcode := cpu.Mem.LoadByte(addr) 78 | inst := cpu.InstSet.Lookup(opcode) 79 | return addr + uint16(inst.Length) 80 | } 81 | 82 | // Step the cpu by one instruction. 83 | func (cpu *CPU) Step() { 84 | // Grab the next opcode at the current PC 85 | opcode := cpu.Mem.LoadByte(cpu.Reg.PC) 86 | 87 | // Look up the instruction data for the opcode 88 | inst := cpu.InstSet.Lookup(opcode) 89 | 90 | // If the instruction is undefined, reset the CPU (for now). 91 | if inst.fn == nil { 92 | cpu.reset() 93 | return 94 | } 95 | 96 | // If a BRK instruction is about to be executed and a BRK handler has been 97 | // installed, call the BRK handler instead of executing the instruction. 98 | if inst.Opcode == 0x00 && cpu.brkHandler != nil { 99 | cpu.brkHandler.OnBrk(cpu) 100 | return 101 | } 102 | 103 | // Fetch the operand (if any) and advance the PC 104 | var buf [2]byte 105 | operand := buf[:inst.Length-1] 106 | cpu.Mem.LoadBytes(cpu.Reg.PC+1, operand) 107 | cpu.LastPC = cpu.Reg.PC 108 | cpu.Reg.PC += uint16(inst.Length) 109 | 110 | // Execute the instruction 111 | cpu.pageCrossed = false 112 | cpu.deltaCycles = 0 113 | inst.fn(cpu, inst, operand) 114 | 115 | // Update the CPU cycle counter, with special-case logic 116 | // to handle a page boundary crossing 117 | cpu.Cycles += uint64(int8(inst.Cycles) + cpu.deltaCycles) 118 | if cpu.pageCrossed { 119 | cpu.Cycles += uint64(inst.BPCycles) 120 | } 121 | 122 | // Update the debugger so it handle breakpoints. 123 | if cpu.debugger != nil { 124 | cpu.debugger.onUpdatePC(cpu, cpu.Reg.PC) 125 | } 126 | } 127 | 128 | // AttachBrkHandler attaches a handler that is called whenever the BRK 129 | // instruction is executed. 130 | func (cpu *CPU) AttachBrkHandler(handler BrkHandler) { 131 | cpu.brkHandler = handler 132 | } 133 | 134 | // AttachDebugger attaches a debugger to the CPU. The debugger receives 135 | // notifications whenever the CPU executes an instruction or stores a byte 136 | // to memory. 137 | func (cpu *CPU) AttachDebugger(debugger *Debugger) { 138 | cpu.debugger = debugger 139 | cpu.storeByte = (*CPU).storeByteDebugger 140 | } 141 | 142 | // DetachDebugger detaches the currently debugger from the CPU. 143 | func (cpu *CPU) DetachDebugger() { 144 | cpu.debugger = nil 145 | cpu.storeByte = (*CPU).storeByteNormal 146 | } 147 | 148 | // Load a byte value from using the requested addressing mode 149 | // and the operand to determine where to load it from. 150 | func (cpu *CPU) load(mode Mode, operand []byte) byte { 151 | switch mode { 152 | case IMM: 153 | return operand[0] 154 | case ZPG: 155 | zpaddr := operandToAddress(operand) 156 | return cpu.Mem.LoadByte(zpaddr) 157 | case ZPX: 158 | zpaddr := operandToAddress(operand) 159 | zpaddr = offsetZeroPage(zpaddr, cpu.Reg.X) 160 | return cpu.Mem.LoadByte(zpaddr) 161 | case ZPY: 162 | zpaddr := operandToAddress(operand) 163 | zpaddr = offsetZeroPage(zpaddr, cpu.Reg.Y) 164 | return cpu.Mem.LoadByte(zpaddr) 165 | case ABS: 166 | addr := operandToAddress(operand) 167 | return cpu.Mem.LoadByte(addr) 168 | case ABX: 169 | addr := operandToAddress(operand) 170 | addr, cpu.pageCrossed = offsetAddress(addr, cpu.Reg.X) 171 | return cpu.Mem.LoadByte(addr) 172 | case ABY: 173 | addr := operandToAddress(operand) 174 | addr, cpu.pageCrossed = offsetAddress(addr, cpu.Reg.Y) 175 | return cpu.Mem.LoadByte(addr) 176 | case IDX: 177 | zpaddr := operandToAddress(operand) 178 | zpaddr = offsetZeroPage(zpaddr, cpu.Reg.X) 179 | addr := cpu.Mem.LoadAddress(zpaddr) 180 | return cpu.Mem.LoadByte(addr) 181 | case IDY: 182 | zpaddr := operandToAddress(operand) 183 | addr := cpu.Mem.LoadAddress(zpaddr) 184 | addr, cpu.pageCrossed = offsetAddress(addr, cpu.Reg.Y) 185 | return cpu.Mem.LoadByte(addr) 186 | case ACC: 187 | return cpu.Reg.A 188 | default: 189 | panic("Invalid addressing mode") 190 | } 191 | } 192 | 193 | // Load a 16-bit address value from memory using the requested addressing mode 194 | // and the 16-bit instruction operand. 195 | func (cpu *CPU) loadAddress(mode Mode, operand []byte) uint16 { 196 | switch mode { 197 | case ABS: 198 | return operandToAddress(operand) 199 | case IND: 200 | addr := operandToAddress(operand) 201 | return cpu.Mem.LoadAddress(addr) 202 | default: 203 | panic("Invalid addressing mode") 204 | } 205 | } 206 | 207 | // Store a byte value using the specified addressing mode and the 208 | // variable-sized instruction operand to determine where to store it. 209 | func (cpu *CPU) store(mode Mode, operand []byte, v byte) { 210 | switch mode { 211 | case ZPG: 212 | zpaddr := operandToAddress(operand) 213 | cpu.storeByte(cpu, zpaddr, v) 214 | case ZPX: 215 | zpaddr := operandToAddress(operand) 216 | zpaddr = offsetZeroPage(zpaddr, cpu.Reg.X) 217 | cpu.storeByte(cpu, zpaddr, v) 218 | case ZPY: 219 | zpaddr := operandToAddress(operand) 220 | zpaddr = offsetZeroPage(zpaddr, cpu.Reg.Y) 221 | cpu.storeByte(cpu, zpaddr, v) 222 | case ABS: 223 | addr := operandToAddress(operand) 224 | cpu.storeByte(cpu, addr, v) 225 | case ABX: 226 | addr := operandToAddress(operand) 227 | addr, cpu.pageCrossed = offsetAddress(addr, cpu.Reg.X) 228 | cpu.storeByte(cpu, addr, v) 229 | case ABY: 230 | addr := operandToAddress(operand) 231 | addr, cpu.pageCrossed = offsetAddress(addr, cpu.Reg.Y) 232 | cpu.storeByte(cpu, addr, v) 233 | case IDX: 234 | zpaddr := operandToAddress(operand) 235 | zpaddr = offsetZeroPage(zpaddr, cpu.Reg.X) 236 | addr := cpu.Mem.LoadAddress(zpaddr) 237 | cpu.storeByte(cpu, addr, v) 238 | case IDY: 239 | zpaddr := operandToAddress(operand) 240 | addr := cpu.Mem.LoadAddress(zpaddr) 241 | addr, cpu.pageCrossed = offsetAddress(addr, cpu.Reg.Y) 242 | cpu.storeByte(cpu, addr, v) 243 | case ACC: 244 | cpu.Reg.A = v 245 | default: 246 | panic("Invalid addressing mode") 247 | } 248 | } 249 | 250 | // Execute a branch using the instruction operand. 251 | func (cpu *CPU) branch(operand []byte) { 252 | offset := operandToAddress(operand) 253 | oldPC := cpu.Reg.PC 254 | if offset < 0x80 { 255 | cpu.Reg.PC += uint16(offset) 256 | } else { 257 | cpu.Reg.PC -= uint16(0x100 - offset) 258 | } 259 | cpu.deltaCycles++ 260 | if ((cpu.Reg.PC ^ oldPC) & 0xff00) != 0 { 261 | cpu.deltaCycles++ 262 | } 263 | } 264 | 265 | // Store the byte value 'v' add the address 'addr'. 266 | func (cpu *CPU) storeByteNormal(addr uint16, v byte) { 267 | cpu.Mem.StoreByte(addr, v) 268 | } 269 | 270 | // Store the byte value 'v' add the address 'addr'. 271 | func (cpu *CPU) storeByteDebugger(addr uint16, v byte) { 272 | cpu.debugger.onDataStore(cpu, addr, v) 273 | cpu.Mem.StoreByte(addr, v) 274 | } 275 | 276 | // Push a value 'v' onto the stack. 277 | func (cpu *CPU) push(v byte) { 278 | cpu.storeByte(cpu, stackAddress(cpu.Reg.SP), v) 279 | cpu.Reg.SP-- 280 | } 281 | 282 | // Push the address 'addr' onto the stack. 283 | func (cpu *CPU) pushAddress(addr uint16) { 284 | cpu.push(byte(addr >> 8)) 285 | cpu.push(byte(addr)) 286 | } 287 | 288 | // Pop a value from the stack and return it. 289 | func (cpu *CPU) pop() byte { 290 | cpu.Reg.SP++ 291 | return cpu.Mem.LoadByte(stackAddress(cpu.Reg.SP)) 292 | } 293 | 294 | // Pop a 16-bit address off the stack. 295 | func (cpu *CPU) popAddress() uint16 { 296 | lo := cpu.pop() 297 | hi := cpu.pop() 298 | return uint16(lo) | (uint16(hi) << 8) 299 | } 300 | 301 | // Update the Zero and Negative flags based on the value of 'v'. 302 | func (cpu *CPU) updateNZ(v byte) { 303 | cpu.Reg.Zero = (v == 0) 304 | cpu.Reg.Sign = ((v & 0x80) != 0) 305 | } 306 | 307 | // Handle a handleInterrupt by storing the program counter and status flags on 308 | // the stack. Then switch the program counter to the requested address. 309 | func (cpu *CPU) handleInterrupt(brk bool, addr uint16) { 310 | cpu.pushAddress(cpu.Reg.PC) 311 | cpu.push(cpu.Reg.SavePS(brk)) 312 | 313 | cpu.Reg.InterruptDisable = true 314 | if cpu.Arch == CMOS { 315 | cpu.Reg.Decimal = false 316 | } 317 | 318 | cpu.Reg.PC = cpu.Mem.LoadAddress(addr) 319 | } 320 | 321 | // Generate a maskable IRQ (hardware) interrupt request (unused) 322 | func (cpu *CPU) irq() { 323 | if !cpu.Reg.InterruptDisable { 324 | cpu.handleInterrupt(false, vectorIRQ) 325 | } 326 | } 327 | 328 | // Generate a non-maskable interrupt (unused) 329 | func (cpu *CPU) nmi() { 330 | cpu.handleInterrupt(false, vectorNMI) 331 | } 332 | 333 | // Generate a reset signal. 334 | func (cpu *CPU) reset() { 335 | cpu.Reg.PC = cpu.Mem.LoadAddress(vectorReset) 336 | } 337 | 338 | // Add with carry (CMOS) 339 | func (cpu *CPU) adcc(inst *Instruction, operand []byte) { 340 | acc := uint32(cpu.Reg.A) 341 | add := uint32(cpu.load(inst.Mode, operand)) 342 | carry := boolToUint32(cpu.Reg.Carry) 343 | var v uint32 344 | 345 | cpu.Reg.Overflow = (((acc ^ add) & 0x80) == 0) 346 | 347 | switch cpu.Reg.Decimal { 348 | case true: 349 | cpu.deltaCycles++ 350 | 351 | lo := (acc & 0x0f) + (add & 0x0f) + carry 352 | 353 | var carrylo uint32 354 | if lo >= 0x0a { 355 | carrylo = 0x10 356 | lo -= 0xa 357 | } 358 | 359 | hi := (acc & 0xf0) + (add & 0xf0) + carrylo 360 | 361 | if hi >= 0xa0 { 362 | cpu.Reg.Carry = true 363 | if hi >= 0x180 { 364 | cpu.Reg.Overflow = false 365 | } 366 | hi -= 0xa0 367 | } else { 368 | cpu.Reg.Carry = false 369 | if hi < 0x80 { 370 | cpu.Reg.Overflow = false 371 | } 372 | } 373 | 374 | v = hi | lo 375 | 376 | case false: 377 | v = acc + add + carry 378 | if v >= 0x100 { 379 | cpu.Reg.Carry = true 380 | if v >= 0x180 { 381 | cpu.Reg.Overflow = false 382 | } 383 | } else { 384 | cpu.Reg.Carry = false 385 | if v < 0x80 { 386 | cpu.Reg.Overflow = false 387 | } 388 | } 389 | } 390 | 391 | cpu.Reg.A = byte(v) 392 | cpu.updateNZ(cpu.Reg.A) 393 | } 394 | 395 | // Add with carry (NMOS) 396 | func (cpu *CPU) adcn(inst *Instruction, operand []byte) { 397 | acc := uint32(cpu.Reg.A) 398 | add := uint32(cpu.load(inst.Mode, operand)) 399 | carry := boolToUint32(cpu.Reg.Carry) 400 | var v uint32 401 | 402 | switch cpu.Reg.Decimal { 403 | case true: 404 | lo := (acc & 0x0f) + (add & 0x0f) + carry 405 | 406 | var carrylo uint32 407 | if lo >= 0x0a { 408 | carrylo = 0x10 409 | lo -= 0x0a 410 | } 411 | 412 | hi := (acc & 0xf0) + (add & 0xf0) + carrylo 413 | 414 | if hi >= 0xa0 { 415 | cpu.Reg.Carry = true 416 | hi -= 0xa0 417 | } else { 418 | cpu.Reg.Carry = false 419 | } 420 | 421 | v = hi | lo 422 | 423 | cpu.Reg.Overflow = ((acc^v)&0x80) != 0 && ((acc^add)&0x80) == 0 424 | 425 | case false: 426 | v = acc + add + carry 427 | cpu.Reg.Carry = (v >= 0x100) 428 | cpu.Reg.Overflow = (((acc & 0x80) == (add & 0x80)) && ((acc & 0x80) != (v & 0x80))) 429 | } 430 | 431 | cpu.Reg.A = byte(v) 432 | cpu.updateNZ(cpu.Reg.A) 433 | } 434 | 435 | // Boolean AND 436 | func (cpu *CPU) and(inst *Instruction, operand []byte) { 437 | cpu.Reg.A &= cpu.load(inst.Mode, operand) 438 | cpu.updateNZ(cpu.Reg.A) 439 | } 440 | 441 | // Arithmetic Shift Left 442 | func (cpu *CPU) asl(inst *Instruction, operand []byte) { 443 | v := cpu.load(inst.Mode, operand) 444 | cpu.Reg.Carry = ((v & 0x80) == 0x80) 445 | v = v << 1 446 | cpu.updateNZ(v) 447 | cpu.store(inst.Mode, operand, v) 448 | if cpu.Arch == CMOS && inst.Mode == ABX && !cpu.pageCrossed { 449 | cpu.deltaCycles-- 450 | } 451 | } 452 | 453 | // Branch if Carry Clear 454 | func (cpu *CPU) bcc(inst *Instruction, operand []byte) { 455 | if !cpu.Reg.Carry { 456 | cpu.branch(operand) 457 | } 458 | } 459 | 460 | // Branch if Carry Set 461 | func (cpu *CPU) bcs(inst *Instruction, operand []byte) { 462 | if cpu.Reg.Carry { 463 | cpu.branch(operand) 464 | } 465 | } 466 | 467 | // Branch if EQual (to zero) 468 | func (cpu *CPU) beq(inst *Instruction, operand []byte) { 469 | if cpu.Reg.Zero { 470 | cpu.branch(operand) 471 | } 472 | } 473 | 474 | // Bit Test 475 | func (cpu *CPU) bit(inst *Instruction, operand []byte) { 476 | v := cpu.load(inst.Mode, operand) 477 | cpu.Reg.Zero = ((v & cpu.Reg.A) == 0) 478 | cpu.Reg.Sign = ((v & 0x80) != 0) 479 | cpu.Reg.Overflow = ((v & 0x40) != 0) 480 | } 481 | 482 | // Branch if MInus (negative) 483 | func (cpu *CPU) bmi(inst *Instruction, operand []byte) { 484 | if cpu.Reg.Sign { 485 | cpu.branch(operand) 486 | } 487 | } 488 | 489 | // Branch if Not Equal (not zero) 490 | func (cpu *CPU) bne(inst *Instruction, operand []byte) { 491 | if !cpu.Reg.Zero { 492 | cpu.branch(operand) 493 | } 494 | } 495 | 496 | // Branch if PLus (positive) 497 | func (cpu *CPU) bpl(inst *Instruction, operand []byte) { 498 | if !cpu.Reg.Sign { 499 | cpu.branch(operand) 500 | } 501 | } 502 | 503 | // Branch always (65c02 only) 504 | func (cpu *CPU) bra(inst *Instruction, operand []byte) { 505 | cpu.branch(operand) 506 | } 507 | 508 | // Break 509 | func (cpu *CPU) brk(inst *Instruction, operand []byte) { 510 | cpu.Reg.PC++ 511 | cpu.handleInterrupt(true, vectorBRK) 512 | } 513 | 514 | // Branch if oVerflow Clear 515 | func (cpu *CPU) bvc(inst *Instruction, operand []byte) { 516 | if !cpu.Reg.Overflow { 517 | cpu.branch(operand) 518 | } 519 | } 520 | 521 | // Branch if oVerflow Set 522 | func (cpu *CPU) bvs(inst *Instruction, operand []byte) { 523 | if cpu.Reg.Overflow { 524 | cpu.branch(operand) 525 | } 526 | } 527 | 528 | // Clear Carry flag 529 | func (cpu *CPU) clc(inst *Instruction, operand []byte) { 530 | cpu.Reg.Carry = false 531 | } 532 | 533 | // Clear Decimal flag 534 | func (cpu *CPU) cld(inst *Instruction, operand []byte) { 535 | cpu.Reg.Decimal = false 536 | } 537 | 538 | // Clear InterruptDisable flag 539 | func (cpu *CPU) cli(inst *Instruction, operand []byte) { 540 | cpu.Reg.InterruptDisable = false 541 | } 542 | 543 | // Clear oVerflow flag 544 | func (cpu *CPU) clv(inst *Instruction, operand []byte) { 545 | cpu.Reg.Overflow = false 546 | } 547 | 548 | // Compare to accumulator 549 | func (cpu *CPU) cmp(inst *Instruction, operand []byte) { 550 | v := cpu.load(inst.Mode, operand) 551 | cpu.Reg.Carry = (cpu.Reg.A >= v) 552 | cpu.updateNZ(cpu.Reg.A - v) 553 | } 554 | 555 | // Compare to X register 556 | func (cpu *CPU) cpx(inst *Instruction, operand []byte) { 557 | v := cpu.load(inst.Mode, operand) 558 | cpu.Reg.Carry = (cpu.Reg.X >= v) 559 | cpu.updateNZ(cpu.Reg.X - v) 560 | } 561 | 562 | // Compare to Y register 563 | func (cpu *CPU) cpy(inst *Instruction, operand []byte) { 564 | v := cpu.load(inst.Mode, operand) 565 | cpu.Reg.Carry = (cpu.Reg.Y >= v) 566 | cpu.updateNZ(cpu.Reg.Y - v) 567 | } 568 | 569 | // Decrement memory value 570 | func (cpu *CPU) dec(inst *Instruction, operand []byte) { 571 | v := cpu.load(inst.Mode, operand) - 1 572 | cpu.updateNZ(v) 573 | cpu.store(inst.Mode, operand, v) 574 | } 575 | 576 | // Decrement X register 577 | func (cpu *CPU) dex(inst *Instruction, operand []byte) { 578 | cpu.Reg.X-- 579 | cpu.updateNZ(cpu.Reg.X) 580 | } 581 | 582 | // Decrement Y register 583 | func (cpu *CPU) dey(inst *Instruction, operand []byte) { 584 | cpu.Reg.Y-- 585 | cpu.updateNZ(cpu.Reg.Y) 586 | } 587 | 588 | // Boolean XOR 589 | func (cpu *CPU) eor(inst *Instruction, operand []byte) { 590 | cpu.Reg.A ^= cpu.load(inst.Mode, operand) 591 | cpu.updateNZ(cpu.Reg.A) 592 | } 593 | 594 | // Increment memory value 595 | func (cpu *CPU) inc(inst *Instruction, operand []byte) { 596 | v := cpu.load(inst.Mode, operand) + 1 597 | cpu.updateNZ(v) 598 | cpu.store(inst.Mode, operand, v) 599 | } 600 | 601 | // Increment X register 602 | func (cpu *CPU) inx(inst *Instruction, operand []byte) { 603 | cpu.Reg.X++ 604 | cpu.updateNZ(cpu.Reg.X) 605 | } 606 | 607 | // Increment Y register 608 | func (cpu *CPU) iny(inst *Instruction, operand []byte) { 609 | cpu.Reg.Y++ 610 | cpu.updateNZ(cpu.Reg.Y) 611 | } 612 | 613 | // Jump to memory address (NMOS 6502) 614 | func (cpu *CPU) jmpn(inst *Instruction, operand []byte) { 615 | cpu.Reg.PC = cpu.loadAddress(inst.Mode, operand) 616 | } 617 | 618 | // Jump to memory address (CMOS 65c02) 619 | func (cpu *CPU) jmpc(inst *Instruction, operand []byte) { 620 | if inst.Mode == IND && operand[0] == 0xff { 621 | // Fix bug in NMOS 6502 address loading. In NMOS 6502, a JMP ($12FF) 622 | // would load LSB of jmp target from $12FF and MSB from $1200. 623 | // In CMOS, it loads the MSB from $1300. 624 | addr0 := uint16(operand[1])<<8 | 0xff 625 | addr1 := addr0 + 1 626 | lo := cpu.Mem.LoadByte(addr0) 627 | hi := cpu.Mem.LoadByte(addr1) 628 | cpu.Reg.PC = uint16(lo) | uint16(hi)<<8 629 | cpu.deltaCycles++ 630 | return 631 | } 632 | 633 | cpu.Reg.PC = cpu.loadAddress(inst.Mode, operand) 634 | } 635 | 636 | // Jump to subroutine 637 | func (cpu *CPU) jsr(inst *Instruction, operand []byte) { 638 | addr := cpu.loadAddress(inst.Mode, operand) 639 | cpu.pushAddress(cpu.Reg.PC - 1) 640 | cpu.Reg.PC = addr 641 | } 642 | 643 | // load Accumulator 644 | func (cpu *CPU) lda(inst *Instruction, operand []byte) { 645 | cpu.Reg.A = cpu.load(inst.Mode, operand) 646 | cpu.updateNZ(cpu.Reg.A) 647 | } 648 | 649 | // load the X register 650 | func (cpu *CPU) ldx(inst *Instruction, operand []byte) { 651 | cpu.Reg.X = cpu.load(inst.Mode, operand) 652 | cpu.updateNZ(cpu.Reg.X) 653 | } 654 | 655 | // load the Y register 656 | func (cpu *CPU) ldy(inst *Instruction, operand []byte) { 657 | cpu.Reg.Y = cpu.load(inst.Mode, operand) 658 | cpu.updateNZ(cpu.Reg.Y) 659 | } 660 | 661 | // Logical Shift Right 662 | func (cpu *CPU) lsr(inst *Instruction, operand []byte) { 663 | v := cpu.load(inst.Mode, operand) 664 | cpu.Reg.Carry = ((v & 1) == 1) 665 | v = v >> 1 666 | cpu.updateNZ(v) 667 | cpu.store(inst.Mode, operand, v) 668 | if cpu.Arch == CMOS && inst.Mode == ABX && !cpu.pageCrossed { 669 | cpu.deltaCycles-- 670 | } 671 | } 672 | 673 | // No-operation 674 | func (cpu *CPU) nop(inst *Instruction, operand []byte) { 675 | // Do nothing 676 | } 677 | 678 | // Boolean OR 679 | func (cpu *CPU) ora(inst *Instruction, operand []byte) { 680 | cpu.Reg.A |= cpu.load(inst.Mode, operand) 681 | cpu.updateNZ(cpu.Reg.A) 682 | } 683 | 684 | // Push Accumulator 685 | func (cpu *CPU) pha(inst *Instruction, operand []byte) { 686 | cpu.push(cpu.Reg.A) 687 | } 688 | 689 | // Push Processor flags 690 | func (cpu *CPU) php(inst *Instruction, operand []byte) { 691 | cpu.push(cpu.Reg.SavePS(true)) 692 | } 693 | 694 | // Push X register (65c02 only) 695 | func (cpu *CPU) phx(inst *Instruction, operand []byte) { 696 | cpu.push(cpu.Reg.X) 697 | } 698 | 699 | // Push Y register (65c02 only) 700 | func (cpu *CPU) phy(inst *Instruction, operand []byte) { 701 | cpu.push(cpu.Reg.Y) 702 | } 703 | 704 | // Pull (pop) Accumulator 705 | func (cpu *CPU) pla(inst *Instruction, operand []byte) { 706 | cpu.Reg.A = cpu.pop() 707 | cpu.updateNZ(cpu.Reg.A) 708 | } 709 | 710 | // Pull (pop) Processor flags 711 | func (cpu *CPU) plp(inst *Instruction, operand []byte) { 712 | v := cpu.pop() 713 | cpu.Reg.RestorePS(v) 714 | } 715 | 716 | // Pull (pop) X register (65c02 only) 717 | func (cpu *CPU) plx(inst *Instruction, operand []byte) { 718 | cpu.Reg.X = cpu.pop() 719 | cpu.updateNZ(cpu.Reg.X) 720 | } 721 | 722 | // Pull (pop) Y register (65c02 only) 723 | func (cpu *CPU) ply(inst *Instruction, operand []byte) { 724 | cpu.Reg.Y = cpu.pop() 725 | cpu.updateNZ(cpu.Reg.Y) 726 | } 727 | 728 | // Rotate Left 729 | func (cpu *CPU) rol(inst *Instruction, operand []byte) { 730 | tmp := cpu.load(inst.Mode, operand) 731 | v := (tmp << 1) | boolToByte(cpu.Reg.Carry) 732 | cpu.Reg.Carry = ((tmp & 0x80) != 0) 733 | cpu.updateNZ(v) 734 | cpu.store(inst.Mode, operand, v) 735 | if cpu.Arch == CMOS && inst.Mode == ABX && !cpu.pageCrossed { 736 | cpu.deltaCycles-- 737 | } 738 | } 739 | 740 | // Rotate Right 741 | func (cpu *CPU) ror(inst *Instruction, operand []byte) { 742 | tmp := cpu.load(inst.Mode, operand) 743 | v := (tmp >> 1) | (boolToByte(cpu.Reg.Carry) << 7) 744 | cpu.Reg.Carry = ((tmp & 1) != 0) 745 | cpu.updateNZ(v) 746 | cpu.store(inst.Mode, operand, v) 747 | if cpu.Arch == CMOS && inst.Mode == ABX && !cpu.pageCrossed { 748 | cpu.deltaCycles-- 749 | } 750 | } 751 | 752 | // Return from Interrupt 753 | func (cpu *CPU) rti(inst *Instruction, operand []byte) { 754 | v := cpu.pop() 755 | cpu.Reg.RestorePS(v) 756 | cpu.Reg.PC = cpu.popAddress() 757 | } 758 | 759 | // Return from Subroutine 760 | func (cpu *CPU) rts(inst *Instruction, operand []byte) { 761 | addr := cpu.popAddress() 762 | cpu.Reg.PC = addr + 1 763 | } 764 | 765 | // Subtract with Carry (CMOS) 766 | func (cpu *CPU) sbcc(inst *Instruction, operand []byte) { 767 | acc := uint32(cpu.Reg.A) 768 | sub := uint32(cpu.load(inst.Mode, operand)) 769 | carry := boolToUint32(cpu.Reg.Carry) 770 | cpu.Reg.Overflow = ((acc ^ sub) & 0x80) != 0 771 | var v uint32 772 | 773 | switch cpu.Reg.Decimal { 774 | case true: 775 | cpu.deltaCycles++ 776 | 777 | lo := 0x0f + (acc & 0x0f) - (sub & 0x0f) + carry 778 | 779 | var carrylo uint32 780 | if lo < 0x10 { 781 | lo -= 0x06 782 | carrylo = 0 783 | } else { 784 | lo -= 0x10 785 | carrylo = 0x10 786 | } 787 | 788 | hi := 0xf0 + (acc & 0xf0) - (sub & 0xf0) + carrylo 789 | 790 | if hi < 0x100 { 791 | cpu.Reg.Carry = false 792 | if hi < 0x80 { 793 | cpu.Reg.Overflow = false 794 | } 795 | hi -= 0x60 796 | } else { 797 | cpu.Reg.Carry = true 798 | if hi >= 0x180 { 799 | cpu.Reg.Overflow = false 800 | } 801 | hi -= 0x100 802 | } 803 | 804 | v = hi | lo 805 | 806 | case false: 807 | v = 0xff + acc - sub + carry 808 | if v < 0x100 { 809 | cpu.Reg.Carry = false 810 | if v < 0x80 { 811 | cpu.Reg.Overflow = false 812 | } 813 | } else { 814 | cpu.Reg.Carry = true 815 | if v >= 0x180 { 816 | cpu.Reg.Overflow = false 817 | } 818 | } 819 | } 820 | 821 | cpu.Reg.A = byte(v) 822 | cpu.updateNZ(cpu.Reg.A) 823 | } 824 | 825 | // Subtract with Carry (NMOS) 826 | func (cpu *CPU) sbcn(inst *Instruction, operand []byte) { 827 | acc := uint32(cpu.Reg.A) 828 | sub := uint32(cpu.load(inst.Mode, operand)) 829 | carry := boolToUint32(cpu.Reg.Carry) 830 | var v uint32 831 | 832 | switch cpu.Reg.Decimal { 833 | case true: 834 | lo := 0x0f + (acc & 0x0f) - (sub & 0x0f) + carry 835 | 836 | var carrylo uint32 837 | if lo < 0x10 { 838 | lo -= 0x06 839 | carrylo = 0 840 | } else { 841 | lo -= 0x10 842 | carrylo = 0x10 843 | } 844 | 845 | hi := 0xf0 + (acc & 0xf0) - (sub & 0xf0) + carrylo 846 | 847 | if hi < 0x100 { 848 | cpu.Reg.Carry = false 849 | hi -= 0x60 850 | } else { 851 | cpu.Reg.Carry = true 852 | hi -= 0x100 853 | } 854 | 855 | v = hi | lo 856 | 857 | cpu.Reg.Overflow = ((acc^v)&0x80) != 0 && ((acc^sub)&0x80) != 0 858 | 859 | case false: 860 | v = 0xff + acc - sub + carry 861 | cpu.Reg.Carry = (v >= 0x100) 862 | cpu.Reg.Overflow = (((acc & 0x80) != (sub & 0x80)) && ((acc & 0x80) != (v & 0x80))) 863 | } 864 | 865 | cpu.Reg.A = byte(v) 866 | cpu.updateNZ(byte(v)) 867 | } 868 | 869 | // Set Carry flag 870 | func (cpu *CPU) sec(inst *Instruction, operand []byte) { 871 | cpu.Reg.Carry = true 872 | } 873 | 874 | // Set Decimal flag 875 | func (cpu *CPU) sed(inst *Instruction, operand []byte) { 876 | cpu.Reg.Decimal = true 877 | } 878 | 879 | // Set InterruptDisable flag 880 | func (cpu *CPU) sei(inst *Instruction, operand []byte) { 881 | cpu.Reg.InterruptDisable = true 882 | } 883 | 884 | // Store Accumulator 885 | func (cpu *CPU) sta(inst *Instruction, operand []byte) { 886 | cpu.store(inst.Mode, operand, cpu.Reg.A) 887 | } 888 | 889 | // Store X register 890 | func (cpu *CPU) stx(inst *Instruction, operand []byte) { 891 | cpu.store(inst.Mode, operand, cpu.Reg.X) 892 | } 893 | 894 | // Store Y register 895 | func (cpu *CPU) sty(inst *Instruction, operand []byte) { 896 | cpu.store(inst.Mode, operand, cpu.Reg.Y) 897 | } 898 | 899 | // Store Zero (65c02 only) 900 | func (cpu *CPU) stz(inst *Instruction, operand []byte) { 901 | cpu.store(inst.Mode, operand, 0) 902 | } 903 | 904 | // Transfer Accumulator to X register 905 | func (cpu *CPU) tax(inst *Instruction, operand []byte) { 906 | cpu.Reg.X = cpu.Reg.A 907 | cpu.updateNZ(cpu.Reg.X) 908 | } 909 | 910 | // Transfer Accumulator to Y register 911 | func (cpu *CPU) tay(inst *Instruction, operand []byte) { 912 | cpu.Reg.Y = cpu.Reg.A 913 | cpu.updateNZ(cpu.Reg.Y) 914 | } 915 | 916 | // Test and Reset Bits (65c02 only) 917 | func (cpu *CPU) trb(inst *Instruction, operand []byte) { 918 | v := cpu.load(inst.Mode, operand) 919 | cpu.Reg.Zero = ((v & cpu.Reg.A) == 0) 920 | nv := (v & (cpu.Reg.A ^ 0xff)) 921 | cpu.store(inst.Mode, operand, nv) 922 | } 923 | 924 | // Test and Set Bits (65c02 only) 925 | func (cpu *CPU) tsb(inst *Instruction, operand []byte) { 926 | v := cpu.load(inst.Mode, operand) 927 | cpu.Reg.Zero = ((v & cpu.Reg.A) == 0) 928 | nv := (v | cpu.Reg.A) 929 | cpu.store(inst.Mode, operand, nv) 930 | } 931 | 932 | // Transfer stack pointer to X register 933 | func (cpu *CPU) tsx(inst *Instruction, operand []byte) { 934 | cpu.Reg.X = cpu.Reg.SP 935 | cpu.updateNZ(cpu.Reg.X) 936 | } 937 | 938 | // Transfer X register to Accumulator 939 | func (cpu *CPU) txa(inst *Instruction, operand []byte) { 940 | cpu.Reg.A = cpu.Reg.X 941 | cpu.updateNZ(cpu.Reg.A) 942 | } 943 | 944 | // Transfer X register to the stack pointer 945 | func (cpu *CPU) txs(inst *Instruction, operand []byte) { 946 | cpu.Reg.SP = cpu.Reg.X 947 | } 948 | 949 | // Transfer Y register to the Accumulator 950 | func (cpu *CPU) tya(inst *Instruction, operand []byte) { 951 | cpu.Reg.A = cpu.Reg.Y 952 | cpu.updateNZ(cpu.Reg.A) 953 | } 954 | 955 | // Unused instruction (6502) 956 | func (cpu *CPU) unusedn(inst *Instruction, operand []byte) { 957 | // Do nothing 958 | } 959 | 960 | // Unused instruction (65c02) 961 | func (cpu *CPU) unusedc(inst *Instruction, operand []byte) { 962 | // Do nothing 963 | } 964 | -------------------------------------------------------------------------------- /cpu/cpu_test.go: -------------------------------------------------------------------------------- 1 | package cpu_test 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/beevik/go6502/asm" 9 | "github.com/beevik/go6502/cpu" 10 | ) 11 | 12 | func loadCPU(t *testing.T, asmString string) *cpu.CPU { 13 | b := strings.NewReader(asmString) 14 | r, sm, err := asm.Assemble(b, "test.asm", 0x1000, os.Stdout, 0) 15 | if err != nil { 16 | t.Error(err) 17 | return nil 18 | } 19 | 20 | mem := cpu.NewFlatMemory() 21 | cpu := cpu.NewCPU(cpu.NMOS, mem) 22 | mem.StoreBytes(sm.Origin, r.Code) 23 | cpu.SetPC(sm.Origin) 24 | return cpu 25 | } 26 | 27 | func stepCPU(cpu *cpu.CPU, steps int) { 28 | for i := 0; i < steps; i++ { 29 | cpu.Step() 30 | } 31 | } 32 | 33 | func runCPU(t *testing.T, asmString string, steps int) *cpu.CPU { 34 | cpu := loadCPU(t, asmString) 35 | if cpu != nil { 36 | stepCPU(cpu, steps) 37 | } 38 | return cpu 39 | } 40 | 41 | func expectPC(t *testing.T, cpu *cpu.CPU, pc uint16) { 42 | if cpu.Reg.PC != pc { 43 | t.Errorf("PC incorrect. exp: $%04X, got: $%04X", pc, cpu.Reg.PC) 44 | } 45 | } 46 | 47 | func expectCycles(t *testing.T, cpu *cpu.CPU, cycles uint64) { 48 | if cpu.Cycles != cycles { 49 | t.Errorf("Cycles incorrect. exp: %d, got: %d", cycles, cpu.Cycles) 50 | } 51 | } 52 | 53 | func expectACC(t *testing.T, cpu *cpu.CPU, acc byte) { 54 | if cpu.Reg.A != acc { 55 | t.Errorf("Accumulator incorrect. exp: $%02X, got: $%02X", acc, cpu.Reg.A) 56 | } 57 | } 58 | 59 | func expectSP(t *testing.T, cpu *cpu.CPU, sp byte) { 60 | if cpu.Reg.SP != sp { 61 | t.Errorf("stack pointer incorrect. exp: %02X, got $%02X", sp, cpu.Reg.SP) 62 | } 63 | } 64 | 65 | func expectMem(t *testing.T, cpu *cpu.CPU, addr uint16, v byte) { 66 | got := cpu.Mem.LoadByte(addr) 67 | if got != v { 68 | t.Errorf("Memory at $%04X incorrect. exp: $%02X, got: $%02X", addr, v, got) 69 | } 70 | } 71 | 72 | func TestAccumulator(t *testing.T) { 73 | asm := ` 74 | .ORG $1000 75 | LDA #$5E 76 | STA $15 77 | STA $1500` 78 | 79 | cpu := runCPU(t, asm, 3) 80 | if cpu == nil { 81 | return 82 | } 83 | 84 | expectPC(t, cpu, 0x1007) 85 | expectCycles(t, cpu, 9) 86 | expectACC(t, cpu, 0x5e) 87 | expectMem(t, cpu, 0x15, 0x5e) 88 | expectMem(t, cpu, 0x1500, 0x5e) 89 | } 90 | 91 | func TestStack(t *testing.T) { 92 | asm := ` 93 | .ORG $1000 94 | LDA #$11 95 | PHA 96 | LDA #$12 97 | PHA 98 | LDA #$13 99 | PHA 100 | 101 | PLA 102 | STA $2000 103 | PLA 104 | STA $2001 105 | PLA 106 | STA $2002` 107 | 108 | cpu := loadCPU(t, asm) 109 | stepCPU(cpu, 6) 110 | 111 | expectSP(t, cpu, 0xfc) 112 | expectACC(t, cpu, 0x13) 113 | expectMem(t, cpu, 0x1ff, 0x11) 114 | expectMem(t, cpu, 0x1fe, 0x12) 115 | expectMem(t, cpu, 0x1fd, 0x13) 116 | 117 | stepCPU(cpu, 6) 118 | expectACC(t, cpu, 0x11) 119 | expectSP(t, cpu, 0xff) 120 | expectMem(t, cpu, 0x2000, 0x13) 121 | expectMem(t, cpu, 0x2001, 0x12) 122 | expectMem(t, cpu, 0x2002, 0x11) 123 | } 124 | 125 | func TestIndirect(t *testing.T) { 126 | asm := ` 127 | .ORG $1000 128 | LDX #$80 129 | LDY #$40 130 | LDA #$EE 131 | STA $2000,X 132 | STA $2000,Y 133 | 134 | LDA #$11 135 | STA $06 136 | LDA #$05 137 | STA $07 138 | LDX #$01 139 | LDY #$01 140 | LDA #$BB 141 | STA ($05,X) 142 | STA ($06),Y 143 | ` 144 | 145 | cpu := runCPU(t, asm, 14) 146 | expectMem(t, cpu, 0x2080, 0xee) 147 | expectMem(t, cpu, 0x2040, 0xee) 148 | expectMem(t, cpu, 0x0511, 0xbb) 149 | expectMem(t, cpu, 0x0512, 0xbb) 150 | } 151 | 152 | func TestPageCross(t *testing.T) { 153 | asm := ` 154 | .ORG $1000 155 | LDA #$55 ; 2 cycles 156 | STA $1101 ; 4 cycles 157 | LDA #$00 ; 2 cycles 158 | LDX #$FF ; 2 cycles 159 | LDA $1002,X ; 5 cycles` 160 | 161 | cpu := runCPU(t, asm, 5) 162 | if cpu == nil { 163 | return 164 | } 165 | 166 | expectPC(t, cpu, 0x100c) 167 | expectCycles(t, cpu, 15) 168 | expectACC(t, cpu, 0x55) 169 | expectMem(t, cpu, 0x1101, 0x55) 170 | } 171 | 172 | func TestUnused65c02(t *testing.T) { 173 | asm := ` 174 | .ORG $1000 175 | .ARCH 65c02 176 | .DH 0200 177 | .DH 03 178 | .DH 07 179 | .DH 0b 180 | .DH 0f 181 | .DH fc0102` 182 | 183 | cpu := runCPU(t, asm, 6) 184 | if cpu == nil { 185 | return 186 | } 187 | 188 | expectPC(t, cpu, 0x1009) 189 | expectCycles(t, cpu, 10) 190 | } 191 | -------------------------------------------------------------------------------- /cpu/debugger.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2018 Brett Vickers. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package cpu 6 | 7 | import "sort" 8 | 9 | // The Debugger interface may be implemented to intercept instructions before 10 | // and after they are executed on the emulated CPU. 11 | type Debugger struct { 12 | breakpointHandler BreakpointHandler 13 | breakpoints map[uint16]*Breakpoint 14 | dataBreakpoints map[uint16]*DataBreakpoint 15 | } 16 | 17 | // The BreakpointHandler interface should be implemented by any object that 18 | // wishes to receive debugger breakpoint notifications. 19 | type BreakpointHandler interface { 20 | OnBreakpoint(cpu *CPU, b *Breakpoint) 21 | OnDataBreakpoint(cpu *CPU, b *DataBreakpoint) 22 | } 23 | 24 | // A Breakpoint represents an address that will cause the debugger to stop 25 | // code execution when the program counter reaches it. 26 | type Breakpoint struct { 27 | Address uint16 // address of execution breakpoint 28 | Disabled bool // this breakpoint is currently disabled 29 | } 30 | 31 | // A DataBreakpoint represents an address that will cause the debugger to 32 | // stop executing code when a byte is stored to it. 33 | type DataBreakpoint struct { 34 | Address uint16 // breakpoint triggered by stores to this address 35 | Disabled bool // this breakpoint is currently disabled 36 | Conditional bool // this breakpoint is conditional on a certain Value being stored 37 | Value byte // the value that must be stored if the breakpoint is conditional 38 | } 39 | 40 | // NewDebugger creates a new CPU debugger. 41 | func NewDebugger(breakpointHandler BreakpointHandler) *Debugger { 42 | return &Debugger{ 43 | breakpointHandler: breakpointHandler, 44 | breakpoints: make(map[uint16]*Breakpoint), 45 | dataBreakpoints: make(map[uint16]*DataBreakpoint), 46 | } 47 | } 48 | 49 | type byBPAddr []*Breakpoint 50 | 51 | func (a byBPAddr) Len() int { return len(a) } 52 | func (a byBPAddr) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 53 | func (a byBPAddr) Less(i, j int) bool { return a[i].Address < a[j].Address } 54 | 55 | // GetBreakpoint looks up a breakpoint by address and returns it if found. 56 | // Otherwise it returns nil. 57 | func (d *Debugger) GetBreakpoint(addr uint16) *Breakpoint { 58 | if b, ok := d.breakpoints[addr]; ok { 59 | return b 60 | } 61 | return nil 62 | } 63 | 64 | // GetBreakpoints returns all breakpoints currently set in the debugger. 65 | func (d *Debugger) GetBreakpoints() []*Breakpoint { 66 | var breakpoints []*Breakpoint 67 | for _, b := range d.breakpoints { 68 | breakpoints = append(breakpoints, b) 69 | } 70 | sort.Sort(byBPAddr(breakpoints)) 71 | return breakpoints 72 | } 73 | 74 | // AddBreakpoint adds a new breakpoint address to the debugger. If the 75 | // breakpoint was already set, the request is ignored. 76 | func (d *Debugger) AddBreakpoint(addr uint16) *Breakpoint { 77 | b := &Breakpoint{Address: addr} 78 | d.breakpoints[addr] = b 79 | return b 80 | } 81 | 82 | // RemoveBreakpoint removes a breakpoint from the debugger. 83 | func (d *Debugger) RemoveBreakpoint(addr uint16) { 84 | delete(d.breakpoints, addr) 85 | } 86 | 87 | type byDBPAddr []*DataBreakpoint 88 | 89 | func (a byDBPAddr) Len() int { return len(a) } 90 | func (a byDBPAddr) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 91 | func (a byDBPAddr) Less(i, j int) bool { return a[i].Address < a[j].Address } 92 | 93 | // GetDataBreakpoint looks up a data breakpoint on the provided address 94 | // and returns it if found. Otherwise it returns nil. 95 | func (d *Debugger) GetDataBreakpoint(addr uint16) *DataBreakpoint { 96 | if b, ok := d.dataBreakpoints[addr]; ok { 97 | return b 98 | } 99 | return nil 100 | } 101 | 102 | // GetDataBreakpoints returns all data breakpoints currently set in the 103 | // debugger. 104 | func (d *Debugger) GetDataBreakpoints() []*DataBreakpoint { 105 | var breakpoints []*DataBreakpoint 106 | for _, b := range d.dataBreakpoints { 107 | breakpoints = append(breakpoints, b) 108 | } 109 | sort.Sort(byDBPAddr(breakpoints)) 110 | return breakpoints 111 | } 112 | 113 | // AddDataBreakpoint adds an unconditional data breakpoint on the requested 114 | // address. 115 | func (d *Debugger) AddDataBreakpoint(addr uint16) *DataBreakpoint { 116 | b := &DataBreakpoint{Address: addr} 117 | d.dataBreakpoints[addr] = b 118 | return b 119 | } 120 | 121 | // AddConditionalDataBreakpoint adds a conditional data breakpoint on the 122 | // requested address. 123 | func (d *Debugger) AddConditionalDataBreakpoint(addr uint16, value byte) { 124 | d.dataBreakpoints[addr] = &DataBreakpoint{ 125 | Address: addr, 126 | Conditional: true, 127 | Value: value, 128 | } 129 | } 130 | 131 | // RemoveDataBreakpoint removes a (conditional or unconditional) data 132 | // breakpoint at the requested address. 133 | func (d *Debugger) RemoveDataBreakpoint(addr uint16) { 134 | delete(d.dataBreakpoints, addr) 135 | } 136 | 137 | func (d *Debugger) onUpdatePC(cpu *CPU, addr uint16) { 138 | if d.breakpointHandler != nil { 139 | if b, ok := d.breakpoints[addr]; ok && !b.Disabled { 140 | d.breakpointHandler.OnBreakpoint(cpu, b) 141 | } 142 | } 143 | } 144 | 145 | func (d *Debugger) onDataStore(cpu *CPU, addr uint16, v byte) { 146 | if d.breakpointHandler != nil { 147 | if b, ok := d.dataBreakpoints[addr]; ok && !b.Disabled { 148 | if !b.Conditional || b.Value == v { 149 | d.breakpointHandler.OnDataBreakpoint(cpu, b) 150 | } 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /cpu/instructions.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2018 Brett Vickers. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package cpu 6 | 7 | import "strings" 8 | 9 | // An opsym is an internal symbol used to associate an opcode's data 10 | // with its instructions. 11 | type opsym byte 12 | 13 | const ( 14 | symADC opsym = iota 15 | symAND 16 | symASL 17 | symBCC 18 | symBCS 19 | symBEQ 20 | symBIT 21 | symBMI 22 | symBNE 23 | symBPL 24 | symBRA 25 | symBRK 26 | symBVC 27 | symBVS 28 | symCLC 29 | symCLD 30 | symCLI 31 | symCLV 32 | symCMP 33 | symCPX 34 | symCPY 35 | symDEC 36 | symDEX 37 | symDEY 38 | symEOR 39 | symINC 40 | symINX 41 | symINY 42 | symJMP 43 | symJSR 44 | symLDA 45 | symLDX 46 | symLDY 47 | symLSR 48 | symNOP 49 | symORA 50 | symPHA 51 | symPHP 52 | symPHX 53 | symPHY 54 | symPLA 55 | symPLP 56 | symPLX 57 | symPLY 58 | symROL 59 | symROR 60 | symRTI 61 | symRTS 62 | symSBC 63 | symSEC 64 | symSED 65 | symSEI 66 | symSTA 67 | symSTZ 68 | symSTX 69 | symSTY 70 | symTAX 71 | symTAY 72 | symTRB 73 | symTSB 74 | symTSX 75 | symTXA 76 | symTXS 77 | symTYA 78 | ) 79 | 80 | type instfunc func(c *CPU, inst *Instruction, operand []byte) 81 | 82 | // Emulator implementation for each opcode 83 | type opcodeImpl struct { 84 | sym opsym 85 | name string 86 | fn [2]instfunc // NMOS=0, CMOS=1 87 | } 88 | 89 | var impl = []opcodeImpl{ 90 | {symADC, "ADC", [2]instfunc{(*CPU).adcn, (*CPU).adcc}}, 91 | {symAND, "AND", [2]instfunc{(*CPU).and, (*CPU).and}}, 92 | {symASL, "ASL", [2]instfunc{(*CPU).asl, (*CPU).asl}}, 93 | {symBCC, "BCC", [2]instfunc{(*CPU).bcc, (*CPU).bcc}}, 94 | {symBCS, "BCS", [2]instfunc{(*CPU).bcs, (*CPU).bcs}}, 95 | {symBEQ, "BEQ", [2]instfunc{(*CPU).beq, (*CPU).beq}}, 96 | {symBIT, "BIT", [2]instfunc{(*CPU).bit, (*CPU).bit}}, 97 | {symBMI, "BMI", [2]instfunc{(*CPU).bmi, (*CPU).bmi}}, 98 | {symBNE, "BNE", [2]instfunc{(*CPU).bne, (*CPU).bne}}, 99 | {symBPL, "BPL", [2]instfunc{(*CPU).bpl, (*CPU).bpl}}, 100 | {symBRA, "BRA", [2]instfunc{nil, (*CPU).bra}}, 101 | {symBRK, "BRK", [2]instfunc{(*CPU).brk, (*CPU).brk}}, 102 | {symBVC, "BVC", [2]instfunc{(*CPU).bvc, (*CPU).bvc}}, 103 | {symBVS, "BVS", [2]instfunc{(*CPU).bvs, (*CPU).bvs}}, 104 | {symCLC, "CLC", [2]instfunc{(*CPU).clc, (*CPU).clc}}, 105 | {symCLD, "CLD", [2]instfunc{(*CPU).cld, (*CPU).cld}}, 106 | {symCLI, "CLI", [2]instfunc{(*CPU).cli, (*CPU).cli}}, 107 | {symCLV, "CLV", [2]instfunc{(*CPU).clv, (*CPU).clv}}, 108 | {symCMP, "CMP", [2]instfunc{(*CPU).cmp, (*CPU).cmp}}, 109 | {symCPX, "CPX", [2]instfunc{(*CPU).cpx, (*CPU).cpx}}, 110 | {symCPY, "CPY", [2]instfunc{(*CPU).cpy, (*CPU).cpy}}, 111 | {symDEC, "DEC", [2]instfunc{(*CPU).dec, (*CPU).dec}}, 112 | {symDEX, "DEX", [2]instfunc{(*CPU).dex, (*CPU).dex}}, 113 | {symDEY, "DEY", [2]instfunc{(*CPU).dey, (*CPU).dey}}, 114 | {symEOR, "EOR", [2]instfunc{(*CPU).eor, (*CPU).eor}}, 115 | {symINC, "INC", [2]instfunc{(*CPU).inc, (*CPU).inc}}, 116 | {symINX, "INX", [2]instfunc{(*CPU).inx, (*CPU).inx}}, 117 | {symINY, "INY", [2]instfunc{(*CPU).iny, (*CPU).iny}}, 118 | {symJMP, "JMP", [2]instfunc{(*CPU).jmpn, (*CPU).jmpc}}, 119 | {symJSR, "JSR", [2]instfunc{(*CPU).jsr, (*CPU).jsr}}, 120 | {symLDA, "LDA", [2]instfunc{(*CPU).lda, (*CPU).lda}}, 121 | {symLDX, "LDX", [2]instfunc{(*CPU).ldx, (*CPU).ldx}}, 122 | {symLDY, "LDY", [2]instfunc{(*CPU).ldy, (*CPU).ldy}}, 123 | {symLSR, "LSR", [2]instfunc{(*CPU).lsr, (*CPU).lsr}}, 124 | {symNOP, "NOP", [2]instfunc{(*CPU).nop, (*CPU).nop}}, 125 | {symORA, "ORA", [2]instfunc{(*CPU).ora, (*CPU).ora}}, 126 | {symPHA, "PHA", [2]instfunc{(*CPU).pha, (*CPU).pha}}, 127 | {symPHP, "PHP", [2]instfunc{(*CPU).php, (*CPU).php}}, 128 | {symPHX, "PHX", [2]instfunc{nil, (*CPU).phx}}, 129 | {symPHY, "PHY", [2]instfunc{nil, (*CPU).phy}}, 130 | {symPLA, "PLA", [2]instfunc{(*CPU).pla, (*CPU).pla}}, 131 | {symPLP, "PLP", [2]instfunc{(*CPU).plp, (*CPU).plp}}, 132 | {symPLX, "PLX", [2]instfunc{nil, (*CPU).plx}}, 133 | {symPLY, "PLY", [2]instfunc{nil, (*CPU).ply}}, 134 | {symROL, "ROL", [2]instfunc{(*CPU).rol, (*CPU).rol}}, 135 | {symROR, "ROR", [2]instfunc{(*CPU).ror, (*CPU).ror}}, 136 | {symRTI, "RTI", [2]instfunc{(*CPU).rti, (*CPU).rti}}, 137 | {symRTS, "RTS", [2]instfunc{(*CPU).rts, (*CPU).rts}}, 138 | {symSBC, "SBC", [2]instfunc{(*CPU).sbcn, (*CPU).sbcc}}, 139 | {symSEC, "SEC", [2]instfunc{(*CPU).sec, (*CPU).sec}}, 140 | {symSED, "SED", [2]instfunc{(*CPU).sed, (*CPU).sed}}, 141 | {symSEI, "SEI", [2]instfunc{(*CPU).sei, (*CPU).sei}}, 142 | {symSTA, "STA", [2]instfunc{(*CPU).sta, (*CPU).sta}}, 143 | {symSTX, "STX", [2]instfunc{(*CPU).stx, (*CPU).stx}}, 144 | {symSTY, "STY", [2]instfunc{(*CPU).sty, (*CPU).sty}}, 145 | {symSTZ, "STZ", [2]instfunc{nil, (*CPU).stz}}, 146 | {symTAX, "TAX", [2]instfunc{(*CPU).tax, (*CPU).tax}}, 147 | {symTAY, "TAY", [2]instfunc{(*CPU).tay, (*CPU).tay}}, 148 | {symTRB, "TRB", [2]instfunc{nil, (*CPU).trb}}, 149 | {symTSB, "TSB", [2]instfunc{nil, (*CPU).tsb}}, 150 | {symTSX, "TSX", [2]instfunc{(*CPU).tsx, (*CPU).tsx}}, 151 | {symTXA, "TXA", [2]instfunc{(*CPU).txa, (*CPU).txa}}, 152 | {symTXS, "TXS", [2]instfunc{(*CPU).txs, (*CPU).txs}}, 153 | {symTYA, "TYA", [2]instfunc{(*CPU).tya, (*CPU).tya}}, 154 | } 155 | 156 | // Mode describes a memory addressing mode. 157 | type Mode byte 158 | 159 | // All possible memory addressing modes 160 | const ( 161 | IMM Mode = iota // Immediate 162 | IMP // Implied (no operand) 163 | REL // Relative 164 | ZPG // Zero Page 165 | ZPX // Zero Page,X 166 | ZPY // Zero Page,Y 167 | ABS // Absolute 168 | ABX // Absolute,X 169 | ABY // Absolute,Y 170 | IND // (Indirect) 171 | IDX // (Indirect,X) 172 | IDY // (Indirect),Y 173 | ACC // Accumulator (no operand) 174 | ) 175 | 176 | // Opcode data for an (opcode, mode) pair 177 | type opcodeData struct { 178 | sym opsym // internal opcode symbol 179 | mode Mode // addressing mode 180 | opcode byte // opcode hex value 181 | length byte // length of opcode + operand in bytes 182 | cycles byte // number of CPU cycles to execute command 183 | bpcycles byte // additional CPU cycles if command crosses page boundary 184 | cmos bool // whether the opcode/mode pair is valid only on 65C02 185 | } 186 | 187 | // All valid (opcode, mode) pairs 188 | var data = []opcodeData{ 189 | {symLDA, IMM, 0xa9, 2, 2, 0, false}, 190 | {symLDA, ZPG, 0xa5, 2, 3, 0, false}, 191 | {symLDA, ZPX, 0xb5, 2, 4, 0, false}, 192 | {symLDA, ABS, 0xad, 3, 4, 0, false}, 193 | {symLDA, ABX, 0xbd, 3, 4, 1, false}, 194 | {symLDA, ABY, 0xb9, 3, 4, 1, false}, 195 | {symLDA, IDX, 0xa1, 2, 6, 0, false}, 196 | {symLDA, IDY, 0xb1, 2, 5, 1, false}, 197 | {symLDA, IND, 0xb2, 2, 5, 0, true}, 198 | 199 | {symLDX, IMM, 0xa2, 2, 2, 0, false}, 200 | {symLDX, ZPG, 0xa6, 2, 3, 0, false}, 201 | {symLDX, ZPY, 0xb6, 2, 4, 0, false}, 202 | {symLDX, ABS, 0xae, 3, 4, 0, false}, 203 | {symLDX, ABY, 0xbe, 3, 4, 1, false}, 204 | 205 | {symLDY, IMM, 0xa0, 2, 2, 0, false}, 206 | {symLDY, ZPG, 0xa4, 2, 3, 0, false}, 207 | {symLDY, ZPX, 0xb4, 2, 4, 0, false}, 208 | {symLDY, ABS, 0xac, 3, 4, 0, false}, 209 | {symLDY, ABX, 0xbc, 3, 4, 1, false}, 210 | 211 | {symSTA, ZPG, 0x85, 2, 3, 0, false}, 212 | {symSTA, ZPX, 0x95, 2, 4, 0, false}, 213 | {symSTA, ABS, 0x8d, 3, 4, 0, false}, 214 | {symSTA, ABX, 0x9d, 3, 5, 0, false}, 215 | {symSTA, ABY, 0x99, 3, 5, 0, false}, 216 | {symSTA, IDX, 0x81, 2, 6, 0, false}, 217 | {symSTA, IDY, 0x91, 2, 6, 0, false}, 218 | {symSTA, IND, 0x92, 2, 5, 0, true}, 219 | 220 | {symSTX, ZPG, 0x86, 2, 3, 0, false}, 221 | {symSTX, ZPY, 0x96, 2, 4, 0, false}, 222 | {symSTX, ABS, 0x8e, 3, 4, 0, false}, 223 | 224 | {symSTY, ZPG, 0x84, 2, 3, 0, false}, 225 | {symSTY, ZPX, 0x94, 2, 4, 0, false}, 226 | {symSTY, ABS, 0x8c, 3, 4, 0, false}, 227 | 228 | {symSTZ, ZPG, 0x64, 2, 3, 0, true}, 229 | {symSTZ, ZPX, 0x74, 2, 4, 0, true}, 230 | {symSTZ, ABS, 0x9c, 3, 4, 0, true}, 231 | {symSTZ, ABX, 0x9e, 3, 5, 0, true}, 232 | 233 | {symADC, IMM, 0x69, 2, 2, 0, false}, 234 | {symADC, ZPG, 0x65, 2, 3, 0, false}, 235 | {symADC, ZPX, 0x75, 2, 4, 0, false}, 236 | {symADC, ABS, 0x6d, 3, 4, 0, false}, 237 | {symADC, ABX, 0x7d, 3, 4, 1, false}, 238 | {symADC, ABY, 0x79, 3, 4, 1, false}, 239 | {symADC, IDX, 0x61, 2, 6, 0, false}, 240 | {symADC, IDY, 0x71, 2, 5, 1, false}, 241 | {symADC, IND, 0x72, 2, 5, 1, true}, 242 | 243 | {symSBC, IMM, 0xe9, 2, 2, 0, false}, 244 | {symSBC, ZPG, 0xe5, 2, 3, 0, false}, 245 | {symSBC, ZPX, 0xf5, 2, 4, 0, false}, 246 | {symSBC, ABS, 0xed, 3, 4, 0, false}, 247 | {symSBC, ABX, 0xfd, 3, 4, 1, false}, 248 | {symSBC, ABY, 0xf9, 3, 4, 1, false}, 249 | {symSBC, IDX, 0xe1, 2, 6, 0, false}, 250 | {symSBC, IDY, 0xf1, 2, 5, 1, false}, 251 | {symSBC, IND, 0xf2, 2, 5, 1, true}, 252 | 253 | {symCMP, IMM, 0xc9, 2, 2, 0, false}, 254 | {symCMP, ZPG, 0xc5, 2, 3, 0, false}, 255 | {symCMP, ZPX, 0xd5, 2, 4, 0, false}, 256 | {symCMP, ABS, 0xcd, 3, 4, 0, false}, 257 | {symCMP, ABX, 0xdd, 3, 4, 1, false}, 258 | {symCMP, ABY, 0xd9, 3, 4, 1, false}, 259 | {symCMP, IDX, 0xc1, 2, 6, 0, false}, 260 | {symCMP, IDY, 0xd1, 2, 5, 1, false}, 261 | {symCMP, IND, 0xd2, 2, 5, 0, true}, 262 | 263 | {symCPX, IMM, 0xe0, 2, 2, 0, false}, 264 | {symCPX, ZPG, 0xe4, 2, 3, 0, false}, 265 | {symCPX, ABS, 0xec, 3, 4, 0, false}, 266 | 267 | {symCPY, IMM, 0xc0, 2, 2, 0, false}, 268 | {symCPY, ZPG, 0xc4, 2, 3, 0, false}, 269 | {symCPY, ABS, 0xcc, 3, 4, 0, false}, 270 | 271 | {symBIT, IMM, 0x89, 2, 2, 0, true}, 272 | {symBIT, ZPG, 0x24, 2, 3, 0, false}, 273 | {symBIT, ZPX, 0x34, 2, 4, 0, true}, 274 | {symBIT, ABS, 0x2c, 3, 4, 0, false}, 275 | {symBIT, ABX, 0x3c, 3, 4, 1, true}, 276 | 277 | {symCLC, IMP, 0x18, 1, 2, 0, false}, 278 | {symSEC, IMP, 0x38, 1, 2, 0, false}, 279 | {symCLI, IMP, 0x58, 1, 2, 0, false}, 280 | {symSEI, IMP, 0x78, 1, 2, 0, false}, 281 | {symCLD, IMP, 0xd8, 1, 2, 0, false}, 282 | {symSED, IMP, 0xf8, 1, 2, 0, false}, 283 | {symCLV, IMP, 0xb8, 1, 2, 0, false}, 284 | 285 | {symBCC, REL, 0x90, 2, 2, 1, false}, 286 | {symBCS, REL, 0xb0, 2, 2, 1, false}, 287 | {symBEQ, REL, 0xf0, 2, 2, 1, false}, 288 | {symBNE, REL, 0xd0, 2, 2, 1, false}, 289 | {symBMI, REL, 0x30, 2, 2, 1, false}, 290 | {symBPL, REL, 0x10, 2, 2, 1, false}, 291 | {symBVC, REL, 0x50, 2, 2, 1, false}, 292 | {symBVS, REL, 0x70, 2, 2, 1, false}, 293 | {symBRA, REL, 0x80, 2, 2, 1, true}, 294 | 295 | {symBRK, IMP, 0x00, 1, 7, 0, false}, 296 | 297 | {symAND, IMM, 0x29, 2, 2, 0, false}, 298 | {symAND, ZPG, 0x25, 2, 3, 0, false}, 299 | {symAND, ZPX, 0x35, 2, 4, 0, false}, 300 | {symAND, ABS, 0x2d, 3, 4, 0, false}, 301 | {symAND, ABX, 0x3d, 3, 4, 1, false}, 302 | {symAND, ABY, 0x39, 3, 4, 1, false}, 303 | {symAND, IDX, 0x21, 2, 6, 0, false}, 304 | {symAND, IDY, 0x31, 2, 5, 1, false}, 305 | {symAND, IND, 0x32, 2, 5, 0, true}, 306 | 307 | {symORA, IMM, 0x09, 2, 2, 0, false}, 308 | {symORA, ZPG, 0x05, 2, 3, 0, false}, 309 | {symORA, ZPX, 0x15, 2, 4, 0, false}, 310 | {symORA, ABS, 0x0d, 3, 4, 0, false}, 311 | {symORA, ABX, 0x1d, 3, 4, 1, false}, 312 | {symORA, ABY, 0x19, 3, 4, 1, false}, 313 | {symORA, IDX, 0x01, 2, 6, 0, false}, 314 | {symORA, IDY, 0x11, 2, 5, 1, false}, 315 | {symORA, IND, 0x12, 2, 5, 0, true}, 316 | 317 | {symEOR, IMM, 0x49, 2, 2, 0, false}, 318 | {symEOR, ZPG, 0x45, 2, 3, 0, false}, 319 | {symEOR, ZPX, 0x55, 2, 4, 0, false}, 320 | {symEOR, ABS, 0x4d, 3, 4, 0, false}, 321 | {symEOR, ABX, 0x5d, 3, 4, 1, false}, 322 | {symEOR, ABY, 0x59, 3, 4, 1, false}, 323 | {symEOR, IDX, 0x41, 2, 6, 0, false}, 324 | {symEOR, IDY, 0x51, 2, 5, 1, false}, 325 | {symEOR, IND, 0x52, 2, 5, 0, true}, 326 | 327 | {symINC, ZPG, 0xe6, 2, 5, 0, false}, 328 | {symINC, ZPX, 0xf6, 2, 6, 0, false}, 329 | {symINC, ABS, 0xee, 3, 6, 0, false}, 330 | {symINC, ABX, 0xfe, 3, 7, 0, false}, 331 | {symINC, ACC, 0x1a, 1, 2, 0, true}, 332 | 333 | {symDEC, ZPG, 0xc6, 2, 5, 0, false}, 334 | {symDEC, ZPX, 0xd6, 2, 6, 0, false}, 335 | {symDEC, ABS, 0xce, 3, 6, 0, false}, 336 | {symDEC, ABX, 0xde, 3, 7, 0, false}, 337 | {symDEC, ACC, 0x3a, 1, 2, 0, true}, 338 | 339 | {symINX, IMP, 0xe8, 1, 2, 0, false}, 340 | {symINY, IMP, 0xc8, 1, 2, 0, false}, 341 | 342 | {symDEX, IMP, 0xca, 1, 2, 0, false}, 343 | {symDEY, IMP, 0x88, 1, 2, 0, false}, 344 | 345 | {symJMP, ABS, 0x4c, 3, 3, 0, false}, 346 | {symJMP, ABX, 0x7c, 3, 6, 0, true}, 347 | {symJMP, IND, 0x6c, 3, 5, 0, false}, 348 | 349 | {symJSR, ABS, 0x20, 3, 6, 0, false}, 350 | {symRTS, IMP, 0x60, 1, 6, 0, false}, 351 | 352 | {symRTI, IMP, 0x40, 1, 6, 0, false}, 353 | 354 | {symNOP, IMP, 0xea, 1, 2, 0, false}, 355 | 356 | {symTAX, IMP, 0xaa, 1, 2, 0, false}, 357 | {symTXA, IMP, 0x8a, 1, 2, 0, false}, 358 | {symTAY, IMP, 0xa8, 1, 2, 0, false}, 359 | {symTYA, IMP, 0x98, 1, 2, 0, false}, 360 | {symTXS, IMP, 0x9a, 1, 2, 0, false}, 361 | {symTSX, IMP, 0xba, 1, 2, 0, false}, 362 | 363 | {symTRB, ZPG, 0x14, 2, 5, 0, true}, 364 | {symTRB, ABS, 0x1c, 3, 6, 0, true}, 365 | {symTSB, ZPG, 0x04, 2, 5, 0, true}, 366 | {symTSB, ABS, 0x0c, 3, 6, 0, true}, 367 | 368 | {symPHA, IMP, 0x48, 1, 3, 0, false}, 369 | {symPLA, IMP, 0x68, 1, 4, 0, false}, 370 | {symPHP, IMP, 0x08, 1, 3, 0, false}, 371 | {symPLP, IMP, 0x28, 1, 4, 0, false}, 372 | {symPHX, IMP, 0xda, 1, 3, 0, true}, 373 | {symPLX, IMP, 0xfa, 1, 4, 0, true}, 374 | {symPHY, IMP, 0x5a, 1, 3, 0, true}, 375 | {symPLY, IMP, 0x7a, 1, 4, 0, true}, 376 | 377 | {symASL, ACC, 0x0a, 1, 2, 0, false}, 378 | {symASL, ZPG, 0x06, 2, 5, 0, false}, 379 | {symASL, ZPX, 0x16, 2, 6, 0, false}, 380 | {symASL, ABS, 0x0e, 3, 6, 0, false}, 381 | {symASL, ABX, 0x1e, 3, 7, 0, false}, 382 | 383 | {symLSR, ACC, 0x4a, 1, 2, 0, false}, 384 | {symLSR, ZPG, 0x46, 2, 5, 0, false}, 385 | {symLSR, ZPX, 0x56, 2, 6, 0, false}, 386 | {symLSR, ABS, 0x4e, 3, 6, 0, false}, 387 | {symLSR, ABX, 0x5e, 3, 7, 0, false}, 388 | 389 | {symROL, ACC, 0x2a, 1, 2, 0, false}, 390 | {symROL, ZPG, 0x26, 2, 5, 0, false}, 391 | {symROL, ZPX, 0x36, 2, 6, 0, false}, 392 | {symROL, ABS, 0x2e, 3, 6, 0, false}, 393 | {symROL, ABX, 0x3e, 3, 7, 0, false}, 394 | 395 | {symROR, ACC, 0x6a, 1, 2, 0, false}, 396 | {symROR, ZPG, 0x66, 2, 5, 0, false}, 397 | {symROR, ZPX, 0x76, 2, 6, 0, false}, 398 | {symROR, ABS, 0x6e, 3, 6, 0, false}, 399 | {symROR, ABX, 0x7e, 3, 7, 0, false}, 400 | } 401 | 402 | // Unused opcodes 403 | type unused struct { 404 | opcode byte 405 | mode Mode 406 | length byte 407 | cycles byte 408 | } 409 | 410 | var unusedData = []unused{ 411 | {0x02, ZPG, 2, 2}, 412 | {0x22, ZPG, 2, 2}, 413 | {0x42, ZPG, 2, 2}, 414 | {0x62, ZPG, 2, 2}, 415 | {0x82, ZPG, 2, 2}, 416 | {0xc2, ZPG, 2, 2}, 417 | {0xe2, ZPG, 2, 2}, 418 | {0x03, ACC, 1, 1}, 419 | {0x13, ACC, 1, 1}, 420 | {0x23, ACC, 1, 1}, 421 | {0x33, ACC, 1, 1}, 422 | {0x43, ACC, 1, 1}, 423 | {0x53, ACC, 1, 1}, 424 | {0x63, ACC, 1, 1}, 425 | {0x73, ACC, 1, 1}, 426 | {0x83, ACC, 1, 1}, 427 | {0x93, ACC, 1, 1}, 428 | {0xa3, ACC, 1, 1}, 429 | {0xb3, ACC, 1, 1}, 430 | {0xc3, ACC, 1, 1}, 431 | {0xd3, ACC, 1, 1}, 432 | {0xe3, ACC, 1, 1}, 433 | {0xf3, ACC, 1, 1}, 434 | {0x44, ZPG, 2, 3}, 435 | {0x54, ZPG, 2, 4}, 436 | {0xd4, ZPG, 2, 4}, 437 | {0xf4, ZPG, 2, 4}, 438 | {0x07, ACC, 1, 1}, 439 | {0x17, ACC, 1, 1}, 440 | {0x27, ACC, 1, 1}, 441 | {0x37, ACC, 1, 1}, 442 | {0x47, ACC, 1, 1}, 443 | {0x57, ACC, 1, 1}, 444 | {0x67, ACC, 1, 1}, 445 | {0x77, ACC, 1, 1}, 446 | {0x87, ACC, 1, 1}, 447 | {0x97, ACC, 1, 1}, 448 | {0xa7, ACC, 1, 1}, 449 | {0xb7, ACC, 1, 1}, 450 | {0xc7, ACC, 1, 1}, 451 | {0xd7, ACC, 1, 1}, 452 | {0xe7, ACC, 1, 1}, 453 | {0xf7, ACC, 1, 1}, 454 | {0x0b, ACC, 1, 1}, 455 | {0x1b, ACC, 1, 1}, 456 | {0x2b, ACC, 1, 1}, 457 | {0x3b, ACC, 1, 1}, 458 | {0x4b, ACC, 1, 1}, 459 | {0x5b, ACC, 1, 1}, 460 | {0x6b, ACC, 1, 1}, 461 | {0x7b, ACC, 1, 1}, 462 | {0x8b, ACC, 1, 1}, 463 | {0x9b, ACC, 1, 1}, 464 | {0xab, ACC, 1, 1}, 465 | {0xbb, ACC, 1, 1}, 466 | {0xcb, ACC, 1, 1}, 467 | {0xdb, ACC, 1, 1}, 468 | {0xeb, ACC, 1, 1}, 469 | {0xfb, ACC, 1, 1}, 470 | {0x5c, ABS, 3, 8}, 471 | {0xdc, ABS, 3, 4}, 472 | {0xfc, ABS, 3, 4}, 473 | {0x0f, ACC, 1, 1}, 474 | {0x1f, ACC, 1, 1}, 475 | {0x2f, ACC, 1, 1}, 476 | {0x3f, ACC, 1, 1}, 477 | {0x4f, ACC, 1, 1}, 478 | {0x5f, ACC, 1, 1}, 479 | {0x6f, ACC, 1, 1}, 480 | {0x7f, ACC, 1, 1}, 481 | {0x8f, ACC, 1, 1}, 482 | {0x9f, ACC, 1, 1}, 483 | {0xaf, ACC, 1, 1}, 484 | {0xbf, ACC, 1, 1}, 485 | {0xcf, ACC, 1, 1}, 486 | {0xdf, ACC, 1, 1}, 487 | {0xef, ACC, 1, 1}, 488 | {0xff, ACC, 1, 1}, 489 | } 490 | 491 | // An Instruction describes a CPU instruction, including its name, 492 | // its addressing mode, its opcode value, its operand size, and its CPU cycle 493 | // cost. 494 | type Instruction struct { 495 | Name string // all-caps name of the instruction 496 | Mode Mode // addressing mode 497 | Opcode byte // hexadecimal opcode value 498 | Length byte // combined size of opcode and operand, in bytes 499 | Cycles byte // number of CPU cycles to execute the instruction 500 | BPCycles byte // additional cycles required if boundary page crossed 501 | fn instfunc // emulator implementation of the function 502 | } 503 | 504 | // An InstructionSet defines the set of all possible instructions that 505 | // can run on the emulated CPU. 506 | type InstructionSet struct { 507 | Arch Architecture 508 | instructions [256]Instruction // all instructions by opcode 509 | variants map[string][]*Instruction // variants of each instruction 510 | } 511 | 512 | // Lookup retrieves a CPU instruction corresponding to the requested opcode. 513 | func (s *InstructionSet) Lookup(opcode byte) *Instruction { 514 | return &s.instructions[opcode] 515 | } 516 | 517 | // GetInstructions returns all CPU instructions whose name matches the 518 | // provided string. 519 | func (s *InstructionSet) GetInstructions(name string) []*Instruction { 520 | return s.variants[strings.ToUpper(name)] 521 | } 522 | 523 | // Create an instruction set for a CPU architecture. 524 | func newInstructionSet(arch Architecture) *InstructionSet { 525 | set := &InstructionSet{Arch: arch} 526 | 527 | // Create a map from symbol to implementation for fast lookups. 528 | symToImpl := make(map[opsym]*opcodeImpl, len(impl)) 529 | for i := range impl { 530 | symToImpl[impl[i].sym] = &impl[i] 531 | } 532 | 533 | // Create a map from instruction name to the slice of all instruction 534 | // variants matching that name. 535 | set.variants = make(map[string][]*Instruction) 536 | 537 | unusedName := "???" 538 | 539 | // For each instruction, create a list of opcode variants valid for 540 | // the architecture. 541 | for _, d := range data { 542 | inst := &set.instructions[d.opcode] 543 | 544 | // If opcode has only a CMOS implementation and this is NMOS, create 545 | // an unused instruction for it. 546 | if d.cmos && arch != CMOS { 547 | inst.Name = unusedName 548 | inst.Mode = d.mode 549 | inst.Opcode = d.opcode 550 | inst.Length = d.length 551 | inst.Cycles = d.cycles 552 | inst.BPCycles = 0 553 | inst.fn = (*CPU).unusedn 554 | continue 555 | } 556 | 557 | impl := symToImpl[d.sym] 558 | if impl.fn[arch] == nil { 559 | continue // some opcodes have no architecture implementation 560 | } 561 | 562 | inst.Name = impl.name 563 | inst.Mode = d.mode 564 | inst.Opcode = d.opcode 565 | inst.Length = d.length 566 | inst.Cycles = d.cycles 567 | inst.BPCycles = d.bpcycles 568 | inst.fn = impl.fn[arch] 569 | 570 | set.variants[inst.Name] = append(set.variants[inst.Name], inst) 571 | } 572 | 573 | // Add unused opcodes to the instruction set. This information is useful 574 | // mostly for 65c02, where unused operations do something predicable 575 | // (i.e., eat cycles and nothing else). 576 | for _, u := range unusedData { 577 | inst := &set.instructions[u.opcode] 578 | inst.Name = unusedName 579 | inst.Mode = u.mode 580 | inst.Opcode = u.opcode 581 | inst.Length = u.length 582 | inst.Cycles = u.cycles 583 | inst.BPCycles = 0 584 | switch arch { 585 | case NMOS: 586 | inst.fn = (*CPU).unusedn 587 | case CMOS: 588 | inst.fn = (*CPU).unusedc 589 | } 590 | } 591 | 592 | for i := 0; i < 256; i++ { 593 | if set.instructions[i].Name == "" { 594 | panic("missing instruction") 595 | } 596 | } 597 | return set 598 | } 599 | 600 | var instructionSets [2]*InstructionSet 601 | 602 | // GetInstructionSet returns an instruction set for the requested CPU 603 | // architecture. 604 | func GetInstructionSet(arch Architecture) *InstructionSet { 605 | if instructionSets[arch] == nil { 606 | // Lazy-create the instruction set. 607 | instructionSets[arch] = newInstructionSet(arch) 608 | } 609 | return instructionSets[arch] 610 | } 611 | -------------------------------------------------------------------------------- /cpu/memory.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2018 Brett Vickers. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package cpu 6 | 7 | import "errors" 8 | 9 | // Errors 10 | var ( 11 | ErrMemoryOutOfBounds = errors.New("Memory access out of bounds") 12 | ) 13 | 14 | // The Memory interface presents an interface to the CPU through which all 15 | // memory accesses occur. 16 | type Memory interface { 17 | // LoadByte loads a single byte from the address and returns it. 18 | LoadByte(addr uint16) byte 19 | 20 | // LoadBytes loads multiple bytes from the address and stores them into 21 | // the buffer 'b'. 22 | LoadBytes(addr uint16, b []byte) 23 | 24 | // LoadAddress loads a 16-bit address value from the requested address and 25 | // returns it. 26 | LoadAddress(addr uint16) uint16 27 | 28 | // StoreByte stores a byte to the requested address. 29 | StoreByte(addr uint16, v byte) 30 | 31 | // StoreBytes stores multiple bytes to the requested address. 32 | StoreBytes(addr uint16, b []byte) 33 | 34 | // StoreAddres stores a 16-bit address 'v' to the requested address. 35 | StoreAddress(addr uint16, v uint16) 36 | } 37 | 38 | // FlatMemory represents an entire 16-bit address space as a singular 39 | // 64K buffer. 40 | type FlatMemory struct { 41 | b [64 * 1024]byte 42 | } 43 | 44 | // NewFlatMemory creates a new 16-bit memory space. 45 | func NewFlatMemory() *FlatMemory { 46 | return &FlatMemory{} 47 | } 48 | 49 | // LoadByte loads a single byte from the address and returns it. 50 | func (m *FlatMemory) LoadByte(addr uint16) byte { 51 | return m.b[addr] 52 | } 53 | 54 | // LoadBytes loads multiple bytes from the address and returns them. 55 | func (m *FlatMemory) LoadBytes(addr uint16, b []byte) { 56 | if int(addr)+len(b) <= len(m.b) { 57 | copy(b, m.b[addr:]) 58 | } else { 59 | r0 := len(m.b) - int(addr) 60 | r1 := len(b) - r0 61 | copy(b, m.b[addr:]) 62 | copy(b[r0:], make([]byte, r1)) 63 | } 64 | } 65 | 66 | // LoadAddress loads a 16-bit address value from the requested address and 67 | // returns it. 68 | // 69 | // When the address spans 2 pages (i.e., address ends in 0xff), the low 70 | // byte of the loaded address comes from a page-wrapped address. For example, 71 | // LoadAddress on $12FF reads the low byte from $12FF and the high byte from 72 | // $1200. This mimics the behavior of the NMOS 6502. 73 | func (m *FlatMemory) LoadAddress(addr uint16) uint16 { 74 | if (addr & 0xff) == 0xff { 75 | return uint16(m.b[addr]) | uint16(m.b[addr-0xff])<<8 76 | } 77 | return uint16(m.b[addr]) | uint16(m.b[addr+1])<<8 78 | } 79 | 80 | // StoreByte stores a byte at the requested address. 81 | func (m *FlatMemory) StoreByte(addr uint16, v byte) { 82 | m.b[addr] = v 83 | } 84 | 85 | // StoreBytes stores multiple bytes to the requested address. 86 | func (m *FlatMemory) StoreBytes(addr uint16, b []byte) { 87 | copy(m.b[addr:], b) 88 | } 89 | 90 | // StoreAddress stores a 16-bit address value to the requested address. 91 | func (m *FlatMemory) StoreAddress(addr uint16, v uint16) { 92 | m.b[addr] = byte(v & 0xff) 93 | if (addr & 0xff) == 0xff { 94 | m.b[addr-0xff] = byte(v >> 8) 95 | } else { 96 | m.b[addr+1] = byte(v >> 8) 97 | } 98 | } 99 | 100 | // Return the offset address 'addr' + 'offset'. If the offset 101 | // crossed a page boundary, return 'pageCrossed' as true. 102 | func offsetAddress(addr uint16, offset byte) (newAddr uint16, pageCrossed bool) { 103 | newAddr = addr + uint16(offset) 104 | pageCrossed = ((newAddr & 0xff00) != (addr & 0xff00)) 105 | return newAddr, pageCrossed 106 | } 107 | 108 | // Offset a zero-page address 'addr' by 'offset'. If the address 109 | // exceeds the zero-page address space, wrap it. 110 | func offsetZeroPage(addr uint16, offset byte) uint16 { 111 | addr += uint16(offset) 112 | if addr >= 0x100 { 113 | addr -= 0x100 114 | } 115 | return addr 116 | } 117 | 118 | // Convert a 1- or 2-byte operand into an address. 119 | func operandToAddress(operand []byte) uint16 { 120 | switch { 121 | case len(operand) == 1: 122 | return uint16(operand[0]) 123 | case len(operand) == 2: 124 | return uint16(operand[0]) | uint16(operand[1])<<8 125 | } 126 | return 0 127 | } 128 | 129 | // Given a 1-byte stack pointer register, return the stack 130 | // corresponding memory address. 131 | func stackAddress(offset byte) uint16 { 132 | return uint16(0x100) + uint16(offset) 133 | } 134 | -------------------------------------------------------------------------------- /cpu/register.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2018 Brett Vickers. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package cpu 6 | 7 | // Registers contains the state of all 6502 registers. 8 | type Registers struct { 9 | A byte // accumulator 10 | X byte // X indexing register 11 | Y byte // Y indexing register 12 | SP byte // stack pointer ($100 + SP = stack memory location) 13 | PC uint16 // program counter 14 | Carry bool // PS: Carry bit 15 | Zero bool // PS: Zero bit 16 | InterruptDisable bool // PS: Interrupt disable bit 17 | Decimal bool // PS: Decimal bit 18 | Overflow bool // PS: Overflow bit 19 | Sign bool // PS: Sign bit 20 | } 21 | 22 | // Bits assigned to the processor status byte 23 | const ( 24 | CarryBit = 1 << 0 25 | ZeroBit = 1 << 1 26 | InterruptDisableBit = 1 << 2 27 | DecimalBit = 1 << 3 28 | BreakBit = 1 << 4 29 | ReservedBit = 1 << 5 30 | OverflowBit = 1 << 6 31 | SignBit = 1 << 7 32 | ) 33 | 34 | // SavePS saves the CPU processor status into a byte value. The break bit 35 | // is set if requested. 36 | func (r *Registers) SavePS(brk bool) byte { 37 | var ps byte = ReservedBit // always saved as on 38 | if r.Carry { 39 | ps |= CarryBit 40 | } 41 | if r.Zero { 42 | ps |= ZeroBit 43 | } 44 | if r.InterruptDisable { 45 | ps |= InterruptDisableBit 46 | } 47 | if r.Decimal { 48 | ps |= DecimalBit 49 | } 50 | if brk { 51 | ps |= BreakBit 52 | } 53 | if r.Overflow { 54 | ps |= OverflowBit 55 | } 56 | if r.Sign { 57 | ps |= SignBit 58 | } 59 | return ps 60 | } 61 | 62 | // RestorePS restores the CPU processor status from a byte. 63 | func (r *Registers) RestorePS(ps byte) { 64 | r.Carry = ((ps & CarryBit) != 0) 65 | r.Zero = ((ps & ZeroBit) != 0) 66 | r.InterruptDisable = ((ps & InterruptDisableBit) != 0) 67 | r.Decimal = ((ps & DecimalBit) != 0) 68 | r.Overflow = ((ps & OverflowBit) != 0) 69 | r.Sign = ((ps & SignBit) != 0) 70 | } 71 | 72 | func boolToUint32(v bool) uint32 { 73 | if v { 74 | return 1 75 | } 76 | return 0 77 | } 78 | 79 | func boolToByte(v bool) byte { 80 | if v { 81 | return 1 82 | } 83 | return 0 84 | } 85 | 86 | // Init initializes all registers. A, X, Y = 0. SP = 0xff. PC = 0. PS = 0. 87 | func (r *Registers) Init() { 88 | r.A = 0 89 | r.X = 0 90 | r.Y = 0 91 | r.SP = 0xff 92 | r.PC = 0 93 | r.RestorePS(0) 94 | } 95 | -------------------------------------------------------------------------------- /disasm/disasm.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2018 Brett Vickers. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package disasm implements a 6502 instruction set 6 | // disassembler. 7 | package disasm 8 | 9 | import ( 10 | "fmt" 11 | "strings" 12 | 13 | "github.com/beevik/go6502/cpu" 14 | ) 15 | 16 | // Theme is a struct of color escape codes used to colorize the output 17 | // of the disassembler. 18 | type Theme struct { 19 | Addr string 20 | Code string 21 | Inst string 22 | Operand string 23 | RegName string 24 | RegValue string 25 | RegEqual string 26 | Source string 27 | Annotation string 28 | Reset string 29 | } 30 | 31 | // Disassembler formatting for addressing modes 32 | var modeFormat = []string{ 33 | "#$%s", // IMM 34 | "%s", // IMP 35 | "$%s", // REL 36 | "$%s", // ZPG 37 | "$%s,X", // ZPX 38 | "$%s,Y", // ZPY 39 | "$%s", // ABS 40 | "$%s,X", // ABX 41 | "$%s,Y", // ABY 42 | "($%s)", // IND 43 | "($%s,X)", // IDX 44 | "($%s),Y", // IDY 45 | "%s", // ACC 46 | } 47 | 48 | var hex = "0123456789ABCDEF" 49 | 50 | type Flags uint8 51 | 52 | const ( 53 | ShowAddress Flags = 1 << iota 54 | ShowCode 55 | ShowInstruction 56 | ShowRegisters 57 | ShowCycles 58 | ShowAnnotations 59 | 60 | ShowBasic = ShowAddress | ShowCode | ShowInstruction | ShowAnnotations 61 | ShowFull = ShowAddress | ShowCode | ShowInstruction | ShowRegisters | ShowCycles 62 | ) 63 | 64 | // Disassemble the machine code at memory address addr. Return a string 65 | // representing the disassembled instruction and the address of the next 66 | // instruction. 67 | func Disassemble(c *cpu.CPU, addr uint16, flags Flags, anno string, theme *Theme) (line string, next uint16) { 68 | opcode := c.Mem.LoadByte(addr) 69 | inst := c.InstSet.Lookup(opcode) 70 | next = addr + uint16(inst.Length) 71 | line = "" 72 | 73 | if (flags & ShowAddress) != 0 { 74 | line += fmt.Sprintf("%s%04X%s- ", theme.Addr, addr, theme.Reset) 75 | } 76 | 77 | if (flags & ShowCode) != 0 { 78 | var csbuf [3]byte 79 | c.Mem.LoadBytes(addr, csbuf[:next-addr]) 80 | line += fmt.Sprintf("%s%-8s%s ", theme.Code, codeString(csbuf[:next-addr]), theme.Reset) 81 | } 82 | 83 | if (flags & ShowInstruction) != 0 { 84 | var buf [2]byte 85 | operand := buf[:inst.Length-1] 86 | c.Mem.LoadBytes(addr+1, operand) 87 | if inst.Mode == cpu.REL { 88 | // Convert relative offset to absolute address. 89 | operand = buf[:] 90 | braddr := int(addr) + int(inst.Length) + byteToInt(operand[0]) 91 | operand[0] = byte(braddr) 92 | operand[1] = byte(braddr >> 8) 93 | } 94 | 95 | // Return string composed of CPU instruction and operand. 96 | line += fmt.Sprintf("%s%s %s"+modeFormat[inst.Mode]+"%s", theme.Inst, inst.Name, theme.Operand, hexString(operand), theme.Reset) 97 | 98 | // Pad to next column using uncolorized version of the operand. 99 | dummy := fmt.Sprintf(modeFormat[inst.Mode], hexString(operand)) 100 | line += strings.Repeat(" ", 9-len(dummy)) 101 | } 102 | 103 | if (flags & ShowRegisters) != 0 { 104 | line += GetRegisterString(&c.Reg, theme) 105 | } 106 | 107 | if (flags & ShowCycles) != 0 { 108 | line += GetCyclesString(c, theme) 109 | } 110 | 111 | if (flags&ShowAnnotations) != 0 && anno != "" { 112 | line += fmt.Sprintf(" ; %s%s%s", theme.Annotation, anno, theme.Reset) 113 | } 114 | 115 | return line, next 116 | } 117 | 118 | // GetCyclesString returns a string describing the number of elapsed 119 | // CPU cycles. 120 | func GetCyclesString(c *cpu.CPU, theme *Theme) string { 121 | return fmt.Sprintf("%sC%s=%s%d%s", 122 | theme.RegName, theme.RegEqual, theme.RegValue, c.Cycles, 123 | theme.Reset) 124 | } 125 | 126 | // GetRegisterString returns a string describing the contents of the 6502 127 | // registers. 128 | func GetRegisterString(r *cpu.Registers, theme *Theme) string { 129 | fmt8 := func(name string, val byte) string { 130 | return fmt.Sprintf("%s%s%s=%s%02X ", 131 | theme.RegName, name, theme.RegEqual, theme.RegValue, val) 132 | } 133 | fmt16 := func(name string, val uint16) string { 134 | return fmt.Sprintf("%s%s%s=%s%04X ", 135 | theme.RegName, name, theme.RegEqual, theme.RegValue, val) 136 | } 137 | fmtS := func(name string, val string) string { 138 | return fmt.Sprintf("%s%s%s=%s[%s] ", 139 | theme.RegName, name, theme.RegEqual, theme.RegValue, val) 140 | } 141 | 142 | return fmt8("A", r.A) + 143 | fmt8("X", r.X) + 144 | fmt8("Y", r.Y) + 145 | fmtS("PS", getStatusBits(r)) + 146 | fmt8("SP", r.SP) + 147 | fmt16("PC", r.PC) + 148 | theme.Reset 149 | } 150 | 151 | func codeString(b []byte) string { 152 | switch len(b) { 153 | case 1: 154 | return fmt.Sprintf("%02X", b[0]) 155 | case 2: 156 | return fmt.Sprintf("%02X %02X", b[0], b[1]) 157 | case 3: 158 | return fmt.Sprintf("%02X %02X %02X", b[0], b[1], b[2]) 159 | default: 160 | return "" 161 | } 162 | } 163 | 164 | // Return a hexadecimal string representation of the byte slice. 165 | func hexString(b []byte) string { 166 | hexlen := len(b) * 2 167 | hexbuf := make([]byte, hexlen) 168 | j := hexlen - 1 169 | for _, n := range b { 170 | hexbuf[j] = hex[n&0xf] 171 | hexbuf[j-1] = hex[n>>4] 172 | j -= 2 173 | } 174 | return string(hexbuf) 175 | } 176 | 177 | func getStatusBits(r *cpu.Registers) string { 178 | v := func(bit bool, ch byte) byte { 179 | if bit { 180 | return ch 181 | } 182 | return '-' 183 | } 184 | b := []byte{ 185 | v(r.Sign, 'N'), 186 | v(r.Zero, 'Z'), 187 | v(r.Carry, 'C'), 188 | v(r.InterruptDisable, 'I'), 189 | v(r.Decimal, 'D'), 190 | v(r.Overflow, 'V'), 191 | } 192 | return string(b) 193 | } 194 | 195 | func byteToInt(b byte) int { 196 | if b >= 0x80 { 197 | return int(b) - 256 198 | } 199 | return int(b) 200 | } 201 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/beevik/go6502 2 | 3 | go 1.21 4 | 5 | require ( 6 | github.com/beevik/cmd v0.3.0 7 | github.com/beevik/prefixtree/v2 v2.0.1 8 | golang.org/x/sys v0.29.0 9 | ) 10 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/beevik/cmd v0.3.0 h1:QjmDdyORcj+KCndcQHT3op2ONMVcV+gKaBlkmXjwl/8= 2 | github.com/beevik/cmd v0.3.0/go.mod h1:dpRu0gHueCpDS1wE+UVWiMEgvs23E4djsyePI2l/IsY= 3 | github.com/beevik/prefixtree/v2 v2.0.1 h1:RFXjlvdx/whSsnb47Z88Nnd0wpnI5kEi23N5IcD8J1g= 4 | github.com/beevik/prefixtree/v2 v2.0.1/go.mod h1:XHO9bShx0lkQYkbYKtfnQqtVCVvchjxgCRnLd4qNdgI= 5 | golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= 6 | golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 7 | -------------------------------------------------------------------------------- /host/.gitignore: -------------------------------------------------------------------------------- 1 | host 2 | debug.test 3 | -------------------------------------------------------------------------------- /host/cmds.go: -------------------------------------------------------------------------------- 1 | package host 2 | 3 | import "github.com/beevik/cmd" 4 | 5 | var cmds *cmd.Tree 6 | 7 | func init() { 8 | root := cmd.NewTree(cmd.TreeDescriptor{Name: "go6502"}) 9 | root.AddCommand(cmd.CommandDescriptor{ 10 | Name: "help", 11 | Description: "Display help for a command.", 12 | Usage: "help []", 13 | Data: (*Host).cmdHelp, 14 | }) 15 | root.AddCommand(cmd.CommandDescriptor{ 16 | Name: "annotate", 17 | Brief: "Annotate an address", 18 | Description: "Provide a code annotation at a memory address." + 19 | " When disassembling code at this address, the annotation will" + 20 | " be displayed.", 21 | Usage: "annotate
", 22 | Data: (*Host).cmdAnnotate, 23 | }) 24 | 25 | // Assemble commands 26 | as := root.AddSubtree(cmd.TreeDescriptor{Name: "assemble", Brief: "Assemble commands"}) 27 | as.AddCommand(cmd.CommandDescriptor{ 28 | Name: "file", 29 | Brief: "Assemble a file from disk and save the binary to disk", 30 | Description: "Run the cross-assembler on the specified file," + 31 | " producing a binary file and source map file if successful." + 32 | " If you want verbose output, specify true as a second parameter.", 33 | Usage: "assemble file []", 34 | Data: (*Host).cmdAssembleFile, 35 | }) 36 | as.AddCommand(cmd.CommandDescriptor{ 37 | Name: "interactive", 38 | Brief: "Start interactive assembly mode", 39 | Description: "Start interactive assembler mode. A new prompt will" + 40 | " appear, allowing you to enter assembly language instructions" + 41 | " interactively. Once you type END, the instructions will be" + 42 | " assembled and stored in memory at the specified address.", 43 | Usage: "assemble interactive
", 44 | Data: (*Host).cmdAssembleInteractive, 45 | }) 46 | as.AddCommand(cmd.CommandDescriptor{ 47 | Name: "map", 48 | Brief: "Create a source map file", 49 | Description: "Create an empty source map file for an existing binary" + 50 | " file. Pass the name of the binary file and the origin address it" + 51 | " should load at.", 52 | Usage: "assemble map ", 53 | Data: (*Host).cmdAssembleMap, 54 | }) 55 | 56 | // Breakpoint commands 57 | bp := root.AddSubtree(cmd.TreeDescriptor{Name: "breakpoint", Brief: "Breakpoint commands"}) 58 | bp.AddCommand(cmd.CommandDescriptor{ 59 | Name: "list", 60 | Brief: "List breakpoints", 61 | Description: "List all current breakpoints.", 62 | Usage: "breakpoint list", 63 | Data: (*Host).cmdBreakpointList, 64 | }) 65 | bp.AddCommand(cmd.CommandDescriptor{ 66 | Name: "add", 67 | Brief: "Add a breakpoint", 68 | Description: "Add a breakpoint at the specified address." + 69 | " The breakpoints starts enabled.", 70 | Usage: "breakpoint add
", 71 | Data: (*Host).cmdBreakpointAdd, 72 | }) 73 | bp.AddCommand(cmd.CommandDescriptor{ 74 | Name: "remove", 75 | Brief: "Remove a breakpoint", 76 | Description: "Remove a breakpoint at the specified address.", 77 | Usage: "breakpoint remove
", 78 | Data: (*Host).cmdBreakpointRemove, 79 | }) 80 | bp.AddCommand(cmd.CommandDescriptor{ 81 | Name: "enable", 82 | Brief: "Enable a breakpoint", 83 | Description: "Enable a previously added breakpoint.", 84 | Usage: "breakpoint enable
", 85 | Data: (*Host).cmdBreakpointEnable, 86 | }) 87 | bp.AddCommand(cmd.CommandDescriptor{ 88 | Name: "disable", 89 | Brief: "Disable a breakpoint", 90 | Description: "Disable a previously added breakpoint. This" + 91 | " prevents the breakpoint from being hit when running the" + 92 | " CPU", 93 | Usage: "breakpoint disable
", 94 | Data: (*Host).cmdBreakpointDisable, 95 | }) 96 | 97 | // Data breakpoint commands 98 | db := root.AddSubtree(cmd.TreeDescriptor{Name: "databreakpoint", Brief: "Data Breakpoint commands"}) 99 | db.AddCommand(cmd.CommandDescriptor{ 100 | Name: "list", 101 | Brief: "List data breakpoints", 102 | Description: "List all current data breakpoints.", 103 | Usage: "databreakpoint list", 104 | Data: (*Host).cmdDataBreakpointList, 105 | }) 106 | db.AddCommand(cmd.CommandDescriptor{ 107 | Name: "add", 108 | Brief: "Add a data breakpoint", 109 | Description: "Add a new data breakpoint at the specified" + 110 | " memory address. When the CPU stores data at this address, the " + 111 | " breakpoint will stop the CPU. Optionally, a byte " + 112 | " value may be specified, and the CPU will stop only " + 113 | " when this value is stored. The data breakpoint starts" + 114 | " enabled.", 115 | Usage: "databreakpoint add
[]", 116 | Data: (*Host).cmdDataBreakpointAdd, 117 | }) 118 | db.AddCommand(cmd.CommandDescriptor{ 119 | Name: "remove", 120 | Brief: "Remove a data breakpoint", 121 | Description: "Remove a previously added data breakpoint at" + 122 | " the specified memory address.", 123 | Usage: "databreakpoint remove
", 124 | Data: (*Host).cmdDataBreakpointRemove, 125 | }) 126 | db.AddCommand(cmd.CommandDescriptor{ 127 | Name: "enable", 128 | Brief: "Enable a data breakpoint", 129 | Description: "Enable a previously added breakpoint.", 130 | Usage: "databreakpoint enable
", 131 | Data: (*Host).cmdDataBreakpointEnable, 132 | }) 133 | db.AddCommand(cmd.CommandDescriptor{ 134 | Name: "disable", 135 | Brief: "Disable a data breakpoint", 136 | Description: "Disable a previously added breakpoint.", 137 | Usage: "databreakpoint disable
", 138 | Data: (*Host).cmdDataBreakpointDisable, 139 | }) 140 | 141 | root.AddCommand(cmd.CommandDescriptor{ 142 | Name: "disassemble", 143 | Brief: "Disassemble code", 144 | Description: "Disassemble machine code starting at the requested" + 145 | " address. The number of instruction lines to disassemble may be" + 146 | " specified as an option. If no address is specified, the" + 147 | " disassembly continues from where the last disassembly left off.", 148 | Usage: "disassemble [
] []", 149 | Data: (*Host).cmdDisassemble, 150 | }) 151 | root.AddCommand(cmd.CommandDescriptor{ 152 | Name: "evaluate", 153 | Brief: "Evaluate an expression", 154 | Description: "Evaluate a mathemetical expression.", 155 | Usage: "evaluate ", 156 | Data: (*Host).cmdEvaluate, 157 | }) 158 | root.AddCommand(cmd.CommandDescriptor{ 159 | Name: "execute", 160 | Brief: "Execute a go6502 script file", 161 | Description: "Load a go6502 script file from disk and execute the" + 162 | " commands it contains.", 163 | Usage: "execute ", 164 | Data: (*Host).cmdExecute, 165 | }) 166 | root.AddCommand(cmd.CommandDescriptor{ 167 | Name: "exports", 168 | Brief: "List exported addresses", 169 | Description: "Display a list of all memory addresses exported by" + 170 | " loaded binary files. Exported addresses are stored in a binary" + 171 | " file's associated source map file.", 172 | Usage: "exports", 173 | Data: (*Host).cmdExports, 174 | }) 175 | root.AddCommand(cmd.CommandDescriptor{ 176 | Name: "list", 177 | Brief: "List source code lines", 178 | Description: "List the source code corresponding to the machine code" + 179 | " at the specified address. A source map containing the address must" + 180 | " have been previously loaded.", 181 | Usage: "list
[]", 182 | Data: (*Host).cmdList, 183 | }) 184 | root.AddCommand(cmd.CommandDescriptor{ 185 | Name: "load", 186 | Brief: "Load a binary file", 187 | Description: "Load the contents of a binary file into the emulated" + 188 | " system's memory. If the file has an associated source map, it" + 189 | " will be loaded too. If the file contains raw binary data, you must" + 190 | " specify the address where the data will be loaded.", 191 | Usage: "load [
]", 192 | Data: (*Host).cmdLoad, 193 | }) 194 | 195 | // Memory commands 196 | me := root.AddSubtree(cmd.TreeDescriptor{Name: "memory", Brief: "Memory commands"}) 197 | me.AddCommand(cmd.CommandDescriptor{ 198 | Name: "dump", 199 | Brief: "Dump memory at address", 200 | Description: "Dump the contents of memory starting from the" + 201 | " specified address. The number of bytes to dump may be" + 202 | " specified as an option. If no address is specified, the" + 203 | " memory dump continues from where the last dump left off.", 204 | Usage: "memory dump [
] []", 205 | Data: (*Host).cmdMemoryDump, 206 | }) 207 | me.AddCommand(cmd.CommandDescriptor{ 208 | Name: "set", 209 | Brief: "Set memory at address", 210 | Description: "Set the contents of memory starting from the specified" + 211 | " address. The values to assign should be a series of" + 212 | " space-separated byte values. You may use an expression for each" + 213 | " byte value.", 214 | Usage: "memory set
[ ...]", 215 | Data: (*Host).cmdMemorySet, 216 | }) 217 | me.AddCommand(cmd.CommandDescriptor{ 218 | Name: "copy", 219 | Brief: "Copy memory", 220 | Description: "Copy memory from one range of addresses to another. You" + 221 | " must specify the destination address, the first byte of the source" + 222 | " address, and the last byte of the source address.", 223 | Usage: "memory copy ", 224 | Data: (*Host).cmdMemoryCopy, 225 | }) 226 | 227 | root.AddCommand(cmd.CommandDescriptor{ 228 | Name: "quit", 229 | Brief: "Quit the program", 230 | Description: "Quit the program.", 231 | Usage: "quit", 232 | Data: (*Host).cmdQuit, 233 | }) 234 | root.AddCommand(cmd.CommandDescriptor{ 235 | Name: "register", 236 | Brief: "View or change register values", 237 | Description: "When used without arguments, this command displays the current" + 238 | " contents of the CPU registers. When used with arguments, this" + 239 | " command changes the value of a register or one of the CPU's status" + 240 | " flags. Allowed register names include A, X, Y, PC and SP. Allowed status" + 241 | " flag names include N (Sign), Z (Zero), C (Carry), I (InterruptDisable)," + 242 | " D (Decimal) and V (Overflow).", 243 | Usage: "register [ ]", 244 | Data: (*Host).cmdRegister, 245 | }) 246 | root.AddCommand(cmd.CommandDescriptor{ 247 | Name: "run", 248 | Brief: "Run the CPU", 249 | Description: "Run the CPU until a breakpoint is hit or until the" + 250 | " user types Ctrl-C.", 251 | Usage: "run", 252 | Data: (*Host).cmdRun, 253 | }) 254 | root.AddCommand(cmd.CommandDescriptor{ 255 | Name: "set", 256 | Brief: "Set a configuration variable", 257 | Description: "Set the value of a configuration variable. To see the" + 258 | " current values of all configuration variables, type set" + 259 | " without any arguments.", 260 | Usage: "set [ ]", 261 | Data: (*Host).cmdSet, 262 | }) 263 | 264 | // Step commands 265 | st := root.AddSubtree(cmd.TreeDescriptor{Name: "step", Brief: "Step the debugger"}) 266 | st.AddCommand(cmd.CommandDescriptor{ 267 | Name: "in", 268 | Brief: "Step into next instruction", 269 | Description: "Step the CPU by a single instruction. If the" + 270 | " instruction is a subroutine call, step into the subroutine." + 271 | " The number of steps may be specified as an option.", 272 | Usage: "step in []", 273 | Data: (*Host).cmdStepIn, 274 | }) 275 | st.AddCommand(cmd.CommandDescriptor{ 276 | Name: "over", 277 | Brief: "Step over next instruction", 278 | Description: "Step the CPU by a single instruction. If the" + 279 | " instruction is a subroutine call, step over the subroutine." + 280 | " The number of steps may be specified as an option.", 281 | Usage: "step over []", 282 | Data: (*Host).cmdStepOver, 283 | }) 284 | st.AddCommand(cmd.CommandDescriptor{ 285 | Name: "out", 286 | Brief: "Step out of the current subroutine", 287 | Description: "Step the CPU until it executes an RTS or RTI" + 288 | " instruction. This has the effect of stepping until the " + 289 | " currently running subroutine has returned.", 290 | Usage: "step out", 291 | Data: (*Host).cmdStepOut, 292 | }) 293 | 294 | // Add command shortcuts. 295 | root.AddShortcut("a", "assemble file") 296 | root.AddShortcut("ai", "assemble interactive") 297 | root.AddShortcut("am", "assemble map") 298 | root.AddShortcut("b", "breakpoint") 299 | root.AddShortcut("bp", "breakpoint") 300 | root.AddShortcut("ba", "breakpoint add") 301 | root.AddShortcut("br", "breakpoint remove") 302 | root.AddShortcut("bl", "breakpoint list") 303 | root.AddShortcut("be", "breakpoint enable") 304 | root.AddShortcut("bd", "breakpoint disable") 305 | root.AddShortcut("d", "disassemble") 306 | root.AddShortcut("db", "databreakpoint") 307 | root.AddShortcut("dbp", "databreakpoint") 308 | root.AddShortcut("dbl", "databreakpoint list") 309 | root.AddShortcut("dba", "databreakpoint add") 310 | root.AddShortcut("dbr", "databreakpoint remove") 311 | root.AddShortcut("dbe", "databreakpoint enable") 312 | root.AddShortcut("dbd", "databreakpoint disable") 313 | root.AddShortcut("e", "evaluate") 314 | root.AddShortcut("l", "list") 315 | root.AddShortcut("m", "memory dump") 316 | root.AddShortcut("mc", "memory copy") 317 | root.AddShortcut("ms", "memory set") 318 | root.AddShortcut("r", "register") 319 | root.AddShortcut("s", "step over") 320 | root.AddShortcut("si", "step in") 321 | root.AddShortcut("so", "step out") 322 | root.AddShortcut("?", "help") 323 | root.AddShortcut(".", "register") 324 | 325 | cmds = root 326 | } 327 | -------------------------------------------------------------------------------- /host/expr.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Brett Vickers. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package host 6 | 7 | import ( 8 | "errors" 9 | "strconv" 10 | ) 11 | 12 | var errExprParse = errors.New("expression syntax error") 13 | 14 | type tokenType byte 15 | 16 | const ( 17 | tokenNil tokenType = iota 18 | tokenIdentifier 19 | tokenNumber 20 | tokenOp 21 | tokenLParen 22 | tokenRParen 23 | ) 24 | 25 | type token struct { 26 | Type tokenType 27 | Value any // nil, string, int64 or *op (depends on Type) 28 | } 29 | 30 | type opType byte 31 | 32 | const ( 33 | opNil opType = 0 + iota 34 | opMultiply 35 | opDivide 36 | opModulo 37 | opAdd 38 | opSubtract 39 | opShiftLeft 40 | opShiftRight 41 | opBitwiseAnd 42 | opBitwiseXor 43 | opBitwiseOr 44 | opBitwiseNot 45 | opUnaryMinus 46 | opUnaryPlus 47 | opUnaryBinary 48 | ) 49 | 50 | type associativity byte 51 | 52 | const ( 53 | left associativity = iota 54 | right 55 | ) 56 | 57 | type op struct { 58 | Symbol string 59 | Type opType 60 | Precedence byte 61 | Assoc associativity 62 | Args byte 63 | UnaryOp opType 64 | Eval func(a, b int64) int64 65 | } 66 | 67 | var ops = []op{ 68 | {"", opNil, 0, right, 2, opNil, nil}, 69 | {"*", opMultiply, 6, right, 2, opNil, func(a, b int64) int64 { return a * b }}, 70 | {"/", opDivide, 6, right, 2, opNil, func(a, b int64) int64 { return a / b }}, 71 | {"%", opModulo, 6, right, 2, opUnaryBinary, func(a, b int64) int64 { return a % b }}, 72 | {"+", opAdd, 5, right, 2, opUnaryPlus, func(a, b int64) int64 { return a + b }}, 73 | {"-", opSubtract, 5, right, 2, opUnaryMinus, func(a, b int64) int64 { return a - b }}, 74 | {"<<", opShiftLeft, 4, right, 2, opNil, func(a, b int64) int64 { return a << uint32(b) }}, 75 | {">>", opShiftRight, 4, right, 2, opNil, func(a, b int64) int64 { return a >> uint32(b) }}, 76 | {"&", opBitwiseAnd, 3, right, 2, opNil, func(a, b int64) int64 { return a & b }}, 77 | {"^", opBitwiseXor, 2, right, 2, opNil, func(a, b int64) int64 { return a ^ b }}, 78 | {"|", opBitwiseOr, 1, right, 2, opNil, func(a, b int64) int64 { return a | b }}, 79 | {"~", opBitwiseNot, 7, left, 1, opNil, func(a, b int64) int64 { return ^a }}, 80 | {"-", opUnaryMinus, 7, left, 1, opNil, func(a, b int64) int64 { return -a }}, 81 | {"+", opUnaryPlus, 7, left, 1, opNil, func(a, b int64) int64 { return a }}, 82 | {"%", opUnaryBinary, 7, left, 1, opNil, func(a, b int64) int64 { return fromBinary(a) }}, 83 | } 84 | 85 | // lexeme identifiers 86 | const ( 87 | lNil byte = iota 88 | lNum 89 | lCha 90 | lIde 91 | lLPa 92 | lRPa 93 | lMul 94 | lDiv 95 | lMod 96 | lAdd 97 | lSub 98 | lShl 99 | lShr 100 | lAnd 101 | lXor 102 | lOra 103 | lNot 104 | ) 105 | 106 | // A table mapping lexeme identifiers to token data and parsers. 107 | var lexeme = []struct { 108 | TokenType tokenType 109 | OpType opType 110 | Parse func(p *exprParser, t tstring) (tok token, remain tstring, err error) 111 | }{ 112 | /*lNil*/ {TokenType: tokenNil, OpType: opNil}, 113 | /*lNum*/ {TokenType: tokenNumber, OpType: opNil, Parse: (*exprParser).parseNumber}, 114 | /*lCha*/ {TokenType: tokenNumber, OpType: opNil, Parse: (*exprParser).parseChar}, 115 | /*lIde*/ {TokenType: tokenIdentifier, OpType: opNil, Parse: (*exprParser).parseIdentifier}, 116 | /*lLPa*/ {TokenType: tokenLParen, OpType: opNil}, 117 | /*lRPa*/ {TokenType: tokenRParen, OpType: opNil}, 118 | /*lMul*/ {TokenType: tokenOp, OpType: opMultiply}, 119 | /*lDiv*/ {TokenType: tokenOp, OpType: opDivide}, 120 | /*lMod*/ {TokenType: tokenOp, OpType: opModulo}, 121 | /*lAdd*/ {TokenType: tokenOp, OpType: opAdd}, 122 | /*lSub*/ {TokenType: tokenOp, OpType: opSubtract}, 123 | /*lShl*/ {TokenType: tokenOp, OpType: opNil, Parse: (*exprParser).parseShiftOp}, 124 | /*lShr*/ {TokenType: tokenOp, OpType: opNil, Parse: (*exprParser).parseShiftOp}, 125 | /*lAnd*/ {TokenType: tokenOp, OpType: opBitwiseAnd}, 126 | /*lXor*/ {TokenType: tokenOp, OpType: opBitwiseXor}, 127 | /*lOra*/ {TokenType: tokenOp, OpType: opBitwiseOr}, 128 | /*lNot*/ {TokenType: tokenOp, OpType: opBitwiseNot}, 129 | } 130 | 131 | // A table mapping the first char of a lexeme to a lexeme identifier. 132 | var lex0 = [96]byte{ 133 | lNil, lNil, lNil, lNil, lNum, lMod, lAnd, lCha, // 32..39 134 | lLPa, lRPa, lMul, lAdd, lNil, lSub, lIde, lDiv, // 40..47 135 | lNum, lNum, lNum, lNum, lNum, lNum, lNum, lNum, // 48..55 136 | lNum, lNum, lNil, lNil, lShl, lNil, lShr, lNil, // 56..63 137 | lNil, lIde, lIde, lIde, lIde, lIde, lIde, lIde, // 64..71 138 | lIde, lIde, lIde, lIde, lIde, lIde, lIde, lIde, // 72..79 139 | lIde, lIde, lIde, lIde, lIde, lIde, lIde, lIde, // 80..87 140 | lIde, lIde, lIde, lNil, lNil, lNil, lXor, lIde, // 88..95 141 | lNil, lIde, lIde, lIde, lIde, lIde, lIde, lIde, // 96..103 142 | lIde, lIde, lIde, lIde, lIde, lIde, lIde, lIde, // 104..111 143 | lIde, lIde, lIde, lIde, lIde, lIde, lIde, lIde, // 112..119 144 | lIde, lIde, lIde, lNil, lOra, lNil, lNot, lNil, // 120..127 145 | } 146 | 147 | type resolver interface { 148 | resolveIdentifier(s string) (int64, error) 149 | } 150 | 151 | // 152 | // exprParser 153 | // 154 | 155 | type exprParser struct { 156 | output tokenStack 157 | operatorStack tokenStack 158 | prevTokenType tokenType 159 | hexMode bool 160 | } 161 | 162 | func newExprParser() *exprParser { 163 | return &exprParser{} 164 | } 165 | 166 | func (p *exprParser) Reset() { 167 | p.output.reset() 168 | p.operatorStack.reset() 169 | p.prevTokenType = tokenNil 170 | } 171 | 172 | func (p *exprParser) Parse(expr string, r resolver) (int64, error) { 173 | defer p.Reset() 174 | 175 | t := tstring(expr) 176 | 177 | for { 178 | tok, remain, err := p.parseToken(t) 179 | if err != nil { 180 | return 0, err 181 | } 182 | if tok.Type == tokenNil { 183 | break 184 | } 185 | t = remain 186 | 187 | switch tok.Type { 188 | case tokenNumber: 189 | p.output.push(tok) 190 | 191 | case tokenIdentifier: 192 | v, err := r.resolveIdentifier(tok.Value.(string)) 193 | if err != nil { 194 | return 0, err 195 | } 196 | tok.Type, tok.Value = tokenNumber, v 197 | p.output.push(tok) 198 | 199 | case tokenLParen: 200 | p.operatorStack.push(tok) 201 | 202 | case tokenRParen: 203 | foundLParen := false 204 | for !p.operatorStack.isEmpty() { 205 | tmp := p.operatorStack.pop() 206 | if tmp.Type == tokenLParen { 207 | foundLParen = true 208 | break 209 | } 210 | p.output.push(tmp) 211 | } 212 | if !foundLParen { 213 | return 0, errExprParse 214 | } 215 | 216 | case tokenOp: 217 | if err := p.checkForUnaryOp(&tok); err != nil { 218 | return 0, err 219 | } 220 | for p.isCollapsible(&tok) { 221 | p.output.push(p.operatorStack.pop()) 222 | } 223 | p.operatorStack.push(tok) 224 | } 225 | 226 | p.prevTokenType = tok.Type 227 | } 228 | 229 | for !p.operatorStack.isEmpty() { 230 | tok := p.operatorStack.pop() 231 | if tok.Type == tokenLParen { 232 | return 0, errExprParse 233 | } 234 | p.output.push(tok) 235 | } 236 | 237 | result, err := p.evalOutput() 238 | if err != nil { 239 | return 0, err 240 | } 241 | if !p.output.isEmpty() { 242 | return 0, errExprParse 243 | } 244 | 245 | return result.Value.(int64), nil 246 | } 247 | 248 | func (p *exprParser) parseToken(t tstring) (tok token, remain tstring, err error) { 249 | t = t.consumeWhitespace() 250 | 251 | // Return the nil token when there are no more tokens to parse. 252 | if len(t) == 0 { 253 | return token{}, t, nil 254 | } 255 | 256 | // Use the first character of the token string to look up lexeme 257 | // parser data. 258 | if t[0] < 32 || t[0] > 127 { 259 | return token{}, t, errExprParse 260 | } 261 | lex := lexeme[lex0[t[0]-32]] 262 | 263 | // One-character lexemes require no additional parsing to generate the 264 | // token. 265 | if lex.Parse == nil { 266 | tok = token{lex.TokenType, nil} 267 | if lex.OpType != opNil { 268 | tok.Value = &ops[lex.OpType] 269 | } 270 | return tok, t.consume(1), nil 271 | } 272 | 273 | // Lexemes that are more than one character in length require custom 274 | // parsing to generate the token. 275 | return lex.Parse(p, t) 276 | } 277 | 278 | func (p *exprParser) parseNumber(t tstring) (tok token, remain tstring, err error) { 279 | base, fn, num := 10, decimal, t 280 | 281 | if p.hexMode { 282 | base, fn = 16, hexadecimal 283 | } 284 | 285 | switch num[0] { 286 | case '$': 287 | if len(num) < 2 { 288 | return token{}, t, errExprParse 289 | } 290 | base, fn, num = 16, hexadecimal, num.consume(1) 291 | 292 | case '0': 293 | if len(num) > 1 && (num[1] == 'x' || num[1] == 'b' || num[1] == 'd') { 294 | if len(num) < 3 { 295 | return token{}, t, errExprParse 296 | } 297 | switch num[1] { 298 | case 'x': 299 | base, fn = 16, hexadecimal 300 | case 'b': 301 | base, fn = 2, binary 302 | case 'd': 303 | base, fn = 10, decimal 304 | } 305 | num = num.consume(2) 306 | } 307 | } 308 | 309 | num, remain = num.consumeWhile(fn) 310 | if num == "" { 311 | return token{}, t, errExprParse 312 | } 313 | 314 | v, err := strconv.ParseInt(string(num), base, 64) 315 | if err != nil { 316 | return token{}, t, errExprParse 317 | } 318 | 319 | tok = token{tokenNumber, v} 320 | return tok, remain, nil 321 | } 322 | 323 | func (p *exprParser) parseChar(t tstring) (tok token, remain tstring, err error) { 324 | if len(t) < 3 || t[2] != '\'' { 325 | return tok, t, errExprParse 326 | } 327 | 328 | tok = token{tokenNumber, int64(t[1])} 329 | return tok, t.consume(3), nil 330 | } 331 | 332 | func (p *exprParser) parseIdentifier(t tstring) (tok token, remain tstring, err error) { 333 | if p.hexMode { 334 | return p.parseNumber(t) 335 | } 336 | 337 | var id tstring 338 | id, remain = t.consumeWhile(identifier) 339 | tok = token{tokenIdentifier, string(id)} 340 | return tok, remain, nil 341 | } 342 | 343 | func (p *exprParser) parseShiftOp(t tstring) (tok token, remain tstring, err error) { 344 | if len(t) < 2 || t[1] != t[0] { 345 | return token{}, t, errExprParse 346 | } 347 | 348 | var op *op 349 | switch t[0] { 350 | case '<': 351 | op = &ops[opShiftLeft] 352 | default: 353 | op = &ops[opShiftRight] 354 | } 355 | 356 | tok = token{tokenOp, op} 357 | return tok, t.consume(2), nil 358 | } 359 | 360 | func (p *exprParser) evalOutput() (token, error) { 361 | if p.output.isEmpty() { 362 | return token{}, errExprParse 363 | } 364 | 365 | tok := p.output.pop() 366 | if tok.Type == tokenNumber { 367 | return tok, nil 368 | } 369 | if tok.Type != tokenOp { 370 | return token{}, errExprParse 371 | } 372 | 373 | op := tok.Value.(*op) 374 | switch op.Args { 375 | case 1: 376 | child, err := p.evalOutput() 377 | if err != nil { 378 | return token{}, err 379 | } 380 | tok.Type = tokenNumber 381 | tok.Value = op.Eval(child.Value.(int64), 0) 382 | return tok, nil 383 | 384 | default: 385 | child2, err := p.evalOutput() 386 | if err != nil { 387 | return token{}, err 388 | } 389 | child1, err := p.evalOutput() 390 | if err != nil { 391 | return token{}, err 392 | } 393 | 394 | tok.Type = tokenNumber 395 | tok.Value = op.Eval(child1.Value.(int64), child2.Value.(int64)) 396 | return tok, nil 397 | } 398 | } 399 | 400 | func (p *exprParser) checkForUnaryOp(tok *token) error { 401 | o := tok.Value.(*op) 402 | if o.UnaryOp == opNil { 403 | return nil 404 | } 405 | 406 | // If this operation follows an operation, a left parenthesis, or nothing, 407 | // then convert it to a unary op. 408 | if p.prevTokenType == tokenOp || p.prevTokenType == tokenLParen || p.prevTokenType == tokenNil { 409 | tok.Value = &ops[o.UnaryOp] 410 | } 411 | return nil 412 | } 413 | 414 | func (p *exprParser) isCollapsible(opToken *token) bool { 415 | if p.operatorStack.isEmpty() { 416 | return false 417 | } 418 | 419 | top := p.operatorStack.peek() 420 | if top.Type != tokenOp { 421 | return false 422 | } 423 | 424 | currOp := opToken.Value.(*op) 425 | topOp := top.Value.(*op) 426 | if topOp.Precedence > currOp.Precedence { 427 | return true 428 | } 429 | if topOp.Precedence == currOp.Precedence && topOp.Assoc == left { 430 | return true 431 | } 432 | return false 433 | } 434 | 435 | // 436 | // tokenStack 437 | // 438 | 439 | type tokenStack struct { 440 | stack []token 441 | } 442 | 443 | func (s *tokenStack) reset() { 444 | s.stack = s.stack[:0] 445 | } 446 | 447 | func (s *tokenStack) isEmpty() bool { 448 | return len(s.stack) == 0 449 | } 450 | 451 | func (s *tokenStack) peek() *token { 452 | return &s.stack[len(s.stack)-1] 453 | } 454 | 455 | func (s *tokenStack) push(t token) { 456 | s.stack = append(s.stack, t) 457 | } 458 | 459 | func (s *tokenStack) pop() token { 460 | top := len(s.stack) - 1 461 | t := s.stack[top] 462 | s.stack = s.stack[:top] 463 | return t 464 | } 465 | 466 | // 467 | // helpers 468 | // 469 | 470 | func fromBinary(a int64) int64 { 471 | v, err := strconv.ParseInt(strconv.FormatInt(a, 10), 2, 64) 472 | if err != nil { 473 | return 0 474 | } 475 | return v 476 | } 477 | 478 | // 479 | // tstring 480 | // 481 | 482 | type tstring string 483 | 484 | func (t tstring) consume(n int) tstring { 485 | return t[n:] 486 | } 487 | 488 | func (t tstring) consumeWhitespace() tstring { 489 | return t.consume(t.scanWhile(whitespace)) 490 | } 491 | 492 | func (t tstring) scanWhile(fn func(c byte) bool) int { 493 | i := 0 494 | for ; i < len(t) && fn(t[i]); i++ { 495 | } 496 | return i 497 | } 498 | 499 | func (t tstring) consumeWhile(fn func(c byte) bool) (consumed, remain tstring) { 500 | i := t.scanWhile(fn) 501 | return t[:i], t[i:] 502 | } 503 | 504 | func whitespace(c byte) bool { 505 | return c == ' ' || c == '\t' 506 | } 507 | 508 | func decimal(c byte) bool { 509 | return (c >= '0' && c <= '9') 510 | } 511 | 512 | func hexadecimal(c byte) bool { 513 | return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f') 514 | } 515 | 516 | func binary(c byte) bool { 517 | return c == '0' || c == '1' 518 | } 519 | 520 | func identifier(c byte) bool { 521 | return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c == '.' 522 | } 523 | -------------------------------------------------------------------------------- /host/settings.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Brett Vickers. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package host 6 | 7 | import ( 8 | "errors" 9 | "fmt" 10 | "io" 11 | "reflect" 12 | "strings" 13 | 14 | "github.com/beevik/prefixtree/v2" 15 | ) 16 | 17 | type settings struct { 18 | HexMode bool `doc:"hexadecimal input mode"` 19 | CompactMode bool `doc:"compact disassembly output"` 20 | MemDumpBytes int `doc:"default number of memory bytes to dump"` 21 | DisasmLines int `doc:"default number of lines to disassemble"` 22 | SourceLines int `doc:"default number of source lines to display"` 23 | MaxStepLines int `doc:"max lines to disassemble when stepping"` 24 | NextDisasmAddr uint16 `doc:"address of next disassembly"` 25 | NextSourceAddr uint16 `doc:"address of next source line display"` 26 | NextMemDumpAddr uint16 `doc:"address of next memory dump"` 27 | } 28 | 29 | func newSettings() *settings { 30 | return &settings{ 31 | HexMode: false, 32 | CompactMode: false, 33 | MemDumpBytes: 64, 34 | DisasmLines: 10, 35 | SourceLines: 10, 36 | MaxStepLines: 20, 37 | NextDisasmAddr: 0, 38 | NextMemDumpAddr: 0, 39 | } 40 | } 41 | 42 | type settingsField struct { 43 | name string 44 | index int 45 | kind reflect.Kind 46 | typ reflect.Type 47 | doc string 48 | } 49 | 50 | var ( 51 | settingsTree = prefixtree.New[*settingsField]() 52 | settingsFields []settingsField 53 | ) 54 | 55 | func init() { 56 | settingsType := reflect.TypeOf(settings{}) 57 | settingsFields = make([]settingsField, settingsType.NumField()) 58 | for i := 0; i < len(settingsFields); i++ { 59 | f := settingsType.Field(i) 60 | doc, _ := f.Tag.Lookup("doc") 61 | settingsFields[i] = settingsField{ 62 | name: f.Name, 63 | index: i, 64 | kind: f.Type.Kind(), 65 | typ: f.Type, 66 | doc: doc, 67 | } 68 | settingsTree.Add(strings.ToLower(f.Name), &settingsFields[i]) 69 | } 70 | } 71 | 72 | func (s *settings) Display(w io.Writer) { 73 | value := reflect.ValueOf(s).Elem() 74 | for i, f := range settingsFields { 75 | v := value.Field(i) 76 | var s string 77 | switch f.kind { 78 | case reflect.String: 79 | s = fmt.Sprintf(" %-16s \"%s\"", f.name, v.String()) 80 | case reflect.Uint8: 81 | s = fmt.Sprintf(" %-16s $%02X", f.name, uint8(v.Uint())) 82 | case reflect.Uint16: 83 | s = fmt.Sprintf(" %-16s $%04X", f.name, uint16(v.Uint())) 84 | default: 85 | s = fmt.Sprintf(" %-16s %v", f.name, v) 86 | } 87 | fmt.Fprintf(w, "%-28s (%s)\n", s, f.doc) 88 | } 89 | } 90 | 91 | func (s *settings) Kind(key string) reflect.Kind { 92 | f, err := settingsTree.FindValue(strings.ToLower(key)) 93 | if err != nil { 94 | return reflect.Invalid 95 | } 96 | return f.kind 97 | } 98 | 99 | func (s *settings) Set(key string, value any) error { 100 | f, err := settingsTree.FindValue(strings.ToLower(key)) 101 | if err != nil { 102 | return err 103 | } 104 | 105 | vIn := reflect.ValueOf(value) 106 | if (f.kind == reflect.String && vIn.Type().Kind() != reflect.String) || 107 | (f.kind != reflect.String && vIn.Type().Kind() == reflect.String) || 108 | !vIn.Type().ConvertibleTo(f.typ) { 109 | return errors.New("invalid type") 110 | } 111 | vInConverted := vIn.Convert(f.typ) 112 | 113 | vOut := reflect.ValueOf(s).Elem().Field(f.index).Addr().Elem() 114 | vOut.Set(vInConverted) 115 | 116 | return nil 117 | } 118 | -------------------------------------------------------------------------------- /host/util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Brett Vickers. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package host 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | ) 11 | 12 | func codeString(b []byte) string { 13 | switch len(b) { 14 | case 1: 15 | return fmt.Sprintf("%02X", b[0]) 16 | case 2: 17 | return fmt.Sprintf("%02X %02X", b[0], b[1]) 18 | case 3: 19 | return fmt.Sprintf("%02X %02X %02X", b[0], b[1], b[2]) 20 | default: 21 | return "" 22 | } 23 | } 24 | 25 | func stringToBool(s string) (bool, error) { 26 | s = strings.ToLower(s) 27 | switch s { 28 | case "0", "false": 29 | return false, nil 30 | case "1", "true": 31 | return true, nil 32 | default: 33 | return false, fmt.Errorf("invalid bool value '%s'", s) 34 | } 35 | } 36 | 37 | var hexString = "0123456789ABCDEF" 38 | 39 | func addrToBuf(addr uint16, b []byte) { 40 | b[0] = hexString[(addr>>12)&0xf] 41 | b[1] = hexString[(addr>>8)&0xf] 42 | b[2] = hexString[(addr>>4)&0xf] 43 | b[3] = hexString[addr&0xf] 44 | } 45 | 46 | func byteToBuf(v byte, b []byte) { 47 | b[0] = hexString[(v>>4)&0xf] 48 | b[1] = hexString[v&0xf] 49 | } 50 | 51 | func toPrintableChar(v byte) byte { 52 | switch { 53 | case v >= 32 && v < 127: 54 | return v 55 | case v >= 160 && v < 255: 56 | return v - 128 57 | default: 58 | return '.' 59 | } 60 | } 61 | 62 | func min(a, b int) int { 63 | if a < b { 64 | return a 65 | } 66 | return b 67 | } 68 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Brett Vickers. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "flag" 9 | "fmt" 10 | "os" 11 | "os/signal" 12 | 13 | "github.com/beevik/go6502/asm" 14 | "github.com/beevik/go6502/host" 15 | ) 16 | 17 | var ( 18 | assemble string 19 | ) 20 | 21 | func init() { 22 | flag.StringVar(&assemble, "a", "", "assemble file") 23 | flag.CommandLine.Usage = func() { 24 | fmt.Println("Usage: go6502 [script] ..\nOptions:") 25 | flag.PrintDefaults() 26 | } 27 | } 28 | 29 | func main() { 30 | flag.Parse() 31 | 32 | // Initiate assembly from the command line if requested. 33 | if assemble != "" { 34 | err := asm.AssembleFile(assemble, 0, os.Stdout) 35 | if err != nil { 36 | fmt.Fprintf(os.Stderr, "Failed to assemble (%v).\n", err) 37 | } 38 | os.Exit(0) 39 | } 40 | 41 | // Create the host 42 | h := host.New() 43 | defer h.Cleanup() 44 | 45 | // Run commands contained in command-line files. 46 | args := flag.Args() 47 | if len(args) > 0 { 48 | for _, filename := range args { 49 | file, err := os.Open(filename) 50 | if err != nil { 51 | fmt.Fprintf(os.Stderr, "ERROR: %v\n", err) 52 | os.Exit(1) 53 | } 54 | ioState := h.EnableProcessedMode(file, os.Stdout) 55 | h.RunCommands(false) 56 | h.RestoreIoState(ioState) 57 | file.Close() 58 | } 59 | } 60 | 61 | // Break on Ctrl-C. 62 | c := make(chan os.Signal, 1) 63 | signal.Notify(c, os.Interrupt) 64 | go handleInterrupt(h, c) 65 | 66 | // Interactively run commands entered by the user. 67 | h.EnableRawMode() 68 | h.RunCommands(true) 69 | } 70 | 71 | func handleInterrupt(h *host.Host, c chan os.Signal) { 72 | for { 73 | <-c 74 | h.Break() 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /monitor.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beevik/go6502/7d0d2cd3094215d3a7deb7d1086b8b2ffdc651d9/monitor.bin -------------------------------------------------------------------------------- /monitor.map: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beevik/go6502/7d0d2cd3094215d3a7deb7d1086b8b2ffdc651d9/monitor.map -------------------------------------------------------------------------------- /sample.asm: -------------------------------------------------------------------------------- 1 | ; Sample program illustrating some of the features of the go6502 macro 2 | ; assembler. 3 | 4 | .ARCH 65c02 ; select CMOS 65c02 chip 5 | .ORG $1000 ; origin address for machine code 6 | 7 | ; ------- 8 | ; Exports 9 | ; ------- 10 | 11 | ; Exported labels are reported by the assembler with 12 | ; their assigned addresses. 13 | 14 | .EX START 15 | .EX DATA 16 | .EX END 17 | .EX LDA_TEST 18 | .EX LDX_TEST 19 | .EX LDY_TEST 20 | .EX JSR_TEST 21 | 22 | ; --------- 23 | ; Constants 24 | ; --------- 25 | ; A constant may be a literal (numeric or character). Or 26 | ; it may be an expression including literals, address labels, 27 | ; and other constants. Such constants may appear in 28 | ; expressions anywhere in the source code. 29 | 30 | STORE .EQ $0200 ; Constant defined with .EQ 31 | X EQU $EE ; Alternative: defined with EQU 32 | Y = $FE ; Alternative: defined with = 33 | 34 | 35 | ; ------- 36 | ; Program 37 | ; ------- 38 | 39 | START: ; Labels may end in ':', which is ignored. 40 | LDA #X 41 | LDA #Y 42 | LDA #128 43 | LDA #$7F 44 | LDA #%01011010 45 | JSR JSR_TEST 46 | JSR LDA_TEST 47 | JSR LDX_TEST 48 | JSR LDY_TEST 49 | BEQ @1 ; Branch to a local label ('@' or '.' prefix) 50 | LDY #';' ; Immediate character ASCII value 51 | LDX #DATA ; Lower byte of DATA 52 | LDX #DATA ; Upper byte of DATA 54 | @1 BRK ; @1 label is valid only within START scope. 55 | 56 | JSR_TEST LDA #$FF 57 | RTS 58 | 59 | LDA_TEST LDA #$20 ; Immediate 60 | LDA $20 ; Zero page 61 | LDA $20,X ; Zero page + X 62 | LDA ($20,X) ; Indirect + X 63 | LDA ($20),Y ; Indirect + Y 64 | LDA $0200 ; Absolute 65 | LDA ABS:$20 ; Absolute (forced) 66 | LDA $0200,X ; Absolute + X 67 | LDA $0200,Y ; Absolute + Y 68 | STA $0300 69 | RTS 70 | 71 | LDX_TEST LDX #$20 ; Immediate 72 | LDX $20 ; Zero page 73 | LDX $20,Y ; Zero page + Y 74 | LDX $0200 ; Absolute 75 | LDX ABS:$20 ; Absolute (forced) 76 | LDX $0200,Y ; Absolute + Y 77 | RTS 78 | 79 | LDY_TEST LDY #$20 ; Immediate 80 | LDY $20 ; Zero page 81 | LDY $20,X ; Zero page + X 82 | LDY $0200 ; Absolute 83 | LDY ABS:$20 ; Absolute (forced) 84 | LDY $0200,X ; Absolute + X 85 | RTS 86 | 87 | ; ---- 88 | ; Data 89 | ; ---- 90 | 91 | DATA: 92 | 93 | .ALIGN 16 ; align next addr on 16-byte boundary 94 | 95 | .BYTES ; .DB data can include literals (string, character and 96 | ; numeric) and math expressions using labels and constants. 97 | ; For numeric values, only the least significant byte is 98 | ; stored. 99 | 100 | .DB "AB,", $00 ; 41 42 2C 00 101 | .DB 'F', ',' ; 46 2C 102 | .DB 1 ; 01 103 | .DB $ABCD ; CD 104 | .DB $ABCD >> 8 ; AB 105 | .DB $0102 ; 02 106 | .DB 0x03040506 ; 06 107 | .DB 1+2+3+4, 5+6+7+8 ; 0A 1A 108 | .DB LDA_TEST, LDA_TEST>>8 ; addr of LDA_TEST 109 | .DB -1, -129 ; FF 7F 110 | .DB $12345678 ; 78 111 | .DB 0b01010101 ; 55 112 | .DB $ - .BYTES ; 12 113 | 114 | .ALIGN 2 115 | 116 | .WORDS ; .DW data works like .DB, except all numeric values are 117 | ; stored as 2-byte words. String literals are still stored 118 | ; with one byte per character. 119 | 120 | .DW "AB", $00 ; 41 42 00 00 121 | .DW 'F', 'F' ; 46 00 46 00 122 | .DW 1 ; 01 00 123 | .DW $ABCD ; CD AB 124 | .DW $ABCD >> 8 ; AB 00 125 | .DW $0102 ; 02 01 126 | .DW 0x03040506 ; 06 05 127 | .DW 1+2+3+4, 5+6+7+8 ; 0A 00 1A 00 128 | .DW LDA_TEST ; 2-byte addr of LDA_TEST 129 | .DW -1, -129 ; FF FF 7F FF 130 | .DW $12345678 ; 78 56 131 | .DW 0b11110101 ; F5 00 132 | .DW $ - .WORDS ; 20 00 133 | 134 | .ALIGN 4 135 | 136 | .DWORDS ; .DD data works like .DB and .DW, except all numeric values 137 | ; are stored as 4-byte double-words. String literals are still 138 | ; stored with one byte per character. 139 | 140 | .DD "AB", $00 ; 41 42 00 00 00 00 141 | .DD 'F', 'F' ; 46 00 00 00 46 00 00 00 142 | .DD 1 ; 01 00 00 00 143 | .DD $ABCD ; CD AB 00 00 144 | .DD $ABCD >> 8 ; AB 00 00 00 145 | .DD $0102 ; 02 01 00 00 146 | .DD 0x03040506 ; 06 05 04 03 147 | .DD 1+2+3+4, 5+6+7+8 ; 0A 00 00 00 1A 00 00 00 148 | .DD LDA_TEST ; 4-byte addr of LDA_TEST 149 | .DD -1, -129 ; FF FF FF FF 7F FF FF FF 150 | .DD $12345678 ; 78 56 34 12 151 | .DD 0b11110101 ; F5 00 00 00 152 | .DD $ - .DWORDS ; 3E 00 00 00 153 | 154 | .ALIGN 4 155 | 156 | .HEXSTRINGS ; .DH data is expressed as a chain of hexadecimal values, 157 | ; which are stored directly into the assembled data. 158 | 159 | .DH 414200 ; 41 42 00 160 | .DH 4646 ; 46 46 161 | .DH 01 ; 01 162 | .DH 12345678 ; 12 34 56 78 163 | .DH 0123456789abcdef ; 01 23 45 67 89 AB CD EF 164 | .DB $ - .HEXSTRINGS ; 12 165 | 166 | .ALIGN 4 167 | 168 | .TSTRINGS ; .DS data works the same way as .DB, except the last byte 169 | ; in a string literal has its most significant bit set. 170 | 171 | .DS "AAA" ; 41 41 C1 172 | .DS "A", 0 ; C1 00 173 | .DB $ - .TSTRINGS ; 05 174 | 175 | .ALIGN 4 176 | 177 | ; Pad the file to a length of 256 bytes. Use FF for padding. 178 | .PADDING .PAD $FF, 256-($-START) 179 | 180 | END 181 | -------------------------------------------------------------------------------- /sample.cmd: -------------------------------------------------------------------------------- 1 | load monitor.bin $F800 2 | assemble file sample.asm 3 | load sample.bin 4 | set compact true 5 | reg PC START 6 | d . 7 | -------------------------------------------------------------------------------- /term/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /term/PATENTS: -------------------------------------------------------------------------------- 1 | Additional IP Rights Grant (Patents) 2 | 3 | "This implementation" means the copyrightable works distributed by 4 | Google as part of the Go project. 5 | 6 | Google hereby grants to You a perpetual, worldwide, non-exclusive, 7 | no-charge, royalty-free, irrevocable (except as stated in this section) 8 | patent license to make, have made, use, offer to sell, sell, import, 9 | transfer and otherwise run, modify and propagate the contents of this 10 | implementation of Go, where such license applies only to those patent 11 | claims, both currently owned or controlled by Google and acquired in 12 | the future, licensable by Google that are necessarily infringed by this 13 | implementation of Go. This grant does not include claims that would be 14 | infringed only as a consequence of further modification of this 15 | implementation. If you or your agent or exclusive licensee institute or 16 | order or agree to the institution of patent litigation against any 17 | entity (including a cross-claim or counterclaim in a lawsuit) alleging 18 | that this implementation of Go or any code incorporated within this 19 | implementation of Go constitutes direct or contributory patent 20 | infringement, or inducement of patent infringement, then any patent 21 | rights granted to you under this License for this implementation of Go 22 | shall terminate as of the date such litigation is filed. 23 | -------------------------------------------------------------------------------- /term/README.md: -------------------------------------------------------------------------------- 1 | # Go terminal/console support 2 | 3 | [![Go Reference](https://pkg.go.dev/badge/golang.org/x/term.svg)](https://pkg.go.dev/golang.org/x/term) 4 | 5 | This repository provides Go terminal and console support packages. 6 | 7 | ## Download/Install 8 | 9 | The easiest way to install is to run `go get -u golang.org/x/term`. You can 10 | also manually git clone the repository to `$GOPATH/src/golang.org/x/term`. 11 | 12 | ## Report Issues / Send Patches 13 | 14 | This repository uses Gerrit for code changes. To learn how to submit changes to 15 | this repository, see https://golang.org/doc/contribute.html. 16 | 17 | The main issue tracker for the term repository is located at 18 | https://github.com/golang/go/issues. Prefix your issue with "x/term:" in the 19 | subject line, so it is easy to find. 20 | -------------------------------------------------------------------------------- /term/term.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package term provides support functions for dealing with terminals, as 6 | // commonly found on UNIX systems. 7 | // 8 | // Putting a terminal into raw mode is the most common requirement: 9 | // 10 | // oldState, err := term.MakeRaw(int(os.Stdin.Fd())) 11 | // if err != nil { 12 | // panic(err) 13 | // } 14 | // defer term.Restore(int(os.Stdin.Fd()), oldState) 15 | // 16 | // Note that on non-Unix systems os.Stdin.Fd() may not be 0. 17 | package term 18 | 19 | // State contains the state of a terminal. 20 | type State struct { 21 | state 22 | } 23 | 24 | // IsTerminal returns whether the given file descriptor is a terminal. 25 | func IsTerminal(fd int) bool { 26 | return isTerminal(fd) 27 | } 28 | 29 | // MakeRawInput puts the terminal connected to the given file descriptor into 30 | // raw input mode and returns the previous state of the terminal so that it 31 | // can be restored. 32 | func MakeRawInput(fd int) (*State, error) { 33 | return makeRawInput(fd) 34 | } 35 | 36 | // MakeRawOutput puts the terminal connected to the given file descriptor into 37 | // raw output mode and returns the previous state of the terminal so that it 38 | // can be restored. 39 | func MakeRawOutput(fd int) (*State, error) { 40 | return makeRawOutput(fd) 41 | } 42 | 43 | // GetState returns the current state of a terminal which may be useful to 44 | // restore the terminal after a signal. 45 | func GetState(fd int) (*State, error) { 46 | return getState(fd) 47 | } 48 | 49 | // PeekKey scans the input buffer for the presence of a "key-down" event 50 | // for the specified key character. Currently supported only on Windows. 51 | func PeekKey(fd int, key rune) bool { 52 | return peekKey(fd, key) 53 | } 54 | 55 | // Restore restores the terminal connected to the given file descriptor to a 56 | // previous state. 57 | func Restore(fd int, oldState *State) error { 58 | return restore(fd, oldState) 59 | } 60 | 61 | // GetSize returns the visible dimensions of the given terminal. 62 | // 63 | // These dimensions don't include any scrollback buffer height. 64 | func GetSize(fd int) (width, height int, err error) { 65 | return getSize(fd) 66 | } 67 | -------------------------------------------------------------------------------- /term/term_plan9.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package term 6 | 7 | import ( 8 | "fmt" 9 | "runtime" 10 | 11 | "golang.org/x/sys/plan9" 12 | ) 13 | 14 | type state struct{} 15 | 16 | func isTerminal(fd int) bool { 17 | path, err := plan9.Fd2path(fd) 18 | if err != nil { 19 | return false 20 | } 21 | return path == "/dev/cons" || path == "/mnt/term/dev/cons" 22 | } 23 | 24 | func makeRaw(fd int) (*State, error) { 25 | return nil, fmt.Errorf("terminal: MakeRaw not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) 26 | } 27 | 28 | func makeRawInput(fd int) (*State, error) { 29 | return makeRaw(fd) 30 | } 31 | 32 | func makeRawOutput(fd int) (*State, error) { 33 | return makeRaw(fd) 34 | } 35 | 36 | func peekKey(fd int, key rune) bool { 37 | return false 38 | } 39 | 40 | func getState(fd int) (*State, error) { 41 | return nil, fmt.Errorf("terminal: GetState not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) 42 | } 43 | 44 | func restore(fd int, state *State) error { 45 | return fmt.Errorf("terminal: Restore not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) 46 | } 47 | 48 | func getSize(fd int) (width, height int, err error) { 49 | return 0, 0, fmt.Errorf("terminal: GetSize not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) 50 | } 51 | -------------------------------------------------------------------------------- /term/term_unix.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos 6 | // +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos 7 | 8 | package term 9 | 10 | import ( 11 | "golang.org/x/sys/unix" 12 | ) 13 | 14 | type state struct { 15 | termios unix.Termios 16 | } 17 | 18 | func isTerminal(fd int) bool { 19 | _, err := unix.IoctlGetTermios(fd, ioctlReadTermios) 20 | return err == nil 21 | } 22 | 23 | func makeRaw(fd int) (*State, error) { 24 | termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios) 25 | if err != nil { 26 | return nil, err 27 | } 28 | 29 | oldState := State{state{termios: *termios}} 30 | 31 | // This attempts to replicate the behaviour documented for cfmakeraw in 32 | // the termios(3) manpage. 33 | termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON 34 | termios.Oflag &^= unix.OPOST 35 | termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.IEXTEN 36 | termios.Cflag &^= unix.CSIZE | unix.PARENB 37 | termios.Cflag |= unix.CS8 38 | termios.Cc[unix.VMIN] = 1 39 | termios.Cc[unix.VTIME] = 0 40 | if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, termios); err != nil { 41 | return nil, err 42 | } 43 | 44 | return &oldState, nil 45 | } 46 | 47 | func makeRawInput(fd int) (*State, error) { 48 | return makeRaw(fd) 49 | } 50 | 51 | func makeRawOutput(fd int) (*State, error) { 52 | return makeRaw(fd) 53 | } 54 | 55 | func peekKey(fd int, key rune) bool { 56 | return false 57 | } 58 | 59 | func getState(fd int) (*State, error) { 60 | termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios) 61 | if err != nil { 62 | return nil, err 63 | } 64 | 65 | return &State{state{termios: *termios}}, nil 66 | } 67 | 68 | func restore(fd int, state *State) error { 69 | return unix.IoctlSetTermios(fd, ioctlWriteTermios, &state.termios) 70 | } 71 | 72 | func getSize(fd int) (width, height int, err error) { 73 | ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ) 74 | if err != nil { 75 | return -1, -1, err 76 | } 77 | return int(ws.Col), int(ws.Row), nil 78 | } 79 | -------------------------------------------------------------------------------- /term/term_unix_bsd.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build darwin || dragonfly || freebsd || netbsd || openbsd 6 | // +build darwin dragonfly freebsd netbsd openbsd 7 | 8 | package term 9 | 10 | import "golang.org/x/sys/unix" 11 | 12 | const ioctlReadTermios = unix.TIOCGETA 13 | const ioctlWriteTermios = unix.TIOCSETA 14 | -------------------------------------------------------------------------------- /term/term_unix_other.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build aix || linux || solaris || zos 6 | // +build aix linux solaris zos 7 | 8 | package term 9 | 10 | import "golang.org/x/sys/unix" 11 | 12 | const ioctlReadTermios = unix.TCGETS 13 | const ioctlWriteTermios = unix.TCSETS 14 | -------------------------------------------------------------------------------- /term/term_unsupported.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !zos && !windows && !solaris && !plan9 6 | // +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!zos,!windows,!solaris,!plan9 7 | 8 | package term 9 | 10 | import ( 11 | "fmt" 12 | "runtime" 13 | ) 14 | 15 | type state struct{} 16 | 17 | func isTerminal(fd int) bool { 18 | return false 19 | } 20 | 21 | func makeRaw(fd int) (*State, error) { 22 | return nil, fmt.Errorf("terminal: MakeRaw not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) 23 | } 24 | 25 | func makeRawInput(fd int) (*State, error) { 26 | return makeRaw(fd) 27 | } 28 | 29 | func makeRawOutput(fd int) (*State, error) { 30 | return makeRaw(fd) 31 | } 32 | 33 | func peekKey(fd int, key rune) bool { 34 | return false 35 | } 36 | 37 | func getState(fd int) (*State, error) { 38 | return nil, fmt.Errorf("terminal: GetState not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) 39 | } 40 | 41 | func restore(fd int, state *State) error { 42 | return fmt.Errorf("terminal: Restore not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) 43 | } 44 | 45 | func getSize(fd int) (width, height int, err error) { 46 | return 0, 0, fmt.Errorf("terminal: GetSize not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) 47 | } 48 | -------------------------------------------------------------------------------- /term/term_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package term 6 | 7 | import ( 8 | "syscall" 9 | "unsafe" 10 | 11 | "golang.org/x/sys/windows" 12 | ) 13 | 14 | type state struct { 15 | mode uint32 16 | } 17 | 18 | type keyRecord struct { 19 | EventType uint32 20 | KeyDown uint32 21 | RepeatCount uint16 22 | VirtualKeyCode uint16 23 | VirtualScanCode uint16 24 | UnicodeChar uint16 25 | ControlKeyState uint32 26 | } 27 | 28 | var ( 29 | dll *windows.DLL 30 | procPeekConsoleInput *windows.Proc 31 | peekBuf []keyRecord 32 | ) 33 | 34 | func isTerminal(fd int) bool { 35 | var st uint32 36 | err := windows.GetConsoleMode(windows.Handle(fd), &st) 37 | return err == nil 38 | } 39 | 40 | func makeRawInput(fd int) (*State, error) { 41 | var mode uint32 42 | if err := windows.GetConsoleMode(windows.Handle(fd), &mode); err != nil { 43 | return nil, err 44 | } 45 | 46 | var enable uint32 = windows.ENABLE_VIRTUAL_TERMINAL_INPUT 47 | var disable uint32 = windows.ENABLE_PROCESSED_INPUT | 48 | windows.ENABLE_LINE_INPUT | 49 | windows.ENABLE_ECHO_INPUT | 50 | windows.ENABLE_MOUSE_INPUT | 51 | windows.ENABLE_WINDOW_INPUT 52 | newMode := (mode & ^disable) | enable 53 | 54 | if err := windows.SetConsoleMode(windows.Handle(fd), newMode); err != nil { 55 | return nil, err 56 | } 57 | 58 | return &State{state{mode}}, nil 59 | } 60 | 61 | func makeRawOutput(fd int) (*State, error) { 62 | var mode uint32 63 | if err := windows.GetConsoleMode(windows.Handle(fd), &mode); err != nil { 64 | return nil, err 65 | } 66 | 67 | var enable uint32 = windows.ENABLE_PROCESSED_OUTPUT | 68 | windows.ENABLE_WRAP_AT_EOL_OUTPUT | 69 | windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING 70 | var disable uint32 = windows.DISABLE_NEWLINE_AUTO_RETURN 71 | newMode := (mode & ^disable) | enable 72 | 73 | if err := windows.SetConsoleMode(windows.Handle(fd), newMode); err != nil { 74 | return nil, err 75 | } 76 | return &State{state{mode}}, nil 77 | } 78 | 79 | func peekKey(fd int, key rune) bool { 80 | // Lazy-allocate the peek buffer. 81 | if peekBuf == nil { 82 | // Lazy-load the kernel32 DLL. 83 | if dll == nil { 84 | dll = windows.MustLoadDLL("kernel32.dll") 85 | } 86 | procPeekConsoleInput = dll.MustFindProc("PeekConsoleInputW") 87 | peekBuf = make([]keyRecord, 16) 88 | } 89 | 90 | // Peek at the console input buffer. 91 | var n int 92 | for { 93 | _, _, err := syscall.SyscallN(procPeekConsoleInput.Addr(), 94 | uintptr(windows.Handle(fd)), 95 | uintptr(unsafe.Pointer(&peekBuf[0])), 96 | uintptr(len(peekBuf)), 97 | uintptr(unsafe.Pointer(&n))) 98 | 99 | if err != 0 { 100 | return false 101 | } 102 | if n < len(peekBuf) { 103 | break 104 | } 105 | 106 | // The record buffer wasn't large enough to hold all events, so grow 107 | // it and try again. 108 | peekBuf = make([]keyRecord, len(peekBuf)*2) 109 | } 110 | 111 | // Search the record buffer for a key-down event containing the requested 112 | // key. 113 | for _, r := range peekBuf[:n] { 114 | if r.EventType == uint32(1) && r.KeyDown == uint32(1) && rune(r.UnicodeChar) == key { 115 | return true 116 | } 117 | } 118 | 119 | return false 120 | } 121 | 122 | func getState(fd int) (*State, error) { 123 | var st uint32 124 | if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil { 125 | return nil, err 126 | } 127 | return &State{state{st}}, nil 128 | } 129 | 130 | func restore(fd int, state *State) error { 131 | return windows.SetConsoleMode(windows.Handle(fd), state.mode) 132 | } 133 | 134 | func getSize(fd int) (width, height int, err error) { 135 | var info windows.ConsoleScreenBufferInfo 136 | if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil { 137 | return 0, 0, err 138 | } 139 | return int(info.Window.Right - info.Window.Left + 1), int(info.Window.Bottom - info.Window.Top + 1), nil 140 | } 141 | -------------------------------------------------------------------------------- /term/terminal.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package term 6 | 7 | import ( 8 | "bytes" 9 | "io" 10 | "strconv" 11 | "sync" 12 | "unicode/utf8" 13 | ) 14 | 15 | // VT100 Escape codes 16 | var ( 17 | Black = string([]byte{keyEscape, '[', '3', '0', 'm'}) 18 | Red = string([]byte{keyEscape, '[', '3', '1', 'm'}) 19 | Green = string([]byte{keyEscape, '[', '3', '2', 'm'}) 20 | Yellow = string([]byte{keyEscape, '[', '3', '3', 'm'}) 21 | Blue = string([]byte{keyEscape, '[', '3', '4', 'm'}) 22 | Magenta = string([]byte{keyEscape, '[', '3', '5', 'm'}) 23 | Cyan = string([]byte{keyEscape, '[', '3', '6', 'm'}) 24 | White = string([]byte{keyEscape, '[', '3', '7', 'm'}) 25 | BrightBlack = string([]byte{keyEscape, '[', '9', '0', 'm'}) 26 | BrightRed = string([]byte{keyEscape, '[', '9', '1', 'm'}) 27 | BrightGreen = string([]byte{keyEscape, '[', '9', '2', 'm'}) 28 | BrightYellow = string([]byte{keyEscape, '[', '9', '3', 'm'}) 29 | BrightBlue = string([]byte{keyEscape, '[', '9', '4', 'm'}) 30 | BrightMagenta = string([]byte{keyEscape, '[', '9', '5', 'm'}) 31 | BrightCyan = string([]byte{keyEscape, '[', '9', '6', 'm'}) 32 | BrightWhite = string([]byte{keyEscape, '[', '9', '7', 'm'}) 33 | Reset = string([]byte{keyEscape, '[', '0', 'm'}) 34 | ) 35 | 36 | // Terminal contains the state for running a VT100 terminal that is capable of 37 | // reading lines of input. 38 | type Terminal struct { 39 | // AutoCompleteCallback, if non-null, is called for each keypress with 40 | // the full input line and the current position of the cursor (in 41 | // bytes, as an index into |line|). If it returns ok=false, the key 42 | // press is processed normally. Otherwise it returns a replacement line 43 | // and the new cursor position. 44 | AutoCompleteCallback func(line string, pos int, key rune) (newLine string, newPos int, ok bool) 45 | 46 | // HistoryTestCallback, if non-null, is called before a line is added to 47 | // the terminal's history buffer. If the line should be accepted into the 48 | // history, the callback should return true. 49 | HistoryTestCallback func(line string) (accept bool) 50 | 51 | // lock protects the terminal and the state in this object from 52 | // concurrent processing of a key press and a Write() call. 53 | lock sync.Mutex 54 | 55 | c io.ReadWriter 56 | prompt []rune 57 | 58 | // line is the current line being entered. 59 | line []rune 60 | // pos is the logical position of the cursor in line 61 | pos int 62 | // echo is true if local echo is enabled 63 | echo bool 64 | 65 | // cursorX contains the current X value of the cursor where the left 66 | // edge is 0. cursorY contains the row number where the first row of 67 | // the current line is 0. 68 | cursorX, cursorY int 69 | // maxLine is the greatest value of cursorY so far. 70 | maxLine int 71 | 72 | termWidth, termHeight int 73 | 74 | // outBuf contains the terminal data to be sent. 75 | outBuf []byte 76 | // remainder contains the remainder of any partial key sequences after 77 | // a read. It aliases into inBuf. 78 | remainder []byte 79 | inBuf [256]byte 80 | 81 | // history contains previously entered commands so that they can be 82 | // accessed with the up and down keys. 83 | history historyBuffer 84 | // historyIndex stores the currently accessed history entry, where zero 85 | // means the immediately previous entry. 86 | historyIndex int 87 | // When navigating up and down the history it's possible to return to 88 | // the incomplete, initial line. That value is stored in 89 | // historyPending. 90 | historyPending string 91 | } 92 | 93 | // NewTerminal runs a VT100 terminal on the given ReadWriter. If the ReadWriter is 94 | // a local terminal, that terminal must first have been put into raw mode. 95 | // prompt is a string that is written at the start of each input line (i.e. 96 | // "> "). 97 | func NewTerminal(c io.ReadWriter, prompt string) *Terminal { 98 | return &Terminal{ 99 | c: c, 100 | prompt: []rune(prompt), 101 | termWidth: 80, 102 | termHeight: 24, 103 | echo: true, 104 | historyIndex: -1, 105 | } 106 | } 107 | 108 | const ( 109 | keyCtrlC = 3 110 | keyCtrlD = 4 111 | keyCtrlU = 21 112 | keyEnter = '\r' 113 | keyEscape = 27 114 | keyBackspace = 127 115 | keyUnknown = 0xd800 /* UTF-16 surrogate area */ + iota 116 | keyUp 117 | keyDown 118 | keyLeft 119 | keyRight 120 | keyAltLeft 121 | keyAltRight 122 | keyHome 123 | keyEnd 124 | keyDeleteWord 125 | keyDeleteLine 126 | keyClearScreen 127 | ) 128 | 129 | var ( 130 | crlf = []byte{'\r', '\n'} 131 | ) 132 | 133 | // bytesToKey tries to parse a key sequence from b. If successful, it returns 134 | // the key and the remainder of the input. Otherwise it returns utf8.RuneError. 135 | func bytesToKey(b []byte) (rune, []byte) { 136 | if len(b) == 0 { 137 | return utf8.RuneError, nil 138 | } 139 | 140 | switch b[0] { 141 | case 1: // ^A 142 | return keyHome, b[1:] 143 | case 2: // ^B 144 | return keyLeft, b[1:] 145 | case 5: // ^E 146 | return keyEnd, b[1:] 147 | case 6: // ^F 148 | return keyRight, b[1:] 149 | case 8: // ^H 150 | return keyBackspace, b[1:] 151 | case 11: // ^K 152 | return keyDeleteLine, b[1:] 153 | case 12: // ^L 154 | return keyClearScreen, b[1:] 155 | case 23: // ^W 156 | return keyDeleteWord, b[1:] 157 | case 14: // ^N 158 | return keyDown, b[1:] 159 | case 16: // ^P 160 | return keyUp, b[1:] 161 | } 162 | 163 | if b[0] != keyEscape { 164 | if !utf8.FullRune(b) { 165 | return utf8.RuneError, b 166 | } 167 | r, l := utf8.DecodeRune(b) 168 | return r, b[l:] 169 | } 170 | 171 | if len(b) >= 3 && b[1] == '[' { 172 | switch b[2] { 173 | case 'A': 174 | return keyUp, b[3:] 175 | case 'B': 176 | return keyDown, b[3:] 177 | case 'C': 178 | return keyRight, b[3:] 179 | case 'D': 180 | return keyLeft, b[3:] 181 | case 'H': 182 | return keyHome, b[3:] 183 | case 'F': 184 | return keyEnd, b[3:] 185 | } 186 | 187 | if len(b) >= 4 && b[3] == '~' { 188 | switch b[2] { 189 | case '3': // DEL key 190 | return keyCtrlD, b[4:] 191 | } 192 | } 193 | 194 | if len(b) >= 6 && b[2] == '1' && b[3] == ';' && b[4] == '3' { 195 | switch b[5] { 196 | case 'C': 197 | return keyAltRight, b[6:] 198 | case 'D': 199 | return keyAltLeft, b[6:] 200 | } 201 | } 202 | } 203 | 204 | // If we get here then we have a key that we don't recognise, or a 205 | // partial sequence. It's not clear how one should find the end of a 206 | // sequence without knowing them all, but it seems that [a-zA-Z~] only 207 | // appears at the end of a sequence. 208 | for i, c := range b[0:] { 209 | if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '~' { 210 | return keyUnknown, b[i+1:] 211 | } 212 | } 213 | 214 | return utf8.RuneError, b 215 | } 216 | 217 | // queue appends data to the end of t.outBuf 218 | func (t *Terminal) queue(data []rune) { 219 | t.outBuf = append(t.outBuf, []byte(string(data))...) 220 | } 221 | 222 | var space = []rune{' '} 223 | 224 | func isPrintable(key rune) bool { 225 | isInSurrogateArea := key >= 0xd800 && key <= 0xdbff 226 | return key >= 32 && !isInSurrogateArea 227 | } 228 | 229 | // moveCursorToPos appends data to t.outBuf which will move the cursor to the 230 | // given, logical position in the text. 231 | func (t *Terminal) moveCursorToPos(pos int) { 232 | if !t.echo { 233 | return 234 | } 235 | 236 | x := visualLength(t.prompt) + pos 237 | y := x / t.termWidth 238 | x = x % t.termWidth 239 | 240 | up := 0 241 | if y < t.cursorY { 242 | up = t.cursorY - y 243 | } 244 | 245 | down := 0 246 | if y > t.cursorY { 247 | down = y - t.cursorY 248 | } 249 | 250 | left := 0 251 | if x < t.cursorX { 252 | left = t.cursorX - x 253 | } 254 | 255 | right := 0 256 | if x > t.cursorX { 257 | right = x - t.cursorX 258 | } 259 | 260 | t.cursorX = x 261 | t.cursorY = y 262 | t.move(up, down, left, right) 263 | } 264 | 265 | func (t *Terminal) move(up, down, left, right int) { 266 | m := []rune{} 267 | 268 | // 1 unit up can be expressed as ^[[A or ^[A 269 | // 5 units up can be expressed as ^[[5A 270 | 271 | if up == 1 { 272 | m = append(m, keyEscape, '[', 'A') 273 | } else if up > 1 { 274 | m = append(m, keyEscape, '[') 275 | m = append(m, []rune(strconv.Itoa(up))...) 276 | m = append(m, 'A') 277 | } 278 | 279 | if down == 1 { 280 | m = append(m, keyEscape, '[', 'B') 281 | } else if down > 1 { 282 | m = append(m, keyEscape, '[') 283 | m = append(m, []rune(strconv.Itoa(down))...) 284 | m = append(m, 'B') 285 | } 286 | 287 | if right == 1 { 288 | m = append(m, keyEscape, '[', 'C') 289 | } else if right > 1 { 290 | m = append(m, keyEscape, '[') 291 | m = append(m, []rune(strconv.Itoa(right))...) 292 | m = append(m, 'C') 293 | } 294 | 295 | if left == 1 { 296 | m = append(m, keyEscape, '[', 'D') 297 | } else if left > 1 { 298 | m = append(m, keyEscape, '[') 299 | m = append(m, []rune(strconv.Itoa(left))...) 300 | m = append(m, 'D') 301 | } 302 | 303 | t.queue(m) 304 | } 305 | 306 | func (t *Terminal) clearLineToRight() { 307 | op := []rune{keyEscape, '[', 'K'} 308 | t.queue(op) 309 | } 310 | 311 | const maxLineLength = 4096 312 | 313 | func (t *Terminal) setLine(newLine []rune, newPos int) { 314 | if t.echo { 315 | t.moveCursorToPos(0) 316 | t.writeLine(newLine) 317 | for i := len(newLine); i < len(t.line); i++ { 318 | t.writeLine(space) 319 | } 320 | t.moveCursorToPos(newPos) 321 | } 322 | t.line = newLine 323 | t.pos = newPos 324 | } 325 | 326 | func (t *Terminal) advanceCursor(places int) { 327 | t.cursorX += places 328 | t.cursorY += t.cursorX / t.termWidth 329 | if t.cursorY > t.maxLine { 330 | t.maxLine = t.cursorY 331 | } 332 | t.cursorX = t.cursorX % t.termWidth 333 | 334 | if places > 0 && t.cursorX == 0 { 335 | // Normally terminals will advance the current position 336 | // when writing a character. But that doesn't happen 337 | // for the last character in a line. However, when 338 | // writing a character (except a new line) that causes 339 | // a line wrap, the position will be advanced two 340 | // places. 341 | // 342 | // So, if we are stopping at the end of a line, we 343 | // need to write a newline so that our cursor can be 344 | // advanced to the next line. 345 | t.outBuf = append(t.outBuf, '\r', '\n') 346 | } 347 | } 348 | 349 | func (t *Terminal) eraseNPreviousChars(n int) { 350 | if n == 0 { 351 | return 352 | } 353 | 354 | if t.pos < n { 355 | n = t.pos 356 | } 357 | t.pos -= n 358 | t.moveCursorToPos(t.pos) 359 | 360 | copy(t.line[t.pos:], t.line[n+t.pos:]) 361 | t.line = t.line[:len(t.line)-n] 362 | if t.echo { 363 | t.writeLine(t.line[t.pos:]) 364 | for i := 0; i < n; i++ { 365 | t.queue(space) 366 | } 367 | t.advanceCursor(n) 368 | t.moveCursorToPos(t.pos) 369 | } 370 | } 371 | 372 | // countToLeftWord returns then number of characters from the cursor to the 373 | // start of the previous word. 374 | func (t *Terminal) countToLeftWord() int { 375 | if t.pos == 0 { 376 | return 0 377 | } 378 | 379 | pos := t.pos - 1 380 | for pos > 0 { 381 | if t.line[pos] != ' ' { 382 | break 383 | } 384 | pos-- 385 | } 386 | for pos > 0 { 387 | if t.line[pos] == ' ' { 388 | pos++ 389 | break 390 | } 391 | pos-- 392 | } 393 | 394 | return t.pos - pos 395 | } 396 | 397 | // countToRightWord returns then number of characters from the cursor to the 398 | // start of the next word. 399 | func (t *Terminal) countToRightWord() int { 400 | pos := t.pos 401 | for pos < len(t.line) { 402 | if t.line[pos] == ' ' { 403 | break 404 | } 405 | pos++ 406 | } 407 | for pos < len(t.line) { 408 | if t.line[pos] != ' ' { 409 | break 410 | } 411 | pos++ 412 | } 413 | return pos - t.pos 414 | } 415 | 416 | // visualLength returns the number of visible glyphs in s. 417 | func visualLength(runes []rune) int { 418 | inEscapeSeq := false 419 | length := 0 420 | 421 | for _, r := range runes { 422 | switch { 423 | case inEscapeSeq: 424 | if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') { 425 | inEscapeSeq = false 426 | } 427 | case r == '\x1b': 428 | inEscapeSeq = true 429 | default: 430 | length++ 431 | } 432 | } 433 | 434 | return length 435 | } 436 | 437 | // handleKey processes the given key and, optionally, returns a line of text 438 | // that the user has entered. 439 | func (t *Terminal) handleKey(key rune) (line string, ok bool) { 440 | switch key { 441 | case keyBackspace: 442 | if t.pos == 0 { 443 | return 444 | } 445 | t.eraseNPreviousChars(1) 446 | case keyAltLeft: 447 | // move left by a word. 448 | t.pos -= t.countToLeftWord() 449 | t.moveCursorToPos(t.pos) 450 | case keyAltRight: 451 | // move right by a word. 452 | t.pos += t.countToRightWord() 453 | t.moveCursorToPos(t.pos) 454 | case keyLeft: 455 | if t.pos == 0 { 456 | return 457 | } 458 | t.pos-- 459 | t.moveCursorToPos(t.pos) 460 | case keyRight: 461 | if t.pos == len(t.line) { 462 | return 463 | } 464 | t.pos++ 465 | t.moveCursorToPos(t.pos) 466 | case keyHome: 467 | if t.pos == 0 { 468 | return 469 | } 470 | t.pos = 0 471 | t.moveCursorToPos(t.pos) 472 | case keyEnd: 473 | if t.pos == len(t.line) { 474 | return 475 | } 476 | t.pos = len(t.line) 477 | t.moveCursorToPos(t.pos) 478 | case keyUp: 479 | entry, ok := t.history.NthPreviousEntry(t.historyIndex + 1) 480 | if !ok { 481 | return "", false 482 | } 483 | if t.historyIndex == -1 { 484 | t.historyPending = string(t.line) 485 | } 486 | t.historyIndex++ 487 | runes := []rune(entry) 488 | t.setLine(runes, len(runes)) 489 | case keyDown: 490 | switch t.historyIndex { 491 | case -1: 492 | return 493 | case 0: 494 | runes := []rune(t.historyPending) 495 | t.setLine(runes, len(runes)) 496 | t.historyIndex-- 497 | default: 498 | entry, ok := t.history.NthPreviousEntry(t.historyIndex - 1) 499 | if ok { 500 | t.historyIndex-- 501 | runes := []rune(entry) 502 | t.setLine(runes, len(runes)) 503 | } 504 | } 505 | case keyEnter: 506 | t.moveCursorToPos(len(t.line)) 507 | t.queue([]rune("\r\n")) 508 | line = string(t.line) 509 | ok = true 510 | t.line = t.line[:0] 511 | t.pos = 0 512 | t.cursorX = 0 513 | t.cursorY = 0 514 | t.maxLine = 0 515 | case keyDeleteWord: 516 | // Delete zero or more spaces and then one or more characters. 517 | t.eraseNPreviousChars(t.countToLeftWord()) 518 | case keyDeleteLine: 519 | // Delete everything from the current cursor position to the 520 | // end of line. 521 | for i := t.pos; i < len(t.line); i++ { 522 | t.queue(space) 523 | t.advanceCursor(1) 524 | } 525 | t.line = t.line[:t.pos] 526 | t.moveCursorToPos(t.pos) 527 | case keyCtrlD: 528 | // Erase the character under the current position. 529 | // The EOF case when the line is empty is handled in 530 | // readLine(). 531 | if t.pos < len(t.line) { 532 | t.pos++ 533 | t.eraseNPreviousChars(1) 534 | } 535 | case keyCtrlU: 536 | t.eraseNPreviousChars(t.pos) 537 | case keyClearScreen: 538 | // Erases the screen and moves the cursor to the home position. 539 | t.queue([]rune("\x1b[2J\x1b[H")) 540 | t.queue(t.prompt) 541 | t.cursorX, t.cursorY = 0, 0 542 | t.advanceCursor(visualLength(t.prompt)) 543 | t.setLine(t.line, t.pos) 544 | default: 545 | if t.AutoCompleteCallback != nil { 546 | prefix := string(t.line[:t.pos]) 547 | suffix := string(t.line[t.pos:]) 548 | 549 | t.lock.Unlock() 550 | newLine, newPos, completeOk := t.AutoCompleteCallback(prefix+suffix, len(prefix), key) 551 | t.lock.Lock() 552 | 553 | if completeOk { 554 | t.setLine([]rune(newLine), utf8.RuneCount([]byte(newLine)[:newPos])) 555 | return 556 | } 557 | } 558 | if !isPrintable(key) { 559 | return 560 | } 561 | if len(t.line) == maxLineLength { 562 | return 563 | } 564 | t.addKeyToLine(key) 565 | } 566 | return 567 | } 568 | 569 | // addKeyToLine inserts the given key at the current position in the current 570 | // line. 571 | func (t *Terminal) addKeyToLine(key rune) { 572 | if len(t.line) == cap(t.line) { 573 | newLine := make([]rune, len(t.line), 2*(1+len(t.line))) 574 | copy(newLine, t.line) 575 | t.line = newLine 576 | } 577 | t.line = t.line[:len(t.line)+1] 578 | copy(t.line[t.pos+1:], t.line[t.pos:]) 579 | t.line[t.pos] = key 580 | if t.echo { 581 | t.writeLine(t.line[t.pos:]) 582 | } 583 | t.pos++ 584 | t.moveCursorToPos(t.pos) 585 | } 586 | 587 | func (t *Terminal) writeLine(line []rune) { 588 | for len(line) != 0 { 589 | remainingOnLine := t.termWidth - t.cursorX 590 | todo := len(line) 591 | if todo > remainingOnLine { 592 | todo = remainingOnLine 593 | } 594 | t.queue(line[:todo]) 595 | t.advanceCursor(visualLength(line[:todo])) 596 | line = line[todo:] 597 | } 598 | } 599 | 600 | // writeWithCRLF writes buf to w but replaces all occurrences of \n with \r\n. 601 | func writeWithCRLF(w io.Writer, buf []byte) (n int, err error) { 602 | for len(buf) > 0 { 603 | i := bytes.IndexByte(buf, '\n') 604 | todo := len(buf) 605 | if i >= 0 { 606 | todo = i 607 | } 608 | 609 | var nn int 610 | nn, err = w.Write(buf[:todo]) 611 | n += nn 612 | if err != nil { 613 | return n, err 614 | } 615 | buf = buf[todo:] 616 | 617 | if i >= 0 { 618 | if _, err = w.Write(crlf); err != nil { 619 | return n, err 620 | } 621 | n++ 622 | buf = buf[1:] 623 | } 624 | } 625 | 626 | return n, nil 627 | } 628 | 629 | func (t *Terminal) Write(buf []byte) (n int, err error) { 630 | t.lock.Lock() 631 | defer t.lock.Unlock() 632 | 633 | if t.cursorX == 0 && t.cursorY == 0 { 634 | // This is the easy case: there's nothing on the screen that we 635 | // have to move out of the way. 636 | return writeWithCRLF(t.c, buf) 637 | } 638 | 639 | // We have a prompt and possibly user input on the screen. We 640 | // have to clear it first. 641 | t.move(0 /* up */, 0 /* down */, t.cursorX /* left */, 0 /* right */) 642 | t.cursorX = 0 643 | t.clearLineToRight() 644 | 645 | for t.cursorY > 0 { 646 | t.move(1 /* up */, 0, 0, 0) 647 | t.cursorY-- 648 | t.clearLineToRight() 649 | } 650 | 651 | if _, err = t.c.Write(t.outBuf); err != nil { 652 | return 653 | } 654 | t.outBuf = t.outBuf[:0] 655 | 656 | if n, err = writeWithCRLF(t.c, buf); err != nil { 657 | return 658 | } 659 | 660 | t.writeLine(t.prompt) 661 | if t.echo { 662 | t.writeLine(t.line) 663 | } 664 | 665 | t.moveCursorToPos(t.pos) 666 | 667 | if _, err = t.c.Write(t.outBuf); err != nil { 668 | return 669 | } 670 | t.outBuf = t.outBuf[:0] 671 | return 672 | } 673 | 674 | // ReadLine returns a line of input from the terminal. 675 | func (t *Terminal) ReadLine() (line string, err error) { 676 | t.lock.Lock() 677 | defer t.lock.Unlock() 678 | 679 | if t.cursorX == 0 && t.cursorY == 0 { 680 | t.writeLine(t.prompt) 681 | t.c.Write(t.outBuf) 682 | t.outBuf = t.outBuf[:0] 683 | } 684 | 685 | for { 686 | rest := t.remainder 687 | lineOk := false 688 | for !lineOk { 689 | var key rune 690 | key, rest = bytesToKey(rest) 691 | if key == utf8.RuneError { 692 | break 693 | } 694 | if key == keyCtrlC { 695 | t.remainder = nil 696 | return "", io.EOF 697 | } 698 | line, lineOk = t.handleKey(key) 699 | } 700 | if len(rest) > 0 { 701 | n := copy(t.inBuf[:], rest) 702 | t.remainder = t.inBuf[:n] 703 | } else { 704 | t.remainder = nil 705 | } 706 | t.c.Write(t.outBuf) 707 | t.outBuf = t.outBuf[:0] 708 | if lineOk { 709 | if t.echo { 710 | t.historyIndex = -1 711 | if t.HistoryTestCallback == nil || t.HistoryTestCallback(line) { 712 | t.history.Add(line) 713 | } 714 | } 715 | return 716 | } 717 | 718 | // t.remainder is a slice at the beginning of t.inBuf 719 | // containing a partial key sequence 720 | readBuf := t.inBuf[len(t.remainder):] 721 | var n int 722 | 723 | t.lock.Unlock() 724 | n, err = t.c.Read(readBuf) 725 | t.lock.Lock() 726 | 727 | if err != nil { 728 | return 729 | } 730 | 731 | t.remainder = t.inBuf[:n+len(t.remainder)] 732 | } 733 | } 734 | 735 | // SetPrompt sets the prompt to be used when reading subsequent lines. 736 | func (t *Terminal) SetPrompt(prompt string) { 737 | t.lock.Lock() 738 | defer t.lock.Unlock() 739 | 740 | t.prompt = []rune(prompt) 741 | } 742 | 743 | func (t *Terminal) clearAndRepaintLinePlusNPrevious(numPrevLines int) { 744 | // Move cursor to column zero at the start of the line. 745 | t.move(t.cursorY, 0, t.cursorX, 0) 746 | t.cursorX, t.cursorY = 0, 0 747 | t.clearLineToRight() 748 | for t.cursorY < numPrevLines { 749 | // Move down a line 750 | t.move(0, 1, 0, 0) 751 | t.cursorY++ 752 | t.clearLineToRight() 753 | } 754 | // Move back to beginning. 755 | t.move(t.cursorY, 0, 0, 0) 756 | t.cursorX, t.cursorY = 0, 0 757 | 758 | t.queue(t.prompt) 759 | t.advanceCursor(visualLength(t.prompt)) 760 | t.writeLine(t.line) 761 | t.moveCursorToPos(t.pos) 762 | } 763 | 764 | func (t *Terminal) SetSize(width, height int) error { 765 | t.lock.Lock() 766 | defer t.lock.Unlock() 767 | 768 | if width == 0 { 769 | width = 1 770 | } 771 | 772 | oldWidth := t.termWidth 773 | t.termWidth, t.termHeight = width, height 774 | 775 | switch { 776 | case width == oldWidth: 777 | // If the width didn't change then nothing else needs to be 778 | // done. 779 | return nil 780 | case len(t.line) == 0 && t.cursorX == 0 && t.cursorY == 0: 781 | // If there is nothing on current line and no prompt printed, 782 | // just do nothing 783 | return nil 784 | case width < oldWidth: 785 | // Some terminals (e.g. xterm) will truncate lines that were 786 | // too long when shinking. Others, (e.g. gnome-terminal) will 787 | // attempt to wrap them. For the former, repainting t.maxLine 788 | // works great, but that behaviour goes badly wrong in the case 789 | // of the latter because they have doubled every full line. 790 | 791 | // We assume that we are working on a terminal that wraps lines 792 | // and adjust the cursor position based on every previous line 793 | // wrapping and turning into two. This causes the prompt on 794 | // xterms to move upwards, which isn't great, but it avoids a 795 | // huge mess with gnome-terminal. 796 | if t.cursorX >= t.termWidth { 797 | t.cursorX = t.termWidth - 1 798 | } 799 | t.cursorY *= 2 800 | t.clearAndRepaintLinePlusNPrevious(t.maxLine * 2) 801 | case width > oldWidth: 802 | // If the terminal expands then our position calculations will 803 | // be wrong in the future because we think the cursor is 804 | // |t.pos| chars into the string, but there will be a gap at 805 | // the end of any wrapped line. 806 | // 807 | // But the position will actually be correct until we move, so 808 | // we can move back to the beginning and repaint everything. 809 | t.clearAndRepaintLinePlusNPrevious(t.maxLine) 810 | } 811 | 812 | _, err := t.c.Write(t.outBuf) 813 | t.outBuf = t.outBuf[:0] 814 | return err 815 | } 816 | 817 | // historyBuffer is a ring buffer of strings. 818 | type historyBuffer struct { 819 | // entries contains max elements. 820 | entries []string 821 | max int 822 | // head contains the index of the element most recently added to the ring. 823 | head int 824 | // size contains the number of elements in the ring. 825 | size int 826 | } 827 | 828 | func (b *historyBuffer) Add(value string) { 829 | if b.entries == nil { 830 | const defaultNumEntries = 128 831 | b.entries = make([]string, defaultNumEntries) 832 | b.max = defaultNumEntries 833 | } 834 | 835 | b.head = (b.head + 1) % b.max 836 | b.entries[b.head] = value 837 | if b.size < b.max { 838 | b.size++ 839 | } 840 | } 841 | 842 | // NthPreviousEntry returns the value passed to the nth previous call to Add. 843 | // If n is zero then the immediately prior value is returned, if one, then the 844 | // next most recent, and so on. If such an element doesn't exist then ok is 845 | // false. 846 | func (b *historyBuffer) NthPreviousEntry(n int) (value string, ok bool) { 847 | if n < 0 || n >= b.size { 848 | return "", false 849 | } 850 | index := b.head - n 851 | if index < 0 { 852 | index += b.max 853 | } 854 | return b.entries[index], true 855 | } 856 | --------------------------------------------------------------------------------