# N64 Programming/Printable version

The current, editable version of this book is available in Wikibooks, the open-content textbooks collection, at
https://en.wikibooks.org/wiki/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.
NOP (no operation) is executed before the next instruction is fetched from memory.
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:
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.:

# Video coprocessor

The RCP (VDP,PPU,video controller) is mainly interesting for polygon crunching and post-filter effects.
Formed from two components - RSP(Reality Signal Processor) and RDP(Reality Drawing Processor).
Texture cache and framebuffers are shared in RDRAM with the CPU.
Actual texture memory is only 4KB -- only allowed to operate on this amount at a time.

## RSP(Reality Signal Processor)

RSP is your transform and lighting unit (TnL). It manipulates world data and textures.
Mathematically, lots of matrices to transform from local data -> world space -> view space (projection w/ z-perspective correction).
Transform means scaling, translating and rotating for polygons, lighting normals, and texture UVs.
Creates primitive lists of triangles and lines for the RDP to render.

## RDP(Reality Drawing Processor)

RDP is the display unit.
Rasterizer, fog, environmental, color blending. Anti-aliasing effects.
Lower-level pixel handler.

## uCodes

The RCP has its own language, dubbed 'uCodes' (256 microcodes).
Think of modern vertex and fragment shaders from OpenGL or vertex and pixel shaders from Direct3D - both stages combined.
R4000 coprocessor (COP2). Each uCode is a string of ASM instructions run by the RSP. Also sets up the RDP batch renderer.
Display lists are a sequence of uCodes defined by the game. This is fed to the RSP.
Note: Emulator authors choose to translate uCodes into higher-level languages.
The programmers can define their own vertex / texture formats. Lighting methods. Overdraw detection and other flexible wizardry.
The microcodes are uploaded to the RCP at run-time. So each game has its own library of drawing functions (some are like DSP1 -> DSP1A -> DSP1B and others are akin to DSP2,DSP3).

## Texture variations

Games use custom formats. Furthermore, linear bitmaps can be any size (320x8, 48x13). 4/8/16/32-bpp is the norm.
The microcode libraries tend to define several 'accepted' formats.
This is a sample list from video plugin authors.
* 16-bit RGBA = 5551. Red,green,blue,alpha (transparency).
* 32-bit RGBA = 8888.
* 4-bit IA = 31. Grayscale intensity (luminosity,brightness) + alpha.
* 8-bit IA = 44.
* 16-bit IA = 88.
* 4-bit I = 40. Grayscale only.
* 8-bit I = 80. Grayscale.
* 16-bit I = (16)(0).
* 4-bit CI = 40. Palette lookup --> 16-bit RGBA or 16-bit IA.
* 8-bit CI = 80. Palette lookup --> 16-bit RGBA or 16-bit IA.
YUV = some other color format. Output is RGB (888) + Full alpha.

# Memory mapping

Naturally, we'd like to keep our data in registers (1 clock cycle).
Having the CPU execute from CDROM would be slow (8 cycles). Even cartridge memory is faster.
Most likely, the game will copy the important code and data to RDRAM to decrease load times. And improve execution.
This is done via Direct-Memory Access (DMA), speedy transfer.
Note that you may also find self-modifying code since we're running from RAM.
Because the code is run off of memory, it is compiled from RAM and not ROM addresses.
Think of 8-bit NES/SMS/GB page offsets.

### Examples

is at ROM $554C (Fushigi no Dungeon 2).

## Memory map

Our memory map goes from $0000:0000-FFFF:FFFF.
COP0 can touch $2000:0000 upwards. Meaning it can change the physical addresses pointed at 24-bit offsets (16 MB). Or 8MB down to 4KB pages.
Generally,
    $0000:0000 = ROM.
    $1000:0000 = ROM.
    $8000:0000 = RDRAM. Code.
    $A000:0000 = RDRAM. Data.
    $A400:0000 = PI,SI. DMA registers.
    $B000:0000 = ROM (DMA, LD).
You'll see this as 'Translation Look-aside buffer' (TLB).

## PI DMA

DMA is a function of all modern systems to quickly move data between different components without involving the CPU. It is important to note that PI DMA memory transfers are done in 64-bit blocks. We can DMA from:
* RDRAM to/from Cartridge (ROM,SRAM,FlashRAM,..)
* RDRAM to/from RCP
DMA registers:
    $A460:0000 = RAM address (address & 0x00FFFFFF)
    $A460:0004 = ROM address (address & 0x1FFFFFFF)
    $A460:0008 = Transfer size (from RAM to cartridge)
    $A460:000C = Transfer size (from cartridge to RAM)
    $A460:0010 = DMA Status
The last two addresses accept the DMA copy length (minus 1--BPL loop) and start the transfer. Which length register you write to depends on the transfer direction you want. Once the transfer has been initiated, the status of the transfer can be checked by reading the status register. The following flags can be used to check the status:
* DMA_BUSY = 0x00000001
* DMA_ERROR = 0x00000008
Below is some example C code to utilize the N64's DMA functions:

# Compiling

The N64 is by no means resource limited, so writing software for it in C is perfectly reasonable. One thing you must keep in mind, though: coding for the N64 requires extensive knowledge of both C and MIPS R4K assembly. However, assembly will only have to be used in small routines that initialize the N64 (or handle exceptions). You also have to be familiar with the GNU toolchain (binutils and gcc namely).

## Initial Steps

Choose a directory that you want the compiled binaries, set this to $PREFIX, and ensure that it exists. For example,
Next, set $GCC to the compiler you are going to use. For example,

## Building binutils

Download the binutils source code (we will use version 2.35.1), and extract it.
Create an out-of-tree build directory, and change into it:
Then run the following commands:
Hopefully everything went well, and now you'll have a binutils package targeting MIPS. Move back to your working directory and now it is time to build GCC.

## Compiling GCC

GCC needs to use some of the binaries that you compiled above, so do the following:
This will make GCC be able to find them.
As with binutils, download the the gcc source (we will use version 10.2.0) and extract it.
Create an out-of-tree build directory, and change into it:
Then run the following commands:
The mips64 toolchain should now be installed in your chosen $PREFIX/bin.

## Coding examples

While coding C for the N64 is no different than any other platform (except that you don't have any libraries at your disposal), you may, at times, have to write to memory mapped registers. The following example (which utilizes DMA) demonstrates this:
You may also have to use inline assembly a fair bit. The function below sets a breakpoint on a region of memory:

## Links

* GCC 10.2.0
* Binutils 2.35.1