From 68fd4ca79157ee616e0d47c43a3b9182a6dec932 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Mon, 15 Jun 2026 01:59:18 -0400 Subject: [PATCH] now we're cookin --- code/duffle/gcc_asm.h | 4 +- code/duffle/gte.h | 14 +- code/duffle/lottes_tape.h | 385 ++++++++++++++++++++----------------- code/duffle/memory.h | 9 +- code/duffle/mips.h | 10 +- code/gte_hello/hello_gte.c | 83 ++++---- 6 files changed, 269 insertions(+), 236 deletions(-) diff --git a/code/duffle/gcc_asm.h b/code/duffle/gcc_asm.h index a6175bc..d9a7851 100644 --- a/code/duffle/gcc_asm.h +++ b/code/duffle/gcc_asm.h @@ -72,8 +72,8 @@ /* ------------------------------------------------------------------------ * * rgcc(n) — GCC-specific bundle for register-variable declarations. * - * Produces `__asm__(reg_str(tmpl(n, Code)))` at expansion time. - * The `tmpl(n, Code)` indirection derives the preprocessor-visible `_Code` + * Produces `__asm__(reg_str(tmpl(n, MipsAtom)))` at expansion time. + * The `tmpl(n, MipsAtom)` indirection derives the preprocessor-visible `_Code` * form from the enum name (which the preprocessor can't expand on its own). * So a call is: register V3_S2* p rgcc(R_T4) = verts[0].ptr; * expands (via tmpl) to: register V3_S2* p __asm__(rlit(R_T4_Code)) = verts[0].ptr; diff --git a/code/duffle/gte.h b/code/duffle/gte.h index dbfea64..0484a67 100644 --- a/code/duffle/gte.h +++ b/code/duffle/gte.h @@ -132,7 +132,7 @@ enum { gte_in_v1_z = C2_VZ1, /* Input Vector 1 (Z) */ gte_in_v2_xy = C2_VXY2, /* Input Vector 2 (X, Y) */ gte_in_v2_z = C2_VZ2, /* Input Vector 2 (Z) */ - gte_in_rgb = C2_RGB, /* Input Color (R, G, B, Code) */ + gte_in_rgb = C2_RGB, /* Input Color (R, G, B, MipsAtom) */ gte_out_scr_xy0 = C2_SXY0, /* Output Screen Coord 0 (X, Y) */ gte_out_scr_xy1 = C2_SXY1, /* Output Screen Coord 1 (X, Y) */ gte_out_scr_xy2 = C2_SXY2, /* Output Screen Coord 2 (X, Y) */ @@ -379,6 +379,12 @@ enum { _C2_OPS_ = 0 #define gte_cmdw_op (gte_cmd_base | enc_gte_cmd(gte_cmd_op )) #define gte_cmdw_mvmva (gte_cmd_base | enc_gte_cmd(gte_cmd_mvmva)) +/* PsyQ compatibility bits for AVSZ3 (Bits 20, 22, 24 must be set) */ +#define gte_cmdw_psyq_avsz3_compat (0x15 << 20) +#define gte_cmd_avsz3 0x2D + +#define gte_cmdw_avsz3 (gte_cmd_base | enc_gte_cmd(gte_cmd_avsz3) | gte_cmdw_psyq_avsz3_compat) + /** * @brief Loads a single SVECTOR to GTE vector register V0 * @@ -646,9 +652,3 @@ enum { , r_use(r0) \ asm_clobber: clb_system, rlit(R_T4_Code), rlit(R_T5_Code), rlit(R_T6_Code) \ ) - -/* PsyQ compatibility bits for AVSZ3 (Bits 20, 22, 24 must be set) */ -#define gte_cmdw_psyq_avsz3_compat (0x15 << 20) -#define gte_cmd_avsz3 0x2D - -#define gte_cmdw_avsz3 (gte_cmd_base | enc_gte_cmd(gte_cmd_avsz3) | gte_cmdw_psyq_avsz3_compat) diff --git a/code/duffle/lottes_tape.h b/code/duffle/lottes_tape.h index 18232f6..755864c 100644 --- a/code/duffle/lottes_tape.h +++ b/code/duffle/lottes_tape.h @@ -7,215 +7,248 @@ # include "memory.h" #endif -/* R_T8 is our dedicated Tape Pointer (TP) */ -#define R_TP R_T8 -#define R_TP_Code R_T8_Code +/* --------------------------------------------------------------------------- + * TAPE DRIVE ABI & REGISTER ALIASES + * --------------------------------------------------------------------------- + * We map the MIPS temporary registers to a persistent global workspace. + * The C compiler is completely unaware of these bindings. + * ---------------------------------------------------------------------------*/ +enum { + R_TapePtr = R_T8, /* The Instruction Stream Pointer */ + R_PrimCur = R_T7, /* VRAM output cursor (primitive buffer) */ + R_FaceCur = R_T4, /* Input data cursor (indices/faces) */ + R_VertBase = R_T5, /* Base address of the vertex array */ + R_OtBase = R_T6, /* Base address of the Ordering Table */ +/* Stringification codes for the GCC inline assembler clobber lists */ +#define R_TapePtr_Code R_T8_Code +#define R_PrimCur_Code R_T7_Code +#define R_FaceCur_Code R_T4_Code +#define R_VertBase_Code R_T5_Code +#define R_OtBase_Code R_T6_Code +}; -/* The 'Yield' sequence for CodeBlobs */ -#define mips_yield \ - load_word(R_T9, R_TP, 0) \ - , add_ui(R_TP, R_TP, 4) \ - , jump_reg(R_T9) \ - , nop +/* The 'Yield' sequence for Tape Atoms. + * Loads the next pointer from the tape, advances the tape, and jumps. + * Cost: ~ 4 cycles */ +#define mips_yield() \ + load_word(R_T9, R_TapePtr, 0) \ + , add_ui_1( R_TapePtr, 4) \ + , jump_reg( R_T9) \ + , nop /* The 'Exit' Atom */ -internal Code CodeBlob_(tape_exit) { jump_reg(rret_addr), nop }; +MipsAtom_(tape_exit) { jump_reg(rret_addr), nop }; typedef Slice_(U4); -FI_ void tape_run(Slice_U4 tape, B1** r_prim_cursor, void* face_cursor, void* vert_base, void* ot_base) { - register U4* tp rgcc(R_TP) = tape.ptr; - register B1* pcur rgcc(R_T7) = r_prim_cursor[0]; - register void* r_t4 rgcc(R_T4) = face_cursor; - register void* r_t5 rgcc(R_T5) = vert_base; - register void* r_t6 rgcc(R_T6) = ot_base; +/* Generalized Tape Engine Runner */ +FI_ void tape_run(Slice_U4 tape) { register U4* tp rgcc(R_TapePtr) = tape.ptr; asm volatile( + asm_words( + add_ui( R_SP, R_SP, -8) /* Allocate stack space */ + , store_word(R_RA, R_SP, 0) /* Safely backup $ra to the stack */ + , load_word( R_T9, R_TapePtr, 0) /* Bootstrap the first jump */ + , add_ui_1( R_TapePtr, 4) /* Advance tape */ + , jump_nreg(R_T9) /* jalr $t9 */ + , nop /* Branch delay slot */ + , load_word(R_RA, R_SP, 0) /* Restore $ra from stack */ + , add_ui_1( R_SP, 8) /* Deallocate stack space */ + ) + asm_rpins, r_use(tp) + asm_clobber: + rlit(R_AT_Code) + , rlit(R_V0_Code), rlit(R_V1_Code) + , rlit(R_T0_Code), rlit(R_T1_Code), rlit(R_T2_Code), rlit(R_T3_Code) + /* Tell GCC the tape engine owns and destroys the workspace registers */ + , rlit(R_PrimCur_Code), rlit(R_FaceCur_Code), rlit(R_VertBase_Code), rlit(R_OtBase_Code) + , rlit(R_T9_Code) + , clb_mem_drain +); } - asm volatile( - "addiu $sp, $sp, -8;" /* Allocate stack space */ - "sw $31, 0($sp);" /* Safely backup $ra to the stack */ - "lw $25, 0(%0);" - "addiu %0, %0, 4;" - "jalr $25;" - "nop;" - "lw $31, 0($sp);" /* Restore $ra from stack */ - "addiu $sp, $sp, 8;" /* Deallocate stack space */ - : "+r"(tp), "+r"(pcur), "+r"(r_t4), "+r"(r_t5), "+r"(r_t6) - : - : "at", "v0", "v1", "t0", "t1", "t2", "t3", "t9", "memory" - ); - - r_prim_cursor[0] = pcur; -} +typedef Struct_(TapeBuilder) { U4 ptr; U4 count; }; +FI_ void tb_init(TapeBuilder* tb, FArena* arena) { tb->ptr = arena->start; tb->count = 0; } +FI_ TapeBuilder tb_make( FArena* arena) { return (TapeBuilder){ arena->start, 0 }; } -typedef Struct_(TapeBuilder) { U4* ptr; U4 count; }; -FI_ TapeBuilder tb_begin(FArena* arena) { return (TapeBuilder){ (U4*)arena->start, 0 }; } +FI_ void tb_emit(TapeBuilder* tb, MipsAtom* atom) { u4_r(tb->ptr)[tb->count] = u4_(atom); ++ tb->count; } +FI_ void tb_data(TapeBuilder* tb, U4 data) { u4_r(tb->ptr)[tb->count] = u4_(data); ++ tb->count; } -I_ void tb_emit(TapeBuilder* tb, Code* atom) { r_(tb->ptr)[tb->count] = u4_(atom); ++ tb->count; } -I_ Slice_U4 tb_end(TapeBuilder* tb) { - tb_emit(tb, code_tape_exit); - return (Slice_U4){ tb->ptr, tb->count }; -} +FI_ Slice_U4 tb_end (TapeBuilder* tb) { tb_emit(tb,code_tape_exit); return (Slice_U4){ C_(U4*,tb->ptr), tb->count }; } +FI_ Slice_U4 tb_slice(TapeBuilder tb) { return (Slice_U4){ C_(U4*,tb.ptr), tb.count }; } +#define tb_scope(tb) for(U4 tbs_once=0;tbs_once==0;++tbs_once,tb_emit(tb,code_tape_exit)) -internal Code CodeBlob_(atom_set_gte_world) { - /* Pop matrix address from tape into R_T3 ($11) */ - load_word(R_T3, R_TP, 0), - add_ui(R_TP, R_TP, 4), +/* --------------------------------------------------------------------------- + * MACRO ATOMS (Reusable Assembly Components) + * These do NOT yield. They are expanded inline inside Tape Atoms. + * ---------------------------------------------------------------------------*/ - /* Load 3x3 Rotation + 3x1 Translation from R_T3 into GTE CONTROL Regs (ctc2) */ - load_word(R_T0, R_T3, 0), load_word(R_T1, R_T3, 4), - gte_ct(R_T0, gte_cr_RT11), gte_ct(R_T1, gte_cr_RT12), - - load_word(R_T0, R_T3, 8), load_word(R_T1, R_T3, 12), load_word(R_T2, R_T3, 16), - gte_ct(R_T0, gte_cr_RT13), gte_ct(R_T1, gte_cr_RT21), gte_ct(R_T2, gte_cr_RT22), - - load_word(R_T0, R_T3, 20), load_word(R_T1, R_T3, 24), load_word(R_T2, R_T3, 28), - gte_ct(R_T0, gte_cr_TRX), gte_ct(R_T1, gte_cr_TRY), gte_ct(R_T2, gte_cr_TRZ), - - mips_yield +/* Loads 3 16-bit indices from the face array */ +#define mac_load_tri_indices(r_idx0, r_idx1, r_idx2) \ + load_half_u(r_idx0, R_FaceCur, 0) \ + , load_half_u(r_idx1, R_FaceCur, 2) \ + , load_half_u(r_idx2, R_FaceCur, 4) + +/* Translates indices to vertex addresses and pushes them to GTE */ +#define mac_load_tri_verts(r_idx0, r_idx1, r_idx2) \ + shift_ll(R_AT, r_idx0, 3), add_u(R_AT, R_AT, R_VertBase), load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4), gte_mt(R_V0, C2_VXY0), gte_mt(R_V1, C2_VZ0) \ + , shift_ll(R_AT, r_idx1, 3), add_u(R_AT, R_AT, R_VertBase), load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4), gte_mt(R_V0, C2_VXY1), gte_mt(R_V1, C2_VZ1) \ + , shift_ll(R_AT, r_idx2, 3), add_u(R_AT, R_AT, R_VertBase), load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4), gte_mt(R_V0, C2_VXY2), gte_mt(R_V1, C2_VZ2) + +/* Formats the primitive memory layout (Tag + Color + Coordinates) */ +#define mac_format_prim_f3(color_hi, color_lo) \ + store_word(R_0, R_PrimCur, 0) \ + , load_ui(R_AT, color_hi), or_i(R_AT, R_AT, color_lo) \ + , store_word(R_AT, R_PrimCur, 4) \ + , gte_sw(C2_SXY0, R_PrimCur, 8) \ + , gte_sw(C2_SXY1, R_PrimCur, 12) \ + , gte_sw(C2_SXY2, R_PrimCur, 16) + +/* Correctly inserts a primitive into the Ordering Table linked list */ +#define mac_insert_ot_tag(r_otz, prim_length) \ + shift_ll( R_T1, r_otz, 2) \ + , add_u( R_T1, R_T1, R_OtBase) /* T1 = &OrderingTable[OTZ] */ \ + , load_word( R_AT, R_T1, 0) /* AT = old_ot_head */ \ + , load_ui( R_V0, prim_length) /* V0 = Length << 24 */ \ + , shift_ll( R_AT, R_AT, 8) /* Strip upper 8 bits from old_ot */ \ + , shift_lr( R_AT, R_AT, 8) \ + , or_u( R_AT, R_AT, R_V0) /* Merge length */ \ + , store_word(R_AT, R_PrimCur, 0) /* prim->tag = old_ot_head */ \ + , shift_ll( R_AT, R_PrimCur, 8) /* AT = PrimCur & 0x00FFFFFF */ \ + , shift_lr( R_AT, R_AT, 8) \ + , store_word(R_AT, R_T1, 0) /* OrderingTable[OTZ] = PrimCur */ + +internal MipsAtom_(bind_workspace) { + /* Pop 4 arguments from the tape directly into the workspace registers */ + load_word(R_PrimCur, R_TapePtr, 0), + load_word(R_FaceCur, R_TapePtr, 4), + load_word(R_VertBase, R_TapePtr, 8), + load_word(R_OtBase, R_TapePtr, 12), + add_ui_1( R_TapePtr, 16), + mips_yield() }; -internal Code CodeBlob_(atom_floor_tri) { - /* 1. Load 3 indices from $t4 */ - load_half_u(R_T0, R_T4, 0), - load_half_u(R_T1, R_T4, 2), - load_half_u(R_T2, R_T4, 4), +internal MipsAtom_(sync_prim_cursor) { + /* Pop the C-struct address and base address from the tape */ + load_word(R_AT, R_TapePtr, 0), /* AT = &pa->used */ + load_word(R_T0, R_TapePtr, 4), /* T0 = prim_base */ + add_ui_1( R_TapePtr, 8), + /* Calculate byte offset and store directly back to RAM */ + sub_u(R_T0, R_PrimCur, R_T0), + store_word(R_T0, R_AT, 0), + mips_yield() +}; - /* 2. Load Vertices: Addr = Base + (idx * 8). Write to GTE DATA Regs (mtc2) */ - shift_ll( R_AT, R_T0, 3), add_u( R_AT, R_AT, R_T5), - load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4), - gte_mt( R_V0, C2_VXY0), gte_mt( R_V1, C2_VZ0), +internal MipsAtom_(set_gte_world) { + /* Pop matrix address from tape into R_T3 ($11) */ + load_word(R_T3, R_TapePtr, 0), + add_ui_1( R_TapePtr, 4), - shift_ll( R_AT, R_T1, 3), add_u( R_AT, R_AT, R_T5), - load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4), - gte_mt( R_V0, C2_VXY1), gte_mt( R_V1, C2_VZ1), + /* Load 3x3 Rotation + 3x1 Translation from R_T3 into GTE CONTROL Regs (ctc2) */ + load_word(R_T0, R_T3, 0), load_word(R_T1, R_T3, 4), + gte_ct( R_T0, gte_cr_RT11), gte_ct( R_T1, gte_cr_RT12), + load_word(R_T0, R_T3, 8), load_word(R_T1, R_T3, 12), load_word(R_T2, R_T3, 16), + gte_ct( R_T0, gte_cr_RT13), gte_ct( R_T1, gte_cr_RT21), gte_ct( R_T2, gte_cr_RT22), + load_word(R_T0, R_T3, 20), load_word(R_T1, R_T3, 24), load_word(R_T2, R_T3, 28), + gte_ct( R_T0, gte_cr_TRX), gte_ct( R_T1, gte_cr_TRY), gte_ct( R_T2, gte_cr_TRZ), - shift_ll( R_AT, R_T2, 3), add_u( R_AT, R_AT, R_T5), - load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4), - gte_mt( R_V0, C2_VXY2), gte_mt( R_V1, C2_VZ2), + mips_yield() +}; - /* 3. RTPT + NCLIP */ - nop, nop, gte_cmdw_rtpt, - nop, nop, gte_cmdw_nclip, - nop, nop, /* Wait for NCLIP to finish */ +internal MipsAtom_(floor_tri) { + mac_load_tri_indices(R_T0, R_T1, R_T2), + mac_load_tri_verts( R_T0, R_T1, R_T2), - /* 4. Check NCLIP. - If MAC0 <= 0 (Backface), branch to end. - Target is 29 instructions past the delay slot. */ - gte_mf(R_T0, C2_MAC0), - nop, /* <--- FILL LOAD DELAY SLOT */ - branch_le_zero(R_T0, 29), /* <--- CULLING RE-ENABLED */ - nop, /* <--- DELAY SLOT (Index 0) */ + /* 3. Execute Math */ + nop, nop, gte_cmdw_rtpt, + nop, nop, gte_cmdw_nclip, + nop, nop, - /* 5. Store Primitive Data */ - /* 1 */ store_word(R_0, R_T7, 0), - /* 2 */ load_ui( R_AT, 0x20FF), /* High: Code 0x20 + Color B:FF */ - /* 3 */ or_i( R_AT, R_AT, 0xFFFF), /* Low: Color G:FF, R:FF (White) */ - /* 4 */ store_word(R_AT, R_T7, 4), - /* 5 */ enc_gte_sw(C2_SXY0, R_T7, 8), - /* 6 */ enc_gte_sw(C2_SXY1, R_T7, 12), - /* 7 */ enc_gte_sw(C2_SXY2, R_T7, 16), + /* 4. Culling (Branch forward 29 instructions if Backface) */ + gte_mf(R_T0, C2_MAC0), + nop, + branch_le_zero(R_T0, 29), + nop, + + /* 5. Format Primitive */ + mac_format_prim_f3(0x20FF, 0xFFFF), /* High: 0x20/B, Low: G/R */ + + /* 6. Calculate Depth */ + nop, nop, gte_cmdw_avsz3, + nop, nop, + gte_mf(R_T1, C2_OTZ), + + /* 7. Bounds Check OTZ < 2048 (Branch forward 13 instructions to skip insertion) */ + add_ui( R_AT, R_0, 2048), + slt_u( R_AT, R_T1, R_AT), + branch_equal(R_AT, R_0, 13), + nop, + + /* 8. Insert into Ordering Table Linked List */ + mac_insert_ot_tag(R_T1, 0x0400), /* Length = 4 words */ - /* 6. OT Insertion with Bounds Checking */ - /* 8 */ nop, - /* 9 */ nop, - /* 10 */ gte_cmdw_avsz3, - /* 11 */ nop, /* Wait for AVSZ3 */ - /* 12 */ nop, /* Wait for AVSZ3 */ - /* 13 */ gte_mf(R_T1, C2_OTZ), /* T1 = Depth index */ - - /* Bounds Check: OTZ < 2048 */ - /* 14 */ add_ui( R_AT, R_0, 2048), - /* 15 */ slt_u( R_AT, R_T1, R_AT), /* AT = (OTZ < 2048) ? 1 : 0 */ - /* 16 */ branch_equal(R_AT, R_0, 13), /* If AT == 0, skip to end (13 instrs past delay) */ - /* 17 */ nop, /* <--- DELAY SLOT (Index 0 for Bounds branch) */ - - /* CORRECTED DMA TAG LOGIC */ - /* 18 (1) */ shift_ll( R_T1, R_T1, 2), - /* 19 (2) */ add_u( R_T1, R_T1, R_T6), /* T1 = & OrderingTable[OTZ] */ - - /* 20 (3) */ load_word( R_AT, R_T1, 0), /* AT = current head (old_ot) */ - /* 21 (4) */ load_ui( R_V0, 0x0400), /* V0 = 0x04000000 (Length = 4) */ - - /* Strip upper 8 bits from old_ot */ - /* 22 (5) */ shift_ll(R_AT, R_AT, 8), - /* 23 (6) */ shift_lr(R_AT, R_AT, 8), /* AT &= 0x00FFFFFF */ - - /* Merge length into prim->tag */ - /* 24 (7) */ or_u(R_AT, R_AT, R_V0), /* AT |= 0x04000000 */ - /* 25 (8) */ store_word(R_AT, R_T7, 0), /* prim->tag = AT */ - - /* Create OT Tag: Len 0 (0x00) in top 8 bits, T7 in bottom 24 */ - /* 26 (9) */ shift_ll(R_AT, R_T7, 8), - /* 27 (10) */ shift_lr(R_AT, R_AT, 8), /* AT = T7 & 0x00FFFFFF */ - /* 28 (11) */ store_word(R_AT, R_T1, 0), /* OrderingTable[OTZ] = AT */ - - /* 29 (12) */ add_ui( R_T7, R_T7, 20), /* Advance Prim Cursor (5 words) */ - - /* 7. Yield */ - /* 30 (13) */ add_ui(R_T4, R_T4, 8), /* Advance Face Cursor (4 * S2 = 8 bytes) */ - mips_yield + add_ui(R_PrimCur, R_PrimCur, 20), /* Advance Prim Cursor (5 words) */ + /* 9. Advance Input Cursor & Yield (Both branch targets land here) */ + add_ui(R_FaceCur, R_FaceCur, 8), /* Advance Face Cursor (4 * S2 = 8 bytes) */ + mips_yield() }; /* DIAGNOSTIC 1: Pure tape loop test */ -internal Code CodeBlob_(atom_diag_yield) { - mips_yield -}; +internal MipsAtom_(diag_yield) { mips_yield() }; /* DIAGNOSTIC 2: Pure memory test (No GTE). Draws a fixed cyan triangle. */ -internal Code CodeBlob_(atom_diag_color) { - store_word(R_0, R_T7, 0), - load_ui(R_AT, 0x20FF), /* High: Code 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), - - /* Fake coordinates - Swapped winding order to prevent GPU culling! */ - load_ui(R_AT, 0x0010), or_i(R_AT, R_AT, 0x0010), store_word(R_AT, R_T7, 8), /* (16, 16) */ - load_ui(R_AT, 0x0050), or_i(R_AT, R_AT, 0x0010), store_word(R_AT, R_T7, 12), /* (80, 16) */ - load_ui(R_AT, 0x0010), or_i(R_AT, R_AT, 0x0050), store_word(R_AT, R_T7, 16), /* (16, 80) */ +internal MipsAtom_(diag_color) { + store_word(R_0, R_T7, 0), + load_ui( R_AT, 0x20FF), /* High: MipsAtom 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), + + /* Fake coordinates - Swapped winding order to prevent GPU culling! */ + load_ui(R_AT, 0x0010), or_i(R_AT, R_AT, 0x0010), store_word(R_AT, R_T7, 8), /* (16, 16) */ + load_ui(R_AT, 0x0050), or_i(R_AT, R_AT, 0x0010), store_word(R_AT, R_T7, 12), /* (80, 16) */ + load_ui(R_AT, 0x0010), or_i(R_AT, R_AT, 0x0050), store_word(R_AT, R_T7, 16), /* (16, 80) */ - add_ui(R_T1, R_0, 10), - shift_ll(R_T1, R_T1, 2), - add_u(R_T1, R_T1, R_T6), - - load_word(R_AT, R_T1, 0), - load_ui(R_V0, 0x0400), /* <--- Fills load delay slot! */ - store_word(R_AT, R_T7, 0), - - shift_ll(R_AT, R_T7, 8), shift_lr(R_AT, R_AT, 8), - or_u(R_AT, R_AT, R_V0), - store_word(R_AT, R_T1, 0), + add_ui( R_T1, R_0, 10), + shift_ll(R_T1, R_T1, 2), + add_u( R_T1, R_T1, R_T6), + + load_word( R_AT, R_T1, 0), + load_ui( R_V0, 0x0400), // <--- Fills load delay slot! + store_word(R_AT, R_T7, 0), + + shift_ll( R_AT, R_T7, 8), shift_lr(R_AT, R_AT, 8), + or_u( R_AT, R_AT, R_V0), + store_word(R_AT, R_T1, 0), - add_ui(R_T7, R_T7, 20), - mips_yield + add_ui(R_T7, R_T7, 20), + + mips_yield() }; /* DIAGNOSTIC 3: Pure GTE test (No Memory Writes) */ -internal Code CodeBlob_(atom_diag_gte) { - /* Load 3 indices */ - load_half_u(R_T0, R_T4, 0), - load_half_u(R_T1, R_T4, 2), - load_half_u(R_T2, R_T4, 4), +internal MipsAtom_(diag_gte) { + /* Load 3 indices */ + load_half_u(R_T0, R_T4, 0), + load_half_u(R_T1, R_T4, 2), + load_half_u(R_T2, R_T4, 4), - /* Load Vertices into GTE */ - shift_ll(R_AT, R_T0, 3), add_u(R_AT, R_AT, R_T5), - load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4), - gte_mt(R_V0, C2_VXY0), gte_mt(R_V1, C2_VZ0), + /* Load Vertices into GTE */ + shift_ll( R_AT, R_T0, 3), add_u( R_AT, R_AT, R_T5), + load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4), + gte_mt( R_V0, C2_VXY0), gte_mt( R_V1, C2_VZ0), - shift_ll(R_AT, R_T1, 3), add_u(R_AT, R_AT, R_T5), - load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4), - gte_mt(R_V0, C2_VXY1), gte_mt(R_V1, C2_VZ1), + shift_ll( R_AT, R_T1, 3), add_u( R_AT, R_AT, R_T5), + load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4), + gte_mt( R_V0, C2_VXY1), gte_mt( R_V1, C2_VZ1), - shift_ll(R_AT, R_T2, 3), add_u(R_AT, R_AT, R_T5), - load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4), - gte_mt(R_V0, C2_VXY2), gte_mt(R_V1, C2_VZ2), + shift_ll( R_AT, R_T2, 3), add_u( R_AT, R_AT, R_T5), + load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4), + gte_mt( R_V0, C2_VXY2), gte_mt( R_V1, C2_VZ2), - /* Run Math */ - nop, nop, gte_cmdw_rtpt, - nop, nop, gte_cmdw_nclip, - nop, nop, + /* Run Math */ + nop, nop, gte_cmdw_rtpt, + nop, nop, gte_cmdw_nclip, + nop, nop, - /* Advance Face Cursor and Yield */ - add_ui(R_T4, R_T4, 8), - mips_yield + /* Advance Face Cursor and Yield */ + add_ui(R_T4, R_T4, 8), + + mips_yield() }; - diff --git a/code/duffle/memory.h b/code/duffle/memory.h index 231f876..3a86802 100644 --- a/code/duffle/memory.h +++ b/code/duffle/memory.h @@ -100,14 +100,15 @@ FI_ void farena_init(FArena_R arena, Slice mem) { assert(arena != nullptr); arena->used = 0; } FI_ FArena farena_make(Slice mem) { FArena a; farena_init(& a, mem); return a; } -I_ Slice farena_push(FArena_R arena, U4 amount, Opt_farena o) { +I_ Slice farena_push(FArena_R arena, U4 amount, Opt_farena o) { if (amount == 0) { return (Slice){}; } U4 desired = amount * (o.type_width == 0 ? 1 : o.type_width); U4 to_commit = align_pow2(desired, o.alignment ? o.alignment : MEM_ALIGNMENT_DEFAULT); - mem_bump(arena->start, arena->capacity - to_commit, & arena->used, to_commit); - return (Slice){ arena->start + arena->used, to_commit }; + U4 ptr = arena->start + arena->used; + mem_bump(arena->start, arena->capacity, & arena->used, to_commit); + return (Slice){ ptr, to_commit }; } -FI_ void farena_reset(FArena_R arena) { arena->used = 0; } +FI_ void farena_reset (FArena_R arena) { arena->used = 0; } FI_ void farena_rewind(FArena_R arena, U4 save_point) { U4 end = arena->start + arena->used; assert_bounds(save_point, arena->start, end); arena->used -= save_point - arena->start; diff --git a/code/duffle/mips.h b/code/duffle/mips.h index 191229e..3d66be9 100644 --- a/code/duffle/mips.h +++ b/code/duffle/mips.h @@ -246,6 +246,9 @@ enum { _BitOffsets = 0 #define xor_i(rt, rs, imm) enc_i(op_xori, (rs), (rt), (imm)) #define load_ui(rt, imm) enc_i(op_lui, R_0, (rt), (imm)) +// Ergonomic add to the same register. +#define add_ui_1(rt_rs, imm) enc_i(op_addiu, (rt_rs), (rt_rs), (imm)) + /* Logic Opcodes */ #define and_u(rd, rs, rt) enc_r(op_special, (rs), (rt), (rd), 0, fc_and) @@ -467,8 +470,8 @@ enum { _BitOffsets = 0 // Binary Metaprogramming -typedef U4 const Code; -#define CodeBlob_(sym) tmpl(code,sym) [] align_(4) = +typedef U4 const MipsAtom; +#define MipsAtom_(sym) MipsAtom tmpl(code,sym) [] align_(4) = enum { bios_flushcache = 0x44, @@ -486,8 +489,7 @@ enum { * 5. lw $ra, 4($sp); jr $ra ; restore & return * 6. sp += 8 */ -internal -Code CodeBlob_(mips_flush_icache) { +internal MipsAtom_(mips_flush_icache) { add_ui(rstack_ptr, rstack_ptr, -8) /* sp -= 8 */ , store_word(rret_addr, rstack_ptr, 4) /* sw $ra, 4($sp) */ , add_ui(rret_0, rdiscard, bios_flushcache) /* addiu $a0, $0, 0x44 */ diff --git a/code/gte_hello/hello_gte.c b/code/gte_hello/hello_gte.c index a8b95c3..c6b25df 100644 --- a/code/gte_hello/hello_gte.c +++ b/code/gte_hello/hello_gte.c @@ -112,7 +112,7 @@ I_ B1* prim__alloc(U4 type_width, Str8 type_name) { gknown PrimitiveArena* pa = & static_mem.primitives; gknown B1* buf = (B1*) r_(static_mem.primitives.buf)[static_mem.active_buf_id]; assert(pa->used + type_width < PrimitiveBuff_Len); - B1* next = buf + pa->used; + B1* next = buf + pa->used; pa->used += type_width; return next; } @@ -294,59 +294,57 @@ void update(PrimitiveArena* pa, U4* ordering_buf) gte_matrix_set_rotation (& static_mem.tform_world); gte_matrix_set_translation(& static_mem.tform_world); - LP_ U4 mem_temp_tape[512]; // Buffer for function addresses - FArena tape_arena; farena_init(&tape_arena, slice_ut(mem_temp_tape, S_(mem_temp_tape))); - - TapeBuilder tb = tb_begin(&tape_arena); { - // Setup state atoms - m3s2_rotation( & static_mem.floor.rot, & static_mem.tform_world); - m3s2_translation( & static_mem.tform_world, & static_mem.floor.pos); - - // Push "Protocol" to tape - tb_emit(& tb, code_atom_set_gte_world); - tb_emit(& tb, (Code*)& static_mem.tform_world); - for (U4 i = 0; i < Floor_num_faces; i++) { - tb_emit(& tb, code_atom_floor_tri); - } - } - Slice_U4 tape = tb_end(& tb); - // --- EXECUTION --- - B1* prim_cursor = (B1*)r_(pa->buf)[static_mem.active_buf_id] + pa->used; - // 2. Fire the Tape Drive (Explicitly bind the workspace variables) - tape_run(tape, & prim_cursor, static_mem.floor.faces, static_mem.floor.verts, ordering_buf); + U4 prim_base = u4_(pa->buf[static_mem.active_buf_id]); + U4 prim_cursor = prim_base + pa->used; - // 3. Update C-side state - pa->used = (U4)prim_cursor - (U4)r_(pa->buf)[static_mem.active_buf_id]; - static_mem.floor.rot.y += 5; + // Prepare the tape. + LP_ U4 mem_temp_tape[512]; FArena tape_arena; farena_init(& tape_arena, slice_ut_arr(mem_temp_tape)); + TapeBuilder tb = tb_make(&tape_arena); tb_scope(& tb) { + // Push "Protocol" to tape + tb_emit(& tb, code_bind_workspace); + tb_data(& tb, prim_cursor); + tb_data(& tb, u4_(static_mem.floor.faces)); + tb_data(& tb, u4_(static_mem.floor.verts)); + tb_data(& tb, u4_(ordering_buf)); + + tb_emit(& tb, code_set_gte_world); + tb_data(& tb, u4_(& static_mem.tform_world)); + + for (U4 i = 0; i < Floor_num_faces; i++) { + tb_emit(& tb, code_floor_tri); + } + + tb_emit(& tb, code_sync_prim_cursor); + tb_data(& tb, u4_(& pa->used)); + tb_data(& tb, prim_base); + } + + // Fire off the tape. + tape_run(tb_slice(tb)); + + // C-side state (pa->used) has already been updated by the tape! + static_mem.floor.rot.y += 5; } // --- TAPE DIAGNOSTICS --- if (0) { - LP_ U4 mem_temp_tape[512]; - FArena tape_arena; - farena_init(&tape_arena, slice_ut(mem_temp_tape, S_(mem_temp_tape))); - - TapeBuilder tb = tb_begin(&tape_arena); { + LP_ U4 mem_temp_tape[512]; FArena tape_arena; farena_init(& tape_arena, slice_ut_arr(mem_temp_tape)); + TapeBuilder tb = tb_make(& tape_arena); tb_scope(& tb) { // Skip set_gte_world atom for diagnostics to isolate the triangle loop for (U4 i = 0; i < Floor_num_faces; i++) { // ======================================================= // SWAP EMIT TO TEST DIFFERENT PARTS OF THE PIPELINE: // ======================================================= - // 1. code_atom_diag_yield -> Tests Tape Engine jump logic - // 2. code_atom_diag_color -> Tests OT and Prim Arena memory - // 3. code_atom_diag_gte -> Tests Vertex arrays and GTE Math - // tb_emit(&tb, code_atom_diag_yield); - tb_emit(& tb, code_atom_diag_color); - // tb_emit(&tb, code_atom_diag_gte); + // 1. code_diag_yield -> Tests Tape Engine jump logic + // 2. code_diag_color -> Tests OT and Prim Arena memory + // 3. code_diag_gte -> Tests Vertex arrays and GTE Math + // tb_emit(& tb, code_diag_yield); + // tb_emit(& tb, code_diag_color); + // tb_emit(& tb, code_diag_gte); } } - Slice_U4 tape = tb_end(& tb); - - // Setup Workspace Registers B1* prim_cursor = (B1*)r_(pa->buf)[static_mem.active_buf_id] + pa->used; - - tape_run(tape, & prim_cursor, static_mem.floor.faces, static_mem.floor.verts, ordering_buf); - + tape_run(tb_slice(tb)); pa->used = (U4)prim_cursor - (U4)r_(pa->buf)[static_mem.active_buf_id]; static_mem.floor.rot.y += 5; } @@ -372,8 +370,7 @@ int main(void) } // gknown gp_screen_init(); gp_screen_init_c11(& static_mem.screen_buf, & static_mem.active_buf_id); - while (1) - { + while (1) { gknown S4* active_buf_id = & static_mem.active_buf_id; gknown U4* ordering_buf = r_(static_mem.ordering_tbl)[active_buf_id[0]]; gknown PrimitiveArena* pa = & static_mem.primitives;