2 Commits
Author SHA1 Message Date
ed 915b14ef31 gte_nclip docs 2026-06-04 19:40:21 -04:00
ed 8d03366d92 cool 2026-06-04 19:08:23 -04:00
4 changed files with 290 additions and 104 deletions
+164 -56
View File
@@ -182,7 +182,7 @@ enum {
/* Core Command IDs (Bits 5-0) */
gte_cmd_rtps = 0x01, /* Rot/Trans Perspective Single (1 vertex) */
gte_cmd_rtpt = 0x02, /* Rot/Trans Perspective Triple (3 vertices) */
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) */
@@ -246,16 +246,16 @@ enum {
#define gte_cr_OFY_Code 31
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,
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
@@ -322,6 +322,53 @@ enum { _C2_OPS_ = 0
| enc_gte_cmd(cmd) \
)
/* Pre-baked 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
* canonical 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 follows the file's convention: `gte_cmd_*` is the raw
* 6-bit `cmd` field id, `gte_cmdw_*` is the fully-encoded 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 everyone has shipped for 25 years.
* 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_mvmva (gte_cmd_base | enc_gte_cmd(gte_cmd_mvmva))
/**
* @brief Loads a single SVECTOR to GTE vector register V0
*
@@ -393,25 +440,25 @@ enum { _C2_OPS_ = 0
* 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_inline( gte_lwc2_v0(base), gte_lwc2_v0z(base) ) \
, "r"(r_ptr) \
asm_clobber( reg_str(R_V0_Code), reg_str(R_T0_Code), reg_str(R_T1_Code), reg_str(R_RA_Code), "memory" ) \
)
asm volatile( \
asm_inline( gte_lwc2_v0(base), gte_lwc2_v0z(base) ) \
, "r"(r_ptr) \
asm_clobber( reg_str(R_V0_Code), reg_str(R_T0_Code), reg_str(R_T1_Code), reg_str(R_RA_Code), "memory" ) \
)
#define gte_load_v1(r_ptr, base) \
asm volatile( \
asm_inline( gte_lwc2_v1(base), gte_lwc2_v1z(base) ) \
, "r"(r_ptr) \
asm_clobber( reg_str(R_V0_Code), reg_str(R_T0_Code), reg_str(R_T1_Code), reg_str(R_RA_Code), "memory" ) \
)
asm volatile( \
asm_inline( gte_lwc2_v1(base), gte_lwc2_v1z(base) ) \
, "r"(r_ptr) \
asm_clobber( reg_str(R_V0_Code), reg_str(R_T0_Code), reg_str(R_T1_Code), reg_str(R_RA_Code), "memory" ) \
)
#define gte_load_v2(r_ptr, base) \
asm volatile( \
asm_inline( gte_lwc2_v2(base), gte_lwc2_v2z(base) ) \
, "r"(r_ptr) \
asm_clobber( reg_str(R_V0_Code), reg_str(R_T0_Code), reg_str(R_T1_Code), reg_str(R_RA_Code), "memory" ) \
)
asm volatile( \
asm_inline( gte_lwc2_v2(base), gte_lwc2_v2z(base) ) \
, "r"(r_ptr) \
asm_clobber( reg_str(R_V0_Code), reg_str(R_T0_Code), reg_str(R_T1_Code), reg_str(R_RA_Code), "memory" ) \
)
/* gte_load_v0v1v2(p0, p1, p2, b0, b1, b2) — the canonical prelude to gte_cmd_rtpt.
*
@@ -426,42 +473,103 @@ enum { _C2_OPS_ = 0
* gte_rtpt();
*/
#define gte_load_v0v1v2(p0, p1, p2, b0, b1, b2) \
asm volatile( \
asm_inline( gte_lwc2_v0(b0), gte_lwc2_v0z(b0), \
gte_lwc2_v1(b1), gte_lwc2_v1z(b1), \
gte_lwc2_v2(b2), gte_lwc2_v2z(b2) ) \
, "r"(p0), "r"(p1), "r"(p2) \
asm_clobber( reg_str(R_V0_Code), reg_str(R_T0_Code), reg_str(R_T1_Code), reg_str(R_RA_Code), "memory" ) \
)
asm volatile( \
asm_inline( gte_lwc2_v0(b0), gte_lwc2_v0z(b0), \
gte_lwc2_v1(b1), gte_lwc2_v1z(b1), \
gte_lwc2_v2(b2), gte_lwc2_v2z(b2) ) \
, "r"(p0), "r"(p1), "r"(p2) \
asm_clobber( reg_str(R_V0_Code), reg_str(R_T0_Code), reg_str(R_T1_Code), reg_str(R_RA_Code), "memory" ) \
)
#define gte_rtpt() \
__asm__ volatile( \
"nop;" \
"nop;" \
"cop2 0x0280030;")
/**
* @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 `clb_system` (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_inline( nop, nop, gte_cmdw_rtpt ) \
asm_clobber( clb_system ) \
)
#define gte_nclip() \
__asm__ volatile( \
"nop;" \
"nop;" \
"cop2 0x01400006;")
#define gte_rtpt_ori() \
__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 `clb_system` (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_inline( nop, nop, gte_cmdw_nclip ) \
asm_clobber( clb_system ) \
)
#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_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;")
#define gte_avsz3() \
__asm__ volatile( \
"nop;" \
"nop;" \
"cop2 0x0158002D;")
/* asm_gte_matrix_set_rotation(r0)
*
+123 -46
View File
@@ -231,7 +231,7 @@ enum { _BitOffsets = 0
* shift_ll(rd, rt, shamt) → sll rd, rt, shamt
* jump_reg(rs) → jr rs
* jump_link(rs, rd) → jalr rs (link in rd, default $ra)
* nop() → sll $0, $0, 0
* nop → sll $0, $0, 0
*/
#define load_word(rt, base, off) enc_i(op_lw, (base), (rt), (off))
#define load_byte(rt, base, off) enc_i(op_lb, (base), (rt), (off))
@@ -261,10 +261,10 @@ enum { _BitOffsets = 0
#define jump_nreg(rs) jump_link((rs), R_RA)
/* j target — absolute jump within the current 256MB region. */
#define jump(off) enc_i(op_j, R_0, R_0, (off))
#define jump(off) enc_i(op_j, R_0, R_0, (off))
/* jal target — absolute call within the current 256MB region. */
#define jump_nlink(off) enc_i(op_jal, R_0, R_0, (off))
#define jump_nlink(off) enc_i(op_jal, R_0, R_0, (off))
/* --- Store family (mirrors the load family) --- */
#define store_byte(rt, base, off) enc_i(op_sb, (base), (rt), (off))
@@ -297,20 +297,20 @@ enum { _BitOffsets = 0
#define div_u(rd, rs, rt) enc_r(op_special, (rs), (rt), (rd), 0, fc_divu)
/* --- Arithmetic I-type (immediate) --- */
#define add_si(rt, rs, imm) enc_i(op_addi, (rs), (rt), (imm))
#define add_si(rt, rs, imm) enc_i(op_addi, (rs), (rt), (imm))
/* add_ui already exists above as add_ui */
/* --- Set on less than (R-type and I-type) --- */
#define slt_s(rd, rs, rt) enc_r(op_special, (rs), (rt), (rd), 0, fc_slt)
#define slt_u(rd, rs, rt) enc_r(op_special, (rs), (rt), (rd), 0, fc_sltu)
#define slt_si(rt, rs, imm) enc_i(op_slti, (rs), (rt), (imm))
#define slt_ui(rt, rs, imm) enc_i(op_sltiu, (rs), (rt), (imm))
#define slt_si(rt, rs, imm) enc_i(op_slti, (rs), (rt), (imm))
#define slt_ui(rt, rs, imm) enc_i(op_sltiu, (rs), (rt), (imm))
/* --- Move from/to HI/LO (mult/div results) --- */
#define mov_from_high(rd) enc_r(op_special, R_0, R_0, (rd), 0, fc_mfhi)
#define mov_from_low(rd) enc_r(op_special, R_0, R_0, (rd), 0, fc_mflo)
#define mov_to_high(rs) enc_r(op_special, (rs), R_0, R_0, 0, fc_mthi)
#define mov_to_low(rs) enc_r(op_special, (rs), R_0, R_0, 0, fc_mtlo)
#define mov_to_high(rs) enc_r(op_special, (rs), R_0, R_0, 0, fc_mthi)
#define mov_to_low(rs) enc_r(op_special, (rs), R_0, R_0, 0, fc_mtlo)
/* --- Atomic branches (no pseudos like bgt/bge; compose with slt_* + branch_ne) ---
* branch_equal rs, rt, off → beq rs, rt, off
@@ -321,21 +321,88 @@ enum { _BitOffsets = 0
* branch_ge_zero rs, off → bgez rs, off
* (For `bgez`, the opcode is `op_bcond` with rt=1 to invert the bltz condition.) */
#define branch_equal(rs, rt, off) enc_i(op_beq, (rs), (rt), (off))
#define branch_ne(rs, rt, off) enc_i(op_bne, (rs), (rt), (off))
#define branch_lt_zero(rs, off) enc_i(op_bltz, R_0, (rs), (off))
#define branch_gt_zero(rs, off) enc_i(op_bgtz, R_0, (rs), (off))
#define branch_le_zero(rs, off) enc_i(op_blez, R_0, (rs), (off))
#define branch_ge_zero(rs, off) enc_i(op_bcond, R_0, (rs), (1u << 16) | ((off) & 0xFFFF))
#define branch_ne(rs, rt, off) enc_i(op_bne, (rs), (rt), (off))
#define branch_lt_zero(rs, off) enc_i(op_bltz, R_0, (rs), (off))
#define branch_gt_zero(rs, off) enc_i(op_bgtz, R_0, (rs), (off))
#define branch_le_zero(rs, off) enc_i(op_blez, R_0, (rs), (off))
#define branch_ge_zero(rs, off) enc_i(op_bcond, R_0, (rs), (1u << 16) | ((off) & 0xFFFF))
/* --- System (kernel) instructions --- */
#define syscall() enc_r(op_special, R_0, R_0, R_0, 0, fc_syscall)
#define breakpoint() enc_r(op_special, R_0, R_0, R_0, 0, fc_break)
/* --- Shift-amount alias (matches the gas convention `\p3 = shamt`) --- */
#define shamt(rd, rt, n) shift_ll(rd, rt, n)
#define shamt(rd, rt, n) shift_ll(rd, rt, n)
/* nop — canonical sll $0, $0, 0 */
#define nop() shift_ll(rdiscard, rdiscard, 0)
#define nop shift_ll(rdiscard, rdiscard, 0)
#define load_imm_1w(rt, imm) add_ui((rt), R_0, (imm))
#define load_imm_1w_s0(rt, imm) add_si((rt)), R_0, (imm))
/* load_imm_2w — unconditional 2-word `li` form: `lui` + (ori | addi).
*
* Granular companion to `load_imm`: skips the compile-time range checks
* and always emits 2 .words. Use this when:
* - you know `imm` is > 0xFFFF (otherwise you're wasting a word), OR
* - `imm` is not a compile-time constant and you want predictable
* 2-word emission without the `__builtin_constant_p` branches.
*
* The lo16 strategy is still chosen at expansion time on the lo half:
* lo16 in 0x0000..0x7FFF → addi (sign-ext is harmless, the lui
* already cleared bits 15..0)
* lo16 in 0x8000..0xFFFF → ori (zero-extends to preserve the
* intended bit pattern)
*
* For situations where you need to bypass even this choice (e.g. to
* force a specific encoding for a known discontiguous high/low pair),
* see `load_imm_2w_ori` and `load_imm_2w_addi` below.
*
* Statement-level (not expression-level): emits its own `asm volatile(...)`.
*/
#define load_imm_2w(rt, imm) do { \
U4 _li2_imm_ = (U4)(imm); \
U4 _li2_lo_ = _li2_imm_ & 0xFFFFU; \
U4 _li2_hi_ = _li2_imm_ >> 16; \
if (_li2_lo_ <= 0x7FFFU) { \
asm volatile( \
asm_inline(lui_op((rt), _li2_hi_), \
add_si((rt), (rt), (S2)(U2)_li2_lo_)) \
asm_clobber(reg_str(R_AT_Code), "memory") \
); \
} \
else { \
asm volatile( \
asm_inline(lui_op((rt), _li2_hi_), \
ori_op((rt), (rt), (U2)_li2_lo_)) \
asm_clobber(reg_str(R_AT_Code), "memory") \
); \
} \
} while (0)
/* load_imm_2w_ori — force the `lui` + `ori` form regardless of lo16 sign.
* Use when you specifically need zero-extension in the lo half. */
#define load_imm_2w_ori(rt, imm) do { \
U4 _li2o_imm_ = (U4)(imm); \
asm volatile( \
asm_inline(lui_op((rt), _li2o_imm_ >> 16), \
ori_op((rt), (rt), (U2)(_li2o_imm_ & 0xFFFFU))) \
asm_clobber(reg_str(R_AT_Code), "memory") \
); \
} while (0)
/* load_imm_2w_addi — force the `lui` + `addi` form regardless of lo16 sign.
* Use when you know sign-extension is fine (e.g. lo16 is treated as
* signed downstream) and you want a smaller effective instruction
* (the assembler/MIPS hardware will sign-extend the imm16). */
#define load_imm_2w_addi(rt, imm) do { \
U4 _li2a_imm_ = (U4)(imm); \
asm volatile( \
asm_inline(lui_op((rt), _li2a_imm_ >> 16), \
add_si((rt), (rt), (S2)(U2)(_li2a_imm_ & 0xFFFFU))) \
asm_clobber(reg_str(R_AT_Code), "memory") \
); \
} while (0)
/* load_imm rt, imm — true `li` semantics (assembler `li` pseudo)
*
@@ -356,35 +423,45 @@ enum { _BitOffsets = 0
* Falls back to a 2-word form if `imm` is not a compile-time constant,
* but that path is unusual (load_imm is most useful with literal
* addresses and magic numbers). */
#define load_imm(rt, imm) do { \
if (__builtin_constant_p(imm) && ((U4)(imm) <= 0x7FFFU)) { \
/* Small positive: addi rt, $0, imm */ \
asm volatile(asm_inline(add_si((rt), R_0, (imm))) \
asm_clobber(reg_str(R_AT_Code), "memory")); \
} else if (__builtin_constant_p(imm) && ((U4)(imm) <= 0xFFFFU)) { \
/* 0x8000..0xFFFF: ori rt, $0, imm (zero-extends) */ \
asm volatile(asm_inline(ori_op((rt), R_0, (imm))) \
asm_clobber(reg_str(R_AT_Code), "memory")); \
} else { \
/* > 16 bits: lui + (ori | addi). \
* If lo16 is in [0, 0x7FFF] use addi (sign-ext is harmless \
* since the high half cleared bits 15..0). Otherwise ori. */ \
U4 _li_imm_ = (U4)(imm); \
U4 _li_lo_ = _li_imm_ & 0xFFFFU; \
U4 _li_hi_ = _li_imm_ >> 16; \
if (_li_lo_ <= 0x7FFFU) { \
asm volatile( \
asm_inline(lui_op((rt), _li_hi_), \
add_si((rt), (rt), (S2)(U2)_li_lo_)) \
asm_clobber(reg_str(R_AT_Code), "memory")); \
} else { \
asm volatile( \
asm_inline(lui_op((rt), _li_hi_), \
ori_op((rt), (rt), (U2)_li_lo_)) \
asm_clobber(reg_str(R_AT_Code), "memory")); \
} \
} \
} while (0)
#define load_imm(rt, imm) do { \
if (__builtin_constant_p(imm) && ((U4)(imm) <= 0x7FFFU)) { \
/* Small positive: addi rt, $0, imm */ \
asm volatile( \
asm_inline(add_si((rt), R_0, (imm))) \
asm_clobber(reg_str(R_AT_Code), "memory") \
); \
} \
else if (__builtin_constant_p(imm) && ((U4)(imm) <= 0xFFFFU)) { \
/* 0x8000..0xFFFF: ori rt, $0, imm (zero-extends) */ \
asm volatile( \
asm_inline(ori_op((rt), R_0, (imm))) \
asm_clobber(reg_str(R_AT_Code), "memory") \
); \
} \
else \
{ \
/* > 16 bits: lui + (ori | addi). \
* If lo16 is in [0, 0x7FFF] use addi (sign-ext is harmless \
* since the high half cleared bits 15..0). Otherwise ori. */ \
U4 _li_imm_ = (U4)(imm); \
U4 _li_lo_ = _li_imm_ & 0xFFFFU; \
U4 _li_hi_ = _li_imm_ >> 16; \
if (_li_lo_ <= 0x7FFFU) { \
asm volatile( \
asm_inline(lui_op((rt), _li_hi_), \
add_si((rt), (rt), (S2)(U2)_li_lo_)) \
asm_clobber(reg_str(R_AT_Code), "memory") \
); \
} \
else { \
asm volatile( \
asm_inline(lui_op((rt), _li_hi_), \
ori_op((rt), (rt), (U2)_li_lo_)) \
asm_clobber(reg_str(R_AT_Code), "memory") \
); \
} \
} \
} while (0 )
// Binary Metaprogramming
@@ -414,7 +491,7 @@ Code CodeBlob_(mips_flush_icache) {
add_ui(rret_0, rdiscard, bios_flushcache), /* addiu $a0, $0, 0x44 */
add_ui(rtmp_0, rdiscard, bios_table_addr), /* addiu $t0, $0, 0xA0 */
jump_link(rtmp_0, rret_addr), /* jalr $t0, $ra */
nop(), /* BD slot */
nop, /* BD slot */
load_word(rret_addr, rstack_ptr, 4), /* lw $ra, 4($sp) */
jump_reg(rret_addr), /* jr $ra */
add_ui(rstack_ptr, rstack_ptr, 8) /* sp += 8 (BD) */
@@ -434,7 +511,7 @@ FI_ void mips_flush_icache(void) { C_(VoidFn*, codeblob_mips_flush_icache)(); }
, add_ui(rret_0, rdiscard, bios_flushcache) \
, add_ui(rtmp_0, rdiscard, bios_table_addr) \
, jump_link(rtmp_0, rret_addr) \
, nop() \
, nop \
, load_word(rret_addr, rstack_ptr, 4) \
, jump_reg(rret_addr) \
, add_ui(rstack_ptr, rstack_ptr, 8) \
+2 -2
View File
@@ -335,9 +335,9 @@ function build-gte_hello {
$compile_args = @()
$compile_args += $f_debug
# $compile_args += $f_optimize_none
$compile_args += $f_optimize_none
# $compile_args += $f_optimize_intrinsics
$compile_args += $f_optimize_size
# $compile_args += $f_optimize_size
# $compile_args += $f_optimize_debug
$compile_args += ($f_include + $path_code)
compile-unit $src_c $module_c $includes $compile_args
Submodule toolchain/psyq_iwyu added at 5cbf9f68d1