As per Ben Eater's tutorial I've setup a 6502 to run off his clock module. I bought both my Mega 2560 and clock module kit from Ben's store to support his work.
Microcontroller As "Memory"Based on a few other examples I've seen online where people use a microcontroller (typically a Teensy or even a Raspberry Pi) to simulate ROM for either the Z80 or the 6502, I wondered if that would work using as slow a microcontroller board as the Mega 2560.
In part 2 of Ben's 6502 tutorial he hooks the 6502 up to his clock module and monitors the address and data lines from the Mega 2560. Ben hard codes NOP (0xEA) on the data bus to give the 6502 something to do. Ben then moves on to implement a bigger program in the EEPROM.
I took a different path. I started with his Arduino sketch from this example and upgraded it from an analyser to a memory simulator. Since the Mega 2560 doesn't have a whole lot of memory, I limited myself to 4KB and mapped it to high memory (0xF000..0xFFFF). This allowed me to cover the reset vector (0xFFFC and 0xFFFD) and simulate some program ROM (starting at 0xF000) while also having spare memory to use as RAM (for my sample program to write to).
This all worked surprisingly well using Ben's original analyser code (with some modifications to make it read and write memory from a 4KB array on the Mega) assuming I kept the clock speed down.
Opening The ThrottleBen's clock module (default components) can go from as slow as a few Hz up to about 450Hz. It can also single step and be halted. All this is done reliably with properly debounced switches. Good little module.
When I open up the throttle on the clock to full speed, the wheels fall off. Initially I thought this was because the serial communication couldn't keep up so I changed the serial speed to the maximum supported (115200 baud) : still no change.
Then I got rid of the Serial println
and replaced it with some code to use the Mega 2560 as a heartbeat (so we know our program is still running as expected). Still no joy. As soon as I crank up the clock the wheels fall off.
The obvious way to improve the performance of Ben's interrupt service routine, onClock(), is to replace the pin-by-pin digitalRead(..) and digitalWrite(..) with low level direct port access (a byte at a time, no extra function calls). Unfortunately to make this work easily you need to connect the address and data lines in port pin order rather than in the more convenient sequencial pin number order.
Electronics Hub has the nicest pinout I could find to explain the next step:
Since we are using ribbon cables to connect the ports (as per Ben's 6502 kit), it helps if the pins we are connecting to are at least in some kind of order that makes sense. I have a ribbon of 8 cables for data and a ribbon of 16 cables for address. In both cases, I tore the ribbon so that I have a black wire on the edge. My convention is that black is always zero (so, D0 and A0 are black).
By far the most convenient GPIO ports to connect to on the Mega 2560 are the double row on the edge of the board. In this double row there are only two adjacent ports and thankfully they are sensibly numbered. That's why I chose ports A and C for the address lines.
A0, the black wire, starts at PA0 and then you simply alternate odd and even lines in the two rows continuing through to PA7 and all the way through PC7 as shown.
That leaves one remaining port in the double row where all the pins are adjacent which is why I chose port L for the data lines.
Oddly the port L pins run in the opposite direction to the pin A & C pins but they are still very conveniently grouped.
In summary:
Port A = A0..A7
Port C = A8..A15
Port L = D0..D7
Now we get to replace the 16 step for loop for the address lines with a one liner:
unsigned int address = PINA + (PINC << 8);
Reading the data lines we now replace the 8 step for loop with another one liner:
unsigned char data = PINL;
There is a bit more to it since I'm both reading and writing the data lines (ROM and RAM, remember?) but you can see that in the full source of my sketch.
ConclusionAfter this optimization of the Arduino sketch, we can run Ben's clock at full speed (~450Hz) while simulating ROM and RAM (4KB total) for our 6502 with a lowly Arduino Mega 2560.
AfterthoughtsI experimented with faster clock frequencies and we start to see failures around 550Hz. By commenting out #define SERIAL_DIAGNOSTICS
and relying on the built-in LED of the Mega 2560 as a heartbeat indicator we get to run a lot faster. With serial logging off I've seen stable success as fast as 11.5kHz (by adjusting the resistors and capacitor in the astable 555 part of the clock module).
Comments