/* ============================================================================
 *  duffle DSL Suffix Conventions
 *  ============================================================================
 *
 *  Every mnemonic in this header follows the same suffix grammar:
 *
 *  Primitive commands: gp0_cmd_poly_f3 = 0x20    (byte opcode)
 *  Packed 32-bit cmd:  gp0_word_poly_f3(r, g, b) (32-bit, shifted)
 *
 *  Type ordering: domain?_(direction)?_action_target_modifier_type?
 *  Examples: add_ui             (add + unsigned + immediate)
 *            add_s              (add + signed, R-type implicit)
 *            shift_lleft        (shift + logical + left)
 *            shift_aright       (shift + arithmetic + right)
 *            call_reg(rs)       (call + register, $ra implicit)
 *            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
#	pragma once
#	include "dsl.h"
#	include "math.h"
#	include "mips.h"
#endif

#pragma region ASM DSL
/* ============================================================================
 *  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.
 *
 *  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
 *  --------
 *  - mips.h: The MIPS encoder layer this builds on.
 */

/* C2 data registers */

/* --- GTE Data Registers (Coprocessor 2) ---
 * Preprocessor-visible integer ids for the COP2 data register file.
 * Each enum value is bound to a parallel `_Code` `#define` so the preprocessor can stringify the integer (for `reg_str`/`rgcc` paths).
 * Same pattern as the GPR `_Code` set in mips.h. */
#define C2_VXY0_Code  0
#define C2_VZ0_Code   1
#define C2_VXY1_Code  2
#define C2_VZ1_Code   3
#define C2_VXY2_Code  4
#define C2_VZ2_Code   5
#define C2_RGB_Code   6
#define C2_OTZ_Code   7
#define C2_IR0_Code   8
#define C2_IR1_Code   9
#define C2_IR2_Code  10
#define C2_IR3_Code  11
#define C2_SXY0_Code 12
#define C2_SXY1_Code 13
#define C2_SXY2_Code 14
#define C2_SXYP_Code 15
#define C2_SZ0_Code  16
#define C2_SZ1_Code  17
#define C2_SZ2_Code  18
#define C2_SZ3_Code  19
#define C2_RGB0_Code 20
#define C2_RGB1_Code 21
#define C2_RGB2_Code 22
#define C2_RES1_Code 23
#define C2_MAC0_Code 24
#define C2_MAC1_Code 25
#define C2_MAC2_Code 26
#define C2_MAC3_Code 27
#define C2_IRGB_Code 28
#define C2_ORGB_Code 29
#define C2_LZCS_Code 30
#define C2_LZCR_Code 31

enum {
	C2_VXY0 = C2_VXY0_Code, C2_VZ0  = C2_VZ0_Code,  C2_VXY1 = C2_VXY1_Code, C2_VZ1  = C2_VZ1_Code,
	C2_VXY2 = C2_VXY2_Code, C2_VZ2  = C2_VZ2_Code,  C2_RGB  = C2_RGB_Code,  C2_OTZ  = C2_OTZ_Code,
	C2_IR0  = C2_IR0_Code,  C2_IR1  = C2_IR1_Code,  C2_IR2  = C2_IR2_Code,  C2_IR3  = C2_IR3_Code,
	C2_SXY0 = C2_SXY0_Code, C2_SXY1 = C2_SXY1_Code, C2_SXY2 = C2_SXY2_Code, C2_SXYP = C2_SXYP_Code,
	C2_SZ0  = C2_SZ0_Code,  C2_SZ1  = C2_SZ1_Code,  C2_SZ2  = C2_SZ2_Code,  C2_SZ3  = C2_SZ3_Code,
	C2_RGB0 = C2_RGB0_Code, C2_RGB1 = C2_RGB1_Code, C2_RGB2 = C2_RGB2_Code, C2_RES1 = C2_RES1_Code,
	C2_MAC0 = C2_MAC0_Code, C2_MAC1 = C2_MAC1_Code, C2_MAC2 = C2_MAC2_Code, C2_MAC3 = C2_MAC3_Code,
	C2_IRGB = C2_IRGB_Code, C2_ORGB = C2_ORGB_Code, C2_LZCS = C2_LZCS_Code, C2_LZCR = C2_LZCR_Code
};

/* Semantic Aliases for GTE Data Registers */
enum {
	gte_in_v0_xy     = C2_VXY0, /* Input Vector 0 (X, Y) */
	gte_in_v0_z      = C2_VZ0,  /* Input Vector 0 (Z) */
	gte_in_v1_xy     = C2_VXY1, /* Input Vector 1 (X, Y) */
	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, 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) */
	gte_out_depth    = C2_OTZ,  /* Output Ordering Table Z (Depth) */
	gte_math_accum0  = C2_MAC0, /* Math Accumulator 0 */
	gte_math_accum1  = C2_MAC1, /* Math Accumulator 1 */
	gte_math_accum2  = C2_MAC2, /* Math Accumulator 2 */
};

