Skip to content

Adding and Taking Away

The most ordinary thing of all, finally: maths. inc, dec, add and sub on a value — and the carry flag, the machine's way of telling you a byte just overflowed.

  • Sinclair ZX Spectrum
  • Assembly

You can move values, compare them, loop, branch, point and call — and yet you’ve never done the most ordinary thing a program does: arithmetic. Before you build a game, the last pieces of plain fluency. This one is the most everyday of all: LET x=x+1.

On the Z80 that’s a single instruction:

  • inc a — add 1 to A. dec a takes 1 away.
  • add a, n — add n to A. sub n takes n away.
  • add a, b — add register B to A (add two values together).

There’s a catch you already half-know: A holds eight bits, 0–255. Add past 255 and it can’t hold the answer — it wraps round and sets the carry flag: 255 + 1 gives 0, 255 + 2 gives 1. That’s the carry we borrowed in Test, Then Jump; here’s where it comes from. (More in the Try this.)

What you’ll see by the end

The screen surrounded by a cyan border.
A cyan border — colour 5, never loaded but computed: start at 2, inc twice to 4, add 1 to 5, then show it.

A cyan border — colour 5. But we never loaded 5; we computed it: start at 2, count up twice, add one. In a real game the numbers are a score or a position you don’t know ahead of time. Here they’re constants so you can check the sum yourself.

Computing a value

arithmetic.asm
; ============================================================================
; PRIMER — Beat 12: Adding and Taking Away
; ============================================================================
; You've moved values, compared them, looped and branched -- but never done
; the most ordinary thing of all: maths. BASIC's `LET x = x + 1` becomes one
; instruction here.
;
;   inc a       -- add 1 to A          (dec a takes 1 away)
;   add a, n    -- add n to A          (sub n takes n away)
;   add a, b    -- add register B to A
;
; A holds 8 bits, 0-255. Add past 255 and it wraps round to 0 and sets the
; CARRY flag -- the machine's "it overflowed" signal (the carry we borrowed
; back in Test, Then Jump). We meet it properly in the Try this.
;
; Here we COMPUTE the border colour rather than just loading it: start at 2,
; count up twice, add one -- 5, cyan. In a game the numbers are a score or a
; position you don't know in advance; here they're constants so you can check.
; ============================================================================

            org     32768

start:
            ld      a, 2             ; start at 2
            inc     a                ; + 1 -> 3
            inc     a                ; + 1 -> 4
            add     a, 1             ; + 1 -> 5   (cyan)
            out     ($FE), a         ; show the computed result

.loop:
            halt
            jr      .loop

            end     start

2, inc to 3, inc to 4, add a, 1 to 5 — then show it. Same shape as loading the border, but the value is worked out instead of written down. That’s the whole difference between a constant and a calculation.

Assemble and run

Code
asm198x --dialect pasmo --cpu z80 --tapbas arithmetic.asm -o primer.tap

A cyan border, arrived at by adding.

Try this: past the edge of a byte

Replace the body with ld a, 255 then add a, 1, and watch A in the register view: it reads 0, not 256. A byte can’t hold 256, so it wraps to 0 — and the carry flag is set, the machine flagging “that overflowed.” A jr c would fire on it. This is how you’d catch a score rolling over, and how numbers bigger than a byte are built (next unit but one).

Try this: taking away

Swap in sub: ld a, 6 then sub 2 leaves 4 (green). sub past zero wraps the other way — ld a, 0 then sub 1 gives 255, and sets carry again (this time as a “borrow”). Predict each before you run it.

When it’s wrong, see why

  • The assembler errors on add 1. The 8-bit add names its target: it’s add a, 1, not add 1. (sub, oddly, is just sub 1 — A is assumed.)
  • The border colour is “wrong”. It isn’t — the border shows only the low 3 bits, so any result is shown mod 8. Check the value in the register view for the true answer.
  • You expected 256 and got 0. That’s the lesson, not a bug: a byte stops at 255.

What you’ve learnt

inc/add add and dec/sub take away. All four wrap a result past 255 or below 0, but only add and sub set the carry flag, the machine’s overflow signal — inc and dec leave it alone.

What’s next

Sometimes you don’t want to change a whole number — just one bit of it. Next — Working With Bits — and, or and xor let you set, clear and flip individual bits, like switching a single attribute’s BRIGHT on without touching its colour.

Sources

Zilog, Z80 CPU User Manual, UM008011-0816: ADD A, n and SUB s, which set the carry flag on a carry or borrow out of bit 7, and INC r and DEC r, which leave the carry flag unaffected. Sinclair Research, ZX Spectrum BASIC Programming, chapter 23, IN and OUT: the border colour from bits 0–2 of port 254.

Run it here

This unit's program, built for you.

Program
primer.tap
Machine
ZX Spectrum 48K (PAL)

Download primer.tap