diff --git a/code/duffle/atom_dsl.h b/code/duffle/atom_dsl.h index 282ad0e..932fe4f 100644 --- a/code/duffle/atom_dsl.h +++ b/code/duffle/atom_dsl.h @@ -1,8 +1,8 @@ /* - * tape_atom_dsl.h + * atom_dsl.h * ============================================================================ * - * TAPE ATOM DSL — annotation layer for tape atoms (lottes_tape.h). + * ATOM DSL — annotation layer for tape atoms (lottes_tape.h). * * This header turns `__attribute__((annotate(...)))` and `_Pragma(...)` into * a small named DSL that the metaprogram can validate against. diff --git a/code/duffle/lottes_tape.h b/code/duffle/lottes_tape.h index cb3701c..c8f6df2 100644 --- a/code/duffle/lottes_tape.h +++ b/code/duffle/lottes_tape.h @@ -44,14 +44,14 @@ MipsAtom_(tape_exit) { jump_reg(rret_addr), nop }; /* 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 */ + add_ui( R_SP, R_SP, -MipsStackAlignment) /* Allocate stack space */ + , store_word(R_RA, R_SP, 0) /* Safely backup $ra to the stack */ + , load_word( R_AtomJmp, R_TapePtr, 0) /* Bootstrap the first jump */ + , add_ui_1( R_TapePtr, S_(MipsCode)) /* Advance tape */ + , jump_nreg( R_AtomJmp) /* jalr $t9 */ + , nop /* Branch delay slot */ + , load_word(R_RA, R_SP, 0) /* Restore $ra from stack */ + , add_ui_1( R_SP, MipsStackAlignment) /* Deallocate stack space */ ) asm_rpins, r_use(tp) asm_clobber: @@ -66,7 +66,7 @@ FI_ void tape_run(Slice_U4 tape) { register U4* tp rgcc(R_TapePtr) = tape.ptr; a typedef Relative_(FArena) Struct_(TapeBuilder) { U4 ptr; U4 capacity; U4 used; }; FI_ void tb_init(TapeBuilder* tb, FArena* arena) { tb->ptr = arena->start; tb->used = 0; } -FI_ TapeBuilder tb_make_old( FArena* arena) { return (TapeBuilder){ arena->start, 0 }; } +FI_ TapeBuilder tb_make_old( FArena* arena) { return (TapeBuilder){ arena->start, 0 }; } FI_ TapeBuilder tb_make(Slice mem) { return (TapeBuilder){ mem.ptr, mem.len, 0 }; } #define tb_emit_(tb, atom) tb_emit(tb, tmpl(code,atom)) @@ -88,17 +88,17 @@ FI_ Slice_U4 tb_slice(TapeBuilder tb) { return (Sli /* The 'Yield' sequence for Tape Atoms. * Loads the next pointer from the tape, advances the tape, and jumps. * Cost: ~ 4 cycles */ -#define mac_yield() \ - load_word(R_AtomJmp, R_TapePtr, 0) \ - , add_ui_1( R_TapePtr, 4) \ - , jump_reg( R_AtomJmp) \ +#define mac_yield() \ + load_word(R_AtomJmp, R_TapePtr, 0) \ + , add_ui_1( R_TapePtr, S_(MipsCode)) \ + , jump_reg( R_AtomJmp) \ , nop /* Words: 3; Loads 3 S2 indices from the face array */ #define mac_load_tri_indices(rId_0, rId_1, rId_2) \ - load_half_u(rId_0, R_FaceCursor, 0) \ - , load_half_u(rId_1, R_FaceCursor, 2) \ - , load_half_u(rId_2, R_FaceCursor, 4) + load_half_u(rId_0, R_FaceCursor, 0 * S_(S2)) \ + , load_half_u(rId_1, R_FaceCursor, 1 * S_(S2)) \ + , load_half_u(rId_2, R_FaceCursor, 2 * S_(S2)) /* Words: 18; Translates indices to vertex addresses and pushes them to GTE R_AT = rId_[#] << 3; @@ -112,6 +112,7 @@ FI_ Slice_U4 tb_slice(TapeBuilder tb) { return (Sli , shift_ll(R_AT, rId_1, 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, rId_2, 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) + //TODO(Ed): Add more type annotation /* Words: 11; 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) \ @@ -176,6 +177,7 @@ enum { * 5. lw $ra, 4($sp); jr $ra ; restore & return * 6. sp += 8 */ +// TODO(Ed): Annotate magic offsets 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) */ @@ -189,11 +191,16 @@ internal MipsAtom_(mips_flush_icache) { , mac_yield() }; +typedef Struct_(Binds_SetGteWorld) { + U4 transform; +}; +// TODO(Ed): Bugged, fix 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), + load_word(R_T3, R_TapePtr, O_(Binds_SetGteWorld,transform)), + add_ui_1( R_TapePtr, S_(Binds_SetGteWorld)), +// TODO(Ed): Annotate magic offsets. /* 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), @@ -207,187 +214,10 @@ internal MipsAtom_(set_gte_world) { // TODO(Ed): I'm not sure yet if the bindings are redundant with the floortri atom yet. -typedef Struct_(Binds_CubeTri) { - U4 PrimCursor; - U4 FaceCursor; - U4 VertBase; - U4 OtBase; -}; -internal MipsAtom_(rbind_cube_tri) { - /* Pop 4 arguments from the tape directly into the workspace registers */ - load_word(R_PrimCursor, R_TapePtr, O_(Binds_CubeTri,PrimCursor)), - load_word(R_FaceCursor, R_TapePtr, O_(Binds_CubeTri,FaceCursor)), - load_word(R_VertBase, R_TapePtr, O_(Binds_CubeTri,VertBase)), - load_word(R_OtBase, R_TapePtr, O_(Binds_CubeTri,OtBase)), - add_ui_1( R_TapePtr, S_(Binds_CubeTri)), - // Note(Ed): This entire thing is argument shuffle? - // TODO(Ed): Eliminate - mac_yield() -}; - -/* ============================================================================ - * cube_tri — 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. - * - * PsyQ RotAverageNclip4 sequence: - * 1. Load V0=p0, V1=p1, V2=p2 - * 2. RTPT → SXY0=p0, SXY1=p1, SXY2=p2, SZ1,SZ2,SZ3 - * 3. NCLIP → MAC0 = cross(p0,p1,p2) ← BEFORE RTPS! - * 4. Store SXY0 (p0) to primitive buffer - * 5. Load V3 into V0 - * 6. RTPS → SXY0=p3, SZ0 - * 7. Store SXY0 (p3) to primitive buffer - * 8. AVSZ3 → OTZ from SZ1,SZ2,SZ3 - * - * PRIMITIVE FORMAT (Poly_G4 = 9 words = 36 bytes) - * ------------------------------------------------ - * Word 0 (offset 0): OT tag (set by mac_insert_ot_tag) - * Word 1 (offset 4): c0 + code = 0x38FF00FF (magenta, opcode 0x38) - * Word 2 (offset 8): p0 = SXY0 (stored BEFORE RTPS) - * Word 3 (offset 12): c1 + pad = 0x0000FFFF (yellow) - * Word 4 (offset 16): p1 = SXY1 - * Word 5 (offset 20): c2 + pad = 0x00FFFF00 (cyan) - * Word 6 (offset 24): p2 = SXY2 - * Word 7 (offset 28): c3 + pad = 0x0000FF00 (green) - * Word 8 (offset 32): p3 = SXY0 (stored AFTER RTPS) - * - * BRANCH OFFSETS - * ---------------------------------------------- - * Outer branch (backface cull): branch_le_zero(R_T0, 49) - * → Skip 49 instructions from BD slot, land at add_ui(R_FaceCur,...) - * Inner branch (OTZ bounds): branch_equal(R_AT, R_0, 13) - * → Skip 13 instructions from BD slot, land at add_ui(R_FaceCur,...) - * ============================================================================ */ - atom_region (cube_tri, REGION_PRIM_ARENA) - atom_group (cube_tri, GROUP_RENDER_PRIMS) - atom_cadence (cube_tri, CADENCE_FRAME) - atom_annot(cube_tri, phase_work, - tape_regs(R_PrimCursor, R_FaceCursor, R_VertBase, R_OtBase), - tape_regs(R_PrimCursor, R_FaceCursor)) -internal -MipsAtom_(cube_tri) { - /* ── 1. Load 4 face indices from R_FaceCur ──────────────────────────── */ - load_half_u(R_T0, R_FaceCursor, 0), /* T0 = face->x (vertex 0 index) */ - load_half_u(R_T1, R_FaceCursor, 2), /* T1 = face->y (vertex 1 index) */ - load_half_u(R_T2, R_FaceCursor, 4), /* T2 = face->z (vertex 2 index) */ - load_half_u(R_T3, R_FaceCursor, 6), /* T3 = face->w (vertex 3 index) */ - - /* ── 2. Load V0, V1, V2 into GTE ────────────────────────────────────── */ - /* V0 = verts[face->x] */ - shift_ll(R_AT, R_T0, 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), - - /* V1 = verts[face->y] */ - shift_ll(R_AT, R_T1, 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), - - /* V2 = verts[face->z] */ - shift_ll(R_AT, R_T2, 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), - - /* ── 3. RTPT — transforms V0/V1/V2 → SXY0/SXY1/SXY2 + SZ1/SZ2/SZ3 ─── */ - nop, nop, gte_cmdw_rtpt, - - /* ── 4. NCLIP — backface culling on SXY0/SXY1/SXY2 (p0,p1,p2) ──────── */ - /* MUST be done BEFORE RTPS overwrites SXY0 with p3! */ - nop, nop, gte_cmdw_nclip, - nop, nop, - - /* ── 5. Cull check: skip format/insert if MAC0 ≤ 0 (backface) ───────── */ - gte_mf(R_T0, C2_MAC0), - nop, - branch_le_zero(R_T0, 49), /* Skip 49 if MAC0 ≤ 0 (backface) → cull */ - nop, /* BD slot */ - - /* ── 6. Store p0,p1,p2 to primitive buffer (BEFORE RTPS overwrites) ─── */ - store_word(R_0, R_PrimCursor, 0), - - /* Word 1: c0 (BGR) + code = 0x38FF00FF (magenta, opcode 0x38) */ - load_ui(R_AT, 0x38FF), or_i(R_AT, R_AT, 0x00FF), - store_word(R_AT, R_PrimCursor, 4), - - /* Word 2: p0 = SXY0 (stored BEFORE RTPS overwrites it) */ - gte_sw(C2_SXY0, R_PrimCursor, 8), - - /* Word 3: c1 (BGR) + pad = 0x0000FFFF (yellow) */ - load_ui(R_AT, 0x0000), or_i(R_AT, R_AT, 0xFFFF), - store_word(R_AT, R_PrimCursor, 12), - - /* Word 4: p1 = SXY1 */ - gte_sw(C2_SXY1, R_PrimCursor, 16), - - /* Word 5: c2 (BGR) + pad = 0x00FFFF00 (cyan) */ - load_ui(R_AT, 0x00FF), or_i(R_AT, R_AT, 0xFF00), - store_word(R_AT, R_PrimCursor, 20), - - /* Word 6: p2 = SXY2 */ - gte_sw(C2_SXY2, R_PrimCursor, 24), - - /* Word 7: c3 (BGR) + pad = 0x0000FF00 (green) */ - load_ui(R_AT, 0x0000), or_i(R_AT, R_AT, 0xFF00), - store_word(R_AT, R_PrimCursor, 28), - - /* ── 7. Load V3 = verts[face->w] into V0 ─────────────────────────────── */ - shift_ll(R_AT, R_T3, 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), - - /* ── 8. RTPS — transforms V0 (now V3) → SXY0 (p3) + SZ0 ─────────────── */ - nop, nop, gte_cmdw_rtps, - - /* Word 8: p3 = SXY0 (written AFTER RTPS with V3's screen coords) */ - gte_sw(C2_SXY0, R_PrimCursor, 32), - - /* ── 9. AVSZ4 — average Z from SZ0/SZ1/SZ2/SZ3 ────────────── */ - nop, nop, gte_cmdw_avsz4, - nop, nop, - gte_mf(R_T1, C2_OTZ), - - /* ── 10. Bounds check OTZ < 2048 ─────────────────────────────────────── */ - add_ui( R_AT, R_0, 2048), - slt_u( R_AT, R_T1, R_AT), - branch_equal(R_AT, R_0, 13), /* Skip 13 → land at add_ui(R_FaceCur,...) */ - nop, /* BD slot */ - - /* ── 11. Insert into Ordering Table (length = 8 for Poly_G4) ─────────── */ - mac_insert_ot_tag(R_T1, 0x0800), /* 0x0800 = 8 << 8 = length 8 in tag */ - - /* ── 12. Advance cursors & yield ─────────────────────────────────────── */ - add_ui(R_PrimCursor, R_PrimCursor, 36), /* 9 words × 4 bytes */ - add_ui(R_FaceCursor, R_FaceCursor, 8), /* 4 × S2 = 8 bytes */ - mac_yield() -}; - - -typedef Struct_(Binds_FloorTri) { - U4 PrimCursor; - U4 FaceCursor; - U4 VertBase; - U4 OtBase; -}; - -internal MipsAtom_(rbind_floor_tri) { - /* Pop 4 arguments from the tape directly into the workspace registers */ - load_word(R_PrimCursor, R_TapePtr, O_(Binds_FloorTri,PrimCursor)), - load_word(R_FaceCursor, R_TapePtr, O_(Binds_FloorTri,FaceCursor)), - load_word(R_VertBase, R_TapePtr, O_(Binds_FloorTri,VertBase)), - load_word(R_OtBase, R_TapePtr, O_(Binds_FloorTri,OtBase)), - add_ui_1( R_TapePtr, S_(Binds_FloorTri)), - // Note(Ed): This entire thing is argument shuffle? - // TODO(Ed): Eliminate - mac_yield() -}; - - /* DIAGNOSTIC 1: Pure tape loop test */ internal MipsAtom_(diag_yield) { mac_yield() }; +// TODO(Ed): Reduce magic numbers/offsets /* DIAGNOSTIC 2: Pure memory test (No GTE). Draws a fixed cyan triangle. */ internal MipsAtom_(diag_color) { store_word(R_0, R_T7, 0), @@ -417,6 +247,7 @@ internal MipsAtom_(diag_color) { mac_yield() }; +// TODO(Ed): Reduce magic numbers/offsets /* DIAGNOSTIC 3: Pure GTE test (No Memory Writes) */ internal MipsAtom_(diag_gte) { /* Load 3 indices */ diff --git a/code/duffle/mips.h b/code/duffle/mips.h index a9443c1..368fa2f 100644 --- a/code/duffle/mips.h +++ b/code/duffle/mips.h @@ -4,6 +4,10 @@ # include "gcc_asm.h" #endif +enum { + MipsStackAlignment = 8, +}; + /* ============================================================================ * REGISTER INTEGER IDS (preprocessor-visible) * ============================================================================ diff --git a/code/gte_hello/gen/hello_gte_tape.offsets.h b/code/gte_hello/gen/hello_gte_tape.offsets.h index 596d12f..d08a82b 100644 --- a/code/gte_hello/gen/hello_gte_tape.offsets.h +++ b/code/gte_hello/gen/hello_gte_tape.offsets.h @@ -5,7 +5,7 @@ #pragma region hello_gte_tape -// --- atom: floor_tri (32 words) --- +// --- atom: floor_tri (51 words) --- #define _atom_offset_culling_floor_tri_exit 17 #define _atom_offset_bounds_chk_floor_tri_exit 3 diff --git a/code/gte_hello/hello_gte.c b/code/gte_hello/hello_gte.c index 7213022..8598eae 100644 --- a/code/gte_hello/hello_gte.c +++ b/code/gte_hello/hello_gte.c @@ -96,6 +96,7 @@ typedef Struct_(Ent_Floor) { A2_V3_S2 faces; }; +enum { scratchpad_size = 1024, }; typedef Struct_(SMemory) { DoubleBuffer screen_buf; A2_OrderingTable_Buffer ordering_tbl; @@ -107,14 +108,19 @@ typedef Struct_(SMemory) { Ent_Cube cube; Ent_Floor floor; - + U4_V scratchpad; // d-cache }; -global SMemory static_mem; -extern SMemory static_mem; +global SMemory smem; +extern SMemory smem; + +// TODO(Ed): +FI_ U4* spad_warm(MipsAtom atom) { + return nullptr; +} 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]; + gknown PrimitiveArena* pa = & smem.primitives; + gknown B1* buf = (B1*) r_(smem.primitives.buf)[smem.active_buf_id]; assert(pa->used + type_width < PrimitiveBuff_Len); B1* next = buf + pa->used; pa->used += type_width; @@ -174,9 +180,9 @@ void update(PrimitiveArena* pa, U4* ordering_buf) orderingtbl_clear_reverse(ordering_buf, OrderingTbl_Len); // Update the position based on acceleration and velocity - gknown V3_S4_R pos = & static_mem.cube.pos; - gknown V3_S4_R vel = & static_mem.cube.vel; - gknown V3_S4_R acc = & static_mem.cube.accel; + gknown V3_S4_R pos = & smem.cube.pos; + gknown V3_S4_R vel = & smem.cube.vel; + gknown V3_S4_R acc = & smem.cube.accel; add_v3s4(vel, acc[0]); add_v3s4_fp(pos, vel[0]); // vel->x += acc->x; @@ -186,7 +192,7 @@ void update(PrimitiveArena* pa, U4* ordering_buf) // pos->y += vel->y; // pos->z += vel->z; - if (pos->y + 150 > static_mem.floor.pos.y) vel->y *= -1; + if (pos->y + 150 > smem.floor.pos.y) vel->y *= -1; // Prep S4 nclip = 0; @@ -197,11 +203,11 @@ void update(PrimitiveArena* pa, U4* ordering_buf) // Draw Cube if (1) { - m3s2_rotation (& static_mem.cube.rot, & static_mem.tform_world); - m3s2_translation(& static_mem.tform_world, & static_mem.cube.pos); - m3s2_scale (& static_mem.tform_world, & static_mem.cube.scale); - gte_matrix_set_rotation (& static_mem.tform_world); - gte_matrix_set_translation(& static_mem.tform_world); + m3s2_rotation (& smem.cube.rot, & smem.tform_world); + m3s2_translation(& smem.tform_world, & smem.cube.pos); + m3s2_scale (& smem.tform_world, & smem.cube.scale); + gte_matrix_set_rotation (& smem.tform_world); + gte_matrix_set_translation(& smem.tform_world); for (U4 face_id = 0; face_id < Cube_num_faces; face_id += 1) { Poly_G4* quad = prim_alloc(Poly_G4); set_poly_g4(quad); @@ -210,11 +216,11 @@ void update(PrimitiveArena* pa, U4* ordering_buf) quad->c2 = rgb8( 0, 255, 255); quad->c3 = rgb8( 0, 255, 0); - V4_S2* face = & static_mem.cube.faces[face_id]; - V3_S2* p0 = & static_mem.cube.verts[face->x]; - V3_S2* p1 = & static_mem.cube.verts[face->y]; - V3_S2* p2 = & static_mem.cube.verts[face->z]; - V3_S2* p3 = & static_mem.cube.verts[face->w]; + V4_S2* face = & smem.cube.faces[face_id]; + V3_S2* p0 = & smem.cube.verts[face->x]; + V3_S2* p1 = & smem.cube.verts[face->y]; + V3_S2* p2 = & smem.cube.verts[face->z]; + V3_S2* p3 = & smem.cube.verts[face->w]; nclip = rtp_avg_nclip_a4_v3s2( p0, p1, p2, p3, @@ -229,29 +235,29 @@ void update(PrimitiveArena* pa, U4* ordering_buf) orderingtbl_add_primitive(ordering_buf[orderingtbl_z], quad); } } - // static_mem.cube.rot.x += 6; - // static_mem.cube.rot.y += 8; - // static_mem.cube.rot.z += 12; - static_mem.cube.rot.y += 30; + // smem.cube.rot.x += 6; + // smem.cube.rot.y += 8; + // smem.cube.rot.z += 12; + smem.cube.rot.y += 30; } // Draw cube (tape method) - two triangles per face if (0) { - m3s2_rotation (& static_mem.cube.rot, & static_mem.tform_world); - m3s2_translation(& static_mem.tform_world, & static_mem.cube.pos); - m3s2_scale (& static_mem.tform_world, & static_mem.cube.scale); - gte_matrix_set_rotation (& static_mem.tform_world); - gte_matrix_set_translation(& static_mem.tform_world); + m3s2_rotation (& smem.cube.rot, & smem.tform_world); + m3s2_translation(& smem.tform_world, & smem.cube.pos); + m3s2_scale (& smem.tform_world, & smem.cube.scale); + gte_matrix_set_rotation (& smem.tform_world); + gte_matrix_set_translation(& smem.tform_world); - U4 prim_base = u4_(pa->buf[static_mem.active_buf_id]); + 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) { tb_emit(& tb, code_rbind_cube_tri); tb_data(& tb, prim_cursor); - tb_data(& tb, u4_(static_mem.cube.faces)); - tb_data(& tb, u4_(static_mem.cube.verts)); + tb_data(& tb, u4_(smem.cube.faces)); + tb_data(& tb, u4_(smem.cube.verts)); tb_data(& tb, u4_(ordering_buf)); for (U4 i = 0; i < Cube_num_faces; i++) { @@ -265,25 +271,25 @@ void update(PrimitiveArena* pa, U4* ordering_buf) } tape_run(tb_slice(tb)); - static_mem.cube.rot.y += 30; + smem.cube.rot.y += 30; } // Draw Floor if (0) { - m3s2_rotation (& static_mem.floor.rot, & static_mem.tform_world); - m3s2_translation(& static_mem.tform_world, & static_mem.floor.pos); - m3s2_scale (& static_mem.tform_world, & static_mem.floor.scale); - gte_matrix_set_rotation (& static_mem.tform_world); - gte_matrix_set_translation(& static_mem.tform_world); + m3s2_rotation (& smem.floor.rot, & smem.tform_world); + m3s2_translation(& smem.tform_world, & smem.floor.pos); + m3s2_scale (& smem.tform_world, & smem.floor.scale); + gte_matrix_set_rotation (& smem.tform_world); + gte_matrix_set_translation(& smem.tform_world); for (U4 face_id = 0; face_id < Floor_num_faces; face_id += 1) { Poly_F3* tri = prim_alloc(Poly_F3); set_poly_f3(tri); tri->color = rgb8(255, 255, 255); - V3_S2* face = & static_mem.floor.faces[face_id]; - register V3_S2* p0 rgcc(R_T4) = & static_mem.floor.verts[face->x]; - register V3_S2* p1 rgcc(R_T5) = & static_mem.floor.verts[face->y]; - register V3_S2* p2 rgcc(R_T6) = & static_mem.floor.verts[face->z]; + V3_S2* face = & smem.floor.faces[face_id]; + register V3_S2* p0 rgcc(R_T4) = & smem.floor.verts[face->x]; + 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); @@ -320,48 +326,49 @@ void update(PrimitiveArena* pa, U4* ordering_buf) } } } - static_mem.floor.rot.y += 5; + smem.floor.rot.y += 5; } // Draw floor tape method if (1) { - m3s2_rotation (& static_mem.floor.rot, & static_mem.tform_world); - m3s2_translation(& static_mem.tform_world, & static_mem.floor.pos); - m3s2_scale (& static_mem.tform_world, & static_mem.floor.scale); - gte_matrix_set_rotation (& static_mem.tform_world); - gte_matrix_set_translation(& static_mem.tform_world); + m3s2_rotation (& smem.floor.rot, & smem.tform_world); + m3s2_translation(& smem.tform_world, & smem.floor.pos); + m3s2_scale (& smem.tform_world, & smem.floor.scale); + gte_matrix_set_rotation (& smem.tform_world); + gte_matrix_set_translation(& smem.tform_world); - U4 prim_base = u4_(pa->buf[static_mem.active_buf_id]); + U4 prim_base = u4_(pa->buf[smem.active_buf_id]); U4 prim_cursor = prim_base + pa->used; - // Prepare the tape. + // TODO(Ed): We should do a bounds check beforehand to confirm pa can hold all tris. + // The tape atoms in-flight should not need to care. + + // Prepare the tape. (Push protocol to tape) LP_ U4 mem_temp_tape[512]; TapeBuilder tb = tb_make(slice_ut_arr(mem_temp_tape)); tb_scope(& tb) { - // Push "Protocol" to tape + // TODO(Ed): This is bugged. + // tb_emit(& tb, code_set_gte_world); + // tb_data(& tb, u4_(& smem.tform_world)); + tb_emit(& tb, code_rbind_floor_tri); // TODO(Ed): Just use a single context struct ref 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_(smem.floor.faces)); + tb_data(& tb, u4_(smem.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); } - // After code_floor_tri iterations complete, the primitive arena's used counter is wrong. + // After code_floor_tri iterations complete, the primitive arena's used counter needs updating. tb_emit(& tb, code_sync_primitive_arena); tb_data(& tb, u4_(& pa->used)); tb_data(& tb, prim_base); } - // Fire off the tape. - tape_run(tb_slice(tb)); + tape_run(tb_slice(tb));// Fire off the tape. // C-side state (pa->used) has already been updated by the tape! - static_mem.floor.rot.y += 5; + smem.floor.rot.y += 5; } // --- TAPE DIAGNOSTICS --- if (0) @@ -381,40 +388,41 @@ void update(PrimitiveArena* pa, U4* ordering_buf) // tb_emit(& tb, code_diag_gte); } } - B1* prim_cursor = (B1*)r_(pa->buf)[static_mem.active_buf_id] + pa->used; + B1* prim_cursor = (B1*)r_(pa->buf)[smem.active_buf_id] + pa->used; 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; + pa->used = (U4)prim_cursor - (U4)r_(pa->buf)[smem.active_buf_id]; + smem.floor.rot.y += 5; } } int main(void) { - static_mem = (SMemory){0}; - static_mem.primitives.used = 0; - ent_cube128_init(& static_mem.cube.verts, & static_mem.cube.faces); { - Ent_Cube* cube = & static_mem.cube; + smem = (SMemory){0}; + smem.scratchpad = C_(U4_V, 0x1F800000); + smem.primitives.used = 0; + ent_cube128_init(& smem.cube.verts, & smem.cube.faces); { + Ent_Cube* cube = & smem.cube; cube->rot = v3s2(0, 0, 0); // cube->pos = v3s4(0, 0, 900); cube->scale = v3s4_fp_one(); cube->accel = v3s4(0, 1, 0); cube->pos = v3s4(0, -400, 1800); } - ent_floor_init(& static_mem.floor.verts, & static_mem.floor.faces); { - Ent_Floor* floor = & static_mem.floor; + ent_floor_init(& smem.floor.verts, & smem.floor.faces); { + Ent_Floor* floor = & smem.floor; floor->rot = v3s2(0, 0, 0); floor->pos = v3s4(0, 450, 1800); floor->scale = v3s4_fp_one(); } // gknown gp_screen_init(); - gp_screen_init_c11(& static_mem.screen_buf, & static_mem.active_buf_id); + gp_screen_init_c11(& smem.screen_buf, & smem.active_buf_id); 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; + gknown S4* active_buf_id = & smem.active_buf_id; + gknown U4* ordering_buf = r_(smem.ordering_tbl)[active_buf_id[0]]; + gknown PrimitiveArena* pa = & smem.primitives; update(pa, ordering_buf); render(); - gp_display_frame(& static_mem.screen_buf, active_buf_id, ordering_buf, pa); + gp_display_frame(& smem.screen_buf, active_buf_id, ordering_buf, pa); }; return 0; } diff --git a/code/gte_hello/hello_gte_tape.c b/code/gte_hello/hello_gte_tape.c index d2229b6..c10969f 100644 --- a/code/gte_hello/hello_gte_tape.c +++ b/code/gte_hello/hello_gte_tape.c @@ -23,6 +23,157 @@ #pragma region Baked Atoms +typedef Struct_(Binds_CubeTri) { + U4 PrimCursor; + U4 FaceCursor; + U4 VertBase; + U4 OtBase; +}; +internal MipsAtom_(rbind_cube_tri) { + /* Pop 4 arguments from the tape directly into the workspace registers */ + load_word(R_PrimCursor, R_TapePtr, O_(Binds_CubeTri,PrimCursor)), + load_word(R_FaceCursor, R_TapePtr, O_(Binds_CubeTri,FaceCursor)), + load_word(R_VertBase, R_TapePtr, O_(Binds_CubeTri,VertBase)), + load_word(R_OtBase, R_TapePtr, O_(Binds_CubeTri,OtBase)), + add_ui_1( R_TapePtr, S_(Binds_CubeTri)), + // Note(Ed): This entire thing is argument shuffle? + // TODO(Ed): Eliminate + mac_yield() +}; + +/* ============================================================================ + * cube_tri — 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. + */ + atom_region (cube_tri, REGION_PRIM_ARENA) + atom_group (cube_tri, GROUP_RENDER_PRIMS) + atom_cadence (cube_tri, CADENCE_FRAME) + atom_annot(cube_tri, phase_work, + tape_regs(R_PrimCursor, R_FaceCursor, R_VertBase, R_OtBase), + tape_regs(R_PrimCursor, R_FaceCursor)) +internal +MipsAtom_(cube_tri) { + /* ── 1. Load 4 face indices from R_FaceCur ──────────────────────────── */ + load_half_u(R_T0, R_FaceCursor, 0), /* T0 = face->x (vertex 0 index) */ + load_half_u(R_T1, R_FaceCursor, 2), /* T1 = face->y (vertex 1 index) */ + load_half_u(R_T2, R_FaceCursor, 4), /* T2 = face->z (vertex 2 index) */ + load_half_u(R_T3, R_FaceCursor, 6), /* T3 = face->w (vertex 3 index) */ + + /* ── 2. Load V0, V1, V2 into GTE ────────────────────────────────────── */ + /* V0 = verts[face->x] */ + shift_ll(R_AT, R_T0, 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), + + /* V1 = verts[face->y] */ + shift_ll(R_AT, R_T1, 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), + + /* V2 = verts[face->z] */ + shift_ll(R_AT, R_T2, 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), + + /* ── 3. RTPT — transforms V0/V1/V2 → SXY0/SXY1/SXY2 + SZ1/SZ2/SZ3 ─── */ + nop, nop, gte_cmdw_rtpt, + + /* ── 4. NCLIP — backface culling on SXY0/SXY1/SXY2 (p0,p1,p2) ──────── */ + /* MUST be done BEFORE RTPS overwrites SXY0 with p3! */ + nop, nop, gte_cmdw_nclip, + nop, nop, + + /* ── 5. Cull check: skip format/insert if MAC0 ≤ 0 (backface) ───────── */ + gte_mf(R_T0, C2_MAC0), + nop, + branch_le_zero(R_T0, 49), /* Skip 49 if MAC0 ≤ 0 (backface) → cull */ + nop, /* BD slot */ + + /* ── 6. Store p0,p1,p2 to primitive buffer (BEFORE RTPS overwrites) ─── */ + store_word(R_0, R_PrimCursor, 0), + + /* Word 1: c0 (BGR) + code = 0x38FF00FF (magenta, opcode 0x38) */ + load_ui(R_AT, 0x38FF), or_i(R_AT, R_AT, 0x00FF), + store_word(R_AT, R_PrimCursor, 4), + + /* Word 2: p0 = SXY0 (stored BEFORE RTPS overwrites it) */ + gte_sw(C2_SXY0, R_PrimCursor, 8), + + /* Word 3: c1 (BGR) + pad = 0x0000FFFF (yellow) */ + load_ui(R_AT, 0x0000), or_i(R_AT, R_AT, 0xFFFF), + store_word(R_AT, R_PrimCursor, 12), + + /* Word 4: p1 = SXY1 */ + gte_sw(C2_SXY1, R_PrimCursor, 16), + + /* Word 5: c2 (BGR) + pad = 0x00FFFF00 (cyan) */ + load_ui(R_AT, 0x00FF), or_i(R_AT, R_AT, 0xFF00), + store_word(R_AT, R_PrimCursor, 20), + + /* Word 6: p2 = SXY2 */ + gte_sw(C2_SXY2, R_PrimCursor, 24), + + /* Word 7: c3 (BGR) + pad = 0x0000FF00 (green) */ + load_ui(R_AT, 0x0000), or_i(R_AT, R_AT, 0xFF00), + store_word(R_AT, R_PrimCursor, 28), + + /* ── 7. Load V3 = verts[face->w] into V0 ─────────────────────────────── */ + shift_ll(R_AT, R_T3, 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), + + /* ── 8. RTPS — transforms V0 (now V3) → SXY0 (p3) + SZ0 ─────────────── */ + nop, nop, gte_cmdw_rtps, + + /* Word 8: p3 = SXY0 (written AFTER RTPS with V3's screen coords) */ + gte_sw(C2_SXY0, R_PrimCursor, 32), + + /* ── 9. AVSZ4 — average Z from SZ0/SZ1/SZ2/SZ3 ────────────── */ + nop, nop, gte_cmdw_avsz4, + nop, nop, + gte_mf(R_T1, C2_OTZ), + + /* ── 10. Bounds check OTZ < 2048 ─────────────────────────────────────── */ + add_ui( R_AT, R_0, 2048), + slt_u( R_AT, R_T1, R_AT), + branch_equal(R_AT, R_0, 13), /* Skip 13 → land at add_ui(R_FaceCur,...) */ + nop, /* BD slot */ + + /* ── 11. Insert into Ordering Table (length = 8 for Poly_G4) ─────────── */ + mac_insert_ot_tag(R_T1, 0x0800), /* 0x0800 = 8 << 8 = length 8 in tag */ + + /* ── 12. Advance cursors & yield ─────────────────────────────────────── */ + add_ui(R_PrimCursor, R_PrimCursor, 36), /* 9 words × 4 bytes */ + add_ui(R_FaceCursor, R_FaceCursor, 8), /* 4 × S2 = 8 bytes */ + mac_yield() +}; + +typedef Struct_(Binds_FloorTri) { + U4 PrimCursor; + U4 FaceCursor; + U4 VertBase; + U4 OtBase; +}; + atom_region(rbind_floor_tri, REGION_PRIM_ARENA) + atom_group(rbind_floor_tri, GROUP_RENDER_FLOOR) + atom_cadence(rbind_floor_tri, CADENCE_FRAME) + atom_annot(rbind_floor_tri, phase_bind + , atom_reads() + , atom_writes(R_PrimCursor, R_FaceCursor, R_VertBase, R_OtBase)) +internal +MipsAtom_(rbind_floor_tri) { + /* Pop 4 arguments from the tape directly into the workspace registers */ + load_word(R_PrimCursor, R_TapePtr, O_(Binds_FloorTri,PrimCursor)), + load_word(R_FaceCursor, R_TapePtr, O_(Binds_FloorTri,FaceCursor)), + load_word(R_VertBase, R_TapePtr, O_(Binds_FloorTri,VertBase)), + load_word(R_OtBase, R_TapePtr, O_(Binds_FloorTri,OtBase)), + add_ui_1( R_TapePtr, S_(Binds_FloorTri)), + mac_yield() +}; + atom_region( floor_tri, REGION_PRIM_ARENA) atom_group( floor_tri, GROUP_RENDER_FLOOR) atom_cadence(floor_tri, CADENCE_FRAME) @@ -31,54 +182,38 @@ atom_writes(R_PrimCursor, R_FaceCursor)) internal MipsAtom_(floor_tri) { - // T0-T2 allocated mac_load_tri_indices(R_T0, R_T1, R_T2), - // load_half_u(R_T0, R_FaceCur, 0 * S_(S2)) - // , load_half_u(R_T1, R_FaceCur, 1 * S_(S2)) - // , load_half_u(R_T2, R_FaceCur, 2 * S_(S2)) - // , mac_load_tri_verts( R_T0, R_T1, R_T2), - // shift_ll(R_AT, R_T0, 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_T1, 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_T2, 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) - // , - - /* 3. Execute Math */ nop, nop, gte_cmdw_rotate_translate_perspective_triple, nop, nop, gte_cmdw_nclip, nop, nop, - - /* 4. Culling (Branch forward if Backface) */ + /* Culling (Branch forward if Backface) */ gte_mf(R_T0, C2_MAC0), nop, branch_le_zero(R_T0, atom_offset(culling, floor_tri_exit)), nop, - - /* 5. Format Primitive */ + /* Format Primitive */ mac_format_f3_color(0x20FF, 0xFFFF), mac_gte_store_f3(), - - /* 6. Calculate Depth */ + /* Calculate Depth */ nop, nop, gte_avg_sort_z3, nop, nop, gte_mf(R_T1, C2_OTZ), - - /* 7. Bounds Check OTZ < 2048 (Branch forward to skip insertion) */ + /* Bounds Check OTZ < 2048 (Branch forward to skip insertion) */ add_ui( R_AT, R_0, OrderingTbl_Len), slt_u( R_AT, R_T1, R_AT), branch_equal(R_AT, R_0, atom_offset(bounds_chk, floor_tri_exit)), nop, - - /* 8. Insert into Ordering Table Linked List */ + /* Insert into Ordering Table Linked List */ mac_insert_ot_tag(R_T1, 0x0400), add_ui_1(R_PrimCursor, S_(Poly_F3)), /* Advance Prim Cursor (5 words) */ - // Note(Ed): No bounds checking, should be checked before atom runs. + // Note(Ed): No bounds checking, should be checked before atom runs. - /* 9. Advance Input Cursor & Yield (Both branch targets land here) */ -atom_label(floor_tri_exit) +/* Advance Input Cursor & Yield (Both branch targets land here) */ +atom_label(floor_tri_exit) add_ui_1(R_FaceCursor, S_(S2) * 4), /* Advance Face Cursor (4 * S2 = 8 bytes) */ mac_yield() }; -typedef Struct_(Binds_SyncPrimitiveArena) { U4 PrimtiveArena_Used; U4 PrimtiveBase; }; +typedef Struct_(Binds_SyncPrimitiveArena) { U4 used; U4 cursor; }; atom_region( sync_primitive_arena, REGION_PRIM_ARENA) atom_group( sync_primitive_arena, GROUP_RENDER_FLOOR) atom_cadence(sync_primitive_arena, CADENCE_FRAME) @@ -86,11 +221,11 @@ typedef Struct_(Binds_SyncPrimitiveArena) { U4 PrimtiveArena_Used; U4 PrimtiveBa atom_reads( R_TapePtr, R_PrimCursor), atom_writes(R_TapePtr)) internal MipsAtom_(sync_primitive_arena) { - load_word(R_AT, R_TapePtr, O_(Binds_SyncPrimitiveArena,PrimtiveArena_Used)), - load_word(R_T0, R_TapePtr, O_(Binds_SyncPrimitiveArena,PrimtiveBase)), + load_word(R_AT, R_TapePtr, O_(Binds_SyncPrimitiveArena,used)), + load_word(R_T0, R_TapePtr, O_(Binds_SyncPrimitiveArena,cursor)), add_ui_1( R_TapePtr, S_(Binds_SyncPrimitiveArena)), /* Calculate byte offset and store directly back to RAM */ - sub_u( R_T0, R_PrimCursor, R_T0), // R_T0 = R_PrimCursor - prim-base + sub_u( R_T0, R_PrimCursor, R_T0), // R_T0 = R_PrimCursor - binds.cursor store_word(R_T0, R_AT, 0), // R_AT[0] = R_T0 mac_yield() };