Skip to content

Instantly share code, notes, and snippets.

@Dragorn421
Created September 24, 2022 16:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Dragorn421/8052ff00f9633873b89bf9170bdafcac to your computer and use it in GitHub Desktop.
Save Dragorn421/8052ff00f9633873b89bf9170bdafcac to your computer and use it in GitHub Desktop.

Memory

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.

Permanently loaded code

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

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.

buffers 1/3: zbuffer

This reserves a part of RAM for the depth buffer, needed for rendering.

More information on the Z-buffer format:

buffers 2/3: gfxbuffers

This reserves RAM for the RSP to do its work when tasked with rendering graphics.

Space for two GfxPools 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).

buffers 3/3: heaps

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.

Memory usage initialization during boot

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.

Color Framebuffers

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

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

The Debug Heap

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.

Memory usage within the System Heap

Arenas

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 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 wrapper

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.

TwoHeadArenas

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.

The GameState TwoHeadArena

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.

Memory usage within the GameState Heap

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.

Play state usage of the GameState Heap

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment