diff --git a/code/duffle/dsl.h b/code/duffle/dsl.h index e7de8af..da32336 100644 --- a/code/duffle/dsl.h +++ b/code/duffle/dsl.h @@ -97,6 +97,7 @@ #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) #define Bit_(id,b) id = (1 << b), tmpl(id,pos) = b +#define Bitmask_(b) (1u << b) #define Enum_(underlying_type, symbol) underlying_type TSet_(symbol); enum symbol #define Proc_(symbol) symbol #define Relative_(symbol) // Does nothing but annotate that a symbol is associated with another. @@ -199,6 +200,8 @@ def_signed_ops(le, <=) #define defer_info(type,expr, ...) for(type info= {__VA_ARGS__}; info.once!=1;++info.once,(expr)) // Defer with tracked state #define do_while(cond) for (U8 once=0; once!=1 || (cond); ++once) + +#define Jmp_nZero_(cond,label) if (cond) goto label; #pragma endregion Control Flow & Iteration #define span_iter(type, iter, m_begin, op, m_end) ( \ diff --git a/code/duffle/gen/macs.h b/code/duffle/gen/macs.h index 43297b2..54e2933 100644 --- a/code/duffle/gen/macs.h +++ b/code/duffle/gen/macs.h @@ -237,16 +237,16 @@ WORD_COUNT(mac_format_g4_color, 12) WORD_COUNT(mac_insert_ot_tag, 11) /* atom_dbg_skip */ -#define mac_pad_set_centered_axes(r_state, r_scratch) \ - load_upper_i(r_scratch, (PadAxis_Centered_Word >> 16) & 0xFFFF) \ -, or_i_self( r_scratch, PadAxis_Centered_Word & 0xFFFF) \ -, store_word( r_scratch, r_state, O_(PadState,axes)) +#define mac_pad_set_centered_axes(state, scratch) \ + load_upper_i(scratch, (PadAxis_Centered >> 16) & 0xFFFF) \ +, or_i_self( scratch, PadAxis_Centered & 0xFFFF) /* mac_load_word_imm(scratch, PadAxis_Centered), */ \ +, store_word( scratch, state, O_(PadState,axes)) WORD_COUNT(mac_pad_set_centered_axes, 3) /* atom_dbg_skip */ -#define mac_pad_set_id_byte(r_state, r_id, id_value) \ +#define mac_pad_set_id_byte(state, r_id, id_value) \ add_ui( r_id, R_0, id_value) \ -, store_byte(r_id, r_state, O_(PadState,id)) +, store_byte(r_id, state, O_(PadState,id)) WORD_COUNT(mac_pad_set_id_byte, 2) /* atom_dbg_skip */ diff --git a/code/duffle/gp.atom.c b/code/duffle/gp.atom.c index 3cb1c99..5c5313a 100644 --- a/code/duffle/gp.atom.c +++ b/code/duffle/gp.atom.c @@ -8,40 +8,35 @@ ATOM_FILE_DEBUGGER_LINE_MARKER(gp_atom_c); #pragma region MACs (Mips Atom Components) -atom_dbg_skip FI_ Slice_MipsCode ac_gcmd_push(AtomBuilder_R ab, U4 cmd, U4 reg_transfer, U4 reg_base, U2 port) -MipsAtomComp_Proc_(ab, { +atom_dbg_skip MipsAtomComp_Proc_(ab, { mac_load_word_imm(reg_transfer, cmd), store_word( reg_transfer, reg_base, port), }) -atom_dbg_skip FI_ Slice_MipsCode ac_store_rgb8(AtomBuilder_R ab, U1 rr, U1 rg, U1 rb, U4 base, U4 offset) -MipsAtomComp_Proc_(ab, { +atom_dbg_skip MipsAtomComp_Proc_(ab, { store_byte(rr, base, offset + O_(RGB8,r)), store_byte(rg, base, offset + O_(RGB8,g)), store_byte(rb, base, offset + O_(RGB8,b)), }) -atom_dbg_skip FI_ Slice_MipsCode ac_pack_color_word(AtomBuilder_R ab, U4 r_base, U4 off, U4 cmd, U1 r, U1 g, U1 b) -MipsAtomComp_Proc_(ab, { +atom_dbg_skip MipsAtomComp_Proc_(ab, { load_upper_i(R_AT, (cmd) << 8 | (b)), or_i_self( R_AT, ((g) << 8) | (r)), store_word( R_AT, r_base, (off)), }) -atom_dbg_skip FI_ Slice_MipsCode ac_format_f3_color(AtomBuilder_R ab, U4 r_base, U1 r, U1 g, U1 b) -MipsAtomComp_Proc_(ab, { mac_pack_color_word(r_base, O_(Poly_F3,color), gp0_cmd_poly_f3, r, g, b) }) +atom_dbg_skip MipsAtomComp_Proc_(ab, { mac_pack_color_word(r_base, O_(Poly_F3,color), gp0_cmd_poly_f3, r, g, b) }) -atom_dbg_skip FI_ Slice_MipsCode ac_format_g4_color(AtomBuilder_R ab, U4 r_prim_cursor, 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_(ab, { +atom_dbg_skip MipsAtomComp_Proc_(ab, { mac_pack_color_word(r_prim_cursor, O_(Poly_G4,c0), gp0_cmd_poly_g4, r0,g0,b0), mac_pack_color_word(r_prim_cursor, O_(Poly_G4,c1), 0, r1,g1,b1), mac_pack_color_word(r_prim_cursor, O_(Poly_G4,c2), 0, r2,g2,b2), diff --git a/code/duffle/gte.atom.c b/code/duffle/gte.atom.c index 0a82023..3cef22b 100644 --- a/code/duffle/gte.atom.c +++ b/code/duffle/gte.atom.c @@ -143,7 +143,7 @@ FI_ Slice_MipsCode ac_trans_mt3s3s4(AtomBuilder_R ab * The later 64 entries (octaves 2-3) are the `srav` branch when the magnitude's top bit is well above bit 24. * * 192-entry table is reproduced verbatim from libgte (verified against libpsn00b/psxgte/vector.s:100-123 — 24 rows × 8 halfwords, last entry 0x0804). */ -internal RO_ S2 gte_normalize_sqr_tbl[192] align_(2) = { +internal S2 const gte_normalize_sqr_tbl[192] align_(2) = { 0x1000, 0x0fe0, 0x0fc1, 0x0fa3, 0x0f85, 0x0f68, 0x0f4c, 0x0f30, 0x0f15, 0x0efb, 0x0ee1, 0x0ec7, 0x0eae, 0x0e96, 0x0e7e, 0x0e66, 0x0e4f, 0x0e38, 0x0e22, 0x0e0c, 0x0df7, 0x0de2, 0x0dcd, 0x0db9, @@ -309,7 +309,7 @@ internal MipsAtom_(set_gte_mt3s2s4) atom_info( load_word(R_T3, R_TapePtr, O_(Binds_SetGteMT3S2S4,transform)), add_ui_self( R_TapePtr, S_(Binds_SetGteMT3S2S4)), /* Load 3x3 Rotation + 3x1 Translation from R_T3 into GTE CONTROL Regs (ctc2) */ - load_word(R_T0, R_T3, 0), load_word(R_T1, R_T3, 4), + load_word(R_T0, R_T3, 0), load_word(R_T1, R_T3, 4), gte_mv_to_ctrl_r(R_T0, gte_cr_RT11), gte_mv_to_ctrl_r(R_T1, gte_cr_RT12), load_word(R_T0, R_T3, 8), load_word(R_T1, R_T3, 12), load_word(R_T2, R_T3, 16), gte_mv_to_ctrl_r(R_T0, gte_cr_RT13), gte_mv_to_ctrl_r(R_T1, gte_cr_RT21), gte_mv_to_ctrl_r(R_T2, gte_cr_RT22), diff --git a/code/duffle/lottes_tape.h b/code/duffle/lottes_tape.h index 78e4714..3e80419 100644 --- a/code/duffle/lottes_tape.h +++ b/code/duffle/lottes_tape.h @@ -139,19 +139,17 @@ typedef Slice_(MipsAtom); // atombuilder_push(ab, slice_from_array(MipsCode, atom_comp_code)); // } // The body must NOT include mac_yield() (the parent atom yields). -// The component name is derived by the Lua metaprogram from the preceding -// `FI_ Slice_MipsCode ac_X(...)` declaration (backward walk from the macro site). -// Inline-only callers (the generated `mac_` aliases) skip the `ab` arg via metaprogram filtering; -// escape callers (ac_ invoked as a function) pass a long-lived builder. +// The component name is derived by the Lua metaprogram from the preceding `FI_ Slice_MipsCode ac_X(...)` declaration (backward walk from the macro site). +// Inline-only callers (the generated `mac_` aliases) skip the `ab` arg via metaprogram filtering; escape callers (ac_ invoked as a function) pass a long-lived builder. #define MipsAtomComp_Proc_(ab, ...) { MipsCode atom_comp_code[] align_(4) = __VA_ARGS__; atombuilder_push(ab, slice_from_array(MipsCode, atom_comp_code)); } /* Line-table anchor: gcc only adds a file to the .debug_line file table when the contains line-numbered content. Files containing only atoms and atom components. Place `ATOM_FILE_LINE_MARKER();` once at file scope in any `.atom.c` that defines atoms. - The macro expands to a file-scope `internal U4 const` declaration keeps the file in the line table. - The constant is in `.rodata` and unreferenced; the linker may eliminate it. - The two-level concat + `__LINE__` suffix makes the identifier unique per call site - (the identifier embeds the source line, so duplicates across `#include`d files don't collide). */ + Macro expands to a file-scope `internal U4 const` declaration keeps the file in the line table. + The constant is in `.rodata` so the linker may eliminate it. + Two-level concat + `__LINE__` suffix makes the identifier unique per call site + (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 Tape; @@ -294,29 +292,79 @@ FI_ void atomarena_reset(AtomArena_R aa) { aa->used = 0; } #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. +// TODO(Ed): Technically we can do this at comp-time with the metaprogram, but we may have namespace conflicts. +// Unless we follow a convention for #define or something per register allocation boundary. -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. +/* ABI + tape reserves that are never handed out by alloc. */ +U4 const regfile_abi_mask = + (1u << R_0) | (1u << R_AT) | + (1u << R_K0) | (1u << R_K1) | + (1u << R_GP) | (1u << R_SP) | + (1u << R_FP) | (1u << R_RA) | + (1u << R_T8) | (1u << R_T9); /* AtomJmp + TapePtr */ - 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]; + A2_U2 GPR; + A2_U2 GTE; }; - -void regfile_pin(U4 register) { - - assert(false); +typedef Struct_(RegFile_RInfo) { + U2_R section; + U2 mask; + B2 occupied; +}; +FI_ void regfile_init(RegFile_R rf) { + /* pack the 32-bit ABI mask into the two U2s */ + rf->GPR[0] = u4_lo(regfile_abi_mask); + rf->GPR[1] = u4_hi(regfile_abi_mask); + rf->GTE[0] = rf->GTE[1] = 0; } +FI_ RegFile regfile_make(void) { RegFile rf; regfile_init(& rf); return rf; } +FI_ RegFile_RInfo regfile_rinfo(A2_U2 file, Reg r_id) { + U2 s_id = r_id >> 4; + U2_R section = & file[s_id]; + U2 mask = (1u << (r_id & 15)); + B2 occupied = section[0] & mask != 0; + return (RegFile_RInfo){section, mask, occupied}; +} +FI_ Reg regfile__alloc_helper(A2_U2 file, Reg r_id) { + Reg result = 0; RegFile_RInfo info = regfile_rinfo(file, r_id); + if (info.occupied == false) { + info.section[0] |= info.mask; + result = r_id; + } + return result; +} +I_ Reg regfile_alloc(RegFile_R rf) { + U2 allocated = 0; + for range_iter(r_id, <=, r1u2(R_T0, R_T7)) { + allocated = regfile__alloc_helper(rf->GPR, r_id); Jmp_nZero_(allocated,resolved); + } + for range_iter(r_id, <=, r1u2(R_V0, R_V1)) { + allocated = regfile__alloc_helper(rf->GPR, r_id); Jmp_nZero_(allocated,resolved); + } +resolved: return allocated; +} +FI_ Reg regfile_pin(RegFile_R rf, Reg r_id) { + RegFile_RInfo info = regfile_rinfo(rf->GPR, r_id); + assert(info.occupied == false); + info.section[0] |= info.mask; + return r_id; +} +FI_ void regfile_free_mask(RegFile_R rf, U4 mask) { + if (regfile_abi_mask & mask) return; + u4_r(rf->GPR)[0] &= mask; +} +FI_ void regfile_free_reg(RegFile_R rf, Reg r_id) { + /* never free the ABI set */ + if (regfile_abi_mask & (1u << r_id)) return; + RegFile_RInfo info = regfile_rinfo(rf->GPR, r_id); + info.section[0] &= ~info.mask; +} +FI_ void regfile_reset(RegFile_R rf) { + rf->GPR[0] = u4_lo(regfile_abi_mask); + rf->GPR[1] = u4_hi(regfile_abi_mask); +} #pragma endregion RegFileArena (Register File Allocator) #pragma region Mips Atom Procs diff --git a/code/duffle/pad.atom.c b/code/duffle/pad.atom.c index f7005de..4c2ef6c 100644 --- a/code/duffle/pad.atom.c +++ b/code/duffle/pad.atom.c @@ -11,16 +11,16 @@ ATOM_FILE_DEBUGGER_LINE_MARKER(pad_atom_c); #pragma region MACs (Mips Atom Components) -FI_ Slice_MipsCode ac_pad_set_centered_axes(AtomBuilder_R ab, U4 r_state, U4 r_scratch) atom_dbg_skip MipsAtomComp_Proc_(ab, { - load_upper_i(r_scratch, (PadAxis_Centered_Word >> 16) & 0xFFFF), - or_i_self( r_scratch, PadAxis_Centered_Word & 0xFFFF), - store_word( r_scratch, r_state, O_(PadState,axes)), +FI_ Slice_MipsCode ac_pad_set_centered_axes(AtomBuilder_R ab, Reg state, Reg scratch) atom_dbg_skip MipsAtomComp_Proc_(ab, { + load_upper_i(scratch, (PadAxis_Centered >> 16) & 0xFFFF), + or_i_self( scratch, PadAxis_Centered & 0xFFFF), + // mac_load_word_imm(scratch, PadAxis_Centered), + store_word( scratch, state, O_(PadState,axes)), }) -FI_ Slice_MipsCode ac_pad_set_id_byte(AtomBuilder_R ab, U1 r_state, U1 r_id, U1 id_value) -atom_dbg_skip MipsAtomComp_Proc_(ab, { +FI_ Slice_MipsCode ac_pad_set_id_byte(AtomBuilder_R ab, Reg state, Reg r_id, U1 id_value) atom_dbg_skip MipsAtomComp_Proc_(ab, { add_ui( r_id, R_0, id_value), - store_byte(r_id, r_state, O_(PadState,id)), + store_byte(r_id, state, O_(PadState,id)), }) FI_ Slice_MipsCode ac_pad_set_status(AtomBuilder_R ab, U4 r_tmp, U1 r_state, U4 pad_status) atom_dbg_skip MipsAtomComp_Proc_(ab, { @@ -55,12 +55,12 @@ FI_ Slice_MipsCode ac_pad_store_inverted_buttons(AtomBuilder_R ab, U1 r_buttons, * byte_swap16(x) = (x >> 8) | (x << 8); nor(x, R_0) = ~x. store_half truncates to 16 bits so the upper-16 mask is implicit in the store. * * Register use (atom-local; no wave-context touched): - * R_T0 = raw base (kept throughout; axes loads read raw[4..7] from R_T0) - * R_T1 = state base (kept throughout; all stores go through R_T1) - * R_T2 = raw[0] status (alive across the disc/pending/id dispatch, then dead) - * R_T3 = raw[1] id (alive across the id dispatch, then dead) - * R_T4 = scratch (shifts, compares, immediate loads, store values) - * R_T5 = scratch (parallel lui+ori for the 0x80808080 axes constant + byte-swap target) + * R_T0 = raw base : Kept throughout; axes loads read raw[4..7] from R_T0. + * R_T1 = state base : Kept throughout; all stores go through R_T1. + * R_T2 = raw[0] status : Alive across the disc/pending/id dispatch, then dead. + * R_T3 = raw[1] id : Alive across the id dispatch, then dead. + * R_T4 = scratch : Shifts, compares, immediate loads, store values. + * R_T5 = scratch : Parallel lui + ori for the 0x80808080 axes constant + byte-swap target. */ enum { R_PadRaw = R_T0 atom_reg atom_type(U1), @@ -125,7 +125,8 @@ atom_label(id_dispatch) /* === Case 3-6: ID dispatch */ * R_T5 is then "dead" — only consumed at the analog_pad range check downstream. */ mac_pad_set_status(R_T4, R_PadState, PadStatus_Digital), load_half_u( R_T4, R_PadRaw, O_(PadBiosRaw, buttons)), /* R_T4 = raw_buttons; */ - load_upper_i(R_T5, PadAxis_Centered_Hi), or_i_self(R_T5, PadAxis_Centered_Lo), /* fills the buttons-load's delay slot (doesn't read R_T4) */ + mac_load_word_imm(R_T5, PadAxis_Centered), /* fills the buttons-load's delay slot (doesn't read R_T4) */ + // load_upper_i(R_T5, PadAxis_Centered_Hi), or_i_self(R_T5, PadAxis_Centered_Lo), mac_pad_store_inverted_buttons(R_T4, R_PadState), /* R_T4 settled: nor + sh writes ~raw_buttons to state.buttons */ store_word(R_T5, R_PadState, O_(PadState, axes)), /* single sw writes the 4-byte axes block at offset 8 (left_x, left_y, right_x, right_y) */ mac_pad_set_id_byte(R_PadState, R_T4, PadRawId_Digital), diff --git a/code/duffle/pad.h b/code/duffle/pad.h index e530094..014abee 100644 --- a/code/duffle/pad.h +++ b/code/duffle/pad.h @@ -81,9 +81,9 @@ typedef Enum_(U1, PadUnknownId) { PadUnknownId_Sentinel = 0xFF, }; typedef Enum_(U4, PadAxisCentered) { - PadAxis_Centered_Hi = 0x8080, - PadAxis_Centered_Lo = 0x8080, - PadAxis_Centered_Word = 0x80808080U, + PadAxis_Centered_Hi = 0x8080, + PadAxis_Centered_Lo = 0x8080, + PadAxis_Centered = 0x80808080U, }; typedef Enum_(U1, PadDeadZone) { PadDeadZone_LowBound = 0x70, /* left_x < LowBound → active; delta = 0x80 - left_x > 0 (rightward pull) */ diff --git a/code/hello_camera/hello_camera.atom.c b/code/hello_camera/hello_camera.atom.c index 7eae002..e0d1880 100644 --- a/code/hello_camera/hello_camera.atom.c +++ b/code/hello_camera/hello_camera.atom.c @@ -779,32 +779,35 @@ internal MipsAtom_(pad_input_cam) atom_info(atom_bind(Binds_PadInputCam) // D-pad Left → cam.pos.x -= 50. and_i fulfills BD-slot for load on R_Cam. and_i(R_T3, R_T0, Pad_Left), branch_le_zero(R_T3, atom_offset(left_x, exit_left_x)), mac_yield_load(), - add_si(R_T1, R_T1, -50), store_word(R_T1, R_Cam, O_(Camera,pos.x)), -atom_label(exit_left_x) + add_si(R_T1, R_T1, -50), store_word(R_T1, R_Cam, O_(Camera,pos.x)), + atom_label(exit_left_x) + /* D-pad Right → cam.pos.x += 50. Reuses R_T1 from Left. */ and_i(R_T3, R_T0, Pad_Right), branch_le_zero(R_T3, atom_offset(right_x, exit_right_x)), nop, add_si(R_T1, R_T1, 50), store_word(R_T1, R_Cam, O_(Camera,pos.x)), -atom_label(exit_right_x) + atom_label(exit_right_x) /* D-pad Up → cam.pos.y -= 50. Load pos.y BEFORE the andi. */ load_word(R_T1, R_Cam, O_(Camera,pos.y)), and_i(R_T3, R_T0, Pad_Up), branch_le_zero(R_T3, atom_offset(up_y, exit_up_y)), nop, add_si(R_T1, R_T1, -50), store_word(R_T1, R_Cam, O_(Camera,pos.y)), -atom_label(exit_up_y) + atom_label(exit_up_y) + /* D-pad Down → cam.pos.y += 50. Reuses R_T1 from Up. */ and_i(R_T3, R_T0, Pad_Down), branch_le_zero(R_T3, atom_offset(down_y, exit_down_y)), nop, add_si(R_T1, R_T1, 50), store_word(R_T1, R_Cam, O_(Camera,pos.y)), -atom_label(exit_down_y) + atom_label(exit_down_y) /* D-pad Cross → cam.pos.z -= 50. Load pos.z BEFORE the andi. */ load_word(R_T1, R_Cam, O_(Camera,pos.z)), and_i(R_T3, R_T0, Pad_Cross), branch_le_zero(R_T3, atom_offset(cross_z, exit_cross_z)), nop, add_si(R_T1, R_T1, -50), store_word(R_T1, R_Cam, O_(Camera,pos.z)), -atom_label(exit_cross_z) + atom_label(exit_cross_z) + /* D-pad Circle → cam.pos.z += 50. Reuses R_T1 from Cross. */ and_i(R_T3, R_T0, Pad_Circle), branch_le_zero(R_T3, atom_offset(circle_z, exit_circle_z)), nop, add_si(R_T1, R_T1, 50), store_word(R_T1, R_Cam, O_(Camera,pos.z)), -atom_label(exit_circle_z) + atom_label(exit_circle_z) mac_yield_tail(), }; diff --git a/code/hello_camera/hello_camera.c b/code/hello_camera/hello_camera.c index 155b1a9..6fd39b6 100644 --- a/code/hello_camera/hello_camera.c +++ b/code/hello_camera/hello_camera.c @@ -132,13 +132,8 @@ I_ void resolve_look_at_c11(MT3_S2S4* look_at, P3_S4* eye, P3_S4* target, V3_S4* FI_ void camera_look_at_c11(Camera* c, P3_S4* target, V3_S4* up_in) { resolve_look_at_c11(& c->look_at, & c->pos, target, up_in); } /* Pre-build all 7 chain atoms of the resolve_look_at bundle into the static arena. - * Called ONCE from main() before the frame loop. - * After this returns, the smem.resolve_look_at_atom_addrs[] array contains valid MIPS atom pointers - * for the frame-time bundle helper to emit via tb_emit(tb, captured_addr). - * * 4 unique procs in hello_camera.atom.c (chain atoms 0, 2, 4, 6); atoms 1, 3, 5 - * share the GENERIC normalize_v3s4_proc from gte.atom.c (called 3x with different - * O_(ResolveLookAtScratch,...) offsets): + * share the GENERIC normalize_v3s4_proc from gte.atom.c * 0: resolve_look_at__input_and_sub_proc * 1: normalize_v3s4_proc (fwd → uz; offsets 0, 16) * 2: resolve_look_at__cross_uz_up_in_to_right_proc @@ -146,82 +141,6 @@ FI_ void camera_look_at_c11(Camera* c, P3_S4* target, V3_S4* up_in) { resolve_lo * 4: resolve_look_at__cross_uz_ux_to_up_proc * 5: normalize_v3s4_proc (up → uy; offsets 64, 80) * 6: resolve_look_at__populate_and_translate_proc - * - * Task 12.16 promotion: the bundle-specific resolve_look_at__chain_normalize_proc - * has been promoted to the generic normalize_v3s4_proc (gte.atom.c), which now - * takes r_scratch + r_src_offset + r_dst_offset as U4 parameters. The 3 callers - * pass O_(ResolveLookAtScratch,...) macros as offset args. The metaprogram emits - * one set of `atom_offset__normalize_v3s4__srav_path__aligned_done` defs - * (namespaced by atom name) in duffle/gen/offsets.h, shared by all 3 callers. - * - * GPR pool per atom: 10 free GPRs (R_T0..R_T3 + R_T5..R_T7 + R_V0 + R_V1 + R_AT). - * R_T4 is reserved as the wave-context carrier (R_ResolveScratch). - */ -/* === EXPLICIT REGISTER ALLOCATION TRACKER === - * Every GPR used by every atom is tracked below. NO GPR is assigned to - * two atoms at overlapping lifetimes. The tape runtime preserves R_T8/R_T9 - * (R_AtomJmp/R_TapePtr) and clobbers R_T0-R_T7, R_AT, R_V0, R_V1. - * R_T4 is reserved as R_ResolveScratch (wave-context carrier). - * - * GPR pool: R_T0($8), R_T1($9), R_T2($10), R_T3($11), R_T5($13), - * R_T6($14), R_T7($15), R_V0($2), R_V1($3), R_AT($1) - * Reserved: R_T4($12) = R_ResolveScratch - * Tape: R_T8($24) = R_AtomJmp, R_T9($25) = R_TapePtr (preserved) - * - * === ATOM 0: input_and_sub (stages eye/up_in, computes fwd) === - * Pop tape → R_T0(target), R_T1(eye), R_T2(up_in). - * Use R_T3,R_T5,R_T6,R_T7 as temps. - * NO conflict with other atoms (each atom has independent lifetime). - * - * === ATOM 1: normalize fwd→uz === - * r_src_offset=0, r_dst_offset=16. - * r_src_ptr=R_T0, r_dst_ptr=R_T1, r_tmp=R_T2 (preserved for stage 4). - * r_mac1=R_T3, r_mac2=R_T5, r_recip=R_T6, r_lzcr=R_T7, r_shift=R_V0, r_branch=R_V1. - * - * === ATOM 2: cross uz×up_in→right === - * r_a=R_T0, r_b=R_T1, r_c=R_T2, r_d=R_T3, r_f(out)=R_T5, r_g=R_T6, r_h=R_T7. - * - * === ATOM 3: normalize right→ux === - * Same GPR pool as atom 1. - * - * === ATOM 4: cross uz×ux→up === - * r_a=R_T0, r_b=R_T1, r_c=R_T2, r_d=R_T3, r_f(out)=R_T5, r_g=R_T6, r_h=R_T7. - * - * === ATOM 5: normalize up→uy === - * Same GPR pool as atom 1. - * - * === ATOM 6a: populate (m[][] from ux/uy/uz, t[]=0) === - * r_look_at=R_T0 (pop tape), r_scratch=R_T4. - * r_pux=R_T1, r_puy=R_T3, r_puz=R_T5. - * r_tmp0=R_T2, r_tmp1=R_T6, r_tmp2=R_V0. - * - * === ATOM 6a.5: set_gte_mt3s2s4 (ctc2 RT matrix) === - * BAKED atom. Uses R_T3 internally (hardcoded in gte.atom.c). - * NO conflict — different GPR pool, and the atom body hardcodes R_T3 - * as the matrix pointer. We DON'T need to assign R_T3 to atom 6a.5 - * because it's a baked atom with its own GPR usage. - * - * === ATOM 6b: matrix_vector (RT * (-eye) >> 12) === - * r_look_at=R_T0 (pop tape), r_scratch=R_T4. - * r_peye=R_T1. - * r_tmp0=R_T2, r_tmp1=R_T3, r_tmp2=R_T5. - * Uses mac_apply_matrix_lv which internally uses these temps. - * - * === ATOM 6c: trans_matrix (off → look_at->t[]) === - * r_look_at=R_T0 (pop tape), r_scratch=R_T4. - * r_off_ptr=R_T1. - * r_tmp0=R_T2. - * - * === CONFLICT CHECK === - * All atoms use the same GPR pool R_T0-R_T3, R_T5-R_T7, R_V0-R_V1. - * But atoms are SEQUENTIAL — each atom's lifetime is disjoint from - * the next atom's lifetime. The tape yield handshake between atoms - * preserves R_TapePtr (R_T9) and R_AtomJmp (R_T8). - * - * The GPR pool is SHARED across atoms (they run sequentially, not - * concurrently). Each atom's build call assigns specific R_T* codes - * for that atom's body. The same R_T* code can be reused across atoms - * because the previous atom's body has already yielded. */ internal void resolve_look_at_init(void) { /* Wrap the static arena in a MipsAtomBuilder. */ @@ -433,10 +352,10 @@ void update(PrimitiveArena* pa, U4* ordering_buf) tb_data_(state, & smem.pad[0]); tb_data_(cam, & smem.cam); - // tb_emit_(pad_input_cube_rotation); - // tb_data_(state, & smem.pad[0]); - // tb_data_(cube_rot, & smem.cube.rot); - // tb_data_(floor_rot, & smem.floor.rot); + tb_emit_(pad_input_cube_rotation); + tb_data_(state, & smem.pad[0]); + tb_data_(cube_rot, & smem.cube.rot); + tb_data_(floor_rot, & smem.floor.rot); } }