Adding and Taking Away
Arithmetic is two instructions: ADD and SUB. They work on a size you choose, and they leave the flags set for a branch to read. Master the size suffix and the carry between sizes and you've got the maths every program runs on.
You've used numbers since Unit 1 — colours, counts, addresses. This unit looks straight at the arithmetic underneath: how the 68000 adds and subtracts, and the two things that catch people out.
The instructions themselves are short. ADD adds; SUB subtracts. Both take a size — .b, .w, .l — that says how wide the sum is, exactly like MOVE back in Unit 3. And both leave the flags set behind them, the same flags Unit 8's branches read: a sum that comes out zero sets the zero flag, a result that won't fit sets the carry. So arithmetic and decisions are joined at the hip — you add, then you branch on how the add turned out.
This unit keeps the sums visible by doing them to a colour: add a channel, take one away, and watch where the value lands.
What you'll see by the end
A blue screen. The code starts with red, adds blue to it, then takes the red away again — and blue is what remains. Every step is a real add or sub, and the final number is what the channel arithmetic left behind.
Add, then take away
;──────────────────────────────────────────────────────────────
; Meet the Machine (Amiga) - Unit 14: Adding and Taking Away
;
; ADD and SUB are the whole of arithmetic. They work on a chosen size (.b/.w/.l)
; and leave the flags set for a branch to read. Here a colour is built by adding
; one channel and taking away another.
;──────────────────────────────────────────────────────────────
CUSTOM equ $dff000
DMACON equ $096
INTENA equ $09a
INTREQ equ $09c
COP1LC equ $080
COPJMP1 equ $088
BPLCON0 equ $100
COLOR00 equ $180
section code,code_c
start:
lea CUSTOM,a5
move.w #$7fff,INTENA(a5)
move.w #$7fff,INTREQ(a5)
move.w #$7fff,DMACON(a5)
lea copperlist,a0
move.l a0,COP1LC(a5)
move.w d0,COPJMP1(a5)
move.w #$8280,DMACON(a5)
; ----------------------------------------------- YOUR CODE START
move.w #$0f00,d0 ; start with red
add.w #$000f,d0 ; add blue -> $0f0f (magenta)
sub.w #$0f00,d0 ; take the red back -> $000f (blue)
move.w d0,colourval ; show the result
; ------------------------------------------------- YOUR CODE END
forever:
bra.s forever
copperlist:
dc.w BPLCON0,$0200
dc.w COLOR00
colourval:
dc.w $0000
dc.w $ffff,$fffe
Three sums build the answer:
move.w #$0f00,d0 ; start with red
add.w #$000f,d0 ; add blue -> $0f0f (magenta)
sub.w #$0f00,d0 ; take the red back -> $000f (blue)
move.w d0,colourval ; show the result
Because the colour's three channels sit in separate groups of bits, adding $000f only touches the blue channel and adding $0f00 only the red. So $0f00 + $000f is $0f0f — red and blue together, which is magenta — and subtracting $0f00 removes the red and leaves $000f, blue. The .w on each instruction says "work sixteen bits wide," which is the size a colour needs.
The flags moved with every line, even though nothing branched on them here. After the last sub the result is non-zero, so the zero flag is clear — and if you followed it with a beq, it wouldn't branch. That's the join from Unit 8: the sum sets the flags, the branch reads them.
Assemble, master, and run
make
A blue screen — red and blue added, then the red taken back.
Try this: change the sums
Swap the sub.w #$0f00 for sub.w #$000f. Now you remove the blue instead and land on $0f00 — red. Or drop the sub line entirely and keep the magenta. The screen is a readout of the arithmetic; change the numbers and you can predict the colour before you run it.
Try this: watch a carry happen
Carry is what spills out of one size into the next. Try move.w #$ffff,d0 then add.w #1,d0: the word wraps to $0000 and the carry flag is set, because the answer needed a seventeenth bit the word couldn't hold. This is how the machine adds numbers bigger than one register — add the low halves, then add the high halves with the carry, using ADDX. You won't need it for colours, but it's why .b, .w and .l exist.
If it doesn't work
- A neighbouring channel changes too. A sum carried out of one channel into the next —
$0f00 + $0100overflows the red nibble. Keep each addend inside the channel you mean to change. - The colour is far from what you predicted. A size suffix slipped —
.bonly touches the low eight bits (green and blue), so a.badd never reaches the red channel. - It won't assemble.
addandsubboth need a size and two operands, likeadd.w #$000f,d0. A missing size or operand is the usual cause.
What you've learnt
Arithmetic is ADD and SUB, each working on a size you choose — .b, .w, .l — and each leaving the flags set for a branch to read. A sum that overflows its size sets the carry, which is how the machine chains sizes together for bigger numbers. Add and branch are two halves of one move: compute, then decide on the result.
What's next
Addition treats a value as a number. Sometimes you want to treat it as a row of independent switches instead. Next — Working With Bits — AND, OR and EOR set, clear and flip individual bits, the tool for packing flags and reading hardware registers.