N64 Programming/Memory mapping

CPU Main Memory (RDRAM) (2)           CDROM (8) |       |                               |  --     |                    |  Registers (1)      Cartridge (ROM) (4)

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
[0361:d824] 8002A14C: AND    k1[0000FF03],k1[0000FF03],at[FFFF00FF] [0369:d825] 8002A150: OR     k1[00000003],k1[00000003],t1[0000FF00] [3c09:a430] 8002A154: LUI    t1[0000FF00],FFFFA430h

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: