mirror of
https://github.com/Ed94/pikuma_ps1.git
synced 2026-08-25 02:20:33 +00:00
Reviewing. Successfuly reworked register allocation for tape runs.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#ifdef INTELLISENSE_DIRECTIVES
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
enum {
|
||||
bios_init_pad_2 = 0x12,
|
||||
bios_start_pad_2 = 0x13,
|
||||
|
||||
+12
-12
@@ -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)
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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) */
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user