-
Notifications
You must be signed in to change notification settings - Fork 2
F3DEX
Mario Kart 64 uses the F3DEX RSP microcode program. This is the successor to the Fast3D microcode used in earlier games such as Super Mario 64.
- RSP is the "Reality Signal Processor", the Nintendo 64's graphic processor (GPU). Unlike many consoles, the N64 has a programmable GPU that works more like a coprocessor.
- RSP is made up of two parts: RCP (Reality CoProcessor) and RDP (Reality Data Processor), but this isn't usually relevant except that you may see those terms in some games' debug screens.
- "Reality" comes from N64's code name, "Project Reality".
- The RSP does, however, have a highly parallel design suitable for 3D graphic rendering, unlike most CPUs.
- RSP is made up of two parts: RCP (Reality CoProcessor) and RDP (Reality Data Processor), but this isn't usually relevant except that you may see those terms in some games' debug screens.
- Microcode is the program running on the RSP. It's responsible for actually drawing the graphics on the screen.
- In most first-party N64 games (Mario Kart included), the game writes high-level commands into a buffer, and the RSP reads from that buffer. The commands tell it how to render the current frame.
- While some games use a more "traditional" 3D model format, Nintendo tended to opt for a faster method, and store most "models" as programs written directly in F3DEX code.
- Almost all games use one of the few Nintendo-provided microcodes. Custom microcodes were not allowed until near the end of the N64's life.
- In most first-party N64 games (Mario Kart included), the game writes high-level commands into a buffer, and the RSP reads from that buffer. The commands tell it how to render the current frame.
- For the most part, the microcode program itself is uninteresting. The interesting part is the bytecode it interprets, referred to as "F3DEX code" throughout these documents.
The RSP works alongside the main CPU, executing a program from main memory (which is usually provided and/or generated by the main CPU) which draws something into a framebuffer, which is then displayed on the screen.
The RSP shares main memory with the main CPU, and also has a small cache of its own. This cache is divided into three parts: program cache (where the actual microcode program is stored), vertex cache (defines polygon vertices), and texture cache (stores texture graphics). Each of these caches is a measly 4096 bytes, which is the main reason N64's graphics are so weak. (Accessing main memory is slow, since it must be shared with the main CPU, and the memory itself is not very fast to begin with.)
The F3DEX program, like most of Nintendo's microcode programs, reads and interprets rendering instructions from a buffer filled in by the main CPU. These instructions are considered a program, though they aren't Turing-complete - there are no branching or flow control instructions. Most instructions map roughly to one OpenGL command.
All(?) F3DEX code instructions are 8 bytes long, with the first byte specifying the opcode, and the following bytes interpreted in some way specific to that opcode.
F3DEX code supports a limited form of subroutine, analogous to OpenGL display lists. They can be called from other display lists, and return to the caller.
The F3DEX vertex cache is 32 entries (compared to Fast3D's 16). All drawing operations that use vertices read them from this cache.
F3DEX code rarely addresses memory directly. Instead, it uses indices into a segment table. These indices are 32-bit values, in which the highest byte specifies which segment, and the other three bytes are a byte offset into that segment:
struct rsp_ptr {
uint8_t segment;
uint32_t offset : 24;
};
This design allows for quick remapping of memory regions. The main CPU need only change the pointer in the segment table to remap a segment. This can be used e.g. for mass texture animation (by remapping the texture segment every frame). Every segment has a fixed size of 24 megabytes, since that's the largest offset that can fit into the low 3 bytes of the pointer. Since the N64 has only 4 to 8MB of RAM, the segments will inevitably overlap. Aside from this limit, there's no "end" to a segment. Most games use each segment for a particular purpose. Mario Kart is no exception; technically, any track resource can be loaded into any segment, but all existing tracks use the same layout:
Segment | Usage |
---|---|
0x00 | Always mapped to 0x00000000 . Could be used to address main memory directly, but most likely isn't used at all. |
0x01 | Points to the frame buffer the RSP is currently rendering into, followed by the F3DEX code it's executing (XXX verify). Alternates between 0x1263D0 and 0x0FD860 each frame, implying a size of 0x28B70 . |
0x02 | Unknown. In some tracks, such as Wario Stadium, changing this will crash the game. |
0x03 | Some 2D object graphics. In Mario Raceway, this appears to contain only the tree graphics. |
0x04 | The track's vertices. |
0x05 | The track's textures. |
0x06 | Used for a wide variety of resources. |
0x07 | (Some of) track's display lists. |
0x08 | Mapped to 0x00000000 . |
0x09 | List of texture file addresses and sizes. Some tracks also store the display list pointers here. |
0x0A | Mapped to 0x00000000 . |
0x0B | Mapped to 0x00000000 . |
0x0C | Mapped to 0x00000000 . |
0x0D | Related to 2D graphics such as trees, HUD, and sky. |
0x0E | Mapped to 0x00000000 . |
0x0F | Used for temporary storage while loading from ROM. During play, points to the processed surface map data, but isn't used. |
(Note, physical RAM is at addresses 0x00000000
- 0x007FFFFF
. Mario Kart, like most games, accesses it through a cached mirror at 0x80000000
- 0x807FFFFF
; however, the RSP doesn't use this cache.)
The segment pointer table is at 0x80150258
in RAM.