Registers
Let's start with the first component of assembly languages, registers.
Registers are few, very small but extremely fast pieces of memory inside a CPU.
They are usually (depending on the CPU) between 8 and 32 in total, and can hold very few bits (depends on the CPU, but usually 32-bit CPUs have 32-bit registers, etc...), but they are very fast to access as they are physically inside the CPU itself (unlike memory, which is further away and slower to access).
Each register has a name that identifies it.
For example, in M68K there are registers: d0, d1, ..., d7, a0, a1, ..., a7.
In C you write int x = 10; and the compiler finds somewhere to keep x. Assembly has no int x,
the registers are the room you have, they are already named, and you pick which one holds what and
remember it yourself. Most instructions can only compute on registers, so a value that lives in
memory is loaded into a register, worked on there, and written back.
This is what each language of this editor gives you:
- M68K: eight data registers,
d0tod7, and eight address registers,a0toa7, 32 bits each. - MIPS: 32 registers, written with a
$in front,$t0,$s0,$a0, etc...$zeroalways reads 0. - RISC-V: 32 registers as well,
t0,s0,a0and so on, and the one calledzeroalways reads 0 here too. - Z80: one 8 bit register
a, plus the 16 bit pairsbc,deandhl, each of which can also be used as its two 8 bit halves.
General Purpose Registers
These are the most common type of registers and can be used for a wide variety of tasks, like storing data, addresses, etc... Some architectures split this category further into:
- Data registers: they contain numeric data, like numbers, characters, etc...
- Address registers: they contain addresses and are used by instructions that indirectly access memory
- Floating point: they contain floating point numbers
- etc...
The M68K is one of the ones that split them, and some instructions take only one of the two kinds:
add.l d1, d0 adds two data registers, lea writes an address register and nothing else. MIPS and
RISC-V do not split them at all, any of their 32 registers can hold a number or an address, and
nothing but your own code says which.
Watching two registers change
Build this program and press Step five times, keeping an eye on d0, d1 and a0 in the
registers panel. d0 and d1 hold numbers, a0 holds the address of stored (a label is just the
address of whatever comes after it).
move.l #10, d0 ; x = 10
add.l #5, d0 ; x = x + 5
lea stored, a0 ; a0 = &stored
move.l (a0), d1 ; y = *a0
add.l d1, d0 ; x = x + y
simhalt ; stop here, before the data
stored: dc.l 100The panel writes the registers in hexadecimal, so what you see is:
| after this line | d0 | d1 | a0 |
|---|---|---|---|
move.l #10, d0 | 0000000A | 00000000 | 00000000 |
add.l #5, d0 | 0000000F | 00000000 | 00000000 |
lea stored, a0 | 0000000F | 00000000 | 00001012 |
move.l (a0), d1 | 0000000F | 00000064 | 00001012 |
add.l d1, d0 | 00000073 | 00000064 | 00001012 |
0000000A is 10, 0000000F is 15, 00000064 is 100 and 00000073 is 115. The two data registers
hold the values used in the calculation. a0 holds 00001012, the address where the assembler
placed the 100. Instructions vary in size: lea stored, a0 occupies six bytes, including the
address it loads, and each of the other four occupies two. The instructions start at 0x1000
and occupy fourteen bytes. The four-byte simhalt follows them, so the data starts eighteen bytes
after 0x1000.
simhalt ends the run before the data. Without it, the CPU would try to execute the four bytes
that hold 100. The bytes themselves do not tell the CPU whether they are code or data.
Try changing dc.l 100 to dc.l 1000 and stepping through it again. d1 comes out at 000003E8
and a0 does not move, because the address of stored is still the same.
Special Purpose Registers
They are special registers usually managed by the CPU itself and used for specific purposes, there will be a more in depth explanation in future lectures. Three of them exist in one form or another in every architecture:
- Program counter: the address of the instruction the CPU runs next. It is written after every instruction, and a jump is nothing more than writing it on purpose.
- Stack pointer: the address of the top of the stack. On the M68K it is
a7, which you can also write assp. - Status register: the flags, one bit each, saying how the last operation came out. Zero, negative, carry, and so on.
Go deeper: this topic in M68K, MIPS, RISC-V, Z80, x86-64.