/* --- GTE Command Semantics (The Bitfield Meanings) --- 
 * A GTE command is a single 32-bit word sent to COP2.
 * It is highly configurable via bitfields.
 */

enum {
/* Shift Fraction (Bit 19) - Determines fixed-point division */

	gte_sf_fractional = 0,  /* Divide result by 4096 (Standard 4.12 fixed point) */
	gte_sf_integer    = 1,  /* No division (Raw integer math) */

/* Matrix Select (Bits 18-17) - Which 3x3 matrix to multiply by */

	gte_mx_rotation   = 0,  /* Rotation Matrix (RT) */
	gte_mx_light      = 1,  /* Light Matrix (LL) */
	gte_mx_color      = 2,  /* Color Matrix (LC) */
	gte_mx_none       = 3,  /* Reserved / Do not multiply */

/* Vector select (Bits 16-15) - Which input vector to use */

	gte_v_v0        = 0,  /* Use Vector 0 (VXY0, VZ0) */
	gte_v_v1        = 1,  /* Use Vector 1 (VXY1, VZ1) */
	gte_v_v2        = 2,  /* Use Vector 2 (VXY2, VZ2) */
	gte_v_ir_regs   = 3,  /* Use Intermediate Registers (IR1, IR2, IR3) */

/* Control Vector Select (Bits 14-13) - Which vector to ADD after multiplication */

	gte_cv_translate  = 0,  /* Add Translation Vector (TRX, TRY, TRZ) */
	gte_cv_bg_color   = 1,  /* Add Background Color (RBK, GBK, BBK) */
	gte_cv_far_color  = 2,  /* Add Far Color (RFC, GFC, BFC) */
	gte_cv_none       = 3,  /* Add Zero (No addition) */

/* Limit/Clamp (Bit 10) - Prevents overflow artifacts */

	gte_lm_normal     = 0,  /* Normal math (can overflow) */
	gte_lm_clamp      = 1,  /* Clamp results to valid hardware ranges (e.g., RGB 0-255) */

/* Core Command IDs (Bits 5-0) */

	gte_cmd_rtps      = 0x01, /* Rot/Trans Perspective Single (1 vertex) */
	gte_cmd_rtpt      = 0x30, /* Rot/Trans Perspective Triple (3 vertices) */
	gte_cmd_nclip     = 0x06, /* Normal Clipping (Backface culling) */
	gte_cmd_op        = 0x0C, /* Outer Product */
	gte_cmd_mvmva     = 0x12, /* Matrix Vector Multiply & Add (Custom math) */
	gte_cmd_sqr       = 0x28, /* Square vector — MAC[i] = IR[i]²; IR[i] ← MAC[i] saturated */
	gte_cmd_gpf       = 0x3D, /* General-purpose Interpolation — MAC[i] = IR0 * IR[i]  */

/* --- GTE Command Bit-Field Layout ---
 * A GTE command word (sent to COP2 with RS=1) is laid out as:
 *
 *   31........25 24 23..19 18..17 16..15 14..13 12..11 10  9.......6  5.......0
 *   +------------+--+-----+------+------+------+------+---+--------+----------+
 *   |  0x3E (COP2)| 1|  -- |  sf  |  mx  |  v   |  cv  | --|  lm  |  -- |  cmd  |
 *   +------------+--+-----+------+------+------+------+---+--------+----------+
 *                                    \_____ GTE_PAYLOAD _____/       \__ GTE_CMD __/
 *
 * Shifts/masks below are the *bit positions* and *bit widths* of each configurable field, used by the ENC_GTE_CMD encoder. 
 * Mirrors the OPCODE_SHIFT / RS_SHIFT convention used in mips.h.
 */

	gte_shift_sf  = 19,  gte_width_sf  = 1,  gte_mask_sf  = 0x1,
	gte_shift_mx  = 17,  gte_width_mx  = 2,  gte_mask_mx  = 0x3,
	gte_shift_v   = 15,  gte_width_v   = 2,  gte_mask_v   = 0x3,
	gte_shift_cv  = 13,  gte_width_cv  = 2,  gte_mask_cv = 0x3,
	gte_shift_lm  = 10,  gte_width_lm  = 1,  gte_mask_lm  = 0x1,
	gte_shift_cmd =  0,  gte_width_cmd = 6,  gte_mask_cmd = 0x3F,

	/* Fake command number (bits 24-20) — IGNORED by the GTE hardware per PSX-SPX `geometrytransformationenginegte.md` line 48.
	 * libgte's compiler emits non-zero values in this field as a disassembly signature. */
	gte_shift_fake_cmd = 20,
	gte_width_fake_cmd = 5,
	gte_mask_fake_cmd  = 0x1F,
};

/* --- GTE Control Register Indices (for ctc2/cfc2) ---
 * Preprocessor-visible integer ids for the COP2 control register file.
 * Each enum value is bound to a parallel `_Code` `#define` so the preprocessor can stringify the integer (for `reg_str`/`rgcc` paths).
 * Same pattern as the GPR `_Code` set in mips.h. Note: indices 21-23 are reserved/unused on real hardware, so there's a gap. */
