Skip to content
Game 0 Unit 10 of 16 1 hr learning time

Counting Toward Zero

Hand the repetition to the machine. Load a count into B, and DJNZ runs the body that many times — one loop fills a whole row where last unit you stepped by hand.

63% of Meet The Machine

Last unit you walked the finger by hand — write, step, write, step — and you could feel the repetition piling up. Three cells was fine; thirty-two would be absurd. So you don't write it thirty-two times. You tell the machine to.

That's a loop, and on the Z80 the everyday one is DJNZ:

  • ld b, 32B is the counter. Load it with how many times you want.
  • djnz .fillDecrement B, Jump if Not Zero. In one instruction it subtracts 1 from B and, if B isn't yet zero, jumps back to the label.

Put a body between the count and the djnz, and that body runs exactly B times. It's BASIC's FOR i = 1 TO 32 … NEXT, assembled from a counter and a jump you already know.

What you'll see by the end

A solid red bar running the full width of the screen, near the top.
The whole top row — 32 colour cells — painted by a two-line body that ran 32 times. You wrote the body once; DJNZ did the counting.

A red bar clear across the top — the whole top row of 32 colour cells, painted by a loop that ran the same two-line body thirty-two times. You wrote the body once.

The loop

; ============================================================================
; PRIMER — Beat 10: Counting Toward Zero
; ============================================================================
; In Beat 9 you stepped the finger by hand: write, inc, write, inc... A loop
; hands that repetition to the machine.
;
;   ld b, 32       -- B is the dedicated COUNTER. Load it with how many times.
;   .fill:  ...    -- the body: do the thing once.
;   djnz .fill     -- DJNZ = "Decrement B, Jump if Not Zero". One instruction
;                     subtracts 1 from B and loops back until B reaches 0.
;
; So the body runs exactly B times. Here it colours one cell and steps the
; finger -- 32 times -- which paints the whole top row of the screen red.
; That's BASIC's `FOR i = 1 TO 32 ... NEXT`, built from a counter and a jump.
;
; The pointer walk (ld hl / ld (hl) / inc hl) is exactly Beat 9; the only new
; thing is letting the machine do the counting.
; ============================================================================

            org     32768

start:
            ld      hl, $5800        ; finger on the first colour cell
            ld      b, 32            ; count: 32 cells = one full row

.fill:
            ld      (hl), $17        ; colour the cell  (PAPER red, INK white)
            inc     hl               ; step to the next
            djnz    .fill            ; B = B - 1; not zero? back to .fill

.loop:
            halt
            jr      .loop

            end     start

The inside of the loop is exactly Beat 9 — colour the cell, step the finger. The only new thing is the wrapper: ld b, 32 sets the count, and djnz .fill does the counting and the jumping in one go. The finger walks all 32 cells; the bar appears.

Why count toward zero? Because the machine already knows, for free, when a result hits zero — that's the zero flag from Beat 7. DJNZ leans on it: it counts down and stops at zero, instead of counting up and comparing against a limit each time. FOR counts up for human comfort; the CPU counts down for its own.

B is the counter DJNZ is wired to — that's its job in this idiom. (It's why we've kept B free in the loops you've seen.)

Assemble and run

pasmonext --sna counting-toward-zero.asm primer.sna

One loop, a full red row.

Try this: change the count

Set ld b, 16 for half a row, ld b, 10 for ten cells. ld b, N is your FOR i = 1 TO N — the count is the loop's whole story. (Avoid ld b, 0: DJNZ decrements first, so a count of 0 wraps to 256 and runs far longer than you meant — a classic first-loop surprise.)

Try this: the dashed line, properly

Remember Beat 5's dashed line — eight near-identical pokes? Here it is as a loop, all 32 cells across — the same djnz body, pointed at the pixel map and writing the dotted byte:

The same loop pointed at $4000, writing the dotted byte
+9-20
11 ; ============================================================================
2-; PRIMER — Beat 10: Counting Toward Zero
2+; PRIMER — Beat 10, "Try this: the dashed line, properly"
33 ; ============================================================================
4-; In Beat 9 you stepped the finger by hand: write, inc, write, inc... A loop
5-; hands that repetition to the machine.
6-;
7-; ld b, 32 -- B is the dedicated COUNTER. Load it with how many times.
8-; .fill: ... -- the body: do the thing once.
9-; djnz .fill -- DJNZ = "Decrement B, Jump if Not Zero". One instruction
10-; subtracts 1 from B and loops back until B reaches 0.
11-;
12-; So the body runs exactly B times. Here it colours one cell and steps the
13-; finger -- 32 times -- which paints the whole top row of the screen red.
14-; That's BASIC's `FOR i = 1 TO 32 ... NEXT`, built from a counter and a jump.
15-;
16-; The pointer walk (ld hl / ld (hl) / inc hl) is exactly Beat 9; the only new
17-; thing is letting the machine do the counting.
4+; Beat 5's dashed line took eight near-identical pokes. Here it is as a loop:
5+; point at the screen, write the dotted byte, step, repeat 32 times -- a full
6+; dashed line across the very top, and not a line of source repeated.
187 ; ============================================================================
198
...
2110
2211 start:
23- ld hl, $5800 ; finger on the first colour cell
24- ld b, 32 ; count: 32 cells = one full row
12+ ld hl, $4000 ; finger on the top-left pixel cell
13+ ld b, 32 ; 32 cells across the top
2514
2615 .fill:
27- ld (hl), $17 ; colour the cell (PAPER red, INK white)
28- inc hl ; step to the next
29- djnz .fill ; B = B - 1; not zero? back to .fill
16+ ld (hl), %10101010 ; the dotted byte from Beat 5
17+ inc hl
18+ djnz .fill
3019
3120 .loop:

Point at the screen instead of the colour map, write the dotted byte, step, repeat. The repetition you typed out by hand in Beat 5 is now four lines that do the lot.

When it's wrong, see why

  • Only one cell is coloured. The inc hl is missing or sits outside the loop, so every pass writes the same box. Both the write and the step belong inside, before djnz.
  • The loop runs far too long / the screen fills up. You loaded ld b, 0. DJNZ decrements first, so 0 becomes 256. Use the count you mean.
  • pasmonext can't find .fill. The label and the djnz .fill must match exactly, case and all — labels are the assembler's rules, from Beat 7.

What you've learnt

ld b, N then djnz label runs the loop body exactly N times — a counter and a jump that count toward zero — so one loop does what would be N lines by hand.

What's next

Your programs are growing, and chunks of them want names. Next — Call, Return, and a Stack You Can See — we package a job into a subroutine you can call from anywhere and ret from, and watch where the machine remembers its way back: the stack, in plain memory.