N64 Programming/CPU overview

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

RegistersEdit

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).

CoprocessorsEdit

In addition to the CPU, there are three other coprocessors (COPs).

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

Branch delaysEdit

Due to 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'.


ExamplesEdit

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 AdditionEdit

The N64 CPU has an interesting quirk in how it handles addition:

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, i.e.:

LI $A0, 8013 ;A0 = 0x80130000
LW $A1, FFFF($A0) ;load word from 0x8012FFFF
Last modified on 2 May 2009, at 16:30