#define gte_cr_RT11_Code  0
#define gte_cr_RT12_Code  1  /* packed with RT13 in bits 16..31 */
#define gte_cr_RT13_Code  2  /* packed with RT22 in bits 16..31 */
#define gte_cr_RT21_Code  3  /* packed with RT31 in bits 16..31 */
#define gte_cr_RT22_Code  4  /* RT33 alone (low 16 bits used) */
// #define gte_cr_RT23_Code  5
// #define gte_cr_RT31_Code  6
// #define gte_cr_RT32_Code  7
// #define gte_cr_RT33_Code  8
#define gte_cr_TRX_Code   5   /* PSX SDK convention: C2 r5 = TRX (alone, 32-bit) */
#define gte_cr_TRY_Code   6   /* PSX SDK convention: C2 r6 = TRY (alone, 32-bit) */
#define gte_cr_TRZ_Code   7   /* PSX SDK convention: C2 r7 = TRZ (alone, 32-bit) */
#define gte_cr_L11_Code  12
#define gte_cr_L12_Code  13
#define gte_cr_L13_Code  14
#define gte_cr_L21_Code  15
#define gte_cr_L22_Code  16
#define gte_cr_L23_Code  17
#define gte_cr_LR1_Code  18
#define gte_cr_LR2_Code  19
#define gte_cr_LR3_Code  20
#define gte_cr_RBK_Code  24
#define gte_cr_GBK_Code  25
#define gte_cr_BBK_Code  26
#define gte_cr_RFC_Code  27
#define gte_cr_GFC_Code  28
#define gte_cr_BFC_Code  29
#define gte_cr_OFX_Code  24
#define gte_cr_OFY_Code  25
#define gte_cr_H_Code    26

enum {
	gte_cr_RT11 = gte_cr_RT11_Code, gte_cr_RT12 = gte_cr_RT12_Code, gte_cr_RT13 = gte_cr_RT13_Code,
	gte_cr_RT21 = gte_cr_RT21_Code, gte_cr_RT22 = gte_cr_RT22_Code, //gte_cr_RT23 = gte_cr_RT23_Code,
	// gte_cr_RT31 = gte_cr_RT31_Code, gte_cr_RT32 = gte_cr_RT32_Code, gte_cr_RT33 = gte_cr_RT33_Code,
	gte_cr_TRX  = gte_cr_TRX_Code,  gte_cr_TRY  = gte_cr_TRY_Code,  gte_cr_TRZ  = gte_cr_TRZ_Code,
	gte_cr_L11  = gte_cr_L11_Code,  gte_cr_L12  = gte_cr_L12_Code,  gte_cr_L13  = gte_cr_L13_Code,
	gte_cr_L21  = gte_cr_L21_Code,  gte_cr_L22  = gte_cr_L22_Code,  gte_cr_L23  = gte_cr_L23_Code,
	gte_cr_LR1  = gte_cr_LR1_Code,  gte_cr_LR2  = gte_cr_LR2_Code,  gte_cr_LR3  = gte_cr_LR3_Code,
	gte_cr_RBK  = gte_cr_RBK_Code,  gte_cr_GBK  = gte_cr_GBK_Code,  gte_cr_BBK  = gte_cr_BBK_Code,
	gte_cr_RFC  = gte_cr_RFC_Code,  gte_cr_GFC  = gte_cr_GFC_Code,  gte_cr_BFC  = gte_cr_BFC_Code,
	gte_cr_OFX  = gte_cr_OFX_Code,  gte_cr_OFY  = gte_cr_OFY_Code,
};

enum { _C2_OPS_ = 0
	, op_lwc2    = 0x32 /* Load Word  to   Coprocessor 2 (GTE) */
	, op_swc2    = 0x3A /* Store Word from Coprocessor 2 (GTE) */
};

/* COP2 transfer sub-opcodes (5-bit field in the `rs` slot of enc_gte_tx).
 *
 * Spans the 2x2 {From, To} × {Data, Control} register classes that the GTE exposes:
 *   bit 1 (0x02): register class — 0 = data,  1 = control
 *   bit 2 (0x04): direction      — 0 = read,  1 = write
 *
 * The values 0x00 (sub_mfc2) and 0x04 (sub_mtc2) are the same 5-bit numbers as general MIPS `cop_mf` / `cop_mt` defined in mips.h
 * (which target the data register file on any coprocessor).
 * They are re-aliased here so the four-way table reads like the spec mnemonics (MFC2 / CFC2 / MTC2 / CTC2)
 * and so the encoding is next to its only consumer (this header).
 *
 * Vendor mnemonic aliases (gte_mfc2 / gte_mtc2 / gte_cfc2 / gte_ctc2) live in gte_vendor_sym.h. */
