From c3a7e22743f92d40d452012893251c970a65f3df Mon Sep 17 00:00:00 2001 From: Ed_ Date: Mon, 6 Jul 2026 08:28:40 -0400 Subject: [PATCH] need to finally make a metaprogram for offset math... --- code/duffle/gte.h | 18 ++++++- code/duffle/lottes_tape.h | 86 +++++++++++---------------------- code/duffle/mips.h | 22 +++++++-- code/gte_hello/hello_gte.c | 33 +++++++------ code/gte_hello/hello_gte.h | 5 ++ code/gte_hello/hello_gte_tape.c | 61 +++++++++++++++++++++++ 6 files changed, 145 insertions(+), 80 deletions(-) create mode 100644 code/gte_hello/hello_gte_tape.c diff --git a/code/duffle/gte.h b/code/duffle/gte.h index 8a59a7d..3224b2c 100644 --- a/code/duffle/gte.h +++ b/code/duffle/gte.h @@ -5,6 +5,7 @@ # include "mips.h" #endif +#pragma region ASM DSL /* ============================================================================ * gte.h — Geometry Transformation Engine (COP2) for the PS1 * ============================================================================ @@ -332,7 +333,7 @@ enum { _C2_OPS_ = 0 | 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 * 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_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) */ #define gte_cmdw_psyq_avsz3_compat (0x15 << 20) #define gte_cmd_avsz3 0x2D #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) */ #define gte_cmd_avsz4 0x2E #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); */ -/* 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_v0z(base) → lwc2 $1, 4(base) ; C2_VZ0 @@ -656,3 +662,11 @@ enum { , r_use(r0) \ asm_clobber: clb_system, rlit(R_T4), rlit(R_T5), rlit(R_T6) \ ) + +#pragma region ASM DSL + +#pragma region Reserved + + + +#pragma endregion Reserved diff --git a/code/duffle/lottes_tape.h b/code/duffle/lottes_tape.h index acae5a7..f3e3958 100644 --- a/code/duffle/lottes_tape.h +++ b/code/duffle/lottes_tape.h @@ -15,6 +15,7 @@ * The C compiler is completely unaware of these bindings. * ---------------------------------------------------------------------------*/ enum { + R_AtomJmp = R_T9, R_TapePtr = R_T8, /* The Instruction Stream Pointer */ R_PrimCur = R_T7, /* VRAM output cursor (primitive buffer) */ 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. * Cost: ~ 4 cycles */ #define mac_yield() \ - load_word(R_T9, R_TapePtr, 0) \ - , add_ui_1( R_TapePtr, 4) \ - , jump_reg( R_T9) \ + load_word(R_AtomJmp, R_TapePtr, 0) \ + , add_ui_1( R_TapePtr, 4) \ + , jump_reg( R_AtomJmp) \ , 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) \ load_half_u(rId_0, R_FaceCur, 0) \ , load_half_u(rId_1, R_FaceCur, 2) \ , 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) \ 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_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) */ -#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 */ +/* Words: 11; Correctly inserts a primitive into the Ordering Table linked list */ #define mac_insert_ot_tag(r_otz, prim_length) \ shift_ll( R_T1, r_otz, 2) \ , 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_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 FI_ void atombuilder_end(MipsAtomBuilder_R ab) { @@ -152,14 +150,19 @@ FI_ void atombuilder_end(MipsAtomBuilder_R ab) { #pragma region Baked Mips Atoms // 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) { /* Pop the C-struct address and base address from the tape */ - load_word(R_AT, R_TapePtr, 0), /* AT = &pa->used */ - load_word(R_T0, R_TapePtr, 4), /* T0 = prim_base */ - add_ui_1( R_TapePtr, 8), + // Note(Ed): Argument shuffle.... + load_word(R_AT, R_TapePtr, O_(Binds_SyncPrimCursor,PrimtiveArena_Used)), + 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 */ - sub_u(R_T0, R_PrimCur, R_T0), - store_word(R_T0, R_AT, 0), + sub_u(R_T0, R_PrimCur, R_T0), // R_T0 = PrimitiveArea_Used(R_AT) - R_PrimCur + store_word(R_T0, R_AT, 0), // PrimitiveBase(R_AT)[0] = R_T0 mac_yield() }; @@ -195,6 +198,8 @@ internal MipsAtom_(rbind_cube_tri) { load_word(R_VertBase, R_TapePtr, O_(Binds_CubeTri,VertBase)), load_word(R_OtBase, R_TapePtr, O_(Binds_CubeTri,OtBase)), add_ui_1( R_TapePtr, S_(Binds_CubeTri)), + // Note(Ed): This entire thing is argument shuffle? + // TODO(Ed): Eliminate mac_yield() }; @@ -345,46 +350,11 @@ internal MipsAtom_(rbind_floor_tri) { load_word(R_VertBase, R_TapePtr, O_(Binds_FloorTri,VertBase)), load_word(R_OtBase, R_TapePtr, O_(Binds_FloorTri,OtBase)), add_ui_1( R_TapePtr, S_(Binds_FloorTri)), + // Note(Ed): This entire thing is argument shuffle? + // TODO(Ed): Eliminate 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 */ internal MipsAtom_(diag_yield) { mac_yield() }; diff --git a/code/duffle/mips.h b/code/duffle/mips.h index 6882480..5bbc866 100644 --- a/code/duffle/mips.h +++ b/code/duffle/mips.h @@ -69,6 +69,7 @@ enum { /* Semantic Aliases for MIPS Registers (O32 ABI) */ , 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_1 = R_V1 /* Second return value (e.g., 64-bit) */ , rarg_0 = R_A0 /* First function argument */ @@ -112,21 +113,30 @@ enum { , op_xori = 0x0E /* XOR Immediate */ , op_lui = 0x0F /* Load Upper Immediate */ , op_cop0 = 0x10 /* Coprocessor 0 (System) */ + , op_cop1 = 0x11 /* Coprocessor 1 (Reserved, FP Unit, Omitted by Sony) */ , op_cop2 = 0x12 /* Coprocessor 2 (GTE) */ - , op_la = 0 - , op_li = 0 + , op_cop3 = 0x13 /* Coprocessor 3 (Reserved, Unused)*/ + /* 14-1F: N/A */ , op_lb = 0x20 /* Load Byte */ , op_lh = 0x21 /* Load Halfword */ + , op_lwl = 0x22 /* Load Word (Left Bits) */ , op_lw = 0x23 /* Load Word */ , op_lbu = 0x24 /* Load Byte Unsigned */ , op_lhu = 0x25 /* Load Halfword Unsigned */ + , op_lwr = 0x26 /* Load Word (Right Bits) */ + /* 27: N/A */ , op_sb = 0x28 /* Store Byte */ , op_sh = 0x29 /* Store Halfword */ + , op_swl = 0x2A /* Store Word (Left Bits) */ , 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_imm = op_li + // , op_load_addr = op_la + // , op_load_imm = op_li , op_jump = op_j , 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 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. #define add_ui_1(rt_rs, imm) enc_i(op_addiu, (rt_rs), (rt_rs), (imm)) diff --git a/code/gte_hello/hello_gte.c b/code/gte_hello/hello_gte.c index 1bb8042..4c40bfe 100644 --- a/code/gte_hello/hello_gte.c +++ b/code/gte_hello/hello_gte.c @@ -14,11 +14,7 @@ #include "duffle/gte.h" #include "duffle/lottes_tape.h" #include "hello_gte.h" - -enum { - PrimitiveBuff_Len = 4096, - OrderingTbl_Len = 2048 -}; +#include "hello_gte_tape.c" typedef U4 OrderingTable_Buffer[OrderingTbl_Len]; typedef Array_(OrderingTable_Buffer, 2); @@ -104,6 +100,8 @@ typedef Struct_(SMemory) { Ent_Cube cube; Ent_Floor floor; + + }; global 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)); TapeBuilder tb = tb_make(&tape_arena); tb_scope(& tb) { tb_emit(& tb, code_rbind_cube_tri); - tb_data(& tb, prim_cursor); - tb_data(& tb, u4_(static_mem.cube.faces)); - tb_data(& tb, u4_(static_mem.cube.verts)); - tb_data(& tb, u4_(ordering_buf)); + tb_data(& tb, prim_cursor); + tb_data(& tb, u4_(static_mem.cube.faces)); + tb_data(& tb, u4_(static_mem.cube.verts)); + tb_data(& tb, u4_(ordering_buf)); for (U4 i = 0; i < Cube_num_faces; i++) { // 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_data(& tb, u4_(& pa->used)); - tb_data(& tb, prim_base); + tb_data(& tb, u4_(& pa->used)); + tb_data(& tb, prim_base); } 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)); TapeBuilder tb = tb_make(&tape_arena); tb_scope(& tb) { // 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, u4_(static_mem.floor.faces)); tb_data(& tb, u4_(static_mem.floor.verts)); tb_data(& tb, u4_(ordering_buf)); + 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++) { tb_emit(& tb, code_floor_tri); } tb_emit(& tb, code_sync_prim_cursor); - tb_data(& tb, u4_(& pa->used)); - tb_data(& tb, prim_base); + tb_data(& tb, u4_(& pa->used)); + tb_data(& tb, prim_base); } // Fire off the tape. diff --git a/code/gte_hello/hello_gte.h b/code/gte_hello/hello_gte.h index 1778a39..4b2305f 100644 --- a/code/gte_hello/hello_gte.h +++ b/code/gte_hello/hello_gte.h @@ -5,6 +5,11 @@ # include "duffle/gp.h" #endif +enum { + PrimitiveBuff_Len = 4096, + OrderingTbl_Len = 2048 +}; + typedef Struct_(DrawEnv_Packed) { U4 tag; U4 code[15]; }; typedef Struct_(DrawEnv) { Rect_S2 clip_area; diff --git a/code/gte_hello/hello_gte_tape.c b/code/gte_hello/hello_gte_tape.c new file mode 100644 index 0000000..b24d9cb --- /dev/null +++ b/code/gte_hello/hello_gte_tape.c @@ -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