8-Bit CPU Comparison
BMOW’s instruction set was a close cousin to the 6502′s, and so when I started on CPLD CPU, I initially intended to try something different. I looked at the Intel 8008 and 8080, Zilog Z80, and Motorola 6800 and 6809, which were all popular 8-bit CPUs back in the 1970′s and 80′s. After studying up on the architecture of all these different CPUs, I decided that the 6502 was still the best choice to use as a model, because I believe it can be implemented using the least CPLD resources.
The cross-CPU comparisons were very interesting. The key differences between these early 8-bit CPUs fell into four main areas: number of data registers, 16-bit instruction support, address registers, indexed addressing.

Number of Data Registers
More registers are just about always better, except where you’re starved for logic resources in a tiny CPLD. While the 6502 has three 8-bit registers A, X, and Y, my plan is to only include A and X in the CPLD CPU instruction set.
CPU 8-Bit Data Registers:
- 8008 - 7, ABCDEHL
- 8080 - 7, ABCDEHL
- Z80 - 7, ABCDEHL
- 6800 - 2, AB
- 6809 - 2, AB
- 6502 - 3, AXY
16-bit Extensions
While all the CPUs in this group are 8-bit designs, some support a limited number of 16-bit operations too. These are very handy for the programmer, but any 16-bit operation can also be performed as a series of 8-bit operations, so the difference is one of speed and not capability.
CPU 16-bit Support:
- 8008 - none
- 8080 - BC, DE, HL can be joined to form 16-bit registers. They support load/store of 16-bit values, and increment/decrement. Addition/subtraction of 16-bit registers is supported when HL is used as an accumulator.
- Z80 - Same as 8080, with some added flexibility on supported operations.
- 6800 - none
- 6809 - AB can be joined to form a 16-bit register D. It supports load/store of 16-bit values, and addition/subtraction/compare with a 16-bit value in memory.
- 6502 - none
Address Registers
Most modern CPUs store an address in a user-visible register, then use other instructions to manipulate the data at that address. These may be registers used specifically for holding addresses, or general purpose data/address registers, but either way the size of the register must be at least as large as the address space of the CPU. For example, the 8008 and 8080 have the M register, which can hold a 16-bit address. A byte can then be fetched from that address into the accumulator with the instruction MOV A,M. This is convenient, because you can do things like perform arithmetic on M in order to construct the right address for a value in a struct.
Another way of approaching addresses is to include them directly in the instruction itself. For example, the 6502 can fetch a byte from address $0123 into the accumulator with the instruction LDA $0123. This has the advantage that you don’t have to load $0123 into an address register first, but it means you can’t do arbitrary arithmetic on the address.
A CPU that supports absolute addresses directly in the instruction must have a hidden, temporary address register. That address in the program code has to be loaded somewhere, so that it can then drive the address bus, and a user-hidden address register fits the bill nicely.
In a CPLD CPU, the user-visible and user-hidden address registers both require space: one macrocell per bit. 10 macrocells for an address register is expensive, when your entire device only has 128. Having only a user-visible address register creates a fairly clunky programming experience, because it rules out having absolute addresses in the instruction, and forces the programmer to manually load the address register first before every memory reference. This is what the 8008 does. Most CPUs have both. The 6502 is unique in this group by having only a hidden address register, and no user-visible one. This gives up some flexibility, but is still a workable solution and eliminates one large register from the CPLD. The 6502′s zero page mode can also turn the first 256 bytes of memory into 128 pseudo-address registers.
CPU Has a User-Visible Address Register?
- 8008 - Yes, HL (also called M). ALL memory references must be done with this (no absolute addressing). Must load L and H separately.
- 8080 - Yes, BC, DE, HL. Can swap DE<->HL. Also supports absolute memory addresses.
- Z80 - Yes, Same as 8080. Adds some additional flexibility for addressing modes.
- 6800 - Yes, has a 16-bit X register. This functions like an address register when used with a absolute base address of 0. Only limited support for arithmetic with X. Also supports absolute memory addresses.
- 6809 - Yes, has 16-bit X and Y registers. Otherwise same as 6800.
- 6502 - No. Supports absolute memory addresses, and page 0 of memory as a set of pseudo-address registers.
Indexed Addressing
A very common pattern in assembly language programming is to reference a memory location using a combination of a base address and an offset. When performing some operation to many consecutive memory locations, this is generally faster and more convenient than altering the base pointer each time through the loop. For example, the 6502 can load a byte to the accumulator at some offset from address $0123 with the instruction LDA $0123,X. This takes the value currently in the X register, adds it to $1023, and uses the resulting address as the location from which to load a byte. Here’s an example 6502 programming using indexed addressing to sum all the values in memory locations $1000 to $100F:
ldx #$0F ; initialize X index
lda #$00 ; register A holds sum, initialize to 0
clc ; clear carry flag
- adc $1000,X ; add the byte from memory to the running total
dex ; decrement X index
bpl - ; if index is >= 0, branch to start of loop and keep going
Not all those early 8-bit CPUs supported indexed addressing. For CPUs with a user-visible address register, it’s not strictly necessary, because the programmer can accomplish a similar result by doing arithmetic on the value in the register. But even where it’s not necessary, it’s still a very handy feature. The 6502 supports indexed addressing in the most resource-efficient manner of the CPUs in this group.
CPU Supports Indexed Addressing?
- 8008 - No, must manipulate the address register manually.
- 8080 - No, must manipulate the address register manually.
- Z80 - Yes, uses two 16-bit address registers, and an 8-bit offset that’s part of the instruction.
- 6800 - Yes, uses one 16-bit address register, and an 8-bit offset that’s part of the instruction.
- 6809 - Yes, uses two 16-bit address registers, and an 8-bit offset that’s part of the instruction.
- 6502 - Yes, uses one 8-bit offset register, and a 16-bit address that’s part of the instruction.
6 Comments so far
Leave a reply. Comments may take a few minutes to appear.
I had a lot of fun programming the 6809 when I was younger. It can actually do a little more than you describe.
In addition to the 16-bit X and Y registers, there’s also a 16-bit U register. The U register can be used just like X and Y (for example, indexed addressing), but it also can be used as a secondary stack pointer. At the other extreme, if you are willing to do without any stack, the S (stack pointer) register can be used exactly like X and Y as well.
For the indexed addressing modes, the offset was signed and could be 0, 5, 8, or 16 bits in size (the different formats allowed more common cases to fit into as small of an instruction as possible) and you could use X, Y, U, or S. The offset could also come from the accumulator registers A, B, or D.
Indexed addressing mode with the stack pointer (S) made using local variables kept on the stack trivial. You could also index using an 8 or 16 bit signed offset relative to the program counter.
Using the indexed addressing modes with the load effective address instruction, you could easily do 16-bit addition and subtraction. Whether or not the result was actually an address didn’t matter. So examples:
LEAX 5,X X = 5+X
LEAY D,U Y = D+Y
LEAU -1234,X U = X-1234
I didn’t know HL was also called M on the 8008; interesting (it had already been displaced by the 8080 by the time I looked at that family of architectures).
Oops, that should have been:
LEAY D,U Y = D+U
I also forgot about the 6809′s indirect indexed addressing modes where the indexed address specifies the address of a pointer to the actual memory operand. The 6502 could do something like that with its Y register too, I think. The fancier indexed addressing modes do come at the expense of longer and more complicated instructions to decode so I agree that the 6502 is probably a better source of inspiration for you CPLD CPU.
Cool, sounds like the 6809 was pretty advanced for its time. Too bad it never caught on very widely.
不知道能不能看的懂啊,也是爱好者,羡慕并学习中……
The IX and IY indexed addressing modes on the Z80 were not of much use, unfortunately.
However, you can treat them as two extra 16-bit registers — and on most Z80′s you could do a bit more than that, although it was never architected.
Let me explain: the IX and IY indexed addressing modes are really just ordinary HL indexed addressing instructions with a special prefix byte in front + a suffix displacement byte at the end. Those prefix bytes can on most Z80′s be used in front of any instruction that uses HL or H or L to make it operate on IX, IY, or their individual bytes instead. Neat, huh?
The Z80 also has a set of “shadow” registers that can be swapped with the normal ones with a single 4-cycle instruction (EXX).
I wish that instead of the IX/IY indexing nonse they would have given us SP+displacement indexed addressing (for parameter passing on the stack)…
@Peter, use IX or IY as a data stack pointer, and leave SP as the return stack pointer. I can’t think of anything that would =require= a single stack for C or Pascal compilers. Also, IX and IY are just what the doctor ordered for referencing fields of data structures, which was their original intent.