enum { _C2_TX_SUBS_ = 0
	, sub_mfc2 = 0x00 /* MFC2: Move From Coprocessor 2 data   reg */
	, sub_cfc2 = 0x02 /* CFC2: Copy From Coprocessor 2 ctrl   reg */
	, sub_mtc2 = 0x04 /* MTC2: Move To   Coprocessor 2 data   reg */
	, sub_ctc2 = 0x06 /* CTC2: Copy To   Coprocessor 2 ctrl   reg */
};

/* COP2 (GTE) Transfer Format: mfc2 / cfc2 / mtc2 / ctc2 rt, rd
 * Layout: [op_cop2:6][sub:5][rt:5][rd:5][0:11]
 *  - sub: one of sub_mfc2 / sub_cfc2 / sub_mtc2 / sub_ctc2
 *  - rt:  GPR source/dest
 *  - rd:  COP2 register index (0..31):
 *    data class → C2_VXY0_Code..C2_LZCR_Code (gte_in_v0_xy..gte_math_accum2 aliases)
 *    ctrl class → gte_cr_RT11_Code..gte_cr_OFY_Code */
#define enc_gte_tx(sub, rt, rd) (enc_op(op_cop2) | enc_rs(sub) | enc_rt(rt) | enc_rd(rd))


// #define gte_mv_to_data_r(rt, rd)   enc_gte_tx(cop_mt, (rt), (rd)) /* Move GPR (rt) to GTE Control Register (rd) */
// #define gte_mv_from_data_r(rt, rd) enc_gte_tx(cop_mf, (rt), (rd)) /* Move GTE Control Register (rd) to GPR (rt) */

/* GTE Data vs Control Register Transfers
 *
 * Each macro emits a single .word constant for one of MFC2/CFC2/MTC2/CTC2.
 *
 * `rd` is the C2 register index in the file the sub-opcode names:
 *   gte_mv_from_data_r / gte_mv_to_data_r  →  C2 data register file
 *   gte_mv_from_ctrl_r / gte_mv_to_ctrl_r  →  C2 ctrl register file
 *
 * Common pairs:
 *   gte_mv_from_data_r(R_T0, C2_MAC0)       — read MAC0 into a GPR
 *   gte_mv_to_data_r  (R_V0, C2_VXY0)       — write GPR into VXY0
 *   gte_mv_to_ctrl_r  (R_T0, gte_cr_RT11)   — write GPR into rotation matrix
 *   gte_mv_from_ctrl_r(R_T0, gte_cr_OFX)    — read screen-X offset */
#define gte_mv_from_data_r(rt, rd) enc_gte_tx(sub_mfc2, (rt), (rd)) /* Move From data reg */
#define gte_mv_from_ctrl_r(rt, rd) enc_gte_tx(sub_cfc2, (rt), (rd)) /* Copy From ctrl reg */
#define gte_mv_to_data_r(rt, rd)   enc_gte_tx(sub_mtc2, (rt), (rd)) /* Move To   data reg */
#define gte_mv_to_ctrl_r(rt, rd)   enc_gte_tx(sub_ctc2, (rt), (rd)) /* Copy To   ctrl reg */

/* COP2 Data Load (lwc2): `lwc2 rt, off(rs)`
 * Layout: [op_lwc2:6][rs:5][rt:5][imm:16]
 *   - rs: GPR base address
 *   - rt: COP2 data register index (0..31)
 *   - imm: signed 16-bit offset
 * NOTE: When `rs` is a runtime register, the encoding cannot be pre-baked
 * into a .word — use the string-style `gte_load_v0` macro below instead. */
#define enc_gte_lw(rt, base, off) enc_i(op_lwc2, (base), (rt), (off))
/* Store Word */
#define enc_gte_sw(rt, base, off) enc_i(op_swc2, (base), (rt), (off))

/* Semantic aliases for the COP2 data load/store. The `c2` in `lwc2`/
 * `swc2` is redundant when we're already inside the `gte_` namespace.
 *   gte_lw rt, base, off  →  lwc2 rt, off(base)
 *   gte_sw rt, base, off  →  swc2 rt, off(base)
 * For the typical user-facing vector-level load (xy + z as two instructions),
 * use the higher-level `gte_load_vN` macros below. */
#define gte_lw(rt, base, off) enc_gte_lw(rt, base, off)
#define gte_sw(rt, base, off) enc_gte_sw(rt, base, off)

/* GTE Command Format
 * Opcode is always MIPS_OP_COP2, RS is always 1 (CO).
 * Lower 25 bits are GTE-specific command payload.
 *
 * The `enc_gte_<field>(x)` macros below mirror the `enc_op`/`enc_rs` pattern in mips.h:
 * Each one self-masks and shifts its own field, so a caller can build up a GTE command piece by piece
 * (handy for state-driven MVMVA emitters that vary one field at a time).
 *
 * `ENC_GTE_CMD` is an all-in-one convenience for emitting a full command word.
 * It just ORs the per-field encoders together. */
#define gte_cmd_base (enc_op(op_cop2) | (1 << 25))

