Jump to content

N64 Programming/CPU overview

From Wikibooks, open books for an open world
The NEC VR4300 at the heart of the N64.

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

Registers

[edit | edit source]

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 | edit source]
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 | edit source]

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 | edit source]

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 | edit source]

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