The N64 has 4 MB of RAM, which can be extended to 8 MB by putting an expansion pack into the controller.
Many OoT versions only require 4 MB of RAM, but the OoT MQ Debug version does require 8 MB.
The boot and code files contain code and data that is permanently loaded, until a reset or a shutdown.
They are loaded at the start of RAM, using it up to _codeSegmentEnd = 0x801759c0
(see build/z64.map
). That is nearly 1.5 MB used.
The buffers
segment in the spec is not a file actually loaded from the ROM (it could as well not be put into the ROM at all).
Its contents indicate how RAM past the code file is used.
It is split into three parts, zbuffer
, gfxbuffers
and heaps
.
All these three parts define symbols in the BSS section of the segment, which is used for uninitialized data. This means that despite referencing large amounts of memory, no data is stored into the file, and having this buffers
file doesn't affect the ROM size much if at all.
This reserves a part of RAM for the depth buffer, needed for rendering.
More information on the Z-buffer format:
- Manual 12.8.1 Image Location and Format
- Manual 15.4 Z Stepper
- Manual 15.5.8-10 Z Calculation, Z Image Format, Z Accuracy
This reserves RAM for the RSP to do its work when tasked with rendering graphics.
Space for two GfxPool
s is also reserved. One GfxPool
stores graphics commands and some data for rendering, and the game needs two for double buffering (building up commands while the previous set of commands is being rendered).
This reserves RAM for the audio code with gAudioHeap
, used to store various pieces of data related to audio.
Finally, gSystemHeap
, which comes at the end of the buffers
segment, marks the start of RAM yet unused.
gSystemHeap = 0x802109e0
means 2.06 MB of RAM are taken and the rest is available for code to use as it wishes.
As part of the game startup ("boot sequence"), the Main
function from the code file is called.
Among many interesting things, it first calls SysCfb_Init
.
SysCfb_Init
determines where in RAM the two color framebuffers are stored. There are two for double-buffering, one is displayed while the other is being worked on during rendering.
The location it chooses depends on how much RAM there is.
If there is 4 MB of RAM only, the framebuffers will be put at the end of the available RAM, ending at 0x80400000
. Since each framebuffer is 2*320*240 bytes = 150 KB
, that puts the framebuffers at 0x803b5000
.
If there is 8 MB of RAM, the framebuffers end at 0x8044BE80
(meaning they start at 0x80400e80
).
The system heap starts at gSystemHeap
(from the buffers
segment), and ends at the color framebuffers.
With 8 MB of RAM, this means the system heap extends from 0x802109e0
(gSystemHeap
) to 0x80400e80
(the start of the framebuffers).
With 8 MB of RAM, the debug heap is located in an otherwise unused part of RAM, the part after the framebuffers. It takes only part of that space.
It is only used by the debug camera.
The game partly handles memory with arenas.
An arena is given a chunk of memory to manage, and provides functions to allocate or free memory inside that chunk. It keeps track of what ranges are used or not.
In the context of OoT hacking, arenas are also more generally refered to as "heaps", but not every "heap" is this kind of arena.
The system heap is an arena, which is given the RAM range described above, by Main
calling SystemHeap_Init
.
It is permanent, as the arena persists without being initialized again throughout execution: it is only initialized once, at startup.
For example, the gGameInfo
data is allocated on the system heap (permanently, it is never freed).
In the main loop handling game states, the memory for the instance of the game state to run is allocated on the system heap.
The GameAlloc
system is a simple wrapper to allocate from the system arena. Its function is to keep track of what has been allocated through it, with the purpose of freeing up these allocations later.
It is initialized when a game state starts, and all memory reserved through it is released when the game state ends.
A TwoHeadArena only provides ways to allocate memory, none to free.
Memory can be allocated either from the head (beginning) or the tail (end) of the free range of memory left, taking away from that range accordingly.
It does not track the details of what has been allocated where, only the head and tail of the memory range available for allocation.
Memory for the GameState heap (a TwoHeadArena) is reserved when a game state starts running, through the GameAlloc
system. That means it is freed when the game state ends.
Because it is a TwoHeadArena, any allocated memory will stay allocated and persist, until the memory allocated for the heap itself is released as the game state ends.
In practice, all allocations in this GameState TwoHeadArena are from the tail, mostly done with calls to GameState_Alloc
.
The default size for the GameState heap is 1 MB.
The game state may change that size by an early call to GameState_Realloc
(before allocating anything). Only the play state does so, to reserve 1.83 MB instead of the default.
How the GameState heap is used is specific to the running game state.
Most states only use it to allocate space to load files to, from the ROM. For example, the game state for showing the N64 logo on startup loads the nintendo_rogo_static
file from the ROM, to RAM allocated with GameState_Alloc
.
A few states do a bit more, but it's the play state which makes the most use of it.