diff --git a/code/duffle/gen/macs.h b/code/duffle/gen/macs.h index 7809a5a..b099074 100644 --- a/code/duffle/gen/macs.h +++ b/code/duffle/gen/macs.h @@ -84,6 +84,13 @@ WORD_COUNT(mac_load_v3s4, 3) , store_word(rt_z, base, offset + O_(V3_S4,z)) WORD_COUNT(mac_store_v3s4, 3) +/* atom_dbg_skip */ +#define mac_add_si_v3s4(rt_x, rt_y, rt_z, base, offset) \ + add_si(rt_x, base, O_(V3_S4,x)) \ +, add_si(rt_y, base, O_(V3_S4,y)) \ +, add_si(rt_z, base, O_(V3_S4,z)) +WORD_COUNT(mac_add_si_v3s4, 3) + /* atom_dbg_skip */ #define mac_sub_v3s4(rds_x, rds_y, rds_z, rt_x, rt_y, rt_z) \ sub_s(rds_x, rds_x, rt_x) \ @@ -105,6 +112,24 @@ WORD_COUNT(mac_store_rects2, 4) , or_i_self( dst, u4_lo(imm)) WORD_COUNT(mac_load_word_imm, 2) +#define mac_shift_aright_v3_self(dt_x, dt_y, dt_z, shift_amount) \ + shift_aright(dt_x, dt_x, shift_amount) \ +, shift_aright(dt_y, dt_y, shift_amount) \ +, shift_aright(dt_z, dt_z, shift_amount) +WORD_COUNT(mac_shift_aright_v3_self, 3) + +#define mac_shift_aright_var_v3(rd_v0, rd_v1, rd_v2, rs_v0, rs_v1, rs_v2, r_shift) \ + shift_aright_var(rd_v0, rs_v0, r_shift) \ +, shift_aright_var(rd_v1, rs_v1, r_shift) \ +, shift_aright_var(rd_v2, rs_v2, r_shift) +WORD_COUNT(mac_shift_aright_var_v3, 3) + +#define mac_shift_aright_var_v3_self(rds_v0, rds_v1, rds_v2, r_shift) \ + shift_aright_var(rds_v0, rds_v0, r_shift) \ +, shift_aright_var(rds_v1, rds_v1, r_shift) \ +, shift_aright_var(rds_v2, rds_v2, r_shift) +WORD_COUNT(mac_shift_aright_var_v3_self, 3) + /* atom_dbg_skip */ #define mac_load_tri_indices(r_face_cusor, r_i0, r_i1, r_i2) \ load_half_u(r_i0, r_face_cusor, 0 * S_(S2)) \ @@ -125,19 +150,19 @@ WORD_COUNT(mac_gte_store_f3, 3) , add_u_self(R_AT, r_vert_base) \ , 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) \ +, LdSlot_ gte_mv_to_data_r(R_V0, C2_VXY0) \ , gte_mv_to_data_r(R_V1, C2_VZ0) \ , shift_lleft(R_AT, r_v1, v3s2_byteoff) \ , add_u_self(R_AT, r_vert_base) \ , 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) \ +, LdSlot_ gte_mv_to_data_r(R_V0, C2_VXY1) \ , gte_mv_to_data_r(R_V1, C2_VZ1) \ , shift_lleft(R_AT, r_v2, v3s2_byteoff) \ , add_u_self(R_AT, r_vert_base) \ , 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) \ +, LdSlot_ gte_mv_to_data_r(R_V0, C2_VXY2) \ , gte_mv_to_data_r(R_V1, C2_VZ2) WORD_COUNT(mac_gte_load_tri_verts, 18) @@ -162,11 +187,11 @@ WORD_COUNT(mac_gte_store_g4_p3, 1) WORD_COUNT(mac_gte_sqr_v3, 8) /* atom_dbg_skip */ -#define mac_gte_sqr_v3s4(r_sx, r_sy, r_sz, nop_slot) \ +#define mac_gte_sqr_v3s4(r_sx, r_sy, r_sz, delay_slot) \ gte_mv_to_data_r(r_sx, C2_IR1) \ , gte_mv_to_data_r(r_sy, C2_IR2) \ , gte_mv_to_data_r(r_sz, C2_IR3) \ -, nop_slot \ +, delay_slot \ , gte_cmdw_sqr WORD_COUNT(mac_gte_sqr_v3s4, 5) @@ -204,18 +229,6 @@ WORD_COUNT(mac_trans_mt3s3s4, 6) , shift_aright(r_mag_sq, r_mag_sq, 1) WORD_COUNT(mac_lzcr_round_even_half_shift, 5) -#define mac_shift_aright_var_v3(rd_v0, rd_v1, rd_v2, rs_v0, rs_v1, rs_v2, r_shift) \ - shift_aright_var(rd_v0, rs_v0, r_shift) \ -, shift_aright_var(rd_v1, rs_v1, r_shift) \ -, shift_aright_var(rd_v2, rs_v2, r_shift) -WORD_COUNT(mac_shift_aright_var_v3, 3) - -#define mac_shift_aright_var_v3_self(rds_v0, rds_v1, rds_v2, r_shift) \ - shift_aright_var(rds_v0, rds_v0, r_shift) \ -, shift_aright_var(rds_v1, rds_v1, r_shift) \ -, shift_aright_var(rds_v2, rds_v2, r_shift) -WORD_COUNT(mac_shift_aright_var_v3_self, 3) - #define mac_gte_general_purpose_interopolation(to_ir0, to_ir1, to_ir2, to_ir3, fr_mac1, fr_mac2, fr_mac3, nop_slot1, nop_slot2) \ gte_mv_to_data_r(to_ir0, C2_IR0) \ , gte_mv_to_data_r(to_ir1, C2_IR1) /* IR1 = src.x (preserved in r_tmp — r_mac2_scratch was clobbered to MAC2 in stage 1.5) */ \ diff --git a/code/duffle/gen/offsets.h b/code/duffle/gen/offsets.h index 149dd06..91c1b9c 100644 --- a/code/duffle/gen/offsets.h +++ b/code/duffle/gen/offsets.h @@ -25,6 +25,14 @@ #pragma region duffle +// --- atom: example_atom (10 words) --- + +#define _atom_offset_example_atom_proc_skip 2 + +enum { + atom_offset_example_atom_proc_skip = _atom_offset_example_atom_proc_skip, +}; + // --- atom: normalize_v3s4 (47 words) --- #define _atom_offset_aligned_done_srav_path 3 diff --git a/code/duffle/gp.atom.c b/code/duffle/gp.atom.c index 5c5313a..619f513 100644 --- a/code/duffle/gp.atom.c +++ b/code/duffle/gp.atom.c @@ -44,7 +44,8 @@ atom_dbg_skip MipsAtomComp_Proc_(ab, { }) /* Words: 11; Correctly inserts a primitive into the Ordering Table linked list. */ -I_ Slice_MipsCode ac_insert_ot_tag(AtomBuilder_R ab, U4 r_ot_base, U4 r_prim_cursor, U4 poly_size) MipsAtomComp_Proc_(ab, { +// TODO(Ed): Expose R_T1 as a r_t0, r_V0 as r_t2 +I_ Slice_MipsCode ac_insert_ot_tag(AtomBuilder_R ab, Reg r_ot_base, Reg r_prim_cursor, U2 poly_size) MipsAtomComp_Proc_(ab, { 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_ot_base), // T1 = & OrderingTable[OTZ] load_word( R_AT, R_T1, O_(PolyTag,code)), // AT = old_ot_head diff --git a/code/duffle/gp.h b/code/duffle/gp.h index 0d37aca..25bfed1 100644 --- a/code/duffle/gp.h +++ b/code/duffle/gp.h @@ -418,14 +418,11 @@ typedef Struct_(PolyTag) { }; }; -/* DSL cast convention: every cast uses `C_()`, every pointer qualifier is `R_` (restrict) or `V_` (volatile). - * No raw C-style casts. RHS values are assumed to be `U4` — caller passes a `U4` directly. */ #define set_len(tag,v) (C_(PolyTag_R,tag)->len = u4_(v)) #define set_addr(tag,v) (C_(PolyTag_R,tag)->addr = u4_(v)) -/* `set_code` is no longer in the new PolyTag design — the code byte lives in the primitive body +/* `set_code` is no longer in the new PolyTag design * (e.g. `((Poly_F3*)(p))->code`), not in the tag. - * Use the typed primitive structs (Poly_F3, Poly_G4, etc.) and the `set_poly_*` setters, - * which set both the tag's length and the code. */ + * Use the typed primitive structs (Poly_F3, Poly_G4, etc.) and the `set_poly_*` setters, which set both the tag's length and the code. */ #define get_len(tag) C_(U4,C_(PolyTag_R,tag)->len) #define get_addr(tag) C_(U4,C_(PolyTag_R,tag)->addr) @@ -572,7 +569,7 @@ enum { /* Default TPage value libpsyx's SetDefDrawEnv writes (matches the `li v1, 10; sh v1, 20(v0)` sequence at C11_only.elf:0x8001273C). */ gp0_tpage_default = 10, - /* TPage semi-transparency mode payload values (NOT bit positions). */ + /* TPage semi-transparency mode payload values. */ gp0_tpage_semi_trans_none = 0x0, gp0_tpage_semi_trans_alpha = 0x1, gp0_tpage_semi_trans_add = 0x2, diff --git a/code/duffle/gte.atom.c b/code/duffle/gte.atom.c index fc66d84..58c1b9e 100644 --- a/code/duffle/gte.atom.c +++ b/code/duffle/gte.atom.c @@ -28,9 +28,9 @@ FI_ Slice_MipsCode ac_gte_store_f3(AtomBuilder_R ab, U4 r_primitive_cursor) atom /* Words: 18; Translates indices to vertex addresses and pushes them to GTE */ I_ Slice_MipsCode ac_gte_load_tri_verts(AtomBuilder_R ab, U4 r_vert_base, U4 r_v0, U4 r_v1, U4 r_v2) atom_dbg_skip MipsAtomComp_Proc_(ab, { - shift_lleft(R_AT, r_v0, v3s2_byteoff), add_u_self(R_AT, r_vert_base), 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_v1, v3s2_byteoff), add_u_self(R_AT, r_vert_base), 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_v2, v3s2_byteoff), add_u_self(R_AT, r_vert_base), 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), + shift_lleft(R_AT, r_v0, v3s2_byteoff), add_u_self(R_AT, r_vert_base), load_word(R_V0, R_AT, O_(V3_S2,x)), load_word(R_V1, R_AT, O_(V3_S2,z)), LdSlot_ gte_mv_to_data_r(R_V0, C2_VXY0), gte_mv_to_data_r(R_V1, C2_VZ0), + shift_lleft(R_AT, r_v1, v3s2_byteoff), add_u_self(R_AT, r_vert_base), load_word(R_V0, R_AT, O_(V3_S2,x)), load_word(R_V1, R_AT, O_(V3_S2,z)), LdSlot_ gte_mv_to_data_r(R_V0, C2_VXY1), gte_mv_to_data_r(R_V1, C2_VZ1), + shift_lleft(R_AT, r_v2, v3s2_byteoff), add_u_self(R_AT, r_vert_base), load_word(R_V0, R_AT, O_(V3_S2,x)), load_word(R_V1, R_AT, O_(V3_S2,z)), LdSlot_ gte_mv_to_data_r(R_V0, C2_VXY2), gte_mv_to_data_r(R_V1, C2_VZ2), }) /* Words: 3; Stores the 3 transformed (V2_S2 screen) vertices of the @@ -38,7 +38,7 @@ I_ Slice_MipsCode ac_gte_load_tri_verts(AtomBuilder_R ab, U4 r_vert_base, U4 r_v * PIPELINE: post-RTPT, pre-RTPS (SXY0=v0.screen, SXY1=v1.screen, SXY2=v2.screen). * MUST be called BEFORE V3-RTPS, otherwise SXY0/1/2 get overwritten with v3 * (RTPS writes only to SXY2, but to keep the three registers aligned with v0/v1/v2 you must store before RTPS). */ -FI_ Slice_MipsCode ac_gte_store_g4_p012(AtomBuilder_R ab, U4 r_primitive_cursor) atom_dbg_skip MipsAtomComp_Proc_(ab, { +FI_ Slice_MipsCode ac_gte_store_g4_p012(AtomBuilder_R ab, Reg r_primitive_cursor) atom_dbg_skip MipsAtomComp_Proc_(ab, { gte_sw(C2_SXY0, r_primitive_cursor, O_(Poly_G4,p0)), gte_sw(C2_SXY1, r_primitive_cursor, O_(Poly_G4,p1)), gte_sw(C2_SXY2, r_primitive_cursor, O_(Poly_G4,p2)), @@ -65,12 +65,12 @@ FI_ Slice_MipsCode ac_gte_sqr_v3(AtomBuilder_R ab, U4 r_sx, U4 r_sy, U4 r_sz, U4 * The SQR command always squares IR1/IR2/IR3 — those C2 registers are fixed. * The GPRs holding the source vector are caller-determined. * Words: 5 (3 mtc2 + 1 nop hazard + 1 cmd). */ -FI_ Slice_MipsCode ac_gte_sqr_v3s4(AtomBuilder_R ab, Reg r_sx, Reg r_sy, Reg r_sz, MipsCode nop_slot) +FI_ Slice_MipsCode ac_gte_sqr_v3s4(AtomBuilder_R ab, Reg r_sx, Reg r_sy, Reg r_sz, MipsCode delay_slot) atom_dbg_skip MipsAtomComp_Proc_(ab, { gte_mv_to_data_r(r_sx, C2_IR1), gte_mv_to_data_r(r_sy, C2_IR2), gte_mv_to_data_r(r_sz, C2_IR3), - nop_slot, gte_cmdw_sqr, + delay_slot, gte_cmdw_sqr, }) /* ─── STAGE 4 of normalize: mtc2 IR0..3 + GPF + mfc2 MAC + srav finalize ─── @@ -132,8 +132,7 @@ FI_ Slice_MipsCode ac_trans_mt3s3s4(AtomBuilder_R ab FI_ Slice_MipsCode ac_lzcr_round_even_half_shift(AtomBuilder_R ab, U4 r_shift, U4 r_mag_sq, - U4 r_mag_sq_copy -) + U4 r_mag_sq_copy) atom_dbg_skip MipsAtomComp_Proc_(ab, { and_i(r_shift, r_shift, gte_lzcr_even_mask), or_u(r_mag_sq_copy, r_mag_sq, 0), @@ -142,25 +141,6 @@ atom_dbg_skip MipsAtomComp_Proc_(ab, { shift_aright(r_mag_sq, r_mag_sq, 1), }) -FI_ Slice_MipsCode ac_shift_aright_var_v3(AtomBuilder_R ab - , Reg rd_v0, Reg rd_v1, Reg rd_v2 - , Reg rs_v0, Reg rs_v1, Reg rs_v2 - , Reg r_shift) -MipsAtomComp_Proc_(ab, { - shift_aright_var(rd_v0, rs_v0, r_shift), - shift_aright_var(rd_v1, rs_v1, r_shift), - shift_aright_var(rd_v2, rs_v2, r_shift), -}) - -FI_ Slice_MipsCode ac_shift_aright_var_v3_self(AtomBuilder_R ab - , Reg rds_v0, Reg rds_v1, Reg rds_v2 - , Reg r_shift) -MipsAtomComp_Proc_(ab, { - shift_aright_var(rds_v0, rds_v0, r_shift), - shift_aright_var(rds_v1, rds_v1, r_shift), - shift_aright_var(rds_v2, rds_v2, r_shift), -}) - FI_ Slice_MipsCode ac_gte_general_purpose_interopolation(AtomBuilder_R ab , Reg to_ir0, Reg to_ir1, Reg to_ir2, Reg to_ir3 , Reg fr_mac1, Reg fr_mac2, Reg fr_mac3 @@ -178,9 +158,8 @@ MipsAtomComp_Proc_(ab, { gte_mv_from_data_r(fr_mac3, C2_MAC3), }) -FI_ Slice_MipsCode gte_mv_from_data_r_mac123(AtomBuilder_R ab - , Reg fr_mac1, Reg fr_mac2, Reg fr_mac3 -) +FI_ Slice_MipsCode ac_gte_mv_from_data_r_mac123(AtomBuilder_R ab + , Reg fr_mac1, Reg fr_mac2, Reg fr_mac3) MipsAtomComp_Proc_(ab, { gte_mv_from_data_r(fr_mac1, C2_MAC1), gte_mv_from_data_r(fr_mac2, C2_MAC2), @@ -257,9 +236,8 @@ internal S2 const gte_normalize_sqr_tbl[192] align_(2) = { 0x0820, 0x081c, 0x0818, 0x0814, 0x0810, 0x080c, 0x0808, 0x0804, }; -#define RegUse_(proc_name) (tmpl(RegUse,proc_name)) typedef Struct_(RegUse_normalize_v3s4_proc) { - Reg scratch; // Scratch base carrier. + Reg const scratch; // Scratch base carrier. Reg src_ptr; Reg dst_ptr; Reg recip_est; // |v|² sum + shift-input + sqrtbl[index] @@ -267,7 +245,7 @@ typedef Struct_(RegUse_normalize_v3s4_proc) { Reg src_x; union { Reg mac1_scratch; } t3; union { Reg mac2_scratch; } t4; - union { Reg shift_count, btarget, lookup_addr, src_z; } t5; + union { Reg btarget, shift_count, lookup_addr, src_z; } t5; }; /* ─── Full normalize (all 4 stages inline) ─── * Generic 4-stage GTE normalize (SQR → sum+LZCR → align+sqrtbl → GPF+srav). @@ -306,7 +284,7 @@ MipsAtom_Proc_(aa, { /* Load src.x/y/z from r_src_ptr (caller-determined address) into r_tmp/r_recip_est/r_branch_tmp. * r.rt1_src_x holds src.x throughout stages 1-2 — r_mac2_scratch is clobbered to MAC2 in stage 1.5 (line below). */ - mac_load_v3s4(r.src_x, r.recip_est, r.t5.lookup_addr, r.src_ptr, 0), + mac_load_v3s4(r.src_x, r.recip_est, r.t5.src_z, r.src_ptr, 0), /* Stage 1: mtc2 src → IR1/2/3, SQR fires. */ LdSlot_ mac_gte_sqr_v3s4(r.src_x, r.recip_est, r.t5.src_z, LdSlot_ nop), diff --git a/code/duffle/gte.h b/code/duffle/gte.h index e6ff613..7aedc1b 100644 --- a/code/duffle/gte.h +++ b/code/duffle/gte.h @@ -33,7 +33,7 @@ * gte.h — Geometry Transformation Engine (COP2) for the PS1 * ============================================================================ * - * Hand-rolled DSL for emitting GTE/MIPS instruction words as raw `.word` constants from C. + * Hand-rolled DSL for emitting GTE/MIPS instruction words from C. * No GCC inline-assembly string syntax in the code body. * * STYLE NOTES @@ -191,27 +191,22 @@ enum { }; /* --- GTE Control Register Aliases (Pitfall 1) --- - * Three pairs of aliases map to the SAME C2 control-register slot on real silicon: + * Three pairs of aliases map to the C2 control-register slot: * C2[24] = gte_cr_RBK (background R) | gte_cr_OFX (screen offset X) * C2[25] = gte_cr_GBK (background G) | gte_cr_OFY (screen offset Y) * C2[26] = gte_cr_BBK (background B) | gte_cr_H (projection plane distance H) - * Cross-alias writes inside one atom body, or across the wave-context boundary, - * silently clobber each other. The metaprogram's check_gte_cr_alias_writes - * (CHECK_RULES row) warns about each pair per source. See - * docs/gte_reference.md §"Control-register alias table" for the silicon - * rationale and the libgte outer-product convention. + * Cross-alias writes inside one atom body, or across the wave-context boundary, silently clobber each other. + * The metaprogram's check_gte_cr_alias_writes (CHECK_RULES row) warns about each pair per source. + * See psx-spx docs/gte_reference.md §"Control-register alias table" for the silicon rationale and the libgte outer-product convention. */ /* --- RT-matrix packed-slot convention (Pitfall 4) --- * The silicon packs two 16-bit RT elements per 32-bit C2 slot: * C2[2] = (RT22 << 16) | RT13 (gte_cr_RT13 writes the low half, gte_cr_RT22 writes the high half) * C2[4] = (RT33 << 16) | RT22 (gte_cr_RT22 writes the low half — clobbers prior RT22 value if RT13 was also written) - * OP and MVMVA read D1/D2/D3 from these packed slots. The libgte outer-product - * convention (see ac_apply_matrix_lv at gte.atom.c:108-122) writes C2[2] then - * C2[4] in sequence; the SECOND write's low half is RT22, not RT13. An agent - * who writes gte_cr_RT13 then gte_cr_RT22 to the SAME source GPR clobbers the - * RT13 value. See docs/gte_reference.md §"RT-matrix packed-slot convention" - * for the canonical write pattern. + * OP and MVMVA read D1/D2/D3 from these packed slots. + * The libgte outer-product convention (see ac_apply_matrix_lv at gte.atom.c:108-122) writes C2[2] then C2[4] in sequence; + * the SECOND write's low half is RT22, not RT13. */ /* --- GTE Control Register Indices (for ctc2/cfc2) --- @@ -300,8 +295,7 @@ enum { _C2_TX_SUBS_ = 0 // #define gte_mv_from_data_r(rt, rd) enc_gte_tx(cop_mf, (rt), (rd)) /* Move GTE Control Register (rd) to GPR (rt) */ /* GTE Data vs Control Register Transfers - * - * Each macro emits a single .word constant for one of MFC2/CFC2/MTC2/CTC2. + * Each macro emits a single instruction for one of MFC2/CFC2/MTC2/CTC2. * * `rd` is the C2 register index in the file the sub-opcode names: * gte_mv_from_data_r / gte_mv_to_data_r → C2 data register file @@ -316,14 +310,14 @@ enum { _C2_TX_SUBS_ = 0 #define gte_mv_from_ctrl_r(rt, rd) enc_gte_tx(sub_cfc2, (rt), (rd)) /* Copy From ctrl reg */ #define gte_mv_to_data_r(rt, rd) enc_gte_tx(sub_mtc2, (rt), (rd)) /* Move To data reg */ #define gte_mv_to_ctrl_r(rt, rd) enc_gte_tx(sub_ctc2, (rt), (rd)) /* Copy To ctrl reg */ +#define DmaSlot_ // Annotate an instruction as filling a CPU <-> GTE DMA delay slot/s /* COP2 Data Load (lwc2): `lwc2 rt, off(rs)` * Layout: [op_lwc2:6][rs:5][rt:5][imm:16] * - rs: GPR base address * - rt: COP2 data register index (0..31) * - imm: signed 16-bit offset - * NOTE: When `rs` is a runtime register, the encoding cannot be pre-baked - * into a .word — use the string-style `gte_load_v0` macro below instead. */ + * NOTE: When `rs` is a runtime register, the encoding cannot be pre-baked into a .word — use the string-style `gte_load_v0` macro below instead. */ #define enc_gte_lw(rt, base, off) enc_i(op_lwc2, (base), (rt), (off)) /* Store Word */ #define enc_gte_sw(rt, base, off) enc_i(op_swc2, (base), (rt), (off)) @@ -332,8 +326,7 @@ enum { _C2_TX_SUBS_ = 0 * `swc2` is redundant when we're already inside the `gte_` namespace. * gte_lw rt, base, off → lwc2 rt, off(base) * gte_sw rt, base, off → swc2 rt, off(base) - * For the typical user-facing vector-level load (xy + z as two instructions), - * use the higher-level `gte_load_vN` macros below. */ + * For the typical user-facing vector-level load (xy + z as two instructions), use the higher-level `gte_load_vN` macros below. */ #define gte_lw(rt, base, off) enc_gte_lw(rt, base, off) #define gte_sw(rt, base, off) enc_gte_sw(rt, base, off) @@ -350,13 +343,13 @@ enum { _C2_TX_SUBS_ = 0 #define gte_cmd_base (enc_op(op_cop2) | (1 << 25)) /* Per-field encoders. Each one does (value & mask) << shift on its own. */ -#define enc_gte_sf(sf) ((sf) << gte_shift_sf ) -#define enc_gte_mx(mx) ((mx) << gte_shift_mx ) -#define enc_gte_v(v) ((v) << gte_shift_v ) -#define enc_gte_cv(cv) ((cv) << gte_shift_cv ) -#define enc_gte_lm(lm) ((lm) << gte_shift_lm ) -#define enc_gte_cmd(cmd) ((cmd) << gte_shift_cmd ) -#define enc_gte_fake_cmd(x) ((x) << gte_shift_fake_cmd) +#define enc_gte_sf(sf) ((sf) << gte_shift_sf ) +#define enc_gte_mx(mx) ((mx) << gte_shift_mx ) +#define enc_gte_v(v) ((v) << gte_shift_v ) +#define enc_gte_cv(cv) ((cv) << gte_shift_cv ) +#define enc_gte_lm(lm) ((lm) << gte_shift_lm ) +#define enc_gte_cmd(cmd) ((cmd) << gte_shift_cmd ) +#define enc_gte_fake_cmd(x) ((x) << gte_shift_fake_cmd) /* Composite: all six GTE fields + the COP2/CO base. */ #define enc_gte_cmdw(sf, mx, v, cv, lm, cmd) ( \ @@ -408,10 +401,11 @@ enum { _C2_TX_SUBS_ = 0 #define gte_cmdw_rtpt (gte_cmd_base | enc_gte_cmd(gte_cmd_rtpt ) | gte_cmdw_psyq_compat) #define gte_cmdw_nclip (gte_cmd_base | enc_gte_cmd(gte_cmd_nclip)) #define gte_cmdw_op (gte_cmd_base | enc_gte_cmd(gte_cmd_op )) -#define gte_cmdw_outer_product gte_cmdw_op /* "outer product" -- NOCASH/Sdk terminology */ -#define gte_cmdw_wedge gte_cmdw_op /* "wedge product" -- geometric-algebra terminology. - * RGA(Lengyel): the GTE OP is a 3D signed-16-bit D x IR cross, not a generic RGA exterior product. - * The wedge alias is the 3D complement interpretation of the same 3 scalars (MAC1..MAC3). */ +#define gte_cmdw_outer_product gte_cmdw_op /* "outer product" -- PSY-Q terminology */ +#define gte_cmdw_wedge gte_cmdw_op /* "wedge product" -- geometric-algebra terminology. */ +#define gte_cmdw_cross gte_cmdw_op /* "cross product" -- geometric-algebra terminology. + * RGA(Lengyel): The GTE OP is a 3D signed-16-bit D x IR cross, not a generic RGA exterior product. + * The wedge alias is a 3D complement interpretation of the same 3 scalars (MAC1..MAC3). */ #define gte_cmdw_mvmva (gte_cmd_base | enc_gte_cmd(gte_cmd_mvmva)) /* MVMVA with sf=0 (no shift, full-integer), cv=3 (no translation), v=3 (IR vector input). diff --git a/code/duffle/lottes_tape.h b/code/duffle/lottes_tape.h index b1b8af7..7723981 100644 --- a/code/duffle/lottes_tape.h +++ b/code/duffle/lottes_tape.h @@ -105,7 +105,7 @@ typedef U2 Reg; // Register parameter used with atom or atom component procedure typedef U4 const MipsCode; // Underlying type to mips asm words. typedef Slice_(MipsCode); -typedef U4 const MipsAtom; +typedef U4 const MipsAtom; // Underlying type to a mips atom defnition typedef Slice_(MipsAtom); // Sometimes a user will define a bundle of atoms that represent a procedure of work as: // MipsAtom* [...]; @@ -242,6 +242,7 @@ atom_dbg_skip MipsAtomComp_(ac_yield_tail) { add_ui_self(R_TapePtr, S_(MipsCode)), jump_reg( R_AtomJmp), nop, }; + #pragma endregion Macro Atom Components #pragma region Atom Builder @@ -378,6 +379,27 @@ FI_ void regfile_reset_mask(RegFile_R rf, U4 mask) { #pragma endregion RegFileArena (Register File Allocator) #pragma region Mips Atom Procs +/* RegUse structs are a convention to organize register allocations for a mips atom procedure. + Unlike the usual enum-based declarations, they provide a namespaced scope + and have view types via union declarations. +*/ +#define RegUse_(proc_name) (tmpl(RegUse,proc_name)) + + typedef Struct_(RegUse_example_atom_proc) { + Reg const ro_register; // Scratch base carrier. + Reg usual_modifiable; + union { Reg view_1, view_2, view_3; } t1; + }; + internal MipsAtom* example_atom_proc(AtomArena_R aa, U2 offset, RegUse_example_atom_proc r) + MipsAtom_Proc_(aa, { + add_si(r.usual_modifiable, r.ro_register, offset), + or_u(r.t1.view_1, r.ro_register, 0), + branch_lt_zero(r.t1.view_1, atom_offset(example_atom_proc, skip)), BdSlot_ nop, + li_s(r.t1.view_2, 100), + atom_label(skip) + add_si(r.t1.view_3, r.usual_modifiable, 10), + mac_yield(), + }) #pragma endregion Mips Atom Procs diff --git a/code/duffle/math.atom.c b/code/duffle/math.atom.c index fc23706..c118287 100644 --- a/code/duffle/math.atom.c +++ b/code/duffle/math.atom.c @@ -39,6 +39,13 @@ FI_ Slice_MipsCode ac_store_v3s4(AtomBuilder_R ab, U4 rt_x, U4 rt_y, U4 rt_z, U4 #define ac_store_p3s4 ac_store_v3s4 #define mac_store_p3s4 mac_store_v3s4 +FI_ Slice_MipsCode ac_add_si_v3s4(AtomBuilder_R ab, Reg rt_x, Reg rt_y, Reg rt_z, Reg base, U2 offset) +atom_dbg_skip MipsAtomComp_Proc_(ab, { + add_si(rt_x, base, O_(V3_S4,x)), + add_si(rt_y, base, O_(V3_S4,y)), + add_si(rt_z, base, O_(V3_S4,z)), +}) + FI_ Slice_MipsCode ac_sub_v3s4(AtomBuilder_R ab, U4 rds_x, U4 rds_y, U4 rds_z, U4 rt_x, U4 rt_y, U4 rt_z) atom_dbg_skip MipsAtomComp_Proc_(ab, { sub_s(rds_x, rds_x, rt_x), sub_s(rds_y, rds_y, rt_y), diff --git a/code/duffle/mips.atom.c b/code/duffle/mips.atom.c index bcb5adb..98d2f5b 100644 --- a/code/duffle/mips.atom.c +++ b/code/duffle/mips.atom.c @@ -16,6 +16,32 @@ atom_dbg_skip MipsAtomComp_Proc_(ab, { or_i_self( dst, u4_lo(imm)), }) +FI_ Slice_MipsCode ac_shift_aright_v3_self(AtomBuilder_R ab, Reg dt_x, Reg dt_y, Reg dt_z, U2 shift_amount) +MipsAtomComp_Proc_( ab, { + shift_aright(dt_x, dt_x, shift_amount), + shift_aright(dt_y, dt_y, shift_amount), + shift_aright(dt_z, dt_z, shift_amount), +}) + +FI_ Slice_MipsCode ac_shift_aright_var_v3(AtomBuilder_R ab + , Reg rd_v0, Reg rd_v1, Reg rd_v2 + , Reg rs_v0, Reg rs_v1, Reg rs_v2 + , Reg r_shift) +MipsAtomComp_Proc_(ab, { + shift_aright_var(rd_v0, rs_v0, r_shift), + shift_aright_var(rd_v1, rs_v1, r_shift), + shift_aright_var(rd_v2, rs_v2, r_shift), +}) + +FI_ Slice_MipsCode ac_shift_aright_var_v3_self(AtomBuilder_R ab + , Reg rds_v0, Reg rds_v1, Reg rds_v2 + , Reg r_shift) +MipsAtomComp_Proc_(ab, { + shift_aright_var(rds_v0, rds_v0, r_shift), + shift_aright_var(rds_v1, rds_v1, r_shift), + shift_aright_var(rds_v2, rds_v2, r_shift), +}) + #pragma endregion MACs (Mips Atom Components) #pragma region Baked Atoms diff --git a/code/duffle/pad.c b/code/duffle/pad.c index 482713f..a2b28c1 100644 --- a/code/duffle/pad.c +++ b/code/duffle/pad.c @@ -64,8 +64,8 @@ NI_ void pad_bios_init_start(PadBiosRaw* raw0, PadBiosRaw* raw1) asm_words( add_ui( R_T1, R_0, bios_start_pad_2), /* $t1 = 0x13 */ add_ui( R_T2, R_0, bios_btable_addr), /* $t2 = 0xB0 (re-load) */ - call_reg(R_T2), /* jalr $t2, $ra */ - nop /* BD slot */ + call_reg(R_T2), /* jalr $t2, $ra */ + nop /* BD slot */ ) asm_clobber: rlit(R_AT), diff --git a/code/hello_camera/gen/offsets.h b/code/hello_camera/gen/offsets.h index 2e95a28..7dd00f1 100644 --- a/code/hello_camera/gen/offsets.h +++ b/code/hello_camera/gen/offsets.h @@ -26,7 +26,7 @@ enum { atom_offset_end_low_exit_stick = _atom_offset_end_low_exit_stick, }; -// --- atom: pad_input_cam (40 words) --- +// --- atom: pad_input_cam (42 words) --- #define _atom_offset_left_x_exit_left_x 3 #define _atom_offset_right_x_exit_right_x 3 diff --git a/code/hello_camera/hello_camera.atom.c b/code/hello_camera/hello_camera.atom.c index 094fe9d..f2325c8 100644 --- a/code/hello_camera/hello_camera.atom.c +++ b/code/hello_camera/hello_camera.atom.c @@ -128,6 +128,11 @@ typedef Struct_(Binds_ResolveLookAtSub) { ResolveLookAtScratch* scratchpad; }; +typedef Struct_(RegUse_resolve_look_at__input_and_sub_proc) { + Reg const scratch; + Reg target; Reg eye; Reg up_in; + Reg t0; Reg t1; Reg t2; Reg t3; Reg t4; +}; /* Atom 0 in the bundle: input_and_sub. Stages C-side inputs into the scratchpad and computes fwd = target - eye. * Staging work: * * Stage eye.x/y/z → scratch (for atom 6's translation column) @@ -146,66 +151,61 @@ typedef Struct_(Binds_ResolveLookAtSub) { * R_V0 : hardcoded (load eye.z / target.z) * Pool cost: 8 GPRs + R_T4 (carrier) + R_AT + R_V0 (hardcoded) = 11 GPRs. */ -internal MipsAtom* resolve_look_at__input_and_sub_proc(AtomArena_R aa, - // TODO(Ed): We can resolve scratch at anytime its fixed to a specific address. - U4 r_scratch - , U4 r_target_ptr,U4 r_eye_ptr, U4 r_up_in_ptr - , U4 r_tmp0, U4 r_tmp1, U4 r_tmp2, U4 r_tmp3 -) MipsAtom_Proc_(aa, { - load_word(r_target_ptr, R_TapePtr, O_(Binds_ResolveLookAtSub,target)), - load_word(r_eye_ptr, R_TapePtr, O_(Binds_ResolveLookAtSub,eye)), - load_word(r_up_in_ptr, R_TapePtr, O_(Binds_ResolveLookAtSub,up_in)), - load_word(r_scratch, R_TapePtr, O_(Binds_ResolveLookAtSub,scratchpad)), - add_ui_self( R_TapePtr, S_(Binds_ResolveLookAtSub)), - - // Stage eye.x/y/z into the scratchpad (atom 6 reads these for the translation column). - mac_load_p3s4( r_tmp0, r_tmp1, r_tmp2, r_eye_ptr, 0), - mac_store_p3s4(r_tmp0, r_tmp1, r_tmp2, r_scratch, O_(ResolveLookAtScratch,eye)), +internal MipsAtom* resolve_look_at__input_and_sub_proc(AtomArena_R aa, RegUse_resolve_look_at__input_and_sub_proc r) +MipsAtom_Proc_(aa, atom_info(atom_bind(Binds_ResolveLookAtSub)){ + load_word(r.target, R_TapePtr, O_(Binds_ResolveLookAtSub,target)), + load_word(r.eye, R_TapePtr, O_(Binds_ResolveLookAtSub,eye)), + load_word(r.up_in, R_TapePtr, O_(Binds_ResolveLookAtSub,up_in)), + load_word(r.scratch, R_TapePtr, O_(Binds_ResolveLookAtSub,scratchpad)), + add_ui_self( R_TapePtr, S_(Binds_ResolveLookAtSub)), /* Stage up_in.x/y/z into the scratchpad. */ - mac_load_p3s4( r_tmp0, r_tmp1, r_tmp2, r_up_in_ptr, 0), - mac_store_p3s4(r_tmp0, r_tmp1, r_tmp2, r_scratch, O_(ResolveLookAtScratch,up_in)), + mac_load_p3s4( r.t0, r.t1, r.t2, r.up_in, 0), + mac_store_p3s4(r.t0, r.t1, r.t2, r.scratch, O_(ResolveLookAtScratch,up_in)), + + // Stage eye.x/y/z into the scratchpad (atom 6 reads these for the translation column). + mac_load_p3s4( r.t0, r.t1, r.t2, r.eye, 0), + mac_store_p3s4(r.t0, r.t1, r.t2, r.scratch, O_(ResolveLookAtScratch,eye)), /* Compute fwd = target - eye. */ - mac_load_p3s4(r_tmp0, r_tmp1, r_tmp2, r_target_ptr, 0), - mac_load_p3s4(r_tmp3, R_AT, R_V0, r_eye_ptr, 0), + // mac_load_p3s4(t3, R_AT, t4, r.eye, 0), + mac_load_p3s4(r.t3, R_AT, r.t4, r.target, 0), mac_sub_v3s4( - r_tmp0, r_tmp1, r_tmp2, - r_tmp3, R_AT, R_V0), - mac_store_v3s4(r_tmp0, r_tmp1, r_tmp2, r_scratch, O_(ResolveLookAtScratch,fwd)), + r.t3, R_AT, r.t4, + r.t0, r.t1, r.t2), + mac_store_v3s4(r.t3, R_AT, r.t4, r.scratch, O_(ResolveLookAtScratch,fwd)), mac_yield() }) +typedef Struct_(RegUse_resolve_look_at__cross_uz_up_into_right_proc) { + Reg scratch; + Reg a; Reg b; Reg c; /* load a.x/y/z; result out.x/y/z */ + Reg d; /* load b.x */ + Reg f; /* r_f = &right (out ptr), r_g = &uz, r_h = &up_in */ + + union { Reg t1, g, target0; }; + union { Reg t2, h, target1; }; + Reg t0; +}; /* Atom 2: cross uz × up_in → right. */ -internal MipsAtom* resolve_look_at__cross_uz_up_in_to_right_proc(AtomArena_R aa, U4 r_scratch - , U4 r_a, U4 r_b, U4 r_c /* load a.x/y/z; result out.x/y/z */ - , U4 r_d /* load b.x */ - , U4 r_f, U4 r_g, U4 r_h /* r_f = &right (out ptr), r_g = &uz, r_h = &up_in */ +internal MipsAtom* resolve_look_at__cross_uz_up_into_right_proc(AtomArena_R aa, + RegUse_resolve_look_at__cross_uz_up_into_right_proc r ) MipsAtom_Proc_(aa, { /* FIX: build packed RT22+RT33 with proper sign extension. */ - add_si(r_g, r_scratch, O_(ResolveLookAtScratch,uz)), /* r_g = &uz */ - add_si(r_h, r_scratch, O_(ResolveLookAtScratch,up_in)), /* r_h = &up_in */ - add_si(r_f, r_scratch, O_(ResolveLookAtScratch,right)), /* r_f = &right (out) */ + add_si(r.g, r.scratch, O_(ResolveLookAtScratch,uz)), /* r_g = &uz */ + add_si(r.h, r.scratch, O_(ResolveLookAtScratch,up_in)), /* r_h = &up_in */ + add_si(r.f, r.scratch, O_(ResolveLookAtScratch,right)), /* r_f = &right (out) */ nop, /* Load a (uz).x/y/z into r_a/r_b/r_c. */ - load_word(r_a, r_g, O_(V3_S4,x)), - load_word(r_b, r_g, O_(V3_S4,y)), - load_word(r_c, r_g, O_(V3_S4,z)), - nop, - + mac_load_v3s4(r.a, r.b, r.c, r.g, 0), /* Load b (up_in).x/y/z into r_d + R_AT/R_V0 (R_AT/R_V0 are hardcoded scratch). */ - load_word(r_d, r_h, O_(V3_S4,x)), - load_word(R_AT, r_h, O_(V3_S4,y)), - load_word(R_V0, r_h, O_(V3_S4,z)), - nop, - - /* Save the two RT control-register slots OP will clobber. We reuse - * r_g/r_h (scratch pointers, no longer needed) as the save targets. */ - gte_mv_from_ctrl_r(r_g, gte_cr_RT11), /* r_g = C2 r0 (RT11|RT12) */ - gte_mv_from_ctrl_r(r_h, gte_cr_RT22), /* r_h = C2 r4 (RT22|RT33) */ + mac_load_v3s4(r.d, R_AT, r.t0, r.h, 0), LdSlot_ + /* Save the two RT control-register slots OP will clobber. We reuse r_g/r_h (scratch pointers, no longer needed) as the save targets. */ + gte_mv_from_ctrl_r(r.target0, gte_cr_RT11), /* r_g = C2 r0 (RT11|RT12) */ + gte_mv_from_ctrl_r(r.target1, gte_cr_RT22), /* r_h = C2 r4 (RT22|RT33) */ /* Load uz.x/uz.y/uz.z into COP2 control registers. * OP reads D1 = RT11 from $0.low, D2 = RT22 from $2.high, D3 = RT33 from $4.high. * RT22 is in BOTH $2.high AND $4.low (shared bit position). OP reads from $2.high. @@ -215,48 +215,43 @@ internal MipsAtom* resolve_look_at__cross_uz_up_in_to_right_proc(AtomArena_R aa, * The 2nd ctc2 DOES clobber $4.low (becomes a.z.low, NOT a.y.high), but since OP * reads RT22 from $2.high (which the 2nd ctc2 doesn't touch), D2 is still a.y.high. * This is libpsyx's OuterProduct12 convention EXACTLY. */ - gte_mv_to_ctrl_r(r_b, gte_cr_RT13), /* $2 = r_b = a.y. RT13=a.y.low, RT22=a.y.high. */ - gte_mv_to_ctrl_r(r_c, gte_cr_RT22), /* $4 = r_c = a.z. RT22=a.z.low, RT33=a.z.high. */ + gte_mv_to_ctrl_r(r.b, gte_cr_RT13), /* $2 = r_b = a.y. RT13=a.y.low, RT22=a.y.high. */ + gte_mv_to_ctrl_r(r.c, gte_cr_RT22), /* $4 = r_c = a.z. RT22=a.z.low, RT33=a.z.high. */ /* Load uz into the RT diagonal. */ - gte_mv_to_ctrl_r(r_a, gte_cr_RT11), /* D1 = RT11 = uz.x (low 16 of $0, sign-extended by OP). */ - nop2, /* CTC2 retirement (CPU→COP2 2-slot delay) */ + gte_mv_to_ctrl_r(r.a, gte_cr_RT11), /* D1 = RT11 = uz.x (low 16 of $0, sign-extended by OP). */ + DmaSlot_ nop2, /* CTC2 retirement (CPU→COP2 2-slot delay) */ /* Load up_in into IR (the second operand for OP). */ - gte_mv_to_data_r(r_d, C2_IR1), /* IR1 = up_in.x */ + gte_mv_to_data_r(r.d, C2_IR1), /* IR1 = up_in.x */ gte_mv_to_data_r(R_AT, C2_IR2), /* IR2 = up_in.y */ - gte_mv_to_data_r(R_V0, C2_IR3), /* IR3 = up_in.z */ - nop2, /* MTC2 retirement (CPU→COP2 2-slot delay) */ + gte_mv_to_data_r(r.t0, C2_IR3), /* IR3 = up_in.z */ + DmaSlot_ nop2, /* MTC2 retirement (CPU→COP2 2-slot delay) */ + // gte_cmdw_cross, /* OP: MAC1/2/3 = uz × up_in gte_cmdw_outer_product, /* OP: MAC1/2/3 = uz × up_in * MAC1 = IR3*D2 - IR2*D3 = up_in.z*uz.y.high - up_in.y*uz.z.high * MAC2 = IR1*D3 - IR3*D1 = up_in.x*uz.z.high - up_in.z*uz.x - * MAC3 = IR2*D1 - IR1*D2 = up_in.y*uz.x - up_in.x*uz.y.high + * MAC3 = IR2*D1 - IR1*D2 = up_in.y*uz.x - up_in.x*uz.y.high * For up_in = (0, -fp_one, 0): * MAC1 = 0 - (-fp_one)*uz.z.high = fp_one*uz.z.high * MAC2 = 0 - 0 = 0 * MAC3 = (-fp_one)*uz.x - 0 = -fp_one*uz.x */ /* Restore the RT slots we clobbered. */ - gte_mv_to_ctrl_r(r_g, gte_cr_RT11), /* restore C2 r0 (RT11|RT12) */ - gte_mv_to_ctrl_r(r_h, gte_cr_RT22), /* restore C2 r4 (RT22|RT33) */ + gte_mv_to_ctrl_r(r.target0, gte_cr_RT11), /* restore C2 r0 (RT11|RT12) */ + gte_mv_to_ctrl_r(r.target1, gte_cr_RT22), /* restore C2 r4 (RT22|RT33) */ /* mfc2 MAC1/2/3 → r_a/r_b/r_c (out.x/y/z). */ - gte_mv_from_data_r(r_a, C2_MAC1), - gte_mv_from_data_r(r_b, C2_MAC2), - gte_mv_from_data_r(r_c, C2_MAC3), - nop, /* MFC2 retirement */ + mac_gte_mv_from_data_r_mac123(r.a, r.b, r.c), + DmaSlot_ nop, /* MFC2 retirement */ /* Right-shift MAC by 12 to convert from GTE's S12.20 fixed-point scale back to libpsyx OuterProduct12 convention (S12.0, fp_one=4096=1<<12). * Without this, MAC values (~16M for unit-vector cross products) overflow the GTE's 16-bit IR registers when atom 3 normalizes via mtc2. */ - shift_aright(r_a, r_a, 12), - shift_aright(r_b, r_b, 12), - shift_aright(r_c, r_c, 12), + mac_shift_aright_v3_self(r.a, r.b, r.c, 12), /* Store out.x/y/z to r_f (out ptr = scratch+32). */ - store_word(r_a, r_f, O_(V3_S4,x)), - store_word(r_b, r_f, O_(V3_S4,y)), - store_word(r_c, r_f, O_(V3_S4,z)), + mac_store_v3s4(r.a, r.b, r.c, r.f, 0), mac_yield() }) @@ -774,34 +769,34 @@ internal MipsAtom_(pad_input_cam) atom_info(atom_bind(Binds_PadInputCam) load_word(R_T1, R_Cam, O_(Camera,pos.x)), // BD-Slot. // 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(), + LdSlot_ and_i(R_T3, R_T0, Pad_Left), branch_le_zero(R_T3, atom_offset(left_x, exit_left_x)), BdSlot_ 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) /* 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, + and_i(R_T3, R_T0, Pad_Right), branch_le_zero(R_T3, atom_offset(right_x, exit_right_x)), BdSlot_ nop, add_si(R_T1, R_T1, 50), store_word(R_T1, R_Cam, O_(Camera,pos.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, + load_word(R_T1, R_Cam, O_(Camera,pos.y)), LdSlot_ + and_i(R_T3, R_T0, Pad_Up), branch_le_zero(R_T3, atom_offset(up_y, exit_up_y)), BdSlot_ nop, add_si(R_T1, R_T1, -50), store_word(R_T1, R_Cam, O_(Camera,pos.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, + and_i(R_T3, R_T0, Pad_Down), branch_le_zero(R_T3, atom_offset(down_y, exit_down_y)), BdSlot_ nop, add_si(R_T1, R_T1, 50), store_word(R_T1, R_Cam, O_(Camera,pos.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, + load_word(R_T1, R_Cam, O_(Camera,pos.z)), LdSlot_ + and_i(R_T3, R_T0, Pad_Cross), branch_le_zero(R_T3, atom_offset(cross_z, exit_cross_z)), BdSlot_ nop, add_si(R_T1, R_T1, -50), store_word(R_T1, R_Cam, O_(Camera,pos.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, + and_i(R_T3, R_T0, Pad_Circle), branch_le_zero(R_T3, atom_offset(circle_z, exit_circle_z)), BdSlot_ nop, add_si(R_T1, R_T1, 50), store_word(R_T1, R_Cam, O_(Camera,pos.z)), atom_label(exit_circle_z) diff --git a/code/hello_camera/hello_camera.c b/code/hello_camera/hello_camera.c index 6fde9de..727e93b 100644 --- a/code/hello_camera/hello_camera.c +++ b/code/hello_camera/hello_camera.c @@ -150,17 +150,19 @@ internal void resolve_look_at_init(void) { U4 pin_mask = regfile_abi_mask | (1 << R_ResolveScratch); RegFile rf = regfile(pin_mask); - U4 r_target_ptr = regfile_alloc(& rf); - U4 r_eye_ptr = regfile_alloc(& rf); - U4 r_up_in_ptr = regfile_alloc(& rf); - U4 r_tmp0 = regfile_alloc(& rf); - U4 r_tmp1 = regfile_alloc(& rf); - U4 r_tmp2 = regfile_alloc(& rf); - U4 r_tmp3 = regfile_alloc(& rf); smem.resolve_look_at_atom_addrs[0] = resolve_look_at__input_and_sub_proc(& ab, - R_ResolveScratch, - r_target_ptr, r_eye_ptr, r_up_in_ptr, - r_tmp0, r_tmp1, r_tmp2, r_tmp3); + RegUse_(resolve_look_at__input_and_sub_proc) { + .scratch = R_ResolveScratch, + .target = regfile_alloc(& rf), + .eye = regfile_alloc(& rf), + .up_in = regfile_alloc(& rf), + .t0 = regfile_alloc(& rf), + .t1 = regfile_alloc(& rf), + .t2 = regfile_alloc(& rf), + .t3 = regfile_alloc(& rf), + .t4 = regfile_alloc(& rf), + } + ); /* === ATOM 1: normalize fwd→uz === */ U2 src_offset = O_(ResolveLookAtScratch, fwd); @@ -180,16 +182,16 @@ internal void resolve_look_at_init(void) { }); /* === ATOM 2: cross uz×up_in→right === */ - U4 r_a_2 = R_T0; - U4 r_b_2 = R_T1; - U4 r_c_2 = R_T2; - U4 r_d_2 = R_T3; - U4 r_f_2 = R_T5; /* out ptr (HARDCODED in body: scratch+32) */ - U4 r_g_2 = R_T6; /* a ptr = scratch+16 */ - U4 r_h_2 = R_T7; /* b ptr = scratch+128 */ - smem.resolve_look_at_atom_addrs[2] = resolve_look_at__cross_uz_up_in_to_right_proc(& ab, - R_ResolveScratch, - r_a_2, r_b_2, r_c_2, r_d_2, r_f_2, r_g_2, r_h_2); + smem.resolve_look_at_atom_addrs[2] = resolve_look_at__cross_uz_up_into_right_proc(& ab, + RegUse_(resolve_look_at__cross_uz_up_into_right_proc) { + .scratch = R_ResolveScratch, + .a = R_T0, .b = R_T1, .c = R_T2, + .d = R_T3, + .f = R_T5, + .t1 = R_T6, + .t2 = R_T7, + .t0 = R_V0, + }); /* === ATOM 3: normalize right→ux === */ src_offset = O_(ResolveLookAtScratch, right);