wip: cube_g4_face is bugged

This commit is contained in:
2026-07-09 16:17:34 -04:00
parent 1fb4883138
commit ca3dc4aff0
8 changed files with 1270 additions and 1202 deletions
+24 -15
View File
@@ -50,14 +50,32 @@ WORD_COUNT(mac_load_tri_verts, 18)
/* Words: 11; Correctly inserts a primitive into the Ordering Table linked list.
* Hardcoded for Poly_F3 (5 words). For Poly_G4, use ac_insert_ot_tag_g4. */
#define mac_insert_ot_tag_f3(...) \
shift_lright(R_AT, R_AT, S_(polytag_len_bits))
WORD_COUNT(mac_insert_ot_tag_f3, 1)
shift_lleft( R_T1, R_T1, S_(U4)/2) /* T1 = otz * S_(U4) (otz arg is implicit R_T1) */ \
, add_u_self( R_T1, R_OtBase) /* T1 = & OrderingTable[OTZ] */ \
, load_word( R_AT, R_T1, O_(PolyTag,bf_addr_len)) /* AT = old_ot_head */ \
, load_upper_i(R_V0, (S_(Poly_F3)/S_(U4) - S_(PolyTag)/S_(U4)) << polytag_len_bits) /* V0 = (5 - 1) << 24 = 4 << 24 */ \
, mask_upper( R_AT, R_AT, S_(polytag_len_bits)) /* Strip upper 8 bits (length from prev cell) → keep only low 24 */ \
, or_u( R_AT, R_AT, R_V0) /* Merge length */ \
, store_word( R_AT, R_PrimCursor, O_(PolyTag,bf_addr_len)) /* prim->tag = packed(prim_length, old_addr) */ \
, shift_lleft( R_AT, R_PrimCursor, S_(polytag_len_bits)) /* AT = (prim_length << 24) | old_addr */ \
, shift_lright(R_AT, R_AT, S_(polytag_len_bits)) \
, store_word( R_AT, R_T1, O_(PolyTag,bf_addr_len)) /* OrderingTable[OTZ] = PrimCursor */
WORD_COUNT(mac_insert_ot_tag_f3, 10)
/* Words: 11; Correctly inserts a primitive into the Ordering Table linked list.
* Hardcoded for Poly_G4 (9 words). For Poly_F3, use ac_insert_ot_tag_f3. */
#define mac_insert_ot_tag_g4(...) \
shift_lright(R_AT, R_AT, S_(polytag_len_bits))
WORD_COUNT(mac_insert_ot_tag_g4, 1)
shift_lleft( R_T1, R_T1, S_(U4)/2) /* T1 = otz * S_(U4) (otz arg is implicit R_T1) */ \
, add_u_self( R_T1, R_OtBase) /* T1 = & OrderingTable[OTZ] */ \
, load_word( R_AT, R_T1, O_(PolyTag,bf_addr_len)) /* AT = old_ot_head */ \
, load_upper_i(R_V0, (S_(Poly_G4)/S_(U4) - S_(PolyTag)/S_(U4)) << polytag_len_bits) /* V0 = (9 - 1) << 24 = 8 << 24 */ \
, mask_upper( R_AT, R_AT, S_(polytag_len_bits)) /* Strip upper 8 bits (length from prev cell) → keep only low 24 */ \
, or_u( R_AT, R_AT, R_V0) /* Merge length */ \
, store_word( R_AT, R_PrimCursor, O_(PolyTag,bf_addr_len)) /* prim->tag = packed(prim_length, old_addr) */ \
, shift_lleft( R_AT, R_PrimCursor, S_(polytag_len_bits)) /* AT = (prim_length << 24) | old_addr */ \
, shift_lright(R_AT, R_AT, S_(polytag_len_bits)) \
, store_word( R_AT, R_T1, O_(PolyTag,bf_addr_len)) /* OrderingTable[OTZ] = PrimCursor */
WORD_COUNT(mac_insert_ot_tag_g4, 10)
#define mac_pack_color_word(off, code, r, g, b) \
load_upper_i(R_AT, (code) << 8 | (b)) \
@@ -70,9 +88,7 @@ WORD_COUNT(mac_pack_color_word, 3)
WORD_COUNT(mac_format_f3_color, 1)
/* Words: 3; Stores the 3 transformed (V2_S2 screen) vertices to the F3.
* PIPELINE: post-RTPT (SXY0=v0.screen, SXY1=v1.screen, SXY2=v2.screen).
* The macro name declares the pipeline position; check #6 (GTE state-
* machine validation) verifies the call site matches the declaration. */
* PIPELINE: post-RTPT (SXY0=v0.screen, SXY1=v1.screen, SXY2=v2.screen). */
#define mac_gte_store_f3_post_rtpt(...) \
gte_sw(C2_SXY0, R_PrimCursor, O_(Poly_F3,p0)) \
, gte_sw(C2_SXY1, R_PrimCursor, O_(Poly_F3,p1)) \
@@ -105,14 +121,7 @@ WORD_COUNT(mac_gte_store_g4_p012_post_rtpt_pre_rtps, 3)
* single-vertex result to SXY2; SXY0 still holds v0.screen from the
* earlier RTPT — DO NOT read SXY0 here, that's the bug this name
* prevents).
* The macro name declares the pipeline position; check #6 (GTE state-
* machine validation) verifies the call site matches the declaration.
*
* History: this macro was named `mac_gte_store_g4_p3` until 2026-07-09
* when it was discovered to be reading C2_SXY0 (which held v0.screen)
* instead of C2_SXY2 (which holds v3.screen after RTPS). The rename
* encodes the pipeline position in the name so the next bug of this
* class is impossible. */
*/
#define mac_gte_store_g4_p3_post_rtps(...) \
gte_sw(C2_SXY2, R_PrimCursor, O_(Poly_G4,p3))
WORD_COUNT(mac_gte_store_g4_p3_post_rtps, 1)
+24 -35
View File
@@ -28,9 +28,8 @@ typedef Slice_MipsCode MipsAtom;
// FI_ MipsAtom ac_X(args) { MipsCode ac_X[] align_(4) = { body }; return slice_from_array(MipsCode, ac_X); }
#define MipsAtomComp_Proc_(sym, ...) { MipsCode sym [] align_(4) = __VA_ARGS__; return slice_from_array(MipsCode, sym); }
// Auto-generated component macros (build/gen/<dir>/<dir>.components.h)
// Auto-generated component macros (<module>/gen/<dir>/<dir>.macs.h)
// are included manually by the unity build. The metaprogram puts them
// under ./build/gen/ (not in the code dirs) so the source tree stays clean.
/* Register aliases (moved up from the Tape Drive region below so that
* mac_yield's body and the Mips Atom Builder functions can reference
@@ -137,9 +136,9 @@ MipsAtomComp_(ac_load_tri_verts) {
/* Words: 11; Correctly inserts a primitive into the Ordering Table linked list.
* Hardcoded for Poly_F3 (5 words). For Poly_G4, use ac_insert_ot_tag_g4. */
MipsAtomComp_(ac_insert_ot_tag_f3) {
shift_lleft( R_T1, R_T1, S_(U4)/2) /* T1 = otz * S_(U4) (otz arg is implicit R_T1) */
, add_u_self( R_T1, R_OtBase) /* T1 = & OrderingTable[OTZ] */
, load_word( R_AT, R_T1, O_(PolyTag,bf_addr_len)) /* AT = old_ot_head */
shift_lleft( R_T1, R_T1, S_(U4)/2) // T1 = otz * S_(U4) (otz arg is implicit R_T1)
, add_u_self( R_T1, R_OtBase) // T1 = & OrderingTable[OTZ]
, load_word( R_AT, R_T1, O_(PolyTag,bf_addr_len)) // AT = old_ot_head
, load_upper_i(R_V0, (S_(Poly_F3)/S_(U4) - S_(PolyTag)/S_(U4)) << polytag_len_bits) /* V0 = (5 - 1) << 24 = 4 << 24 */
, mask_upper( R_AT, R_AT, S_(polytag_len_bits)) /* Strip upper 8 bits (length from prev cell) → keep only low 24 */
, or_u( R_AT, R_AT, R_V0) /* Merge length */
@@ -177,17 +176,14 @@ MipsAtomComp_Proc_(ac_pack_color_word, {
/* Words: 3; Emits the F3 command+color word (cmd byte | BLUE | GREEN | RED)
* Args: _r, _g, _b are 8-bit RGB byte values (not raw 16-bit fields).
* Migrated from hello_gte_tape.c; takes RGB form per the Phase 3
* convention. */
* Migrated from hello_gte_tape.c; takes RGB form per the Phase 3 convention. */
FI_ MipsAtom ac_format_f3_color(U1 r, U1 g, U1 b)
MipsAtomComp_Proc_(ac_format_f3_color, {
mac_pack_color_word(O_(Poly_F3,color), gp0_cmd_poly_f3, r, g, b)
})
/* Words: 3; Stores the 3 transformed (V2_S2 screen) vertices to the F3.
* PIPELINE: post-RTPT (SXY0=v0.screen, SXY1=v1.screen, SXY2=v2.screen).
* The macro name declares the pipeline position; check #6 (GTE state-
* machine validation) verifies the call site matches the declaration. */
* PIPELINE: post-RTPT (SXY0=v0.screen, SXY1=v1.screen, SXY2=v2.screen). */
MipsAtomComp_(ac_gte_store_f3_post_rtpt) {
gte_sw(C2_SXY0, R_PrimCursor, O_(Poly_F3,p0))
, gte_sw(C2_SXY1, R_PrimCursor, O_(Poly_F3,p1))
@@ -196,10 +192,11 @@ MipsAtomComp_(ac_gte_store_f3_post_rtpt) {
/* Words: 12; Emits the four (code|color) words of a Poly_G4.
* Args: rN,gN,bN are 8-bit RGB byte values for each of the 4 vertices. */
FI_ MipsAtom ac_format_g4_color(U1 r0, U1 g0, U1 b0,
U1 r1, U1 g1, U1 b1,
U1 r2, U1 g2, U1 b2,
U1 r3, U1 g3, U1 b3)
FI_ MipsAtom ac_format_g4_color(
U1 r0, U1 g0, U1 b0,
U1 r1, U1 g1, U1 b1,
U1 r2, U1 g2, U1 b2,
U1 r3, U1 g3, U1 b3)
MipsAtomComp_Proc_(ac_format_g4_color, {
mac_pack_color_word(O_(Poly_G4,c0), gp0_cmd_poly_g4, r0,g0,b0)
, mac_pack_color_word(O_(Poly_G4,c1), 0, r1,g1,b1)
@@ -226,14 +223,7 @@ MipsAtomComp_(ac_gte_store_g4_p012_post_rtpt_pre_rtps) {
* single-vertex result to SXY2; SXY0 still holds v0.screen from the
* earlier RTPT — DO NOT read SXY0 here, that's the bug this name
* prevents).
* The macro name declares the pipeline position; check #6 (GTE state-
* machine validation) verifies the call site matches the declaration.
*
* History: this macro was named `mac_gte_store_g4_p3` until 2026-07-09
* when it was discovered to be reading C2_SXY0 (which held v0.screen)
* instead of C2_SXY2 (which holds v3.screen after RTPS). The rename
* encodes the pipeline position in the name so the next bug of this
* class is impossible. */
*/
MipsAtomComp_(ac_gte_store_g4_p3_post_rtps) {
gte_sw(C2_SXY2, R_PrimCursor, O_(Poly_G4,p3))
};
@@ -277,26 +267,25 @@ enum {
};
/* Flushes the Instruction Cache (PSX A-function 0x44 via BIOS stub at 0xA0).
*
* Sequence (per MIPS ABI; arguments in arg registers, RA pushed to stack):
* 1. sp -= 8; sw $ra, 4($sp) ; save RA
* 2. $a0 = bios_flushcache (arg0)
* 3. $t0 = bios_table_addr ; t0 = &BIOS A-function table
* 4. jalr $t0, $ra ; call BIOS(flushcache)
* nop ; branch delay slot
* 5. lw $ra, 4($sp); jr $ra ; restore & return
* 4. jalr $t0, $ra ; call BIOS(flushcache)
* nop ; branch delay slot
* 5. lw $ra, 4($sp); jr $ra ; restore & return
* 6. sp += 8
*/
internal MipsAtom_(mips_flush_icache) {
add_ui(rstack_ptr, rstack_ptr, -MipsStackAlignment) /* sp -= 8 */
, store_word(rret_addr, rstack_ptr, S_(U4)) /* sw $ra, 4($sp) */
, add_ui(rret_0, rdiscard, bios_flushcache) /* addiu $a0, $0, 0x44 */
, add_ui(rtmp_0, rdiscard, bios_table_addr) /* addiu $t0, $0, 0xA0 */
, jump_link(rtmp_0, rret_addr) /* jalr $t0, $ra */
, nop /* BD slot */
, load_word(rret_addr, rstack_ptr, S_(U4)) /* lw $ra, 4($sp) */
, jump_reg(rret_addr) /* jr $ra */
, add_ui(rstack_ptr, rstack_ptr, MipsStackAlignment) /* sp += 8 (BD) */
add_ui(rstack_ptr, rstack_ptr, -MipsStackAlignment) // sp -= 8
, store_word(rret_addr, rstack_ptr, S_(U4)) // sw $ra, 4($sp)
, add_ui(rret_0, rdiscard, bios_flushcache) // addiu $a0, $0, 0x44
, add_ui(rtmp_0, rdiscard, bios_table_addr) // addiu $t0, $0, 0xA0
, jump_link(rtmp_0, rret_addr) // jalr $t0, $ra
, nop // BD slot
, load_word(rret_addr, rstack_ptr, S_(U4)) // lw $ra, 4($sp)
, jump_reg(rret_addr) // jr $ra
, add_ui(rstack_ptr, rstack_ptr, MipsStackAlignment) // sp += 8 (BD)
, mac_yield()
};
+4 -4
View File
@@ -5,9 +5,9 @@
#pragma region hello_gte_tape
// --- atom: cube_g4_face (44 words) ---
// --- atom: cube_g4_face (42 words) ---
#define _atom_offset_cull_cube_g4_face_exit 25
#define _atom_offset_cull_cube_g4_face_exit 26
#define _atom_offset_bounds_chk_cube_g4_face_exit 2
enum {
@@ -15,9 +15,9 @@ enum {
atom_offset_bounds_chk_cube_g4_face_exit = _atom_offset_bounds_chk_cube_g4_face_exit,
};
// --- atom: floor_f3_face (30 words) ---
// --- atom: floor_f3_face (47 words) ---
#define _atom_offset_culling_floor_f3_face_exit 15
#define _atom_offset_culling_floor_f3_face_exit 14
#define _atom_offset_bounds_chk_floor_f3_face_exit 3
enum {
+1
View File
@@ -22,6 +22,7 @@
# include "tape_atom.metadata.h"
# include "gen/gte_hello.offsets.h"
#include "hello_gte.h"
#include "hello_gte_tape.c"
typedef U4 OrderingTable_Buffer[OrderingTbl_Len];
+1
View File
@@ -4,6 +4,7 @@
# include "duffle/atom_dsl.h"
# include "duffle/lottes_tape.h"
# include "tape_atom.metadata.h"
# include "gen/gte_hello.offsets.h"
# include "hello_gte.h"
#endif
+4 -1
View File
@@ -345,7 +345,10 @@ function M.split_top_level_commas(body)
token_start = i
else
local nx = M.skip_str_or_cmt(body, i)
if nx > i then i = nx; token_start = nx else i = i + 1 end
-- Don't advance token_start on skip: a trailing `/* ... */` after
-- a macro call is part of the current entry's chunk, not the
-- start of the next one.
if nx > i then i = nx else i = i + 1 end
end
end
local last = body:sub(token_start)
+28 -6
View File
@@ -427,12 +427,34 @@ end
-- ============================================================
local function main(args)
if #args < 2 then
print("Usage: luajit gen_atom_offsets.lua <metadata.h> <source1> [source2 ...]")
os.exit(1)
end
local word_counts = load_word_counts(args[1])
for i = 2, #args do process_source(args[i], word_counts) end
if #args < 2 then
print("Usage: luajit gen_atom_offsets.lua <metadata.h> <source1> [source2 ...]")
os.exit(1)
end
local word_counts = load_word_counts(args[1])
for i = 2, #args do
local source_path = args[i]
-- Also load the auto-generated <dir>.macs.h for this source's
-- directory (if it exists). The metaprogram emits
-- WORD_COUNT(mac_X, N) entries there for the MipsAtomComp_
-- declarations in the source. Merging ensures the offset
-- computation has the right word counts (no "unknown macro"
-- warnings for the auto-generated components).
local source_dir = dirname(source_path)
-- dirname() keeps the trailing separator; basename_no_ext()
-- on a path with a trailing separator returns "". Strip it.
if #source_dir > 0 and (source_dir:sub(-1) == "/" or source_dir:sub(-1) == "\\") then
source_dir = source_dir:sub(1, -2)
end
local macs_path = source_dir .. "/gen/" .. basename_no_ext(source_dir) .. ".macs.h"
local ok, mc = pcall(load_word_counts, macs_path)
if ok then
for name, count in pairs(mc) do
word_counts[name] = count
end
end
process_source(source_path, word_counts)
end
end
main({...})
File diff suppressed because it is too large Load Diff