After completing my VGA Generator project a while back, I’ve embarked on a new electronics project: building a simple 6502-based homebrew 8-bit computer on a breadboard. There are a bunch of similar projects online from which to draw ideas. Some projects set constraints such as only using contemporary parts of the 8-bit era, no FPGAs, no microcontrollers etc. In my case, I opted instead to keep the constraints minimal and the project simple.
Concretely, I settled for the following:
- The CPU is a physical 6502 (i.e. not a soft core).
- VGA-compatible video output.
- Some easy means to get code onto the machine.
- All parts are still in production; no hard-to-source vintage components.
Video output will need to wait for another day, but the basics of running code on a 6502 are in place. The system is built around a W65C02S CPU and a Digilent Cmod A7-15T FPGA board for glue logic (and eventually VGA output). The FPGA board features a Xilinx Artix-7 FPGA and 512KB of SRAM, of which 64KB are used as system memory. (The FPGA is powerful enough to hold several soft core 6502 implementations, so this is certainly not the most minimal design choice.)
Instead of using ROM, the FPGA board initializes the memory when the system boots up. That is accomplished by loading a memory image from an SD card into RAM. Instead of having the FPGA directly interface with an SD card over SPI, I decided to support a proper file system layer that can be easily accessed by a desktop computer (i.e. FAT). That way, programs can be written as files to the SD card without the hassle to write to the card as a block device. Currently this mechanism is only used for the boot image, but the idea is to extend it to a mass storage system for other files as well.
Since implementing FAT on the FPGA would be challenging and since the 6502 needs a memory image before it can do useful work, I decided to create a small storage controller to handle accessing a FAT file system on the SD card. The controller is implemented on an Arduino Pro Mini clone. The Arduino’s SPI pins are connected to an SD card reader. The SD card is accessed using the Arduino SD library (the Arduino acting as SPI master). The data is then transferred to the FPGA using a second SPI connection (the Arduino acting as SPI slave). The second connection is driven by bitbanging the SPI protocol onto four of the Arduino’s GPIO pins.
At startup, the FPGA sends a read command to the storage controller. Since there isn’t a whole lot of RAM available to the controller, it transfers data in batches. First, it reads a batch of data from the SD card. After that, it will relay the data bit by bit to the FPGA. Then the process starts over by reading the next batch. For flow control, the FPGA waits for a start bit on the SPI connection after receiving each batch, which the controller sends after it finished reading a batch off the SD card. After system memory is initialized, the 6502 CPU is reset and bus control is passed to it.
Presently, the only means for the system to relay data to the outside world is an array of 8 LEDs, which an address decoder makes appear as memory-mapped I/O to the system at address
0xf001. In the picture further below, these are the 8 yellow LEDs. (There are various other LEDs wired to status signals.) My first proof-of-concept is a straightforward program that sets the LEDs to a specific pattern. Here is the assembly for the memory image used:
At boot, the memory image is loaded into RAM starting at address
0xf000. The image contains
0x55 at address
0xf001, which results in setting the LEDs to an on-off pattern while the rest of the image loads:
○●○●○●○●. It takes about 7 seconds to transfer the remainder of the image, which is in total shy of 4KB in size (consisting of mostly zeros for padding in between 3 chunks of data/code). Once the CPU takes control, it reads the reset vector at addresses
0xfffd and starts execution at the specified location, i.e.
0xf100. The code at that location writes
0xcc to the LED I/O port, causing the pattern to change:
To make the pattern less static and more interesting, a few more lines of code will create a moving light:
What’s next? Getting proper video output to be able to show something more interesting than LEDs turning on and off is on the top of the list. To accomplish that, a VGA signal will be generated based on a region of memory designated as framebuffer. This will also require working out the necessary bus arbitration since only one device can access the memory at any given time. The basics are already in place, but working out the timing between VGA and CPU will require some twiddling.