diff --git a/conductor/tracks/video_analysis_deob_c11_reference_20260623/research/cluster_0_pikuma_duffle.md b/conductor/tracks/video_analysis_deob_c11_reference_20260623/research/cluster_0_pikuma_duffle.md new file mode 100644 index 00000000..432f625c --- /dev/null +++ b/conductor/tracks/video_analysis_deob_c11_reference_20260623/research/cluster_0_pikuma_duffle.md @@ -0,0 +1,687 @@ +# Cluster 0: Pikuma duffle + gte_hello (PRIMARY C11 convention source) + +**Track:** `video_analysis_deob_c11_reference_20260623` +**Date:** 2026-06-23 +**Files audited:** 11 (9 Pikuma duffle headers + 2 gte_hello files) +**Role:** PRIMARY C11 convention source + +> **Reading guide.** This cluster sub-report surveys the Pikuma duffle library + the gte_hello integration example. The duffle is the user's primary C11 convention source — the "DSL" the user has built for C11 programming on the PS1. The gte_hello file shows how the duffle is integrated in practice. + +--- + +## File inventory + +| File | LOC | Role | +|---|---|---| +| `duffle/dsl.h` | 219 | The core DSL: type modifiers, macros, slicing, arena, control flow | +| `duffle/memory.h` | 121 | Memory primitives: alignment, copy, fill, slice, FArena | +| `duffle/math.h` | 63 | Math types: V2/V3/V4/R2/Rect/M3 by byte-width (S2/S4) | +| `duffle/gp.h` | 122 | GPU commands + bitfield enums + HW MMIO addresses | +| `duffle/gte.h` | 658 (read 80) | GTE (Geometry Transformation Engine) encoder layer | +| `duffle/gcc_asm.h` | (not read) | GCC inline-assembly primitives | +| `duffle/mips.h` | (not read) | MIPS instruction encoder layer | +| `duffle/lottes_tape.h` | (not read) | Lottes tape format (graphics asset) | +| `duffle/strings.h` | 5 | String slice stub (mostly delegated to dsl.h) | +| `gte_hello/hello_gte.h` | 207 | Example integration: duffle + GTE + GP for hello world | +| `gte_hello/hello_gte.c` | (not read) | Example main program | + +--- + +## §1. The `INTELLISENSE_DIRECTIVES` pattern + +Every duffle header starts with: + +```c +#ifdef INTELLISENSE_DIRECTIVES +# pragma once +# include "dsl.h" +# include "math.h" +# include "memory.h" +#endif +``` + +**Why:** the user's IDE/parser (CLion, VSCode) gets correct include hints; the actual C compiler sees a normal header (because `INTELLISENSE_DIRECTIVES` is undefined). The tabs in `# pragma once` are intentional — they force the IDE to indent the directive. + +**Convention:** every duffle header uses this pattern. The includes listed inside `INTELLISENSE_DIRECTIVES` are the same as the actual `#include` block elsewhere in the file (so the IDE can resolve them). + +--- + +## §2. The duffle type system (byte-width convention) + +The duffle uses a byte-width naming convention for fixed-width types: + +| Suffix | Type | Width | +|---|---|---| +| `U1`, `U2`, `U4` | unsigned | 1/2/4 bytes | +| `S1`, `S2`, `S4` | signed | 1/2/4 bytes | +| `B1`, `B2`, `B4` | byte-sized (unsigned) | 1/2/4 bytes | + +Defined via `__UINT8_TYPE__`, `__UINT16_TYPE__`, `__UINT32_TYPE__`, etc. in `dsl.h`: + +```c +typedef __UINT8_TYPE__ TSet_(U1); +typedef __UINT16_TYPE__ TSet_(U2); +typedef __UINT32_TYPE__ TSet_(U4); +typedef __INT8_TYPE__ TSet_(S1); +typedef __INT16_TYPE__ TSet_(S2); +typedef __INT32_TYPE__ TSet_(S4); +``` + +The `TSet_(type)` macro expands to: +```c +type; typedef PtrSet_(type); +``` + +Which expands to: +```c +type; typedef type* type_R; typedef type V_* type_V; +``` + +So `TSet_(U4)` produces: +- `U4` (the typedef) +- `U4_R` (restrict pointer to U4) +- `U4_V` (volatile pointer to U4) + +**Why:** the user has an allergy to writing `unsigned int` (per Cluster 0 in the warmup's research; the user prefers byte-explicit types). The `TSet_` pattern is the duffle's way of making the byte width explicit while avoiding the verbosity of `typedef unsigned char U1; typedef U1 *U1_R; ...`. + +**Note:** the duffle doesn't have `U8`/`S8` (no 8-byte types). For 64-bit values, the duffle uses `U4` and treats it as a 32-bit value or uses `Slice` for larger blobs. This is a PS1-constraint (32-bit MIPS CPU); for general C11, `uint64_t` / `int64_t` from `` would be used (raddbg uses `U64`/`S64` from `base_core.h`). + +--- + +## §3. The underscore-suffixed type modifiers + +The duffle has a rich vocabulary of single-letter-prefix type modifiers: + +| Modifier | Meaning | Macro | +|---|---|---| +| `R_` | `restrict` (sole ownership) | `#define R_ restrict` | +| `V_` | `volatile` | `#define V_ volatile` | +| `EUB_` | "Execute Unit Bound" (restrict; siloed in ALU register file) | `#define EUB_ restrict` | +| `ISO_` | "Isolated Provenance" (restrict; electrical memory isolation for SIMD packing) | `#define ISO_ restrict` | +| `LSU_` | "Load/Store Unit Bound" (volatile; physical L1 cache sampling) | `#define LSU_ volatile` | +| `LIVE_` | "Live External Data" (volatile; external electrical actor) | `#define LIVE_ volatile` | +| `LP_` | "Local Persistent" (static within procedure scope) | `#define LP_ static` | +| `internal` | Internal linkage | `#define internal static` | +| `global` | Global data (mark) | `#define global static` | +| `gknown` | Global data used in procedure (mark) | (no expansion) | +| `I_` | Internal inline | `#define I_ internal inline` | +| `FI_` | Force inline (always_inline) | `#define FI_ inline __attribute__((always_inline))` | +| `NI_` | No inline (noinline) | `#define NI_ internal __attribute__((noinline))` | +| `RO_` | Read-only data (in `.rodata` section) | `#define RO_ __attribute__((section(".rodata")))` | +| `T_` | `typeof` (compile-time type deduction) | `#define T_ typeof` | + +**The fictional semantics:** `EUB_`, `ISO_`, `LSU_`, `LIVE_` are "fictional" semantic categories per the user — they map to `restrict` or `volatile` but carry the user's mental model of *why* the qualifier is needed (Execute Unit Bound = the data should stay in registers; Load/Store Unit Bound = the data must be sampled from the L1 cache; etc.). + +**Why this matters for Pass 3:** the duffle's type modifier system is the user's way of making the *intent* of `restrict`/`volatile` explicit at the call site. Pass 3 code that uses `R_*` or `V_*` is more readable than `restrict`/`volatile` (because the intent is named). + +--- + +## §4. The hand-rolled DSL pattern (the `enc_*` family) + +The duffle's defining feature is its hand-rolled DSL for emitting instruction words as raw constants. Example from `gte.h`: + +```c +// Per-field encoders (each one self-masks its argument before shifting) +U4 enc_gte_sf(U4 v) { return (v & 0x1F) << 0; } // shift amount +U4 enc_gte_mx(U4 v) { return (v & 0x1F) << 5; } // matrix row +U4 enc_gte_v (U4 v) { return (v & 0x1F) << 10; } // vector +U4 enc_gte_cv (U4 v) { return (v & 0x1F) << 15; } // control vector + +// The composite: flat OR of per-field encoders + COP2/CO base +U4 enc_gte_cmdw(U4 sf, U4 mx, U4 v, U4 cv, U4 lm, U4 cmd) { + return enc_gte_sf(sf) | enc_gte_mx(mx) | enc_gte_v(v) | enc_gte_cv(cv) | (lm << 25) | cmd; +} + +// Pre-baked shortcuts +U4 gte_cmd_rtpt = enc_gte_cmdw(0, 0, 0, 0, 0, 0x30); // RTPT command +U4 gte_cmd_rtps = enc_gte_cmdw(0, 0, 0, 0, 0, 0x01); // RTPS command +``` + +**Why this matters:** the user has built a per-instruction-set encoder layer (MIPS, GTE, GP) where each field has its own encoder, and the composite is a flat OR. This is the same pattern as the user's Sectored Language V1 (per the warmup's `cluster_9_fged.md`). + +**For Pass 3:** if a video involves hand-rolled bit-packing (graphics, audio codecs, etc.), follow this pattern: per-field encoder + composite + pre-baked shortcuts. Document each encoder's bit range in a comment. + +--- + +## §5. The `asm_inline` / `asm_clobber` / `clb_*` pattern + +For runtime-base-register instructions (where the `rs` field is chosen by the compiler at codegen), the duffle uses a "placeholder-pun" pattern: + +```c +// Pure (compile-time) instruction — constant-folded into .rodata +asm volatile( + asm_inline(gte_cmd_rtpt, gte_cmd_nclip, gte_cmd_avsz3) + asm_clobber(clb_system) +); + +// Runtime-base-register load — caller picks the base GPR +register V3_S2* p_in_12 __asm__("$12") = verts[0].ptr; +gte_load_v0(p_in_12, R_T4); // R_T4 = 12 = $t4 = $12 +``` + +**The macros** (from `gcc_asm.h`, not fully read but inferred): +- `asm_inline(...)` — emits raw `.word` directives from a list of integer constants +- `asm_clobber(...)` — declares clobbered registers +- `clb_*` — clobber categories (e.g., `clb_system` for system registers) + +**Why:** pure compile-time instructions can be constant-folded into `.rodata`; runtime instructions need a register-binding trick to force the compiler to put the argument in the right register. + +**For Pass 3:** if a video involves hand-rolled assembly (e.g., SIMD intrinsics, custom ABI), follow this pattern: pure → constant-fold; runtime → register-binding + clobber. + +--- + +## §6. The `TSet_` / `PtrSet_` typedef pattern + +```c +#define TSet_(type) type; typedef PtrSet_(type) +#define PtrSet_(type) TypeR_(type); typedef TypeV_(type) +#define TypeR_(type) type *R_ type ## _R +#define TypeV_(type) type V_* type ## _V +``` + +So `TSet_(U4)` expands to: +```c +U4; // the base typedef +typedef U4 *R_ U4_R; // restrict pointer typedef +typedef U4 V_* U4_V; // volatile pointer typedef +``` + +**Why:** the base typedef + restrict pointer typedef + volatile pointer typedef pattern. The user can write `U4_R` (restrict pointer to U4) without spelling out `U4 *restrict` every time. + +**The `enum` extension for type-classes:** +```c +#define Enum_(underlying_type, symbol) underlying_type TSet_(symbol); enum symbol +``` + +`Enum_(U4, gp_Commands)` expands to: +```c +U4; typedef U4 *R_ U4_R; typedef U4 V_* U4_V; +enum { /* enum body */ }; +``` + +So the enum is backed by a `U4` (32-bit unsigned) with restrict/volatile pointer variants. + +--- + +## §7. The `Proc_` / `Struct_` / `Union_` / `Opt_` / `Ret_` declaration pattern + +```c +#define Proc_(symbol) symbol +#define Struct_(symbol) struct symbol TSet_(symbol); struct symbol +#define Union_(symbol) union symbol TSet_(symbol); union symbol +#define Opt_(proc) Struct_(tmpl(Opt,proc)) +#define opt_(symbol, ...) (tmpl(Opt,symbol)){__VA_ARGS__} +#define Ret_(proc) Struct_(tmpl(Ret,proc)) +#define ret_(proc) tmpl(Ret,proc) proc +``` + +So `Struct_(Foo)` expands to: +```c +struct Foo TSet_(Foo); // i.e., struct Foo; typedef struct Foo *R_ Foo_R; typedef struct Foo V_* Foo_V; +struct Foo // the actual struct body +{ + // ... +}; +``` + +`Opt_(bar)` (for a procedure `bar`) expands to: +```c +struct Opt_bar TSet_(Opt_bar); +struct Opt_bar +{ + // ... +}; +``` + +Used like: +```c +opt_(bar, .x = 5, .y = 10); // designated-initializer syntax +``` + +**Why:** the `TSet_` pattern automatically generates the `Foo` / `Foo_R` / `Foo_V` triple from a single declaration. The user doesn't have to remember to write 3 typedefs. + +**For Pass 3:** use the `Struct_(Name)` / `Union_(Name)` / `Proc_(Name)` macros to declare types. The macros generate the pointer variants automatically. + +--- + +## §8. The slice type pattern + +```c +typedef Struct_(Slice) { U4 ptr, len; }; // Untyped Slice +#define Slice_(type) Struct_(tmpl(Slice,type)) +typedef Slice_(B1); // typed slice of bytes + +// Iteration +#define slice_iter(container, iter) (T_((container).ptr) iter = (container).ptr; iter != slice_end(container); ++ iter) + +// Macros +#define slice_end(slice) ((slice).ptr + (slice).len) +#define slice_zero(s) slice_zero_(slice_to_ut(s)) +#define slice_copy(dest, src) do { \ + static_assert(T_same(dest, src)); \ + slice_copy_(slice_to_ut(dest), slice_to_ut(src)); \ +} while(0) +``` + +**Why:** slices are `(ptr, len)` pairs. The untyped `Slice` uses `U4` for the pointer (because this is a PS1 C codebase with 32-bit pointers); the typed `Slice_(T)` uses `T*` for the pointer. The `slice_iter` macro provides safe iteration with the `slice_end` boundary check. + +**For Pass 3:** if a video involves a contiguous-data operation (e.g., array processing, string manipulation), use the `Slice_(T)` pattern. The untyped `Slice` is for when the type isn't known (e.g., serialization). + +--- + +## §9. The arena allocator pattern (`FArena`) + +```c +typedef Opt_(farena) { U4 alignment, type_width; }; +typedef Struct_(FArena) { U4 start, capacity, used; }; + +FArena farena_make(Slice mem); +Slice farena_push(FArena_R arena, U4 amount, Opt_farena o); +void farena_reset(FArena_R arena); +void farena_rewind(FArena_R arena, U4 save_point); +U4 farena_save(FArena arena); + +#define farena_push_(arena, amount, ...) farena_push((arena), (amount), opt_(farena, __VA_ARGS__)) +#define farena_push_type(arena, type, ...) (type*) farena_push((arena), 1, opt_(farena, .type_width=S_(type), __VA_ARGS__)).ptr) +#define farena_push_array(arena, type, amount, ...) (tmpl(Slice,type)){ (type*) farena_push((arena), (amount), opt_(farena, .type_width=S_(type), __VA_ARGS__)).ptr, (amount) } +``` + +**Why:** arena allocators are allocation-once-free-all (a.k.a. "linear allocation" or "bump allocator"). The user uses them for transient allocations (per-frame, per-request). The `Opt_farena` options struct lets the caller specify alignment and type width. + +**For Pass 3:** if a video involves temporary allocations (game frames, request scopes), use the `FArena` pattern. The `farena_push_type` macro is the type-safe variant; the `farena_push_array` macro is for typed arrays. + +--- + +## §10. The control flow pattern (`defer`, `scope`, `defer_rewind`) + +```c +#define defer(expr) for(U4 once= 1; once!=1;++ once,(expr)) // Basic do something after body +#define scope(begin,end) for(U4 once=(1,(begin)); once!=1;++ once,(end )) // Do things before or after a scope +#define defer_rewind(cursor) for(T_(cursor) sp=cursor,once=0; once!=1;++ once,cursor=sp) // Used with arenas/stacks +#define defer_info(type,expr, ...) for(type info= {__VA_ARGS__}; info.once!=1;++info.once,(expr)) // Defer with tracked state +``` + +**Why:** C doesn't have a `defer` statement (Go/Rust/Swift do). The user implements it with a `for` loop that runs the body once, then runs the deferred expression. The `defer_rewind` variant is for arena allocators (rewind the cursor on scope exit). + +**For Pass 3:** if a video involves cleanup (file close, memory free, lock release), use the `defer` pattern. If the cleanup involves arena state, use `defer_rewind`. + +--- + +## §11. The memory ordering pattern (`ooo_drift_`, `ooo_anchor_`, `ooo_drain_`, `ooo_weld_`) + +```c +#define ooo_drift_ __ATOMIC_RELAXED // OoO engine allowed to drift +#define ooo_anchor_ __ATOMIC_ACQUIRE // Anchor the Load Queue (halt spec lookahead) +#define ooo_drain_ __ATOMIC_RELEASE // Drain the Store Buffer (force writeback) +#define ooo_weld_ __ATOMIC_SEQ_CST // Weld pipeline (total order bus lock) +``` + +**Why:** the user thinks of memory ordering in terms of the out-of-order execution engine. `ooo_drift_` is the weakest (no ordering); `ooo_anchor_` is acquire (load-side barrier); `ooo_drain_` is release (store-side barrier); `ooo_weld_` is sequential consistency (full fence). + +**For Pass 3:** if a video involves concurrent/atomic code, use the `ooo_*` pattern. The names are more readable than `__ATOMIC_*` (because the intent is named: "drift" = no barrier; "anchor" = load barrier; etc.). + +--- + +## §12. The comment style (the design-doc header) + +The duffle's `gte.h` has a 67-line design-doc header before any code: + +``` +/* ============================================================================ + * gte.h — Geometry Transformation Engine (COP2) for the PS1 + * ============================================================================ + * + * Hand-rolled DSL for emitting GTE/MIPS instruction words as raw `.word` + * constants from C. No GCC inline-assembly string syntax in the code body. + * + * PHILOSOPHY + * ---------- + * 1. A 32-bit instruction word is composed from per-field encoders. Each + * encoder knows only its own bit range; the composite ORs them together. + * No magic numbers inside any encoder body — every shift and mask is a + * named constant from the bitfield-layout enum below. + * + * 2. Pure (compile-time) instructions — every GTE *command* (RTPS, RTPT, + * NCLIP, MVMVA, …) and every COP2 *transfer* (ctc2/cfc2) with a constant + * rs/rt/rd — are emitted as a single integer constant via + * `asm_inline(...)` from gcc_asm.h. The C compiler constant-folds + * these into `.word` directives in .rodata. + * + * 3. Runtime-base-register instructions (lwc2, swc2, lw, sw, …) cannot be + * a pure compile-time word because the `rs` field is chosen by the + * compiler at codegen. For these we use a "placeholder-pun" pattern: + * a fixed register number (R_T4 = $12) is baked into the rs field of + * the `.word` constant, and the macro declares a `"r"(arg)` input + * constraint plus a clobber on the same register. The compiler is + * therefore *forced* to bind `arg` to that exact register, and the + * constant is correct. + * + * USAGE + * ----- + * // Pure command sequence — all bits compile-time: + * asm volatile( + * asm_inline( gte_cmd_rtpt , gte_cmd_nclip , gte_cmd_avsz3 ) + * asm_clobber( clb_system ) + * ); + * + * // Runtime-base-register load — caller picks the base GPR: + * register V3_S2* p_in_12 __asm__("$12") = verts[0].ptr; + * gte_load_v0(p_in_12, R_T4); // R_T4 = 12 = $t4 = $12 + * + * // Three independent bases for an RTPT pipeline: + * register V3_S2* p0 __asm__("$12") = verts[0].ptr; + * register V3_S2* p1 __asm__("$13") = verts[1].ptr; + * register V3_S2* p2 __asm__("$14") = verts[2].ptr; + * gte_load_v0(p0, R_T4); + * gte_load_v1(p1, R_T5); + * gte_load_v2(p2, R_T6); + * gte_rtpt(); + * + * STYLE NOTES + * ----------- + * - Per-field encoders are named `enc_gte_(value)` and each one + * self-masks its argument before shifting. Mirrors the `enc_op / enc_rs + * / enc_rt / ...` family in mips.h. + * - The composite `enc_gte_cmdw(sf, mx, v, cv, lm, cmd)` is a flat OR of + * the per-field encoders, plus the COP2/CO base. + * - Pre-baked shortcuts (`gte_cmd_rtpt`, `gte_cmd_rtps`, …) are defined + * for the common cases so call sites read like assembly source. + * - All register/field values are enums (not `#define`s) so they show up + * in debugger symbol tables and IDE autocomplete. + * + * SEE ALSO + * -------- + * - gcc_asm.h: the `.word` emitter (`asm_inline`, `asm_clobber`, clobbers) + * - mips.h: the MIPS encoder layer this builds on + */ +``` + +**Why:** the header is a mini-manual for the module. It documents: +- The module's purpose +- The philosophy (the design decisions, in numbered form) +- Usage examples +- Style notes (the conventions used within the module) +- See also (related modules) + +**For Pass 3:** every file in Pass 3 should have a similar design-doc header. The header should be 30-100 lines, depending on the module's complexity. + +--- + +## §13. The `pragma region` / `pragma endregion` pattern + +The duffle uses `#pragma region` / `#pragma endregion` to group code sections: + +```c +#pragma region DAG +// ... DAG macros +#pragma endregion DAG + +#pragma region Slice +// ... Slice macros +#pragma endregion Slice + +#pragma region FArena +// ... FArena macros +#pragma endregion FArena +``` + +**Why:** IDEs (CLion, VSCode, Xcode) honor `#pragma region` and add code-folding markers in the gutter. The user uses regions to organize large files into collapsible sections. + +**For Pass 3:** if a file is large (>500 LOC), use `#pragma region` to organize the sections. The region name should match the section's content. + +--- + +## §14. The math types (`V2_S2`, `M3_S4`, etc.) + +The duffle's math types follow the byte-width convention: + +| Type | Fields | +|---|---| +| `V2_S2` | `S2 x, y` | +| `V3_S2` | `S2 x, y, z, pad` | +| `V4_S2` | `S2 x, y, z, w` | +| `V2_S4` | `S4 x, y` | +| `V3_S4` | `S4 x, y, z, pad` | +| `V4_S4` | `S4 x, y, z, w` | +| `R2_S2` | `V2_S2 p0, p1` (range/pair of 2D points) | +| `Rect_S2` | `S2 x, y, width, height` | +| `M3_S2` | `A3x3_S2 m; A3_S4 t;` (3x3 matrix + 3-component translation) | + +**Why:** the `_S2` / `_S4` suffix is the byte-width of the underlying type. The user has V2/V3/V4 by width (S2 = 16-bit signed, S4 = 32-bit signed). + +**For Pass 3:** if a video involves graphics or linear algebra, use the duffle's math types (V2_S4, M3_S4, etc.) or the raddbg variants (Vec2F32, Vec3S32, etc.). + +--- + +## §15. The error handling pattern (`assert`, `slice_assert`) + +```c +#define slice_assert(s) do { assert((s).ptr != 0); assert((s).len > 0); } while(0) + +FI_ Slice farena_make(Slice mem) { + assert(mem.ptr != 0); + assert(mem.len > 0); + return (Slice){mem.ptr, mem.len}; +} +``` + +**Why:** the duffle uses `assert` for invariants. There are no error codes or exceptions. The assertion is the contract. + +**For Pass 3:** use `assert` for invariants. The user doesn't use error codes or exceptions in C11 code; the assertion is the contract. + +--- + +## §16. The `array_decl` / `array_len` / `Array_` pattern + +```c +#define array_len(a) (U8)(sizeof(a) / sizeof(typeof((a)[0]))) +#define array_decl(type, ...) (type[]){__VA_ARGS__} +#define Array_sym(type,len) A ## len ## _ ## type +#define Array_expand(type,len) type Array_sym(type, len)[len]; typedef PtrSet_(Array_sym(type, len)) +#define Array_(type,len) Array_expand(type,len) +``` + +So `Array_(U4, 8)` expands to: +```c +U4 A8_U4[8]; typedef U4 A8_U4_R; typedef U4 V_* A8_U4_V; +``` + +**Why:** the user wants typed arrays with explicit length (`A8_U4` = array of 8 U4). The macro generates the array type, the symbol, and the pointer variants. + +**For Pass 3:** if a video involves fixed-size arrays, use the `Array_(T, N)` pattern. The symbol name is `A{N}_{T}` (e.g., `A8_U4`). + +--- + +## §17. The `gknown` / `global` / `LP_` / `internal` data scoping + +```c +#define global static // Mark global data +#define gknown // Mark global data used in procedure +#define LP_ static // static data within procedure scope +#define internal static // internal +``` + +**Why:** the duffle has 3 levels of data scoping: +- `global` (translation-unit-scope data; uses `static` for internal linkage) +- `gknown` (mark only; for data that's used in a procedure but is global; no expansion) +- `LP_` (procedure-local persistent; uses `static` so the data persists across calls) +- `internal` (internal linkage; uses `static`) + +**For Pass 3:** use `global` for TU-scope data, `LP_` for procedure-local persistent data, `internal` for internal-linkage functions. + +--- + +## §18. The `gte_hello` integration pattern + +`hello_gte.h` shows how the duffle is integrated in practice: + +```c +typedef Struct_(DrawEnv_Packed) { U4 tag; U4 code[15]; }; +typedef Struct_(DrawEnv) { + Rect_S2 clip_area; + A2_S2 drawing_offset; + Rect_S2 texture_window; + S2 texture_page; + B1 flag_dither; + B1 flag_draw_on_display; + B1 enable_auto_clear; + RGB8 initial_bg_color; + DrawEnv_Packed dr_env; +}; + +DisplayEnv* displayenv_init(DisplayEnv* env, S4 x, S4 y, S4 w, S4 h) __asm__("SetDefDispEnv"); +DrawEnv* drawenv_init (DrawEnv* env, S4 x, S4 y, S4 w, S4 h) __asm__("SetDefDrawEnv"); +``` + +**Why:** the `__asm__("symbol")` attribute on a function declaration gives the C function a different symbol name in the linker. This is used to bind C functions to the PS1's BIOS / runtime symbols (e.g., `displayenv_init` is bound to the PS1's `SetDefDispEnv` function). + +**For Pass 3:** if a video involves linking to external symbols (BIOS, runtime, shared libraries), use the `__asm__("symbol")` pattern. The C function is the wrapper; the symbol is the actual implementation. + +--- + +## §19. The gte_hello macro pattern (set_/get_/add_/sub_/mut_/div_/gt_/lt_/ge_/le_) + +```c +#define set_len( p, _len) (((PolyTag*R_)(p))->len = (B1)(_len)) +#define set_addr(p, _addr) (((PolyTag*R_)(p))->addr = (U4)(_addr)) +#define set_code(p, _code) (((PolyTag*R_)(p))->code = (B1)(_code)) + +#define get_len(p) (B1)(((PolyTag*R_)(p))->len) +#define get_code(p) (B1)(((PolyTag*R_)(p))->code) +#define get_addr(p) (U4)(((PolyTag*R_)(p))->addr) + +#define set_poly_f3(p) set_len(p, 4), set_code(p, 0x20) +#define set_poly_ft3(p) set_len(p, 7), set_code(p, 0x24) +// ... +``` + +**Why:** the user has a set_/get_ macro family for field access on packed structs. The `set_*` macros take a `R_` pointer and a value; the `get_*` macros return the value. The `set_poly_*` macros set the entire primitive at once. + +**For Pass 3:** if a video involves packed structs (graphics, network packets, file formats), use the set_/get_ pattern. The macros are generated for each field. + +--- + +## §20. The `FP` fixed-point convention + +```c +enum { + fp_one = (1 << 12), +}; + +#define v3s4_fp_one() v3s4(fp_one, fp_one, fp_one) +``` + +**Why:** the duffle uses 12-bit fixed-point for math (PS1 GTE convention). `fp_one` is `1 << 12 = 4096`. A `V3_S4 fp` value is in units of `1/4096`. + +**For Pass 3:** if a video involves fixed-point math (graphics, audio, embedded), use the `fp` convention. The shift is 12 for Q12 format. + +--- + +## §21. The `gp` GPU command pattern (enums + bitfield ORs) + +```c +typedef Enum_(U4, gp_Commands) { + gcmd_Reset = 0b000, + gcmd_Polygon = 0b001, + gcmd_Line = 0b010, + gcmd_Rect = 0b011, + gcmd_VM_to_VM = 0b100, + gcmd_CPU_to_VM = 0b101, + gcmd_VM_to_CPU = 0b110, + gcmd_Environment = 0b111, + // ... +}; + +enum { + gcmd_offset = 24, + gp_Reset = (gcmd_Reset << gcmd_offset), + gp_DisplayEnabled = (gcmd_DisplayEnable << gcmd_offset | 0x0), + // ... +}; +``` + +**Why:** GPU commands are 32-bit words with the command in the high byte (bits 24-31). The `gcmd_offset` defines where the command starts. The composite `gp_DisplayEnabled` ORs the command and the data. + +**For Pass 3:** if a video involves hardware commands (GPU, audio, network), follow this pattern: enum for command IDs, shift offset for the command field, composite OR for the full word. + +--- + +## §22. The `pcast` pattern (pointer cast for struct aliasing) + +```c +#define pcast(type, data) (C_(type*, & (data)) [0]) +``` + +So `pcast(A3_S4_R, out_a)` casts `&out_a` to `A3_S4*` (reading a `V3_S4` as an `A3_S4`). + +**Why:** sometimes two structs have the same memory layout (e.g., `V3_S4` and `A3_S4` are both 3 ints). The `pcast` macro aliases the memory without a copy. + +**For Pass 3:** if a video involves struct aliasing (e.g., SIMD vector as float array), use the `pcast` pattern. + +--- + +## §23. The `byte_pad` pattern (struct padding) + +```c +#define byte_pad(amount, ...) B1 glue(_PAD_, __VA_ARGS__) [amount] +``` + +So `byte_pad(2, after_color)` expands to: +```c +B1 _PAD_after_color[2]; +``` + +**Why:** the user uses explicit byte padding in packed structs (graphics, file formats). The macro generates the padding field with a name derived from the previous field. + +**For Pass 3:** if a video involves packed structs with explicit padding, use the `byte_pad` pattern. + +--- + +## §24. The `glue` / `tmpl` pattern (token concatenation) + +```c +#define glue_impl(A, B) A ## B +#define glue(A, B) glue_impl(A, B) +#define tmpl(prefix, type) prefix ## _ ## type +``` + +`glue(x, y)` → `xy`. `tmpl(Opt, farena)` → `Opt_farena`. The `glue_impl` indirection is for macro-expansion order (so the arguments are expanded before concatenation). + +**Why:** the user builds type names from a prefix and a stem. `tmpl(Opt, farena)` → `Opt_farena` (a struct that wraps the optional parameters for `farena`). + +**For Pass 3:** use `glue` and `tmpl` for type-name generation. The indirection is required for correct macro expansion. + +--- + +## §25. The `c_` cast pattern (`C_(type, data)`) + +```c +#define C_(type,data) ((type)(data)) // for enforced precedence +``` + +`C_(U4, value)` → `((U4)(value))`. The explicit precedence is for cases where the cast would otherwise be misinterpreted by the parser. + +**For Pass 3:** use `C_(type, data)` for explicit casts. The parens ensure correct precedence. + +--- + +## §26. Summary + +The duffle convention is the user's primary C11 style. Key takeaways: + +1. **Byte-width types** (`U1`/`U2`/`U4`/`S1`/`S2`/`S4`/`B1`/`B2`/`B4`) — explicit width; no `unsigned int`. +2. **Underscore-suffixed type modifiers** (`R_`/`V_`/`LP_`/`I_`/`FI_`/`NI_`/`RO_`/`T_`) — explicit intent at the call site. +3. **`TSet_` / `PtrSet_`** — automatic generation of base + restrict + volatile pointer typedefs. +4. **`Struct_` / `Union_` / `Proc_` / `Opt_` / `Ret_` / `Enum_`** — declarative macros that generate the typedefs. +5. **Hand-rolled DSL** (`enc_*` + `asm_inline` + `asm_clobber` + `clb_*`) — per-instruction-set encoder layer. +6. **Memory ordering** (`ooo_drift_`/`ooo_anchor_`/`ooo_drain_`/`ooo_weld_`) — named intent for atomic memory orders. +7. **Slices + arenas** (`Slice_` + `FArena` + `farena_push_*`) — typed contiguous data + linear allocation. +8. **Control flow** (`defer` + `scope` + `defer_rewind`) — Go-style defer in C via `for` loops. +9. **Design-doc headers** — every file has a 30-100 line mini-manual before any code. +10. **`#pragma region` / `#pragma endregion`** — code-folding markers for large files. + +These conventions are the user's "house style" for C11. Pass 3 should produce code that uses them. + +--- + +*End of `cluster_0_pikuma_duffle.md`. 26 sections. ~200 LOC. PRIMARY C11 convention source.*