-
Notifications
You must be signed in to change notification settings - Fork 2
F3DEX Instruction Set
These are all known instructions interpreted by the F3DEX program. Many are not well understood. Much of this information is accumulated from various sources over the years. These names are probably not what most other sources use either.
Every instruction (except 0xE4
) is 8 bytes; the first specifying the instruction itself, and the others being used/ignored depending on the instruction. Every instruction must be at an 8-byte-aligned RAM address.
struct f3dex_matrix {
uint8_t opcode; // 0x01
uint8_t unused : 5;
uint8_t push : 1; //0=discard current mtx, 1=push current mtx
uint8_t mult : 1; //0=multiply with current mtx, 1=replace current mtx
uint8_t mtx : 1; //0=modelview mtx, 1=projection mtx
uint16_t size; //size of matrix data, always 0x0040
rsp_ptr address; //location of matrix data.
};
The matrix data is 16 32-bit signed fixed-point scalars. The first half is the whole parts, and the second half is the fractional parts. (XXX what?)
struct f3dex_loadvtx {
uint8_t opcode; // 0x03
uint8_t index : 7; //vtx cache index to load to.
uint8_t unused : 1;
uint8_t nVtxs : 6; //number of vertices to load.
uint16_t nBytes :10; //number of bytes to load (redundant?)
rsp_ptr src; //address to load from.
};
Effectively nVtxs
and nBytes
together can be treated as a uint16_t
which is numberOfVertices * 0x410
.
struct f3dex_call {
uint8_t opcode; // 0x06
uint8_t unused[3];
rsp_ptr address; //address to call.
};
struct f3dex_tri2 {
uint8_t opcode; // 0xB1
uint8_t tri1[3]; //first triangle
uint8_t unused;
uint8_t tri2[3]; //second triangle
};
Draw two triangles, which can be completely independent of eachother. The elements of tri1
and tri2
are indices into the vertex cache; specifically, the high 7 bits are the index, and the lowest bit seems to be unused.
struct f3dex_line {
uint8_t opcode; // 0xB5
uint8_t unused[5];
uint8_t vtxs[2];
};
vtxs
works the same as in Draw2Triangles
.
These set up various rendering parameters. More research is needed!
Apppears at the beginning of several display lists.
struct f3dex_setsegptr {
uint8_t opcode; // 0xBC
uint16_t index; //Table index * 4 (offset into segment memory)
uint8_t area; //RSP RAM area to write to; 06 = segment table
uint32_t ptr; //The pointer to write
};
(XXX this is probably wildly incorrect)
struct f3dex_popmtx {
uint8_t opcode; // 0xBD
uint8_t unused[6];
uint8_t which; //0=modelview, 1=projection; upper bits likely unused
};
Works just like Draw2Triangles
, but only the tri2
field is used.
struct f3dex_texrect {
uint8_t opcode; // 0xE4
uint16_t x1 :12; //top left X coord
uint16_t y1 :12; //top left Y coord
uint8_t unused : 5;
uint8_t tile : 3; //"tile descriptor index"
uint16_t x2 :12; //bottom right X coord
uint16_t y2 :12; //bottom right Y coord
uint16_t S, T; //top left texture cooords
uint16_t SX, TY; //change in S per change in X, and T per Y
};
Only case of an instruction being more than 8 bytes. Unsure what this actually does.
struct f3dex_loadtlut {
uint8_t opcode; // 0xF0
uint16_t S :12; //Texture S coord
uint16_t T :12; //Texture T coord
uint8_t unused : 5;
uint8_t tile : 3;
uint32_t wtf : 24; //"the s coordinate of the lower right corner of the texture tile and some sort of fixed point slope"
};
struct f3dex_settilesize {
uint8_t opcode; // 0xF2
uint16_t S1 :12; //Top left S coord
uint16_t T1 :12; //Top left T coord
uint8_t unused : 5;
uint8_t tile : 3;
uint16_t S2 :12; //Bottom right S coord
uint16_t T2 :12; //Bottom right T coord
};
Similar to LoadTLUT
. Unclear how this works; the 5th byte seems to specify the texture width in pixels, minus one (e.g. 0x3F = 64).
struct f3dex_settile { //byte bits comment
uint8_t opcode; // 0 xxxxxxxx 0xF5
uint8_t format : 4; // 1 xxxx....
uint8_t size : 2; // 1 ....xx..
uint8_t line : 2; // 1 ......xx
uint16_t tmem; // 2 xxxxxxxx
// 3 xxxxxxxx
uint8_t unused : 5; // 4 xxxxx...
uint8_t tile : 3; // 4 .....xxx
uint8_t palette: 6; // 5 xxxxxx.. might only be 4 bits? unclear...
uint8_t TFlags : 2; // 5 ......xx
uint8_t TMask : 6; // 6 xxxxxx..
uint8_t SFlags : 2; // 6 ......xx
uint8_t SMask : 4; // 7 xxxx....
uint8_t SShift : 4; // 7 ....xxxx
};
struct f3dex_fillrect {
uint8_t opcode; // 0xF6
uint16_t xpos :10;
uint8_t xfrac : 2; //X position fraction
uint8_t unused;
uint16_t ypos :10;
uint8_t yfrac : 2; //Y position fraction
};
My understanding is that the N64 renders with quarter-pixel accuracy (for antialiasing), so this probably specifies the pixel coordinate to draw at in the framebuffer. No idea what specifies the size; maybe two of these commands specify the corners?
struct f3dex_setcombine {
uint8_t opcode; // 0xFC
uint8_t muxs0[3];
uint8_t muxs1[4]; //probably only 3 bytes after 1 unsued.
};
No idea what this does.
struct f3dex_settextureloc {
uint8_t opcode; // 0xFD
uint8_t format : 3; //0=RGBA, 1=YUV, 2=CI, 3=IA, 4=I
uint8_t size : 2;
uint8_t unused : 3;
uint16_t width :12; //always zero? notes say it's width
rsp_ptr address; //address of image (64-byte aligned)
};