BMOW title
Floppy Emu banner

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.
Read 11 comments and join the conversation 

11 Comments so far

  1. Erik Petrich March 27th, 2010 7:18 pm

    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).

  2. Erik Petrich March 28th, 2010 7:07 pm

    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.

  3. Steve March 29th, 2010 6:59 am

    Cool, sounds like the 6809 was pretty advanced for its time. Too bad it never caught on very widely.

  4. lining April 7th, 2010 6:13 am


  5. Peter Lund April 13th, 2010 7:35 am

    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)…

  6. munch October 23rd, 2010 5:06 pm

    @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.

  7. gsteemso June 28th, 2015 10:43 am

    One thing I wish this little CPU survey had addressed is support for position-independent code. On a 6502 the only purely relative addressing is found in the branch instructions, which include a signed 8-bit displacement that gets added to the PC if the branch is taken; all other instructions (barring the register-only stuff like operations using immediate data, index-register increments, and register-to-register transfers) require at least one memory address to be hard-coded in the instruction. Can anyone comment on how the other five CPUs considered here stack up in this regard?

  8. Paul July 27th, 2015 4:43 am

    gsteemso, the 6809 allowed fully position independent, reentrant code. The stack pointers were 16 bit so the stack could be anywhere, and branch operations all had ‘long branch’ versions, so you could use something like a BEQ (branch if equal) to go up to 127 bytes forward or 128 bytes backwards, or an LBEQ (long branch if equal) to go up to 32767 bytes forward or 32768 bytes backwards (covering the whole address space). The index registers were all 16 bit as well, and you could generate their addresses relative to PC, so those could be position independent as well.

    OS-9 was a full multi-tasking/multi-user OS written for the 6809 processor which relied on position independent/reentrant code. Modules (including device drivers & user programs) could be loaded anywhere there was a gap in memory (without having to be ‘fixed up’) and they’d just work, and if two users each ran the spreadsheet, it would just load the module once (to save memory), and give each instance a different stack & data address space.

  9. dav May 20th, 2016 3:36 am

    this is wrong:

    “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”

    there is no such thing as an m register. here m is just shorthand for ‘the byte at the address held in hl’, translated for the 8080’s absolutely horribly set of mnemonics. the z80 mercifully just calls this ld a,(hl)

  10. Brian H January 24th, 2017 5:04 pm

    Fascinating write-up. I think it is worth noting that the big commercial successes were the Z80 and the 6502. But, of course, those were both designed to be low-cost knock-offs of the 8080 and the 6800, respectively. Motorola fired back by creating the 6809. While not commercially that successful, it probably should hold the distinction for being the best of the lot, technically. I think it’s noteworthy that OS-9 is likely the most modern’ish and capable full-featured OS ever made for an 8-bit CPU, and could only be done for the 6809. Intel fired back by making the 16-bit 8086, releasing the scaled-down 8/16-bit 8088, and getting it selected for the IBM PC – the rest, as they say, is history. If (big if) IBM had decided to go with the 6809 instead, the microprocessor would might look a lot different today.

  11. David Morton October 3rd, 2017 5:20 am

    Actually Steve the 6809 was quite popular in many roles outside the desktop microcomputer, its Wikipedia article gives more. In fact, it is still being made by Rochester and I am currently working on a highly specialized, new project that may use it. Why still use it, because it is all I need; and the way many newer systems based on popular chips are hacked, a less popular chip is less prone to attack.

Leave a reply. Comments may not be monitored regularly. For product support questions, visit the Contact page.