mirror of
https://github.com/Ed94/pikuma_ps1.git
synced 2026-07-16 14:21:26 -07:00
review pass on c code.
This commit is contained in:
+7
-10
@@ -6,8 +6,6 @@
|
||||
* The metaprogram (scripts/passes/annotation.lua) reads source-as-written and validates:
|
||||
* - atom_info(...) shape: up to three sub-calls (atom_bind(Binds_X), atom_reads(...), atom_writes(...)) in any order and are optional.
|
||||
* - rbind atoms (atom_info(..., atom_bind(Binds_X), ...)) reference a real Binds_* struct declaration.
|
||||
* - wave-context positions only reference the 4-register set: R_PrimCursor / R_FaceCursor / R_VertBase / R_OtBase.
|
||||
* Note(Ed): Not sure if I'll generlaize this later.
|
||||
* - atom word-counts in word_counts.metadata.h match the body's actual .word count.
|
||||
*
|
||||
* Pure macro anntation.
|
||||
@@ -48,13 +46,13 @@
|
||||
* ----------------
|
||||
* 1. atom_info(...) is OPTIONAL. Atoms without atom_info are silently skipped by the metaprogram.
|
||||
* 2. If present, atom_info takes up to three sub-calls, all order-independent within the arg list:
|
||||
* - atom_bind(Binds_X) (binds a struct context to an atom).
|
||||
* - atom_reads(...) (context registers)
|
||||
* - atom_writes(...) (context registers)
|
||||
* - atom_bind(Binds_X)
|
||||
* - atom_reads(...)
|
||||
* - atom_writes(...)
|
||||
* 3. atom_bind(Binds_X): metaprogram cross-references Binds_X against the `typedef struct Binds_X { ... } Binds_X;` declaration.
|
||||
* 4. atom_reads(...) and atom_writes(...) used to to check if registers are used correctly in macros: R_PrimCursor / R_FaceCursor / R_VertBase / R_OtBase.
|
||||
* 5. atom_label(name) utilize with atom_offset as a target location.
|
||||
* 6. atom_offset(F, T) is resolved by gen/atom_offsets.h, generated from the atom_label markers. Calculated during the offset pass of the lua metaprogram.
|
||||
* 4. atom_reads(...) and atom_writes(...): Used to to check if registers are used correctly in macros: R_PrimCursor / R_FaceCursor / R_VertBase / R_OtBase.
|
||||
* 5. atom_label(name: Utilize with atom_offset as a target location.
|
||||
* 6. atom_offset(F, T): Resolved by gen/atom_offsets.h, generated from the atom_label markers. Calculated during the offset pass of the lua metaprogram.
|
||||
*/
|
||||
|
||||
#ifdef INTELLISENSE_DIRECTIVES
|
||||
@@ -116,8 +114,7 @@
|
||||
* The metaprogram generates gen/atom_offsets.h with one #define with the offset value per atom_offset(F, T) call.
|
||||
* The preprocessor then expands the call to the right immediate value.
|
||||
*
|
||||
* If gen/atom_offsets.h is stale (or atom_label(name) is undefined), `atom_offset_F_T`
|
||||
* becomes an undefined macro and the C build fails.
|
||||
* If gen/atom_offsets.h is stale (or atom_label(name) is undefined), `atom_offset_F_T` becomes an undefined macro and the C build fails.
|
||||
* ============================================================================*/
|
||||
#define atom_offset(F, T) atom_offset_ ## F ## _ ## T
|
||||
// atom_label is a pure annotation for the metaprogram's offset calculations.
|
||||
|
||||
+37
-64
@@ -1,7 +1,6 @@
|
||||
/* ============================================================================
|
||||
* duffle DSL Suffix Conventions
|
||||
* ============================================================================
|
||||
*
|
||||
* Every mnemonic in this header follows the same suffix grammar:
|
||||
*
|
||||
* Primitive commands: gp0_cmd_poly_f3 = 0x20 (byte opcode)
|
||||
@@ -26,8 +25,7 @@
|
||||
* 0. Opcode IDs gp0_cmd_poly_f3 = 0x20
|
||||
*
|
||||
* Vendor mnemonics (gte_mtc2, gte_mfc2, etc.) are NOT in this header.
|
||||
* They live in the opt-in `gp_vendor_sym.h` for users who prefer the
|
||||
* PSYQ-style names.
|
||||
* They live in the opt-in `gp_vendor_sym.h` for users who prefer the PSYQ-style names.
|
||||
* ============================================================================ */
|
||||
|
||||
#ifdef INTELLISENSE_DIRECTIVES
|
||||
@@ -41,15 +39,14 @@
|
||||
/* ============================================================================
|
||||
* Hardware MMIO Addresses
|
||||
* ============================================================================
|
||||
*
|
||||
* PSX GPU has two 32-bit ports in the I/O register region at KSEG2
|
||||
* 0x1F800000+. GP0 (offset 0x10) is the data port (commands + params).
|
||||
* GP1 (offset 0x14) is the control port (status, ctrl writes).
|
||||
* ============================================================================ */
|
||||
/* IO base address (KSEG2 0x1F800000+ for the I/O register region).
|
||||
* The 16-bit upper half `IO_BASE_ADDR_HI16` is the form used by
|
||||
* tape-side macros that pin a register to hold the IO base and access
|
||||
* ports via offsets — `lui $reg, 0x1F80` (1 word) then `sw $data, GPIO_PORT*_OFFSET($reg)` (1 word).
|
||||
* The 16-bit upper half `IO_BASE_ADDR_HI16` is the form used by tape-side macros that pin a register
|
||||
* to hold the IO base and access ports via offsets:
|
||||
* `lui $reg, 0x1F80` (1 word) then `sw $data, GPIO_PORT*_OFFSET($reg)` (1 word).
|
||||
* Mirrors the `IO_BASE_ADDR equ 0x1F80` + `gpio_port0 equ 0x1810` pattern from graphics_hello/gp.s. */
|
||||
enum {
|
||||
IO_BASE_ADDR = 0x1F800000, /* full 32-bit I/O region base */
|
||||
@@ -75,12 +72,10 @@ enum {
|
||||
/* ============================================================================
|
||||
* GP0 command byte constants + Layer 1 (GPU bitfield shifts)
|
||||
* ============================================================================
|
||||
*
|
||||
* 8-bit GP0 opcodes (the upper byte of a primitive's first word). These are the BYTE only.
|
||||
* The layer-1 bitfield-layout constants live in the same enum block
|
||||
* so the encoder can reference them by name.
|
||||
* NO macro body past this point uses a raw shift or raw mask.
|
||||
* Every shift/width/mask is named here, named once.
|
||||
* The layer-1 bitfield-layout constants live in the same enum block so the encoder can reference them by name.
|
||||
* NO macro body past this point uses a raw shift or raw mask.
|
||||
* Every shift/width/mask is named here, named once.
|
||||
* Mirrors the OPCODE_SHIFT / RS_SHIFT / REG_MASK convention from mips.h.
|
||||
* ============================================================================ */
|
||||
enum {
|
||||
@@ -143,9 +138,7 @@ enum {
|
||||
/* ============================================================================
|
||||
* Layer 1.5 (per-field encoders) + Layer 2 (composite) + Layer 3 (semantic GP0 word builders)
|
||||
* ============================================================================
|
||||
*
|
||||
* Layer 1.5 encoders take one field's value, mask it to its own width,
|
||||
* and shift it to its own position.
|
||||
* Layer 1.5 encoders take one field's value, mask it to its own width, and shift it to its own position.
|
||||
* Mirrors `enc_op` / `enc_rs` / `enc_rt` in mips.h and `enc_gte_sf` / `enc_gte_mx` in gte.h.
|
||||
* Layer-2 composite encoders OR the per-field encoders together; layer-3 semantic macros delegate to the composites.
|
||||
* No raw shifts or magic numbers in any macro body below this point.
|
||||
@@ -186,10 +179,9 @@ enum {
|
||||
/* ============================================================================
|
||||
* GP1 command byte constants + Layer 1 (display-mode + range + draw-area bitfield shifts)
|
||||
* ============================================================================
|
||||
*
|
||||
* GP1 status bits are read from HW_GP1; ctrl writes use GP1 commands
|
||||
* packed into 32-bit words (cmd byte in the upper 8 bits via
|
||||
* `enc_gp0_cmd(cmd)` — never a raw shift).
|
||||
* GP1 status bits are read from HW_GP1;
|
||||
* ctrl writes use GP1 commands packed into 32-bit words
|
||||
* (cmd byte in the upper 8 bits via `enc_gp0_cmd(cmd)`).
|
||||
* ============================================================================ */
|
||||
enum {
|
||||
gp1_cmd_Reset = 0x00,
|
||||
@@ -202,10 +194,9 @@ enum {
|
||||
gp1_cmd_VerticalDisplayRange = 0x07,
|
||||
gp1_cmd_DisplayMode = 0x08,
|
||||
/* Note: GP1 only has commands 0x00..0x08.
|
||||
* The state-setter commands (SetTextureWindow, * SetDrawArea*,
|
||||
* SetDrawOffset, SetMaskBit) live in the GP0 enum as * 0xE1..0xE6.
|
||||
* DrawArea word builders are below as GP0s * macros
|
||||
* (since they emit GP0 commands). */
|
||||
* The state-setter commands (SetTextureWindow, * SetDrawArea*, SetDrawOffset, SetMaskBit)
|
||||
* live in the GP0 enum as * 0xE1..0xE6.
|
||||
* DrawArea word builders are below as GP0s * macros (since they emit GP0 commands). */
|
||||
|
||||
/* ---- Display-mode payload flags (per PSX-SPX §"GP1 Display Mode").
|
||||
* Bit positions match the encoder shifts below; values are the
|
||||
@@ -259,8 +250,7 @@ enum {
|
||||
#define enc_gp1_vrange_word(y1, y2) (enc_gp0_cmd(gp1_cmd_VerticalDisplayRange) | enc_gp1_vrange_y1(y1) | enc_gp1_vrange_y2(y2))
|
||||
|
||||
/* ---- Layer 2: GP0 state-setter composite encoders ----
|
||||
* GP0(0xE3) SetDrawArea top-left and GP0(0xE4) SetDrawArea bottom-right
|
||||
* both use the same X/Y 10-bit signed payload as GP1 DisplayRange. */
|
||||
* GP0(0xE3) SetDrawArea top-left and GP0(0xE4) SetDrawArea bottom-right both use the same X/Y 10-bit signed payload as GP1 DisplayRange. */
|
||||
#define enc_gp0_draw_area_tl_word(x, y) (enc_gp0_cmd(gp0_cmd_SetDrawArea_TopLeft) | enc_gp1_draw_x(x) | enc_gp1_draw_y(y))
|
||||
#define enc_gp0_draw_area_br_word(x, y) (enc_gp0_cmd(gp0_cmd_SetDrawArea_BotRight) | enc_gp1_draw_x(x) | enc_gp1_draw_y(y))
|
||||
|
||||
@@ -282,7 +272,6 @@ enum {
|
||||
/* ============================================================================
|
||||
* Pre-baked GPU state words
|
||||
* ============================================================================
|
||||
*
|
||||
* Common command words for boot-time GPU init and standard display configurations.
|
||||
* ============================================================================ */
|
||||
|
||||
@@ -356,7 +345,6 @@ enum {
|
||||
/* ============================================================================
|
||||
* Primitive structs (8 polygon variants + tag)
|
||||
* ============================================================================
|
||||
*
|
||||
* Each struct follows the GPU-documented memory layout for the corresponding primitive command.
|
||||
* The PolyTag is the OT-link header; the rest of the struct is the primitive's body.
|
||||
*
|
||||
@@ -390,9 +378,9 @@ typedef Struct_(PolyTag) {
|
||||
* No raw C-style casts. RHS values are assumed to be `U4` — caller passes a `U4` directly. */
|
||||
#define set_len(tag,v) (C_(PolyTag_R,tag)->len = u4_(v))
|
||||
#define set_addr(tag,v) (C_(PolyTag_R,tag)->addr = u4_(v))
|
||||
/* `set_code` is no longer in the new PolyTag design — the code byte lives
|
||||
* in the primitive body (e.g. `((Poly_F3*)(p))->code`), not in the tag.
|
||||
* Use the typed primitive structs (Poly_F3, Poly_G4, etc.) and the `set_poly_*` setters,
|
||||
/* `set_code` is no longer in the new PolyTag design — the code byte lives in the primitive body
|
||||
* (e.g. `((Poly_F3*)(p))->code`), not in the tag.
|
||||
* Use the typed primitive structs (Poly_F3, Poly_G4, etc.) and the `set_poly_*` setters,
|
||||
* which set both the tag's length and the code. */
|
||||
#define get_len(tag) C_(U4,C_(PolyTag_R,tag)->len)
|
||||
#define get_addr(tag) C_(U4,C_(PolyTag_R,tag)->addr)
|
||||
@@ -511,7 +499,6 @@ typedef Struct_(Poly_GT4) {
|
||||
/* ============================================================================
|
||||
* Texture Page (TPage) bit layout
|
||||
* ============================================================================
|
||||
*
|
||||
* The TPage data word sent via GP0(0x2X) has:
|
||||
* bits 0..3 = texture page X (4 bits, 64-px units, 0..16)
|
||||
* bit 4 = texture page Y (1 bit, 64-px units, 0/1)
|
||||
@@ -575,7 +562,6 @@ typedef Struct_(TexturePage) { U4 raw; };
|
||||
/* ============================================================================
|
||||
* CLUT (Color Look-Up Table) semantics
|
||||
* ============================================================================
|
||||
*
|
||||
* CLUT is loaded into VRAM by sending a GP0 command whose payload is:
|
||||
* bits 0..5 = Y in 16-px units (palette row)
|
||||
* bits 6..14 = X in 16-px units (palette column)
|
||||
@@ -608,7 +594,6 @@ enum {
|
||||
/* ============================================================================
|
||||
* TIM file format constants and headers
|
||||
* ============================================================================
|
||||
*
|
||||
* TIM (Sony .TIM texture image) file structure:
|
||||
* +0x00 U4 file_id (always 0x10 = TIM magic)
|
||||
* +0x04 U4 version (always 0x00 for v1)
|
||||
@@ -626,9 +611,8 @@ enum {
|
||||
* +0x06 U2 px_height
|
||||
* +0x08 ... pixel data
|
||||
*
|
||||
* Future?: add `tim_load_to_vram(tim_ptr, vram_addr)` that
|
||||
* emits the necessary GP0 commands. Stoppped for now at the
|
||||
* struct + enum level.
|
||||
* Future?: add `tim_load_to_vram(tim_ptr, vram_addr)` that emits the necessary GP0 commands.
|
||||
* Stoppped for now at the struct + enum level.
|
||||
* ============================================================================ */
|
||||
enum {
|
||||
tim_file_id_magic = 0x10,
|
||||
@@ -659,35 +643,24 @@ typedef Struct_(TIM_SectionHeader) {
|
||||
* Tape-side GPU operations (NOT in this header)
|
||||
* ============================================================================
|
||||
*
|
||||
* No `mac_gp0_send` or related macros live in gp.h. Rationale: the
|
||||
* Lottes tape model uses OT-DMA for primitive submission, so atom bodies
|
||||
* write to main RAM (the OT/primitive buffer) and to GTE state — never
|
||||
* directly to the GPU ports at 0x1F801810 / 0x1F801814. See
|
||||
* `mac_format_f3_color`, `mac_insert_ot_tag`, `mac_gte_store_f3` in
|
||||
* lottes_tape.h for the patterns atom bodies actually use.
|
||||
* No `mac_gp0_send` or related macros live in gp.h.
|
||||
* Rationale: the Lottes tape model uses OT-DMA for primitive submission, so atom bodies write to main RAM (the OT/primitive buffer)
|
||||
* and to GTE state — never directly to the GPU ports at 0x1F801810 / 0x1F801814.
|
||||
* See `mac_format_f3_color`, `mac_insert_ot_tag`, `mac_gte_store_f3` in lottes_tape.h for the patterns atom bodies actually use.
|
||||
*
|
||||
* If a feature need arises requires tape-side GPU port writes (e.g. DMA-kick to
|
||||
* start GPU consumption of the OT, VBlank sync via GP1 status poll),
|
||||
* the right home is `lottes_tape.h` alongside the rest of the `mac_*`
|
||||
* family — the encoder infrastructure is already in place:
|
||||
* If a feature need arises requires tape-side GPU port writes
|
||||
* (e.g. DMA-kick to start GPU consumption of the OT, VBlank sync via GP1 status poll),
|
||||
* the right home is `lottes_tape.h` alongside the rest of the `mac_*` family:
|
||||
* 1. The caller pins a register to hold the IO base, e.g. register U4 r_io rgcc(R_T4) = IO_BASE_ADDR;
|
||||
* The compiler emits `lui R_T4, IO_BASE_ADDR_HI16` outside the atom body (in the C prologue before tape_run).
|
||||
* 2. The atom body uses `store_word(R_data, R_T4, GPIO_PORT0_OFFSET)` to write to GP0, and `store_word(R_data, R_T4, GPIO_PORT1_OFFSET)`
|
||||
* to write to GP1. Both are preprocessor-encodable because R_T4 is a fixed register and the GPIO_PORT*_OFFSET constants
|
||||
* fit in the `sw`'s 16-bit signed offset field. No placeholder-pun, no asm constraints, no hidden register choice.
|
||||
* Same pattern as the old graphics_hello/hello_gp_routines.s `reg_io_offset`/`gcmd_push` convention.
|
||||
*
|
||||
* 1. The caller pins a register to hold the IO base, e.g.
|
||||
* register U4 r_io rgcc(R_T4) = IO_BASE_ADDR;
|
||||
* The compiler emits `lui R_T4, IO_BASE_ADDR_HI16` outside the
|
||||
* atom body (in the C prologue before tape_run).
|
||||
*
|
||||
* 2. The atom body uses `store_word(R_data, R_T4, GPIO_PORT0_OFFSET)`
|
||||
* to write to GP0, and `store_word(R_data, R_T4, GPIO_PORT1_OFFSET)`
|
||||
* to write to GP1. Both are preprocessor-encodable because R_T4 is
|
||||
* a fixed register and the GPIO_PORT*_OFFSET constants fit in the
|
||||
* `sw`'s 16-bit signed offset field. No placeholder-pun, no asm
|
||||
* constraints, no hidden register choice. Same pattern as the
|
||||
* old graphics_hello/hello_gp_routines.s `reg_io_offset`/`gcmd_push`
|
||||
* convention.
|
||||
*
|
||||
* This mirrors the existing tape-side wave-context discipline: the
|
||||
* caller binds the IO-base register via `rgcc()`, the macro assumes
|
||||
* the binding is in effect, and the encoding falls out at preprocessor
|
||||
* time. No additional GPU-domain macro layer required.
|
||||
* This mirrors the existing tape-side wave-context discipline:
|
||||
* the caller binds the IO-base register via `rgcc()`, the macro assumes the binding is in effect,
|
||||
* and the encoding falls out at preprocessor time.
|
||||
* No additional GPU-domain macro layer required.
|
||||
* ============================================================================ */
|
||||
#pragma endregion Tape-Side Macros
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
* duffle DSL — GPU Vendor Mnemonics (opt-in)
|
||||
* ============================================================================
|
||||
*
|
||||
* Provides the PSYQ-style CamelCase aliases for the canonical duffle GPU
|
||||
* primitive setters and OT operations. The duffle snake_case names are
|
||||
* primary; this header is for users who prefer the PSYQ SDK function
|
||||
* names from the legacy C API.
|
||||
* Provides the PSYQ-style CamelCase aliases for the canonical duffle GPU primitive setters and OT operations.
|
||||
* The duffle snake_case names are primary; this header is for users who prefer the PSYQ SDK function names from the legacy C API.
|
||||
*
|
||||
* USAGE: #include "duffle/gp_vendor_sym.h" // after gp.h
|
||||
*
|
||||
@@ -23,15 +21,11 @@
|
||||
* OT operations:
|
||||
* AddPrim(ot, p) -> orderingtbl_add_primitive(ot, p)
|
||||
*
|
||||
* The gp0_cmd_* / gp1_cmd_* byte constants are already short and
|
||||
* descriptive; no vendor alias is provided for them.
|
||||
*
|
||||
* The vendor mnemonics are NOT registered with the duffle word-count
|
||||
* metadata (word_counts.metadata.h). They expand to the duffle canonical
|
||||
* macros which DO have word-count entries (the ones emitted by
|
||||
* mac_format_f3_color / mac_gte_store_f3 / etc.). Verification: V13
|
||||
* (objdump byte-identical) holds.
|
||||
* The gp0_cmd_* / gp1_cmd_* byte constants are already short and descriptive; no vendor alias is provided for them.
|
||||
*
|
||||
* The vendor mnemonics are NOT registered with the duffle word-count metadata (word_counts.metadata.h).
|
||||
* They expand to the duffle canonical macros which DO have word-count entries
|
||||
* (the ones emitted by mac_format_f3_color / mac_gte_store_f3 / etc.). Verification: V13 (objdump byte-identical) holds.
|
||||
* ============================================================================ */
|
||||
|
||||
#ifdef INTELLISENSE_DIRECTIVES
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
* duffle DSL — GTE Vendor Mnemonics (opt-in)
|
||||
* ============================================================================
|
||||
*
|
||||
* Provides the textbook MIPS assembly mnemonics for the GTE/COP2
|
||||
* instructions as thin aliases to the canonical duffle macros in gte.h.
|
||||
* The duffle names are primary; this header is for users who prefer
|
||||
* the textbook mnemonics.
|
||||
* Provides the textbook MIPS assembly mnemonics for the GTE/COP2 instructions as thin aliases to the canonical duffle macros in gte.h.
|
||||
* The duffle names are primary; this header is for users who prefer the textbook mnemonics.
|
||||
*
|
||||
* USAGE: #include "duffle/gte_vendor_sym.h" // after gte.h
|
||||
*
|
||||
|
||||
@@ -16,14 +16,12 @@ typedef Slice_MipsCode MipsAtom;
|
||||
|
||||
#define MipsAtom_(sym) MipsCode sym [] align_(4) =
|
||||
|
||||
// Bare form: file-scope declaration with hardcoded body.
|
||||
// Used for components with no args (e.g., ac_load_tri_indices) or identifier-args (hardcoded register names).
|
||||
// MipsAtomComp_(ac_X) { body }
|
||||
// expands to:
|
||||
// MipsCode ac_X[] align_(4) = { body };
|
||||
#define MipsAtomComp_(sym) MipsCode sym [] align_(4) =
|
||||
|
||||
// Function form: function-body block that returns a MipsAtom slice.
|
||||
// Used for components with value-args (e.g., ac_format_f3_color).
|
||||
// FI_ MipsAtom ac_X(args) MipsAtomComp_Proc_(ac_X, { body })
|
||||
// expands to:
|
||||
@@ -109,8 +107,7 @@ FI_ Slice_U4 tb_slice(TapeBuilder tb) { return (Sli
|
||||
MipsAtomComp_(ac_yield) {
|
||||
load_word(R_AtomJmp, R_TapePtr, 0),
|
||||
add_ui_self( R_TapePtr, S_(MipsCode)),
|
||||
jump_reg( R_AtomJmp),
|
||||
nop,
|
||||
jump_reg( R_AtomJmp), nop,
|
||||
};
|
||||
|
||||
/* Words: 3; Loads 3 S2 indices from the face array */
|
||||
@@ -268,8 +265,7 @@ internal MipsAtom_(mips_flush_icache) {
|
||||
store_word(rret_addr, rstack_ptr, S_(U4)), // sw $ra, 4($sp)
|
||||
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
|
||||
jump_link(rtmp_0, rret_addr), nop, // jalr $t0, $ra, BD slot
|
||||
load_word(rret_addr, rstack_ptr, S_(U4)), // lw $ra, 4($sp)
|
||||
jump_reg(rret_addr), // jr $ra
|
||||
add_ui(rstack_ptr, rstack_ptr, MipsStackAlignment), // sp += 8 (BD)
|
||||
|
||||
+72
-111
@@ -1,38 +1,28 @@
|
||||
/* ============================================================================
|
||||
* duffle DSL Suffix Conventions
|
||||
* ============================================================================
|
||||
*
|
||||
* Every mnemonic in this header follows the same suffix grammar:
|
||||
*
|
||||
* _i Immediate value (16-bit constant operand). Combine with
|
||||
* _u or _s (single-letter modifier + type combined): add_ui,
|
||||
* add_si. Examples: add_ui, add_si, and_i, or_i, xor_i,
|
||||
* load_upper_i. and_i is sign-agnostic (andi zero-extends).
|
||||
* load_upper_i is a unique verb; _i is the immediate marker,
|
||||
* not a modifier+type combination.
|
||||
*
|
||||
* _u Unsigned (no-overflow, no-sign-extension). R-type
|
||||
* arithmetic examples: add_u, sub_u, mult_u, div_u. I-type
|
||||
* (combined with _i): add_ui.
|
||||
*
|
||||
* _s Signed (overflow-traps, sign-extends). R-type: add_s,
|
||||
* sub_s, mult_s, div_s, set_lt_s. I-type (combined with _i):
|
||||
* add_si.
|
||||
* _i: Immediate value (16-bit constant operand).
|
||||
* Combine with _u or _s (single-letter modifier + type combined): add_ui, add_si.
|
||||
* Examples: add_ui, add_si, and_i, or_i, xor_i, load_upper_i. and_i is sign-agnostic (andi zero-extends).
|
||||
* load_upper_i is a unique verb; _i is the immediate marker, not a modifier+type combination.
|
||||
* _u: Unsigned (no-overflow, no-sign-extension).
|
||||
* R-type arithmetic examples: add_u, sub_u, mult_u, div_u. I-type (combined with _i): add_ui.
|
||||
* _s: Signed (overflow-traps, sign-extends).
|
||||
* R-type: add_s, sub_s, mult_s, div_s, set_lt_s. I-type (combined with _i): add_si.
|
||||
*
|
||||
* --- Shift family (R-type): verb-modifier-direction ---
|
||||
* The shift macros use `shift_<modifier><direction>`. Modifier is
|
||||
* the single letter `l` (logical) or `a` (arithmetic). Direction
|
||||
* is the word `left` or `right`. Combined: `_lleft`, `_lright`,
|
||||
* `_aright`. Examples: shift_lleft( rd, rt, shamt) (= sll)
|
||||
* shift_lright(rd, rt, shamt) (= srl)
|
||||
* shift_aright(rd, rt, shamt) (= sra)
|
||||
* (no `_aleft`; MIPS has no `sla` — arithmetic-left is bit-identical
|
||||
* to logical-left, so use shift_lleft for that case)
|
||||
* The shift macros use `shift_<modifier><direction>`.
|
||||
* Modifier is the single letter `l` (logical) or `a` (arithmetic).
|
||||
* Direction is the word `left` or `right`. Combined: `_lleft`, `_lright`, `_aright`.
|
||||
* Examples: shift_lleft( rd, rt, shamt) (= sll)
|
||||
* shift_lright(rd, rt, shamt) (= srl)
|
||||
* shift_aright(rd, rt, shamt) (= sra)
|
||||
* (no `_aleft`; MIPS has no `sla` — arithmetic-left is bit-identical to logical-left, so use shift_lleft for that case)
|
||||
*
|
||||
* --- Jump/Call family ---
|
||||
* Simple jumps keep the original short names: jump (j), jump_reg
|
||||
* (jr), jump_link (jalr rs, rd). The jump-and-link-to variants
|
||||
* (jal, jalr rs with default $ra) get the `call_` verb instead:
|
||||
* Simple jumps keep the original short names: jump (j), jump_reg (jr), jump_link (jalr rs, rd).
|
||||
* The jump-and-link-to variants (jal, jalr rs with default $ra) get the `call_` verb instead:
|
||||
* call_addr (jal), call_reg (jalr rs, default $ra).
|
||||
* Examples: jump(off) (= j)
|
||||
* jump_reg(rs) (= jr)
|
||||
@@ -40,32 +30,22 @@
|
||||
* call_reg(rs) (= jalr rs, default $ra)
|
||||
* call_addr(off) (= jal)
|
||||
*
|
||||
* _r Register marker — used only when the register type needs
|
||||
* disambiguation (e.g., GTE data register vs control
|
||||
* register). NOT used in plain R-type arithmetic (the
|
||||
* R-type is implicit). Examples: gte_mv_to_data_r,
|
||||
* gte_mv_to_ctrl_r.
|
||||
* _r: Register marker — used only when the register type needs disambiguation (e.g., GTE data register vs control register).
|
||||
* NOT used in plain R-type arithmetic (the R-type is implicit). Examples: gte_mv_to_data_r, gte_mv_to_ctrl_r.
|
||||
* _self: Destination equals one source operand.
|
||||
* Examples: add_ui_self (I-type, to self), add_u_self (R-type, to self).
|
||||
* _mv_to_: Direction: data flows into X.
|
||||
* Example: gte_mv_to_data_r, gte_mv_to_ctrl_r.
|
||||
* _mv_from_: Direction: data flows out of X.
|
||||
* Example: gte_mv_from_data_r, gte_mv_from_ctrl_r.
|
||||
* _str: String-form — emits inline-asm string instead of `.word`.
|
||||
* Example: gte_rtpt_asm_str.
|
||||
* _2w / _1w: Word count of the emitted sequence.
|
||||
* Example: load_imm_2w.
|
||||
*
|
||||
* _self Destination equals one source operand.
|
||||
* Examples: add_ui_self (I-type, to self),
|
||||
* add_u_self (R-type, to self).
|
||||
*
|
||||
* _mv_to_ Direction: data flows into X.
|
||||
* Example: gte_mv_to_data_r, gte_mv_to_ctrl_r.
|
||||
*
|
||||
* _mv_from_ Direction: data flows out of X.
|
||||
* Example: gte_mv_from_data_r, gte_mv_from_ctrl_r.
|
||||
*
|
||||
* _str String-form — emits inline-asm string instead of `.word`.
|
||||
* Example: gte_rtpt_asm_str.
|
||||
*
|
||||
* _2w / _1w Word count of the emitted sequence.
|
||||
* Example: load_imm_2w.
|
||||
*
|
||||
* _cop2 RESERVED — DO NOT USE in macro names. The `gte_` namespace
|
||||
* prefix already implies coprocessor 2. Use `c2` only in:
|
||||
* (a) integer opcode enums (op_lwc2 = 0x32, op_swc2 = 0x3A)
|
||||
* (b) vendor-mnemonic macro aliases (gte_mtc2, gte_mfc2)
|
||||
* _cop2: RESERVED — DO NOT USE in macro names. The `gte_` namespace prefix already implies coprocessor 2. Use `c2` only in:
|
||||
* (a) integer opcode enums (op_lwc2 = 0x32, op_swc2 = 0x3A)
|
||||
* (b) vendor-mnemonic macro aliases (gte_mtc2, gte_mfc2)
|
||||
*
|
||||
* Primitive commands: gp0_cmd_poly_f3 = 0x20 (byte opcode)
|
||||
* Packed 32-bit cmd: gp0_word_poly_f3(r, g, b) (32-bit, shifted)
|
||||
@@ -80,9 +60,8 @@
|
||||
* gte_lw_v0_xy(base) (gte + lw + v0 + xy)
|
||||
* load_upper_i (load-upper + immediate, unique verb)
|
||||
*
|
||||
* Vendor mnemonics (sll, srl, sra, jr, j, jal, jalr) are NOT in this
|
||||
* header. They live in the opt-in `mips_vendor_sym.h` for users who
|
||||
* prefer the textbook MIPS assembly mnemonics.
|
||||
* Vendor mnemonics (sll, srl, sra, jr, j, jal, jalr) are NOT in this header.
|
||||
* They live in the opt-in `mips_vendor_sym.h` for users who prefer the textbook MIPS assembly mnemonics.
|
||||
* ============================================================================ */
|
||||
|
||||
#ifdef INTELLISENSE_DIRECTIVES
|
||||
@@ -98,19 +77,17 @@ enum {
|
||||
/* ============================================================================
|
||||
* REGISTER INTEGER IDS (preprocessor-visible)
|
||||
* ============================================================================
|
||||
* Every R_* enum below has a parallel R_*_Code `#define` so that the
|
||||
* preprocessor can stringify the integer (e.g. for asm clobber lists and
|
||||
* register-variable declarations via `rgcc(R_X)`). The enum value is
|
||||
* bound to the `#define` so the two forms cannot drift apart.
|
||||
* Every R_* enum below has a parallel R_*_Code `#define` so that the preprocessor can stringify the integer
|
||||
* (e.g. for asm clobber lists and register-variable declarations via `rgcc(R_X)`).
|
||||
* The enum value is bound to the `#define` so the two forms cannot drift apart.
|
||||
*
|
||||
* Only registers that get stringified need a `_Code` form; the rest are
|
||||
* plain enum values. If you need to add a new one, follow the pattern:
|
||||
* Only registers that get stringified need a `_Code` form; the rest are plain enum values.
|
||||
* If you need to add a new one, follow the pattern:
|
||||
* #define R_T7_Code 15
|
||||
* R_T7 = R_T7_Code, // in the enum
|
||||
* R_T7 = R_T7_Code, // in the enum
|
||||
*
|
||||
* User code should always reference the enum form (`R_T4`) at arithmetic
|
||||
* sites and let `rlit(R_T4_Code)` / `rgcc(R_T4)` handle the stringify
|
||||
* cases — never write the bare number `12`.
|
||||
* User code should always reference the enum form (`R_T4`) at arithmetic sites and let
|
||||
* `rlit(R_T4_Code)` / `rgcc(R_T4)` handle the stringify cases — never write the bare number `12`.
|
||||
* ============================================================================ */
|
||||
#define R_0_Code 0
|
||||
#define R_AT_Code 1
|
||||
@@ -225,7 +202,6 @@ enum {
|
||||
/* 2F: N/A */
|
||||
// , op_lwc0
|
||||
|
||||
|
||||
// , op_load_addr = op_la
|
||||
// , op_load_imm = op_li
|
||||
, op_jump = op_j
|
||||
@@ -327,15 +303,15 @@ enum { _BitOffsets = 0
|
||||
* Argument order matches the MIPS assembly syntax:
|
||||
* dest-first, then source operands, then immediate last.
|
||||
*
|
||||
* load_word(rt, base, off) → lw rt, off(base)
|
||||
* store_word(rt, base, off) → sw rt, off(base)
|
||||
* add_ui(rt, rs, imm) → addiu rt, rs, imm
|
||||
* shift_lleft(rd, rt, shamt) → sll rd, rt, shamt
|
||||
* shift_lright(rd, rt, shamt) → srl rd, rt, shamt
|
||||
* shift_aright(rd, rt, shamt) → sra rd, rt, shamt
|
||||
* jump_reg(rs) → jr rs
|
||||
* jump_link(rs, rd) → jalr rs (link in rd, default $ra)
|
||||
* nop → sll $0, $0, 0
|
||||
* load_word(rt, base, off) → lw rt, off(base)
|
||||
* store_word(rt, base, off) → sw rt, off(base)
|
||||
* add_ui(rt, rs, imm) → addiu rt, rs, imm
|
||||
* shift_lleft(rd, rt, shamt) → sll rd, rt, shamt
|
||||
* shift_lright(rd, rt, shamt) → srl rd, rt, shamt
|
||||
* shift_aright(rd, rt, shamt) → sra rd, rt, shamt
|
||||
* jump_reg(rs) → jr rs
|
||||
* jump_link(rs, rd) → jalr rs (link in rd, default $ra)
|
||||
* 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))
|
||||
@@ -404,12 +380,9 @@ enum { _BitOffsets = 0
|
||||
* mult_s / mult_u → mult / multu (writes HI/LO; result in LO)
|
||||
* div_s / div_u → div / divu (LO = quot, HI = rem)
|
||||
*
|
||||
* NOTE: dsl.h defines `add_s`/`sub_s`/`mut_s`/`gt_s`/etc. as
|
||||
* _Generic-based signed integer-arithmetic helpers for U1/U2/U4. Those
|
||||
* live in a different conceptual layer (generic arithmetic on DSL
|
||||
* types) and would collide with the instruction encoders here. The
|
||||
* `#undef` below lets the gas-style names below win; if a file needs
|
||||
* both, the dsl.h versions can be reached via their long forms
|
||||
* NOTE: dsl.h defines `add_s`/`sub_s`/`mut_s`/`gt_s`/etc. as _Generic-based signed integer-arithmetic helpers for U1/U2/U4.
|
||||
* Those live in a different conceptual layer (generic arithmetic on DSL types) and would collide with the instruction encoders here.
|
||||
* The `#undef` below lets the gas-style names below win; if a file needs both, the dsl.h versions can be reached via their long forms
|
||||
* (e.g. `def_signed_op`-style or the underlying `add_s1/s2/s4`). */
|
||||
#undef add_s
|
||||
#undef sub_s
|
||||
@@ -441,7 +414,7 @@ enum { _BitOffsets = 0
|
||||
#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
|
||||
* branch_equal rs, rt, off → beq rs, rt, off
|
||||
* branch_ne rs, rt, off → bne rs, rt, off
|
||||
* branch_lt_zero rs, off → bltz rs, off
|
||||
* branch_gt_zero rs, off → bgtz rs, off
|
||||
@@ -472,22 +445,18 @@ enum { _BitOffsets = 0
|
||||
|
||||
/* 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:
|
||||
* 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)
|
||||
* 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),
|
||||
* 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_forced` and `load_imm_2w_addi_forced` below.
|
||||
*
|
||||
* Statement-level (not expression-level): emits its own `asm volatile(...)`.
|
||||
*/
|
||||
#define load_imm_2w(rt, imm) do { \
|
||||
@@ -518,9 +487,8 @@ enum { _BitOffsets = 0
|
||||
} while (0)
|
||||
|
||||
/* load_imm_2w_addi_forced — 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). */
|
||||
* 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_forced(rt, imm) do { \
|
||||
/*U4 _li2a_imm_ = (U4)(imm);*/ \
|
||||
asm volatile(asm_words( \
|
||||
@@ -532,23 +500,17 @@ enum { _BitOffsets = 0
|
||||
|
||||
/* load_imm rt, imm — true `li` semantics (assembler `li` pseudo)
|
||||
*
|
||||
* Dispatches at compile time on the immediate's range, picking the
|
||||
* smallest single-instruction form when possible:
|
||||
*
|
||||
* imm in 0 .. 0x7FFF → addi rt, $0, imm (1 word)
|
||||
* imm in 0x8000 .. 0xFFFF → ori rt, $0, imm (1 word; sign-bit must be zeroed)
|
||||
* imm in 0x10000 .. 0xFFFFFFFF → lui + (ori | addi) (2 words)
|
||||
*
|
||||
* Statement-level (not expression-level): the macro emits its own
|
||||
* `asm volatile(...)` block with 1 or 2 .word constants. Callers can
|
||||
* group multiple `load_imm` calls in a single volatile by using the
|
||||
* lower-level encoders directly:
|
||||
* Dispatches at compile time on the immediate's range, picking the smallest single-instruction form when possible:
|
||||
* imm in 0 .. 0x7FFF → addi rt, $0, imm (1 word)
|
||||
* imm in 0x8000 .. 0xFFFF → ori rt, $0, imm (1 word; sign-bit must be zeroed)
|
||||
* imm in 0x10000 .. 0xFFFFFFFF → lui + (ori | addi) (2 words)
|
||||
*
|
||||
* Statement-level (not expression-level): the macro emits its own `asm volatile(...)` block with 1 or 2 .word constants.
|
||||
* Callers can group multiple `load_imm` calls in a single volatile by using the lower-level encoders directly:
|
||||
* load_imm(R_T4, 0x12345678); // emits 2 .words
|
||||
*
|
||||
* 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). */
|
||||
* 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 (cexpr_(imm) && ((imm) <= 0x7FFFU)) { \
|
||||
/* Small positive: addi rt, $0, imm */ \
|
||||
@@ -588,9 +550,8 @@ enum { _BitOffsets = 0
|
||||
|
||||
|
||||
/* 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. */
|
||||
* 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 clbr_volatile_gprs rlit(R_V0), rlit(R_T0), rlit(R_T1), rlit(R_RA), clb_mem_drain
|
||||
|
||||
#define asm_mips_flush_icache() asm volatile( asm_words( \
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
* duffle DSL — MIPS Vendor Mnemonics (opt-in)
|
||||
* ============================================================================
|
||||
*
|
||||
* Provides the textbook MIPS assembly mnemonics as thin aliases to the
|
||||
* canonical duffle macros in mips.h. The duffle names are primary; this
|
||||
* header is for users who prefer the textbook mnemonics.
|
||||
* Provides the textbook MIPS assembly mnemonics as thin aliases to the canonical duffle macros in mips.h.
|
||||
* The duffle names are primary; this header is for users who prefer the textbook mnemonics.
|
||||
*
|
||||
* USAGE: #include "duffle/mips_vendor_sym.h" // after mips.h
|
||||
*
|
||||
|
||||
@@ -5,11 +5,10 @@
|
||||
// Format: WORD_COUNT(MACRO_NAME, COUNT)
|
||||
// One line per macro that appears in your atom sources.
|
||||
//
|
||||
// This file is encoding-macros-only. The auto-generated component
|
||||
// macros (mac_X) live in duffle/gen/<dir>.macs.h (included separately
|
||||
// by the unity build). The unity build should include THIS file and
|
||||
// the .macs.h file in the same TU, with both wrapped (or the
|
||||
// include guard order handled) to avoid WORD_COUNT redeclaration.
|
||||
// This file is encoding-macros-only.
|
||||
// The auto-generated component macros (mac_X) live in duffle/gen/<dir>.macs.h (included separately by the unity build).
|
||||
// The unity build should include THIS file and the .macs.h file in the same TU, with both wrapped
|
||||
// (or the include guard order handled) to avoid WORD_COUNT redeclaration.
|
||||
//
|
||||
// To regenerate: hand-count the instructions in each macro definition.
|
||||
// (You'll only need to do this once per macro — they don't change often.)
|
||||
|
||||
@@ -259,8 +259,8 @@ void update(PrimitiveArena* pa, U4* ordering_buf)
|
||||
U4 prim_base = u4_(pa->buf[smem.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_old(&tape_arena); tb_scope(& tb) {
|
||||
LP_ U4 mem_temp_tape[512];
|
||||
TapeBuilder tb = tb_make(slice_ut_arr(mem_temp_tape)); tb_scope(& tb) {
|
||||
tb_emit(& tb, rbind_cube_g4_face);
|
||||
tb_data(& tb, prim_cursor);
|
||||
tb_data(& tb, u4_(smem.cube.faces));
|
||||
|
||||
@@ -35,12 +35,7 @@ internal MipsAtom_(rbind_cube_g4_face) atom_info(atom_bind(Binds_CubeTri)
|
||||
mac_yield()
|
||||
};
|
||||
|
||||
/* ============================================================================
|
||||
* cube_g4_face — 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.
|
||||
*/
|
||||
// cube_g4_face — Draw one cube face (Gouraud-shaded quad) via the GTE tape pipeline
|
||||
internal
|
||||
MipsAtom_(cube_g4_face) atom_info(
|
||||
atom_reads( R_PrimCursor, R_FaceCursor, R_VertBase, R_OtBase),
|
||||
|
||||
Reference in New Issue
Block a user