For my first 6502 project I wanted to hook up a microcontroller to the RAM and ROM so that it could monitor all data and relay it back to the PC for debugging purposes. Then I got the idea to leave the RAM and ROM out altogether and store all data on the PC with the microcontroller relaying data back and forth.
Attachment:
IMAG0743.jpg [ 358.05 KiB | Viewed 1769 times ]
This board is the result. The MSP430 I am used to using does not have enough GPIOs so I have two IO expanders on there as well.
The software communicates with the microcontroller and also shows the contents of the memory held on the PC. Every cycle the window jumps to the address the 6502 has requested so that you can see what opcode or data is being fetched. Sections of memory can be color coded. Here you can see four sequential views of the memory window as it is executing:
Attachment:
simulator_view.png [ 120.96 KiB | Viewed 1769 times ]
The red Xs are uninitialized memory and the software throws an error any time it is accessed. This has saved me many times already! As a beginner I find it is easy to write LDA $FF instead of LDA #$FF and it is nice that the program catches your mistake. It is also much easier to debug when you can see exactly what the chip is doing every cycle. The white column on the left of the memory window is where you can set breakpoints. It is also possible to mark memory there as read-only or data/code so that the program can break on access types that should not be allowed.
To get the 6502 binary into memory the program parses a plaintext program listing generated by CA65. It would also be possible to load a linked binary but then I would lose the label names and original source you see in the rightmost column of the memory window. This unfortunately means that code is organized with .org statements since the listing itself is generated by the assembler/compiler before the linker is ever invoked. The only way I know around this is to produce a binary with the linker, then reconstruct the labels and original source from the various debug files CA65 is able to produce.
Attachment:
6502_memman.jpg [ 86.76 KiB | Viewed 1769 times ]
The reason I decided to call it a trainer is because I wanted to use it to learn assembly but also because it would help me experiment with peripherals. Blinking an LED is not so hard for a beginner to the 6502 like me but more complicated things like reading key matrixes or driving displays are out of my reach. Since all of the memory is virtual it makes sense to have virtual memory mapped peripherals too. The software has a place to drag and drop different kinds of peripherals, which can then be mapped to anywhere in the memory.
Attachment:
Trainer.png [ 20.88 KiB | Viewed 1769 times ]
If I want a few dozen LEDs it is easier to put them there than to breaboard them all and try to get the memory mapping for them working correctly. It's also possible to configure some of the peripherals in ways that would be hard to duplicate on a breadboard:
Attachment:
LED example.png [ 7.17 KiB | Viewed 1769 times ]
One problem with this approach is that USB is meant for a relatively small number of large chunks of data instead of the large number of small chunks a program like this needs. As a result, the UART to USB converter I am using is not serviced very often by the OS of the PC and execution maxes out at about 90 cycles per second. One way to speed it up is to send not only the opcode requested but also the next few bytes of data as well, since opcodes like CLC, DEX, and TAY don't need to access any other parts of the memory. The microcontroller can pass opcodes like these on to the 6502 without requesting additional data from the PC. This raises execution to about 150 cycles per second, still not fast enough to do anything interesting. The next step was to add an SPI 128k SRAM you can see on the left side of the board. It holds the entire 64k the 6502 can address plus 64k describing the status of every byte in memory (breakpoint, read-only, uninitialized, etc.) Every 100ms the microcontroller and PC sync their memories so that peripherals reflect the memory they are mapped to without much delay and input from the user can be processed at a usable rate. It may seem counter-intuitive to route all memory access over SPI through the microcontroller and IO expanders but it actually takes up much less room than wiring the 6502 to a traditional parallel SRAM since that SRAM would also need to be wired to the IO expanders in order for the microcontroller to monitor what is going on (it also wouldn't be quicker). With this system I can get about 14,000 cycles per second (0.01MHz!), which is more than enough for any kind of program I would want to do while trying to learn assembly.