The 6502 is setup on a breadboard and I have confirmed that it is working as expected. For now the data 0xEA is hard wired on the data bus. The goat today is to provide the CPU with dynamic data based on the address it requests, so that it can execute a meaningful program. Traditionally, this is the role of a memory chip. For now, however, I am experimenting with Arduino.

Dynamic data

In addition to the 4 address lines used so far, I add the 8 data bus lines. I also connect the read/write signal to detect when the CPU is attempting to read from the bus.

Before going any further, I rework the program to display the new data, and I also make the Arduino generate the clock signal. This makes it easier to coordinate bus operations with the clock and allows me to freely adjust the clock speed. The 555-based module I was using, although adjustable, cannot go as low as I want.

I now write code that places 0xEA on the bus whenever the CPU is performing a read operation.

The Arduino connected to the address bus and the data bus of the 6502

The Arduino writes 0xEA to the bus whenever it detects that the 6502 is reading

Now that we can provide dynamic data to the CPU, we can try to make it execute a small program. Very small, in fact, because since I am only working with 4 address bits, I can provide only 16 bytes in total, including the two bytes of the reset vector located at addresses 0xC and 0xD. Technically, I could have added one or two extra wires, which would have doubled or quadrupled the available space, respectively, but I thought it would be nice challenge.

To make things interesting, I choose instructions that generate both memory reads and writes. The LDA (Load Accumulator) instruction loads into the accumulator the value stored at the specified address. ADC (Add with Carry) adds to the accumulator the value read from the specified address. STA (Store Accumulator) writes the contents of the accumulator back to memory. Each of these instructions takes a two-byte memory address as a parameter, with the low byte first. With just three instructions we are already at 9 bytes. Immediately after I place the two data bytes that will be added by the CPU. That leaves only a single free byte before the reset vector.

Here is the final program (data shown in hexadecimal):

Addr Instruction Data
0x0 LDA $0009 AD 09 00
0x3 ADC $000A 6D 0A 00
0x6 STA $000B 8D 0B 00
0x9   28
0xA   02
0xB    
0xC   00
0xD   00
0xE    
0xF    

Explanation:

  1. LDA $0009: Load the value at address 0x09 into the accumulator.
  2. ADC $000A: Add the value at address 0x0A to the value currently in the accumulator.
  3. STA $000B: Store the value currently in the accumulator at address 0x0B.

We should therefore observe the following events on the bus:

Addr R/W Data Event
0xC R 00 Reading the reset vector
0xD R 00
0x0 R AD Reading the first instruction
0x1 R 09
0x2 R 00
0x9 R 28 Reading the data
0x3 R 6D Reading the second instruction
0x4 R 0A
0x5 R 00
0xA R 02 Reading the data
0x6 R 8D Reading the third instruction
0x7 R 0B
0x8 R 00
0xB W 2A Writing the data

The result matches exactly what was expected:

Execution of the first program

Arduino code is available on GitHub.

Using an EEPROM

The Arduino is nice and all, but in the final system the data will be stored in a memory chip. Here I am using a 32KB AT28C256 EEPROM. This type of memory is intended to be used as read-only memory, but it can be erased and programmed electronically.

I want to start with the basics by performing reads and writes by manipulating the control signals manually. On a breadboard I connect eight LEDs to the ROM’s data bus and force all address lines to zero using jumpers, except for the four least significant bits, which I connect with easily repositionable jumper wires.

The ROM chip has three control signals:

Signal Meaning Purpose
/CE Chip Enable Activates the chip
/OE Output Enable Drives the data bus
/WE Write Enable Triggers a write operation

On the breadboard, I tie /CE to 0 to enable the chip and manually control /OE and /WE.

ROM test setup

I start by testing reads. I set /OE to 0 to enable the outputs and /WE to 1 to place the chip in read mode, then cycle through the addresses by repositioning the four dedicated wires. The LEDs display the value stored at each address.