/* Per-field encoders. Each one does (value & mask) << shift on its own. */
#define enc_gte_sf(sf)          (((sf)  & gte_mask_sf      ) << gte_shift_sf )
#define enc_gte_mx(mx)          (((mx)  & gte_mask_mx      ) << gte_shift_mx )
#define enc_gte_v(v)            (((v)   & gte_mask_v       ) << gte_shift_v  )
#define enc_gte_cv(cv)          (((cv)  & gte_mask_cv      ) << gte_shift_cv )
#define enc_gte_lm(lm)          (((lm)  & gte_mask_lm      ) << gte_shift_lm )
#define enc_gte_cmd(cmd)        (((cmd) & gte_mask_cmd     ) << gte_shift_cmd )
#define enc_gte_fake_cmd(x)     (((x)   & gte_mask_fake_cmd) << gte_shift_fake_cmd)

/* Composite: all six GTE fields + the COP2/CO base. */
#define enc_gte_cmdw(sf, mx, v, cv, lm, cmd) ( \
     gte_cmd_base     \
   | enc_gte_sf(sf)   \
   | enc_gte_mx(mx)   \
   | enc_gte_v(v)     \
   | enc_gte_cv(cv)   \
   | enc_gte_lm(lm)   \
   | enc_gte_cmd(cmd) \
)

/* GTE command words for the common cases.
 *
 * These are pure compile-time integer constants — the C compiler constant-folds them into `.word` directives in .rodata.
 * Use them inside `asm_inline(...)` blocks (see `gte_rtpt` below for the idiom).
 *
 * Decomposition (per the `enc_gte_<field>` definitions above):
 *   gte_cmdw_<name> = gte_cmd_base | enc_gte_cmd(<cmd>)
 
 * The SF / MX / V / CV / LM fields are all zero in the common cases 
 * (standard rotation-matrix, no scaling factor, V0 vector, translation vector, no clamp),
 * so the only varying bits are the `cmd` field.
 *
 * Naming convention:
 * - `gte_cmd_*` : Raw 6-bit `cmd` field id 
 * - `gte_cmdw_* : 32-bit instruction word ready to drop into a `.word` directive.
 *
 * --------------------------------------------------------------------------
 *  PsyQ-compatibility note (RTPS/RTPT):
 *  The original Sony PsyQ `inline_n.h` ships RTPT as `cop2 0x0280030` and RTPS as `cop2 0x0180001`.
 *  Both have `0x20` set in the upper-reserved region (bit 21) AND `sf=1` (bit 19) — i.e. the "no division" flag.
 *  Per psx-spec these bits are reserved/must-be-zero,
 *  but the real GTE hardware and PCSX-Redux's GTE model both IGNORE them on these two commands
 *  (the perspective divide happens regardless of `sf`).
 *
 *  If we emit a strictly-spec-compliant word (`sf=0`, reserved bits clear),
 *  PCSX-Redux's GTE checks those bits more strictly than the silicon does and RTPT silently no-ops.
 *  The floor's screen coordinates come out as raw projection-of-rotation (Z never divided), 
 *  `nclip` ends up wrong, and the triangle is culled.
 *
 *  So for RTPS and RTPT we OR-in the `0x28` "PsyQ compat" pattern to match the working bit pattern.
 *  NCLIP / OP / MVMVA stay spec-clean — their reserved bits really are zero in the original PsyQ source.
 * --------------------------------------------------------------------------
 */
#define gte_cmdw_psyq_compat  (1u << 21 | enc_gte_sf(gte_sf_integer))

#define gte_cmdw_rtps   (gte_cmd_base | enc_gte_cmd(gte_cmd_rtps ) | gte_cmdw_psyq_compat)
#define gte_cmdw_rtpt   (gte_cmd_base | enc_gte_cmd(gte_cmd_rtpt ) | gte_cmdw_psyq_compat)
#define gte_cmdw_nclip  (gte_cmd_base | enc_gte_cmd(gte_cmd_nclip))
#define gte_cmdw_op     (gte_cmd_base | enc_gte_cmd(gte_cmd_op   ))
#define gte_cmdw_outer_product gte_cmdw_op      /* "outer product" -- NOCASH/Sdk terminology */
#define gte_cmdw_wedge         gte_cmdw_op      /* "wedge product"  -- geometric-algebra terminology.
	* RGA(Lengyel): the GTE OP is a 3D signed-16-bit D x IR cross, not a generic RGA exterior product.
	* The wedge alias is the 3D complement interpretation of the same 3 scalars (MAC1..MAC3). */
#define gte_cmdw_mvmva  (gte_cmd_base | enc_gte_cmd(gte_cmd_mvmva))

/* SQR / GPF cosmetic-bits compat helpers.
 * Each command's `_compat` macro ORs in the `fake_cmd` field value libgte happens to emit.
 * The hardware ignores these bits (per PSX-SPX line 48). */
#define gte_cmdw_sqr_fake_sig  enc_gte_fake_cmd(0x0A)
#define gte_cmdw_gpf_fake_sig  enc_gte_fake_cmd(0x19)

