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

This commit is contained in:
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"
#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
+28 -58
View File
@@ -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() };
+18 -4
View File
@@ -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))
+17 -16
View File
@@ -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.
+5
View File
@@ -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;
+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