The M68K instruction set

The shape every M68K instruction has, the families the mnemonics fall into, and the letters glued on the end of one: the a of movea, the q of addq and the eq of beq.

Every program so far has been written in the same shape, and this is it: an M68K instruction is a mnemonic, an optional size, and at most two operands:

    mnemonic.size source, destination

The destination is the operand on the right, and it is the one that gets written. add.l d1, d0 leaves the sum in d0 and d1 untouched. sub.l d1, d0 computes d0 - d1, source subtracted from destination, which is the order you have to keep in your head, because the operand you read first is the one taken away.

d0 comes out at 7 and d3 at FFFFFFF9, which is -7: the same two numbers, subtracted the other way round. MIPS and RISC-V name all three registers (sub t0, t1, t2), so nothing of theirs is overwritten unless you say so; on the M68K one of the two operands always is.

The families

About fifty mnemonics, in eight groups. You will use the first two lines of this table for most of what you write.

what it doesthe instructions
move datamove, movea, moveq, movem, lea, pea, exg, swap, clr
arithmeticadd, sub, muls, mulu, divs, divu, neg, ext
logicand, or, eor, not
shifts and rotateslsl, lsr, asl, asr, rol, ror
single bitsbtst, bset, bclr, bchg
compare and testcmp, cmpa, cmpi, cmpm, tst
go somewhere elsebra, b<cc>, db<cc>, dbra, s<cc>, jmp, jsr, bsr, rts
stack frames and otherlink, unlk, trap, nop

The whole list, with the addressing modes and sizes each one takes and the flags it writes, is on the M68K documentation pages, and every instruction there has a program you can run.

The letters on the end

An M68K mnemonic is often a base name with a letter glued on, and the letter says which version you want. Learn the five and a name you have never seen becomes readable.

  • a, address register. movea, adda, suba, cmpa are the versions whose destination is an address register. You rarely write them, because the assembler picks them when it sees a0 on the right of a move, add, sub or cmp.
  • i, immediate. addi, subi, andi, ori, eori, cmpi take a plain number as their source. add takes one too, so add.l #5, d0 and addi.l #5, d0 are the same thing written twice.
  • q, quick. addq and subq take a source between 1 and 8 and fit in a shorter encoding; moveq takes one between -128 and 127.
  • m, memory. cmpm compares two memory operands, which the plain cmp cannot.
  • s and u, signed and unsigned. muls against mulu, divs against divu.

All four registers come out at 00000069, which is 105. Try changing addq.l #5, d1 to addq.l #9, d1 and pressing Build: addq refuses it, because 9 does not fit in the three bits the encoding gives the source.

The conditions glued on

Three of those mnemonics are fourteen mnemonics each, because a condition code of two letters is part of the name:

  • b<cc> label, branch if the condition holds. beq, bne, blt, bcc, and so on.
  • db<cc> dn, label, decrement and branch, the counted loop instruction.
  • s<cc> destination, set the destination byte to $FF if the condition holds and $00 if it does not, without branching anywhere.

The conditions are eq, ne, gt, ge, lt, le, hi, ls, cc, cs, pl, mi, vc, vs, and cc and cs can also be written hs and lo. s<cc> takes two more, st and sf, which are always true and always false. Each one reads a combination of the flags, and they are the subject of "The condition code register", later in this course.

d1, d4 and d5 come out at 000000FF, d2 and d6 at 00000000. s<cc> writes one byte and leaves the rest of the register alone, which is why the answers are FF rather than FFFFFFFF. It is how you turn a comparison into a value without a branch, the way x = (a == b) does in C.

The shifts and rotates glue a letter on the same way, l for left and r for right: ls<d> is the logical shift, as<d> the arithmetic one, ro<d> the rotate.

d0 is 16, d1 is 8, d2 is 00000003 and d3 is C0000000. A shift drops the bit that falls off the end, a rotate puts it back in at the other end.

What the assembler does with all this

It turns each mnemonic into the opcode for the exact combination you wrote, and the combination matters: move.l d0, d1, move.l #5, d1 and move.l (a0), d1 are three different encodings of one mnemonic. It works out the expressions, replaces labels with addresses, and swaps in the a versions when the destination is an address register.

What it will not do is guess. Each operand of each instruction accepts a fixed set of addressing modes, and the documentation page of an instruction lists them: swap takes Dn and nothing else, lea takes an address and An, eor needs a data register as its source. An operand outside that set is a build error naming the line, not an instruction that quietly does something else.

Your turn

Two instructions, no branch. The test starts d0 and d1 both at 7, and wants $FF in d2 when the two registers are equal.

Show solution

The second one starts d0 at $00001234. Leave d0 multiplied by 8 in d1, using a shift, and d0 with its two words exchanged in d2.

Show solution