The machine: RISC5
RISC5 is the processor Wirth designed for Project Oberon when he decided that commercial processors had become too complicated to explain to students. The description runs to about forty pages, the implementation to roughly two hundred lines of Verilog.
Registers
Sixteen 32-bit registers: R0…R15. All are equal as far as the hardware is
concerned; the division of labour is the compiler's convention, written down in
ORG.Mod:
| register | role |
|---|---|
R0…R11 | computation, parameters, temporaries |
R12 (MT) | module table; traps go through it too |
R13 (SB) | static base: the start of the current module's data |
R14 (SP) | top of stack |
R15 (LNK) | return address from a procedure |
Plus four flags — N (sign), Z (zero), C (carry), V (overflow) — and a
separate register H, which receives the high word of a product and the
remainder of a division.
The program counter addresses words, not bytes, and it is twenty-two bits wide. That gives an address space of four million words, sixteen megabytes; the system has one megabyte of actual memory.
Four instruction formats
The whole instruction set fits into four formats, told apart by the two top bits.
F0 00uv | a | b | op | (unused) | c register-register
F1 01uv | a | b | op | im (16) register-immediate
F2 10uv | a | b | off (20) load and store
F3 11uv | cond | off (24) branch
The fields: a is the destination, b and c the operands, op the operation
code, u and v modifiers. There are exactly sixteen operations:
MOV LSL ASR ROR AND ANN IOR XOR ADD SUB MUL DIV FAD FSB FML FDV
ANN is "and-not": b AND NOT c. Multiplication and division take tens of
cycles and stall the pipeline while they run. The last four are single-precision
floating point, in hardware.
The u and v bits refine the operation where refinement is needed:
ADDwithu=1adds with carry,SUBwithu=1subtracts with borrow;MULwithu=1is the mixed multiply,DIVwithu=1is unsigned;MOVwithu=1reaches theHregister and the flags;FADwithu=1converts integer to float, withv=1converts back.
For most operations u and v are not read at all. That is not a guess: we
checked it by execution — ran every form beside the plain one on the same
operands and combined the results with exclusive-or. Twenty-seven of thirty-two
comparisons came out zero.
Branches
Format F3 gives the condition in four bits. The top bit inverts; the lower
three choose what is examined:
| code | condition | code | condition |
|---|---|---|---|
| 0 | MI negative | 8 | PL |
| 1 | EQ zero | 9 | NE |
| 2 | CS carry | 10 | CC |
| 3 | VS overflow | 11 | VC |
| 4 | LS lower or same | 12 | HI |
| 5 | LT less | 13 | GE |
| 6 | LE less or equal | 14 | GT |
| 7 | always | 15 | never |
A branch may take a displacement or a register, with or without a link. "With a
link" means the address of the next instruction is placed in R15 — that is how
a procedure is called.
Traps
RISC5 has no separate trap instruction. A trap is a branch through the MT
register with a link, and into the instruction's unused bits the compiler puts a
payload: bits 23–8 carry the position in the source text, bits 7–4 the error
number. The hardware never reads those bits; the handler digs them out — from
the very instruction that tripped.
That is why an error message looks like this:
pos 6734 TRAP 4 in ORB at 0001EC10
Number 4 means a NIL dereference. Number 1 is an array bound violation:
precisely the check the code generator inserts before every index operation.
Memory and devices
The map is simple:
| range | what |
|---|---|
000000…0FFFFF | RAM, one megabyte |
0E7F00…0FFFFF | framebuffer, 1024×768 pixels, one bit each |
FFE000…FFFFBF | ROM with the boot loader |
FFFFC0…FFFFFF | device registers |
There are not many devices: a timer, LEDs, a serial port, SPI for the SD card, mouse and keyboard. The mouse is read as a single word: coordinates in the two low fields of twelve bits each, buttons in bits 24–26, keyboard readiness in bit 28.
The framebuffer is laid out unusually: rows run bottom-up. The first word of the buffer is the bottom row of the screen. The naive layout gives an upside-down picture, and everyone writing for this machine for the first time trips over it.
What the machine does not have
No memory management unit, no protection rings, no division between kernel and user mode. Any instruction can write any word of memory. No cache, no branch prediction, no out-of-order execution — an instruction's execution time comes from a table and is always the same.
This is a position rather than an omission: the machine is meant to fit in one head entire. What that costs in practice you will see in lab 4.