From 87d1cf5681201f836d1957524e41e9e0fbb40769 Mon Sep 17 00:00:00 2001 From: Sauraen Date: Mon, 21 Jul 2025 22:05:12 -0700 Subject: [PATCH] Replaced tri strip and fan with SPTriSnake --- f3dex3.s | 92 +++++++++++++---------- gbi.h | 210 +++++++++++++++++++++++++++++++++++++++++----------- rsp/gbi.inc | 7 +- 3 files changed, 221 insertions(+), 88 deletions(-) diff --git a/f3dex3.s b/f3dex3.s index 53c3b79..7cc9ede 100644 --- a/f3dex3.s +++ b/f3dex3.s @@ -254,13 +254,17 @@ otherMode0: // command byte included, same as above otherMode1: .dw 0x00000000 +// TODO: This is unnecessary, the state only has to be saved between the two +// commands making up the texrect command. Could put this in the part of the +// clip buffer that's kept over yields. // Saved texrect state for combining the multiple input commands into one RDP texrect command texrectWord1: .fill 4 // first word, has command byte, xh and yh texrectWord2: .fill 4 // second word, has tile, xl, yl -// First half of RDP value for split commands +// First half of RDP value for split commands. Also used as temp storage for +// tri vertices during tri commands. rdpHalf1Val: .fill 4 @@ -542,6 +546,8 @@ numLightsxSize: // RDP/Immediate Command Mini Table // 1 byte per entry, after << 2 points to an addr in first 1/4 of IMEM +miniTableEntry G_LIGHTTORDP_handler +miniTableEntry G_RELSEGMENT_handler miniTableEntry G_FLUSH_handler miniTableEntry G_MEMSET_handler miniTableEntry G_DMA_IO_handler @@ -595,10 +601,7 @@ miniTableEntry G_BRANCH_WZ_handler miniTableEntry G_TRI1_handler miniTableEntry G_TRI2_handler miniTableEntry G_QUAD_handler -miniTableEntry G_TRISTRIP_handler -miniTableEntry G_TRIFAN_handler -miniTableEntry G_LIGHTTORDP_handler -miniTableEntry G_RELSEGMENT_handler +miniTableEntry G_TRISNAKE_handler // The maximum number of generated vertices in a clip polygon. In reality, this @@ -837,7 +840,7 @@ clipPolyWrite equ $21 // Write pointer within current polygon being clipped viLtFlag equ $9 // Holds pointLightFlag or dirLightsXfrmValid // Misc -nextRA equ $10 // Address to return to after overlay load +nextRA equ $10 // Address to return to after overlay load ovlInitClock equ $16 // Temp for profiling dmaLen equ $19 // DMA length in bytes minus 1 dmemAddr equ $20 // DMA address in DMEM or IMEM. Also = rdpCmdBufPtr - rdpCmdBufEndP1 for flush_rdp_buffer @@ -1159,6 +1162,8 @@ call_ret_common: sb $1, displayListStackLength andi inputBufferPos, cmd_w0, 0x00F8 // Byte 3, how many cmds to drop from load (max 0xA0) displaylist_dma: + li nextRA, run_next_DL_command +displaylist_dma_tri_snake: // Load INPUT_BUFFER_SIZE_BYTES - inputBufferPos cmds (inputBufferPos >= 0, mult of 8) addi inputBufferPos, inputBufferPos, -INPUT_BUFFER_SIZE_BYTES // inputBufferPos = - num cmds .if CFG_PROFILING_A @@ -1169,8 +1174,6 @@ displaylist_dma: move cmd_w1_dram, taskDataPtr // set up the DRAM address to read from sub taskDataPtr, taskDataPtr, inputBufferPos // increment the DRAM address to read from next time addi dmemAddr, inputBufferPos, inputBufferEnd // set the address to DMA read to -dma_and_wait_goto_next_command: - li nextRA, run_next_DL_command dma_and_wait_goto_next_ra: j dma_read_write li $ra, wait_goto_next_ra @@ -1266,26 +1269,29 @@ G_MODIFYVTX_handler: j do_moveword // Moveword adds cmd_w0 to $10 for final addr lbu cmd_w0, (inputBufferEnd - 0x07)(inputBufferPos) // offset in vtx, bit 15 clear -G_TRIFAN_handler: // 17 - li $1, 0x8000 // $ra negative = flag for G_TRIFAN -G_TRISTRIP_handler: - addi $ra, $1, tri_strip_fan_loop // otherwise $1 == 0 - addi cmd_w0, inputBufferPos, inputBufferEnd - 8 // Start pointing to cmd byte -tri_strip_fan_loop: - lw cmd_w1_dram, 0(cmd_w0) // Load tri indices to lower 3 bytes of word - addi $11, inputBufferPos, inputBufferEnd - 3 // Off end of command - beq $11, cmd_w0, tris_end // If off end of command, exit - sll $10, cmd_w1_dram, 24 // Put sign bit of vtx 3 in sign bit - bltz $10, tris_end // If negative, exit - sw cmd_w1_dram, 4(rdpCmdBufPtr) // Store non-shuffled indices - bltz $ra, tri_fan_store // Finish handling G_TRIFAN - addi cmd_w0, cmd_w0, 1 // Increment - andi $11, cmd_w0, 1 // If odd, this is the 1st/3rd/5th tri - bnez $11, tri_main // Draw as is - srl $10, cmd_w1_dram, 8 // Move vtx 2 to LSBs - sb cmd_w1_dram, 6(rdpCmdBufPtr) // Store vtx 3 to spot for 2 + +// Index = bits 1-6; direction flag = bit 0; end flag = bit 7 +// CM 02 01 03 04 05 06 07 +// [bb^cc] Indices b and c +// | +// cmd_w0 + inputBufferEnd +G_TRISNAKE_handler: + sw cmd_w0, rdpHalf1Val // Store indices a, b, c + addi inputBufferPos, inputBufferPos, -5 // Point to byte 3, index c of 1st tri +tri_snake_loop: + lh $3, (inputBufferEnd - 1)(inputBufferPos) // Load indices b and c +tri_snake_loop_from_input_buffer: + lb $2, rdpHalf1Val + 1 // Old v1; == index b, except when bridging between old and new load + li $ra, tri_snake_loop // For tri_main + bltz $3, tri_snake_end // Upper bit of real index b set = done + andi $11, $3, 1 // Get direction flag from index c + beqz inputBufferPos, tri_snake_over_input_buffer // == 0 at end of input buffer + andi $3, $3, 0x7E // Mask out flags from index c + sb $3, rdpHalf1Val + 1 // Store index c as vertex 1 + sb $2, (rdpHalf1Val + 2)($11) // Store old v1 as 2 if dir clear or 3 if set j tri_main - sb $10, 7(rdpCmdBufPtr) // Store vtx 2 to spot for 3 + addi inputBufferPos, inputBufferPos, 1 // Increment indices being read + // H = highest on screen = lowest Y value; then M = mid, L = low tHAtF equ $v5 @@ -1304,15 +1310,15 @@ tPosHmM equ $v11 G_TRI2_handler: G_QUAD_handler: jal tri_main // Send second tri; return here for first tri - sw cmd_w1_dram, 4(rdpCmdBufPtr) // Store second tri indices + sw cmd_w1_dram, rdpHalf1Val // Store second tri indices G_TRI1_handler: li $ra, tris_end // After done with this tri, exit tri processing - sw cmd_w0, 4(rdpCmdBufPtr) // Store first tri indices + sw cmd_w0, rdpHalf1Val // Store first tri indices tri_main: - lpv $v27[0], 0(rdpCmdBufPtr) // To vector unit - lbu $1, 5(rdpCmdBufPtr) - lbu $2, 6(rdpCmdBufPtr) - lbu $3, 7(rdpCmdBufPtr) + lpv $v27[4], 0(rdpHalf1Val) // To vector unit in elems 5-7 + lbu $1, 1(rdpHalf1Val) + lbu $2, 2(rdpHalf1Val) + lbu $3, 3(rdpHalf1Val) vclr vZero lhu $1, (vertexTable)($1) vmudn $v29, vOne, vTRC_VB // Address of vertex buffer @@ -1827,7 +1833,8 @@ g_dma_io_ovl3: jal segmented_to_physical // Convert the provided segmented address (in cmd_w1_dram) to a virtual one lh dmemAddr, (inputBufferEnd - 0x07)(inputBufferPos) // Get the 16 bits in the middle of the command word (since inputBufferPos was already incremented for the next command) andi dmaLen, cmd_w0, 0x0FF8 // Mask out any bits in the length to ensure 8-byte alignment - j dma_and_wait_goto_next_command // Trigger a DMA read or write, depending on the G_DMA_IO flag (which will occupy the sign bit of dmemAddr) + li nextRA, run_next_DL_command + j dma_and_wait_goto_next_ra // Trigger a DMA read or write, depending on the G_DMA_IO flag (which will occupy the sign bit of dmemAddr) // At this point, dmemAddr's highest bit is the flag, it's next 13 bits are the DMEM address, and then it's last two bits are the upper 2 of size // So an arithmetic shift right 2 will preserve the flag as being the sign bit and get rid of the 2 size bits, shifting the DMEM address to start at the LSbit sra dmemAddr, dmemAddr, 2 @@ -2596,11 +2603,18 @@ tris_end: lqv vTRC, (vTRCValue)($zero) // Restore value overwritten by matrix .endif -tri_fan_store: - lb $11, (inputBufferEnd - 7)(inputBufferPos) // Load vtx 1 - sh cmd_w1_dram, 5(rdpCmdBufPtr) // Store vtx N+2 and N+3 as 1 and 2 - j tri_main - sb $11, 7(rdpCmdBufPtr) // Store vtx 1 as 3 +tri_snake_end: + addi inputBufferPos, inputBufferPos, 7 // Round up to whole input command + addi $11, $zero, 0xFFF8 // Sign-extend; andi is zero-extend! + j tris_end + and inputBufferPos, inputBufferPos, $11 // inputBufferPos has to be negative + +tri_snake_over_input_buffer: + j displaylist_dma_tri_snake // inputBufferPos is now 0; load whole buffer + li nextRA, tri_snake_ret_from_input_buffer +tri_snake_ret_from_input_buffer: + j tri_snake_loop_from_input_buffer // inputBufferPos pointing to first byte loaded + lbu $3, (inputBufferEnd)(inputBufferPos) // Load c; clear real index b sign bit -> don't exit // Converts the segmented address in cmd_w1_dram to the corresponding physical address segmented_to_physical: // 7 diff --git a/gbi.h b/gbi.h index 82473e9..28e2af2 100644 --- a/gbi.h +++ b/gbi.h @@ -63,10 +63,12 @@ of warnings if you use -Wpedantic. */ /* * GBI commands in order */ +#define G_LIGHTTORDP 0xD2 /*#define G_SPECIAL_3 0xD3 no-op in F3DEX2 */ +#define G_RELSEGMENT 0xD3 /*#define G_SPECIAL_2 0xD4 no-op in F3DEX2 */ -/*#define G_SPECIAL_1 0xD5 triggered MVP recalculation in F3DEX2 for debug */ #define G_FLUSH 0xD4 +/*#define G_SPECIAL_1 0xD5 triggered MVP recalculation in F3DEX2 for debug */ #define G_MEMSET 0xD5 #define G_DMA_IO 0xD6 #define G_TEXTURE 0xD7 @@ -118,10 +120,7 @@ of warnings if you use -Wpedantic. */ #define G_TRI1 0x05 #define G_TRI2 0x06 #define G_QUAD 0x07 -#define G_TRISTRIP 0x08 /* = G_LINE3D was a no-op in F3DEX2, has been removed */ -#define G_TRIFAN 0x09 -#define G_LIGHTTORDP 0x0A -#define G_RELSEGMENT 0x0B +#define G_TRISNAKE 0x08 /* = G_LINE3D was a no-op in F3DEX2, has been removed */ /* names differ between F3DEX2 and F3DZEX */ #define G_BRANCH_Z G_BRANCH_WZ @@ -2679,67 +2678,188 @@ _DW({ \ __gsSP1Triangle_w1f(v10, v11, v12, flag1) \ } -/* - * 5 Triangles base commands +/** + * Make the triangle snake turn left before drawing this triangle. + * @see gSPTriSnake */ -#define _gSP5Triangles(pkt, cmd, v1, v2, v3, v4, v5, v6, v7) \ -_DW({ \ - Gfx *_g = (Gfx *)(pkt); \ - _g->words.w0 = (_SHIFTL(cmd, 24, 8) | \ - _SHIFTL((v1)*2, 16, 8) | \ - _SHIFTL((v2)*2, 8, 8) | \ - _SHIFTL((v3)*2, 0, 8)); \ - _g->words.w1 = (_SHIFTL((v4)*2, 24, 8) | \ - _SHIFTL((v5)*2, 16, 8) | \ - _SHIFTL((v6)*2, 8, 8) | \ - _SHIFTL((v7)*2, 0, 8)); \ +#define G_SNAKE_LEFT 0 +/** + * Make the triangle snake turn right before drawing this triangle. + * @see gSPTriSnake + */ +#define G_SNAKE_RIGHT 1 +/** + * Logical-OR this into a triangle index to mark it as the last triangle of the + * snake. In other words, this gets OR'd into the last valid index, not the + * first invalid index. + * + * @note Due to tri indices being multiplied by 2 in the binary encoding, this + * is actually 0x80--the byte's sign bit--in the binary encoding. + * + * @see gSPTriSnake + */ +#define G_SNAKE_LAST 0x40 + +#define _gSPTriSnakeW0(i1, i2, i3) \ + (_SHIFTL(G_TRISNAKE, 24, 8) | \ + _SHIFTL((i2)*2, 16, 8) | \ + _SHIFTL((i1)*2, 8, 8) | \ + _SHIFTL((i3)*2|G_SNAKE_RIGHT, 0, 8)) +#define _gSPTriSnakeW1(i4, i4d, i5, i5d, i6, i6d, i7, i7d) \ + (_SHIFTL((i4)*2|(i4d), 24, 8) | \ + _SHIFTL((i5)*2|(i5d), 16, 8) | \ + _SHIFTL((i6)*2|(i6d), 8, 8) | \ + _SHIFTL((i7)*2|(i7d), 0, 8)) + +/** + * Triangle snake is F3DEX3's accelerated triangles command. It is a generalized + * form of a triangle strip or fan, which can represent any sequential chain of + * connected triangles by encoding which side of the current triangle the next + * triangle attaches to. This allows the chain of triangles to "snake" around + * and double back next to itself, unlike a triangle strip. For more information + * on the design, see Triangle Snake in the documentation. + * + * The drawing algorithm is: + * - Initialize 3 bytes of stored triangle indices, A-B-C, to i3-i1-i2, and draw + * this triangle. (This initialization and draw is actually implemented by + * storing i2-i1-i3 and then running the algorithm below with G_SNAKE_RIGHT, + * which ends up storing i2 to C and i3 to A, ultimately creating i3-i1-i2.) + * - Loop: + * - If the index in A has G_SNAKE_LAST or'd into it, exit. + * - Increment the input pointer, and read the next index and its direction + * flag (currently i4 and i4d). + * - If the direction flag is G_SNAKE_LEFT, copy A to B; else + * (G_SNAKE_RIGHT), copy A to C. + * - Store the new index (currently i4) to A. + * - Draw the triangle A-B-C and repeat the loop. + * + * For example, after drawing the first triangle i3-i1-i2, if i4 is + * G_SNAKE_LEFT, the snake turns left and draws i4-i3-i2: + * 4 -->-- 3 + * \' /'\ (winding order and + * \ / \ first vertex for flat + * \ / \ shading are marked) + * 2 --<-- 1 + * Conversely, after the first triangle i3-i1-i2, if i4 is G_SNAKE_RIGHT, the + * snake turns right and draws i4-i1-i3: + * 3 -->-- 4 + * /'\ '/ + * / \ / + * / \ / + * 2 --<-- 1 + * If the snake turns in the same direction repeatedly, it will coil up, forming + * a triangle fan. If it slithers left and right alternately, this will form a + * triangle strip. Any combination of these is also possible. In particular, a + * useful shape is a triangle strip for a few tris, then a tri fan for a couple + * tris to "turn around", then another tri strip alongside the first, and so on. + * This shape can cover almost all tris of a typical surface with a single + * snake, except for tris which have two unconnected edges which can only be the + * first or last tris of the snake. + * + * @see gSPContinueSnake to extend the snake to more than 5 triangles. + */ +#define gSPTriSnake(pkt, i1, i2, i3, i4, i4d, i5, i5d, i6, i6d, i7, i7d) \ +_DW({ \ + Gfx *_g = (Gfx *)(pkt); \ + _g->words.w0 = _gSPTriSnakeW0(i1, i2, i3); \ + _g->words.w1 = _gSPTriSnakeW1(i4, i4d, i5, i5d, i6, i6d, i7, i7d); \ }) -#define _gsSP5Triangles(cmd, v1, v2, v3, v4, v5, v6, v7) \ -{ \ - (_SHIFTL(cmd, 24, 8) | \ - _SHIFTL((v1)*2, 16, 8) | \ - _SHIFTL((v2)*2, 8, 8) | \ - _SHIFTL((v3)*2, 0, 8)), \ - (_SHIFTL((v4)*2, 24, 8) | \ - _SHIFTL((v5)*2, 16, 8) | \ - _SHIFTL((v6)*2, 8, 8) | \ - _SHIFTL((v7)*2, 0, 8)) \ +/** + * @copydetails gSPTriSnake + */ +#define gsSPTriSnake(i1, i2, i3, i4, i4d, i5, i5d, i6, i6d, i7, i7d) \ +{ \ + _gSPTriSnakeW0(i1, i2, i3), \ + _gSPTriSnakeW1(i4, i4d, i5, i5d, i6, i6d, i7, i7d) \ } + +/** + * Continue a triangle snake for up to 8 more triangles. This is actually not + * a display list command--there's no command byte. The data is just the next + * 8 bytes of the display list data, still being processed by the previous + * gSPTriSnake. Note that the microcode implementation does correctly handle + * the case when the snake continues past the end of the current data in the + * input buffer (which is a copy in DMEM of a chunk of the display list); the + * input buffer is reloaded like it would be for more commands. So the snake can + * be an unlimited length by continuing to append gSPContinueSnake commands. + */ +#define gSPContinueSnake(pkt, i0, i0d, i1, i1d, i2, i2d, i3, i3d, \ + i4, i4d, i5, i5d, i6, i6d, i7, i7d) \ +_DW({ \ + Gfx *_g = (Gfx *)(pkt); \ + _g->words.w0 = _gSPTriSnakeW1(i0, i0d, i1, i1d, i2, i2d, i3, i3d); \ + _g->words.w1 = _gSPTriSnakeW1(i4, i4d, i5, i5d, i6, i6d, i7, i7d); \ +}) +/** + * @copydetails gSPContinueSnake + */ +#define gsSPContinueSnake(i0, i0d, i1, i1d, i2, i2d, i3, i3d, \ + i4, i4d, i5, i5d, i6, i6d, i7, i7d) \ +{ \ + _gSPTriSnakeW1(i0, i0d, i1, i1d, i2, i2d, i3, i3d) \ + _gSPTriSnakeW1(i4, i4d, i5, i5d, i6, i6d, i7, i7d) \ +} + /** * 5 Triangles in strip arrangement. Draws the following tris: - * v1-v2-v3, v2-v4-v3, v3-v4-v5, v4-v6-v5, v5-v6-v7 - * If you want to draw fewer tris, set indices to -1 from the right. - * e.g. to draw 4 tris, set v7 to -1; to draw 3 tris, set v6 to -1. + * v3-v1-v2, v4-v3-v2, v5-v3-v4, v6-v5-v4, v7-v5-v6 + * To draw fewer than 5 tris, set indices to -1 from the right; for example to + * draw 4 tris, set v7 to -1, or to draw 3 tris set v6 to -1. * - * @note Any set of 3 adjacent tris can be drawn with either SPTriStrip - * or SPTriFan. For arbitrary sets of 4 adjacent tris, four out of five of them - * can be drawn with one of SPTriStrip or SPTriFan. The 4-triangle formation - * which can't be drawn with either command looks like the Triforce--maybe - * F3DEX4 will support gsSPTriForce. :) - * * @note The first index of each triangle drawn is different, so that in * !G_SHADING_SMOOTH (flat shading) mode, the single color or single normal of * each triangle can be set independently. + * + * @deprecated This used to be directly implemented in the microcode, but is + * now implemented as a special case of gSPTriSnake. The latter is more general + * and should be used directly. + * + * @note One of the two handednesses of a 4 tri strip cannot be drawn directly + * with gSPTriStrip, unless v1 and v2 are set to the same vertex to create a + * degenerate triangle, which costs a little performance. However, now this + * shape can be drawn with gSPTriSnake (directions right-left-right). */ -#define gSPTriStrip(pkt, v1, v2, v3, v4, v5, v6, v7) \ - _gSP5Triangles(pkt, G_TRISTRIP, v1, v2, v3, v4, v5, v6, v7) +#define gSPTriStrip(pkt, v1, v2, v3, v4, v5, v6, v7) \ + gSPTriSnake(pkt, v1, v2, \ + v3 | ((v4 & 0x80) ? G_SNAKE_LAST : 0), \ + v4 | ((v5 & 0x80) ? G_SNAKE_LAST : 0), G_SNAKE_LEFT, \ + v5 | ((v6 & 0x80) ? G_SNAKE_LAST : 0), G_SNAKE_RIGHT, \ + v6 | ((v7 & 0x80) ? G_SNAKE_LAST : 0), G_SNAKE_LEFT, \ + v7, G_SNAKE_RIGHT) /** * @copydetails gSPTriStrip */ -#define gsSPTriStrip(v1, v2, v3, v4, v5, v6, v7) \ - _gsSP5Triangles(G_TRISTRIP, v1, v2, v3, v4, v5, v6, v7) +#define gsSPTriStrip(v1, v2, v3, v4, v5, v6, v7) \ + gsSPTriSnake(v1, v2, \ + v3 | ((v4 & 0x80) ? G_SNAKE_LAST : 0), \ + v4 | ((v5 & 0x80) ? G_SNAKE_LAST : 0), G_SNAKE_LEFT, \ + v5 | ((v6 & 0x80) ? G_SNAKE_LAST : 0), G_SNAKE_RIGHT, \ + v6 | ((v7 & 0x80) ? G_SNAKE_LAST : 0), G_SNAKE_LEFT, \ + v7, G_SNAKE_RIGHT) /** * 5 Triangles in fan arrangement. Draws the following tris: - * v2-v3-v1, v3-v4-v1, v4-v5-v1, v5-v6-v1, v6-v7-v1 - * Otherwise works the same as @see SPTriStrip. + * v3-v1-v2, v4-v1-v3, v5-v1-v4, v6-v1-v5, v7-v1-v6 + * Otherwise works the same as @see gSPTriStrip. + * + * @deprecated Use gSPTriSnake directly. */ #define gSPTriFan(pkt, v1, v2, v3, v4, v5, v6, v7) \ - _gSP5Triangles(pkt, G_TRIFAN, v1, v2, v3, v4, v5, v6, v7) + gSPTriSnake(pkt, v1, v2, \ + v3 | ((v4 & 0x80) ? G_SNAKE_LAST : 0), \ + v4 | ((v5 & 0x80) ? G_SNAKE_LAST : 0), G_SNAKE_RIGHT, \ + v5 | ((v6 & 0x80) ? G_SNAKE_LAST : 0), G_SNAKE_RIGHT, \ + v6 | ((v7 & 0x80) ? G_SNAKE_LAST : 0), G_SNAKE_RIGHT, \ + v7, G_SNAKE_RIGHT) /** * @copydetails gSPTriFan */ #define gsSPTriFan(v1, v2, v3, v4, v5, v6, v7) \ - _gsSP5Triangles(G_TRIFAN, v1, v2, v3, v4, v5, v6, v7) + gsSPTriSnake(v1, v2, \ + v3 | ((v4 & 0x80) ? G_SNAKE_LAST : 0), \ + v4 | ((v5 & 0x80) ? G_SNAKE_LAST : 0), G_SNAKE_RIGHT, \ + v5 | ((v6 & 0x80) ? G_SNAKE_LAST : 0), G_SNAKE_RIGHT, \ + v6 | ((v7 & 0x80) ? G_SNAKE_LAST : 0), G_SNAKE_RIGHT, \ + v7, G_SNAKE_RIGHT) /* diff --git a/rsp/gbi.inc b/rsp/gbi.inc index 691ea80..c9e0ead 100644 --- a/rsp/gbi.inc +++ b/rsp/gbi.inc @@ -31,6 +31,8 @@ G_SHADING_SMOOTH equ 0x00200000 G_TRI_FILL equ 0xc8 // not a GBI command +G_LIGHTTORDP equ 0xd2 +G_RELSEGMENT equ 0xd3 G_FLUSH equ 0xd4 G_MEMSET equ 0xd5 G_DMA_IO equ 0xd6 @@ -83,10 +85,7 @@ G_BRANCH_WZ equ 0x04 G_TRI1 equ 0x05 G_TRI2 equ 0x06 G_QUAD equ 0x07 -G_TRISTRIP equ 0x08 -G_TRIFAN equ 0x09 -G_LIGHTTORDP equ 0x0a -G_RELSEGMENT equ 0x0b +G_TRISNAKE equ 0x08 G_BRANCH_Z equ G_BRANCH_WZ