From d4795cf9de00678ae8cba0d76bb355003c39349f Mon Sep 17 00:00:00 2001 From: Ed_ Date: Mon, 17 Aug 2026 10:34:43 -0400 Subject: [PATCH] Extract out a cross roduct component. --- code/duffle/gen/macs.h | 16 +++++++ code/duffle/gte.atom.c | 20 ++++---- scripts/passes/components.lua | 89 ++++++++++++++++++++++++++++++++++- 3 files changed, 116 insertions(+), 9 deletions(-) diff --git a/code/duffle/gen/macs.h b/code/duffle/gen/macs.h index 3afc8cc..034cc21 100644 --- a/code/duffle/gen/macs.h +++ b/code/duffle/gen/macs.h @@ -211,6 +211,18 @@ WORD_COUNT(mac_gte_mv_to_cr_diag_v3s4, 3) , gte_mv_to_data_r(v.z, C2_IR3) WORD_COUNT(mac_gte_ld_ir123_v3s4, 3) +/* atom_dbg_skip */ +#define mac_gte_op_cross_v3s4(a, b) \ + mac_gte_mv_to_cr_diag_v3s4(a) \ + GteDelay_ /* RT diagonal: D1 = a.x, D2 = a.y, D3 = a.z */ \ +, mac_gte_ld_ir123_v3s4(b) \ + GteDelay_ /* IR: second operand (b.xyz) */ \ +, gte_cmdw_cross /* OP: MAC1/2/3 = a × b (S12.20) */ \ +, mac_gte_mv_from_mac123_v3s4(a) \ + GteDelay_ /* Read MAC1/2/3 → a.xyz (overwrites source-A's load targets) */ \ +, mac_shift_aright_v3s4_self(a, 12) /* Right-shift MAC by 12 (S12.20 → S12.0 OuterProduct12) */ +WORD_COUNT(mac_gte_op_cross_v3s4, 16) + /* atom_dbg_skip */ #define mac_gte_store_f3(r_primitive_cursor) \ gte_sw(C2_SXY0, r_primitive_cursor, O_(Poly_F3,p0)) \ @@ -322,6 +334,10 @@ WORD_COUNT(mac_gte_general_purpose_interopolation, 10) , gte_mv_from_data_r(fr_mac3, C2_MAC3) WORD_COUNT(mac_gte_mv_from_data_r_mac123, 3) +#define mac_gte_mv_from_mac123_v3s4(v) \ + mac_gte_mv_from_data_r_mac123(v.x, v.y, v.z) +WORD_COUNT(mac_gte_mv_from_mac123_v3s4, 3) + /* atom_dbg_skip */ #define mac_gcmd_push(cmd, reg_transfer, reg_base, port) \ mac_load_word_imm(reg_transfer, cmd) \ diff --git a/code/duffle/gte.atom.c b/code/duffle/gte.atom.c index 5ed9384..81e2dc6 100644 --- a/code/duffle/gte.atom.c +++ b/code/duffle/gte.atom.c @@ -30,6 +30,14 @@ FI_ Slice_MipsCode ac_gte_ld_ir123_v3s4(AtomBuilder_R ab, Reg_(V3_S4) v) MipsAto gte_mv_to_data_r(v.z, C2_IR3), }) +FI_ Slice_MipsCode ac_gte_op_cross_v3s4(AtomBuilder_R ab, Reg_(V3_S4) a, Reg_(V3_S4) b) atom_dbg_skip MipsAtomComp_Proc_(ab, { + mac_gte_mv_to_cr_diag_v3s4(a), GteDelay_ /* RT diagonal: D1 = a.x, D2 = a.y, D3 = a.z */ + mac_gte_ld_ir123_v3s4(b), GteDelay_ /* IR: second operand (b.xyz) */ + gte_cmdw_cross, /* OP: MAC1/2/3 = a × b (S12.20) */ + mac_gte_mv_from_mac123_v3s4(a), GteDelay_ /* Read MAC1/2/3 → a.xyz (overwrites source-A's load targets) */ + mac_shift_aright_v3s4_self(a, 12), /* Right-shift MAC by 12 (S12.20 → S12.0 OuterProduct12) */ +}) + /* 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). */ FI_ Slice_MipsCode ac_gte_store_f3(AtomBuilder_R ab, U4 r_primitive_cursor) atom_dbg_skip MipsAtomComp_Proc_(ab, { @@ -173,6 +181,9 @@ MipsAtomComp_Proc_(ab, { gte_mv_from_data_r(fr_mac3, C2_MAC3), }) + +FI_ Slice_MipsCode ac_gte_mv_from_mac123_v3s4(AtomBuilder_R ab, Reg_(V3_S4) v) MipsAtomComp_ProcMap_(ab, mac_gte_mv_from_data_r_mac123(v.x, v.y, v.z)) + #pragma endregion MACs (Mips Atom Components) #pragma region Atom Procs @@ -348,18 +359,11 @@ atom_info(atom_bind(Binds_gte_cross_v3s4)) MipsAtom_Proc_(aa, { mac_load_v3s4(r.a, r.src_a, 0), LdSlot_ mac_load_v3s4(r.b, r.src_b, 0), LdSlot_ - - mac_gte_mv_to_cr_diag_v3s4(r.a), GteDelay_ /* RT diagonal: D1 = a.x, D2 = a.y, D3 = a.z. */ - mac_gte_ld_ir123_v3s4(r.b), GteDelay_ /* IR: second operand. */ - gte_cmdw_cross, /* MAC1/2/3 = a × b */ - mac_gte_mv_from_data_r_mac123(r.a.x, r.a.y, r.a.z), GteDelay_ //nop, - - mac_shift_aright_v3s4_self(r.a, 12), + mac_gte_op_cross_v3s4(r.a, r.b), /* RT diagonal + IR + OP + MAC read + shift (one component call). */ mac_store_v3s4(r.a, r.out, 0), mac_yield() }) - #pragma endregion Atom Procs #pragma region Baked Atoms diff --git a/scripts/passes/components.lua b/scripts/passes/components.lua index 0ca7090..5711edc 100644 --- a/scripts/passes/components.lua +++ b/scripts/passes/components.lua @@ -504,16 +504,103 @@ local function strip_trailing_continuation(lines) end end +--- Classify a token as a "pure delay marker token" (a delay-marker identifier +--- with no following instruction — only whitespace and/or block comments). +--- Examples that match: +--- * `GteDelay_` → marker alone +--- * `GteDelay_ /* RT diagonal: D1 = a.x... */` → marker + block comment +--- * `GteDelay_ /* RT diagonal: ... */\n\t` → marker + comment + trailing whitespace +--- Examples that DO NOT match (these contain a real instruction after the marker +--- and must be preserved verbatim so the instruction still gets emitted): +--- * `GteDelay_ nop2` +--- * `GteDelay_ add_si(r.dst_ptr, r.scratch, dst_offset)` +--- +--- Why this classification matters: the metaprogram emits tokens separated by `,` +--- and joins them with `\` line continuations. After C preprocessor +--- phase 2 (line splicing), the macro body collapses to a single logical line. +--- Each delay-marker identifier expands to empty (its definition +--- `#define GteDelay_ // ...` consumes the `//` line comment during preprocessing +--- of the definition itself, leaving an empty replacement list). When a token +--- is purely a delay marker with only a trailing comment, the `,` the metaprogram +--- normally adds before each token-after-the-first brackets empty content and +--- produces the syntax error `,,` (`expected expression before ',' token`) at +--- C compile. The metaprogram therefore emits such tokens WITHOUT the leading +--- `,` (see `token_skips_leading_comma`) — but the marker + trailing comment +--- are still emitted verbatim so the annotation is preserved in `gen/macs.h`. +--- @param tok string -- a single token from split_top_level_commas (already trimmed at the start, may contain trailing whitespace + block comment) +--- @return boolean +local function is_pure_delay_marker_token(tok) + local markers = duffle.DELAY_MARKERS + if type(markers) ~= "table" then return false end + + -- Identify a leading delay-marker identifier (e.g. `GteDelay_`). + local ident_end = 1 + while ident_end <= #tok do + local ch = tok:sub(ident_end, ident_end) + if ch:match("[%w_]") then + ident_end = ident_end + 1 + else + break + end + end + local ident = tok:sub(1, ident_end - 1) + if not markers[ident] then return false end + + -- Walk the remainder: only whitespace and block comments are allowed. + local scan = ident_end + while scan <= #tok do + local ch = tok:sub(scan, scan) + if ch:match("%s") then + scan = scan + 1 + elseif ch == "/" and tok:sub(scan + 1, scan + 1) == "*" then + local close = tok:find("*/", scan + 2, true) + if not close then return false end + scan = close + 2 + else + -- Non-whitespace, non-block-comment content: a real instruction + -- follows the marker (e.g. `GteDelay_ nop2`); keep this token intact. + return false + end + end + return true +end + +--- Classify a token's "leading comma requirement". +--- Pure delay-marker tokens (`GteDelay_` / `LdSlot_` / `BdSlot_` / `DmaSlot_` +--- followed by whitespace + optional block comment and NOTHING ELSE) expand +--- to empty at C preprocessor time. Emitting them WITHOUT the leading `,` +--- separator that the metaprogram normally adds before each token after the +--- first keeps exactly one `,` between the surrounding real expressions in +--- the spliced macro body: +--- +--- * before this rule: ` ,\t ,\t` → after expansion +--- ` , /* comment */ , ` → `,,` syntax error. +--- * after this rule: ` \t ,\t` → after expansion +--- ` /* comment */ , ` → `, ` — valid. +--- +--- Tokens like `GteDelay_ nop2` keep the leading `,` (the marker is followed +--- by a real instruction, so the marker + instruction together need the +--- separator on the LEFT to land between two real expressions). +--- @param tok string +--- @return boolean -- true if the token needs NO leading `,` separator. +local function token_skips_leading_comma(tok) + return is_pure_delay_marker_token(tok) +end + --- Emit the `#define mac_X(sig) \\t \,\t ...` block. --- Converts `//` line comments to `/* */` block comments in each token so they don't break the C macro `\` line continuations. +--- +--- Pure delay-marker tokens (`GteDelay_` / `LdSlot_` / `BdSlot_` / `DmaSlot_` with only a trailing block comment, no real instruction) are emitted WITHOUT a leading `,` separator; the annotation IS preserved in the generated header (so the comment + marker remain visible to anyone reading `gen/macs.h`), but the C preprocessor expands the marker to empty, so leaving the `,` separator out is what stops the `,,` syntax error. See `token_skips_leading_comma` for the contract. local function emit_macro_body(lines, c, sig, tokens) for tok_idx = 1, #tokens do tokens[tok_idx] = convert_line_comments_to_block(tokens[tok_idx]) end + if #tokens == 0 then return end lines[#lines + 1] = "#define mac_" .. c.name .. "(" .. sig .. ") \\" lines[#lines + 1] = "\t" .. tokens[1] .. " \\" for tok_idx = 2, #tokens do - lines[#lines + 1] = ",\t" .. tokens[tok_idx] .. " \\" + local sep = token_skips_leading_comma(tokens[tok_idx]) and "\t" or ",\t" + lines[#lines + 1] = sep .. tokens[tok_idx] .. " \\" end strip_trailing_continuation(lines) end