WIP: Better step debug on atom components, better db_skip annotation, lots of curation passes on lua.

Still don't have this thing in its final state for  the curse but its close.
This commit is contained in:
ed
2026-07-26 13:55:47 -04:00
parent f247d56c32
commit 80a35aa23a
21 changed files with 1214 additions and 1244 deletions
+10 -5
View File
@@ -11,7 +11,7 @@
* Pure macro anntation.
* ---------------
* Don't want to constraint the macro usage to some attribute placment constraint, etc, don't want ot dela with the compiler.
* atom_info, atom_bind, atom_reads, atom_writes, atom_label, atom_dbg_skip_over each expand to a C comment or to nothing
* atom_info, atom_bind, atom_reads, atom_writes, atom_label, atom_dbg_skip each expand to a C comment or to nothing
* (C preprocessor strips them to whitespace).
*
* ============================================================================
@@ -90,13 +90,18 @@
#define atom_info(...) /* atom_info(__VA_ARGS__) */
/* ----------------------------------------------------------------------------
* DEBUG SOURCE-STEP MARKERS
* DEBUG SOURCE-STEP MARKER
*
* Place atom_dbg_skip_over() before a MipsAtom_, MipsAtomComp_, or MipsAtomComp_Proc_.
* Place `atom_dbg_skip` (BARE) before a MipsAtom_, MipsAtomComp_, or MipsAtomComp_Proc_.
* The following declaration kind determines whether the marker selects a whole atom or a component inline view.
* The source scanner associates the marker with that declaration; placement diagnostics are handled by the annotation pass.
*
* Example:
* atom_dbg_skip MipsAtom_(tape_exit) { jump_reg(rret_addr), nop };
* atom_dbg_skip MipsAtomComp_(ac_yield) { ... };
* atom_dbg_skip MipsAtomComp_Proc_(ac_format_f3_color, { ... });
* ----------------------------------------------------------------------------*/
#define atom_dbg_skip_over() /* atom_dbg_skip_over: skip the following atom or component source view */
#define atom_dbg_skip /* atom_dbg_skip: skip the following atom or component source view */
/* ----------------------------------------------------------------------------
* Typed-view annotations (Registry for DWARF RR_<R_X> chain resolution)
@@ -117,7 +122,7 @@
* The preferred correlation mechanism; atom_ctx is the escape hatch for non-natural cases.
*
* All three expand to C comments
* (the bare-token convention matching `atom_reg` and `atom_dbg_skip_over`).
* (the bare-token convention matching `atom_reg` and `atom_dbg_skip`).
* The Lua scanner reads the bare tokens in source-as-written; the C preprocessor strips them.
* ----------------------------------------------------------------------------*/
#define atom_type(T) /* atom_type: associate <T> with the preceding enum entry (enum site) or this register (atom-info site) */
+13
View File
@@ -9,6 +9,12 @@
#define WORD_COUNT(name, count) enum { words_##name = (count) };
#endif
/* atom_dbg_skip */
/* ---------------------------------------------------------------------------
* MACRO ATOM Components (Reusable Assembly Components)
* These do NOT yield. They are expanded inline inside Tape Atoms.
* ---------------------------------------------------------------------------*/
// The 'Yield' sequence for Tape Atoms (mac_yield).
#define mac_yield(...) \
load_word(R_AtomJmp, R_TapePtr, 0) \
, add_ui_self( R_TapePtr, S_(MipsCode)) \
@@ -16,6 +22,7 @@
, nop
WORD_COUNT(mac_yield, 4)
/* atom_dbg_skip */
/* Words: 3; Loads 3 S2 indices from the face array */
#define mac_load_tri_indices(...) \
load_half_u(R_T0, R_FaceCursor, 0 * S_(S2)) \
@@ -23,6 +30,8 @@ WORD_COUNT(mac_yield, 4)
, load_half_u(R_T2, R_FaceCursor, 2 * S_(S2))
WORD_COUNT(mac_load_tri_indices, 3)
/* atom_dbg_skip */
/* Words: 18; Translates indices to vertex addresses and pushes them to GTE */
#define mac_gte_load_tri_verts(...) \
shift_lleft(R_AT, R_T0, v3s2_byteoff) \
, add_u_self(R_AT, R_VertBase) \
@@ -80,10 +89,12 @@ WORD_COUNT(mac_insert_ot_tag_g4, 11)
, store_word( R_AT, R_PrimCursor, (off))
WORD_COUNT(mac_pack_color_word, 3)
/* atom_dbg_skip */
#define mac_format_f3_color(r, g, b) \
mac_pack_color_word(O_(Poly_F3,color), gp0_cmd_poly_f3, r, g, b)
WORD_COUNT(mac_format_f3_color, 3)
/* atom_dbg_skip */
/* 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). */
#define mac_gte_store_f3_post_rtpt(...) \
@@ -99,6 +110,7 @@ WORD_COUNT(mac_gte_store_f3_post_rtpt, 3)
, mac_pack_color_word(O_(Poly_G4,c3), 0, r3,g3,b3)
WORD_COUNT(mac_format_g4_color, 12)
/* atom_dbg_skip */
/* Words: 3; Stores the 3 transformed (V2_S2 screen) vertices of the
* G4 triangle portion to p0/p1/p2.
* PIPELINE: post-RTPT, pre-RTPS (SXY0=v0.screen, SXY1=v1.screen, SXY2=v2.screen).
@@ -113,6 +125,7 @@ WORD_COUNT(mac_format_g4_color, 12)
, gte_sw(C2_SXY2, R_PrimCursor, O_(Poly_G4,p2))
WORD_COUNT(mac_gte_store_g4_p012_post_rtpt_pre_rtps, 3)
/* atom_dbg_skip */
/* Words: 1; Stores the V3 screen coord to the G4's p3 slot.
* PIPELINE: post-RTPS (SXY2 holds v3.screen because RTPS writes its
* single-vertex result to SXY2; SXY0 still holds v0.screen from the
+2 -2
View File
@@ -2,7 +2,7 @@
* duffle DSL — GPU Vendor Mnemonics (opt-in)
* ============================================================================
*
* Provides the PSYQ-style CamelCase aliases for the canonical duffle GPU primitive setters and OT operations.
* Provides the PSYQ-style CamelCase aliases for the duffle GPU primitive setters and OT operations.
* The duffle snake_case names are primary; this header is for users who prefer the PSYQ SDK function names from the legacy C API.
*
* USAGE: #include "duffle/gp_vendor_sym.h" // after gp.h
@@ -24,7 +24,7 @@
* The gp0_cmd_* / gp1_cmd_* byte constants are already short and descriptive; no vendor alias is provided for them.
*
* The vendor mnemonics are NOT registered with the duffle word-count metadata (word_counts.metadata.h).
* They expand to the duffle canonical macros which DO have word-count entries
* They expand to the duffle macros which DO have word-count entries
* (the ones emitted by mac_format_f3_color / mac_gte_store_f3 / etc.). Verification: V13 (objdump byte-identical) holds.
* ============================================================================ */
+31 -44
View File
@@ -353,41 +353,34 @@ enum { _C2_TX_SUBS_ = 0
/* 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
* inside `asm_inline(...)` blocks (see `gte_rtpt` below for the
* canonical idiom).
* These are pure compile-time integer constants — the C compiler constant-folds them into `.word` directives in .rodata.
* Use them inside `asm_inline(...)` blocks (see `gte_rtpt` below for the idiom).
*
* Decomposition (per the `enc_gte_<field>` definitions above):
* gte_cmdw_<name> = gte_cmd_base | enc_gte_cmd(<cmd>)
* The SF/MX/V/CV/LM fields are all zero in the common cases (standard
* rotation-matrix, no scaling factor, V0 vector, translation vector,
* no clamp), so the only varying bits are the `cmd` field.
* The SF/MX/V/CV/LM fields are all zero in the common cases
* (standard rotation-matrix, no scaling factor, V0 vector, translation vector, no clamp),
* so the only varying bits are the `cmd` field.
*
* Naming follows the file's convention: `gte_cmd_*` is the raw
* 6-bit `cmd` field id, `gte_cmdw_*` is the fully-encoded 32-bit
* instruction word ready to drop into a `.word` directive.
* Naming follows the file's convention: `gte_cmd_*` is the raw 6-bit `cmd` field id, `gte_cmdw_*`
* is the fully-encoded 32-bit instruction word ready to drop into a `.word` directive.
*
* --------------------------------------------------------------------------
* PsyQ-compatibility note (RTPS/RTPT):
* The original Sony PsyQ `inline_n.h` ships RTPT as `cop2 0x0280030` and
* RTPS as `cop2 0x0180001`. Both have `0x20` set in the upper-reserved
* region (bit 21) AND `sf=1` (bit 19) — i.e. the "no division" flag.
* Per psx-spec these bits are reserved/must-be-zero, but the real GTE
* hardware and PCSX-Redux's GTE model both IGNORE them on these two
* commands (the perspective divide happens regardless of `sf`).
* The original Sony PsyQ `inline_n.h` ships RTPT as `cop2 0x0280030` and RTPS as `cop2 0x0180001`.
* Both have `0x20` set in the upper-reserved region (bit 21) AND `sf=1` (bit 19) — i.e. the "no division" flag.
* Per psx-spec these bits are reserved/must-be-zero,
* but the real GTE hardware and PCSX-Redux's GTE model both IGNORE them on these two commands
* (the perspective divide happens regardless of `sf`).
*
* If we emit a strictly-spec-compliant word (`sf=0`, reserved bits
* clear), PCSX-Redux's GTE checks those bits more strictly than the
* silicon does and RTPT silently no-ops — the floor's screen
* coordinates come out as raw projection-of-rotation (Z never
* divided), `nclip` ends up wrong, and the triangle is culled.
* If we emit a strictly-spec-compliant word (`sf=0`, reserved bits clear),
* PCSX-Redux's GTE checks those bits more strictly than the silicon does and RTPT silently no-ops —
* the floor's screen coordinates come out as raw projection-of-rotation (Z never divided),
* `nclip` ends up wrong, and the triangle is culled.
*
* So for RTPS and RTPT we OR-in the `0x28` "PsyQ compat" pattern to
* match the working bit pattern everyone has shipped for 25 years.
* NCLIP/OP/MVMVA stay spec-clean — their reserved bits really are
* zero in the original PsyQ source.
* So for RTPS and RTPT we OR-in the `0x28` "PsyQ compat" pattern to match the working bit pattern everyone has shipped for 25 years.
* NCLIP/OP/MVMVA stay spec-clean — their reserved bits really are zero in the original PsyQ source.
* --------------------------------------------------------------------------
*/
#define gte_cmdw_psyq_compat (1u << 21 | enc_gte_sf(gte_sf_integer))
@@ -424,9 +417,9 @@ enum { _C2_TX_SUBS_ = 0
* @details Loads values from an SVECTOR struct to GTE data registers C2_VXY0
* (XY at offset 0) and C2_VZ0 (Z at offset 4) using `lwc2`.
*
* Uses string-style GCC inline asm with `%0` substitution because the
* base register `r0` is a runtime GPR chosen by the compiler — it cannot
* be encoded into a static `.word` constant.
* Uses string-style GCC inline asm with `%0` substitution because the
* base register `r0` is a runtime GPR chosen by the compiler.
* It cannot be encoded into a static `.word` constant.
*
* Usage:
* asm_gte_load_v0(svector_ptr);
@@ -458,26 +451,21 @@ enum {
/* gte_load_vN(r_ptr, base) — placeholder-punned lwc2 loaders
*
* Emits `.word` constants encoding `lwc2 $N, off(<base>)` for the chosen
* GTE vector register, where `<base>` is the GPR number you pass in
* Emits `.word` constants encoding `lwc2 $N, off(<base>)` for the chosen GTE vector register, where `<base>` is the GPR number you pass in
* (typically one of R_T4..R_T9 for the standard "3-pointer" pattern).
*
* The caller MUST bind `r_ptr` to that same GPR via a register variable:
* register V3_S2* p_in_12 __asm__("$12") = my_ptr;
* gte_load_v0(p_in_12, R_T4); // R_T4 = 12, base is $12
*
* Then `"r"(r_ptr)` inside the asm binds to $12 (the only register
* `p_in_12` can live in), which is exactly the register the .word
* constants expect. A `"$12"` clobber would conflict with the
* register-variable binding ("asm specifier for variable conflicts
* with asm clobber list"), so we omit it. The other ABI-clobbers
* ($2/$8/$9/$31) stay because the GTE instructions don't touch
* caller-saved GPRs but the kernel does treat them as volatile.
* Then `"r"(r_ptr)` inside the asm binds to $12 (the only register `p_in_12` can live in),
* which is exactly the register the .word constants expect. A `"$12"` clobber would conflict with the register-variable binding
* ("asm specifier for variable conflicts with asm clobber list"), so we omit it.
* The other ABI-clobbers ($2/$8/$9/$31) stay because the GTE instructions don't touch caller-saved GPRs but the kernel does treat them as volatile.
*
* WHICH REGISTER TO PICK
* ----------------------
* Any caller-saved GPR is safe. Recommended default for an RTPT-style
* 3-pointer pipeline:
* Any caller-saved GPR is safe. Recommended default for an RTPT-style 3-pointer pipeline:
* gte_load_v0(p0, R_T4); // $12
* gte_load_v1(p1, R_T5); // $13
* gte_load_v2(p2, R_T6); // $14
@@ -490,8 +478,7 @@ enum {
* clobbers section : "$2", "$8", ..., "memory" (from asm_clobber)
* 3 colons total, GCC-legal. No string-syntax mnemonics in the .word body.
*
* The `asm_clobber(...)` helper from gcc_asm.h prepends the colon that
* starts the clobbers section. */
* The `asm_clobber(...)` helper from gcc_asm.h prepends the colon that starts the clobbers section. */
#define gte_load_v0(r_ptr, base) asm volatile( \
asm_words( gte_lw_v0_xy(base), gte_lw_v0_z(base) ) \
asm_rpins, r_use(r_ptr) \
@@ -510,11 +497,11 @@ enum {
asm_clobber: rlit(R_V0), rlit(R_T0), rlit(R_T1), rlit(R_RA), clb_mem_drain \
)
/* gte_load_v0v1v2(p0, p1, p2, b0, b1, b2) — the canonical prelude to gte_cmd_rtpt.
/* gte_load_v0v1v2(p0, p1, p2, b0, b1, b2) — prelude to gte_cmd_rtpt.
*
* Loads all three GTE input vectors (6 words) from three separate pointers,
* one per GTE vector register, each loaded from its own base GPR. Caller
* must bind each `pN` to `bN` via a register variable.
* one per GTE vector register, each loaded from its own base GPR.
* Caller must bind each `pN` to `bN` via a register variable.
*
* register V3_S2* p0 rgcc(R_T4) = verts[0].ptr; // → __asm__("$12")
* register V3_S2* p1 rgcc(R_T5) = verts[1].ptr; // → __asm__("$13")
+1 -1
View File
@@ -2,7 +2,7 @@
* duffle DSL — GTE Vendor Mnemonics (opt-in)
* ============================================================================
*
* Provides the textbook MIPS assembly mnemonics for the GTE/COP2 instructions as thin aliases to the canonical duffle macros in gte.h.
* Provides the textbook MIPS assembly mnemonics for the GTE/COP2 instructions as thin aliases to the duffle macros in gte.h.
* The duffle names are primary; this header is for users who prefer the textbook mnemonics.
*
* USAGE: #include "duffle/gte_vendor_sym.h" // after gte.h
+8 -8
View File
@@ -57,7 +57,7 @@ enum {
* ---------------------------------------------------------------------------*/
/* The 'Exit' Atom */
atom_dbg_skip_over() MipsAtom_(tape_exit) { jump_reg(rret_addr), nop };
atom_dbg_skip MipsAtom_(tape_exit) { jump_reg(rret_addr), nop };
/* Generalized Tape Engine Runner */
NI_ void tape_run(Slice_MipsCode tape) { register U4* tp rgcc(R_TapePtr) = u4_r(tape.ptr); asm volatile(
@@ -104,21 +104,21 @@ FI_ Slice_MipsCode tb_slice(TapeBuilder tb) { return (Sl
* ---------------------------------------------------------------------------*/
// The 'Yield' sequence for Tape Atoms (mac_yield).
atom_dbg_skip_over() MipsAtomComp_(ac_yield) {
atom_dbg_skip MipsAtomComp_(ac_yield) {
load_word(R_AtomJmp, R_TapePtr, 0),
add_ui_self( R_TapePtr, S_(MipsCode)),
jump_reg( R_AtomJmp), nop,
};
/* Words: 3; Loads 3 S2 indices from the face array */
atom_dbg_skip_over() MipsAtomComp_(ac_load_tri_indices) {
atom_dbg_skip MipsAtomComp_(ac_load_tri_indices) {
load_half_u(R_T0, R_FaceCursor, 0 * S_(S2)),
load_half_u(R_T1, R_FaceCursor, 1 * S_(S2)),
load_half_u(R_T2, R_FaceCursor, 2 * S_(S2)),
};
/* Words: 18; Translates indices to vertex addresses and pushes them to GTE */
atom_dbg_skip_over() MipsAtomComp_(ac_gte_load_tri_verts) {
atom_dbg_skip MipsAtomComp_(ac_gte_load_tri_verts) {
shift_lleft(R_AT, R_T0, v3s2_byteoff), add_u_self(R_AT, R_VertBase), load_word(R_V0, R_AT, O_(V3_S2,x)), load_word(R_V1, R_AT, O_(V3_S2,z)), gte_mv_to_data_r(R_V0, C2_VXY0), gte_mv_to_data_r(R_V1, C2_VZ0),
shift_lleft(R_AT, R_T1, v3s2_byteoff), add_u_self(R_AT, R_VertBase), load_word(R_V0, R_AT, O_(V3_S2,x)), load_word(R_V1, R_AT, O_(V3_S2,z)), gte_mv_to_data_r(R_V0, C2_VXY1), gte_mv_to_data_r(R_V1, C2_VZ1),
shift_lleft(R_AT, R_T2, v3s2_byteoff), add_u_self(R_AT, R_VertBase), load_word(R_V0, R_AT, O_(V3_S2,x)), load_word(R_V1, R_AT, O_(V3_S2,z)), gte_mv_to_data_r(R_V0, C2_VXY2), gte_mv_to_data_r(R_V1, C2_VZ2),
@@ -166,11 +166,11 @@ 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). */
FI_ MipsAtom ac_format_f3_color(U1 r, U1 g, U1 b)
atom_dbg_skip_over() MipsAtomComp_Proc_(ac_format_f3_color, { mac_pack_color_word(O_(Poly_F3,color), gp0_cmd_poly_f3, r, g, b) })
atom_dbg_skip 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). */
atom_dbg_skip_over() MipsAtomComp_(ac_gte_store_f3_post_rtpt) {
atom_dbg_skip 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)),
gte_sw(C2_SXY2, R_PrimCursor, O_(Poly_F3,p2)),
@@ -198,7 +198,7 @@ MipsAtomComp_Proc_(ac_format_g4_color, {
* three registers aligned with v0/v1/v2 you must store before RTPS).
* The macro name declares the pipeline position; check #6 (GTE state-
* machine validation) verifies the call site matches the declaration. */
atom_dbg_skip_over() MipsAtomComp_(ac_gte_store_g4_p012_post_rtpt_pre_rtps) {
atom_dbg_skip MipsAtomComp_(ac_gte_store_g4_p012_post_rtpt_pre_rtps) {
gte_sw(C2_SXY0, R_PrimCursor, O_(Poly_G4,p0)),
gte_sw(C2_SXY1, R_PrimCursor, O_(Poly_G4,p1)),
gte_sw(C2_SXY2, R_PrimCursor, O_(Poly_G4,p2)),
@@ -210,7 +210,7 @@ atom_dbg_skip_over() MipsAtomComp_(ac_gte_store_g4_p012_post_rtpt_pre_rtps) {
* earlier RTPT — DO NOT read SXY0 here, that's the bug this name
* prevents).
*/
atom_dbg_skip_over() MipsAtomComp_(ac_gte_store_g4_p3_post_rtps) { gte_sw(C2_SXY2, R_PrimCursor, O_(Poly_G4,p3)) };
atom_dbg_skip MipsAtomComp_(ac_gte_store_g4_p3_post_rtps) { gte_sw(C2_SXY2, R_PrimCursor, O_(Poly_G4,p3)) };
#pragma endregion Macro Atom Components
+1 -1
View File
@@ -436,7 +436,7 @@ enum { _BitOffsets = 0
/* --- Shift-amount alias (matches the gas convention `\p3 = shamt`) --- */
#define shift_amount(rd, rt, n) shift_lleft(rd, rt, n)
/* nop — canonical sll $0, $0, 0 */
/* nop — sll $0, $0, 0 */
#define nop shift_lleft(rdiscard, rdiscard, 0)
#define nop2 nop, nop
+1 -1
View File
@@ -2,7 +2,7 @@
* duffle DSL — MIPS Vendor Mnemonics (opt-in)
* ============================================================================
*
* Provides the textbook MIPS assembly mnemonics as thin aliases to the canonical duffle macros in mips.h.
* Provides the textbook MIPS assembly mnemonics as thin aliases to the duffle macros in mips.h.
* The duffle names are primary; this header is for users who prefer the textbook mnemonics.
*
* USAGE: #include "duffle/mips_vendor_sym.h" // after mips.h
+1 -1
View File
@@ -103,7 +103,7 @@ MipsAtom_(rbind_floor_f3_face) atom_info(atom_bind(Binds_FloorTri), atom_phase(f
mac_yield()
};
// atom_dbg_skip_over()
atom_dbg_skip
internal
MipsAtom_(floor_f3_face) atom_info(atom_phase(floor_f3)
, atom_reads( R_PrimCursor, R_FaceCursor, R_VertBase, R_OtBase)