wip: starting to review and update lua metaprogram with more modeling of gte.

This commit is contained in:
ed
2026-08-13 18:51:09 -04:00
parent 4688566767
commit 640dab7e61
6 changed files with 284 additions and 61 deletions
+24
View File
@@ -191,6 +191,30 @@ enum {
gte_mask_fake_cmd = 0x1F,
};
/* --- GTE Control Register Aliases (Pitfall 1) ---
* Three pairs of aliases map to the SAME C2 control-register slot on real silicon:
* C2[24] = gte_cr_RBK (background R) | gte_cr_OFX (screen offset X)
* C2[25] = gte_cr_GBK (background G) | gte_cr_OFY (screen offset Y)
* C2[26] = gte_cr_BBK (background B) | gte_cr_H (projection plane distance H)
* Cross-alias writes inside one atom body, or across the wave-context boundary,
* silently clobber each other. The metaprogram's check_gte_cr_alias_writes
* (CHECK_RULES row) warns about each pair per source. See
* docs/gte_reference.md §"Control-register alias table" for the silicon
* rationale and the libgte outer-product convention.
*/
/* --- RT-matrix packed-slot convention (Pitfall 4) ---
* The silicon packs two 16-bit RT elements per 32-bit C2 slot:
* C2[2] = (RT22 << 16) | RT13 (gte_cr_RT13 writes the low half, gte_cr_RT22 writes the high half)
* C2[4] = (RT33 << 16) | RT22 (gte_cr_RT22 writes the low half — clobbers prior RT22 value if RT13 was also written)
* OP and MVMVA read D1/D2/D3 from these packed slots. The libgte outer-product
* convention (see ac_apply_matrix_lv at gte.atom.c:108-122) writes C2[2] then
* C2[4] in sequence; the SECOND write's low half is RT22, not RT13. An agent
* who writes gte_cr_RT13 then gte_cr_RT22 to the SAME source GPR clobbers the
* RT13 value. See docs/gte_reference.md §"RT-matrix packed-slot convention"
* for the canonical write pattern.
*/
/* --- GTE Control Register Indices (for ctc2/cfc2) ---
* Preprocessor-visible integer ids for the COP2 control register file.
* Each enum value is bound to a parallel `_Code` `#define` so the preprocessor can stringify the integer (for `reg_str`/`rgcc` paths).
+38 -2
View File
@@ -103,7 +103,15 @@ enum {
typedef U4 const MipsCode; // Underlying type to mips asm words.
typedef Slice_(MipsCode);
typedef U4 const MipsAtom; // Underlying type to an array of mips asm words that must terminate with an ac_yield.
typedef U4 const MipsAtom;
typedef Slice_(MipsAtom);
// Sometimes a user will define a bundle of atoms that represent a procedure of work as:
// MipsAtom* <identifier>[...];
// Unfortuantely if using slice_from_array it will make the slice's pointer: MipsAtom** so this enforce its defined as MipsAtom*
// TODO(Ed): Alternatively we can make the MipsAtom an opaque pointer to the atom... so that the blow returns 'MipsAtom'.
#define atombundle_from_array(array) (Slice_MipsAtom){.ptr=array[0],.len=Array_len(array)}
// Underlying type to an ptr to an array of mips asm words that must terminate with an ac_yield.
#define MipsAtom_(sym) MipsCode sym [] align_(4) =
// Used for atoms with value-args
@@ -139,7 +147,7 @@ typedef U4 const MipsAtom; // Underlying type to an array of mips asm words that
(the identifier embeds the source line, so duplicates across `#include`d files don't collide). */
#define ATOM_FILE_DEBUGGER_LINE_MARKER(file_name) internal U4 const tmpl(atom_file_debugger_line_marker,file_name) = 0
typedef Slice_(MipsAtom); typedef Slice_MipsAtom Tape;
typedef Slice_MipsAtom Tape;
/* The 'Exit' Atom */
atom_dbg_skip MipsAtom_(tape_exit) { jump_reg(rret_addr), nop };
@@ -276,6 +284,34 @@ FI_ MipsAtom* atomarena_push(AtomArena_R aa, Slice_MipsCode code) {
FI_ void atomarena_reset(AtomArena_R aa) { aa->used = 0; }
#pragma region Atom Arena
#pragma region RegFile (Register File Allocator)
// A specialized allocator utilized to help the user track which registers are bound to values
// that must be preserved for the arena's bounds.
enum {
RegFileArena_Len,
};
typedef Enum_(U4, RegFileEntry) {
// TODO(Ed): Define RF_Field, each field is maped by index + bit pos.
// the index is the upper portion of a U4 and the bit pos in the lower pos.
regfileentry_todo_,
// TODO(Ed): Is there a trick we can do with the current register enums to
// just resolve an entry automatically when doing a pin?
};
typedef Struct_(RegFile) {
U1 GPR[RegFileArena_Len];
U1 GTE[RegFileArena_Len];
U1 GP[RegFileArena_Len];
};
void regfile_pin(U4 register) {
assert(false);
}
#pragma endregion RegFileArena (Register File Allocator)
#pragma region Mips Atom Procs
#pragma endregion Mips Atom Procs
+6 -11
View File
@@ -563,17 +563,12 @@ internal MipsAtom* resolve_look_at__matrix_vector_proc(AtomArena_R aa
nop,
/* === Load RT matrix from look_at into C2[0..4] via ctc2 ===
* Exact same sequence as set_gte_mt3s2s4 / C11's ApplyMatrixLV. */
load_word(r_tmp0, r_look_at, 0), nop,
gte_mv_to_ctrl_r(r_tmp0, gte_cr_RT11),
load_word(r_tmp0, r_look_at, 4), nop,
gte_mv_to_ctrl_r(r_tmp0, gte_cr_RT12),
load_word(r_tmp0, r_look_at, 8), nop,
gte_mv_to_ctrl_r(r_tmp0, gte_cr_RT13),
load_word(r_tmp0, r_look_at, 12), nop,
gte_mv_to_ctrl_r(r_tmp0, gte_cr_RT21),
load_half_u(r_tmp0, r_look_at, 16), nop,
gte_mv_to_ctrl_r(r_tmp0, gte_cr_RT22),
* Exact s ame sequence as set_gte_mt3s2s4 / C11's ApplyMatrixLV. */
load_word( r_tmp0, r_look_at, 0), nop, gte_mv_to_ctrl_r(r_tmp0, gte_cr_RT11),
load_word( r_tmp0, r_look_at, 4), nop, gte_mv_to_ctrl_r(r_tmp0, gte_cr_RT12),
load_word( r_tmp0, r_look_at, 8), nop, gte_mv_to_ctrl_r(r_tmp0, gte_cr_RT13),
load_word( r_tmp0, r_look_at, 12), nop, gte_mv_to_ctrl_r(r_tmp0, gte_cr_RT21),
load_half_u(r_tmp0, r_look_at, 16), nop, gte_mv_to_ctrl_r(r_tmp0, gte_cr_RT22),
nop2, /* CTC2 retirement (2 slots × 5 ctc2s) */
/* Load pos = -eye after the matrix load releases r_tmp0. */
+14 -47
View File
@@ -340,7 +340,7 @@ internal void resolve_look_at_init(void) {
* the matrix pointer (popped from tape). It does NOT need GPR
* assignment from us — it has its own internal GPR usage.
* We just take its address. */
smem.resolve_look_at_atom_addrs[9] = (MipsAtom*) & set_gte_mt3s2s4;
smem.resolve_look_at_atom_addrs[7] = (MipsAtom*) & set_gte_mt3s2s4;
/* === ATOM 6b: matrix_vector (RT * (-eye) >> 12) ===
* Uses mac_apply_matrix_lv component macro which internally uses
@@ -352,7 +352,7 @@ internal void resolve_look_at_init(void) {
U4 const r_tmp0_6b = R_T2;
U4 const r_tmp1_6b = R_T3;
U4 const r_tmp2_6b = R_T5;
smem.resolve_look_at_atom_addrs[7] = resolve_look_at__matrix_vector_proc(& ab,
smem.resolve_look_at_atom_addrs[8] = resolve_look_at__matrix_vector_proc(& ab,
r_scratch_6b, r_peye_6b, r_look_at_6b,
r_tmp0_6b, r_tmp1_6b, r_tmp2_6b);
@@ -361,7 +361,7 @@ internal void resolve_look_at_init(void) {
U4 const r_scratch_6c = R_ResolveScratch;
U4 const r_off_ptr_6c = R_T1; /* &scratch.eye (= off dst) */
U4 const r_tmp0_6c = R_T2;
smem.resolve_look_at_atom_addrs[8] = resolve_look_at__trans_matrix_proc(& ab,
smem.resolve_look_at_atom_addrs[9] = resolve_look_at__trans_matrix_proc(& ab,
r_look_at_6c, r_scratch_6c, r_off_ptr_6c, r_tmp0_6c);
/* Sanity check: arena didn't overflow. */
@@ -386,43 +386,30 @@ I_ void resolve_look_at(
, P3_S4* target
, V3_S4* up_in
){
// tb_emit_bundle(tb, slice_from_array(MipsAtom, smem.resolve_look_at_atom_addrs));
/* Atom 0: input_and_sub — stages eye/up_in into scratchpad + computes fwd. */
tb_emit(tb, smem.resolve_look_at_atom_addrs[0]); {
tb_data(tb, u4_(target)); /* Binds_ResolveLookAtSub.target (C-side P3_S4*) */
tb_data(tb, u4_(eye)); /* Binds_ResolveLookAtSub.eye (C-side P3_S4*) */
tb_data(tb, u4_(up_in)); /* Binds_ResolveLookAtSub.up_in (C-side V3_S4*) */
tb_data(tb, u4_(smem.scratchpad)); /* Binds_ResolveLookAtScratch.scratch_base */
tb_data(tb, u4_(target));
tb_data(tb, u4_(eye));
tb_data(tb, u4_(up_in));
tb_data(tb, u4_(smem.scratchpad));
}
/* Atoms 1 + 2: enabled. */
tb_emit(tb, smem.resolve_look_at_atom_addrs[1]); { }
tb_emit(tb, smem.resolve_look_at_atom_addrs[2]); { }
tb_emit(tb, smem.resolve_look_at_atom_addrs[3]); { }
tb_emit(tb, smem.resolve_look_at_atom_addrs[4]); { }
tb_emit(tb, smem.resolve_look_at_atom_addrs[5]); { }
/* Atom 6a: populate — pop look_at* for the matrix destination. */
tb_emit(tb, smem.resolve_look_at_atom_addrs[6]); {
tb_data(tb, u4_(look_at)); /* Binds_ResolveLookAtPopAndTrans.look_at (MT3_S2S4*) */
tb_data(tb, u4_(look_at));
}
/* Atom 6a.5: load_rt — pop look_at* again, ctc2 look_at->m[][] into GTE
* C2[0..4]. Pattern identical to set_gte_mt3s2s4 (proven correct for
* cube rendering via RTPT/RTPS). The mac_yield between atoms gives
* the GTE pipeline time to fully retire the ctc2s. */
tb_emit(tb, smem.resolve_look_at_atom_addrs[9]); {
tb_data(tb, u4_(look_at)); /* Binds_ResolveLookAtPopAndTrans.look_at (MT3_S2S4*) */
}
/* Atom 6b: matrix_vector — pops look_at* for mac_apply_matrix_lv (which loads
* the RT matrix from it). Reads eye from scratch, packs pos as SVECTOR,
* lwc2 into V0, RTPS (sf=1, v=0, cv=3, mx=0), stores MAC1/2/3 → scratch+96. */
tb_emit(tb, smem.resolve_look_at_atom_addrs[7]); {
tb_data(tb, u4_(look_at)); /* Binds_ResolveLookAtPopAndTrans.look_at (MT3_S2S4*) */
tb_data(tb, u4_(look_at));
}
/* Atom 6c: trans_matrix — pop look_at* for the matrix destination. */
tb_emit(tb, smem.resolve_look_at_atom_addrs[8]); {
tb_data(tb, u4_(look_at)); /* Binds_ResolveLookAtPopAndTrans.look_at (MT3_S2S4*) */
tb_data(tb, u4_(look_at));
}
tb_emit(tb, smem.resolve_look_at_atom_addrs[9]); {
tb_data(tb, u4_(look_at));
}
}
@@ -485,26 +472,6 @@ void update(PrimitiveArena* pa, U4* ordering_buf)
tb.used = 0; tb_scope_run(& tb) {
resolve_look_at(& tb, & smem.cam.look_at, & smem.cam.pos, & smem.cube.pos, & v3s4(0, -fp_one, 0));
}
V3_S4 right, up, forward;
V3_S4 ux, uy, uz;
V3_S4 pos, off;
ResolveLookAtScratch_V scratch = C_scratch(ResolveLookAtScratch_V);
/* Atoms 0-5 emit into scratch; bundle dispatch for atom 6 is still
* commented at the resolve_look_at_init helper. Until atom 6 is
* enabled, populate look_at.m[][] from the wave-context outputs. */
forward = scratch->fwd;
uz = scratch->uz;
right = scratch->right;
ux = scratch->ux;
up = scratch->up;
uy = scratch->uy;
// pos = smem.cam.pos; mul_v3s4(& pos, v3s4(-1,-1,-1));
// mul_m3s2_v3s4(& smem.cam.look_at, & pos, & off);
// trans_m3s2( & smem.cam.look_at, & off);
}
// Draw cube
@@ -620,7 +587,7 @@ int main(void)
Ent_Cube* cube = & smem.cube;
cube->rot = v3s2(0, 0, 0);
cube->scale = v3s4_fp_one();
cube->accel = v3s4(0, 0, 0);
cube->accel = v3s4(0, 1, 0);
cube->pos = v3s4(0, -400, 1800);
}
ent_floor_init(& smem.floor.verts, & smem.floor.faces); {