From 5375478044cb08d8b99e0585a4776c42ea895073 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Wed, 8 Jul 2026 10:21:31 -0400 Subject: [PATCH] gp.h improvements --- code/duffle/gp.h | 698 +++++++++++++++++--- code/duffle/gp_vendor_sym.h | 58 ++ code/duffle/gte.h | 103 +-- code/duffle/lottes_tape.h | 23 +- code/duffle/mips.h | 44 +- code/gte_hello/gen/hello_gte_tape.offsets.h | 4 +- code/gte_hello/hello_gte.c | 1 - code/gte_hello/hello_gte.h | 107 +-- code/gte_hello/hello_gte_tape.c | 21 +- 9 files changed, 725 insertions(+), 334 deletions(-) create mode 100644 code/duffle/gp_vendor_sym.h diff --git a/code/duffle/gp.h b/code/duffle/gp.h index 4605d18..bd65db8 100644 --- a/code/duffle/gp.h +++ b/code/duffle/gp.h @@ -1,127 +1,611 @@ +/* ============================================================================ + * 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) + * + * --- GPU-domain layer cake --- + * Every gp.h macro follows the same 4-layer composition as mips.h and gte.h: + * 4. Semantic encoders gp0_word_poly_f3(r,g,b) + * 3. Composite encoders enc_color_word(cmd, r, g, b) + * 2. Per-field encoders enc_gp0_color_r(r), enc_gp0_color_g(g), ... + * 1. Bitfield layout consts gp0_color_red_shift = 0, gp0_color_red_mask = 0xFF + * 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. + * ============================================================================ */ + #ifdef INTELLISENSE_DIRECTIVES # pragma once # include "dsl.h" # include "math.h" +# include "mips.h" #endif -typedef Enum_(U4, gp_Commands) { - gcmd_Reset = 0b000, - gcmd_Polygon = 0b001, - gcmd_Line = 0b010, - gcmd_Rect = 0b011, - gcmd_VM_to_VM = 0b100, - gcmd_CPU_to_VM = 0b101, - gcmd_VM_to_CPU = 0b110, - gcmd_Environment = 0b111, - - gcmd_SetDrawMode = 0xE1, - gcmd_SetTextureWindow = 0xE2, - gcmd_SetDrawArea_TopLeft = 0xE3, - gcmd_SetDrawArea_BotRight = 0xE4, - gcmd_SetDrawOffset = 0xE5, - gcmd_SetMaskBit = 0xE6, - - gcmd_ResetCommandBuffer = 0x01, - gcmd_AcknowledgeGPUInterrupt = 0x02, - gcmd_DisplayEnable = 0x03, - gcmd_DMA_Request = 0x04, - gcmd_DispArea_Start = 0x05, - gcmd_HorizontalDisplayRange = 0x06, - gcmd_VerticalDisplayRange = 0x07, - gcmd_DisplayMode = 0x08, - - gcmd_SetVramSize = 0x09, -}; - +#pragma region GPU Ports & Commands +/* ============================================================================ + * 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). + * ============================================================================ */ enum { - gpio_port_0 = 0x1810, - gpio_port_1 = 0x1814, - - gcmd_offset = 24, - - gp_Reset = (gcmd_Reset << gcmd_offset), - - gp_DisplayEnabled = (gcmd_DisplayEnable << gcmd_offset | 0x0), - gp_DisplayDisabled = (gcmd_DisplayEnable << gcmd_offset | 0x1), - - gp_DMA_FIFO = 1, - gp_DMA_CPU_to_GPU = 2, - gp_DMA_GPU_to_CPU = 3, - gp_DMA_Request = (gcmd_DMA_Request << gcmd_offset), - - gp_HorizontalDisplayRange_3168_608 = (gcmd_HorizontalDisplayRange << gcmd_offset | 0xC60 << 12 | 0x260), - - gp_VerticalDiplayRange = (gcmd_VerticalDisplayRange << gcmd_offset), - gp_VerticalDisplayRange_264_24 = (gp_VerticalDiplayRange | 264 << 10 | 24), - gp_VerticalDisplayRange_504_24 = (gp_VerticalDiplayRange | 504 << 10 | 24), - - gp_DisplayMode = (gcmd_DisplayMode << gcmd_offset), - gp_Disp_HRes_256 = (0x0), - gp_Disp_HRes_320 = (0x1), - gp_Disp_HRes_512 = (0x2), - gp_Disp_HRes_640 = (0x3), - gp_Disp_VRes_240 = (0x0 << 2), - gp_Disp_VRes_480 = (0x1 << 2), - gp_Disp_Color15 = (0x0 << 4), - gp_Disp_Color24 = (0x1 << 4), - gp_Disp_VInterlace = (0x1 << 5), - gp_DisplayMode_320x240_15bit_NTSC = (gp_DisplayMode | gp_Disp_HRes_320 | gp_Disp_VRes_240 | gp_Disp_Color15), - gp_DisplayMOde_640x480_24bbp_NTSC = (gp_DisplayMode | gp_Disp_HRes_640 | gp_Disp_VRes_480 | gp_Disp_Color24 | gp_Disp_VInterlace), - - gp_DrawMode_DrawAllowed = 10, - gp_SetDrawMode_DrawAllowed = (gcmd_SetDrawMode << gcmd_offset | 0x1 << gp_DrawMode_DrawAllowed), - - gp_SetArea_TopLeft = (gcmd_SetDrawArea_TopLeft << gcmd_offset), - gp_SetArea_BottomRight = (gcmd_SetDrawArea_BotRight << gcmd_offset), + HW_GP0_ADDR = 0x1F801810, /* GPU data port (commands + parameters) */ + HW_GP1_ADDR = 0x1F801814, /* GPU control port (status, ctrl writes) */ }; +#define HW_GP0 C_(U4 V_*, HW_GP0_ADDR) +#define HW_GP1 C_(U4 V_*, HW_GP1_ADDR) + +#define gp0_send(word) (HW_GP0[0] = (word)) +#define gp1_send(word) (HW_GP1[0] = (word)) + +/* ============================================================================ + * 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; pre-baked 32-bit words are in §10.4. + * The layer-1 bitfield-layout constants live in the same enum block + * so the encoder in §10.4 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 lines 276-293. + * ============================================================================ */ +enum { + gp0_cmd_Nop = 0x00, + + /* Cache management */ + gp0_cmd_ClearCache = 0x01, + gp0_cmd_FillVram = 0x02, + gp0_cmd_CopyVram = 0x80, + gp0_cmd_CopyVramChained = 0x81, + gp0_cmd_ReadVram = 0xC0, + + /* Polygons */ + gp0_cmd_poly_f3 = 0x20, /* Flat Triangle */ + gp0_cmd_poly_ft3 = 0x24, /* Flat Textured Triangle */ + gp0_cmd_poly_g3 = 0x30, /* Gouraud Triangle */ + gp0_cmd_poly_gt3 = 0x34, /* Gouraud Textured Tri */ + gp0_cmd_poly_f4 = 0x28, /* Flat Quad */ + gp0_cmd_poly_ft4 = 0x2C, /* Flat Textured Quad */ + gp0_cmd_poly_g4 = 0x38, /* Gouraud Quad */ + gp0_cmd_poly_gt4 = 0x3C, /* Gouraud Textured Quad */ + + /* Lines */ + gp0_cmd_line_f2 = 0x40, + gp0_cmd_line_g2 = 0x50, + + /* Sprites + Tiles + Rects */ + gp0_cmd_sprt_1 = 0x64, + gp0_cmd_sprt_8 = 0x74, + gp0_cmd_sprt_16 = 0x7C, + gp0_cmd_tile_1 = 0x60, + gp0_cmd_tile_8 = 0x68, + gp0_cmd_tile_16 = 0x70, + + /* bitfield shifts / widths / masks ---- + * + * Generic GP0/GP1 command byte (upper 8 bits of every word sent + * to either port). Used by `enc_gp0_cmd(cmd)` and friends below. */ + gp0_cmd_shift = 24, + gp0_cmd_width = 8, + gp0_cmd_mask = 0xFF, + + /* Color word layout (lives in Poly_F3.color, Poly_G4.c0..c3, etc.): + * bits 31..24 = command byte + * bits 23..16 = BLUE + * bits 15..08 = GREEN + * bits 07..00 = RED (PSX GPU is BGR, NOT RGB) */ + gp0_color_cmd_shift = 24, gp0_color_cmd_width = 8, gp0_color_cmd_mask = 0xFF, + gp0_color_blue_shift = 16, gp0_color_blue_width = 8, gp0_color_blue_mask = 0xFF, + gp0_color_green_shift = 8, gp0_color_green_width = 8, gp0_color_green_mask = 0xFF, + gp0_color_red_shift = 0, gp0_color_red_width = 8, gp0_color_red_mask = 0xFF, +}; + +/* ============================================================================ + * 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. Mirrors `enc_op` / `enc_rs` / + * `enc_rt` in mips.h lines 295-301 and `enc_gte_sf` / `enc_gte_mx` in + * gte.h lines 342-347. 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. + * ============================================================================ */ + +/* ---- Layer 1.5: per-field encoders ---- */ +#define enc_gp0_cmd(cmd) (((cmd) & gp0_cmd_mask) << gp0_cmd_shift) + +#define enc_gp0_color_cmd(cmd) (((cmd) & gp0_color_cmd_mask) << gp0_color_cmd_shift) +#define enc_gp0_color_r(r) (((r) & gp0_color_red_mask) << gp0_color_red_shift) +#define enc_gp0_color_g(g) (((g) & gp0_color_green_mask) << gp0_color_green_shift) +#define enc_gp0_color_b(b) (((b) & gp0_color_blue_mask) << gp0_color_blue_shift) + +/* ---- Layer 2: composite encoders ---- */ +#define enc_color_word(cmd, r, g, b) (enc_gp0_color_cmd(cmd) | enc_gp0_color_r(r) | enc_gp0_color_g(g) | enc_gp0_color_b(b)) + +#define enc_gp0_cmd_word(cmd) (enc_gp0_cmd(cmd)) + +/* ---- Layer 3: semantic GP0 word builders ---- */ + +/* Pre-baked color+command words for all 8 polygon variants. + * Mirrors `load_word` / `add_ui` / `jump_reg` style in mips.h lines 340-388. */ +#define gp0_word_poly_f3(r,g,b) enc_color_word(gp0_cmd_poly_f3, (r),(g),(b)) +#define gp0_word_poly_ft3(r,g,b) enc_color_word(gp0_cmd_poly_ft3, (r),(g),(b)) +#define gp0_word_poly_g3(r,g,b) enc_color_word(gp0_cmd_poly_g3, (r),(g),(b)) +#define gp0_word_poly_gt3(r,g,b) enc_color_word(gp0_cmd_poly_gt3, (r),(g),(b)) +#define gp0_word_poly_f4(r,g,b) enc_color_word(gp0_cmd_poly_f4, (r),(g),(b)) +#define gp0_word_poly_ft4(r,g,b) enc_color_word(gp0_cmd_poly_ft4, (r),(g),(b)) +#define gp0_word_poly_g4(r,g,b) enc_color_word(gp0_cmd_poly_g4, (r),(g),(b)) +#define gp0_word_poly_gt4(r,g,b) enc_color_word(gp0_cmd_poly_gt4, (r),(g),(b)) + +/* Cache management — bare-cmd words (no color/range payload). */ +#define gp0_word_clear_cache() enc_gp0_cmd_word(gp0_cmd_ClearCache) +#define gp0_word_fill_vram() enc_gp0_cmd_word(gp0_cmd_FillVram) +#define gp0_word_copy_vram() enc_gp0_cmd_word(gp0_cmd_CopyVram) +#define gp0_word_read_vram() enc_gp0_cmd_word(gp0_cmd_ReadVram) + +/* ============================================================================ + * 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). + * ============================================================================ */ +enum { + gp1_cmd_Reset = 0x00, + gp1_cmd_ResetCmdBuffer = 0x01, + gp1_cmd_AcknowledgeIRQ = 0x02, + gp1_cmd_DisplayEnable = 0x03, + gp1_cmd_DMADirection = 0x04, + gp1_cmd_StartDisplayArea = 0x05, + gp1_cmd_HorizontalDisplayRange = 0x06, + gp1_cmd_VerticalDisplayRange = 0x07, + gp1_cmd_DisplayMode = 0x08, + gp1_cmd_SetTextureWindow = 0x0E, + gp1_cmd_SetDrawAreaTopLeft = 0xE0, + gp1_cmd_SetDrawAreaBottomRight = 0xE1, + gp1_cmd_SetDrawOffset = 0xE2, + gp1_cmd_SetMaskBit = 0xE3, + + /* ---- Display-mode payload flags (per PSX-SPX §"GP1 Display Mode"). + * Bit positions match the encoder shifts below; values are the + * *payload* bits only (the cmd byte is OR'd in by enc_gp1_disp_mode_word). */ + gp1_disp_HRes_256 = 0x0, + gp1_disp_HRes_320 = 0x1, + gp1_disp_HRes_512 = 0x2, + gp1_disp_HRes_640 = 0x3, + gp1_disp_VRes_240 = 0x0, + gp1_disp_VRes_480 = 0x1, + gp1_disp_Color15 = 0x0, + gp1_disp_Color24 = 0x1, + gp1_disp_VInterlace = 0x1, + + /* ---- Layer 1: GP1 display-mode + range + draw-area shifts/masks ---- */ + gp1_disp_hres_shift = 0, gp1_disp_hres_width = 2, gp1_disp_hres_mask = 0x3, + gp1_disp_vres_shift = 2, gp1_disp_vres_width = 1, gp1_disp_vres_mask = 0x1, + gp1_disp_color_shift = 4, gp1_disp_color_width = 1, gp1_disp_color_mask = 0x1, + gp1_disp_interlace_shift = 5, gp1_disp_interlace_width = 1, gp1_disp_interlace_mask = 0x1, + + /* GP1 horizontal display range: bits 0..11 = X2, bits 12..23 = X1 */ + gp1_hrange_x1_shift = 12, gp1_hrange_x1_width = 12, gp1_hrange_x1_mask = 0xFFF, + gp1_hrange_x2_shift = 0, gp1_hrange_x2_width = 12, gp1_hrange_x2_mask = 0xFFF, + + /* GP1 vertical display range: bits 0..9 = Y2, bits 10..19 = Y1 */ + gp1_vrange_y1_shift = 10, gp1_vrange_y1_width = 10, gp1_vrange_y1_mask = 0x3FF, + gp1_vrange_y2_shift = 0, gp1_vrange_y2_width = 10, gp1_vrange_y2_mask = 0x3FF, + + /* GP1 draw area (top-left or bottom-right): bits 0..9 = X, bits 10..19 = Y + * (10-bit signed — caller pre-signs and masks with the named mask) */ + gp1_draw_x_shift = 0, gp1_draw_x_width = 10, gp1_draw_x_mask = 0x3FF, + gp1_draw_y_shift = 10, gp1_draw_y_width = 10, gp1_draw_y_mask = 0x3FF, +}; + +/* ---- Layer 1.5: GP1 per-field encoders ---- */ +#define enc_gp1_disp_hres(h) (((h) & gp1_disp_hres_mask) << gp1_disp_hres_shift) +#define enc_gp1_disp_vres(v) (((v) & gp1_disp_vres_mask) << gp1_disp_vres_shift) +#define enc_gp1_disp_color(c) (((c) & gp1_disp_color_mask) << gp1_disp_color_shift) +#define enc_gp1_disp_interlace(i) (((i) & gp1_disp_interlace_mask) << gp1_disp_interlace_shift) + +#define enc_gp1_hrange_x1(x1) (((x1) & gp1_hrange_x1_mask) << gp1_hrange_x1_shift) +#define enc_gp1_hrange_x2(x2) (((x2) & gp1_hrange_x2_mask) << gp1_hrange_x2_shift) +#define enc_gp1_vrange_y1(y1) (((y1) & gp1_vrange_y1_mask) << gp1_vrange_y1_shift) +#define enc_gp1_vrange_y2(y2) (((y2) & gp1_vrange_y2_mask) << gp1_vrange_y2_shift) +#define enc_gp1_draw_x(x) (((x) & gp1_draw_x_mask) << gp1_draw_x_shift) +#define enc_gp1_draw_y(y) (((y) & gp1_draw_y_mask) << gp1_draw_y_shift) + +/* ---- Layer 2: GP1 composite encoders ---- */ +#define enc_gp1_disp_mode_word(h, v, c, i) (enc_gp0_cmd(gp1_cmd_DisplayMode) | enc_gp1_disp_hres(h) | enc_gp1_disp_vres(v) | enc_gp1_disp_color(c) | enc_gp1_disp_interlace(i)) +#define enc_gp1_hrange_word(x1, x2) (enc_gp0_cmd(gp1_cmd_HorizontalDisplayRange) | enc_gp1_hrange_x1(x1) | enc_gp1_hrange_x2(x2)) +#define enc_gp1_vrange_word(y1, y2) (enc_gp0_cmd(gp1_cmd_VerticalDisplayRange) | enc_gp1_vrange_y1(y1) | enc_gp1_vrange_y2(y2)) +#define enc_gp1_draw_area_tl_word(x, y) (enc_gp0_cmd(gp1_cmd_SetDrawAreaTopLeft) | enc_gp1_draw_x(x) | enc_gp1_draw_y(y)) +#define enc_gp1_draw_area_br_word(x, y) (enc_gp0_cmd(gp1_cmd_SetDrawAreaBottomRight) | enc_gp1_draw_x(x) | enc_gp1_draw_y(y)) + +/* ---- Layer 3: GP1 semantic word builders ---- */ +#define gp1_word_display_enable(on) (enc_gp0_cmd(gp1_cmd_DisplayEnable) | ((on) & 1)) +#define gp1_word_display_disable() gp1_word_display_enable(0) +#define gp1_word_display_mode_320x240_15bit_ntsc enc_gp1_disp_mode_word(gp1_disp_HRes_320, gp1_disp_VRes_240, gp1_disp_Color15, 0) +#define gp1_word_display_mode_640x480_24bit_ntsc_interlaced enc_gp1_disp_mode_word(gp1_disp_HRes_640, gp1_disp_VRes_480, gp1_disp_Color24, gp1_disp_VInterlace) + +#define gp1_word_horizontal_range(x1, x2) enc_gp1_hrange_word((x1), (x2)) +#define gp1_word_vertical_range(y1, y2) enc_gp1_vrange_word((y1), (y2)) + +/* DrawArea: top-left = (X, Y), bottom-right = (X, Y) — X/Y in 10-bit signed. + * Caller is responsible for sign-conversion before passing in. */ +#define gp1_word_draw_area_top_left(x, y) enc_gp1_draw_area_tl_word((x), (y)) +#define gp1_word_draw_area_bottom_right(x, y) enc_gp1_draw_area_br_word((x), (y)) + +#pragma endregion GPU Ports & Commands + +#pragma region GPU Status +/* ============================================================================ + * GPU status register bits + * ============================================================================ + * Read from HW_GP1; the lower bits are DMA-block-size (variable-width). + * ============================================================================ */ +enum { + gp1_Status_BitReady = 31, + gp1_Status_BitSendingDMA = 25, + gp1_Status_DMABlockSizeShift = 0, +}; + +#define gp1_status_is_ready() ((HW_GP1[0] >> gp1_Status_BitReady) & 1) +#define gp1_status_is_sending_dma() ((HW_GP1[0] >> gp1_Status_BitSendingDMA) & 1) +#pragma endregion GPU Status + +#pragma region Primitives +/* ============================================================================ + * 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. + * + * The current working layouts match the existing demo (floor_tri uses + * Poly_F3; cube_tri uses Poly_G4). They are NOT necessarily byte-identical + * to the PSX-SPX reference layout — the demo layout uses color+vertex + * interleaving that doesn't match the standard PSX SDK file format. For + * PSX-SDK file compatibility, the textured variants (FT*, GT*) would need + * layout adjustments; out of scope for this track. + * ============================================================================ */ + +/* ---------- RGB8 (3-byte packed color) ---------- */ typedef Struct_(RGB8) { B1 r; B1 g; B1 b; }; -#define rgb8(r, g, b) (RGB8){ r, g, b } - -typedef B1 gp_Pixel16[1]; -typedef B1 gp_Pixel24[3]; +#define rgb8(r,g,b) ((RGB8){r,g,b}) +/* ---------- PolyTag (the OT-link header; 1 word) ---------- */ enum { - gp_b10_X = 0, - gp_b10_Y = 10, - gp_b16_X = 0, - gp_b16_Y = 16, + polytag_len_bits = 8, + polytag_addr_bits = 24, +}; +typedef Struct_(PolyTag) { + union { + U4 bf_addr_len; + struct { + U4 addr: 24; + U4 len: 8; + }; + }; }; -typedef Struct_(gp_Vec2) { U2 y; U2 x; }; +/* DSL cast convention: every cast uses `C_()`, every pointer qualifier + * is `R_` (restrict) or `V_` (volatile). No raw C-style casts. RHS values + * are assumed to be `U4` — caller passes a `U4` directly. + * + * IMPORTANT: do NOT name an arg the same as a struct member being + * accessed in the body — preprocessor substitution would replace the + * member name with the caller's value expression, yielding `->expr` + * which is a parse error. Use `v` (value) for the arg instead. */ +#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, 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) -#if 1 -void gp_screen_init(void) __asm__("gp_screen_init_asm"); -#else -#define gp_screen_init() gp_screen_init_c11() -#endif - - - -// TODO REVIEW: - -enum { - gcmd_poly_f3 = 0x20, - - gp_poly_f3 +/* ---------- Poly_F3 (Flat Triangle; 5 words) ---------- */ +typedef Struct_(Poly_F3) { + U4 tag; + RGB8 color; + B1 code; + union { + struct { V2_S2 p0; V2_S2 p1; V2_S2 p2; }; + A3_V2_S2 points; + }; }; -/* --- GPU Command Semantics (GP0) --- */ +/* ---------- Poly_F4 (Flat Quad; 6 words) ---------- */ +typedef Struct_(Poly_F4) { + U4 tag; + RGB8 color; + B1 code; + union { + struct { V2_S2 p0; V2_S2 p1; V2_S2 p2; V2_S2 p3; }; + A4_V2_S2 points; + }; +}; -#define GPU_CMD_CLEAR_CACHE 0x01 -#define GPU_CMD_VRAM_FILL 0x02 -#define GPU_CMD_VRAM_COPY 0x80 -#define GPU_CMD_VRAM_READ 0xC0 -#define GPU_CMD_POLY_F3 0x20 /* Flat Triangle */ -#define GPU_CMD_POLY_FT3 0x24 /* Flat Textured Triangle */ -#define GPU_CMD_POLY_G3 0x30 /* Gouraud Triangle */ -#define GPU_CMD_POLY_GT3 0x34 /* Gouraud Textured Triangle */ -#define GPU_CMD_POLY_F4 0x28 /* Flat Quad */ -#define GPU_CMD_POLY_FT4 0x2C /* Flat Textured Quad */ -#define GPU_CMD_POLY_G4 0x38 /* Gouraud Quad */ -#define GPU_CMD_POLY_GT4 0x3C /* Gouraud Textured Quad */ +/* ---------- Poly_G3 (Gouraud Triangle; 6 words) ---------- */ +typedef Struct_(Poly_G3) { + U4 tag; RGB8 c0; B1 code; + V2_S2 p0; RGB8 c1; B1 pad1; + V2_S2 p1; RGB8 c2; B1 pad2; + V2_S2 p2; +}; -/* --- Hardware MMIO Addresses --- */ +/* ---------- Poly_G4 (Gouraud Quad; 5 words in the demo's interleaved layout) ---------- */ +typedef Struct_(Poly_G4) { + U4 tag; RGB8 c0; B1 code; + V2_S2 p0; RGB8 c1; B1 pad1; + V2_S2 p1; RGB8 c2; B1 pad2; + V2_S2 p2; RGB8 c3; B1 pad3; + V2_S2 p3; +}; -#define HW_GP0_ADDR 0x1F801810 /* GPU Data Port */ -#define HW_GP1_ADDR 0x1F801814 /* GPU Status/Control Port */ +/* ---------- Poly_FT3 (Flat Textured Triangle; placeholder layout) ---------- */ +/* TODO(Ed): verify the textured-variant layout against PSX-SPX when needed. */ +typedef Struct_(Poly_FT3) { + U4 tag; + RGB8 color; + B1 code; + U4 tpage; + U4 clut; + V2_S2 p0; U1 u0; U1 v0; + V2_S2 p1; U1 u1; U1 v1; + V2_S2 p2; U1 u2; U1 v2; +}; + +/* ---------- Poly_FT4 (Flat Textured Quad; placeholder layout) ---------- */ +typedef Struct_(Poly_FT4) { + U4 tag; + RGB8 color; + B1 code; + U4 tpage; + U4 clut; + V2_S2 p0; U1 u0; U1 v0; + V2_S2 p1; U1 u1; U1 v1; + V2_S2 p2; U1 u2; U1 v2; + V2_S2 p3; U1 u3; U1 v3; +}; + +/* ---------- Poly_GT3 (Gouraud Textured Triangle; placeholder layout) ---------- */ +typedef Struct_(Poly_GT3) { + U4 tag; RGB8 c0; B1 code; + V2_S2 p0; RGB8 c1; B1 pad1; + V2_S2 p1; RGB8 c2; B1 pad2; + V2_S2 p2; + U4 tpage; + U4 clut; + V2_S2 tp0; U1 u0; U1 v0; + V2_S2 tp1; U1 u1; U1 v1; + V2_S2 tp2; U1 u2; U1 v2; +}; + +/* ---------- Poly_GT4 (Gouraud Textured Quad; placeholder layout) ---------- */ +typedef Struct_(Poly_GT4) { + U4 tag; RGB8 c0; B1 code; + V2_S2 p0; RGB8 c1; B1 pad1; + V2_S2 p1; RGB8 c2; B1 pad2; + V2_S2 p2; RGB8 c3; B1 pad3; + V2_S2 p3; + U4 tpage; + U4 clut; + V2_S2 tp0; U1 u0; U1 v0; + V2_S2 tp1; U1 u1; U1 v1; + V2_S2 tp2; U1 u2; U1 v2; + V2_S2 tp3; U1 u3; U1 v3; +}; + +/* ---------- Primitive setters (C-level, no emitted words) ---------- + * DSL cast convention: every cast via C_(), every pointer via R_/V_. */ +#define set_poly_f3(p) set_len(p, 4), C_(Poly_F3_R, p)->code = gp0_cmd_poly_f3 +#define set_poly_ft3(p) set_len(p, 7), C_(Poly_FT3_R,p)->code = gp0_cmd_poly_ft3 +#define set_poly_g3(p) set_len(p, 6), C_(Poly_G3_R, p)->code = gp0_cmd_poly_g3 +#define set_poly_gt3(p) set_len(p, 9), C_(Poly_GT3_R,p)->code = gp0_cmd_poly_gt3 +#define set_poly_f4(p) set_len(p, 5), C_(Poly_F4_R, p)->code = gp0_cmd_poly_f4 +#define set_poly_ft4(p) set_len(p, 9), C_(Poly_FT4_R,p)->code = gp0_cmd_poly_ft4 +#define set_poly_g4(p) set_len(p, 8), C_(Poly_G4_R, p)->code = gp0_cmd_poly_g4 +#define set_poly_gt4(p) set_len(p, 12), C_(Poly_GT4_R,p)->code = gp0_cmd_poly_gt4 + +/* ---------- Ordering table ops ---------- */ +#define orderingtbl_add_primitive(ot, p) set_addr(p, get_addr(ot)), set_addr(ot, p) +#define orderingtbl_add_primitives(ot, p0, p1) set_addr(p1, get_addr(ot)), set_addr(ot, p0) + +#pragma endregion Primitives + +#pragma region TPage +/* ============================================================================ + * 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) + * bits 5..6 = semi-transparency (2 bits, 0..3) + * bits 7..8 = texture page colors (2 bits, 4bpp/8bpp/16bpp/2bpp-mixed) + * bit 9 = dither (1 bit, 0/1) + * bit 10 = drawing to display area (1 bit) + * bit 11 = texture disable (1 bit) + * bits 12..31 = reserved (zero) + * + * The previous version of this file had `gp0_tpage_semi_trans_shift + * = 7`, which is WRONG — semi-transparency lives at bits 5..6 (after Y + * at bit 4). Likewise the prior `gp0_tpage_clut_depth_shift = 12` and + * `gp0_tpage_y_flip_bit = 15` referenced fields that don't exist on the + * TPage word. See design.md §10.9 and PSX-SPX §"Rendering Attributes". + * ============================================================================ */ +enum { + /* ---- Layer 1: TPage bitfield shifts / widths / masks ---- */ + gp0_tpage_x_shift = 0, gp0_tpage_x_width = 4, gp0_tpage_x_mask = 0xF, + gp0_tpage_y_shift = 4, gp0_tpage_y_width = 1, gp0_tpage_y_mask = 0x1, + gp0_tpage_semi_trans_shift = 5, gp0_tpage_semi_trans_width = 2, gp0_tpage_semi_trans_mask = 0x3, + gp0_tpage_color_depth_shift = 7, gp0_tpage_color_depth_width = 2, gp0_tpage_color_depth_mask = 0x3, + gp0_tpage_dither_shift = 9, gp0_tpage_dither_width = 1, gp0_tpage_dither_mask = 0x1, + gp0_tpage_draw_to_disp_shift = 10, gp0_tpage_draw_to_disp_width = 1, gp0_tpage_draw_to_disp_mask = 0x1, + gp0_tpage_tex_disable_shift = 11, gp0_tpage_tex_disable_width = 1, gp0_tpage_tex_disable_mask = 0x1, + + /* TPage color-depth payload values (NOT bit positions — these go in + * the 2-bit field at gp0_tpage_color_depth_shift). */ + gp0_tpage_color_4bpp = 0x0, + gp0_tpage_color_8bpp = 0x1, + gp0_tpage_color_16bpp = 0x2, + + /* TPage semi-transparency mode payload values (NOT bit positions). */ + gp0_tpage_semi_trans_none = 0x0, + gp0_tpage_semi_trans_alpha = 0x1, + gp0_tpage_semi_trans_add = 0x2, + gp0_tpage_semi_trans_sub = 0x3, +}; + +/* ---- Layer 1.5: TPage per-field encoders. Mirrors enc_gte_sf/mx/v in + * gte.h lines 342-347. ---- */ +#define enc_gp0_tpage_x(x) (((x) & gp0_tpage_x_mask) << gp0_tpage_x_shift) +#define enc_gp0_tpage_y(y) (((y) & gp0_tpage_y_mask) << gp0_tpage_y_shift) +#define enc_gp0_tpage_semi_trans(s) (((s) & gp0_tpage_semi_trans_mask) << gp0_tpage_semi_trans_shift) +#define enc_gp0_tpage_color_depth(c) (((c) & gp0_tpage_color_depth_mask) << gp0_tpage_color_depth_shift) +#define enc_gp0_tpage_dither(d) (((d) & gp0_tpage_dither_mask) << gp0_tpage_dither_shift) +#define enc_gp0_tpage_draw_to_disp(d) (((d) & gp0_tpage_draw_to_disp_mask) << gp0_tpage_draw_to_disp_shift) +#define enc_gp0_tpage_tex_disable(t) (((t) & gp0_tpage_tex_disable_mask) << gp0_tpage_tex_disable_shift) + +/* ---- Layer 2: TPage composite encoder. Mirrors enc_gte_cmdw in gte.h + * line 350. ---- */ +#define enc_gp0_tpage_word(x, y, semi_trans, color_depth, dither, draw_to_disp, tex_disable) \ + (enc_gp0_tpage_x(x) \ + | enc_gp0_tpage_y(y) \ + | enc_gp0_tpage_semi_trans(semi_trans) \ + | enc_gp0_tpage_color_depth(color_depth) \ + | enc_gp0_tpage_dither(dither) \ + | enc_gp0_tpage_draw_to_disp(draw_to_disp) \ + | enc_gp0_tpage_tex_disable(tex_disable)) + +typedef Struct_(TexturePage) { U4 raw; }; + +/* ---- Layer 3: TPage semantic word builder ---- */ +#define gp0_word_tpage(x, y, semi_trans, color_depth, dither, draw_to_disp, tex_disable) \ + enc_gp0_tpage_word((x), (y), (semi_trans), (color_depth), (dither), (draw_to_disp), (tex_disable)) +#pragma endregion TPage + +#pragma region CLUT +/* ============================================================================ + * 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) + * bits 15..23 = reserved (zero) + * bits 24..31 = command byte — 0x20 (4bpp load) or 0x25 (8bpp load) + * + * The previous version used `depth_4bpp ? 0 : 5` as the lower-5 bits + * of the cmd byte — that's an opaque ternary that hides which opcode + * is being sent. The two cmd-byte values are now named; one macro per + * depth. Mirrors the named-opcode rule from mips.h lines 188-271 and + * the `gte_cmd_rtpt` / `gte_cmd_nclip` named-opcode pattern from + * gte.h lines 209-214. + * ============================================================================ */ +enum { + /* ---- Layer 1: CLUT bitfield shifts / widths / masks ---- */ + gp0_clut_y_shift = 0, gp0_clut_y_width = 6, gp0_clut_y_mask = 0x3F, + gp0_clut_x_shift = 6, gp0_clut_x_width = 9, gp0_clut_x_mask = 0x1FF, + /* CLUT-load cmd-byte variants — the upper byte of the GP0 word. */ + gp0_clut_cmd_Load4bpp = 0x20, + gp0_clut_cmd_Load8bpp = 0x25, +}; + +/* ---- Layer 1.5: CLUT per-field encoders ---- */ +#define enc_gp0_clut_x(x) (((x) & gp0_clut_x_mask) << gp0_clut_x_shift) +#define enc_gp0_clut_y(y) (((y) & gp0_clut_y_mask) << gp0_clut_y_shift) + +/* ---- Layer 2: CLUT composite encoder ---- */ +#define enc_gp0_clut_word(cmd, x, y) (enc_gp0_cmd(cmd) | enc_gp0_clut_x(x) | enc_gp0_clut_y(y)) + +/* ---- Layer 3: CLUT semantic word builders — one per depth variant, + * named cmd-byte (no opaque ternary). ---- */ +#define gp0_word_clut_load_4bpp(x, y) enc_gp0_clut_word(gp0_clut_cmd_Load4bpp, (x), (y)) +#define gp0_word_clut_load_8bpp(x, y) enc_gp0_clut_word(gp0_clut_cmd_Load8bpp, (x), (y)) +#pragma endregion CLUT + +#pragma region TIM File Format +/* ============================================================================ + * 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) + * +0x08 U4 flags (bits 0..2 = type, bit 3 = has_CLUT) + * +0x0C ... CLUT section (if flags & 0x8) + * +0x00 U4 clut_section_length + * +0x04 U2 clut_org_x + * +0x06 U2 clut_org_y + * +0x08 U2 num_colors + * +0x0A U2 depth_bpp + * +0x0C ... palette data + * ... ... Pixel section + * +0x00 U4 px_section_length + * +0x04 U2 px_width + * +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 for this track. + * ============================================================================ */ +enum { + tim_file_id_magic = 0x10, + tim_type_4bpp = 0x00, + tim_type_8bpp = 0x01, + tim_type_16bpp = 0x02, + tim_type_32bpp = 0x03, + tim_type_mixed = 0x04, + tim_flag_has_clut = 0x08, +}; + +typedef Struct_(TIM_Header) { + U4 file_id; /* always 0x10 = "TIM" magic */ + U4 version; /* ignored; always 0 */ + U4 flags; /* bits 0..2 = type, bit 3 = has_clut */ +}; +typedef Struct_(TIM_SectionHeader) { + U4 section_length; /* bytes in this section including this header */ + U2 org_x; /* origin in VRAM */ + U2 org_y; + U2 width; /* width in pixels */ + U2 height; /* height in pixels */ +}; +#pragma endregion TIM File Format + +#pragma region Tape-Side Macros +/* ============================================================================ + * Tape-side macro components + * ============================================================================ + * + * TODO: mac_gp0_send — write a 32-bit GPU command word to HW_GP0 from + * within an atom body. Requires placeholder-pun on a runtime GPR holding + * the port address. + * ============================================================================ */ +/* #define mac_gp0_send(r_gp_port, word) ... deferred */ +#pragma endregion Tape-Side Macros diff --git a/code/duffle/gp_vendor_sym.h b/code/duffle/gp_vendor_sym.h new file mode 100644 index 0000000..f421a80 --- /dev/null +++ b/code/duffle/gp_vendor_sym.h @@ -0,0 +1,58 @@ +/* ============================================================================ + * 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. + * + * USAGE: #include "duffle/gp_vendor_sym.h" // after gp.h + * + * Mapping (vendor -> duffle): + * Primitive setters (PSYQ SDK-style): + * setPolyF3 -> set_poly_f3 + * setPolyF4 -> set_poly_f4 + * setPolyG3 -> set_poly_g3 + * setPolyG4 -> set_poly_g4 + * setPolyFT3 -> set_poly_ft3 + * setPolyFT4 -> set_poly_ft4 + * setPolyGT3 -> set_poly_gt3 + * setPolyGT4 -> set_poly_gt4 + * + * 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 (tape_atom.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 +# pragma once +# include "gp.h" +#endif + +#ifndef DUFFLE_GP_VENDOR_SYM_H +#define DUFFLE_GP_VENDOR_SYM_H + +/* Primitive setters (PSYQ SDK-style) */ +#define setPolyF3(p) set_poly_f3(p) +#define setPolyF4(p) set_poly_f4(p) +#define setPolyG3(p) set_poly_g3(p) +#define setPolyG4(p) set_poly_g4(p) +#define setPolyFT3(p) set_poly_ft3(p) +#define setPolyFT4(p) set_poly_ft4(p) +#define setPolyGT3(p) set_poly_gt3(p) +#define setPolyGT4(p) set_poly_gt4(p) + +/* OT operations */ +#define AddPrim(ot, p) orderingtbl_add_primitive((ot), (p)) + +#endif diff --git a/code/duffle/gte.h b/code/duffle/gte.h index 1090426..2b2e9a9 100644 --- a/code/duffle/gte.h +++ b/code/duffle/gte.h @@ -1,84 +1,21 @@ /* ============================================================================ - * duffle DSL Suffix Conventions (Style B) + * 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. - * - * --- Shift family (R-type): verb-modifier-direction --- - * The shift macros use `shift_`. 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: - * call_addr (jal), call_reg (jalr rs, default $ra). - * Examples: jump(off) (= j) - * jump_reg(rs) (= jr) - * jump_link(rs, rd) (= jalr rs, rd) - * 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. - * - * _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. - * - * _1w / _2w Emitted word count of the 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 aliases (gte_mtc2, gte_mfc2) - * - * Primitive commands: gp0_cmd_poly_f3 = 0x20 (byte opcode) + * 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) + * 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 live in the opt-in `gte_vendor_sym.h` for @@ -104,10 +41,10 @@ * ---------- * 1. A 32-bit instruction word is composed from per-field encoders. Each * encoder knows only its own bit range; the composite ORs them together. - * No magic numbers inside any encoder body — every shift and mask is a + * No magic numbers inside any encoder body. Every shift and mask is a * named constant from the bitfield-layout enum below. * - * 2. Pure (compile-time) instructions — every GTE *command* (RTPS, RTPT, + * 2. Pure (compile-time) instructions. Every GTE *command* (RTPS, RTPT, * NCLIP, MVMVA, …) and every COP2 *transfer* (ctc2/cfc2) with a constant * rs/rt/rd — are emitted as a single integer constant via * `asm_inline(...)` from gcc_asm.h. The C compiler constant-folds @@ -135,9 +72,9 @@ * gte_load_v0(p_in_12, R_T4); // R_T4 = 12 = $t4 = $12 * * // Three independent bases for an RTPT pipeline: - * register V3_S2* p0 __asm__("$12") = verts[0].ptr; - * register V3_S2* p1 __asm__("$13") = verts[1].ptr; - * register V3_S2* p2 __asm__("$14") = verts[2].ptr; + * register V3_S2* p0 gcc_reg(R_T4) = verts[0].ptr; + * register V3_S2* p1 gcc_reg(R_T5) = verts[1].ptr; + * register V3_S2* p2 gcc_reg(R_T6) = verts[2].ptr; * gte_load_v0(p0, R_T4); * gte_load_v1(p1, R_T5); * gte_load_v2(p2, R_T6); @@ -362,11 +299,11 @@ enum { _C2_OPS_ = 0 // #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) */ -/* Explicit GTE Data vs Control Register Transfers */ +/* GTE Data vs Control Register Transfers */ #define gte_mv_from_data_r(rt, rd) enc_gte_tx(0x00, (rt), (rd)) /* Move from GTE Data Reg (e.g. MAC0, OTZ) */ -#define gte_mv_from_ctrl_r(rt, rd) enc_gte_tx(0x02, (rt), (rd)) /* Move from GTE Control Reg */ -#define gte_mv_to_data_r(rt, rd) enc_gte_tx(0x04, (rt), (rd)) /* Move to GTE Data Reg (e.g. VXY0) */ -#define gte_mv_to_ctrl_r(rt, rd) enc_gte_tx(0x06, (rt), (rd)) /* Move to GTE Control Reg (e.g. Matrices) */ +#define gte_mv_from_ctrl_r(rt, rd) enc_gte_tx(0x02, (rt), (rd)) /* Move from GTE Control Reg */ +#define gte_mv_to_data_r(rt, rd) enc_gte_tx(0x04, (rt), (rd)) /* Move to GTE Data Reg (e.g. VXY0) */ +#define gte_mv_to_ctrl_r(rt, rd) enc_gte_tx(0x06, (rt), (rd)) /* Move to GTE Control Reg (e.g. Matrices) */ /* COP2 Data Load (lwc2): `lwc2 rt, off(rs)` * Layout: [op_lwc2:6][rs:5][rt:5][imm:16] @@ -388,7 +325,7 @@ enum { _C2_OPS_ = 0 #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 (The math engine trigger) +/* GTE Command Format * Opcode is always MIPS_OP_COP2, RS is always 1 (CO). * The lower 25 bits are the GTE-specific command payload. * diff --git a/code/duffle/lottes_tape.h b/code/duffle/lottes_tape.h index 82cbfe9..11a2129 100644 --- a/code/duffle/lottes_tape.h +++ b/code/duffle/lottes_tape.h @@ -112,13 +112,28 @@ FI_ Slice_U4 tb_slice(TapeBuilder tb) { return (Sli , add_u( R_T1, R_T1, R_OtBase) /* T1 = & OrderingTable[OTZ] */ \ , load_word( R_AT, R_T1, O_(PolyTag,bf_addr_len)) /* AT = old_ot_head */ \ , load_upper_i(R_V0, prim_length) /* V0 = prim_length << 16 (high 16 bits of a tag) */ \ - , mask_upper( R_AT, R_AT, S_(PolyTag_len_bits)) /* Strip upper 8 bits (length from prev cell) → keep only low 24 */ \ + , mask_upper( R_AT, R_AT, S_(polytag_len_bits)) /* Strip upper 8 bits (length from prev cell) → keep only low 24 */ \ , or_u( R_AT, R_AT, R_V0) /* Merge length */ \ , store_word( R_AT, R_PrimCursor, O_(PolyTag,bf_addr_len)) /* prim->tag = packed(prim_length, old_addr) */ \ - , shift_lleft( R_AT, R_PrimCursor, S_(PolyTag_len_bits)) /* AT = (prim_length << 24) | old_addr */ \ - , shift_lright(R_AT, R_AT, S_(PolyTag_len_bits)) \ + , shift_lleft( R_AT, R_PrimCursor, S_(polytag_len_bits)) /* AT = (prim_length << 24) | old_addr */ \ + , shift_lright(R_AT, R_AT, S_(polytag_len_bits)) \ , store_word( R_AT, R_T1, O_(PolyTag,bf_addr_len)) /* OrderingTable[OTZ] = PrimCursor */ +/* Words: 3; Emits the F3 command+color word (cmd byte | BLUE | GREEN | RED) + * Args: _r, _g, _b are 8-bit RGB byte values (not raw 16-bit fields). + * Migrated from hello_gte_tape.c; takes RGB form per the Phase 3 + * convention. */ +#define mac_format_f3_color(_r, _g, _b) \ + load_upper_i( R_AT, gp0_cmd_poly_f3 << 8 | (_b)) \ + , or_i( R_AT, R_AT, ((_g) << 8) | (_r)) \ + , store_word(R_AT, R_PrimCursor, O_(Poly_F3,color)) + +/* Words: 3; Stores the 3 transformed (V2_S2 screen) vertices to the F3 */ +#define mac_gte_store_f3() \ + gte_sw(C2_SXY0, R_PrimCursor, O_(Poly_F3,p0)) \ + , gte_sw(C2_SXY1, R_PrimCursor, O_(Poly_F3,p1)) \ + , gte_sw(C2_SXY2, R_PrimCursor, O_(Poly_F3,p2)) + #pragma endregion Macro Atom Components #pragma region Mips Atom Builder @@ -212,7 +227,7 @@ internal MipsAtom_(diag_yield) { mac_yield() }; /* DIAGNOSTIC 2: Pure memory test (No GTE). Draws a fixed cyan triangle. */ internal MipsAtom_(diag_color) { store_word( R_0, R_T7, 0), - load_upper_i(R_AT, gcmd_poly_f3 << 8 | 0xFF), /* High: MipsCode Poly_F3(0x20) + Color B:FF */ + load_upper_i(R_AT, gp0_cmd_poly_f3 << 8 | 0xFF), /* High: MipsCode Poly_F3(0x20) + Color B:FF */ or_i( R_AT, R_AT, 0xFF00), /* Low: Color G:FF, R:00 (Cyan) */ store_word( R_AT, R_T7, 4), diff --git a/code/duffle/mips.h b/code/duffle/mips.h index 3ba93b5..d3a5952 100644 --- a/code/duffle/mips.h +++ b/code/duffle/mips.h @@ -1,5 +1,5 @@ /* ============================================================================ - * duffle DSL Suffix Conventions (Style B) + * duffle DSL Suffix Conventions * ============================================================================ * * Every mnemonic in this header follows the same suffix grammar: @@ -23,9 +23,9 @@ * The shift macros use `shift_`. 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) + * `_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) * @@ -34,11 +34,11 @@ * (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) - * jump_link(rs, rd) (= jalr rs, rd) - * call_reg(rs) (= jalr rs, default $ra) - * call_addr(off) (= jal) + * Examples: jump(off) (= j) + * jump_reg(rs) (= jr) + * jump_link(rs, rd) (= jalr rs, rd) + * 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 @@ -71,14 +71,14 @@ * 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) + * 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 (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 @@ -417,7 +417,7 @@ enum { _BitOffsets = 0 #define div_s(rd, rs, rt) enc_r(op_special, (rs), (rt), (rd), 0, fc_div) #define div_u(rd, rs, rt) enc_r(op_special, (rs), (rt), (rd), 0, fc_divu) -#define add_u_self(rd_rs, rt) add_u(rd_rs, rd_rs, rt) +#define add_u_self(rd_rs, rt) add_u(rd_rs, rd_rs, rt) /* --- Arithmetic I-type (immediate) --- */ #define add_si(rt, rs, imm) enc_i(op_addi, (rs), (rt), (imm)) @@ -503,7 +503,7 @@ enum { _BitOffsets = 0 /* load_imm_2w_ori_forced — 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_forced(rt, imm) do { \ +#define load_imm_2w_ori_forced(rt, imm) do { \ asm volatile( \ asm_words(load_ui((rt), u4_lo(imm)), \ or_i((rt), (rt), C_(U2,u4_hi(imm))) ) \ @@ -515,7 +515,7 @@ enum { _BitOffsets = 0 * 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 { \ +#define load_imm_2w_addi_forced(rt, imm) do { \ /*U4 _li2a_imm_ = (U4)(imm);*/ \ asm volatile(asm_words( \ lui_op((rt), u4_lo(imm)), \ @@ -588,7 +588,7 @@ enum { _BitOffsets = 0 #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( \ - add_ui(rstack_ptr, rstack_ptr, -MipsStackAlignment) \ + add_ui(rstack_ptr, rstack_ptr, -MipsStackAlignment) \ , store_word(rret_addr, rstack_ptr, 4) \ , add_ui(rret_0, rdiscard, bios_flushcache) \ , add_ui(rtmp_0, rdiscard, bios_table_addr) \ @@ -596,5 +596,5 @@ enum { _BitOffsets = 0 , nop \ , load_word(rret_addr, rstack_ptr, 4) \ , jump_reg(rret_addr) \ - , add_ui(rstack_ptr, rstack_ptr, MipsStackAlignment) \ + , add_ui(rstack_ptr, rstack_ptr, MipsStackAlignment) \ ) asm_clobber: clbr_volatile_gprs ) diff --git a/code/gte_hello/gen/hello_gte_tape.offsets.h b/code/gte_hello/gen/hello_gte_tape.offsets.h index d08a82b..b8ddd9f 100644 --- a/code/gte_hello/gen/hello_gte_tape.offsets.h +++ b/code/gte_hello/gen/hello_gte_tape.offsets.h @@ -5,9 +5,9 @@ #pragma region hello_gte_tape -// --- atom: floor_tri (51 words) --- +// --- atom: floor_tri (49 words) --- -#define _atom_offset_culling_floor_tri_exit 17 +#define _atom_offset_culling_floor_tri_exit 15 #define _atom_offset_bounds_chk_floor_tri_exit 3 enum { diff --git a/code/gte_hello/hello_gte.c b/code/gte_hello/hello_gte.c index f6b490c..63b9efe 100644 --- a/code/gte_hello/hello_gte.c +++ b/code/gte_hello/hello_gte.c @@ -291,7 +291,6 @@ void update(PrimitiveArena* pa, U4* ordering_buf) register V3_S2* p1 rgcc(R_T5) = & smem.floor.verts[face->y]; register V3_S2* p2 rgcc(R_T6) = & smem.floor.verts[face->z]; - // Three independent bases — full register discretion at the call site gte_load_v0(p0, R_T4); /* asm volatile( ".word " "%0" ", %1" : : diff --git a/code/gte_hello/hello_gte.h b/code/gte_hello/hello_gte.h index 89476fb..af1479c 100644 --- a/code/gte_hello/hello_gte.h +++ b/code/gte_hello/hello_gte.h @@ -63,106 +63,13 @@ U4 vsync(U4 mode) __asm__("VSync"); void draw_orderingtbl(U4* buf) __asm__("DrawOTag"); -enum { - PolyTag_addr_bits = 24, - PolyTag_len_bits = 8, -}; -typedef Struct_(PolyTag) { - union { - U4 bf_addr_len; - struct { - U4 addr: 24; - U4 len: 8; - }; - }; - RGB8 color; - B1 code; -}; - -/* - * Primitive Handling Macros - */ - -#define set_len( p, _len) (((PolyTag*R_)(p))->len = (B1)(_len)) -#define set_addr(p, _addr) (((PolyTag*R_)(p))->addr = (U4)(_addr)) -#define set_code(p, _code) (((PolyTag*R_)(p))->code = (B1)(_code)) - -#define get_len(p) (B1)(((PolyTag*R_)(p))->len) -#define get_code(p) (B1)(((PolyTag*R_)(p))->code) -#define get_addr(p) (U4)(((PolyTag*R_)(p))->addr) - -#define orderingtbl_add_primitive(ot, p) set_addr(p, get_addr(ot)), set_addr(ot, p) -#define orderingtbl_add_primitives(ot, p0, p1) set_addr(p1, get_addr(ot)), set_addr(ot, p0) - -/* Primitive Length Code */ - -#define set_poly_f3(p) set_len(p, 4), set_code(p, 0x20) -#define set_poly_ft3(p) set_len(p, 7), set_code(p, 0x24) -#define set_poly_g3(p) set_len(p, 6), set_code(p, 0x30) -#define set_poly_gt3(p) set_len(p, 9), set_code(p, 0x34) -#define set_poly_f4(p) set_len(p, 5), set_code(p, 0x28) -#define set_poly_ft4(p) set_len(p, 9), set_code(p, 0x2c) -#define set_poly_g4(p) set_len(p, 8), set_code(p, 0x38) -#define set_poly_gt4(p) set_len(p, 12), set_code(p, 0x3c) - -// #define setSprt8(p) setlen(p, 3), setcode(p, 0x74) -// #define setSprt16(p) setlen(p, 3), setcode(p, 0x7c) -// #define setSprt(p) setlen(p, 4), setcode(p, 0x64) - -// #define setTile1(p) set_len(p, 2), set_code(p, 0x68) -// #define setTile8(p) set_len(p, 2), set_code(p, 0x70) -// #define setTile16(p) set_len(p, 2), set_code(p, 0x78) -#define set_tile(p) set_len(p, 3), set_code(p, 0x60) -// #define setLineF2(p) set_len(p, 3), set_code(p, 0x40) -// #define setLineG2(p) set_len(p, 4), set_code(p, 0x50) -// #define setLineF3(p) set_len(p, 5), set_code(p, 0x48),(p)->pad = 0x55555555 -// #define setLineG3(p) set_len(p, 7), set_code(p, 0x58),(p)->pad = 0x55555555, (p)->p2 = 0 -// #define setLineF4(p) set_len(p, 6), set_code(p, 0x4c),(p)->pad = 0x55555555 -// #define setLineG4(p) set_len(p, 9), set_code(p, 0x5c),(p)->pad = 0x55555555, (p)->p2 = 0, (p)->p3 = 0 - -typedef Struct_(Poly_F3) { - U4 tag; - RGB8 color; - B1 code; - union { - struct { - V2_S2 p0; - V2_S2 p1; - V2_S2 p2; - }; - A3_V2_S2 points; - }; -}; - -typedef Struct_(Poly_G3) { - U4 tag; RGB8 c0; B1 code; - V2_S2 p0; RGB8 c1; B1 pad1; - V2_S2 p1; RGB8 c2; B1 pad2; - V2_S2 p2; -}; - -typedef Struct_(Poly_F4) { - U4 tag; - RGB8 color; - B1 code; - union { - struct { - V2_S2 p0; - V2_S2 p1; - V2_S2 p2; - V2_S2 p3; - }; - A4_V2_S2 points; - }; -}; - -typedef Struct_(Poly_G4) { - U4 tag; RGB8 c0; B1 code; - V2_S2 p0; RGB8 c1; B1 pad1; - V2_S2 p1; RGB8 c2; B1 pad2; - V2_S2 p2; RGB8 c3; B1 pad3; - V2_S2 p3; -}; +/* Primitive Handling Macros + * All primitive types (PolyTag, Poly_F3, Poly_F4, Poly_G3, Poly_G4, + * Poly_FT3, Poly_FT4, Poly_GT3, Poly_GT4) and the set_poly_* setters, + * set_len / set_addr / get_len / get_addr macros, and the + * orderingtbl_add_primitive(s) helpers all live in `duffle/gp.h` + * now (per the Phase 3 gp.h overhaul). This file no longer duplicates + * those definitions. */ typedef Struct_(Tile) { U4 tag; diff --git a/code/gte_hello/hello_gte_tape.c b/code/gte_hello/hello_gte_tape.c index 8ebfe2a..ba4d32c 100644 --- a/code/gte_hello/hello_gte_tape.c +++ b/code/gte_hello/hello_gte_tape.c @@ -7,27 +7,18 @@ #endif #pragma region MACs (Mips Atom components) - // load_ui( R_AT, color_hi) \ +/* The macros mac_format_f3_color and mac_gte_store_f3 moved to + * lottes_tape.h during the Phase 3 gp.h overhaul. Both are now RGB-form + * (mac_format_f3_color takes _r, _g, _b byte values rather than raw + * 16-bit half-words). */ enum fack { - ah = gcmd_poly_f3 << 8 | 0xFF, + ah = gp0_cmd_poly_f3 << 8 | 0xFF, }; void fk() { (void*)ah; } -/* Words: 3; High: 0x20/B, Low: G/R */ -#define mac_format_f3_color(color_hi, color_lo) \ - load_upper_i( R_AT, gcmd_poly_f3 << 8 | color_hi) \ - , or_i( R_AT, R_AT, color_lo) \ - , store_word(R_AT, R_PrimCursor, O_(Poly_F3,color)) \ - -/* Words: 3 */ -#define mac_gte_store_f3() \ - gte_sw(C2_SXY0, R_PrimCursor, O_(Poly_F3,p0)) \ - , gte_sw(C2_SXY1, R_PrimCursor, O_(Poly_F3,p1)) \ - , gte_sw(C2_SXY2, R_PrimCursor, O_(Poly_F3,p2)) - #pragma endregion MACs #pragma region Baked Atoms @@ -202,7 +193,7 @@ MipsAtom_(floor_tri) { nop, /* Format Primitive */ // mac_format_f3_color(0x20FF, 0xFFFF), // works - mac_format_f3_color(0xFF, 0xFFFF), // doesn't work + mac_format_f3_color(0xFF, 0xFF, 0xFF), // RGB-form (R=FF, G=FF, B=FF = white) mac_gte_store_f3(), /* Calculate Depth */ nop, nop, gte_avg_sort_z3,