Here are the results:

Address Data
0x0000 00
0x0001 FF
0x0002 00
0x0003 FF
0x0004 00
0x0005 FF
0x0006 00
0x0007 FF
0x0008 FF
0x0009 FF
0x000A FF
0x000B FF
0x000C FF
0x000D FF
0x000E FF
0x000F FF

Next, I test writing. The process works as follows: under normal conditions (read mode), /WE is high. When /WE goes low, the chip captures the address present on the address bus. When /WE returns high, it captures the data and writes it to memory.

I first disable the outputs by setting /OE high. I configure address 0, then place the value 0xEA on the data bus using jumpers and verify it on the LEDs. Finally, I toggle /WE low and then high again.

I switch back to read mode by removing the jumpers from the data lines and setting /OE low. 0xEA appears on the LEDs. I check the other addresses and find the same values as before, except at address 0 that now contains 0xEA. First manual write successful!

I then connect the address lines, data lines, and control signals to the Arduino and write a program to automate reads and writes.

Initially, the program sequentially scans the 16 addresses and displays the corresponding data. It works well, and I retrieve the original values with OxEA at address 0. I then implement writing, starting with a function that writes a single byte. It reproduces the same control signal sequence that I had previously performed manually. After that, I add support for writing a sequence of values one byte at a time.

Function for writing a single byte to the ROM

Writing the first 14 bytes and then reading them back

Arduino code is available on GitHub.

Connecting the EEPROM to the 6502

The goal now is to connect the ROM to the CPU so that it can read and execute the program. Leaving the ROM on its breadboard, I connect the four address lines and the data bus to the corresponding pins on the 6502. While doing this, I realize that the address lines I choose to manipulate on the ROM were not actually the four least significant bits as I intended. They were in fact lines 13, 8, 9, and 11. That is actually not an issue. As long as I connect the same line to the CPU in the same way, it will find the data at the expected address.

To analyze the results, I decide to rely on the LEDs connected to the ROM’s data bus. This is not necessarily the most convenient method, and I could connect the Arduino as before, but I do not want to add even more jumper wires to an already crowded setup. More importantly, I like to take every opportunity to compare my predictions against reality. Sometimes it highlights gaps in my understanding.

Before running the test, I modify the program so that the bus remains in read mode. I remove the STA instruction and slightly reorganize the program. Here is the new version:

Addr Instruction Data
0x0 LDA $0009 AD 09 00
0x3 LDA $000A AD 0A 00
0x6 ADC $000B 6D 0B 00
0x9   28
0xA   02
0xB   0A
0xC   00
0xD   00
0xE    
0xF    

If everything works correctly, we should observe the following data on the bus:

Hex Binary Explanation
000000 0000Reset vector
000000 0000
AD1010 1101First instruction
090000 1001
000000 0000
280010 1000Data
AD1010 1101Second instruction
0A0000 1010
000000 0000
020000 0010Data
6D0110 1101Third instruction
0B0000 1011
000000 0000
0A0000 1010Data

My first observations left me puzzled. The results did not seem to match my expectations, and I started wondering what was wrong. The 02 byte (0000 0010) made me suspect that the data was showing backwards, but that did not make much sense to me, until I realized that I was reading the LEDs backwards. I expected to see the same bit pattern as the numbers displayed on my screen, but the LEDs had the least significant bits on the left rather than on the right. I turned the board around and it seemed to match pretty well. I recorded the sequence to analyze it afterward, and confirmed everything matches perfectly.

Observing data going through the bus

What’s Next

The next goal is to connect the ROM to the 6502 in a more permanent way on the same breadboard, wiring up all address and data lines as well as the chip-enable logic. To do that, it will be useful to be able to program the entire memory space, and I have a few ideas about how to achieve this. After that, I can start installing the I/O chip that will make it possible to produce more directly observable results, and maybe drive some peripherals?