
The 65c02 CPU (on the right) hooked up for a “free run”. The Arduino on the left is providing power and a clock signal, and is reading the 6502’s address bus. The Big Red Button is hooked to the 6502’s reset pin.
If you’re building a computer from scratch, where do you start? I suppose you could hook up all the pieces, apply power, and… spend the next few weeks trying to figure out what isn’t working. Yeah, that seems like a bad way to do it.
When you write software, it’s best to do it in small pieces that can be tested individually. This is even true if the pieces don’t do anything useful, because it really is possible to write code that’ll do nothing useful and still do it incorrectly. If you’re being formal about your software development, you’ll even write the tests first and then fill them in with code.
For my Project:65 computer, I wanted to find the most stripped-down piece that I could hook up and get to do… well, anything, really. If you start with just the 6502 CPU, how much hardware do you have to add to make it do something, just to prove that the chip is alive? And what’s the smallest, simplest way to give it what it needs?
You usually think of a microprocessor as needing RAM, ROM, and some kind of IO in order to work. But it’s easy to write an assembly language routine that runs from ROM and doesn’t store any values in RAM. So RAM is optional.
What about IO? I don’t need a specific result; I just need to prove that the CPU is doing what’s expected of it. One way to do that is to monitor the flow control – the changing value in the CPU’s program counter that shows where in memory it’s reading instructions from. When the 6502 processor wants to read the next instruction from ROM, it puts the value in its program counter on its 16 address bus pins. If you watch the voltages on those pins, you can see what the program counter’s value is, and no other IO is needed.
How about ROM, though? We still need to get our program from somewhere, right? Well, that’s where things get really interesting. To understand what I’m doing next, you need two facts:
- What does the 6502 do when it starts up? How does it know what code to run? The chip expects that the last few bytes of its address space will contain a set of vectors – basically, the addresses where it should start reading instructions from in the event of an interrupt or reset. That’s why the system ROM is usually at the end of the memory map on 6502-family systems like the Commodore 64. When the CPU gets a reset signal, it reads the data in locations $FFFC and $FFFD and sets the program counter. From there, it’s off and running.
- The 6502 (like every other CPU) has a NOP instruction – “no operation”. When the CPU is instructed to NOP, it just increments the program counter and moves on to the next instruction. The opcode for the 6502’s NOP instruction is the single byte $EA.
With those two facts, it’s possible to set up a 6502 to do a “free run” – an endless cycle through its address space. All you have to do is wire up the 6502’s 8-bit data bus to always read the value $EA. That’s 11101010 in binary – you just hook up +5v to the “1” pins, and ground the “0” pins. No matter what memory location the 6502 tries to read, it’ll always see $EA on its data bus. When it resets, it’ll read $EAEA from the reset vector, and set the program counter accordingly. Then it tries to read from address $EAEA and receives a NOP instruction. So it increments the program counter and reads another NOP from location $EAEB, and so on until it reads a NOP from $FFFF and circles back around to the beginning of the address space. And so on. Forever!
Okay, there are a few minor details. The 6502 needs a clock, for example. That’s just an input that switches from 0v to +5v. Usually you’d hook up a crystal oscillator, but since the part I ordered hasn’t arrived yet, I just hooked it up to one pin of my Arduino. It’s not a very fast clock, or a very regular clock, but right now it doesn’t have to be.
There are a few other pins on the 6502 that have to have particular values before it will do anything. For example, there are two interrupt input pins – IRQ and NMI – that should be kept high at all times. When I was initially debugging this setup, I hooked up IRQ correctly, but forgot about NMI and left it floating. This resulted in a circuit that seemed to work correctly, but if I happened to touch the breadboard at all the CPU would get confused and reset. That’s a very spooky error to try to debug, until you understand what’s happening.