/* SQR — Square Vector.
 * PSX-SPX `geometrytransformationenginegte.md` §"SQR":
 *   [MAC1,MAC2,MAC3] = [IR1*IR1, IR2*IR2, IR3*IR3] SHR (sf*12)
 *   [IR1,IR2,IR3]    = [MAC1,MAC2,MAC3] (saturated to 0x7FFF when lm=1)
 * Sourced verbatim from libgte msc02 VectorNormal disassembly at 0x800160b0:
 *   0x4AA00428 = gte_cmd_base | gte_cmdw_sqr_compat | enc_gte_lm(1) | enc_gte_cmd(0x28)
 *   bit 19     sf=0
 *   bit 10     lm=1
 *   bits 5-0   cmd=0x28=SQR
 *   bits 24-20 = 0x0A (libgte "nonsense SDK command number" signature) */
#define gte_cmdw_sqr   (gte_cmd_base | enc_gte_cmd(gte_cmd_sqr) | enc_gte_lm(1) | gte_cmdw_sqr_fake_sig)

/* GPF — General-purpose Interpolation.
 * PSX-SPX `geometrytransformationenginegte.md` §"GPF":
 *   [MAC1,MAC2,MAC3] = (([IR1,IR2,IR3] * IR0) + [MAC1,MAC2,MAC3]) SAR (sf*12)
 *   [IR1,IR2,IR3]    = [MAC1,MAC2,MAC3]
 * Sourced verbatim from libgte msc02 VectorNormal disassembly at 0x8001613c:
 *   0x4B90003D = gte_cmd_base | gte_cmdw_gpf_compat | enc_gte_cmd(0x3D)
 *   bit 19     sf=0
 *   bit 10     lm=0
 *   bits 5-0   cmd=0x3D=GPF
 *   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)

#define gte_cmdw_rotate_translate_perspective_single gte_cmdw_rtps
#define gte_cmdw_rotate_translate_perspective_triple gte_cmdw_rtpt
/* RGA(Lengyel): RTPS/RTPT consume the matrix expansion of a rigid transformation (rotation matrix + translation vector) loaded into the RT/TR control registers.
 * For unitized points the same result equals the motor antiproduct; the GTE executes the LA form, not a symbolic antiproduct. */

/* PsyQ compatibility bits for AVSZ3 (Bits 20, 22, 24 must be set) */
#define gte_cmdw_psyq_avsz3_compat (0x15 << 20)
#define gte_cmd_avsz3               0x2D

#define gte_cmdw_avsz3 (gte_cmd_base | enc_gte_cmd(gte_cmd_avsz3) | gte_cmdw_psyq_avsz3_compat)

// Takes the three screen-space Z values of the triangle → averages them → writes the result into the OTZ register.
#define gte_avg_sort_z3 gte_cmdw_avsz3

/* 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)

#define gte_cmdw_avg_sort_z4 gte_cmdw_avsz4

/**
 * @brief Loads a single SVECTOR to GTE vector register V0
 * @details Loads values from an SVECTOR struct to GTE data registers C2_VXY0
 * (XY at offset 0) and C2_VZ0 (Z at offset 4) using `lwc2`.
 *
 * Uses string-style GCC inline asm with `%0` substitution because the base register `r0` is a runtime GPR chosen by the compiler.
 * It cannot be encoded into a static `.word` constant.
 *
 * Usage: asm_gte_load_v0(svector_ptr);
 */

/* lwc2 encoding helpers parameterized on the base GPR.
 * gte_lw_v0_xy(base)  → lwc2 $0,  0(base)   ; C2_VXY0
 * gte_lw_v0_z(base)   → lwc2 $1,  4(base)   ; C2_VZ0
 * gte_lw_v1_xy(base)  → lwc2 $2,  0(base)   ; C2_VXY1
 * gte_lw_v1_z(base)   → lwc2 $3,  4(base)   ; C2_VZ1
 * gte_lw_v2_xy(base)  → lwc2 $4,  0(base)   ; C2_VXY2
 * gte_lw_v2_z(base)   → lwc2 $5,  4(base)   ; C2_VZ2
 *
 * `base` is the GPR number to bake into the .word constant's `rs` field.
 * These are pure compile-time integers; the C compiler constant-folds them into .word directives. */

enum {
	GTE_Z_Offset = 4
};

#define gte_lw_v0_xy(base)  enc_gte_lw(gte_in_v0_xy, (base), 0)
#define gte_lw_v0_z(base)   enc_gte_lw(gte_in_v0_z,  (base), GTE_Z_Offset)
#define gte_lw_v1_xy(base)  enc_gte_lw(gte_in_v1_xy, (base), 0)
#define gte_lw_v1_z(base)   enc_gte_lw(gte_in_v1_z,  (base), GTE_Z_Offset)
#define gte_lw_v2_xy(base)  enc_gte_lw(gte_in_v2_xy, (base), 0)
#define gte_lw_v2_z(base)   enc_gte_lw(gte_in_v2_z,  (base), GTE_Z_Offset)

