Writing the Same Program in Both

8-Bit CPUs — 6502/6510 & Z80

Chapter 8 · Writing the Same Program in Both

Chapters 2–7 studied each chip in isolation. This chapter puts them side by side for the first time — one identical task, written twice, so every difference on the page is a real difference in the chips themselves, not an accident of two separate examples. The task: sum five bytes stored in an array into a single running total.

The 6502 Version

ARRAY and SUM are both placed in zero page (cpu8bit1-4), so ARRAY,X below is genuinely zero-page,X indexed addressing, not the more expensive absolute,X form:

        LDX #0        ; X = array index = 0
        LDA #0        ; A = 0
        STA SUM      ; SUM = 0
LOOP    LDA SUM
        CLC          ; NEW — clear Carry before ADC (see warn-box below)
        ADC ARRAY,X  ; SUM += ARRAY[X] — zero-page,X (cpu8bit1-4)
        STA SUM
        INX          ; NEW — increment X (X++)
        CPX #5       ; NEW — compare X against 5, setting flags
        BNE LOOP      ; NEW — branch if the comparison found "not equal"
DONE    JMP DONE      ; the 6502 has no HALT — loop on itself forever

ARRAY   .BYTE 10,20,30,40,50   ; zero page, e.g. $10
SUM     .BYTE 0                    ; zero page, e.g. $20

Three genuinely new instructions here: INX (increment X), CPX (compare X against a value, setting the same flags cpu8bit1-3 already introduced), and BNE (branch if the Z flag is clear — "not equal"). Notice the loop needs three separate instructionsINX, CPX #5, BNE LOOP — just to advance and test the loop counter.

Forgetting CLC before ADC is a classic 6502 bug
ADC always folds the current Carry flag into its addition, per cpu8bit1-3's own Carry explanation. If Carry happens to be set from something earlier in the program, an unguarded ADC silently adds one extra — a famous, well-documented source of off-by-one bugs in real 6502 code. CLC immediately before ADC (when you want ordinary, uncontaminated addition) is close to a reflex among 6502 programmers.

The Z80 Version

        LD HL, ARRAY  ; HL = pointer to the array's start (cpu8bit1-6)
        LD B, 5       ; B = remaining count = 5
        LD A, 0       ; A = 0
LOOP    ADD A, (HL)  ; A += the byte HL points to — register-indirect (cpu8bit1-7)
        INC HL       ; advance the pointer to the next byte
        DJNZ LOOP     ; NEW — decrement B, loop if B != 0, all in ONE instruction
        LD (SUM), A  ; store the final sum
DONE    JR DONE       ; the Z80 has no HALT either — loop on itself

ARRAY   DEFB 10,20,30,40,50
SUM     DEFB 0

One genuinely new instruction: DJNZ ("Decrement and Jump if Not Zero") — decrements B and branches back to the target in a single instruction if the result isn't zero, falling straight through if it is. It's a famous, well-known Z80 instruction, and this routine is exactly the pattern it exists for.

The Comparison

This is the concrete payoff cpu8bit1-5 and cpu8bit1-6 only described in the abstract. The 6502 needs three separate instructions (INX, CPX #5, BNE) to manage its loop counter every single iteration; the Z80's DJNZ folds decrement-compare-and-branch into one. That's the "richness" from cpu8bit1-5, made visible in real code rather than described as a slogan.

6502Z80
Instructions in the loop body7 (LDA, CLC, ADC, STA, INX, CPX, BNE)3 (ADD, INC, DJNZ)
Cycles per iteration (approx.)1926

Fewer instructions on the Z80 side — but more total cycles per iteration, not fewer. Richness bought real code-density here, not raw speed.

Raw cycle counts alone can mislead
Cycle counts only measure work done per clock tick — they say nothing about how fast that clock actually ticks, and the two chips didn't run at the same speed in real machines. A Z80 running at a higher clock speed than a 6502 in a comparable home computer could easily finish this loop in less real, wall-clock time despite needing more cycles per iteration to do it. Comparing cycle counts alone, without knowing each chip's actual clock speed in the machine it's running on, is comparing two different units and reading them as if they were one.
Two threads still ahead
cpu8bit1-9 returns to this same minimalism-vs-richness contrast one more time, applied specifically to interrupt handling — including the shadow register set's own real payoff from cpu8bit1-6. cpu8bit1-11 is where this chapter's own concrete evidence gets formally tied back into the RISC-vs-CISC preview cpu8bit1-1 opened the whole course with.

Hands-On Exercises

Exercise 1

Trace the 6502 version's loop for X = 0 through 4. After the fifth pass through the loop body, X becomes 5. Identify exactly which single instruction is responsible for actually ending the loop at that point, and explain what condition it's checking.

📄 View solution
Exercise 2

Explain, in your own words, exactly what three separate jobs DJNZ combines into one instruction, and what specific consequence that has for the "7 instructions vs. 3 instructions" comparison this chapter draws between the two loop bodies.

📄 View solution
Exercise 3

Using this chapter's own cycle counts (19 for the 6502, 26 for the Z80) and the real historical clock speeds of a 6502 running at roughly 1MHz and a Z80 running at roughly 3.5MHz, calculate the real wall-clock time each chip takes to complete one loop iteration, and state which one is actually faster in practice.

📄 View solution

Chapter 8 Quick Reference

  • Same task (sum 5 bytes) implemented on both chips, using only addressing modes and registers already covered in Chapters 2–7
  • New 6502 instructions: INX (increment X), CPX (compare X), BNE (branch if not equal) — three instructions to manage one loop counter
  • New Z80 instruction: DJNZ — decrement B and branch, all in one instruction
  • Loop body: 7 instructions / 19 cycles (6502) vs. 3 instructions / 26 cycles (Z80) — fewer instructions didn't mean fewer cycles here
  • Cycle counts alone can't tell you which chip is actually faster in real time — clock speed matters too, and the two chips didn't run at the same speed
  • Always CLC before ADC on the 6502 unless you specifically want the current Carry folded into the addition — a classic, well-known bug source
  • Neither chip has a dedicated HALT — both loop on themselves (JMP DONE / JR DONE) to stop, unlike LC-3's own TRAP-based HALT (assembly1-8)