Bytes, words and longs
The five ways of writing a number in M68K source, the three sizes an instruction can carry and which one you get when you leave it off, and the instructions that read the same bits as signed or as unsigned.
The overview of this topic is in Assembly basics. The same topic in MIPS, RISC-V, Z80.
Memory is bytes and a register is 32 bits, and neither of them says what the bits mean. On the M68K two things decide that: the size you put on the instruction, and which of the signed or unsigned instructions you picked.
Five ways of writing the same number
The assembler reads four bases and a character literal, and a # in front means the number itself
rather than the address it names.
| written | base |
|---|---|
100 | decimal |
$64 | hexadecimal |
%1100100 | binary |
@144 | octal |
'd' | the ASCII code of d |
All five of those are 100, and the last one is 100 because ASCII gives the letter d the code
$64. A literal of more than one character is packed into as many bytes: #'Hi' is $4869, an H
and an i side by side.
An immediate can also be an expression, worked out by the assembler while it assembles, with labels allowed inside it:
d0 to d4 all come out at 00000064. d5 is 00004869 and d6 is 00000202, which is 514.
Nothing of the expression survives into the program: the assembler puts 514 in the instruction and
the CPU never sees the multiplication.
The three sizes
Every instruction that moves or computes carries one of three sizes, and it says how many bytes of the destination it writes:
.b, one byte, 8 bits, the lowest byte of a register..w, one word, 2 bytes, 16 bits, the lowest word..l, one long, 4 bytes, all 32 bits.
Leave the size off and you get a word. That holds for move, add, sub, clr, neg, not
and ext, and it is the single most common way to write a bug on this machine, because a word is
what you want least often.
| register | value |
|---|---|
d0 | AABB1111 |
d1 | AABB0000 |
d2 | AABB3323 |
d3 | AABBCCDE |
AABB survived all four of them. Write .l on every one of those lines and the whole register
changes instead. A few instructions have no size at all, because there is only one thing they could
mean: lea and movea always work on a long address, swap always on the two words of a register,
btst and its family always on one bit, and the branches on nothing.
moveq, the size that is not a size
moveq #n, dn takes a number between -128 and 127, sign extends it to 32 bits, and writes all
of dn. It exists because that is the common case and it fits in a shorter encoding on a real 68000.
d0 comes out at AABBCCFF and d1 at FFFFFFFF, from the same -1. d2 is 0000007F and d3
is FFFFFF80. moveq #128, d4 does not assemble, because 128 does not fit in a signed byte.
Sign extension
$FF in a byte is 255 read as unsigned and -1 read as signed, and the two mean different things once
that byte is copied into a long: 255 is 000000FF and -1 is FFFFFFFF. Sign extension is
filling the bytes above with copies of the top bit, which is what keeps a signed number the same
number in a bigger box.
ext does it in place on a data register. ext.w extends the low byte into the low word, and
ext.l extends the low word into the whole register, so a byte becomes a long in two steps.
d1 goes 000000F0, then 0000FFF0, then FFFFFFF0, which is -16 the whole way. d2 stays
0000007F, because $7F is positive and extending a positive number fills with zeroes.
Two other places do it for you. moveq sign extends its byte, as above, and a word written into an
address register is sign extended into all 32 bits, which is why move.w #$FFFE, a0 leaves
FFFFFFFE.
Signed or unsigned is your choice, not the register's
Nothing in a register says whether its bits are a signed or an unsigned number. add, sub, move
and the logic instructions do not care: the bits come out the same either way, and only the flags
differ. Where the answer really is different, the M68K gives you two instructions and you pick:
muluanddivuread their operands as unsigned,mulsanddivsas signed.lsrshifts right and feeds in zeroes,asrshifts right and drags the sign bit along.- The branches come in two families,
bhi,bls,bccandbcsfor unsigned comparisons andbgt,ble,bgeandbltfor signed ones. They get their own lecture, "Compare and branch".
d2 comes out at 0002FFFD, which is 196605, and d3 at FFFFFFFD, which is -3. d4 is
FFFFFFFB, which is -5, and d5 is 3FFFFFFB, which is 1073741819. Two instructions, the same
input bits, two right answers to two different questions.
The registers panel has the same choice. The B, W and L buttons in its header cut each register into bytes, words or one long, and hovering a value shows you its signed and unsigned readings side by side.
Your turn
The test starts d0 at $123456F0. Leave the lowest byte of d0 in d1, read as a signed number
and extended to a full long, so d1 comes out at $FFFFFFF0.
Show solution
The second one starts d0 at $0000FFFF and d1 at 3. Multiply them twice: leave the unsigned
product in d2 and the signed product in d3. Since $FFFF is 65535 unsigned and -1 signed, d2
comes out at $0002FFFD and d3 at $FFFFFFFD.