N64 Programming/CPU overview

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

The NEC VR4300 at the heart of the N64.

Registers edit

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 edit

The RCP, also known as COP2.

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 edit

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 edit

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 edit

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