/* gte_load_vN(r_ptr, base) — placeholder-punned lwc2 loaders
 * Emits `.word` constants encoding `lwc2 $N, off(<base>)` for the chosen GTE vector register, where `<base>` is the GPR number you pass in
 * (typically one of R_T4..R_T9 for the standard "3-pointer" pattern).
 *
 * The caller MUST bind `r_ptr` to that same GPR via a register variable:
 *     register V3_S2* p_in_12 __asm__("$12") = my_ptr;
 *     gte_load_v0(p_in_12, R_T4);   // R_T4 = 12, base is $12
 *
 * Then `"r"(r_ptr)` inside the asm binds to $12 (the only register `p_in_12` can live in),
 * which is exactly the register the .word constants expect.
 * A `"$12"` clobber would conflict with the register-variable binding ("asm specifier for variable conflicts with asm clobber list"), so we omit it.
 * The other ABI-clobbers ($2/$8/$9/$31) stay because the GTE instructions don't touch caller-saved GPRs but the kernel does treat them as volatile.
 *
 * WHICH REGISTER TO PICK
 * ----------------------
 * Any caller-saved GPR is safe. Recommended default for an RTPT-style 3-pointer pipeline:
 *   gte_load_v0(p0, R_T4);  // $12
 *   gte_load_v1(p1, R_T5);  // $13
 *   gte_load_v2(p2, R_T6);  // $14
 * Avoid $0 (zero), $1 (at), $26/$27 (k0/k1), $28-$31 (gp/sp/fp/ra).
 *
 * 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)   — r_ptr bound to <base>
 *   clobbers section   : "$2", "$8", ..., "memory"      (from asm_clobber)
 *   3 colons total, GCC-legal. No string-syntax mnemonics in the .word body.
 *
 * 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(       \
	asm_words( gte_lw_v0_xy(base), gte_lw_v0_z(base) ) \
	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(       \
	asm_words( gte_lw_v1_xy(base), gte_lw_v1_z(base) ) \
	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_xy(base), gte_lw_v2_z(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) — 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.
 *   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")
 *   gte_load_v0v1v2(p0, p1, p2, R_T4, R_T5, R_T6);
 *   gte_rtpt();
 */
#define gte_load_v0v1v2(p0, p1, p2, b0, b1, b2) asm volatile( \
	asm_words( \
		gte_lw_v0_xy(b0), gte_lw_v0_z(b0),  \
		gte_lw_v1_xy(b1), gte_lw_v1_z(b1),  \
		gte_lw_v2_xy(b2), gte_lw_v2_z(b2) ) \
	asm_rpins                             \
		, r_use(p0), r_use(p1), r_use(p2)   \
	asm_clobber: rlit(R_V0), rlit(R_T0), rlit(R_T1), rlit(R_RA), clb_mem_drain \
)

/**
 * @brief Rotate, Translate and Perspective Triple (23 cycles)
 * @details Performs rotation, translation and perspective calculation of three vertices at once.
 * The equation performed is the same as gte_rtps() only repeated three times for each vertex.
 * The result of the first vertex is stored in GTE data register C2_SXY0, the second vector in C2_SXY1 then C2_SXY2.
 *
 * Encoder-style emission (no inline-asm strings in the code body):
 *   1. Two `nop` words fill the COP2 pipeline latency — the GTE takes ~8 cycles per perspective divide, 
 *      and the nops let any preceding lwc2/swc2 retire before RTPT starts reading its inputs from V0/V1/V2.
 *   2. The RTPT command word itself is `gte_cmdw_rtpt` (see the pre-baked encoders above) — 
 *      `0x0280030` decoded as `op_cop2` | CO(1) | cmd=RTPT, with all SF/MX/V/CV/LM fields zero
 *      (standard rotation, no scaling, V0 vector, translation vector, no clamp).
 *
 * Clobbers the caller-saved GPRs via `clbr_volatile_gprs` (per the kernel ABI)
 * plus the standard "memory" barrier. Does not clobber any COP2 data/control register — 
 * those have to be saved by the caller if they need to survive across the call (RTPT writes SXY0..2, SZ0..3, OTZ, MAC0..3, IR0..3, etc.).
 */
#define gte_rtpt()                        \
	asm volatile(                           \
		asm_words( nop, nop, gte_cmdw_rtpt )  \
		asm_clobber: clbr_volatile_gprs       \
	)

#define gte_rtpt_asm_str() \
	__asm__ volatile( \
		"nop;"          \
		"nop;"          \
		"cop2 0x0280030;")

