Skip to content
RetrogamingDeep Dive Published Updated 7 min readViews unavailable

Nintendo 64 R4300 TLB: Virtual Address Translation and Exceptions

Trace Nintendo 64 R4300 virtual addresses through paired TLB entries, permissions, direct-mapped segments, and precise translation exceptions.

The Nintendo 64’s R4300 CPU does not treat every software address as a direct physical offset into RAM. The processor has virtual address regions and a translation lookaside buffer (TLB) that maps virtual pages to physical pages with access attributes. Kernel code configures these mappings, while user-mode software can fault when it touches an unmapped or disallowed page. An emulator that masks every address into RDRAM may run simple games but will mis-handle exceptions, cache behavior, and software that relies on the CPU’s memory-management rules.

The TLB is available to privileged software for address mapping and protection, but that does not mean an N64 game runs like a demand-paged desktop process. Nintendo’s programming manual says N64 software runs in kernel mode with 32-bit addressing, and its TLB guidance explicitly says the system provides no support for handling and recovering from TLB misses. Treat the TLB as real CPU architecture and a useful tool for specialized mappings, while distinguishing that from an assumption that ordinary game software can transparently refill missing pages. It also affects debugging: a faulting virtual address must be interpreted through current TLB state before it can be correlated with a physical bus access.

Address regions and direct-mapped segments

The R4300 uses MIPS address segments with different translation and cache behavior. In the conventional 32-bit kernel model, KSEG0 is a cached direct-mapped region and KSEG1 is an uncached direct-mapped region; both map a virtual address to physical space by removing the segment prefix within the supported physical-address range. User segments and other regions use TLB translation according to privilege and configuration.

Do not apply a direct-map mask indiscriminately to every virtual address. Segment decoding and privilege state determine whether translation is bypassed, which cache attributes apply, or whether the address should cause an exception. A fast memory access path may optimize common KSEG0/KSEG1 cases, but it must preserve the same visible rules as the architectural path.

Virtual address translation also differs from console bus mapping. A translated physical address may land in RDRAM, ROM, MMIO, or an unmapped region depending on the N64 physical map. The TLB answers the virtual-to-physical and permission question; the system bus then determines which device responds. Keep those two stages separate to avoid confusing a TLB miss with an invalid cartridge address.

Paired-page TLB entries

R4300 TLB entries describe a pair of virtual pages, conventionally represented through EntryHi and two EntryLo values, with a page mask and address-space identifier. Each half has a physical page number and attributes such as valid, dirty/write permission, and cache coherency. A global bit can make an entry match independently of the current address-space identifier. Page size influences how VPN and offset bits are divided.

The processor provides instructions for probing and reading/writing TLB entries, including the operations conventionally named TLBP, TLBR, TLBWI, and TLBWR. A software write to a TLB register is not equivalent to an immediate flat-map update unless the emulator also updates the translation structure with the correct mask and permissions. The current Mupen64Plus implementation is a useful source for how a mature emulator represents the 32 entries and derives read/write lookups, but architecture behavior should be grounded in the R4300 contract rather than copied blindly from one implementation.

For each access, the matching logic considers the virtual page, page size, ASID/global rules, and relevant half of the entry. A match with valid clear, or a write with dirty permission absent, leads to a different exception path from no matching entry. These distinctions affect software handlers, fault addresses, and retry behavior.

Distinguish TLB exception classes

A TLB miss occurs when no entry matches the virtual address and current address-space context. A TLB invalid exception occurs when a matching translation is present but its valid bit is clear. A modified exception occurs when a store targets a valid translation that is not writable under the entry’s dirty/write attribute. Load/fetch and store paths can report different exception codes and update processor state differently.

When debugging a crash, record the virtual address, access type, PC, privilege mode, EntryHi, PageMask, relevant EntryLo half, and exception registers. Then determine whether the failure is a miss, invalid page, permission violation, or a later bus/device error. A generic “TLB exception” message is not enough to diagnose a stale mapping or incorrect ROM bank.

At the processor-architecture level, an exception handler can inspect the fault state and software designed for a refill path can install a mapping and retry. That is not the standard Nintendo 64 operating-system contract: Nintendo’s SDK manual calls a TLB miss an unrecoverable fault to the N64 system. Do not infer demand paging or transparent recovery for ordinary N64 software from the CPU’s exception mechanism. The emulator must still leave the architecture-defined exception context intact, including the faulting address and cause information; if it handles the fault internally or translates it into a host memory exception, the guest must observe the behavior supported by its software environment.

The CP0 state is the evidence needed to reconstruct that path. Preserve BadVAddr for the faulting virtual address, the exception cause and exception program counter, and the relevant Context/EntryHi fields used by software to locate or refill a mapping. Exception entry behavior and vector selection depend on processor status and exception class. Do not implement every translation fault as a host segmentation fault followed by a generic reset; the guest handler may be expected to repair a miss and resume at the original instruction.

TLB updates, caches, and invalidation

TLB writes and address-space switches must update both the architectural registers and any optimized emulator lookup tables. If a fast LUT caches virtual translations, invalidate or replace entries when software writes a mapping, changes page masks, switches ASID, or flushes translations. A stale host-side fast path can make code continue reading an old physical page after guest software updated the TLB.

The TLB and CPU caches are separate mechanisms. A valid translation says which physical address and cache attributes apply; it does not mean the cached data is coherent with DMA or another bus master. N64 software may need cache maintenance around DMA. Keep TLB translation and R4300 cache-line behavior separate in both implementation and diagnosis.

TLB probe and replacement behavior deserves its own test. A probe should reflect the current entry set, page-size matching, and address-space identifier rules; a read or indexed write should preserve the architecturally visible register format. Random replacement must use the processor’s defined/randomized state rather than a host map iteration order if guest software can observe the replacement result. Tests should also verify that changing a mapping invalidates any fast translation cache without unnecessarily invalidating unrelated physical data-cache state.

Test both cached and uncached segment behavior, TLB hits, misses, invalid entries, write-protection/modified faults, page-size masks, ASID/global matching, and TLB probe/read/write operations. Add tests that change an entry and immediately repeat a memory access so stale lookup caches become visible. Use physical addresses that cross device regions to validate the second stage of bus decoding.

Emulator validation and practical diagnostics

Use a small diagnostic program or existing CPU test suite to construct TLB entries with controlled attributes and perform one load or store at a time. Record expected exception state and mapped data. Compare with hardware or a trusted reference where possible. For integration tests, exercise game startup and task switching, but do not rely on boot success to prove permission and invalid-page behavior.

When an N64 title fails only under a particular core or memory expansion configuration, capture the virtual faulting address and TLB state before changing RDRAM size, ROM mapping, or cache settings. A patch that forces the address to a physical pointer may conceal an emulator bug and break other games. Keep interpreter and dynamic recompiler behavior consistent: optimized translation blocks need to honor mapping changes and exception boundaries.

Acceptance should include an architectural test matrix for direct segments and translated pages, exception-cause verification, immediate mapping updates, ASID changes, and memory accesses crossing the CPU-to-device map. Preserve the test binary hash, emulator revision, and trace. Document any hardware-specific behavior that has not been verified rather than presenting an emulator convention as a silicon guarantee.

The R4300 TLB is a translation and permission mechanism, not a generic ROM mapper. Separating virtual translation, exception state, cache attributes, and physical bus decoding helps explain N64 faults precisely and prevents shortcuts from turning one title-specific fix into a systemic compatibility regression.

Related:

Sources:

Comments