Connecting the 6502 for its free run. The clock signal, as well as address lines A8 through A15, are connected to an Arduino.
I used a program called Fritzing to create this circuit diagram. It’s my first try with that program, and kind of crude, but it gets the general point across. In addition to generating the clock, I connected the upper 8 address bits of the 6502 to the Arduino’s digital inputs, so the Arduino could report the value back to my PC. When the circuit worked correctly, I could see that value change from $EA to $EB, $EC… $FF, $00, $01, etc., incrementing once for every 256 NOP instructions.
The software on the Arduino side is trivial. It runs a loop where it toggles the clock output on and off, and then reads the values of the 8 address lines I hooked up. If the value has changed, it writes out the hexadecimal value on its serial port. I didn’t have enough digital inputs to read all 16 address lines, but this was enough to verify that the 6502 was working correctly.
At this point, I have a CPU that I know works, but that can’t do anything besides spin in circles. And it’s not even spinning in circles quickly – I only got it up to a couple kilohertz with this method. The next step for Project:65 has to be getting it to read a real program, instead of an endless stream of NOPs. I hope to have more on that topic next week!
Excellent!!!
I’m also tinkering with old Motorola stuff at the moment but I used the SID, aka MOS6581, for my first project. It’s yet another SID player but this was done as an exercise prior to attacking the bigger “sister” Paula. My goal is to build a hw tracker player based on the Paula chip from the Amiga 500.
No emulators around provide the same audio output so it needs to be done 😉
I shall follow this build with excited eyes because I sincerely miss the “old” days.
I was wondering if you could post your free run sketch for the arduino. I have an arduino and 6502 and would like to give this a try but my programming from scratch is limiting.
Here’s what I used for monitoring the free run and running the clock. It counts the number of times we loop through memory and tries to detect resets.
Wiring up the breadboard now. The caps are 47uf? Should I be using some 104 .1uf decoupling caps?
I’ve been keeping a close eye on veronica over at blondihacks, I think I saw you post a few comments over there. Eventually I would like to do something like that with my 6502 but i think some experimental projects with the arduino will get me more familiar with how the 6502 works.
Thanks for all the awesome info and insight!
The smoothing capacitor (the one on the left) is 100 uf. The cap in the reset circuit was 10 uf – but I had trouble with the reset circuit later on, and eventually removed the capacitor completely. I still need to come up with a better reset circuit.
At this stage I didn’t have any decoupling caps, but I did add them later when I had a few more chips hooked up. I used .1uf for those, since that’s what most of the sources I’ve looked at suggest.
Good luck!
LOL
I hit a snag with the code, it won’t compile.
Stops at: Serial.print (“roll count: “);
I’m using arduino 1.0.3 with an uno.
Found a similiar sketch for reading to the serial monitor but doesn’t solve my problem.
Here’s the error I get:
freerun:48: error: stray ‘\’ in program
freerun:48: error: stray ‘\’ in program
freerun:56: error: stray ‘\’ in program
freerun:56: error: stray ‘\’ in program
freerun.ino: In function ‘void loop()’:
freerun:37: error: expected primary-expression before ‘>’ token
freerun:37: error: expected `;’ before ‘)’ token
freerun:48: error: ‘u201croll’ was not declared in this scope
freerun:56: error: ‘u201c’ was not declared in this scope
freerun.ino: In function ‘char int2hex(int)’:
freerun:65: error: lvalue required as left operand of assignment
Yeah, looks like the WordPress software kind of mangled the code when I pasted it into the comment. I went back and edited it, so try copying it again and see if that works better.
Sorry, there seems to be something wrong with the sketch — for one thing, I don’t see a digitalRead() anywhere to pick up what the CPU is sending. Are you sure this is the correct code?
Oh, and thanks for all the work!
Apparently I screwed up the first time I tried to fix the formatting of the reply with the source code – there were several lines in the middle of loop() that were just missing. I fixed it in the original. Thanks for pointing that out.
Works like a charm now. Thanks!
FYI I can confirm that the sketch will work with a 1 MHz clock can attached to the CPU as well. Just comment out the delay line to make it faster. Thanks again!
Pingback: Make your own Apple 1 replica | Dave Cheney
Note that without RAM, you can’t run a subroutine, even if it’s in ROM, as the return address is stored on the stack in RAM. You can have a minimal 6502 computer with only five ICs though, having uP, ROM, RAM, I/O, reset circuit, clock circuit, and address decoding. See http://wilsonminesco.com/6502primer/potpourri.html#BAS_CPU
For a NOP generator, I like to recommend $A9 so you get LDA #$A9 because then you get one memory read per clock and one address advance per clock.
Keep us posted. Are you on http://forum.6502.org/ ?
I have a 6502 primer (and loads of other 6502 articles, links, etc., and lots more coming as time allows) at http://wilsonminesco.com/ .
Hey, thanks for your comment. Your primer is a great resource, and it was one of the things that inspired me to start this project – I know I refer to it in at least a couple of the P:65 posts.
I’ve lurked on 6502.org, but I don’t tend to post much on forums lately. I really should swing by and say ‘hi’ one of these days 🙂