Reviewing. Successfuly reworked register allocation for tape runs.

This commit is contained in:
ed
2026-08-18 00:26:36 -04:00
parent 5a4bfb1224
commit da007d342e
19 changed files with 841 additions and 1787 deletions
+1
View File
@@ -1,6 +1,7 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
#endif
enum {
bios_init_pad_2 = 0x12,
bios_start_pad_2 = 0x13,
+12 -12
View File
@@ -283,7 +283,7 @@ WORD_COUNT(mac_gte_sqr_v3s4, 5)
, 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) \
, nop2 /* retire IR0..IR3 → GPF input pre-fill (matches libgte 0x80016134..0x80016138) */ \
, GteDelay_ nop2 /* retire IR0..IR3 → GPF input pre-fill (matches libgte 0x80016134..0x80016138) */ \
, gte_cmdw_gpf \
, gte_mv_from_data_r(r_dx, C2_MAC1) \
, gte_mv_from_data_r(r_dy, C2_MAC2) \
@@ -291,12 +291,12 @@ WORD_COUNT(mac_gte_sqr_v3s4, 5)
, shift_aright_var(r_dx, r_dx, r_shift) \
, shift_aright_var(r_dy, r_dy, r_shift) \
, shift_aright_var(r_dz, r_dz, r_shift)
WORD_COUNT(mac_gte_gpf_scale, 13)
WORD_COUNT(mac_gte_gpf_scale, 12)
#define mac_trans_mt3s3s4(r_mtx, r_off, r_t0, r_t1, r_t2) \
load_word(r_t0, r_off, O_(V3_S4,x)) \
, load_word(r_t1, r_off, O_(V3_S4,y)) \
, load_word(r_t2, r_off, O_(V3_S4,z)) \
load_word( r_t0, r_off, O_(V3_S4,x)) \
, load_word( r_t1, r_off, O_(V3_S4,y)) \
, load_word( r_t2, r_off, O_(V3_S4,z)) \
, store_word(r_t0, r_mtx, O_(MT3_S2S4,t[0])) \
, store_word(r_t1, r_mtx, O_(MT3_S2S4,t[1])) \
, store_word(r_t2, r_mtx, O_(MT3_S2S4,t[2]))
@@ -306,8 +306,8 @@ WORD_COUNT(mac_trans_mt3s3s4, 6)
#define mac_lzcr_round_even_half_shift(r_shift, r_mag_sq, r_mag_sq_copy) \
and_i(r_shift, r_shift, gte_lzcr_even_mask) \
, or_u(r_mag_sq_copy, r_mag_sq, 0) \
, li_s(r_mag_sq, 31) \
, sub_s(r_mag_sq, r_mag_sq, r_shift) \
, li_s( r_mag_sq, 31) \
, sub_s( r_mag_sq, r_mag_sq, r_shift) \
, shift_aright(r_mag_sq, r_mag_sq, 1)
WORD_COUNT(mac_lzcr_round_even_half_shift, 5)
@@ -337,7 +337,7 @@ WORD_COUNT(mac_gte_mv_from_mac123_v3s4, 3)
/* atom_dbg_skip */
#define mac_gcmd_push(cmd, reg_transfer, reg_base, port) \
mac_load_word_imm(reg_transfer, cmd) \
, store_word( reg_transfer, reg_base, port)
, store_word( reg_transfer, reg_base, port)
WORD_COUNT(mac_gcmd_push, 3)
/* atom_dbg_skip */
@@ -383,8 +383,8 @@ WORD_COUNT(mac_insert_ot_tag, 11)
/* atom_dbg_skip */
#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))
, 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 */
@@ -401,7 +401,7 @@ WORD_COUNT(mac_pad_set_status, 2)
/* atom_dbg_skip */
#define mac_pad_store_inverted_buttons(r_buttons, r_pad_state) \
nor_u( r_buttons, r_buttons, R_0) \
, store_half( r_buttons, r_pad_state, O_(PadState,buttons))
nor_u( r_buttons, r_buttons, R_0) \
, store_half(r_buttons, r_pad_state, O_(PadState,buttons))
WORD_COUNT(mac_pad_store_inverted_buttons, 2)
+1 -1
View File
@@ -11,7 +11,7 @@ ATOM_FILE_DEBUGGER_LINE_MARKER(gp_atom_c);
FI_ Slice_MipsCode ac_gcmd_push(AtomBuilder_R ab, U4 cmd, U4 reg_transfer, U4 reg_base, U2 port)
atom_dbg_skip MipsAtomComp_Proc_(ab, {
mac_load_word_imm(reg_transfer, cmd),
store_word( reg_transfer, reg_base, port),
store_word( reg_transfer, reg_base, port),
})
FI_ Slice_MipsCode ac_store_rgb8(AtomBuilder_R ab, U1 rr, U1 rg, U1 rb, U4 base, U4 offset)
+29 -23
View File
@@ -118,7 +118,7 @@ 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),
nop2, /* retire IR0..IR3 → GPF input pre-fill (matches libgte 0x80016134..0x80016138) */
GteDelay_ nop2, /* retire IR0..IR3 → GPF input pre-fill (matches libgte 0x80016134..0x80016138) */
gte_cmdw_gpf,
gte_mv_from_data_r(r_dx, C2_MAC1),
gte_mv_from_data_r(r_dy, C2_MAC2),
@@ -137,29 +137,26 @@ FI_ Slice_MipsCode ac_trans_mt3s3s4(AtomBuilder_R ab
, U4 r_mtx, U4 r_off
, U4 r_t0, U4 r_t1, U4 r_t2
) MipsAtomComp_Proc_(ab, {
load_word(r_t0, r_off, O_(V3_S4,x)),
load_word(r_t1, r_off, O_(V3_S4,y)),
load_word(r_t2, r_off, O_(V3_S4,z)),
load_word( r_t0, r_off, O_(V3_S4,x)),
load_word( r_t1, r_off, O_(V3_S4,y)),
load_word( r_t2, r_off, O_(V3_S4,z)),
store_word(r_t0, r_mtx, O_(MT3_S2S4,t[0])),
store_word(r_t1, r_mtx, O_(MT3_S2S4,t[1])),
store_word(r_t2, r_mtx, O_(MT3_S2S4,t[2])),
})
/* ─── LZCR ROUND EVEN + HALF-SHIFT ───
* Takes the raw LZCR leading-zero/ones count (from mfc2 C2_LZCR, range 1..32
* per PSX-SPX cop2r31) and the |v|² sum (in r_mag_sq from the MAC1+MAC2+MAC3
* add). Produces:
* Takes the raw LZCR leading-zero/ones count (from mfc2 C2_LZCR, range 1..32 per PSX-SPX cop2r31) and the |v|² sum (in r_mag_sq from the MAC1+MAC2+MAC3 add).
* Produces:
* r_shift ← LZCR rounded down to even (clear bit 0)
* r_mag_sq_copy ← |v|² sum (moved out of r_mag_sq before it's overwritten)
* r_mag_sq ← (31 - even_LZCR) / 2 = the final srav/GPF shift amount
*
* Rounding to even ensures (31 - LZCR) is always odd, so the >> 1 division
* is consistent — no 0.5 loss. The caller branches on LZCR < 24 to decide
* left-shift vs right-shift of r_mag_sq_copy, then saves the shift count.
* Rounding to even ensures (31 - LZCR) is always odd, so the >> 1 division is consistent — no 0.5 loss.
* The caller branches on LZCR < 24 to decide left-shift vs right-shift of r_mag_sq_copy, then saves the shift count.
*
* Note: C2_LZCR (cop2r31) is a fixed read-only C2 data register — the caller
* must read it via mfc2 from C2_LZCR; there is no register choice at the
* hardware level. Only the GPR that holds the result is caller-determined. */
* Note: C2_LZCR (cop2r31) is a fixed read-only C2 data register — the caller must read it via mfc2 from C2_LZCR;
* there is no register choice at the hardware level. Only the GPR that holds the result is caller-determined. */
FI_ Slice_MipsCode ac_lzcr_round_even_half_shift(AtomBuilder_R ab,
U4 r_shift,
U4 r_mag_sq,
@@ -167,8 +164,8 @@ FI_ Slice_MipsCode ac_lzcr_round_even_half_shift(AtomBuilder_R ab,
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),
li_s(r_mag_sq, 31),
sub_s(r_mag_sq, r_mag_sq, r_shift),
li_s( r_mag_sq, 31),
sub_s( r_mag_sq, r_mag_sq, r_shift),
shift_aright(r_mag_sq, r_mag_sq, 1),
})
@@ -206,8 +203,7 @@ FI_ Slice_MipsCode ac_gte_mv_from_mac123_v3s4(AtomBuilder_R ab, Reg_(V3_S4) v) M
/* ─── Local copy of PSYQ's sqrtbl (1/sqrt lookup table for VectorNormal). ───
* Source: PSYQ 4.7 libgte sqrtbl at 0x800185B4 in hello_camera.elf.
* objdump -s --start-address=0x800185B4 --stop-address=0x800185F4 hello_camera.elf
* → 192 entries × 16-bit signed, in 1.12 fixed-point (max value 0x1000 = 1.0).
* objdump -s --start-address=0x800185B4 --stop-address=0x800185F4 hello_camera.elf → 192 entries × 16-bit signed, in 1.12 fixed-point (max value 0x1000 = 1.0).
*
* Data is identical to the libgte original (byte-for-byte verified).
*
@@ -400,12 +396,22 @@ 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),
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),
load_word(R_T0, R_T3, 20), load_word(R_T1, R_T3, 24), load_word(R_T2, R_T3, 28),
gte_mv_to_ctrl_r(R_T0, gte_cr_TRX), gte_mv_to_ctrl_r(R_T1, gte_cr_TRY), gte_mv_to_ctrl_r(R_T2, gte_cr_TRZ),
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),
load_word(R_T0, R_T3, 20),
load_word(R_T1, R_T3, 24),
load_word(R_T2, R_T3, 28),
gte_mv_to_ctrl_r(R_T0, gte_cr_TRX),
gte_mv_to_ctrl_r(R_T1, gte_cr_TRY),
gte_mv_to_ctrl_r(R_T2, gte_cr_TRZ),
mac_yield()
};
+7 -14
View File
@@ -16,9 +16,6 @@
* gte_mv_to_data_r (gte + mv + to + data + register)
* gte_lw_v0_xy(base) (gte + lw + v0 + xy)
* load_upper_i (load-upper + immediate, unique verb)
*
* Vendor mnemonics (gte_mtc2, gte_mfc2, gte_lwc2, gte_swc2, etc.) are NOT in this header.
* They are in the opt-in `gte_vendor_sym.h` for users who prefer the textbook MIPS assembly mnemonics.
* ============================================================================ */
#ifdef INTELLISENSE_DIRECTIVES
@@ -442,9 +439,8 @@ enum { _C2_TX_SUBS_ = 0
/* MVMVA: sf=1 (>>12), mx=0 (RT matrix), v=0 (V0), cv=3 (no TR). */
#define gte_cmdw_mvmva_sf1_mx0_v0_cv3 (gte_cmd_base | enc_gte_sf(1) | enc_gte_cv(3) | enc_gte_v(0) | enc_gte_mx(0) | enc_gte_cmd(gte_cmd_mvmva))
/* RTPS with sf=1 (12-bit shift, no translation): matches the output of libgte's
* ApplyMatrixLV when the GTE pipeline expects R*pos >> 12. The shift produces
* values like (-270, 710, 1713) which match the C11 reference path. */
/* RTPS with sf=1 (12-bit shift, no translation): matches the output of libgte's ApplyMatrixLV when the GTE pipeline expects R*pos >> 12.
* The shift produces values like (-270, 710, 1713) which match the C11 reference path. */
#define gte_cmdw_rtps_sf1 (gte_cmd_base | enc_gte_sf(1) | enc_gte_cv(3) | enc_gte_cmd(gte_cmd_rtps))
/* SQR / GPF cosmetic-bits compat helpers.
@@ -477,10 +473,8 @@ enum { _C2_TX_SUBS_ = 0
* bits 24-20 = 0x19 (libgte "nonsense SDK command number" signature) */
#define gte_cmdw_gpf (gte_cmd_base | enc_gte_cmd(gte_cmd_gpf) | gte_cmdw_gpf_fake_sig)
/* Mask to round LZCR (leading-zero/ones count, range 1..32 per PSX-SPX cop2r31)
* down to even. The normalize_v3s4 half-shift logic computes (31 - LZCR) >> 1;
* clearing bit 0 ensures the subtraction result is always odd,
* so the >> 1 division is consistent (no 0.5 loss). */
/* Mask to round LZCR (leading-zero/ones count, range 1..32 per PSX-SPX cop2r31) down to even.
* The normalize_v3s4 half-shift logic computes (31 - LZCR) >> 1; clearing bit 0 ensures the subtraction result is always odd, so the >> 1 division is consistent (no 0.5 loss). */
enum {
gte_lzcr_even_mask = 0xFFFE, /* all bits except bit 0 */
};
@@ -587,8 +581,8 @@ enum {
/* 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.
* 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.
* register V3_S2* p0 rgcc(R_T4) = verts[0].ptr; // → __asm__("$12")
* register V3_S2* p1 rgcc(R_T5) = verts[1].ptr; // → __asm__("$13")
* register V3_S2* p2 rgcc(R_T6) = verts[2].ptr; // → __asm__("$14")
@@ -682,8 +676,7 @@ enum {
* Loads the 3x3 rotation matrix at `r0` into the GTE's rotation-matrix control registers (RT11..RT22, indices 0..4) via ctc2.
*
* Memory layout at r0: five contiguous 32-bit words (offsets 0..16), each holding two packed 16-bit matrix elements.
* The first 1.5 rows of a standard PSX SDK MATRIX struct (where each row is laid out as
* [RT_xx, RT_xy] | [RT_xz, pad] | ...).
* The first 1.5 rows of a standard PSX SDK MATRIX struct (where each row is laid out as [RT_xx, RT_xy] | [RT_xz, pad] | ...).
*
* Generated MIPS (mirrors the source macro):
* lw $12, 0( %0 ) ; word 0
+127 -89
View File
@@ -66,40 +66,53 @@
* */
/* Register Allocation Info */
enum {
R_AtomJmp = R_T8 atom_reg, /* debug-visible; tape yield handshake scratch */
R_TapePtr = R_T9 atom_reg, /* The Instruction Stream Pointer */
R_ScratchBase = R_SP atom_reg, /* Scratchpad base address (host frame top) */
R_AtomJmp = R_FP atom_reg, /* Next atom target (yield handshake scratch) */
R_TapePtr = R_RA atom_reg, /* The Instruction Stream Pointer */
/* Stringification codes for the GCC inline assembler clobber lists. */
#define R_AtomJmp_Code R_T8_Code
#define R_TapePtr_Code R_T9_Code
#define R_ScratchBase_Code R_SP_Code
#define R_AtomJmp_Code R_FP_Code
#define R_TapePtr_Code R_RA_Code
// R_InCursor = R_T4,
// #define R_InCursor_Code R_T4_Code
// Reserved Registers (Callee-saved):
// - R_T9: Holds the Tape Ptr which we need to increment
// - R_RA: Return address register
// Needed by ac_yield but can be used as atom scratch:
// - R_T8: Will be used as the atom jump register.
// Reserved Registers (Callee-saved across the host ABI transition):
// - R_SP: Holds the scratchpad base while tape code executes.
// - R_FP: Holds the next atom target.
// - R_RA: Holds the tape cursor.
// All atom-body allocations must stay out of these.
// Atom bodies may freely use R2-R25.
// All allocatable registers for mips atoms:
// All allocatable registers for atom bodies (R2-R25, 24 registers):
// TODO(Ed): Make this the R_AtomJmp register since its better to clobber across atoms.
R_TScratchVolatile = R_AT, // This one is reserved for psuedo instructions, but you can technically use it.
R_PsuedoVolatile = R_AT, // Assembler temporary; never allocate.
R_TScratch0 = R_T0,
R_TScratch1 = R_T1,
R_TScratch2 = R_T2,
R_TScratch3 = R_T3,
R_TScratch4 = R_T4,
R_TScratch5 = R_T5,
R_TScratch6 = R_T6,
R_TScratch7 = R_T7,
R_TScratch8 = R_T8, // Clobbered by the yield on a per-atom boundary.
R_TScratch10 = R_V0, // Tend to be used with gte DMAs
R_TScratch11 = R_V1, // Tend to be used with gte DMAs
// Note(Ed): We can technically clobber these, but don't unless we hit a bottleneck.
// A 0-2
// S 0-7
// Atom Allocation Pool
R_Atom0 = R_T0,
R_Atom1 = R_T1,
R_Atom2 = R_T2,
R_Atom3 = R_T3,
R_Atom4 = R_T4,
R_Atom5 = R_T5,
R_Atom6 = R_T6,
R_Atom7 = R_T7,
R_Atom8 = R_T8,
R_Atom9 = R_T9,
R_Atom10 = R_V0, // Tend to be used with gte DMAs
R_Atom11 = R_V1, // Tend to be used with gte DMAs
R_Atom12 = R_A0,
R_Atom13 = R_A1,
R_Atom14 = R_A2,
R_Atom15 = R_A3,
R_Atom16 = R_S0,
R_Atom17 = R_S1,
R_Atom18 = R_S2,
R_Atom19 = R_S3,
R_Atom20 = R_S4,
R_Atom21 = R_S5,
R_Atom22 = R_S6,
R_Atom23 = R_S7,
};
typedef U2 Reg; // Register parameter used with atom or atom component procedures
@@ -109,7 +122,8 @@ typedef U4 const MipsCode; // Underlying type to mips asm words.
typedef Slice_(MipsCode);
typedef U4 const MipsAtom; // Underlying type to a mips atom defnition
typedef Slice_(MipsAtom);
typedef Slice_(MipsAtom);
// Sometimes a user will define a bundle of atoms that represent a procedure of work as:
// MipsAtom* <identifier>[...];
// Unfortuantely if using slice_from_array it will make the slice's pointer: MipsAtom** so this enforce its defined as MipsAtom*
@@ -153,53 +167,71 @@ typedef Slice_(MipsAtom);
Files containing only atoms and atom components.
Place `ATOM_FILE_LINE_MARKER();` once at file scope in any `.atom.c` that defines atoms.
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). */
The constant is in `.rodata` so the linker may eliminate it. */
#define ATOM_FILE_DEBUGGER_LINE_MARKER(file_name) internal U4 const tmpl(atom_file_debugger_line_marker,file_name) = 0
typedef Slice_MipsAtom Tape;
/* The 'Exit' Atom */
atom_dbg_skip MipsAtom_(tape_exit) { jump_reg(R_RA), nop };
typedef Struct_(TapeHostFrame) {
U4 s0;
U4 s1;
U4 s2;
U4 s3;
U4 s4;
U4 s5;
U4 s6;
U4 s7;
U4 fp;
U4 sp;
U4 ra;
};
// TODO(Ed): When we have a substantial workload/throughput, profile each of these to see impact at ABI boundaries.
enum {
TapeHostFrame_Loc = Scratchpad_End - S_(TapeHostFrame),
TapeScratch_Len = TapeHostFrame_Loc - Scratchpad_Loc,
};
static_assert(S_(TapeHostFrame) == 11 * S_(U4));
static_assert(TapeHostFrame_Loc == 0x1F8003D4);
/* Tape Runner (Default) */
FI_ void tape_run(Tape tape) { register U4* tape_ptr rgcc(R_TapePtr) = u4_r(tape.ptr); asm volatile(
asm_words(
load_word( R_AtomJmp, R_TapePtr, 0) /* Bootstrap the first jump */
, add_ui_self(R_TapePtr, S_(MipsAtom)) /* Advance tape */
, call_reg( R_AtomJmp) /* jalr $t8 */
, BdSlot_ nop /* Branch delay slot */
)
asm_rpins, r_use(tape_ptr)
asm_clobber:
rlit(R_AT),
rlit(R_V0), rlit(R_V1), // We clobber these for GTE ACs (that don't expose register selection, might expose them in the future...)
rlit(R_T0), rlit(R_T1), rlit(R_T2), rlit(R_T3), rlit(R_T4),
rlit(R_T5), rlit(R_T6), rlit(R_T7), rlit(R_T8),
clb_mem_drain
); }
atom_dbg_skip MipsAtom_(tape_enter) {
mac_load_word_imm(R_V0, u4_(TapeHostFrame_Loc)),
store_word(R_S0, R_V0, O_(TapeHostFrame,s0)),
store_word(R_S1, R_V0, O_(TapeHostFrame,s1)),
store_word(R_S2, R_V0, O_(TapeHostFrame,s2)),
store_word(R_S3, R_V0, O_(TapeHostFrame,s3)),
store_word(R_S4, R_V0, O_(TapeHostFrame,s4)),
store_word(R_S5, R_V0, O_(TapeHostFrame,s5)),
store_word(R_S6, R_V0, O_(TapeHostFrame,s6)),
store_word(R_S7, R_V0, O_(TapeHostFrame,s7)),
store_word(R_FP, R_V0, O_(TapeHostFrame,fp)),
store_word(R_SP, R_V0, O_(TapeHostFrame,sp)),
store_word(R_RA, R_V0, O_(TapeHostFrame,ra)),
add_ui(R_TapePtr, R_A0, 0),
load_upper_i(R_ScratchBase, u4_hi(Scratchpad_Loc)),
load_word(R_AtomJmp, R_TapePtr, 0),
add_ui_self( R_TapePtr, S_(MipsAtom)),
jump_reg(R_AtomJmp), BdSlot_ nop,
};
/* Tape Runner (Static and Arg Clobbers) */
FI_ void tape_run_a02_s07(Tape tape) { register U4* tape_ptr rgcc(R_TapePtr) = u4_r(tape.ptr); asm volatile(
asm_words(
load_word( R_AtomJmp, R_TapePtr, 0) /* Bootstrap the first jump */
, add_ui_self(R_TapePtr, S_(MipsAtom)) /* Advance tape */
, call_reg( R_AtomJmp) /* jalr $t8 */
, BdSlot_ nop /* Branch delay slot */
)
asm_rpins, r_use(tape_ptr)
asm_clobber:
rlit(R_AT),
rlit(R_V0), rlit(R_V1), rlit(R_A0), rlit(R_A1), rlit(R_A2),
rlit(R_T0), rlit(R_T1), rlit(R_T2), rlit(R_T3), rlit(R_T4),
rlit(R_T5), rlit(R_T6), rlit(R_T7), rlit(R_T8),
rlit(R_S0), rlit(R_S1), rlit(R_S2), rlit(R_S3), rlit(R_S4),
rlit(R_S5), rlit(R_S6), rlit(R_S7),
clb_mem_drain
); }
atom_dbg_skip MipsAtom_(tape_exit) {
mac_load_word_imm(R_V0, u4_(TapeHostFrame_Loc)),
load_word(R_S0, R_V0, O_(TapeHostFrame,s0)),
load_word(R_S1, R_V0, O_(TapeHostFrame,s1)),
load_word(R_S2, R_V0, O_(TapeHostFrame,s2)),
load_word(R_S3, R_V0, O_(TapeHostFrame,s3)),
load_word(R_S4, R_V0, O_(TapeHostFrame,s4)),
load_word(R_S5, R_V0, O_(TapeHostFrame,s5)),
load_word(R_S6, R_V0, O_(TapeHostFrame,s6)),
load_word(R_S7, R_V0, O_(TapeHostFrame,s7)),
load_word(R_RA, R_V0, O_(TapeHostFrame,ra)),
load_word(R_FP, R_V0, O_(TapeHostFrame,fp)),
load_word(R_SP, R_V0, O_(TapeHostFrame,sp)),
jump_reg(R_RA), BdSlot_ nop,
};
typedef void Proc_(TapeEntryFn)(MipsAtom* tape_ptr);
FI_ void tape_run(Tape tape) { C_(TapeEntryFn*, tape_enter)(tape.ptr); }
// Procedural authoring of tapes:
typedef Relative_(FArena) Struct_(TapeBuilder) { U4 ptr; U4 capacity; U4 used; };
@@ -242,18 +274,13 @@ atom_dbg_skip MipsAtomComp_(ac_yield_load) {
atom_dbg_skip MipsAtomComp_(ac_yield_tail) {
add_ui_self(R_TapePtr, S_(MipsCode)),
jump_reg( R_AtomJmp),
BdSlot_ nop,
jump_reg( R_AtomJmp), BdSlot_ nop,
};
#pragma endregion Macro Atom Components
#pragma region Atom Builder
// This helps with runtime procedural authoring of mips atoms.
typedef Struct_(FMipsAtom512) { U4 data[512]; U4 used; };
// FArena Related
typedef Relative_(FArena) Struct_(AtomBuilder) { U4 start; U4 capacity; U4 used; };
// Usual way to resolve an atom after the bulder is done.
@@ -274,7 +301,6 @@ FI_ void tb_emit_atombuilder(TapeBuilder_R tb, AtomBuilder_R ab) { tb_emit(tb, a
#pragma region Atom Arena
// Just a dedicated FArena that is meant to mem_copy and return atom definitions made with MipsAtom_Proc_
typedef Relative_(FArena) Struct_(AtomArena) { U4 start; U4 capacity; U4 used; };
#define atomarena_unused_start(ab) ((ab).start + (ab).used)
@@ -299,13 +325,24 @@ FI_ void atomarena_reset(AtomArena_R aa) { aa->used = 0; }
// 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 <Scope_Prefix> or something per register allocation boundary.
/* ABI + tape reserves that are never handed out by alloc. */
/* ABI reserves that are never handed out by alloc.
* R_AT is the assembler temporary (per the MIPS O32 ABI).
* R_K0/K1 are kernel reserves.
* R_GP stays the host global pointer.
* R_SP/R_FP/R_RA are tape runtime carriers between tape_enter and tape_exit. */
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 */
(1u << R_0) | (1u << R_AT) |
(1u << R_K0) | (1u << R_K1) |
(1u << R_GP) | (1u << R_SP) |
(1u << R_FP) | (1u << R_RA);
internal Reg const regfile_alloc_order[] = {
R_V0, R_V1,
R_A0, R_A1, R_A2, R_A3,
R_T0, R_T1, R_T2, R_T3, R_T4, R_T5, R_T6, R_T7,
R_S0, R_S1, R_S2, R_S3, R_S4, R_S5, R_S6, R_S7,
R_T8, R_T9,
};
typedef Struct_(RegFile) {
A2_U2 GPR;
@@ -336,17 +373,20 @@ 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;
result = r_id;
}
return result;
}
/* regfile_alloc picks the next free GPR from regfile_alloc_order.
* The table is the first-fit allocation order: T0..T7, V0..V1, A0..A3,
* S0..S7, T8..T9. The 24 entries leave room for the tape program to use
* any of them while R0, R1, R26-R31 remain reserved. */
I_ Reg regfile_alloc(RegFile_R rf) {
U2 allocated = 0;
for index_iter(Reg, r_id, R_T0, <=, R_T7) {
allocated = regfile__alloc_helper(rf->GPR, r_id); Jmp_nZero_(allocated,resolved);
Reg allocated = 0;
for index_iter(U4, idx, 0, <, Array_len(regfile_alloc_order)) {
allocated = regfile__alloc_helper(rf->GPR, idx);
Jmp_nZero_(allocated,resolved);
}
allocated = regfile__alloc_helper(rf->GPR, R_V0); Jmp_nZero_(allocated,resolved);
allocated = regfile__alloc_helper(rf->GPR, R_V1);
assert(allocated != 0);
resolved: return allocated;
}
@@ -383,9 +423,7 @@ FI_ void regfile_reset_to_mask(RegFile_R rf, U4 mask) {
#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.
*/
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) {
+4 -2
View File
@@ -134,10 +134,12 @@ FI_ U4 farena_unused_start(FArena arena) { return arena.start + arena.used; }
#pragma region BIOS Scratchpad
/* BIOS scratchpad location. 1 KB at 0x1F800000.
* The PS1 BIOS A-functions use this region for inter-call communication and as a temp storage area.
* The Tape runtime uses scratch region along with explicit data structures instead of the stack-based scratch from the C-Runtime. */
* TapeHostFrame occupies the final 44 bytes while tape code executes.
* Atom scratch is bounded by the TapeHostFrame_Loc declaration in lottes_tape.h. */
enum {
Scratchpad_Loc = 0x1F800000,
Scratchpad_Len = 0x400, /* 1 KB */
Scratchpad_End = Scratchpad_Loc + Scratchpad_Len, /* 0x1F800400 */
};
#define C_scratch(type) C_(type, Scratchpad_Loc)
#pragma endregion BIOS Scratchpad
+9 -10
View File
@@ -13,9 +13,8 @@ ATOM_FILE_DEBUGGER_LINE_MARKER(pad_atom_c);
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)),
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, Reg state, Reg r_id, U1 id_value) atom_dbg_skip MipsAtomComp_Proc_(ab, {
@@ -32,8 +31,8 @@ FI_ Slice_MipsCode ac_pad_set_status(AtomBuilder_R ab, U4 r_tmp, U1 r_state, U4
* r_buttons must already be loaded (the caller is responsible for filling the load-delay slot of
* the preceding load_half_u with an instruction that doesn't read r_buttons). */
FI_ Slice_MipsCode ac_pad_store_inverted_buttons(AtomBuilder_R ab, U1 r_buttons, U1 r_pad_state) atom_dbg_skip MipsAtomComp_Proc_(ab, {
nor_u( r_buttons, r_buttons, R_0),
store_half( r_buttons, r_pad_state, O_(PadState,buttons)),
nor_u( r_buttons, r_buttons, R_0),
store_half(r_buttons, r_pad_state, O_(PadState,buttons)),
})
#pragma endregion MACs (Mips Atom Components)
@@ -91,8 +90,8 @@ atom_label(snap_root) /* === Case 1: Disconnected (status == 0xFF). */
* If branch NOT taken (fall through to pending/id_dispatch), R_T4 is overwritten by the next case body's add_ui — harmless. */
atom_label(disconnected) /* === Disconnected body. */
mac_pad_set_status(R_T4, R_PadState, PadStatus_Disconnected),
store_half( R_0, R_PadState, O_(PadState,buttons)),
mac_pad_set_status(R_T4, R_PadState, PadStatus_Disconnected),
store_half( R_0, R_PadState, O_(PadState,buttons)),
mac_pad_set_centered_axes(R_PadState, R_T4),
mac_pad_set_id_byte(R_PadState, R_RawId, PadRawStatus_Timeout),
jump_rel(atom_offset(disconnected, snap_end)),
@@ -108,8 +107,8 @@ atom_label(skip_disconnected)
* If branch NOT taken (fall through to id_dispatch), R_T4 is overwritten by the digital/analog body add_ui - harmless. */
atom_label(pending) /* === Pending body (status=0, id=0 — pre-IRQ-empty buffer). */
mac_pad_set_status(R_T4, R_PadState, PadStatus_Pending),
store_half( R_0, R_PadState, O_(PadState,buttons)),
mac_pad_set_status(R_T4, R_PadState, PadStatus_Pending),
store_half( R_0, R_PadState, O_(PadState,buttons)),
mac_pad_set_centered_axes(R_PadState, R_T4),
store_byte(R_RawId, R_PadState, O_(PadState,id)),
jump_rel(atom_offset(pending, snap_end)),
@@ -125,7 +124,7 @@ 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; */
mac_load_word_imm(R_T5, PadAxis_Centered), /* 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) */
+8 -8
View File
@@ -101,14 +101,14 @@ MipsAtomComp_Proc_(ab, {
#pragma region resolve_look_at
/* ─── resolve_look_at bundle chain atoms ──────────────────────────── */
typedef AtomBundle_(resolve_look_at) { MipsAtom*
input_and_sub,
normalize_fwd_uz,
cross_to_right,
normalize_right_ux,
cross_to_up,
normalize_up_uy,
pop_mv_trans;
typedef AtomBundle_(resolve_look_at) { MipsAtom
*input_and_sub,
*normalize_fwd_uz,
*cross_to_right,
*normalize_right_ux,
*cross_to_up,
*normalize_up_uy,
*pop_mv_trans;
};
enum {
+64 -116
View File
@@ -53,7 +53,6 @@
#pragma endregion Hello Joypad TUs
enum {
Scratchpad_Len = 1024,
MemTape_Len = 512,
ResolveLookAtArena_Words = 1024,
@@ -86,11 +85,7 @@ typedef Struct_(SMemory) {
U1 ct_init_atom_mem[CT_InitAtomMem_Size];
MipsAtom* normalize_v3s4;
MipsAtom* gte_cross_v3s4; /* GTE OP OuterProduct12 (a × b → out).
* Baked once at init from compile_gte_cross_v3s4() into ct_init_atom_mem.
* One instance serves both cross call sites in resolve_look_at;
* per-frame tape emits push src_a/src_b/out (3 pointers = 12 bytes)
* for Binds_gte_cross_v3s4. */
MipsAtom* gte_cross_v3s4;
U1 resolve_look_at_mem[ResolveLookAtArena_Size];
MipsAtom* resolve_look_at_bundle[AtomBundle_Len(resolve_look_at)];
@@ -138,70 +133,52 @@ 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); }
internal void compile_init_atoms(void) {
/* Single shared arena: each compile_* function pushes its atom into the same
* ct_init_atom_mem backing. If each function called atomarena_make() locally,
* the second call would reset arena.used to 0 and overwrite the first atom.
*/
AtomArena ab = atomarena_make(slice_ut_arr(smem.ct_init_atom_mem));
RegFile rf = regfile(regfile_abi_mask);
#define ralloc() regfile_alloc(& rf)
#define ralloc_v3() { ralloc(), ralloc(), ralloc() }
/* === gte_cross_v3s4 — GTE OP OuterProduct12 (a × b → out) ===
* Reused by both cross call sites in resolve_look_at. No scratch carrier.
* GPR pool: 9 allocatable (no carrier). Pool R_T0..R_T7, R_V0, R_V1 minus ABI = 9.
* Fits exactly. */
{
smem.gte_cross_v3s4 = gte_cross_v3s4(& ab,
RegUse_(gte_cross_v3s4) {
.a = { regfile_alloc(& rf), regfile_alloc(& rf), regfile_alloc(& rf) },
.b = { regfile_alloc(& rf), regfile_alloc(& rf), regfile_alloc(& rf) },
.x = regfile_alloc(& rf), /* out / t0 shared */
.y = regfile_alloc(& rf), /* src_a / t1 / rt11 shared */
.z = regfile_alloc(& rf), /* src_b / t2 / rt22 shared */
});
regfile_reset(& rf);
}
smem.gte_cross_v3s4 = gte_cross_v3s4(& ab,
RegUse_(gte_cross_v3s4) {
.a = ralloc_v3(),
.b = ralloc_v3(),
.x = ralloc(),
.y = ralloc(),
.z = ralloc(),
});
regfile_reset(& rf);
/* === build_normalize_v3s4 — Generic 4-stage GTE normalize ===
* Reused by all 3 normalize call sites in resolve_look_at. Reads scratch + src/dst
* offsets from tape (no carrier — atom is fully self-contained per call).
* GPR pool: no carrier, 10 allocatable (R_T0..R_T7, R_V0, R_V1 minus ABI pins).
* 10 fields (scratch + src_ptr + dst_ptr + recip_est + norm + shift + src_x +
* t3 + t4 + t5) → just fits. Union members t5.src_offset, t3.dst_offset, etc.
* share GPRs via lifetime discipline. */
{
RegFile rf = regfile(regfile_abi_mask);
smem.normalize_v3s4 = build_normalize_v3s4(& ab,
RegUse_(build_normalize_v3s4) {
.scratch = regfile_alloc(& rf),
.src_ptr = regfile_alloc(& rf),
.dst_ptr = regfile_alloc(& rf),
.recip_est = regfile_alloc(& rf),
.norm = regfile_alloc(& rf),
.shift = regfile_alloc(& rf),
.src_x = regfile_alloc(& rf),
.t3 = regfile_alloc(& rf),
.t4 = regfile_alloc(& rf),
.t5 = regfile_alloc(& rf),
});
}
smem.normalize_v3s4 = build_normalize_v3s4(& ab,
RegUse_(build_normalize_v3s4) {
.scratch = ralloc(),
.src_ptr = ralloc(),
.dst_ptr = ralloc(),
.recip_est = ralloc(),
.norm = ralloc(),
.shift = ralloc(),
.src_x = ralloc(),
.t3 = ralloc(),
.t4 = ralloc(),
.t5 = ralloc(),
});
regfile_reset(& rf);
assert(ab.used <= CT_InitAtomMem_Size);
#undef ralloc
#undef ralloc_v3
}
internal void compile_resolve_look_at(void) {
/* Wrap the static arena in a MipsAtomBuilder. */
AtomArena ab = atomarena_make(slice_ut_arr(smem.resolve_look_at_mem));
TapeBuilder tb = tb_make(slice_ut_arr(smem.resolve_look_at_bundle));
AtomArena ab = atomarena_make(slice_ut_arr(smem.resolve_look_at_mem));
AtomBundle_resolve_look_at_R bundle = C_(void*, smem.resolve_look_at_bundle);
U4 pin_mask = regfile_abi_mask | (1 << R_ResolveScratch);
RegFile rf = regfile(pin_mask);
#define ralloc() regfile_alloc(& rf)
#define ralloc_v3() { ralloc(), ralloc(), ralloc() }
tb_emit_(AtomBundleEntry_(resolve_look_at, input_and_sub)(& ab,
bundle->input_and_sub = AtomBundleEntry_(resolve_look_at, input_and_sub)(& ab,
RegUse_(resolve_look_at_input_and_sub) {
.scratch = R_ResolveScratch,
.target = ralloc(),
@@ -212,37 +189,25 @@ internal void compile_resolve_look_at(void) {
.t2 = ralloc(),
.t3 = ralloc(),
.t4 = ralloc(),
}
));
});
regfile_reset_to_mask(& rf, pin_mask);
/* === ATOM 1: normalize fwd→uz === */
smem.resolve_look_at_bundle[1] = smem.normalize_v3s4;
bundle->normalize_fwd_uz = smem.normalize_v3s4;
bundle->cross_to_right = smem.gte_cross_v3s4;
bundle->normalize_right_ux = smem.normalize_v3s4;
bundle->cross_to_up = smem.gte_cross_v3s4;
bundle->normalize_up_uy = smem.normalize_v3s4;
/* === ATOM 2: cross uz×up_in→right (reuses smem.gte_cross_v3s4) === */
smem.resolve_look_at_bundle[2] = smem.gte_cross_v3s4;
/* === ATOM 3: normalize right→ux === */
smem.resolve_look_at_bundle[3] = smem.normalize_v3s4;
/* === ATOM 4: cross uz×ux→up (reuses smem.gte_cross_v3s4) === */
smem.resolve_look_at_bundle[4] = smem.gte_cross_v3s4;
/* === ATOM 5: normalize up→uy === */
smem.resolve_look_at_bundle[5] = smem.normalize_v3s4;
/* === ATOM 6 (FUSED): populate + ctc2 RT + MVMVA + trans_matrix → look_at === */
smem.resolve_look_at_bundle[6] = resolve_look_at__pop_mv_trans(& ab,
RegUse_(resolve_look_at__pop_mv_trans){
.scratch = R_ResolveScratch,
.look_at = ralloc(), /* T0 */
.eye = ralloc(), /* T1 — allocated BEFORE row so it doesn't alias row.y */
.row = ralloc_v3(), /* T2 T3 T5 */
.t6 = ralloc(), /* T6 = ux (populate) / v_x (matrix_vector) */
.t7 = ralloc(), /* T7 = uy (populate) / v_y (matrix_vector) */
.t8 = ralloc(), /* V0 = uz (populate) / v_z (matrix_vector) */
});
/* No regfile_reset between phases: the fused atom uses all 9 GPRs throughout. */
bundle->pop_mv_trans = resolve_look_at__pop_mv_trans(& ab,
RegUse_(resolve_look_at__pop_mv_trans){
.scratch = R_ResolveScratch,
.look_at = ralloc(),
.eye = ralloc(),
.row = ralloc_v3(),
.t6 = ralloc(),
.t7 = ralloc(),
.t8 = ralloc(),
});
/* Sanity check: arena didn't overflow. */
assert(ab.used <= ResolveLookAtArena_Size);
@@ -258,42 +223,34 @@ I_ void resolve_look_at(TapeBuilder_R tb
){
/* Typed view of the scratchpad for field-address arithmetic. */
ResolveLookAtScratch* sp = C_scratch(ResolveLookAtScratch*);
AtomBundle_resolve_look_at_R bundle = C_(void*, smem.resolve_look_at_bundle);
tb_emit(tb, smem.resolve_look_at_bundle[0]); {
tb_emit(tb, bundle->input_and_sub); {
tb_data(tb, u4_(target));
tb_data(tb, u4_(eye));
tb_data(tb, u4_(up_in));
tb_data(tb, u4_(smem.scratchpad));
}
tb_emit(tb, smem.resolve_look_at_bundle[1]); {
/* Binds_NormalizeV3S4: src_offset (low 16) | dst_offset (high 16). Both U2s
* packed into a single U4 because the atom body reads at byte offsets 0 and 2
* from R_TapePtr. Scratch base is baked into the atom (load_word_imm of
* Scratchpad_Loc). */
tb_emit(tb, bundle->normalize_fwd_uz); {
tb_data(tb, u4_(O_(ResolveLookAtScratch, fwd) | (O_(ResolveLookAtScratch, uz) << 16)));
}
tb_emit(tb, smem.resolve_look_at_bundle[2]); {
/* Binds_gte_cross_v3s4: src_a, src_b, out (3 pointers). */
tb_data(tb, u4_(& sp->uz)); /* src_a */
tb_data(tb, u4_(& sp->up_in)); /* src_b */
tb_data(tb, u4_(& sp->right)); /* out */
tb_emit(tb, bundle->cross_to_right); {
tb_data(tb, u4_(& sp->uz));
tb_data(tb, u4_(& sp->up_in));
tb_data(tb, u4_(& sp->right));
}
tb_emit(tb, smem.resolve_look_at_bundle[3]); {
tb_emit(tb, bundle->normalize_right_ux); {
tb_data(tb, u4_(O_(ResolveLookAtScratch, right) | (O_(ResolveLookAtScratch, ux) << 16)));
}
tb_emit(tb, smem.resolve_look_at_bundle[4]); {
/* Binds_gte_cross_v3s4: src_a, src_b, out (3 pointers). */
tb_data(tb, u4_(& sp->uz)); /* src_a */
tb_data(tb, u4_(& sp->ux)); /* src_b */
tb_data(tb, u4_(& sp->up)); /* out */
tb_emit(tb, bundle->cross_to_up); {
tb_data(tb, u4_(& sp->uz));
tb_data(tb, u4_(& sp->ux));
tb_data(tb, u4_(& sp->up));
}
tb_emit(tb, smem.resolve_look_at_bundle[5]); {
tb_emit(tb, bundle->normalize_up_uy); {
tb_data(tb, u4_(O_(ResolveLookAtScratch, up) | (O_(ResolveLookAtScratch, uy) << 16)));
}
/* === FUSED atom: populate + matrix_vector + trans_matrix (replaces 4 separate emits) === */
tb_emit(tb, smem.resolve_look_at_bundle[6]); {
tb_emit(tb, bundle->pop_mv_trans); {
tb_data(tb, u4_(look_at));
}
}
@@ -333,19 +290,13 @@ void update(PrimitiveArena* pa, U4* ordering_buf)
gknown V3_S4_R acc = & smem.cube.accel;
add_v3s4(vel, acc[0]);
add_v3s4_fp(pos, vel[0]);
// vel->x += acc->x;
// vel->y += acc->y;
// vel->z += acc->z;
// pos->x += vel->x;
// pos->y += vel->y;
// pos->z += vel->z;
if (pos->y + 150 > smem.floor.pos.y) vel->y *= -1;
// Prep
S4 nclip = 0;
S4 orderingtbl_z = 0;
A2_S2 p; //???
A2_S2 p; //???
S4 flag; //????
B4 use_c11_path = false;
@@ -371,9 +322,6 @@ void update(PrimitiveArena* pa, U4* ordering_buf)
gte_matrix_set_rotation (& smem.tform_view);
gte_matrix_set_translation(& smem.tform_view);
// gte_matrix_set_rotation (& smem.tform_world);
// gte_matrix_set_translation(& smem.tform_world);
U4 prim_base = u4_(pa->buf[smem.active_buf_id]);
U4 prim_cursor = prim_base + pa->used;
@@ -393,7 +341,7 @@ void update(PrimitiveArena* pa, U4* ordering_buf)
tb_data(& tb, u4_(& pa->used));
tb_data(& tb, prim_base);
}
tape_run_a02_s07(tb_slice(tb));// Fire off the tape (bigger-clobber variant).
tape_run(tb_slice(tb));// Fire off the tape (bigger-clobber variant).
// smem.cube.rot.y += 30;
}
@@ -435,7 +383,7 @@ void update(PrimitiveArena* pa, U4* ordering_buf)
tb_data(& tb, u4_(& pa->used));
tb_data(& tb, prim_base);
}
tape_run_a02_s07(tb_slice(tb));// Fire off the tape (bigger-clobber variant).
tape_run(tb_slice(tb));// Fire off the tape (bigger-clobber variant).
// C-side state (pa->used) has already been updated by the tape!
// smem.floor.rot.y += 5;