Files
pikuma_ps1/code/duffle/lottes_tape.h
T

389 lines
16 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
# include "dsl.h"
# include "gcc_asm.h"
# include "mips.h"
# include "gte.h"
# include "memory.h"
#endif
/* ---------------------------------------------------------------------------
* TAPE DRIVE ABI & REGISTER ALIASES
* ---------------------------------------------------------------------------
* We map the MIPS temporary registers to a persistent global workspace.
* The C compiler is completely unaware of these bindings.
* ---------------------------------------------------------------------------*/
enum {
R_TapePtr = R_T8, /* The Instruction Stream Pointer */
R_PrimCur = R_T7, /* VRAM output cursor (primitive buffer) */
R_FaceCur = R_T4, /* Input data cursor (indices/faces) */
R_InCursor = R_T4, /* Input data cursor (indices/faces) */
R_VertBase = R_T5, /* Base address of the vertex array */
R_OtBase = R_T6, /* Base address of the Ordering Table */
/* Stringification codes for the GCC inline assembler clobber lists */
#define R_TapePtr_Code R_T8_Code
#define R_PrimCur_Code R_T7_Code
#define R_FaceCur_Code R_T4_Code
#define R_InCursor_Code R_T4_Code
#define R_VertBase_Code R_T5_Code
#define R_OtBase_Code R_T6_Code
};
/* The 'Yield' sequence for Tape Atoms.
* Loads the next pointer from the tape, advances the tape, and jumps.
* Cost: ~ 4 cycles */
#define mips_yield() \
load_word(R_T9, R_TapePtr, 0) \
, add_ui_1( R_TapePtr, 4) \
, jump_reg( R_T9) \
, nop
/* The 'Exit' Atom */
MipsAtom_(tape_exit) { jump_reg(rret_addr), nop };
typedef Slice_(U4);
/* Generalized Tape Engine Runner */
FI_ void tape_run(Slice_U4 tape) { register U4* tp rgcc(R_TapePtr) = tape.ptr; asm volatile(
asm_words(
add_ui( R_SP, R_SP, -8) /* Allocate stack space */
, store_word(R_RA, R_SP, 0) /* Safely backup $ra to the stack */
, load_word( R_T9, R_TapePtr, 0) /* Bootstrap the first jump */
, add_ui_1( R_TapePtr, 4) /* Advance tape */
, jump_nreg(R_T9) /* jalr $t9 */
, nop /* Branch delay slot */
, load_word(R_RA, R_SP, 0) /* Restore $ra from stack */
, add_ui_1( R_SP, 8) /* Deallocate stack space */
)
asm_rpins, r_use(tp)
asm_clobber:
rlit(R_AT)
, rlit(R_V0), rlit(R_V1)
, rlit(R_T0), rlit(R_T1), rlit(R_T2), rlit(R_T3)
/* Tell GCC the tape engine owns and destroys the workspace registers */
, rlit(R_PrimCur), rlit(R_FaceCur), rlit(R_VertBase), rlit(R_OtBase)
, rlit(R_T9)
, clb_mem_drain
); }
typedef Struct_(TapeBuilder) { U4 ptr; U4 count; };
FI_ void tb_init(TapeBuilder* tb, FArena* arena) { tb->ptr = arena->start; tb->count = 0; }
FI_ TapeBuilder tb_make( FArena* arena) { return (TapeBuilder){ arena->start, 0 }; }
FI_ void tb_emit(TapeBuilder* tb, MipsCode* atom) { u4_r(tb->ptr)[tb->count] = u4_(atom); ++ tb->count; }
FI_ void tb_data(TapeBuilder* tb, U4 data) { u4_r(tb->ptr)[tb->count] = u4_(data); ++ tb->count; }
FI_ Slice_U4 tb_end (TapeBuilder* tb) { tb_emit(tb,code_tape_exit); return (Slice_U4){ C_(U4*,tb->ptr), tb->count }; }
FI_ Slice_U4 tb_slice(TapeBuilder tb) { return (Slice_U4){ C_(U4*,tb.ptr), tb.count }; }
#define tb_scope(tb) for(U4 tbs_once=0;tbs_once==0;++tbs_once,tb_emit(tb,code_tape_exit))
/* ---------------------------------------------------------------------------
* MACRO ATOM Components (Reusable Assembly Components)
* These do NOT yield. They are expanded inline inside Tape Atoms.
* ---------------------------------------------------------------------------*/
/* Loads 3 16-bit indices from the face array */
#define mac_load_tri_indices(rId_0, rId_1, rId_2) \
load_half_u(rId_0, R_FaceCur, 0) \
, load_half_u(rId_1, R_FaceCur, 2) \
, load_half_u(rId_2, R_FaceCur, 4)
/* Translates indices to vertex addresses and pushes them to GTE */
#define mac_load_tri_verts(rId_0, rId_1, rId_2) \
shift_ll(R_AT, rId_0, 3), add_u(R_AT, R_AT, R_VertBase), load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4), gte_mt(R_V0, C2_VXY0), gte_mt(R_V1, C2_VZ0) \
, shift_ll(R_AT, rId_1, 3), add_u(R_AT, R_AT, R_VertBase), load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4), gte_mt(R_V0, C2_VXY1), gte_mt(R_V1, C2_VZ1) \
, shift_ll(R_AT, rId_2, 3), add_u(R_AT, R_AT, R_VertBase), load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4), gte_mt(R_V0, C2_VXY2), gte_mt(R_V1, C2_VZ2)
/* Formats the primitive memory layout (Tag + Color + Coordinates) */
#define mac_format_prim_f3(color_hi, color_lo) \
store_word(R_0, R_PrimCur, 0) \
, load_ui(R_AT, color_hi), or_i(R_AT, R_AT, color_lo) \
, store_word(R_AT, R_PrimCur, 4) \
, gte_sw(C2_SXY0, R_PrimCur, 8) \
, gte_sw(C2_SXY1, R_PrimCur, 12) \
, gte_sw(C2_SXY2, R_PrimCur, 16)
/* Correctly inserts a primitive into the Ordering Table linked list */
#define mac_insert_ot_tag(r_otz, prim_length) \
shift_ll( R_T1, r_otz, 2) \
, add_u( R_T1, R_T1, R_OtBase) /* T1 = &OrderingTable[OTZ] */ \
, load_word( R_AT, R_T1, 0) /* AT = old_ot_head */ \
, load_ui( R_V0, prim_length) /* V0 = Length << 24 */ \
, shift_ll( R_AT, R_AT, 8) /* Strip upper 8 bits from old_ot */ \
, shift_lr( R_AT, R_AT, 8) \
, or_u( R_AT, R_AT, R_V0) /* Merge length */ \
, store_word(R_AT, R_PrimCur, 0) /* prim->tag = old_ot_head */ \
, shift_ll( R_AT, R_PrimCur, 8) /* AT = PrimCur & 0x00FFFFFF */ \
, shift_lr( R_AT, R_AT, 8) \
, store_word(R_AT, R_T1, 0) /* OrderingTable[OTZ] = PrimCur */
internal MipsAtom_(bind_workspace) {
/* Pop 4 arguments from the tape directly into the workspace registers */
load_word(R_PrimCur, R_TapePtr, 0),
load_word(R_FaceCur, R_TapePtr, 4),
load_word(R_VertBase, R_TapePtr, 8),
load_word(R_OtBase, R_TapePtr, 12),
add_ui_1( R_TapePtr, 16),
mips_yield()
};
internal MipsAtom_(sync_prim_cursor) {
/* Pop the C-struct address and base address from the tape */
load_word(R_AT, R_TapePtr, 0), /* AT = &pa->used */
load_word(R_T0, R_TapePtr, 4), /* T0 = prim_base */
add_ui_1( R_TapePtr, 8),
/* Calculate byte offset and store directly back to RAM */
sub_u(R_T0, R_PrimCur, R_T0),
store_word(R_T0, R_AT, 0),
mips_yield()
};
internal MipsAtom_(set_gte_world) {
/* Pop matrix address from tape into R_T3 ($11) */
load_word(R_T3, R_TapePtr, 0),
add_ui_1( R_TapePtr, 4),
/* 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_ct( R_T0, gte_cr_RT11), gte_ct( 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_ct( R_T0, gte_cr_RT13), gte_ct( R_T1, gte_cr_RT21), gte_ct( 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_ct( R_T0, gte_cr_TRX), gte_ct( R_T1, gte_cr_TRY), gte_ct( R_T2, gte_cr_TRZ),
mips_yield()
};
/* ============================================================================
* cube_tri — Draw one cube face (Gouraud-shaded quad) via the GTE tape pipeline
* ============================================================================
*
* Reads 4 indices from R_FaceCur (V4_S2 = 8 bytes), loads 4 vertices into
* the GTE, runs the PsyQ RotAverageNclip4 sequence, and renders a Poly_G4.
*
* PsyQ RotAverageNclip4 sequence:
* 1. Load V0=p0, V1=p1, V2=p2
* 2. RTPT → SXY0=p0, SXY1=p1, SXY2=p2, SZ1,SZ2,SZ3
* 3. NCLIP → MAC0 = cross(p0,p1,p2) ← BEFORE RTPS!
* 4. Store SXY0 (p0) to primitive buffer
* 5. Load V3 into V0
* 6. RTPS → SXY0=p3, SZ0
* 7. Store SXY0 (p3) to primitive buffer
* 8. AVSZ3 → OTZ from SZ1,SZ2,SZ3
*
* PRIMITIVE FORMAT (Poly_G4 = 9 words = 36 bytes)
* ------------------------------------------------
* Word 0 (offset 0): OT tag (set by mac_insert_ot_tag)
* Word 1 (offset 4): c0 + code = 0x38FF00FF (magenta, opcode 0x38)
* Word 2 (offset 8): p0 = SXY0 (stored BEFORE RTPS)
* Word 3 (offset 12): c1 + pad = 0x0000FFFF (yellow)
* Word 4 (offset 16): p1 = SXY1
* Word 5 (offset 20): c2 + pad = 0x00FFFF00 (cyan)
* Word 6 (offset 24): p2 = SXY2
* Word 7 (offset 28): c3 + pad = 0x0000FF00 (green)
* Word 8 (offset 32): p3 = SXY0 (stored AFTER RTPS)
*
* BRANCH OFFSETS
* ----------------------------------------------
* Outer branch (backface cull): branch_le_zero(R_T0, 49)
* → Skip 49 instructions from BD slot, land at add_ui(R_FaceCur,...)
* Inner branch (OTZ bounds): branch_equal(R_AT, R_0, 13)
* → Skip 13 instructions from BD slot, land at add_ui(R_FaceCur,...)
* ============================================================================ */
internal MipsAtom_(cube_tri) {
/* ── 1. Load 4 face indices from R_FaceCur ──────────────────────────── */
load_half_u(R_T0, R_FaceCur, 0), /* T0 = face->x (vertex 0 index) */
load_half_u(R_T1, R_FaceCur, 2), /* T1 = face->y (vertex 1 index) */
load_half_u(R_T2, R_FaceCur, 4), /* T2 = face->z (vertex 2 index) */
load_half_u(R_T3, R_FaceCur, 6), /* T3 = face->w (vertex 3 index) */
/* ── 2. Load V0, V1, V2 into GTE ────────────────────────────────────── */
/* V0 = verts[face->x] */
shift_ll(R_AT, R_T0, 3), add_u(R_AT, R_AT, R_VertBase),
load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4),
gte_mt(R_V0, C2_VXY0), gte_mt(R_V1, C2_VZ0),
/* V1 = verts[face->y] */
shift_ll(R_AT, R_T1, 3), add_u(R_AT, R_AT, R_VertBase),
load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4),
gte_mt(R_V0, C2_VXY1), gte_mt(R_V1, C2_VZ1),
/* V2 = verts[face->z] */
shift_ll(R_AT, R_T2, 3), add_u(R_AT, R_AT, R_VertBase),
load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4),
gte_mt(R_V0, C2_VXY2), gte_mt(R_V1, C2_VZ2),
/* ── 3. RTPT — transforms V0/V1/V2 → SXY0/SXY1/SXY2 + SZ1/SZ2/SZ3 ─── */
nop, nop, gte_cmdw_rtpt,
/* ── 4. NCLIP — backface culling on SXY0/SXY1/SXY2 (p0,p1,p2) ──────── */
/* MUST be done BEFORE RTPS overwrites SXY0 with p3! */
nop, nop, gte_cmdw_nclip,
nop, nop,
/* ── 5. Cull check: skip format/insert if MAC0 ≤ 0 (backface) ───────── */
gte_mf(R_T0, C2_MAC0),
nop,
branch_le_zero(R_T0, 49), /* Skip 49 if MAC0 ≤ 0 (backface) → cull */
nop, /* BD slot */
/* ── 6. Store p0,p1,p2 to primitive buffer (BEFORE RTPS overwrites) ─── */
store_word(R_0, R_PrimCur, 0),
/* Word 1: c0 (BGR) + code = 0x38FF00FF (magenta, opcode 0x38) */
load_ui(R_AT, 0x38FF), or_i(R_AT, R_AT, 0x00FF),
store_word(R_AT, R_PrimCur, 4),
/* Word 2: p0 = SXY0 (stored BEFORE RTPS overwrites it) */
gte_sw(C2_SXY0, R_PrimCur, 8),
/* Word 3: c1 (BGR) + pad = 0x0000FFFF (yellow) */
load_ui(R_AT, 0x0000), or_i(R_AT, R_AT, 0xFFFF),
store_word(R_AT, R_PrimCur, 12),
/* Word 4: p1 = SXY1 */
gte_sw(C2_SXY1, R_PrimCur, 16),
/* Word 5: c2 (BGR) + pad = 0x00FFFF00 (cyan) */
load_ui(R_AT, 0x00FF), or_i(R_AT, R_AT, 0xFF00),
store_word(R_AT, R_PrimCur, 20),
/* Word 6: p2 = SXY2 */
gte_sw(C2_SXY2, R_PrimCur, 24),
/* Word 7: c3 (BGR) + pad = 0x0000FF00 (green) */
load_ui(R_AT, 0x0000), or_i(R_AT, R_AT, 0xFF00),
store_word(R_AT, R_PrimCur, 28),
/* ── 7. Load V3 = verts[face->w] into V0 ─────────────────────────────── */
shift_ll(R_AT, R_T3, 3), add_u(R_AT, R_AT, R_VertBase),
load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4),
gte_mt(R_V0, C2_VXY0), gte_mt(R_V1, C2_VZ0),
/* ── 8. RTPS — transforms V0 (now V3) → SXY0 (p3) + SZ0 ─────────────── */
nop, nop, gte_cmdw_rtps,
/* Word 8: p3 = SXY0 (written AFTER RTPS with V3's screen coords) */
gte_sw(C2_SXY0, R_PrimCur, 32),
/* ── 9. AVSZ4 — average Z from SZ0/SZ1/SZ2/SZ3 ────────────── */
nop, nop, gte_cmdw_avsz4,
nop, nop,
gte_mf(R_T1, C2_OTZ),
/* ── 10. Bounds check OTZ < 2048 ─────────────────────────────────────── */
add_ui( R_AT, R_0, 2048),
slt_u( R_AT, R_T1, R_AT),
branch_equal(R_AT, R_0, 13), /* Skip 13 → land at add_ui(R_FaceCur,...) */
nop, /* BD slot */
/* ── 11. Insert into Ordering Table (length = 8 for Poly_G4) ─────────── */
mac_insert_ot_tag(R_T1, 0x0800), /* 0x0800 = 8 << 8 = length 8 in tag */
/* ── 12. Advance cursors & yield ─────────────────────────────────────── */
add_ui(R_PrimCur, R_PrimCur, 36), /* 9 words × 4 bytes */
add_ui(R_FaceCur, R_FaceCur, 8), /* 4 × S2 = 8 bytes */
mips_yield()
};
internal MipsAtom_(floor_tri) {
mac_load_tri_indices(R_T0, R_T1, R_T2),
mac_load_tri_verts( R_T0, R_T1, R_T2),
/* 3. Execute Math */
nop, nop, gte_cmdw_rtpt,
nop, nop, gte_cmdw_nclip,
nop, nop,
/* 4. Culling (Branch forward 29 instructions if Backface) */
gte_mf(R_T0, C2_MAC0),
nop,
branch_le_zero(R_T0, 29),
nop,
/* 5. Format Primitive */
mac_format_prim_f3(0x20FF, 0xFFFF), /* High: 0x20/B, Low: G/R */
/* 6. Calculate Depth */
nop, nop, gte_cmdw_avsz3,
nop, nop,
gte_mf(R_T1, C2_OTZ),
/* 7. Bounds Check OTZ < 2048 (Branch forward 13 instructions to skip insertion) */
add_ui( R_AT, R_0, 2048),
slt_u( R_AT, R_T1, R_AT),
branch_equal(R_AT, R_0, 13),
nop,
/* 8. Insert into Ordering Table Linked List */
mac_insert_ot_tag(R_T1, 0x0400), /* Length = 4 words */
add_ui(R_PrimCur, R_PrimCur, 20), /* Advance Prim Cursor (5 words) */
/* 9. Advance Input Cursor & Yield (Both branch targets land here) */
add_ui(R_FaceCur, R_FaceCur, 8), /* Advance Face Cursor (4 * S2 = 8 bytes) */
mips_yield()
};
/* DIAGNOSTIC 1: Pure tape loop test */
internal MipsAtom_(diag_yield) { mips_yield() };
/* DIAGNOSTIC 2: Pure memory test (No GTE). Draws a fixed cyan triangle. */
internal MipsAtom_(diag_color) {
store_word(R_0, R_T7, 0),
load_ui( R_AT, 0x20FF), /* High: MipsCode 0x20 + Color B:FF */
or_i( R_AT, R_AT, 0xFF00), /* Low: Color G:FF, R:00 (Cyan) */
store_word(R_AT, R_T7, 4),
/* Fake coordinates - Swapped winding order to prevent GPU culling! */
load_ui(R_AT, 0x0010), or_i(R_AT, R_AT, 0x0010), store_word(R_AT, R_T7, 8), /* (16, 16) */
load_ui(R_AT, 0x0050), or_i(R_AT, R_AT, 0x0010), store_word(R_AT, R_T7, 12), /* (80, 16) */
load_ui(R_AT, 0x0010), or_i(R_AT, R_AT, 0x0050), store_word(R_AT, R_T7, 16), /* (16, 80) */
add_ui( R_T1, R_0, 10),
shift_ll(R_T1, R_T1, 2),
add_u( R_T1, R_T1, R_T6),
load_word( R_AT, R_T1, 0),
load_ui( R_V0, 0x0400), // <--- Fills load delay slot!
store_word(R_AT, R_T7, 0),
shift_ll( R_AT, R_T7, 8), shift_lr(R_AT, R_AT, 8),
or_u( R_AT, R_AT, R_V0),
store_word(R_AT, R_T1, 0),
add_ui(R_T7, R_T7, 20),
mips_yield()
};
/* DIAGNOSTIC 3: Pure GTE test (No Memory Writes) */
internal MipsAtom_(diag_gte) {
/* Load 3 indices */
load_half_u(R_T0, R_T4, 0),
load_half_u(R_T1, R_T4, 2),
load_half_u(R_T2, R_T4, 4),
/* Load Vertices into GTE */
shift_ll( R_AT, R_T0, 3), add_u( R_AT, R_AT, R_T5),
load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4),
gte_mt( R_V0, C2_VXY0), gte_mt( R_V1, C2_VZ0),
shift_ll( R_AT, R_T1, 3), add_u( R_AT, R_AT, R_T5),
load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4),
gte_mt( R_V0, C2_VXY1), gte_mt( R_V1, C2_VZ1),
shift_ll( R_AT, R_T2, 3), add_u( R_AT, R_AT, R_T5),
load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4),
gte_mt( R_V0, C2_VXY2), gte_mt( R_V1, C2_VZ2),
/* Run Math */
nop, nop, gte_cmdw_rtpt,
nop, nop, gte_cmdw_nclip,
nop, nop,
/* Advance Face Cursor and Yield */
add_ui(R_T4, R_T4, 8),
mips_yield()
};