mirror of
https://github.com/Ed94/pikuma_ps1.git
synced 2026-08-07 08:08:49 +00:00
Compare commits
4
Commits
68fd4ca791
...
54968d20e0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
54968d20e0 | ||
|
|
13a0b9dca4 | ||
|
|
b1982ba862 | ||
|
|
288f92ff5b |
+5
-4
@@ -87,7 +87,7 @@
|
||||
#define PtrSet_(type) TypeR_(type); typedef TypeV_(type)
|
||||
#define TSet_(type) type; typedef PtrSet_(type)
|
||||
|
||||
#define array_len(a) (U8)(sizeof(a) / sizeof(typeof((a)[0])))
|
||||
#define array_len(a) (U4)(sizeof(a) / sizeof(typeof((a)[0])))
|
||||
#define array_decl(type, ...) (type[]){__VA_ARGS__}
|
||||
#define Array_sym(type,len) A ## len ## _ ## type
|
||||
#define Array_expand(type,len) type Array_sym(type, len)[len]; typedef PtrSet_(Array_sym(type, len))
|
||||
@@ -95,6 +95,7 @@
|
||||
#define Bit_(id,b) id = (1 << b), tmpl(id,pos) = b
|
||||
#define Enum_(underlying_type, symbol) underlying_type TSet_(symbol); enum symbol
|
||||
#define Proc_(symbol) symbol
|
||||
#define Relative_(symbol) // Does nothing but annotate that a symbol is associated with another.
|
||||
#define Struct_(symbol) struct symbol TSet_(symbol); struct symbol
|
||||
#define Union_(symbol) union symbol TSet_(symbol); union symbol
|
||||
|
||||
@@ -140,7 +141,9 @@ typedef void Proc_(VoidFn) (void);
|
||||
#define tera(n) (C_(U4, n) << 40)
|
||||
#define null C_(U4, 0)
|
||||
#define nullptr C_(void*, 0)
|
||||
#define O_(type,member) C_(U4,__builtin_offsetof(type,member))
|
||||
#define O_(type, field) (C_(U4, & C_(type*,0)->field))
|
||||
|
||||
#define OT_(field) O_(typeof_ptr(& field), filed))
|
||||
#define S_(data) C_(U4, sizeof(data))
|
||||
|
||||
#define sop_1(op,a,b) C_(U1, s1_(a) op s1_(b))
|
||||
@@ -171,8 +174,6 @@ def_signed_ops(le, <=)
|
||||
#define le_s(a,b) def_generic_sop(le, a,b)
|
||||
#undef def_generic_sop
|
||||
|
||||
#define o_(field) offset_of(typeof_ptr(& field), filed))
|
||||
|
||||
#define alignas _Alignas
|
||||
#define alignof _Alignof
|
||||
#define byte_pad(amount, ...) B1 glue(_PAD_, __VA_ARGS__) [amount]
|
||||
|
||||
@@ -66,14 +66,15 @@
|
||||
* register V3_S2* p0 rgcc(R_T4) = ...; // bundled
|
||||
*
|
||||
* asm volatile("nop" : : : reg_str(R_RA), "memory"); // clobber list */
|
||||
#define rlit_impl(n) "$" #n
|
||||
#define rlit(n) rlit_impl(n)
|
||||
#define rlit_stringfy(n) "$" stringify(n)
|
||||
#define rlit_tmpl(n) rlit_stringfy(tmpl(n,Code))
|
||||
#define rlit(n) rlit_tmpl(n)
|
||||
|
||||
/* ------------------------------------------------------------------------ *
|
||||
* rgcc(n) — GCC-specific bundle for register-variable declarations.
|
||||
*
|
||||
* Produces `__asm__(reg_str(tmpl(n, MipsAtom)))` at expansion time.
|
||||
* The `tmpl(n, MipsAtom)` indirection derives the preprocessor-visible `_Code`
|
||||
* Produces `__asm__(reg_str(tmpl(n, MipsCode)))` at expansion time.
|
||||
* The `tmpl(n, MipsCode)` indirection derives the preprocessor-visible `_Code`
|
||||
* form from the enum name (which the preprocessor can't expand on its own).
|
||||
* So a call is: register V3_S2* p rgcc(R_T4) = verts[0].ptr;
|
||||
* expands (via tmpl) to: register V3_S2* p __asm__(rlit(R_T4_Code)) = verts[0].ptr;
|
||||
@@ -91,8 +92,7 @@
|
||||
*
|
||||
* For clobber lists and asm-template strings, use the bare `rlit(R_T4_Code)`.
|
||||
* ------------------------------------------------------------------------ */
|
||||
#define rgcc_(n) __asm__(rlit(tmpl(n, Code)))
|
||||
#define rgcc(n) rgcc_(n)
|
||||
#define rgcc(n) __asm__(rlit(n))
|
||||
|
||||
/* rgcc_ref(n) — GCC operand-reference form "%N". Not currently used
|
||||
* by the placeholder-pun macros (the .word bodies are fully baked
|
||||
|
||||
+17
-13
@@ -132,7 +132,7 @@ enum {
|
||||
gte_in_v1_z = C2_VZ1, /* Input Vector 1 (Z) */
|
||||
gte_in_v2_xy = C2_VXY2, /* Input Vector 2 (X, Y) */
|
||||
gte_in_v2_z = C2_VZ2, /* Input Vector 2 (Z) */
|
||||
gte_in_rgb = C2_RGB, /* Input Color (R, G, B, MipsAtom) */
|
||||
gte_in_rgb = C2_RGB, /* Input Color (R, G, B, MipsCode) */
|
||||
gte_out_scr_xy0 = C2_SXY0, /* Output Screen Coord 0 (X, Y) */
|
||||
gte_out_scr_xy1 = C2_SXY1, /* Output Screen Coord 1 (X, Y) */
|
||||
gte_out_scr_xy2 = C2_SXY2, /* Output Screen Coord 2 (X, Y) */
|
||||
@@ -385,6 +385,10 @@ enum { _C2_OPS_ = 0
|
||||
|
||||
#define gte_cmdw_avsz3 (gte_cmd_base | enc_gte_cmd(gte_cmd_avsz3) | gte_cmdw_psyq_avsz3_compat)
|
||||
|
||||
/* AVSZ4 — average Z of 4 vertices (for quads) */
|
||||
#define gte_cmd_avsz4 0x2E
|
||||
#define gte_cmdw_avsz4 (gte_cmd_base | enc_gte_cmd(gte_cmd_avsz4) | gte_cmdw_psyq_avsz3_compat)
|
||||
|
||||
/**
|
||||
* @brief Loads a single SVECTOR to GTE vector register V0
|
||||
*
|
||||
@@ -460,22 +464,22 @@ enum {
|
||||
*
|
||||
* The `asm_clobber(...)` helper from gcc_asm.h prepends the colon that
|
||||
* starts the clobbers section. */
|
||||
#define gte_load_v0(r_ptr, base) asm volatile( \
|
||||
#define gte_load_v0(r_ptr, base) asm volatile( \
|
||||
asm_words( gte_lw_v0(base), gte_lw_v0z(base) ) \
|
||||
asm_rpins, r_use(r_ptr) \
|
||||
asm_clobber: rlit(R_V0_Code), rlit(R_T0_Code), rlit(R_T1_Code), rlit(R_RA_Code), clb_mem_drain \
|
||||
asm_rpins, r_use(r_ptr) \
|
||||
asm_clobber: rlit(R_V0), rlit(R_T0), rlit(R_T1), rlit(R_RA), clb_mem_drain \
|
||||
)
|
||||
|
||||
#define gte_load_v1(r_ptr, base) asm volatile( \
|
||||
#define gte_load_v1(r_ptr, base) asm volatile( \
|
||||
asm_words( gte_lw_v1(base), gte_lw_v1z(base) ) \
|
||||
asm_rpins, r_use(r_ptr) \
|
||||
asm_clobber: rlit(R_V0_Code), rlit(R_T0_Code), rlit(R_T1_Code), rlit(R_RA_Code), clb_mem_drain \
|
||||
asm_rpins, r_use(r_ptr) \
|
||||
asm_clobber: rlit(R_V0), rlit(R_T0), rlit(R_T1), rlit(R_RA), clb_mem_drain \
|
||||
)
|
||||
|
||||
#define gte_load_v2(r_ptr, base) asm volatile( \
|
||||
asm_words( gte_lw_v2(base), gte_lw_v2z(base) ) \
|
||||
asm_rpins, r_use(r_ptr) \
|
||||
asm_clobber: rlit(R_V0_Code), rlit(R_T0_Code), rlit(R_T1_Code), rlit(R_RA_Code), clb_mem_drain \
|
||||
#define gte_load_v2(r_ptr, base) asm volatile( \
|
||||
asm_words( gte_lw_v2(base), gte_lw_v2z(base) ) \
|
||||
asm_rpins, r_use(r_ptr) \
|
||||
asm_clobber: rlit(R_V0), rlit(R_T0), rlit(R_T1), rlit(R_RA), clb_mem_drain \
|
||||
)
|
||||
|
||||
/* gte_load_v0v1v2(p0, p1, p2, b0, b1, b2) — the canonical prelude to gte_cmd_rtpt.
|
||||
@@ -497,7 +501,7 @@ enum {
|
||||
gte_lw_v2(b2), gte_lw_v2z(b2) ) \
|
||||
asm_rpins \
|
||||
, r_use(p0), r_use(p1), r_use(p2) \
|
||||
asm_clobber: rlit(R_V0_Code), rlit(R_T0_Code), rlit(R_T1_Code), rlit(R_RA_Code), clb_mem_drain \
|
||||
asm_clobber: rlit(R_V0), rlit(R_T0), rlit(R_T1), rlit(R_RA), clb_mem_drain \
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -650,5 +654,5 @@ enum {
|
||||
, gte_mt( R_T4, 4) \
|
||||
) \
|
||||
, r_use(r0) \
|
||||
asm_clobber: clb_system, rlit(R_T4_Code), rlit(R_T5_Code), rlit(R_T6_Code) \
|
||||
asm_clobber: clb_system, rlit(R_T4), rlit(R_T5), rlit(R_T6) \
|
||||
)
|
||||
|
||||
+245
-47
@@ -7,6 +7,7 @@
|
||||
# include "memory.h"
|
||||
#endif
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
* TAPE DRIVE ABI & REGISTER ALIASES
|
||||
* ---------------------------------------------------------------------------
|
||||
@@ -17,30 +18,21 @@ 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(
|
||||
@@ -55,42 +47,53 @@ FI_ void tape_run(Slice_U4 tape) { register U4* tp rgcc(R_TapePtr) = tape.ptr; a
|
||||
)
|
||||
asm_rpins, r_use(tp)
|
||||
asm_clobber:
|
||||
rlit(R_AT_Code)
|
||||
, rlit(R_V0_Code), rlit(R_V1_Code)
|
||||
, rlit(R_T0_Code), rlit(R_T1_Code), rlit(R_T2_Code), rlit(R_T3_Code)
|
||||
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_Code), rlit(R_FaceCur_Code), rlit(R_VertBase_Code), rlit(R_OtBase_Code)
|
||||
, rlit(R_T9_Code)
|
||||
, 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; }
|
||||
typedef Relative_(FArena) Struct_(TapeBuilder) { U4 ptr; U4 capacity; U4 used; };
|
||||
FI_ void tb_init(TapeBuilder* tb, FArena* arena) { tb->ptr = arena->start; tb->used = 0; }
|
||||
FI_ TapeBuilder tb_make( FArena* arena) { return (TapeBuilder){ arena->start, 0 }; }
|
||||
|
||||
FI_ void tb_emit(TapeBuilder* tb, MipsAtom* 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; }
|
||||
#define tb_emit_(tb, atom) tb_emit(tb, tmpl(code,atom))
|
||||
FI_ void tb_emit(TapeBuilder* tb, MipsCode* atom) { u4_r(tb->ptr)[tb->used] = u4_(atom); ++ tb->used; }
|
||||
FI_ void tb_data(TapeBuilder* tb, U4 data) { u4_r(tb->ptr)[tb->used] = u4_(data); ++ tb->used; }
|
||||
|
||||
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 }; }
|
||||
FI_ Slice_U4 tb_end (TapeBuilder* tb) { tb_emit(tb,code_tape_exit); return (Slice_U4){ C_(U4*,tb->ptr), tb->used }; }
|
||||
FI_ Slice_U4 tb_slice(TapeBuilder tb) { return (Slice_U4){ C_(U4*,tb.ptr), tb.used }; }
|
||||
#define tb_scope(tb) for(U4 tbs_once=0;tbs_once==0;++tbs_once,tb_emit(tb,code_tape_exit))
|
||||
|
||||
#pragma region Macro Mips Atom Components
|
||||
/* ---------------------------------------------------------------------------
|
||||
* MACRO ATOMS (Reusable Assembly Components)
|
||||
* MACRO ATOM Components (Reusable Assembly Components)
|
||||
* These do NOT yield. They are expanded inline inside Tape Atoms.
|
||||
* ---------------------------------------------------------------------------*/
|
||||
|
||||
/* The 'Yield' sequence for Tape Atoms.
|
||||
* Loads the next pointer from the tape, advances the tape, and jumps.
|
||||
* Cost: ~ 4 cycles */
|
||||
#define mac_yield() \
|
||||
load_word(R_T9, R_TapePtr, 0) \
|
||||
, add_ui_1( R_TapePtr, 4) \
|
||||
, jump_reg( R_T9) \
|
||||
, nop
|
||||
|
||||
/* Loads 3 16-bit indices from the face array */
|
||||
#define mac_load_tri_indices(r_idx0, r_idx1, r_idx2) \
|
||||
load_half_u(r_idx0, R_FaceCur, 0) \
|
||||
, load_half_u(r_idx1, R_FaceCur, 2) \
|
||||
, load_half_u(r_idx2, R_FaceCur, 4)
|
||||
#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(r_idx0, r_idx1, r_idx2) \
|
||||
shift_ll(R_AT, r_idx0, 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, r_idx1, 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, r_idx2, 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)
|
||||
#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) \
|
||||
@@ -115,15 +118,39 @@ FI_ Slice_U4 tb_slice(TapeBuilder tb) { return (Sli
|
||||
, 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()
|
||||
};
|
||||
#pragma endregion Macro Atom Components
|
||||
|
||||
#pragma region Mips Atom Builder
|
||||
// This allows for runtime procedural authoring of mips atoms.
|
||||
|
||||
typedef Struct_(FMipsAtom512) { U4 data[512]; U4 used; };
|
||||
|
||||
typedef Slice_(MipsCode); typedef Slice_MipsCode MipsAtom;
|
||||
// FArena Related
|
||||
typedef Relative_(FArena) Struct_(MipsAtomBuilder) { U4 start; U4 capacity; U4 used; };
|
||||
// Whatever the builder is writting to should most likely coresspond
|
||||
// to something that can fit within instruction cache?
|
||||
|
||||
FI_ void atombuilder_unroll(MipsAtomBuilder_R ab, Slice_MipsCode_R code) {
|
||||
assert(ab->capacity - ab->used - code->len);
|
||||
mem_copy(ab->start, u4_(code->ptr), code->len);
|
||||
mem_bump(ab->start, ab->capacity, & ab->used, code->len);
|
||||
}
|
||||
#define atombuilder_unroll_mac(ab, mac)
|
||||
|
||||
// When done authoring, utilize this to cap-off the atom
|
||||
FI_ void atombuilder_end(MipsAtomBuilder_R ab) {
|
||||
LP_ MipsAtom_(yield) { mac_yield() };
|
||||
mem_copy(ab->start, u4_(code_yield), S_(code_yield));
|
||||
mem_bump(ab->start, ab->capacity, & ab->used, S_(code_yield));
|
||||
}
|
||||
|
||||
#define mipsatom_from_builder(ab) (MipsAtom){ab.start, ab.used}
|
||||
|
||||
#pragma endregion Mips Atom Builder
|
||||
|
||||
#pragma region Baked Mips Atoms
|
||||
// These atoms are resolved at compile time and are (usually) statically linked readonly data.
|
||||
|
||||
internal MipsAtom_(sync_prim_cursor) {
|
||||
/* Pop the C-struct address and base address from the tape */
|
||||
@@ -133,7 +160,7 @@ internal MipsAtom_(sync_prim_cursor) {
|
||||
/* 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()
|
||||
mac_yield()
|
||||
};
|
||||
|
||||
internal MipsAtom_(set_gte_world) {
|
||||
@@ -149,7 +176,176 @@ internal MipsAtom_(set_gte_world) {
|
||||
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()
|
||||
mac_yield()
|
||||
};
|
||||
|
||||
|
||||
// TODO(Ed): I'm not sure yet if the bindings are redundant with the floortri atom yet.
|
||||
|
||||
typedef Struct_(Binds_CubeTri) {
|
||||
U4 PrimCursor;
|
||||
U4 FaceCursor;
|
||||
U4 VertBase;
|
||||
U4 OtBase;
|
||||
};
|
||||
internal MipsAtom_(rbind_cube_tri) {
|
||||
/* Pop 4 arguments from the tape directly into the workspace registers */
|
||||
load_word(R_PrimCur, R_TapePtr, O_(Binds_CubeTri,PrimCursor)),
|
||||
load_word(R_FaceCur, R_TapePtr, O_(Binds_CubeTri,FaceCursor)),
|
||||
load_word(R_VertBase, R_TapePtr, O_(Binds_CubeTri,VertBase)),
|
||||
load_word(R_OtBase, R_TapePtr, O_(Binds_CubeTri,OtBase)),
|
||||
add_ui_1( R_TapePtr, S_(Binds_CubeTri)),
|
||||
mac_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 */
|
||||
mac_yield()
|
||||
};
|
||||
|
||||
|
||||
typedef Struct_(Binds_FloorTri) {
|
||||
U4 PrimCursor;
|
||||
U4 FaceCursor;
|
||||
U4 VertBase;
|
||||
U4 OtBase;
|
||||
};
|
||||
|
||||
internal MipsAtom_(rbind_floor_tri) {
|
||||
/* Pop 4 arguments from the tape directly into the workspace registers */
|
||||
load_word(R_PrimCur, R_TapePtr, O_(Binds_FloorTri,PrimCursor)),
|
||||
load_word(R_FaceCur, R_TapePtr, O_(Binds_FloorTri,FaceCursor)),
|
||||
load_word(R_VertBase, R_TapePtr, O_(Binds_FloorTri,VertBase)),
|
||||
load_word(R_OtBase, R_TapePtr, O_(Binds_FloorTri,OtBase)),
|
||||
add_ui_1( R_TapePtr, S_(Binds_FloorTri)),
|
||||
mac_yield()
|
||||
};
|
||||
|
||||
internal MipsAtom_(floor_tri) {
|
||||
@@ -187,16 +383,16 @@ internal MipsAtom_(floor_tri) {
|
||||
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()
|
||||
mac_yield()
|
||||
};
|
||||
|
||||
/* DIAGNOSTIC 1: Pure tape loop test */
|
||||
internal MipsAtom_(diag_yield) { mips_yield() };
|
||||
internal MipsAtom_(diag_yield) { mac_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: MipsAtom 0x20 + Color B:FF */
|
||||
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),
|
||||
|
||||
@@ -219,7 +415,7 @@ internal MipsAtom_(diag_color) {
|
||||
|
||||
add_ui(R_T7, R_T7, 20),
|
||||
|
||||
mips_yield()
|
||||
mac_yield()
|
||||
};
|
||||
|
||||
/* DIAGNOSTIC 3: Pure GTE test (No Memory Writes) */
|
||||
@@ -250,5 +446,7 @@ internal MipsAtom_(diag_gte) {
|
||||
/* Advance Face Cursor and Yield */
|
||||
add_ui(R_T4, R_T4, 8),
|
||||
|
||||
mips_yield()
|
||||
mac_yield()
|
||||
};
|
||||
|
||||
#pragma endregion Baked Mips Atoms
|
||||
|
||||
@@ -88,6 +88,8 @@ FI_ void slice_copy_(Slice dest, Slice src) {
|
||||
slice_copy_(slice_to_ut(dest), slice_to_ut(src)); \
|
||||
} while(0)
|
||||
|
||||
typedef Slice_(U4);
|
||||
|
||||
#pragma endregion Slice
|
||||
|
||||
#pragma region FArena
|
||||
|
||||
+11
-11
@@ -377,14 +377,14 @@ enum { _BitOffsets = 0
|
||||
asm volatile( \
|
||||
asm_words(load_ui((rt), u4_hi(imm), \
|
||||
add_si((rt), (rt), (S2)C_(U2,u4_lo(imm))) \
|
||||
asm_clobber: rlit(R_AT_Code), clb_mem_drain \
|
||||
asm_clobber: rlit(R_AT), clb_mem_drain \
|
||||
); \
|
||||
} \
|
||||
else { \
|
||||
asm volatile(asm_words( \
|
||||
load_ui((rt), u4_hi(imm)), \
|
||||
or_i((rt), (rt), C_(U2,u4_lo(imm)) \
|
||||
asm_clobber: rlit(R_AT_Code), clb_mem_drain \
|
||||
asm_clobber: rlit(R_AT), clb_mem_drain \
|
||||
); \
|
||||
} \
|
||||
} while (0)
|
||||
@@ -395,7 +395,7 @@ enum { _BitOffsets = 0
|
||||
asm volatile( \
|
||||
asm_words(load_ui((rt), u4_lo(imm)), \
|
||||
or_i((rt), (rt), C_(U2,u4_hi(imm))) ) \
|
||||
asm_clobber: rlit(R_AT_Code), clb_mem_drain \
|
||||
asm_clobber: rlit(R_AT), clb_mem_drain \
|
||||
); \
|
||||
} while (0)
|
||||
|
||||
@@ -408,7 +408,7 @@ enum { _BitOffsets = 0
|
||||
asm volatile(asm_words( \
|
||||
lui_op((rt), u4_lo(imm)), \
|
||||
add_si((rt), (rt), (S2)C_(U2,u4_hi(imm))) ) \
|
||||
asm_clobber: rlit(R_AT_Code), clb_mem_drain \
|
||||
asm_clobber: rlit(R_AT), clb_mem_drain \
|
||||
); \
|
||||
} while (0)
|
||||
|
||||
@@ -436,14 +436,14 @@ enum { _BitOffsets = 0
|
||||
/* Small positive: addi rt, $0, imm */ \
|
||||
asm volatile( \
|
||||
asm_words(add_si((rt), R_0, (imm))) \
|
||||
asm_clobber: rlit(R_AT_Code), clb_mem_drain \
|
||||
asm_clobber: rlit(R_AT), clb_mem_drain \
|
||||
); \
|
||||
} \
|
||||
else if (cexpr_(imm) && ((U4)(imm) <= 0xFFFFU)) { \
|
||||
/* 0x8000..0xFFFF: ori rt, $0, imm (zero-extends) */ \
|
||||
asm volatile( \
|
||||
asm_words(or_i((rt), R_0, (imm))) \
|
||||
asm_clobber: rlit(R_AT_Code), clb_mem_drain \
|
||||
asm_clobber: rlit(R_AT), clb_mem_drain \
|
||||
); \
|
||||
} \
|
||||
else \
|
||||
@@ -455,14 +455,14 @@ enum { _BitOffsets = 0
|
||||
asm volatile(asm_words( \
|
||||
load_ui((rt), u4_hi(imm)), \
|
||||
add_si((rt), (rt), (S2)C_(U2,u4_lo(imm))) \
|
||||
asm_clobber: rlit(R_AT_Code), clb_mem_drain \
|
||||
asm_clobber: rlit(R_AT), clb_mem_drain \
|
||||
); \
|
||||
} \
|
||||
else { \
|
||||
asm volatile(asm_words( \
|
||||
load_ui((rt), u4_hi(imm)), \
|
||||
or_i((rt), (rt), C_(U2,u4_lo(imm)) \
|
||||
asm_clobber: rlit(R_AT_Code), clb_mem_drain \
|
||||
asm_clobber: rlit(R_AT), clb_mem_drain \
|
||||
); \
|
||||
} \
|
||||
} \
|
||||
@@ -470,8 +470,8 @@ enum { _BitOffsets = 0
|
||||
|
||||
// Binary Metaprogramming
|
||||
|
||||
typedef U4 const MipsAtom;
|
||||
#define MipsAtom_(sym) MipsAtom tmpl(code,sym) [] align_(4) =
|
||||
typedef U4 const MipsCode;
|
||||
#define MipsAtom_(sym) MipsCode tmpl(code,sym) [] align_(4) =
|
||||
|
||||
enum {
|
||||
bios_flushcache = 0x44,
|
||||
@@ -506,7 +506,7 @@ I_ void mips_flush_icache(void) { C_(VoidFn*, code_mips_flush_icache)(); }
|
||||
* GPRs that the kernel treats as volatile (v0/v1/t0/t1/ra) plus the
|
||||
* "memory" barrier. The register ids are passed through `rlit` so
|
||||
* the R_*_Code `#define`s are stringified into "$N" at expansion time. */
|
||||
#define clb_system rlit(R_V0_Code), rlit(R_T0_Code), rlit(R_T1_Code), rlit(R_RA_Code), clb_mem_drain
|
||||
#define clb_system rlit(R_V0), rlit(R_T0), rlit(R_T1), rlit(R_RA), clb_mem_drain
|
||||
|
||||
#define asm_mips_flush_icache() asm volatile( asm_words( \
|
||||
add_ui(rstack_ptr, rstack_ptr, -8) \
|
||||
|
||||
@@ -230,6 +230,39 @@ void update(PrimitiveArena* pa, U4* ordering_buf)
|
||||
// static_mem.cube.rot.z += 12;
|
||||
static_mem.cube.rot.y += 30;
|
||||
}
|
||||
// Draw cube (tape method) - two triangles per face
|
||||
if (0)
|
||||
{
|
||||
m3s2_rotation (& static_mem.cube.rot, & static_mem.tform_world);
|
||||
m3s2_translation(& static_mem.tform_world, & static_mem.cube.pos);
|
||||
m3s2_scale (& static_mem.tform_world, & static_mem.cube.scale);
|
||||
gte_matrix_set_rotation (& static_mem.tform_world);
|
||||
gte_matrix_set_translation(& static_mem.tform_world);
|
||||
|
||||
U4 prim_base = u4_(pa->buf[static_mem.active_buf_id]);
|
||||
U4 prim_cursor = prim_base + pa->used;
|
||||
|
||||
LP_ U4 mem_temp_tape[512]; FArena tape_arena; farena_init(& tape_arena, slice_ut_arr(mem_temp_tape));
|
||||
TapeBuilder tb = tb_make(&tape_arena); tb_scope(& tb) {
|
||||
tb_emit(& tb, code_rbind_cube_tri);
|
||||
tb_data(& tb, prim_cursor);
|
||||
tb_data(& tb, u4_(static_mem.cube.faces));
|
||||
tb_data(& tb, u4_(static_mem.cube.verts));
|
||||
tb_data(& tb, u4_(ordering_buf));
|
||||
|
||||
for (U4 i = 0; i < Cube_num_faces; i++) {
|
||||
// Two triangles per quad face: (x,y,z) and (x,z,w)
|
||||
tb_emit(& tb, code_cube_tri);
|
||||
}
|
||||
|
||||
tb_emit(& tb, code_sync_prim_cursor);
|
||||
tb_data(& tb, u4_(& pa->used));
|
||||
tb_data(& tb, prim_base);
|
||||
}
|
||||
tape_run(tb_slice(tb));
|
||||
|
||||
static_mem.cube.rot.y += 30;
|
||||
}
|
||||
// Draw Floor
|
||||
if (0)
|
||||
{
|
||||
@@ -301,12 +334,11 @@ void update(PrimitiveArena* pa, U4* ordering_buf)
|
||||
LP_ U4 mem_temp_tape[512]; FArena tape_arena; farena_init(& tape_arena, slice_ut_arr(mem_temp_tape));
|
||||
TapeBuilder tb = tb_make(&tape_arena); tb_scope(& tb) {
|
||||
// Push "Protocol" to tape
|
||||
tb_emit(& tb, code_bind_workspace);
|
||||
tb_data(& tb, prim_cursor);
|
||||
tb_data(& tb, u4_(static_mem.floor.faces));
|
||||
tb_data(& tb, u4_(static_mem.floor.verts));
|
||||
tb_data(& tb, u4_(ordering_buf));
|
||||
|
||||
tb_emit_(& tb, rbind_floor_tri);
|
||||
tb_data(& tb, prim_cursor);
|
||||
tb_data(& tb, u4_(static_mem.floor.faces));
|
||||
tb_data(& tb, u4_(static_mem.floor.verts));
|
||||
tb_data(& tb, u4_(ordering_buf));
|
||||
tb_emit(& tb, code_set_gte_world);
|
||||
tb_data(& tb, u4_(& static_mem.tform_world));
|
||||
|
||||
@@ -339,7 +371,7 @@ void update(PrimitiveArena* pa, U4* ordering_buf)
|
||||
// 2. code_diag_color -> Tests OT and Prim Arena memory
|
||||
// 3. code_diag_gte -> Tests Vertex arrays and GTE Math
|
||||
// tb_emit(& tb, code_diag_yield);
|
||||
// tb_emit(& tb, code_diag_color);
|
||||
// tb_emit(& tb, code_diag_color); //TODO(Ed): Stopped working
|
||||
// tb_emit(& tb, code_diag_gte);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user