mirror of
https://github.com/Ed94/pikuma_ps1.git
synced 2026-08-05 07:08:49 +00:00
compiles..
This commit is contained in:
+99
-68
@@ -5,6 +5,64 @@
|
||||
# include "mips.h"
|
||||
#endif
|
||||
|
||||
/* ============================================================================
|
||||
* gte.h — Geometry Transformation Engine (COP2) for the PS1
|
||||
* ============================================================================
|
||||
*
|
||||
* Hand-rolled DSL for emitting GTE/MIPS instruction words as raw `.word`
|
||||
* constants from C. No GCC inline-assembly string syntax in the code body.
|
||||
*
|
||||
* PHILOSOPHY
|
||||
* ----------
|
||||
* 1. A 32-bit instruction word is composed from per-field encoders. Each
|
||||
* encoder knows only its own bit range; the composite ORs them together.
|
||||
* No magic numbers inside any encoder body — every shift and mask is a
|
||||
* named constant from the bitfield-layout enum below.
|
||||
*
|
||||
* 2. Pure (compile-time) instructions — every GTE *command* (RTPS, RTPT,
|
||||
* NCLIP, MVMVA, …) and every COP2 *transfer* (ctc2/cfc2) with a constant
|
||||
* rs/rt/rd — are emitted as a single integer constant via
|
||||
* `asm_inline(...)` from gcc_asm.h. The C compiler constant-folds
|
||||
* these into `.word` directives in .rodata.
|
||||
*
|
||||
* 3. Runtime-base-register instructions (lwc2, swc2, lw, sw, …) cannot be
|
||||
* a pure compile-time word because the `rs` field is chosen by the
|
||||
* compiler at codegen. For these we use a "placeholder-pun" pattern:
|
||||
* a fixed register number (R_T4 = $12) is baked into the rs field of
|
||||
* the `.word` constant, and the macro declares a `"r"(arg)` input
|
||||
* constraint plus a clobber on the same register. The compiler is
|
||||
* therefore *forced* to bind `arg` to that exact register, and the
|
||||
* constant is correct.
|
||||
*
|
||||
* USAGE
|
||||
* -----
|
||||
* // Pure command sequence — all bits compile-time:
|
||||
* asm volatile(
|
||||
* asm_inline( gte_cmd_rtpt , gte_cmd_nclip , gte_cmd_avsz3 )
|
||||
* asm_clobber( clb_system )
|
||||
* );
|
||||
*
|
||||
* // Runtime-base-register load — uses R_T4 ($12) under the hood:
|
||||
* gte_load_v0( my_svector_ptr );
|
||||
*
|
||||
* STYLE NOTES
|
||||
* -----------
|
||||
* - Per-field encoders are named `enc_gte_<field>(value)` and each one
|
||||
* self-masks its argument before shifting. Mirrors the `enc_op / enc_rs
|
||||
* / enc_rt / ...` family in mips.h.
|
||||
* - The composite `enc_gte_cmdw(sf, mx, v, cv, lm, cmd)` is a flat OR of
|
||||
* the per-field encoders, plus the COP2/CO base.
|
||||
* - Pre-baked shortcuts (`gte_cmd_rtpt`, `gte_cmd_rtps`, …) are defined
|
||||
* for the common cases so call sites read like assembly source.
|
||||
* - All register/field values are enums (not `#define`s) so they show up
|
||||
* in debugger symbol tables and IDE autocomplete.
|
||||
*
|
||||
* SEE ALSO
|
||||
* --------
|
||||
* - gcc_asm.h: the `.word` emitter (`asm_inline`, `asm_clobber`, clobbers)
|
||||
* - mips.h: the MIPS encoder layer this builds on
|
||||
*/
|
||||
|
||||
/* C2 data registers */
|
||||
|
||||
/* --- GTE Data Registers (Coprocessor 2) --- */
|
||||
@@ -199,86 +257,58 @@ enum { _C2_OPS_ = 0
|
||||
#define gte_lwc2_v2_RT4 enc_cop2_lwc2(gte_in_v2_xy, R_T4, 0)
|
||||
#define gte_lwc2_v2z_RT4 enc_cop2_lwc2(gte_in_v2_z, R_T4, 4)
|
||||
|
||||
/* The actual call-site macros — zero string syntax in the .word body.
|
||||
/* gte_load_vN(r_ptr) — placeholder-punned lwc2 loaders
|
||||
*
|
||||
* The "r"(r_ptr) input constraint is the irreducible GCC-syntax bit: the
|
||||
* base register of lwc2 is a runtime value, so the compiler must allocate
|
||||
* one for us. The "$12" clobber + the .word constants having rs=R_T4 ($12)
|
||||
* hardwired form the "placeholder-pun" — GCC is forced to bind r_ptr to
|
||||
* $12, which is exactly the register the .word constants expect.
|
||||
* Each emits a small sequence of `.word` constants that encode `lwc2 $N,
|
||||
* off($12)` for the chosen GTE vector register. The base register is
|
||||
* forced to be R_T4 ($12) at runtime via:
|
||||
* - `"r"(r_ptr)`: GCC picks a GPR for `r_ptr`
|
||||
* - `"$12"` in the clobber list: GCC can't put any other live value in $12
|
||||
* - Net effect: GCC must place `r_ptr` in $12, the register the .word
|
||||
* constants expect.
|
||||
*
|
||||
* Uses asm_block_4(code, outs, ins, clb) from gcc_asm.h.
|
||||
* asm_inline(...) produces the 2-colon code:outputs:inputs body
|
||||
* "r"(r_ptr) is the runtime-input section body
|
||||
* "$2", ..., "$12" is the clobber section body
|
||||
* asm_block_4() joins them with 3 colons and wraps in asm volatile
|
||||
* Shape of the generated `asm volatile (...)`:
|
||||
* code section : ".word %0, %1" (from asm_inline)
|
||||
* outputs section : (empty, the 2nd colon)
|
||||
* inputs section : "i"(w0), "i"(w1), "r"(r_ptr)
|
||||
* clobbers section : "$2", "$8", ..., "$12" (from asm_clobber)
|
||||
* 3 colons total, GCC-legal. No string-syntax mnemonics in the .word body.
|
||||
*
|
||||
* The parens `(...)` around each section let the preprocessor treat the
|
||||
* section's contents as a single arg (shielding internal commas), and
|
||||
* the `_strip` helper inside asm_block_4 removes those parens so the
|
||||
* final C code is clean.
|
||||
*/
|
||||
* The `asm_clobber(...)` helper from gcc_asm.h prepends the colon that
|
||||
* starts the clobbers section. */
|
||||
#define gte_load_v0(r_ptr) \
|
||||
asm_block_4( \
|
||||
(asm_inline( gte_lwc2_v0_RT4, gte_lwc2_v0z_RT4 )), \
|
||||
(), \
|
||||
("r"(r_ptr)), \
|
||||
("$2", "$8", "$9", "$31", "memory", "$12") \
|
||||
asm volatile( \
|
||||
asm_inline( gte_lwc2_v0_RT4, gte_lwc2_v0z_RT4 ) \
|
||||
, "r"(r_ptr) \
|
||||
asm_clobber( "$2", "$8", "$9", "$31", "memory", "$12" ) \
|
||||
)
|
||||
|
||||
#define gte_load_v1(r_ptr) \
|
||||
asm_block_4( \
|
||||
(asm_inline( gte_lwc2_v1_RT4, gte_lwc2_v1z_RT4 )), \
|
||||
(), \
|
||||
("r"(r_ptr)), \
|
||||
("$2", "$8", "$9", "$31", "memory", "$12") \
|
||||
asm volatile( \
|
||||
asm_inline( gte_lwc2_v1_RT4, gte_lwc2_v1z_RT4 ) \
|
||||
, "r"(r_ptr) \
|
||||
asm_clobber( "$2", "$8", "$9", "$31", "memory", "$12" ) \
|
||||
)
|
||||
|
||||
#define gte_load_v2(r_ptr) \
|
||||
asm_block_4( \
|
||||
(asm_inline( gte_lwc2_v2_RT4, gte_lwc2_v2z_RT4 )), \
|
||||
(), \
|
||||
("r"(r_ptr)), \
|
||||
("$2", "$8", "$9", "$31", "memory", "$12") \
|
||||
asm volatile( \
|
||||
asm_inline( gte_lwc2_v2_RT4, gte_lwc2_v2z_RT4 ) \
|
||||
, "r"(r_ptr) \
|
||||
asm_clobber( "$2", "$8", "$9", "$31", "memory", "$12" ) \
|
||||
)
|
||||
|
||||
/* All three at once -- the canonical prelude to gte_cmd_rtpt. */
|
||||
/* gte_load_v0v1v2(r_ptr) — the canonical prelude to gte_cmd_rtpt.
|
||||
* Loads all three GTE input vectors (6 words) from a contiguous array
|
||||
* of three SVECTORs (24 bytes total). */
|
||||
#define gte_load_v0v1v2(r_ptr) \
|
||||
asm_block_4( \
|
||||
(asm_inline( gte_lwc2_v0_RT4, gte_lwc2_v0z_RT4, \
|
||||
gte_lwc2_v1_RT4, gte_lwc2_v1z_RT4, \
|
||||
gte_lwc2_v2_RT4, gte_lwc2_v2z_RT4 )), \
|
||||
(), \
|
||||
("r"(r_ptr)), \
|
||||
("$2", "$8", "$9", "$31", "memory", "$12") \
|
||||
asm volatile( \
|
||||
asm_inline( gte_lwc2_v0_RT4, gte_lwc2_v0z_RT4, \
|
||||
gte_lwc2_v1_RT4, gte_lwc2_v1z_RT4, \
|
||||
gte_lwc2_v2_RT4, gte_lwc2_v2z_RT4 ) \
|
||||
, "r"(r_ptr) \
|
||||
asm_clobber( "$2", "$8", "$9", "$31", "memory", "$12" ) \
|
||||
)
|
||||
|
||||
/**
|
||||
* @brief Loads a single SVECTOR to GTE vector register V1
|
||||
*
|
||||
* @details Loads values from an SVECTOR struct to GTE data registers C2_VXY1
|
||||
* and C2_VZ1.
|
||||
*/
|
||||
// #define gte_load_v1( r0 ) __asm__ volatile ( \
|
||||
// "lwc2 $2, 0( %0 );" \
|
||||
// "lwc2 $3, 4( %0 );" \
|
||||
// : \
|
||||
// : "r"( r0 ) \
|
||||
// : "$t0" )
|
||||
|
||||
/**
|
||||
* @brief Loads a single SVECTOR to GTE vector register V2
|
||||
*
|
||||
* @details Loads values from an SVECTOR struct to GTE data registers C2_VXY2
|
||||
* and C2_VZ2.
|
||||
*/
|
||||
// #define gte_load_v2( r0 ) __asm__ volatile ( \
|
||||
// "lwc2 $4, 0( %0 );" \
|
||||
// "lwc2 $5, 4( %0 );" \
|
||||
// : \
|
||||
// : "r"( r0 ) \
|
||||
// : "$t0" )
|
||||
|
||||
#define gte_ldv0(r0) \
|
||||
__asm__ volatile( \
|
||||
"lwc2 $0, 0( %0 );" \
|
||||
@@ -352,9 +382,10 @@ enum { _C2_OPS_ = 0
|
||||
* ctc2 $13, $3 ; → C2_RT21
|
||||
* ctc2 $14, $4 ; → C2_RT22
|
||||
*
|
||||
* Uses string-style GCC inline asm with `%0` substitution because the
|
||||
* base register `r0` is a runtime GPR — the `lw` offsets use literal
|
||||
* values (0, 4, 8, ...) so only the base register needs substitution.
|
||||
* Uses the placeholder-pun: R_T4 ($12) is hard-wired into the `lw` base
|
||||
* field of every `.word` constant, and the `"r"(r0)` constraint + `"$12"`
|
||||
* clobber force GCC to put `r0` in $12 at runtime. The `lw` offsets are
|
||||
* literal values (0, 4, 8, ...) so the only runtime GPR in play is $12.
|
||||
*
|
||||
* WARNING: Incomplete by design. The source macro only writes RT11..RT22
|
||||
* (5 of 9 rotation elements); RT23 and the entire RT3x row are left
|
||||
|
||||
Reference in New Issue
Block a user