N64 Programming/CPU overview

Processor: 93.75 MHz NEC VR4300, based on MIPS R4300i-series 64-bit RISC CPU (info)

Registers
32 general registers, of which Nintendo has given a naming convention.


 * R0 = always zero. Any attempts to modify this register silently fail.
 * T0-T9 = scratch registers. CPU RAM.
 * S0-S7 = registers saved upon function protocol. Trash at will if you know how.
 * A0-A3 = parameter passing to subroutines. Formal but not rigid.
 * RA   = return address from subroutine. Not pulled from 'stack'. Change at convenience.
 * V0-V1 = arithmetic values, function return values.
 * SP   = stack pointer. Informal.
 * AT   = assembler temporary. Free use.

These are formal definitions but not strictly enforced, save for R0 which is hardwired.

Instructions are WORD sized (32-bits).

Coprocessors
In addition to the CPU, there are three other coprocessors.


 * COP0 = memory management unit (MMU). Better known as 'virtual memory'.
 * COP1 = floating-point unit (FPU).
 * COP2 = video coprocessor (RCP).

Branch delays
When performing branches, a 1-cycle delay is incurred. This means a branch instruction such as beq r0,r0,8006D234h would also execute the instruction following it. There is a limit on which opcodes can be placed in the delay slot.

Note that 'beq r0,r0,TARGET' is effectively 'bra TARGET'.

Examples
The following machine code is found in Mario Golf. [1120:0027] 800B0130: BEQ    t1[800FBBD0],r0[00000000],800B01D0h [0000:0000] 800B0134: NOP NOP (no operation) is executed before the next instruction is fetched from memory.

[0c02:c0d7] 800B01B4: JAL    800B035C [0120:2021] 800B01B8: ADDU   a0[00000038],t1[800FBBD0],r0[00000000] An ADDU (add unsigned) is executed as the program counter is set to address 800B035C.

For our hobbyist purposes, it is much safer to always inefficiently waste a NOP (no operation) in the slot, and optimize later if necessary.

Signed Addition
Care is required when using the unsigned family of MIPS instructions as the signedness refers to only whether the instruction will generate a trap on overflow. MIPS will sign-extend an operand regardless of whether ADDI or ADDIU is used: LI $A0, 8013 ;A0 = 0x80130000 ADDIU $A0, $A0, FFFF ;A0 = 0x8012FFFF Essentially when adding a value greater than 0x7FFF, even with the unsigned addition instructions, the effect is as if the value were negative. This also affects relative addressing, e.g.: LI $A0, 8013 ;A0 = 0x80130000 LW $A1, FFFF($A0) ;load word from 0x8012FFFF