/**
 * @brief Normal clipping (8 cycles)
 * @details Computes the sign of three screen coordinates (C2_SXY0-2) used for backface culling.
 * If the value of C2_MAC0 is negative, the coordinates are inverted and thus the triangle is back facing.
 *
 * The following equation is performed when executing this GTE command:
 *     MAC0 = SX0*SY1 + SX1*SY2 + SX2*SY0 - SX0*SY2 - SX1*SY0 - SX2*SY1
 * Encoder-style emission (no inline-asm strings in the code body):
 *   1. Two `nop` words fill the COP2 pipeline latency
 *      - the GTE pipeline takes a few cycles per op, and the nops let any preceding
 *      lwc2/swc2/RTPT retire before NCLIP starts reading its inputs from SXY0/SXY1/SXY2.
 *   2. The NCLIP command word itself is `gte_cmdw_nclip` (see the pre-baked encoders above)
 *      - `0x01400006` decoded as  `op_cop2` | CO(1) | cmd=NCLIP, with all SF/MX/V/CV/LM fields zero.
 *      NCLIP is spec-clean in the original PsyQ source (unlike RTPS/RTPT which carry the `gte_cmdw_psyq_compat` quirk),
 *      so `gte_cmdw_nclip` does NOT OR in any reserved bits.
 *
 * Clobbers the caller-saved GPRs via `clbr_volatile_gprs` (per the kernel ABI) plus the standard "memory" barrier.
 * Does not clobber any COP2 data/control register.
 * Those have to be saved by the caller if they need to survive across the call (NCLIP writes MAC0 only;
 * it is purely a sign-of-double-product computation on SXY0..2).
 */
#define gte_nclip()                       \
	asm volatile(                           \
		asm_words( nop, nop, gte_cmdw_nclip ) \
		asm_clobber: clbr_volatile_gprs               \
	)

#define gte_stotz(r0) __asm__ volatile("swc2   $7, 0( %0 )" : : "r"(r0) : "memory")

#define gte_stsxy3(r0, r1, r2)  \
	__asm__ volatile(             \
		"swc2   $12, 0( %0 );"      \
		"swc2   $13, 0( %1 );"      \
		"swc2   $14, 0( %2 )"       \
		:                           \
		: "r"(r0), "r"(r1), "r"(r2) \
		: "memory")

#define gte_avsz3() \
	__asm__ volatile( \
		"nop;"          \
		"nop;"          \
		"cop2 0x0158002D;")

/* asm_gte_matrix_set_rotation(r0)
 * 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] | ...).
 *
 * Generated MIPS (mirrors the source macro):
 *   lw   $12,  0( %0 )    ; word 0
 *   lw   $13,  4( %0 )    ; word 1
 *   ctc2 $12,  $0         ; → C2_RT11
 *   ctc2 $13,  $1         ; → C2_RT12
 *   lw   $12,  8( %0 )    ; word 2
 *   lw   $13, 12( %0 )    ; word 3
 *   lw   $14, 16( %0 )    ; word 4
 *   ctc2 $12,  $2         ; → C2_RT13
 *   ctc2 $13,  $3         ; → C2_RT21
 *   ctc2 $14,  $4         ; → C2_RT22
 *
 * Same contract as gte_load_v0: caller MUST bind `r0` to $12 via a register variable (`rgcc(R_T4)`) for the `lw $12, off(...)`
 * instructions to read from the right base. The `"r"(r0)` constraint alone doesn't force a specific GPR — it just lets GCC pick one.
 * The .word constants here bake R_T4/R_T5/R_T6 into the `rs` field of each lw, so the lw instructions will 
 * only do the right thing if $12 / $13 / $14 hold the matrix base at runtime.
 *
 *   M3_S2* m = ...;
 *   register M3_S2* m_in_12 rgcc(R_T4) = m;
 *   asm_gte_matrix_set_rotation(m_in_12);
 *
 * We clobber $12/$13/$14 (the ones we use as scratch inside the inline asm)
 * plus the system clobbers; we don't clobber `r0` because the `rgcc` binding already says "this variable lives in $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 untouched.
 * Real libpsn00b SetRotMatrix writes all 9. Use only when the GTE's remaining rotation entries are already correct,
 * or you will get stale-RT2x/RT3x artifacts in RTPS/RTPT/MVMVA output.
 */
#define asm_gte_matrix_set_rotation(r0) \
	asm volatile(                         \
		asm_words(                          \
			  load_word(R_T5, R_T4,  0)       \
			, load_word(R_T6, R_T4,  4)       \
			, gte_mv_to_data_r(   R_T5,  0)   \
			, gte_mv_to_data_r(   R_T6,  1)   \
			, load_word(R_T5, R_T4,  8)       \
			, load_word(R_T6, R_T4, 12)       \
			, load_word(R_T4, R_T4, 16)       \
			, gte_mv_to_data_r(   R_T5,  2)   \
			, gte_mv_to_data_r(   R_T6,  3)   \
			, gte_mv_to_data_r(   R_T4,  4)   \
		)                                   \
		, r_use(r0)                         \
		asm_clobber: clbr_volatile_gprs, rlit(R_T4), rlit(R_T5), rlit(R_T6) \
	)

#pragma endregion ASM DSL

#pragma region Reserved



#pragma endregion Reserved
