need to finally make a metaprogram for offset math...

This commit is contained in:
ed
2026-07-06 08:28:40 -04:00
parent 54968d20e0
commit c3a7e22743
6 changed files with 145 additions and 80 deletions
+16 -2
View File
@@ -5,6 +5,7 @@
# include "mips.h" # include "mips.h"
#endif #endif
#pragma region ASM DSL
/* ============================================================================ /* ============================================================================
* gte.h — Geometry Transformation Engine (COP2) for the PS1 * gte.h — Geometry Transformation Engine (COP2) for the PS1
* ============================================================================ * ============================================================================
@@ -332,7 +333,7 @@ enum { _C2_OPS_ = 0
| enc_gte_cmd(cmd) \ | enc_gte_cmd(cmd) \
) )
/* Pre-baked GTE command words for the common cases. /* GTE command words for the common cases.
* *
* These are pure compile-time integer constants — the C compiler * These are pure compile-time integer constants — the C compiler
* constant-folds them into `.word` directives in .rodata. Use them * constant-folds them into `.word` directives in .rodata. Use them
@@ -379,12 +380,17 @@ enum { _C2_OPS_ = 0
#define gte_cmdw_op (gte_cmd_base | enc_gte_cmd(gte_cmd_op )) #define gte_cmdw_op (gte_cmd_base | enc_gte_cmd(gte_cmd_op ))
#define gte_cmdw_mvmva (gte_cmd_base | enc_gte_cmd(gte_cmd_mvmva)) #define gte_cmdw_mvmva (gte_cmd_base | enc_gte_cmd(gte_cmd_mvmva))
#define gte_cmdw_rotate_translate_perspective_triple gte_cmdw_rtpt
/* PsyQ compatibility bits for AVSZ3 (Bits 20, 22, 24 must be set) */ /* PsyQ compatibility bits for AVSZ3 (Bits 20, 22, 24 must be set) */
#define gte_cmdw_psyq_avsz3_compat (0x15 << 20) #define gte_cmdw_psyq_avsz3_compat (0x15 << 20)
#define gte_cmd_avsz3 0x2D #define gte_cmd_avsz3 0x2D
#define gte_cmdw_avsz3 (gte_cmd_base | enc_gte_cmd(gte_cmd_avsz3) | gte_cmdw_psyq_avsz3_compat) #define gte_cmdw_avsz3 (gte_cmd_base | enc_gte_cmd(gte_cmd_avsz3) | gte_cmdw_psyq_avsz3_compat)
// Takes the three screen-space Z values of the triangle → averages them → writes the result into the OTZ register.
#define gte_avg_sort_z3 gte_cmdw_avsz3
/* AVSZ4 — average Z of 4 vertices (for quads) */ /* AVSZ4 — average Z of 4 vertices (for quads) */
#define gte_cmd_avsz4 0x2E #define gte_cmd_avsz4 0x2E
#define gte_cmdw_avsz4 (gte_cmd_base | enc_gte_cmd(gte_cmd_avsz4) | gte_cmdw_psyq_avsz3_compat) #define gte_cmdw_avsz4 (gte_cmd_base | enc_gte_cmd(gte_cmd_avsz4) | gte_cmdw_psyq_avsz3_compat)
@@ -403,7 +409,7 @@ enum { _C2_OPS_ = 0
* asm_gte_load_v0(svector_ptr); * asm_gte_load_v0(svector_ptr);
*/ */
/* Pre-baked lwc2 encoding helpers parameterized on the base GPR. /* lwc2 encoding helpers parameterized on the base GPR.
* *
* gte_lwc2_v0(base) → lwc2 $0, 0(base) ; C2_VXY0 * gte_lwc2_v0(base) → lwc2 $0, 0(base) ; C2_VXY0
* gte_lwc2_v0z(base) → lwc2 $1, 4(base) ; C2_VZ0 * gte_lwc2_v0z(base) → lwc2 $1, 4(base) ; C2_VZ0
@@ -656,3 +662,11 @@ enum {
, r_use(r0) \ , r_use(r0) \
asm_clobber: clb_system, rlit(R_T4), rlit(R_T5), rlit(R_T6) \ asm_clobber: clb_system, rlit(R_T4), rlit(R_T5), rlit(R_T6) \
) )
#pragma region ASM DSL
#pragma region Reserved
#pragma endregion Reserved
+28 -58
View File
@@ -15,6 +15,7 @@
* The C compiler is completely unaware of these bindings. * The C compiler is completely unaware of these bindings.
* ---------------------------------------------------------------------------*/ * ---------------------------------------------------------------------------*/
enum { enum {
R_AtomJmp = R_T9,
R_TapePtr = R_T8, /* The Instruction Stream Pointer */ R_TapePtr = R_T8, /* The Instruction Stream Pointer */
R_PrimCur = R_T7, /* VRAM output cursor (primitive buffer) */ R_PrimCur = R_T7, /* VRAM output cursor (primitive buffer) */
R_FaceCur = R_T4, /* Input data cursor (indices/faces) */ R_FaceCur = R_T4, /* Input data cursor (indices/faces) */
@@ -78,33 +79,30 @@ FI_ Slice_U4 tb_slice(TapeBuilder tb) { return (Sli
* Loads the next pointer from the tape, advances the tape, and jumps. * Loads the next pointer from the tape, advances the tape, and jumps.
* Cost: ~ 4 cycles */ * Cost: ~ 4 cycles */
#define mac_yield() \ #define mac_yield() \
load_word(R_T9, R_TapePtr, 0) \ load_word(R_AtomJmp, R_TapePtr, 0) \
, add_ui_1( R_TapePtr, 4) \ , add_ui_1( R_TapePtr, 4) \
, jump_reg( R_T9) \ , jump_reg( R_AtomJmp) \
, nop , nop
/* Loads 3 16-bit indices from the face array */ /* Words: 3; Loads 3 S2 indices from the face array */
#define mac_load_tri_indices(rId_0, rId_1, rId_2) \ #define mac_load_tri_indices(rId_0, rId_1, rId_2) \
load_half_u(rId_0, R_FaceCur, 0) \ load_half_u(rId_0, R_FaceCur, 0) \
, load_half_u(rId_1, R_FaceCur, 2) \ , load_half_u(rId_1, R_FaceCur, 2) \
, load_half_u(rId_2, R_FaceCur, 4) , load_half_u(rId_2, R_FaceCur, 4)
/* Translates indices to vertex addresses and pushes them to GTE */ /* Words: 18; Translates indices to vertex addresses and pushes them to GTE
R_AT = rId_[#] << 3;
R_AT += R_VertBase;
R_V0 = R_AT[0];
gte_mt(R_V0, V.xy[#]);
gte_mt(R_V1, V.z [#]);
*/
#define mac_load_tri_verts(rId_0, rId_1, rId_2) \ #define mac_load_tri_verts(rId_0, rId_1, rId_2) \
shift_ll(R_AT, rId_0, 3), add_u(R_AT, R_AT, R_VertBase), load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4), gte_mt(R_V0, C2_VXY0), gte_mt(R_V1, C2_VZ0) \ shift_ll(R_AT, rId_0, 3), add_u(R_AT, R_AT, R_VertBase), load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4), gte_mt(R_V0, C2_VXY0), gte_mt(R_V1, C2_VZ0) \
, shift_ll(R_AT, rId_1, 3), add_u(R_AT, R_AT, R_VertBase), load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4), gte_mt(R_V0, C2_VXY1), gte_mt(R_V1, C2_VZ1) \ , shift_ll(R_AT, rId_1, 3), add_u(R_AT, R_AT, R_VertBase), load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4), gte_mt(R_V0, C2_VXY1), gte_mt(R_V1, C2_VZ1) \
, shift_ll(R_AT, rId_2, 3), add_u(R_AT, R_AT, R_VertBase), load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4), gte_mt(R_V0, C2_VXY2), gte_mt(R_V1, C2_VZ2) , shift_ll(R_AT, rId_2, 3), add_u(R_AT, R_AT, R_VertBase), load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4), gte_mt(R_V0, C2_VXY2), gte_mt(R_V1, C2_VZ2)
/* Formats the primitive memory layout (Tag + Color + Coordinates) */ /* Words: 11; Correctly inserts a primitive into the Ordering Table linked list */
#define mac_format_prim_f3(color_hi, color_lo) \
store_word(R_0, R_PrimCur, 0) \
, load_ui(R_AT, color_hi), or_i(R_AT, R_AT, color_lo) \
, store_word(R_AT, R_PrimCur, 4) \
, gte_sw(C2_SXY0, R_PrimCur, 8) \
, gte_sw(C2_SXY1, R_PrimCur, 12) \
, gte_sw(C2_SXY2, R_PrimCur, 16)
/* Correctly inserts a primitive into the Ordering Table linked list */
#define mac_insert_ot_tag(r_otz, prim_length) \ #define mac_insert_ot_tag(r_otz, prim_length) \
shift_ll( R_T1, r_otz, 2) \ shift_ll( R_T1, r_otz, 2) \
, add_u( R_T1, R_T1, R_OtBase) /* T1 = &OrderingTable[OTZ] */ \ , add_u( R_T1, R_T1, R_OtBase) /* T1 = &OrderingTable[OTZ] */ \
@@ -136,7 +134,7 @@ FI_ void atombuilder_unroll(MipsAtomBuilder_R ab, Slice_MipsCode_R code) {
mem_copy(ab->start, u4_(code->ptr), code->len); mem_copy(ab->start, u4_(code->ptr), code->len);
mem_bump(ab->start, ab->capacity, & ab->used, code->len); mem_bump(ab->start, ab->capacity, & ab->used, code->len);
} }
#define atombuilder_unroll_mac(ab, mac) #define atombuilder_unroll_mac(ab, mac) atombuilder_unroll(ab, slice_arg_from_array(Slice_MipsCode, mac))
// When done authoring, utilize this to cap-off the atom // When done authoring, utilize this to cap-off the atom
FI_ void atombuilder_end(MipsAtomBuilder_R ab) { FI_ void atombuilder_end(MipsAtomBuilder_R ab) {
@@ -152,14 +150,19 @@ FI_ void atombuilder_end(MipsAtomBuilder_R ab) {
#pragma region Baked Mips Atoms #pragma region Baked Mips Atoms
// These atoms are resolved at compile time and are (usually) statically linked readonly data. // These atoms are resolved at compile time and are (usually) statically linked readonly data.
typedef Struct_(Binds_SyncPrimCursor) {
U4 PrimtiveArena_Used;
U4 PrimtiveBase;
};
internal MipsAtom_(sync_prim_cursor) { internal MipsAtom_(sync_prim_cursor) {
/* Pop the C-struct address and base address from the tape */ /* Pop the C-struct address and base address from the tape */
load_word(R_AT, R_TapePtr, 0), /* AT = &pa->used */ // Note(Ed): Argument shuffle....
load_word(R_T0, R_TapePtr, 4), /* T0 = prim_base */ load_word(R_AT, R_TapePtr, O_(Binds_SyncPrimCursor,PrimtiveArena_Used)),
add_ui_1( R_TapePtr, 8), load_word(R_T0, R_TapePtr, O_(Binds_SyncPrimCursor,PrimtiveBase)),
add_ui_1( R_TapePtr, S_(Binds_SyncPrimCursor)),
/* Calculate byte offset and store directly back to RAM */ /* Calculate byte offset and store directly back to RAM */
sub_u(R_T0, R_PrimCur, R_T0), sub_u(R_T0, R_PrimCur, R_T0), // R_T0 = PrimitiveArea_Used(R_AT) - R_PrimCur
store_word(R_T0, R_AT, 0), store_word(R_T0, R_AT, 0), // PrimitiveBase(R_AT)[0] = R_T0
mac_yield() mac_yield()
}; };
@@ -195,6 +198,8 @@ internal MipsAtom_(rbind_cube_tri) {
load_word(R_VertBase, R_TapePtr, O_(Binds_CubeTri,VertBase)), load_word(R_VertBase, R_TapePtr, O_(Binds_CubeTri,VertBase)),
load_word(R_OtBase, R_TapePtr, O_(Binds_CubeTri,OtBase)), load_word(R_OtBase, R_TapePtr, O_(Binds_CubeTri,OtBase)),
add_ui_1( R_TapePtr, S_(Binds_CubeTri)), add_ui_1( R_TapePtr, S_(Binds_CubeTri)),
// Note(Ed): This entire thing is argument shuffle?
// TODO(Ed): Eliminate
mac_yield() mac_yield()
}; };
@@ -345,46 +350,11 @@ internal MipsAtom_(rbind_floor_tri) {
load_word(R_VertBase, R_TapePtr, O_(Binds_FloorTri,VertBase)), load_word(R_VertBase, R_TapePtr, O_(Binds_FloorTri,VertBase)),
load_word(R_OtBase, R_TapePtr, O_(Binds_FloorTri,OtBase)), load_word(R_OtBase, R_TapePtr, O_(Binds_FloorTri,OtBase)),
add_ui_1( R_TapePtr, S_(Binds_FloorTri)), add_ui_1( R_TapePtr, S_(Binds_FloorTri)),
// Note(Ed): This entire thing is argument shuffle?
// TODO(Ed): Eliminate
mac_yield() mac_yield()
}; };
internal MipsAtom_(floor_tri) {
mac_load_tri_indices(R_T0, R_T1, R_T2),
mac_load_tri_verts( R_T0, R_T1, R_T2),
/* 3. Execute Math */
nop, nop, gte_cmdw_rtpt,
nop, nop, gte_cmdw_nclip,
nop, nop,
/* 4. Culling (Branch forward 29 instructions if Backface) */
gte_mf(R_T0, C2_MAC0),
nop,
branch_le_zero(R_T0, 29),
nop,
/* 5. Format Primitive */
mac_format_prim_f3(0x20FF, 0xFFFF), /* High: 0x20/B, Low: G/R */
/* 6. Calculate Depth */
nop, nop, gte_cmdw_avsz3,
nop, nop,
gte_mf(R_T1, C2_OTZ),
/* 7. Bounds Check OTZ < 2048 (Branch forward 13 instructions to skip insertion) */
add_ui( R_AT, R_0, 2048),
slt_u( R_AT, R_T1, R_AT),
branch_equal(R_AT, R_0, 13),
nop,
/* 8. Insert into Ordering Table Linked List */
mac_insert_ot_tag(R_T1, 0x0400), /* Length = 4 words */
add_ui(R_PrimCur, R_PrimCur, 20), /* Advance Prim Cursor (5 words) */
/* 9. Advance Input Cursor & Yield (Both branch targets land here) */
add_ui(R_FaceCur, R_FaceCur, 8), /* Advance Face Cursor (4 * S2 = 8 bytes) */
mac_yield()
};
/* DIAGNOSTIC 1: Pure tape loop test */ /* DIAGNOSTIC 1: Pure tape loop test */
internal MipsAtom_(diag_yield) { mac_yield() }; internal MipsAtom_(diag_yield) { mac_yield() };
+18 -4
View File
@@ -69,6 +69,7 @@ enum {
/* Semantic Aliases for MIPS Registers (O32 ABI) */ /* Semantic Aliases for MIPS Registers (O32 ABI) */
, rdiscard = R_0 /* Hardwired to 0 */ , rdiscard = R_0 /* Hardwired to 0 */
, rasm_tmp = R_AT /* Assembler temporary (destroyed by some assembler pseudoinstructions!) */
, rret_0 = R_V0 /* Function return value */ , rret_0 = R_V0 /* Function return value */
, rret_1 = R_V1 /* Second return value (e.g., 64-bit) */ , rret_1 = R_V1 /* Second return value (e.g., 64-bit) */
, rarg_0 = R_A0 /* First function argument */ , rarg_0 = R_A0 /* First function argument */
@@ -112,21 +113,30 @@ enum {
, op_xori = 0x0E /* XOR Immediate */ , op_xori = 0x0E /* XOR Immediate */
, op_lui = 0x0F /* Load Upper Immediate */ , op_lui = 0x0F /* Load Upper Immediate */
, op_cop0 = 0x10 /* Coprocessor 0 (System) */ , op_cop0 = 0x10 /* Coprocessor 0 (System) */
, op_cop1 = 0x11 /* Coprocessor 1 (Reserved, FP Unit, Omitted by Sony) */
, op_cop2 = 0x12 /* Coprocessor 2 (GTE) */ , op_cop2 = 0x12 /* Coprocessor 2 (GTE) */
, op_la = 0 , op_cop3 = 0x13 /* Coprocessor 3 (Reserved, Unused)*/
, op_li = 0 /* 14-1F: N/A */
, op_lb = 0x20 /* Load Byte */ , op_lb = 0x20 /* Load Byte */
, op_lh = 0x21 /* Load Halfword */ , op_lh = 0x21 /* Load Halfword */
, op_lwl = 0x22 /* Load Word (Left Bits) */
, op_lw = 0x23 /* Load Word */ , op_lw = 0x23 /* Load Word */
, op_lbu = 0x24 /* Load Byte Unsigned */ , op_lbu = 0x24 /* Load Byte Unsigned */
, op_lhu = 0x25 /* Load Halfword Unsigned */ , op_lhu = 0x25 /* Load Halfword Unsigned */
, op_lwr = 0x26 /* Load Word (Right Bits) */
/* 27: N/A */
, op_sb = 0x28 /* Store Byte */ , op_sb = 0x28 /* Store Byte */
, op_sh = 0x29 /* Store Halfword */ , op_sh = 0x29 /* Store Halfword */
, op_swl = 0x2A /* Store Word (Left Bits) */
, op_sw = 0x2B /* Store Word */ , op_sw = 0x2B /* Store Word */
/* 2C-2D: N/A */
, op_swr = 0x2E /* Store Word (Right Bits) */
/* 2F: N/A */
// , op_lwc0
, op_load_addr = op_la // , op_load_addr = op_la
, op_load_imm = op_li // , op_load_imm = op_li
, op_jump = op_j , op_jump = op_j
, op_jump_nlink = op_jal , op_jump_nlink = op_jal
@@ -246,6 +256,10 @@ enum { _BitOffsets = 0
#define xor_i(rt, rs, imm) enc_i(op_xori, (rs), (rt), (imm)) #define xor_i(rt, rs, imm) enc_i(op_xori, (rs), (rt), (imm))
#define load_ui(rt, imm) enc_i(op_lui, R_0, (rt), (imm)) #define load_ui(rt, imm) enc_i(op_lui, R_0, (rt), (imm))
#define load_u1 load_byte_u
#define load_u2 load_half_u
#define load_u4 load_word
// Ergonomic add to the same register. // Ergonomic add to the same register.
#define add_ui_1(rt_rs, imm) enc_i(op_addiu, (rt_rs), (rt_rs), (imm)) #define add_ui_1(rt_rs, imm) enc_i(op_addiu, (rt_rs), (rt_rs), (imm))
+17 -16
View File
@@ -14,11 +14,7 @@
#include "duffle/gte.h" #include "duffle/gte.h"
#include "duffle/lottes_tape.h" #include "duffle/lottes_tape.h"
#include "hello_gte.h" #include "hello_gte.h"
#include "hello_gte_tape.c"
enum {
PrimitiveBuff_Len = 4096,
OrderingTbl_Len = 2048
};
typedef U4 OrderingTable_Buffer[OrderingTbl_Len]; typedef U4 OrderingTable_Buffer[OrderingTbl_Len];
typedef Array_(OrderingTable_Buffer, 2); typedef Array_(OrderingTable_Buffer, 2);
@@ -104,6 +100,8 @@ typedef Struct_(SMemory) {
Ent_Cube cube; Ent_Cube cube;
Ent_Floor floor; Ent_Floor floor;
}; };
global SMemory static_mem; global SMemory static_mem;
extern SMemory static_mem; extern SMemory static_mem;
@@ -245,10 +243,10 @@ void update(PrimitiveArena* pa, U4* ordering_buf)
LP_ U4 mem_temp_tape[512]; FArena tape_arena; farena_init(& tape_arena, slice_ut_arr(mem_temp_tape)); LP_ U4 mem_temp_tape[512]; FArena tape_arena; farena_init(& tape_arena, slice_ut_arr(mem_temp_tape));
TapeBuilder tb = tb_make(&tape_arena); tb_scope(& tb) { TapeBuilder tb = tb_make(&tape_arena); tb_scope(& tb) {
tb_emit(& tb, code_rbind_cube_tri); tb_emit(& tb, code_rbind_cube_tri);
tb_data(& tb, prim_cursor); tb_data(& tb, prim_cursor);
tb_data(& tb, u4_(static_mem.cube.faces)); tb_data(& tb, u4_(static_mem.cube.faces));
tb_data(& tb, u4_(static_mem.cube.verts)); tb_data(& tb, u4_(static_mem.cube.verts));
tb_data(& tb, u4_(ordering_buf)); tb_data(& tb, u4_(ordering_buf));
for (U4 i = 0; i < Cube_num_faces; i++) { for (U4 i = 0; i < Cube_num_faces; i++) {
// Two triangles per quad face: (x,y,z) and (x,z,w) // Two triangles per quad face: (x,y,z) and (x,z,w)
@@ -256,8 +254,8 @@ void update(PrimitiveArena* pa, U4* ordering_buf)
} }
tb_emit(& tb, code_sync_prim_cursor); tb_emit(& tb, code_sync_prim_cursor);
tb_data(& tb, u4_(& pa->used)); tb_data(& tb, u4_(& pa->used));
tb_data(& tb, prim_base); tb_data(& tb, prim_base);
} }
tape_run(tb_slice(tb)); tape_run(tb_slice(tb));
@@ -334,21 +332,24 @@ void update(PrimitiveArena* pa, U4* ordering_buf)
LP_ U4 mem_temp_tape[512]; FArena tape_arena; farena_init(& tape_arena, slice_ut_arr(mem_temp_tape)); LP_ U4 mem_temp_tape[512]; FArena tape_arena; farena_init(& tape_arena, slice_ut_arr(mem_temp_tape));
TapeBuilder tb = tb_make(&tape_arena); tb_scope(& tb) { TapeBuilder tb = tb_make(&tape_arena); tb_scope(& tb) {
// Push "Protocol" to tape // Push "Protocol" to tape
tb_emit_(& tb, rbind_floor_tri); tb_emit(& tb, code_rbind_floor_tri);
// Note(Ed): This is technically argument shuffle and would be better if we did a single reference at most to a global batch context.
// Note(Ed): We can technically allocate a single ptr with the global offset to the working context instead of utilizing the tape for this stack of refs.
tb_data(& tb, prim_cursor); tb_data(& tb, prim_cursor);
tb_data(& tb, u4_(static_mem.floor.faces)); tb_data(& tb, u4_(static_mem.floor.faces));
tb_data(& tb, u4_(static_mem.floor.verts)); tb_data(& tb, u4_(static_mem.floor.verts));
tb_data(& tb, u4_(ordering_buf)); tb_data(& tb, u4_(ordering_buf));
tb_emit(& tb, code_set_gte_world); tb_emit(& tb, code_set_gte_world);
tb_data(& tb, u4_(& static_mem.tform_world)); tb_data(& tb, u4_(& static_mem.tform_world));
for (U4 i = 0; i < Floor_num_faces; i++) { for (U4 i = 0; i < Floor_num_faces; i++) {
tb_emit(& tb, code_floor_tri); tb_emit(& tb, code_floor_tri);
} }
tb_emit(& tb, code_sync_prim_cursor); tb_emit(& tb, code_sync_prim_cursor);
tb_data(& tb, u4_(& pa->used)); tb_data(& tb, u4_(& pa->used));
tb_data(& tb, prim_base); tb_data(& tb, prim_base);
} }
// Fire off the tape. // Fire off the tape.
+5
View File
@@ -5,6 +5,11 @@
# include "duffle/gp.h" # include "duffle/gp.h"
#endif #endif
enum {
PrimitiveBuff_Len = 4096,
OrderingTbl_Len = 2048
};
typedef Struct_(DrawEnv_Packed) { U4 tag; U4 code[15]; }; typedef Struct_(DrawEnv_Packed) { U4 tag; U4 code[15]; };
typedef Struct_(DrawEnv) { typedef Struct_(DrawEnv) {
Rect_S2 clip_area; Rect_S2 clip_area;
+61
View File
@@ -0,0 +1,61 @@
#ifdef INTELLISENSE_DIRECTIVES
# include "duffle/lottes_tape.h"
# include "hello_gte.h"
#endif
#pragma region MACs
/* Words: 3; High: 0x20/B, Low: G/R */
#define mac_format_f3_color(color_hi, color_lo) \
load_ui(R_AT, color_hi), or_i(R_AT, R_AT, color_lo) \
, store_word(R_AT, R_PrimCur, O_(Poly_F3,color)) \
/* Words: 3 */
#define mac_gte_store_f3() \
gte_sw(C2_SXY0, R_PrimCur, O_(Poly_F3,p0)) \
, gte_sw(C2_SXY1, R_PrimCur, O_(Poly_F3,p1)) \
, gte_sw(C2_SXY2, R_PrimCur, O_(Poly_F3,p2))
#pragma endregion MACs
#pragma region Baked Atoms
internal MipsAtom_(floor_tri) {
// T0-T2 allocated
mac_load_tri_indices(R_T0, R_T1, R_T2),
mac_load_tri_verts( R_T0, R_T1, R_T2),
/* 3. Execute Math */
nop, nop, gte_cmdw_rotate_translate_perspective_triple,
nop, nop, gte_cmdw_nclip,
nop, nop,
/* 4. Culling (Branch forward 29 instructions if Backface) */
gte_mf(R_T0, C2_MAC0),
nop, branch_le_zero(R_T0, 29),
nop,
/* 5. Format Primitive */
mac_format_f3_color(0x20FF, 0xFFFF),
mac_gte_store_f3(),
/* 6. Calculate Depth */
nop, nop, gte_avg_sort_z3,
nop, nop, gte_mf(R_T1, C2_OTZ),
/* 7. Bounds Check OTZ < 2048 (Branch forward 13 instructions to skip insertion) */
add_ui( R_AT, R_0, OrderingTbl_Len),
slt_u( R_AT, R_T1, R_AT),
branch_equal(R_AT, R_0, 13),
nop,
/* 8. Insert into Ordering Table Linked List */
mac_insert_ot_tag(R_T1, 0x0400),
add_ui(R_PrimCur, R_PrimCur, S_(Poly_F3)), /* Advance Prim Cursor (5 words) */
/* 9. Advance Input Cursor & Yield (Both branch targets land here) */
add_ui(R_FaceCur, R_FaceCur, S_(S2) * 4), /* Advance Face Cursor (4 * S2 = 8 bytes) */
mac_yield()
};
#pragma endregion Baked Atoms