mirror of
https://github.com/Ed94/pikuma_ps1.git
synced 2026-07-16 14:21:26 -07:00
orgnaizing, improving asm annotation
This commit is contained in:
@@ -33,6 +33,7 @@
|
||||
#define align_(value) __attribute__((aligned (value))) // for easy alignment
|
||||
#define C_(type,data) ((type)(data)) // for enforced precedence
|
||||
#define expect_(x, y) __builtin_expect(x, y) // so compiler knows the common path
|
||||
#define cexpr_ __builtin_constant_p
|
||||
#define I_ internal inline
|
||||
#define FI_ inline __attribute__((always_inline)) // inline always
|
||||
#define NI_ internal __attribute__((noinline)) // inline never
|
||||
@@ -128,6 +129,9 @@ typedef __UINT32_TYPE__ TSet_(B4);
|
||||
#define u4_v(value) C_(U4 V_*, value)
|
||||
enum { false = 0, true = 1, true_overflow, };
|
||||
|
||||
#define u4_lo(value) ((value) & 0xFFFFU)
|
||||
#define u4_hi(value) ((value) >> 12)
|
||||
|
||||
typedef void Proc_(VoidFn) (void);
|
||||
|
||||
#define kilo(n) (C_(U4, n) << 10)
|
||||
|
||||
+118
-99
@@ -4,15 +4,125 @@
|
||||
#endif
|
||||
|
||||
/* ============================================================================
|
||||
* GCC INLINE ASSEMBLY MACRO DSL
|
||||
*
|
||||
*
|
||||
* ============================================================================ */
|
||||
* GCC INLINE ASM STATEMENT DSL
|
||||
* ============================================================================
|
||||
* A complete GCC inline-asm statement has up to 5 sections separated by `:`
|
||||
* asm volatile ( "code template" : OUTPUTS : INPUTS : CLOBBERS : GOTO_LABELS );
|
||||
*/
|
||||
|
||||
#pragma region Cruft
|
||||
// Below are used purely for annotation.
|
||||
#define asm_out // OUTPUTS section /* cannot be used with asm_words */
|
||||
#define asm_in // INPUTS section /* can be appended onto after asm_words for pinned registers */
|
||||
#define asm_clobber // CLOBBERS section
|
||||
|
||||
// Pinned Registers after asm_words list (Semantic marker)
|
||||
// We aren't starting a new offical section, its just a continuation of the input section.
|
||||
// asm_words(...) // ".words " code word ids... : : code_words...
|
||||
// asm_rpins, r_use(r0), ... // , pinned registers...
|
||||
// asm_clobber:
|
||||
#define asm_rpins
|
||||
|
||||
/* --- Logic & Control Flow --- */
|
||||
|
||||
/* Annotation for the 'Goto' section of 'asm volatile goto'.
|
||||
* Allows you to jump from assembly directly to a C label. */
|
||||
#define asm_goto // Annotate the last `:` in an asm expression.
|
||||
|
||||
/* `asm_words(...)` dispatches into `_INL_<count>` to emit up to 99 encoded
|
||||
* instruction words. This is the "compiled-instruction" form of `asm_code`.
|
||||
*
|
||||
* Result is a 2-colon body WITHOUT the final clobber section:
|
||||
* ".word %c0, %c1, ..." : --- empty --- : "i"(p0), "i"(p1), ...
|
||||
* |------ code --------| |--- outputs ---| |------- inputs -------|
|
||||
*
|
||||
* Use it inside `asm volatile( ... )` like so:
|
||||
* asm volatile(
|
||||
* asm_words(w0, w1, w3)
|
||||
* asm_clobber: clobbers
|
||||
* )
|
||||
* which expands to:
|
||||
* asm volatile(".word %c0, %c1, %c2"
|
||||
* asm_out: // empty outputs
|
||||
* asm_in: "i"(w0), "i"(w1), "i"(w2)
|
||||
* asm_clobber: "$2", "$8", ...
|
||||
* )
|
||||
*/
|
||||
#define asm_words(...) m_expand(glue(GCC_ASM_INL_, GCC_ASM_COUNT_ARGS(__VA_ARGS__))(__VA_ARGS__))
|
||||
// Very nasty macro expansion. See the Cruft pragma region after all the DSL defines
|
||||
|
||||
/* reg_str(n) — Stringify an integer register id into the GCC asm
|
||||
* string form (e.g. 12 → "$12"). Use this anywhere GCC's parser
|
||||
* expects a literal string identifying a register: clobber lists,
|
||||
* asm templates, etc. The two-level macro is the standard preprocessor
|
||||
* idiom for forcing one level of expansion before stringify — without
|
||||
* it, `#n` would stringify the macro name `R_T4` to `"R_T4"` instead
|
||||
* of expanding `R_T4` to its value first.
|
||||
*
|
||||
* For declaring a register variable bound to a specific GPR, use the
|
||||
* `rgcc(n)` bundle from gcc_asm.h instead — it adds the `__asm__()`
|
||||
* qualifier around the string.
|
||||
*
|
||||
* register V3_S2* p0 __asm__(reg_str(R_T4)) = ...; // verbose
|
||||
* 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)
|
||||
|
||||
/* ------------------------------------------------------------------------ *
|
||||
* rgcc(n) — GCC-specific bundle for register-variable declarations.
|
||||
*
|
||||
* Produces `__asm__(reg_str(tmpl(n, Code)))` at expansion time.
|
||||
* The `tmpl(n, Code)` 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;
|
||||
* which (via reg_str) becomes: register V3_S2* p __asm__("$12") = verts[0].ptr;
|
||||
*
|
||||
* Why bundle the `__asm__()` wrapper?
|
||||
* - The integer R_T4 (= 12, via R_T4_Code) already indicates the register.
|
||||
* - The string "$12" is derived from it via reg_str, so they cannot drift apart.
|
||||
* - Spelling `__asm__(reg_str(R_T4_Code))` at every call site is noise.
|
||||
*
|
||||
* tmpl defined in dsl.h (the token-paste glue).
|
||||
* rgcc define here (gcc_asm.h) because the `__asm__` keyword is GCC-specific.
|
||||
* Anyone porting to a different compiler's asm dialect overrides rgcc,
|
||||
* and the integer→string derivation in rlit can be retargeted in one place.
|
||||
*
|
||||
* 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)
|
||||
|
||||
/* rgcc_ref(n) — GCC operand-reference form "%N". Not currently used
|
||||
* by the placeholder-pun macros (the .word bodies are fully baked
|
||||
* at compile time and have no runtime operand references), but kept
|
||||
* here for completeness in case a future asm template needs to refer
|
||||
* to a runtime input by position. Mirror of rgcc but produces "%N"
|
||||
* instead of "$N". */
|
||||
#define rgcc_ref_(n) "%" #n
|
||||
#define rgcc_ref(n) rgcc_ref_(n)
|
||||
|
||||
/* --- Register Constraint Aliases (for Pinned Variables) --- */
|
||||
|
||||
#define r_use(var) "r"(var) /* General Purpose Register */
|
||||
#define r_set(var) "=r"(var) /* Write-only output */
|
||||
#define r_mod(var) "+r"(var) /* Read-write */
|
||||
#define r_imm(val) "i"(val) /* Immediate / Constant */
|
||||
/* Memory: Forces GCC to sync the variable to RAM before the asm runs.
|
||||
* Essential for DMA buffers or when the hardware reads from memory. */
|
||||
#define r_mem(var) "m"(var)
|
||||
#define r_imm(val) "i"(val) /* Immediate: Forces a compile-time constant. */
|
||||
#define r_fpu(var) "f"(var) /* FPU (PS2/MIPS III/IV): Use for COP1 floating point registers. */
|
||||
#define r_acc(var) "a"(var) /* Accumulator: Use for HI/LO register results (multiplication/division). */
|
||||
|
||||
#define clb_mem_drain "memory"
|
||||
|
||||
// C Preprocessor Iterative Expansion Jank
|
||||
#pragma region Cruft
|
||||
|
||||
/* --- 1. The Argument Counter --- */
|
||||
#define _ASM_COUNT_ARGS_IMPL( \
|
||||
#define GCC_ASM_COUNT_ARGS_IMPL( \
|
||||
_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \
|
||||
_11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
|
||||
_21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
|
||||
@@ -24,7 +134,7 @@
|
||||
_81,_82,_83,_84,_85,_86,_87,_88,_89,_90, \
|
||||
_91,_92,_93,_94,_95,_96,_97,_98,_99, N, ...) N
|
||||
|
||||
#define _ASM_COUNT_ARGS(...) m_expand(_ASM_COUNT_ARGS_IMPL(__VA_ARGS__, \
|
||||
#define GCC_ASM_COUNT_ARGS(...) m_expand(GCC_ASM_COUNT_ARGS_IMPL(__VA_ARGS__, \
|
||||
99, 98, 97, 96, 95, 94, 93, 92, 91, 90, \
|
||||
89, 88, 87, 86, 85, 84, 83, 82, 81, 80, \
|
||||
79, 78, 77, 76, 75, 74, 73, 72, 71, 70, \
|
||||
@@ -344,95 +454,4 @@
|
||||
#define GCC_ASM_INL_98(a, ...) ".word " GCC_ASM_W98 : : GCC_ASM_I98(a, __VA_ARGS__)
|
||||
#define GCC_ASM_INL_99(a, ...) ".word " GCC_ASM_W99 : : GCC_ASM_I99(a, __VA_ARGS__)
|
||||
|
||||
#pragma endregion Cruft
|
||||
|
||||
/* ============================================================================
|
||||
* AST BUILDERS — assemble a complete inline-asm block
|
||||
* ============================================================================
|
||||
*
|
||||
* A complete GCC inline-asm statement has up to 4 sections separated by `:`:
|
||||
* asm volatile ( "code" : OUTPUTS : INPUTS : CLOBBERS );
|
||||
* Below are used purely for annotation.
|
||||
*/
|
||||
#define asm_out
|
||||
#define asm_in
|
||||
#define asm_clobber
|
||||
|
||||
/* `asm_inline(...)` dispatches into `_INL_<count>` to emit up to 99 encoded
|
||||
* instruction words. This is the "compiled-instruction" form of `asm_code`.
|
||||
*
|
||||
* Result is a 2-colon body WITHOUT the final clobber section:
|
||||
* ".word %c0, %c1, ..." : --- empty --- : "i"(p0), "i"(p1), ...
|
||||
* |------ code --------| |--- outputs ---| |------- inputs -------|
|
||||
*
|
||||
* Use it inside `asm volatile( ... )` like so:
|
||||
* asm volatile(
|
||||
* asm_inline(w0, w1, w3)
|
||||
* asm_clobber: clobbers
|
||||
* )
|
||||
* which expands to:
|
||||
* asm volatile(".word %c0, %c1, %c2"
|
||||
* asm_out: // empty outputs
|
||||
* asm_in: "i"(w0), "i"(w1), "i"(w2)
|
||||
* asm_clobber: "$2", "$8", ...
|
||||
* )
|
||||
*/
|
||||
#define asm_words(...) m_expand(glue(GCC_ASM_INL_, _ASM_COUNT_ARGS(__VA_ARGS__))(__VA_ARGS__))
|
||||
|
||||
/* reg_str(n) — Stringify an integer register id into the GCC asm
|
||||
* string form (e.g. 12 → "$12"). Use this anywhere GCC's parser
|
||||
* expects a literal string identifying a register: clobber lists,
|
||||
* asm templates, etc. The two-level macro is the standard preprocessor
|
||||
* idiom for forcing one level of expansion before stringify — without
|
||||
* it, `#n` would stringify the macro name `R_T4` to `"R_T4"` instead
|
||||
* of expanding `R_T4` to its value first.
|
||||
*
|
||||
* For declaring a register variable bound to a specific GPR, use the
|
||||
* `rgcc(n)` bundle from gcc_asm.h instead — it adds the `__asm__()`
|
||||
* qualifier around the string.
|
||||
*
|
||||
* register V3_S2* p0 __asm__(reg_str(R_T4)) = ...; // verbose
|
||||
* 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)
|
||||
|
||||
/* ------------------------------------------------------------------------ *
|
||||
* rgcc(n) — GCC-specific bundle for register-variable declarations.
|
||||
*
|
||||
* Produces `__asm__(reg_str(tmpl(n, Code)))` at expansion time.
|
||||
* The `tmpl(n, Code)` 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;
|
||||
* which (via reg_str) becomes: register V3_S2* p __asm__("$12") = verts[0].ptr;
|
||||
*
|
||||
* Why bundle the `__asm__()` wrapper?
|
||||
* - The integer R_T4 (= 12, via R_T4_Code) already indicates the register.
|
||||
* - The string "$12" is derived from it via reg_str, so they cannot drift apart.
|
||||
* - Spelling `__asm__(reg_str(R_T4_Code))` at every call site is noise.
|
||||
*
|
||||
* tmpl defined in dsl.h (the token-paste glue).
|
||||
* rgcc define here (gcc_asm.h) because the `__asm__` keyword is GCC-specific.
|
||||
* Anyone porting to a different compiler's asm dialect overrides rgcc,
|
||||
* and the integer→string derivation in rlit can be retargeted in one place.
|
||||
*
|
||||
* 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)
|
||||
|
||||
/* rgcc_ref(n) — GCC operand-reference form "%N". Not currently used
|
||||
* by the placeholder-pun macros (the .word bodies are fully baked
|
||||
* at compile time and have no runtime operand references), but kept
|
||||
* here for completeness in case a future asm template needs to refer
|
||||
* to a runtime input by position. Mirror of rgcc but produces "%N"
|
||||
* instead of "$N". */
|
||||
#define rgcc_ref_(n) "%" #n
|
||||
#define rgcc_ref(n) rgcc_ref_(n)
|
||||
/* --- Register Constraint Aliases (for Pinned Variables) --- */
|
||||
|
||||
#define r_use(var) "r"(var) /* Input: Tells GCC we are reading this pinned register */
|
||||
#define r_set(var) "=r"(var) /* Output: Tells GCC we are overwriting this pinned register */
|
||||
#define r_mod(var) "+r"(var) /* Modify: Tells GCC we are reading AND writing this pinned register */
|
||||
#pragma endregion Cruft
|
||||
|
||||
+49
-47
@@ -271,6 +271,9 @@ enum { _C2_OPS_ = 0
|
||||
* - rd: COP2 control register index (0..31) */
|
||||
#define enc_gte_tx(sub, rt, rd) (enc_op(op_cop2) | enc_rs(sub) | enc_rt(rt) | enc_rd(rd))
|
||||
|
||||
#define gte_mt(rt, rd) enc_gte_tx(cop_mt, (rt), (rd)) /* Move GPR (rt) to GTE Control Register (rd) */
|
||||
#define gte_mf(rt, rd) enc_gte_tx(cop_mf, (rt), (rd)) /* Move GTE Control Register (rd) to GPR (rt) */
|
||||
|
||||
/* COP2 Data Load (lwc2): `lwc2 rt, off(rs)`
|
||||
* Layout: [op_lwc2:6][rs:5][rt:5][imm:16]
|
||||
* - rs: GPR base address
|
||||
@@ -332,7 +335,7 @@ enum { _C2_OPS_ = 0
|
||||
*
|
||||
* 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.
|
||||
@@ -401,12 +404,12 @@ enum {
|
||||
GTE_Z_Offset = 4
|
||||
};
|
||||
|
||||
#define gte_load_word_v0(base) enc_gte_lw(gte_in_v0_xy, (base), 0)
|
||||
#define gte_load_word_v0z(base) enc_gte_lw(gte_in_v0_z, (base), GTE_Z_Offset)
|
||||
#define gte_load_word_v1(base) enc_gte_lw(gte_in_v1_xy, (base), 0)
|
||||
#define gte_load_word_v1z(base) enc_gte_lw(gte_in_v1_z, (base), GTE_Z_Offset)
|
||||
#define gte_load_word_v2(base) enc_gte_lw(gte_in_v2_xy, (base), 0)
|
||||
#define gte_load_word_v2z(base) enc_gte_lw(gte_in_v2_z, (base), GTE_Z_Offset)
|
||||
#define gte_lw_v0(base) enc_gte_lw(gte_in_v0_xy, (base), 0)
|
||||
#define gte_lw_v0z(base) enc_gte_lw(gte_in_v0_z, (base), GTE_Z_Offset)
|
||||
#define gte_lw_v1(base) enc_gte_lw(gte_in_v1_xy, (base), 0)
|
||||
#define gte_lw_v1z(base) enc_gte_lw(gte_in_v1_z, (base), GTE_Z_Offset)
|
||||
#define gte_lw_v2(base) enc_gte_lw(gte_in_v2_xy, (base), 0)
|
||||
#define gte_lw_v2z(base) enc_gte_lw(gte_in_v2_z, (base), GTE_Z_Offset)
|
||||
|
||||
/* gte_load_vN(r_ptr, base) — placeholder-punned lwc2 loaders
|
||||
*
|
||||
@@ -445,26 +448,23 @@ 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( \
|
||||
asm_words( gte_load_word_v0(base), gte_load_word_v0z(base) ) \
|
||||
, "r"(r_ptr) \
|
||||
asm_clobber: rlit(R_V0_Code), rlit(R_T0_Code), rlit(R_T1_Code), rlit(R_RA_Code), "memory" \
|
||||
)
|
||||
#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 \
|
||||
)
|
||||
|
||||
#define gte_load_v1(r_ptr, base) \
|
||||
asm volatile( \
|
||||
asm_words( gte_load_word_v1(base), gte_load_word_v1z(base) ) \
|
||||
, "r"(r_ptr) \
|
||||
asm_clobber: rlit(R_V0_Code), rlit(R_T0_Code), rlit(R_T1_Code), rlit(R_RA_Code), "memory" \
|
||||
)
|
||||
#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 \
|
||||
)
|
||||
|
||||
#define gte_load_v2(r_ptr, base) \
|
||||
asm volatile( \
|
||||
asm_words( gte_load_word_v2(base), gte_load_word_v2z(base) ) \
|
||||
, "r"(r_ptr) \
|
||||
asm_clobber: rlit(R_V0_Code), rlit(R_T0_Code), rlit(R_T1_Code), rlit(R_RA_Code), "memory" \
|
||||
)
|
||||
#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 \
|
||||
)
|
||||
|
||||
/* gte_load_v0v1v2(p0, p1, p2, b0, b1, b2) — the canonical prelude to gte_cmd_rtpt.
|
||||
*
|
||||
@@ -478,14 +478,15 @@ enum {
|
||||
* 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_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( rlit(R_V0_Code), rlit(R_T0_Code), rlit(R_T1_Code), rlit(R_RA_Code), "memory" ) \
|
||||
)
|
||||
#define gte_load_v0v1v2(p0, p1, p2, b0, b1, b2) asm volatile( \
|
||||
asm_words( \
|
||||
gte_lw_v0(b0), gte_lw_v0z(b0), \
|
||||
gte_lw_v1(b1), gte_lw_v1z(b1), \
|
||||
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 \
|
||||
)
|
||||
|
||||
/**
|
||||
* @brief Rotate, Translate and Perspective Triple (23 cycles)
|
||||
@@ -623,19 +624,20 @@ enum {
|
||||
* get stale-RT2x/RT3x artifacts in RTPS/RTPT/MVMVA output.
|
||||
*/
|
||||
#define asm_gte_matrix_set_rotation(r0) \
|
||||
asm volatile(asm_words( \
|
||||
load_imm(R_T4, r0, 0) \
|
||||
, load_imm(R_T5, r0, 4) \
|
||||
, enc_cop2_tx(cop_mt, R_T4, 0) \
|
||||
, enc_cop2_tx(cop_mt, R_T5, 1) \
|
||||
, load_imm(R_T4, r0, 8) \
|
||||
, load_imm(R_T5, r0, 12) \
|
||||
, load_imm(R_T6, r0, 16) \
|
||||
, enc_cop2_tx(cop_mt, R_T4, 2) \
|
||||
, enc_cop2_tx(cop_mt, R_T5, 3) \
|
||||
, enc_cop2_tx(cop_mt, R_T6, 4) \
|
||||
asm volatile( \
|
||||
asm_words( \
|
||||
load_word(R_T5, R_T4, 0) \
|
||||
, load_word(R_T6, R_T4, 4) \
|
||||
, gte_mt( R_T5, 0) \
|
||||
, gte_mt( 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_mt( R_T5, 2) \
|
||||
, gte_mt( R_T6, 3) \
|
||||
, gte_mt( R_T4, 4) \
|
||||
) \
|
||||
asm_clobber: clb_system, reg_str(R_T4_Code), reg_str(R_T5_Code), reg_str(R_T6_Code) \
|
||||
: \
|
||||
: "r"(r0) \
|
||||
, r_use(r0) \
|
||||
asm_clobber: clb_system, rlit(R_T4_Code), rlit(R_T5_Code), rlit(R_T6_Code) \
|
||||
)
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
assert((point) <= (end)); \
|
||||
} while(0)
|
||||
|
||||
inline U4 align_pow2(U4 x, U4 b) {
|
||||
I_ U4 align_pow2(U4 x, U4 b) {
|
||||
assert(b != 0);
|
||||
assert((b & (b - 1)) == 0); // Check power of 2
|
||||
return ((x + b - 1) & (~(b - 1)));
|
||||
|
||||
+51
-56
@@ -80,6 +80,7 @@ enum {
|
||||
, rtmp_2 = R_T2 /* Temporary (Caller saved) */
|
||||
, rtmp_3 = R_T3 /* Temporary (Caller saved) */
|
||||
, rtmp_4 = R_T4 /* Temporary (Caller saved) — common GTE base pointer */
|
||||
, rtmp_9 = R_T9 /* Temporary (Caller saved) — common GTE base pointer */
|
||||
, rstatic_0 = R_S0 /* Static (Callee saved, preserved across calls) */
|
||||
, rstatic_1 = R_S1
|
||||
, rstatic_2 = R_S2
|
||||
@@ -165,7 +166,7 @@ enum {
|
||||
/* --- Coprocessor 0 (System Control & Exceptions) --- */
|
||||
|
||||
, cop_mf = 0x00 /* Move From Coprocessor */
|
||||
, cop_mt = 0x04 /* Move To Coprocessor */
|
||||
, cop_mt = 0x04 /* Move To Coprocessor */
|
||||
};
|
||||
|
||||
|
||||
@@ -231,7 +232,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))
|
||||
@@ -240,10 +241,10 @@ enum { _BitOffsets = 0
|
||||
#define load_half_u(rt, base, off) enc_i(op_lhu, (base), (rt), (off))
|
||||
#define store_word(rt, base, off) enc_i(op_sw, (base), (rt), (off))
|
||||
#define add_ui(rt, rs, imm) enc_i(op_addiu, (rs), (rt), (imm))
|
||||
#define andi_op(rt, rs, imm) enc_i(op_andi, (rs), (rt), (imm))
|
||||
#define ori_op(rt, rs, imm) enc_i(op_ori, (rs), (rt), (imm))
|
||||
#define xori_op(rt, rs, imm) enc_i(op_xori, (rs), (rt), (imm))
|
||||
#define lui_op(rt, imm) enc_i(op_lui, R_0, (rt), (imm))
|
||||
#define and_si(rt, rs, imm) enc_i(op_andi, (rs), (rt), (imm))
|
||||
#define or_i(rt, rs, imm) enc_i(op_ori, (rs), (rt), (imm))
|
||||
#define xor_i(rt, rs, imm) enc_i(op_xori, (rs), (rt), (imm))
|
||||
#define load_ui(rt, imm) enc_i(op_lui, R_0, (rt), (imm))
|
||||
|
||||
/* Shift family (R-type). shift_ll/lr/ra: `sll rd, rt, shamt` */
|
||||
#define shift_ll(rd, rt, shamt) enc_r(op_special, R_0, (rt), (rd), (shamt), fc_sll)
|
||||
@@ -332,7 +333,7 @@ enum { _BitOffsets = 0
|
||||
#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 shift_amount(rd, rt, n) shift_ll(rd, rt, n)
|
||||
|
||||
/* nop — canonical sll $0, $0, 0 */
|
||||
#define nop shift_ll(rdiscard, rdiscard, 0)
|
||||
@@ -360,48 +361,44 @@ enum { _BitOffsets = 0
|
||||
*
|
||||
* 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_words(lui_op((rt), _li2_hi_), \
|
||||
add_si((rt), (rt), (S2)(U2)_li2_lo_)) \
|
||||
asm_clobber(rlit(R_AT_Code), "memory") \
|
||||
); \
|
||||
} \
|
||||
else { \
|
||||
asm volatile( \
|
||||
asm_words(lui_op((rt), _li2_hi_), \
|
||||
ori_op((rt), (rt), (U2)_li2_lo_)) \
|
||||
asm_clobber(rlit(R_AT_Code), "memory") \
|
||||
); \
|
||||
} \
|
||||
#define load_imm_2w(rt, imm) do { \
|
||||
if (u4_low(imm) <= 0x7FFFU) { \
|
||||
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 \
|
||||
); \
|
||||
} \
|
||||
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 \
|
||||
); \
|
||||
} \
|
||||
} 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_words(lui_op((rt), _li2o_imm_ >> 16), \
|
||||
ori_op((rt), (rt), (U2)(_li2o_imm_ & 0xFFFFU))) \
|
||||
asm_clobber(rlit(R_AT_Code), "memory") \
|
||||
); \
|
||||
#define load_imm_2w_ori(rt, imm) do { \
|
||||
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 \
|
||||
); \
|
||||
} 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_words(lui_op((rt), _li2a_imm_ >> 16), \
|
||||
add_si((rt), (rt), (S2)(U2)(_li2a_imm_ & 0xFFFFU))) \
|
||||
asm_clobber(rlit(R_AT_Code), "memory") \
|
||||
); \
|
||||
#define load_imm_2w_addi(rt, imm) do { \
|
||||
/*U4 _li2a_imm_ = (U4)(imm);*/ \
|
||||
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 \
|
||||
); \
|
||||
} while (0)
|
||||
|
||||
/* load_imm rt, imm — true `li` semantics (assembler `li` pseudo)
|
||||
@@ -424,18 +421,18 @@ enum { _BitOffsets = 0
|
||||
* 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)) { \
|
||||
if (cexpr_(imm) && ((imm) <= 0x7FFFU)) { \
|
||||
/* Small positive: addi rt, $0, imm */ \
|
||||
asm volatile( \
|
||||
asm_words(add_si((rt), R_0, (imm))) \
|
||||
asm_clobber(rlit(R_AT_Code), "memory") \
|
||||
asm_clobber: rlit(R_AT_Code), clb_mem_drain \
|
||||
); \
|
||||
} \
|
||||
else if (__builtin_constant_p(imm) && ((U4)(imm) <= 0xFFFFU)) { \
|
||||
else if (cexpr_(imm) && ((U4)(imm) <= 0xFFFFU)) { \
|
||||
/* 0x8000..0xFFFF: ori rt, $0, imm (zero-extends) */ \
|
||||
asm volatile( \
|
||||
asm_words(ori_op((rt), R_0, (imm))) \
|
||||
asm_clobber(rlit(R_AT_Code), "memory") \
|
||||
asm_words(or_i((rt), R_0, (imm))) \
|
||||
asm_clobber: rlit(R_AT_Code), clb_mem_drain \
|
||||
); \
|
||||
} \
|
||||
else \
|
||||
@@ -443,20 +440,18 @@ enum { _BitOffsets = 0
|
||||
/* > 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) { \
|
||||
if (u4_lo(imm) <= 0x7FFFU) { \
|
||||
asm volatile(asm_words( \
|
||||
lui_op((rt), _li_hi_), \
|
||||
add_si((rt), (rt), (S2)(U2)_li_lo_)) \
|
||||
asm_clobber(rlit(R_AT_Code), "memory") \
|
||||
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 \
|
||||
); \
|
||||
} \
|
||||
else { \
|
||||
asm volatile(asm_words( \
|
||||
lui_op((rt), _li_hi_), ori_op((rt), (rt), (U2)_li_lo_)) \
|
||||
asm_clobber: rlit(R_AT_Code), "memory" \
|
||||
load_ui((rt), u4_hi(imm)), \
|
||||
or_i((rt), (rt), C_(U2,u4_lo(imm)) \
|
||||
asm_clobber: rlit(R_AT_Code), clb_mem_drain \
|
||||
); \
|
||||
} \
|
||||
} \
|
||||
@@ -465,7 +460,7 @@ enum { _BitOffsets = 0
|
||||
// Binary Metaprogramming
|
||||
|
||||
typedef U4 const Code;
|
||||
#define CodeBlob_(sym) tmpl(codeblob,sym) [] align_(4) =
|
||||
#define CodeBlob_(sym) tmpl(code,sym) [] align_(4) =
|
||||
|
||||
enum {
|
||||
bios_flushcache = 0x44,
|
||||
@@ -495,13 +490,13 @@ Code CodeBlob_(mips_flush_icache) {
|
||||
, jump_reg(rret_addr) /* jr $ra */
|
||||
, add_ui(rstack_ptr, rstack_ptr, 8) /* sp += 8 (BD) */
|
||||
};
|
||||
FI_ void mips_flush_icache(void) { C_(VoidFn*, codeblob_mips_flush_icache)(); }
|
||||
FI_ void mips_flush_icache(void) { C_(VoidFn*, code_mips_flush_icache)(); }
|
||||
|
||||
/* Standard clobber list for pure-MIPS asm volatile blocks: caller-saved
|
||||
* 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), "memory"
|
||||
#define clb_system rlit(R_V0_Code), rlit(R_T0_Code), rlit(R_T1_Code), rlit(R_RA_Code), clb_mem_drain
|
||||
|
||||
#define asm_mips_flush_icache() asm volatile( asm_words( \
|
||||
add_ui(rstack_ptr, rstack_ptr, -8) \
|
||||
|
||||
Reference in New Issue
Block a user