From 02658d360962ea034f5525e2528f1c152789c35c Mon Sep 17 00:00:00 2001 From: Ed_ Date: Tue, 28 Jul 2026 00:35:02 -0400 Subject: [PATCH] Prepare for hello joypad! --- code/hello_gte/gen/hello_gte.offsets.h | 6 +- code/hello_gte/hello_gte.c | 4 +- .../{hello_gte_tape.c => hello_gte.tape.c} | 2 +- code/hello_joypad/gen/hello_joypad.offsets.h | 29 ++ code/hello_joypad/hello_joypad.c | 430 ++++++++++++++++++ code/hello_joypad/hello_joypad.h | 119 +++++ code/hello_joypad/hello_joypad.tape.c | 154 +++++++ scripts/build_psyq.ps1 | 106 ++++- scripts/passes/dwarf_injection.lua | 4 +- 9 files changed, 846 insertions(+), 8 deletions(-) rename code/hello_gte/{hello_gte_tape.c => hello_gte.tape.c} (99%) create mode 100644 code/hello_joypad/gen/hello_joypad.offsets.h create mode 100644 code/hello_joypad/hello_joypad.c create mode 100644 code/hello_joypad/hello_joypad.h create mode 100644 code/hello_joypad/hello_joypad.tape.c diff --git a/code/hello_gte/gen/hello_gte.offsets.h b/code/hello_gte/gen/hello_gte.offsets.h index 7c262ef..b01c772 100644 --- a/code/hello_gte/gen/hello_gte.offsets.h +++ b/code/hello_gte/gen/hello_gte.offsets.h @@ -1,8 +1,8 @@ // Auto-generated by ps1_meta.lua (passes/offsets.lua) — DO NOT EDIT -// Source: C:\projects\Pikuma\ps1\code\hello_gte\hello_gte_tape.c +// Source: C:\projects\Pikuma\ps1\code\hello_gte\hello_gte.tape.c #pragma once -#pragma region hello_gte_tape +#pragma region hello_gte.tape // --- atom: cube_g4_face (77 words) --- @@ -25,5 +25,5 @@ enum { atom_offset_bounds_chk_floor_f3_face_exit = _atom_offset_bounds_chk_floor_f3_face_exit, }; -#pragma endregion hello_gte_tape +#pragma endregion hello_gte.tape diff --git a/code/hello_gte/hello_gte.c b/code/hello_gte/hello_gte.c index 9458143..72a5206 100644 --- a/code/hello_gte/hello_gte.c +++ b/code/hello_gte/hello_gte.c @@ -20,10 +20,10 @@ #include "duffle/lottes_tape.h" #include "duffle/word_count.metadata.h" -# include "gen/gte_hello.offsets.h" +# include "gen/hello_gte.offsets.h" #include "hello_gte.h" -#include "hello_gte_tape.c" +#include "hello_gte.tape.c" typedef U4 OrderingTable_Buffer[OrderingTbl_Len]; typedef Array_(OrderingTable_Buffer, 2); diff --git a/code/hello_gte/hello_gte_tape.c b/code/hello_gte/hello_gte.tape.c similarity index 99% rename from code/hello_gte/hello_gte_tape.c rename to code/hello_gte/hello_gte.tape.c index b02ae19..4afd549 100644 --- a/code/hello_gte/hello_gte_tape.c +++ b/code/hello_gte/hello_gte.tape.c @@ -4,7 +4,7 @@ # include "duffle/atom_dsl.h" # include "duffle/lottes_tape.h" # include "duffle/word_count.metadata.h" -# include "gen/gte_hello.offsets.h" +# include "gen/hello_gte.offsets.h" # include "hello_gte.h" #endif diff --git a/code/hello_joypad/gen/hello_joypad.offsets.h b/code/hello_joypad/gen/hello_joypad.offsets.h new file mode 100644 index 0000000..8faf636 --- /dev/null +++ b/code/hello_joypad/gen/hello_joypad.offsets.h @@ -0,0 +1,29 @@ +// Auto-generated by ps1_meta.lua (passes/offsets.lua) — DO NOT EDIT +// Source: C:\projects\Pikuma\ps1\code\hello_joypad\hello_joypad.tape.c +#pragma once + +#pragma region hello_joypad.tape + + +// --- atom: cube_g4_face (77 words) --- + +#define _atom_offset_cull_cube_g4_face_exit 42 +#define _atom_offset_bounds_chk_cube_g4_face_exit 24 + +enum { + atom_offset_cull_cube_g4_face_exit = _atom_offset_cull_cube_g4_face_exit, + atom_offset_bounds_chk_cube_g4_face_exit = _atom_offset_bounds_chk_cube_g4_face_exit, +}; + +// --- atom: floor_f3_face (58 words) --- + +#define _atom_offset_culling_floor_f3_face_exit 25 +#define _atom_offset_bounds_chk_floor_f3_face_exit 16 + +enum { + atom_offset_culling_floor_f3_face_exit = _atom_offset_culling_floor_f3_face_exit, + atom_offset_bounds_chk_floor_f3_face_exit = _atom_offset_bounds_chk_floor_f3_face_exit, +}; + +#pragma endregion hello_joypad.tape + diff --git a/code/hello_joypad/hello_joypad.c b/code/hello_joypad/hello_joypad.c new file mode 100644 index 0000000..39fe821 --- /dev/null +++ b/code/hello_joypad/hello_joypad.c @@ -0,0 +1,430 @@ +#include +#include +#include +// #include "libgpu.h" +// #include "libetc.h" +// #include "libgte.h" + +#include "duffle/dsl.h" +#include "duffle/memory.h" +#include "duffle/math.h" + +#include "duffle/gcc_asm.h" +#include "duffle/mips.h" +#include "duffle/gp.h" +#include "duffle/gte.h" + +# include "duffle/gen/duffle.macs.h" +# include "duffle/gen/duffle.offsets.h" +#include "duffle/atom_dsl.h" +#include "duffle/lottes_tape.h" +#include "duffle/word_count.metadata.h" + +# include "gen/hello_joypad.offsets.h" +#include "hello_joypad.h" + +#include "hello_joypad.tape.c" + +typedef U4 OrderingTable_Buffer[OrderingTbl_Len]; +typedef Array_(OrderingTable_Buffer, 2); + +typedef B1 PrimitiveBuffer[PrimitiveBuff_Len]; +typedef Array_(PrimitiveBuffer, 2); +typedef Struct_(PrimitiveArena) { + A2_PrimitiveBuffer buf; + U4 used; +}; + +#define Cube_num_verts 8 +typedef Array_(V3_S2, Cube_num_verts); +#define Cube_num_faces 6 +typedef Array_(V4_S2, Cube_num_faces); +I_ void ent_cube128_init(A8_V3_S2* verts, A6_V4_S2* faces) { + LP_ A8_V3_S2 baked_verts = (A8_V3_S2) { + { -128, -128, -128 }, + { 128, -128, -128 }, + { 128, -128, 128 }, + { -128, -128, 128 }, + { -128, 128, -128 }, + { 128, 128, -128 }, + { 128, 128, 128 }, + { -128, 128, 128 } + }; + LP_ A6_V4_S2 baked_faces = (A6_V4_S2) { + { 3, 2, 0, 1 }, + { 0, 1, 4, 5 }, + { 4, 5, 7, 6 }, + { 1, 2, 5, 6 }, + { 2, 3, 6, 7 }, + { 3, 0, 7, 4 }, + }; + mem_copy(u4_(verts), u4_(& baked_verts), S_(A8_V3_S2) ); + mem_copy(u4_(faces), u4_(& baked_faces), S_(A6_V4_S2) ); + return; +} +typedef Struct_(Ent_Cube) { + V3_S4 accel; + V3_S4 vel; + V3_S4 pos; + V3_S4 scale; + V3_S2 rot; + A8_V3_S2 verts; + A6_V4_S2 faces; +}; + +#define Floor_num_verts 4 +typedef Array_(V3_S2, Floor_num_verts); +#define Floor_num_faces 2 +typedef Array_(V3_S2, Floor_num_faces); +I_ void ent_floor_init(A4_V3_S2* verts, A2_V3_S2* faces) { + LP_ A4_V3_S2 baked_verts = (A4_V3_S2) { + { -900, 0, -900 }, + { -900, 0, 900 }, + { 900, 0, -900 }, + { 900, 0, 900 }, + }; + LP_ A2_V3_S2 baked_faces = (A2_V3_S2) { + { 0, 1, 2 }, + { 1, 3, 2 }, + }; + mem_copy(u4_(verts), u4_(& baked_verts), S_(A4_V3_S2)); + mem_copy(u4_(faces), u4_(& baked_faces), S_(A2_V3_S2)); +}; +typedef Struct_(Ent_Floor) { + V3_S4 accel; + V3_S4 pos; + V3_S4 scale; + V3_S2 rot; + A4_V3_S2 verts; + A2_V3_S2 faces; +}; + +enum { + Scratchpad_Len = 1024, + MemTape_Len = 512, +}; +typedef Struct_(SMemory) { + U4 MemTape[MemTape_Len]; + + DoubleBuffer screen_buf; + A2_OrderingTable_Buffer ordering_tbl; + PrimitiveArena primitives; + S4 active_buf_id; + + M3_S2 tform_world; + + Ent_Cube cube; + Ent_Floor floor; + + U4_V scratchpad; // d-cache +}; +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 = & 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; + return next; +} +#define prim_alloc(type) (type*)prim__alloc(S_(type), slit( stringify(type))) + +void gp_screen_init_c11(DoubleBuffer* screen_buf, S4* active_buf_id) +{ + reset_graph(0); + + // Set the current initial buffer + active_buf_id[0] = 0; + + // Just setting env data, not interacting with console hw. + // First buffer area + displayenv_init(& r_(screen_buf->display)[0], 0, 0, ScreenRes_X, ScreenRes_Y); + drawenv_init (& r_(screen_buf->draw )[0], 0, ScreenRes_Y, ScreenRes_X, ScreenRes_Y); + // Second buffer area + displayenv_init(& r_(screen_buf->display)[1], 0, ScreenRes_Y, ScreenRes_X, ScreenRes_Y); + drawenv_init (& r_(screen_buf->draw )[1], 0, 0, ScreenRes_X, ScreenRes_Y); + // Set the back/drawing buffer + screen_buf->draw[0].enable_auto_clear = true; + screen_buf->draw[1].enable_auto_clear = true; + // Set the background clear color + screen_buf->draw[0].initial_bg_color = rgb8( .r = 7, .g = 7, .b = 7 ); + screen_buf->draw[1].initial_bg_color = rgb8( .r = 7, .g = 7, .b = 7 ); + // screen_buf->draw[1].initial_bg_color = rgb8( .r = 47, .g = 13, .b = 0 ); + displayenv_put(& r_(screen_buf->display)[ active_buf_id[0] ]); + drawenv_put (& r_(screen_buf->draw )[ active_buf_id[0] ]); + + // Initialize and setup the GTE geometry offsets + geom_init(); + // NOTE: geom_set_offset/geom_set_screen are kept as-is (the libgte versions + // are known to be broken in this PSYQ 4.7 build — see report 2026-07-09). + // The user's research wants the C-side non-tape reference to work as a + // known-good baseline for comparison against the tape. + geom_set_offset(ScreenRes_CenterX, ScreenRes_CenterY); + geom_set_screen(ScreenZ); + + set_display_enabled(1); // gp_DisplayEnabled +} + +void gp_display_frame(DoubleBuffer* screen_buf, S4* active_buf_id, U4* ordering_buf, PrimitiveArena* pa) { + draw_sync(0); + vsync(0); + displayenv_put(& r_(screen_buf->display)[active_buf_id[0] ]); + drawenv_put (& r_(screen_buf->draw) [active_buf_id[0] ]); + { + draw_orderingtbl(ordering_buf + OrderingTbl_Len - 1); + pa->used = 0; + } + active_buf_id[0] = ! active_buf_id[0]; // Swap current buffer +} + +void render(void) { +} + +GCC_OPTIMIZATION_DISABLE +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 = & 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; + // vel->y += acc->y; + // vel->z += acc->z; + // pos->x += vel->x; + // pos->y += vel->y; + // pos->z += vel->z; + + if (pos->y + 150 > smem.floor.pos.y) vel->y *= -1; + + // Prep + S4 nclip = 0; + S4 orderingtbl_z = 0; + A2_S2 p; //??? + S4 flag; //???? + + TapeBuilder tb = tb_make(slice_ut_arr(smem.MemTape)); + + // Draw Cube + if (0) + { + 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); + quad->c0 = rgb8(255, 0, 255); + quad->c1 = rgb8(255, 255, 0); + quad->c2 = rgb8( 0, 255, 255); + quad->c3 = rgb8( 0, 255, 0); + + 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, + & quad->p0, & quad->p1, & quad->p2, & quad->p3, + & p, & orderingtbl_z, & flag + ); + if (nclip <= 0) { + continue; + } + + if ((orderingtbl_z > 0) && (orderingtbl_z < OrderingTbl_Len)) { + orderingtbl_add_primitive(ordering_buf[orderingtbl_z], quad); + } + } + // 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 (1) + { + 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[smem.active_buf_id]); + U4 prim_cursor = prim_base + pa->used; + + tb.used = 0; tb_scope(& tb) { + tb_emit(& tb, rbind_cube_g4_face); + tb_data(& tb, prim_cursor); + 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++) { + // Two triangles per quad face: (x,y,z) and (x,z,w) + tb_emit(& tb, cube_g4_face); + } + + tb_emit(& tb, sync_primitive_arena); + tb_data(& tb, u4_(& pa->used)); + tb_data(& tb, prim_base); + } + tape_run(tb_slice(tb)); + + smem.cube.rot.y += 30; + } + // Draw Floor + if (0) + { + 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 = & 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]; + + gte_load_v0(p0, R_T4); + /* + asm volatile( ".word " "%0" ", %1" : : + "i"(((op_lwc2 & OPCODE_MASK) << OPCODE_SHIFT) | ((R_T4 & REG_MASK) << RS_SHIFT) | ((gte_in_v0_xy & REG_MASK) << RT_SHIFT) | (0 & IMM_MASK)), + "i"(((op_lwc2 & OPCODE_MASK) << OPCODE_SHIFT) | ((R_T4 & REG_MASK) << RS_SHIFT) | ((gte_in_v0_z & REG_MASK) << RT_SHIFT) | (GTE_Z_Offset & IMM_MASK)), + "r"(p0) : + "$2", "$8", "$9", "$31", "memory" + ); + */ + gte_load_v1(p1, R_T5); + gte_load_v2(p2, R_T6); + + gte_rtpt(); + gte_nclip(); + gte_stotz(& nclip); + + // nclip = rtp_avg_nclip_a3_v3s2(p0, p1, p2 + // , & tri->p0, & tri->p1, & tri->p2 + // , & p, & orderingtbl_z, & flag + // ); + // if (nclip <= 0) { + // continue; + // } + + if (nclip > 0 ) { + gte_stsxy3(& tri->p0, & tri->p1, & tri->p2); + gte_avsz3(); + gte_stotz(& orderingtbl_z); + + if ((orderingtbl_z > 0) && (orderingtbl_z < OrderingTbl_Len)) { + orderingtbl_add_primitive(ordering_buf[orderingtbl_z], tri); + } + } + } + smem.floor.rot.y += 5; + } + // Draw floor tape method + if (1) + { + m3s2_rotation (& smem.floor.rot, & smem.tform_world); + m3s2_translation(& smem.tform_world, & smem.floor.pos); + m3s2_scale (& smem.tform_world, & smem.floor.scale); + + U4 prim_base = u4_(pa->buf[smem.active_buf_id]); + U4 prim_cursor = prim_base + pa->used; + + // 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) + tb.used = 0; tb_scope(& tb) { + tb_emit(& tb, set_gte_world); + tb_data(& tb, u4_(& smem.tform_world)); + + tb_emit(& tb, rbind_floor_f3_face); + // TODO(Ed): Just use a single context struct ref + tb_data(& tb, prim_cursor); + tb_data(& tb, u4_(smem.floor.faces)); + tb_data(& tb, u4_(smem.floor.verts)); + tb_data(& tb, u4_(ordering_buf)); + for (U4 i = 0; i < Floor_num_faces; i++) { + tb_emit(& tb, floor_f3_face); + } + // After floor_f3_face iterations complete, the primitive arena's used counter needs updating. + tb_emit(& tb, sync_primitive_arena); + tb_data(& tb, u4_(& pa->used)); + tb_data(& tb, prim_base); + } + tape_run(tb_slice(tb));// Fire off the tape. + + // C-side state (pa->used) has already been updated by the tape! + smem.floor.rot.y += 5; + } + // --- TAPE DIAGNOSTICS --- + if (0) + { + 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) { + // Skip set_gte_world atom for diagnostics to isolate the triangle loop + for (U4 i = 0; i < Floor_num_faces; i++) { + // tb_emit(& tb, code_diag_yield); + // tb_emit(& tb, code_diag_color); + // tb_emit(& tb, code_diag_gte); + } + } + 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)[smem.active_buf_id]; + } +} +GCC_OPTIMIZATION_ENABLE + +int main(void) +{ + 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(& 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(& smem.screen_buf, & smem.active_buf_id); + while (1) { + 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(& smem.screen_buf, active_buf_id, ordering_buf, pa); + }; + return 0; +} diff --git a/code/hello_joypad/hello_joypad.h b/code/hello_joypad/hello_joypad.h new file mode 100644 index 0000000..9fabd0a --- /dev/null +++ b/code/hello_joypad/hello_joypad.h @@ -0,0 +1,119 @@ +#ifdef INTELLISENSE_DIRECTIVES +# pragma once +# include "duffle/dsl.h" +# include "duffle/math.h" +# include "duffle/gp.h" +#endif + +enum { + PrimitiveBuff_Len = 4096, + OrderingTbl_Len = 2048 +}; + +typedef Struct_(DrawEnv_Packed) { U4 tag; U4 code[15]; }; +typedef Struct_(DrawEnv) { + Rect_S2 clip_area; + A2_S2 drawing_offset; + Rect_S2 texture_window; + S2 texture_page; + B1 flag_dither; + B1 flag_draw_on_display; + B1 enable_auto_clear; + RGB8 initial_bg_color; + DrawEnv_Packed dr_env; // reserved +}; +typedef Struct_(DisplayEnv) { + Rect_S2 display_area; + Rect_S2 screen; + B1 vinterlace; + B1 color24; + B1 pad0; + B1 pad1; +}; +typedef Array_(DrawEnv, 2); +typedef Array_(DisplayEnv, 2); +typedef Struct_(DoubleBuffer) { + A2_DrawEnv draw; + A2_DisplayEnv display; +}; + +#define ScreenRes_X 320 +#define ScreenRes_Y 240 +#define ScreenZ 320 +#define ScreenRes_CenterX (ScreenRes_X >> 1) +#define ScreenRes_CenterY (ScreenRes_Y >> 1) + +DisplayEnv* displayenv_init(DisplayEnv* env, S4 x, S4 y, S4 w, S4 h) __asm__("SetDefDispEnv"); +DrawEnv* drawenv_init (DrawEnv* env, S4 x, S4 y, S4 w, S4 h) __asm__("SetDefDrawEnv"); + +DisplayEnv* displayenv_put(DisplayEnv* env) __asm__("PutDispEnv"); +DrawEnv* drawenv_put (DrawEnv* env) __asm__("PutDrawEnv"); + +U4 geom_init(void) __asm__("InitGeom"); +void geom_set_offset(U4 x, U4 y) __asm__("SetGeomOffset"); +void geom_set_screen(U4 h) __asm__("SetGeomScreen"); + +U4* orderingtbl_clear_reverse(U4* ot, U4 len) __asm__("ClearOTagR"); + +U4 reset_graph(U4 mode) __asm__("ResetGraph"); +void set_display_enabled(U4 mask) __asm__("SetDispMask"); + +U4 draw_sync(U4 mode) __asm__("DrawSync"); +U4 vsync(U4 mode) __asm__("VSync"); + +void draw_orderingtbl(U4* buf) __asm__("DrawOTag"); + +typedef Struct_(Tile) { + U4 tag; + RGB8 color; + B1 code; + Rect_S2 rect; +}; + +/* + Linear Algebra +*/ + +M3_S2* m3s2_rotation (V3_S2* vec, M3_S2* mat) __asm__("RotMatrix"); +M3_S2* m3s2_translation(M3_S2* mat, V3_S4* vec) __asm__("TransMatrix"); +M3_S2* m3s2_scale (M3_S2* mat, V3_S4* vec) __asm__("ScaleMatrix"); + +// Rotation, Translation, Perspective + +S4 rtp_v3s2_raw(V3_S2* vec, S4* xy, S4* pp, S4* flag) __asm__("RotTransPers"); +FI_ S4 rtp_v3s2(V3_S2* vec, V2_S2* xy, A2_S2* pp, S4* flag) { return rtp_v3s2_raw(vec, C_(S4*R_, & xy->x), C_(S4*R_, pp), r_(flag)); } + +S4 rtp_avg_nclip_a3_v3s2_raw(V3_S2* v0, V3_S2* v1, V3_S2* v2, S4* xy1, S4* xy2, S4* xy3, S4* pp, S4* otz, S4* flag) __asm__("RotAverageNclip3"); +FI_ S4 rtp_avg_nclip_a3_v3s2( + V3_S2* v0, V3_S2* v1, V3_S2* v2, + V2_S2* xy0, V2_S2* xy1, V2_S2* xy2, + A2_S2* pp, S4* otz, S4* flag +){ + return rtp_avg_nclip_a3_v3s2_raw( + v0, v1, v2, + C_(S4*R_, xy0), C_(S4*R_, xy1), C_(S4*R_, xy2), + C_(S4*R_, pp), C_(S4*R_, otz), C_(S4*R_, flag) + ); +} + +S4 rtp_avg_nclip_a4_v3s2_raw(V3_S2* v0, V3_S2* v1, V3_S2* v2, V3_S2* v3, S4* xy1, S4* xy2, S4* xy3, S4* xy4, S4* pp, S4* otz, S4* flag) __asm__("RotAverageNclip4"); +FI_ S4 rtp_avg_nclip_a4_v3s2( + V3_S2* v0, V3_S2* v1, V3_S2* v2, V3_S2* v3, + V2_S2* xy0, V2_S2* xy1, V2_S2* xy2, V2_S2* xy3, + A2_S2* pp, S4* otz, S4* flag +){ + return rtp_avg_nclip_a4_v3s2_raw( + v0, v1, v2, v3, + C_(S4*R_, xy0), C_(S4*R_, xy1), C_(S4*R_, xy2), C_(S4*R_, xy3), + C_(S4*R_, pp), C_(S4*R_, otz), C_(S4*R_, flag) + ); +} + +void gte_matrix_set_rotation (M3_S2* mat) __asm__("SetRotMatrix"); +void gte_matrix_set_translation(M3_S2* mat) __asm__("SetTransMatrix"); + +enum { + fp_one = (1 << 12), +}; + +#define v3s4_fp_one() v3s4(fp_one, fp_one, fp_one) diff --git a/code/hello_joypad/hello_joypad.tape.c b/code/hello_joypad/hello_joypad.tape.c new file mode 100644 index 0000000..4c5b5cf --- /dev/null +++ b/code/hello_joypad/hello_joypad.tape.c @@ -0,0 +1,154 @@ +#ifdef INTELLISENSE_DIRECTIVES +# include "duffle/gen/duffle.macs.h" +# include "duffle/gen/duffle.offsets.h" +# include "duffle/atom_dsl.h" +# include "duffle/lottes_tape.h" +# include "duffle/word_count.metadata.h" +# include "gen/hello_joypad.offsets.h" +# include "hello_joypad.h" +#endif + +#pragma region MACs (Mips Atom components) + + + +#pragma endregion MACs + +#pragma region Baked Atoms + +typedef Struct_(Binds_CubeTri) { + U4 PrimCursor; + V4_S2* FaceCursor; + V3_S2* VertBase; + U4* OtBase; +}; +internal MipsAtom_(rbind_cube_g4_face) atom_info(atom_bind(Binds_CubeTri), atom_phase(cube_g4) +, atom_reads(R_TapePtr) +, atom_writes(R_PrimCursor, R_FaceCursor, R_VertBase, R_OtBase) +){ + /* 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_self( R_TapePtr, S_(Binds_CubeTri)), + mac_yield() +}; + + // cube_g4_face — Draw one cube face (Gouraud-shaded quad) via the GTE tape pipeline +internal +MipsAtom_(cube_g4_face) atom_info(atom_phase(cube_g4), + atom_reads( R_PrimCursor, R_FaceCursor, R_VertBase, R_OtBase), + atom_writes(R_PrimCursor, R_FaceCursor) +){ + load_half_u(R_T0, R_FaceCursor, 0 * S_(S2)), + load_half_u(R_T1, R_FaceCursor, 1 * S_(S2)), + load_half_u(R_T2, R_FaceCursor, 2 * S_(S2)), + load_half_u(R_T3, R_FaceCursor, 3 * S_(S2)), + + mac_gte_load_tri_verts(R_T0, R_T1, R_T2), + nop2, gte_cmdw_rotate_translate_perspective_triple, // required cpu -> gte delay slot + gte_cmdw_nclip, + + gte_mv_from_data_r(R_T0, C2_MAC0), nop, + branch_le_zero(R_T0, atom_offset(cull, cube_g4_face_exit)), nop, + store_word(R_0, R_PrimCursor, O_(Poly_G4, tag)), + shift_lleft(R_AT, R_T3, v3s2_byteoff), add_u(R_AT, R_AT, R_VertBase), + load_word(R_V0, R_AT, O_(V3_S2, x)), load_word(R_V1, R_AT, O_(V3_S2, z)), + gte_mv_to_data_r(R_V0, C2_VXY0), gte_mv_to_data_r(R_V1, C2_VZ0), + + mac_gte_store_g4_p012(), + gte_cmdw_rotate_translate_perspective_single, + mac_gte_store_g4_p3(), + + gte_cmdw_avg_sort_z4, + gte_mv_from_data_r(R_T1, C2_OTZ), + add_ui( R_AT, R_0, OrderingTbl_Len), + set_lt_u( R_AT, R_T1, R_AT), + + branch_equal(R_AT, R_0, atom_offset(bounds_chk, cube_g4_face_exit)), nop, + mac_insert_ot_tag_g4(), + mac_format_g4_color( + /* c0 magenta */ 0xFF, 0x00, 0xFF, + /* c1 yellow */ 0xFF, 0xFF, 0x00, + /* c2 cyan */ 0x00, 0xFF, 0xFF, + /* c3 green */ 0x00, 0xFF, 0x00), + // end: branch(bounds_chk) +// end: branch(cull) + +atom_label(cube_g4_face_exit) + add_ui_self(R_PrimCursor, S_(Poly_G4)), /* 9 words = Poly_G4 */ + add_ui_self(R_FaceCursor, S_(S2) * 4), /* 4 × S2 = 8 bytes */ + mac_yield() +}; + +typedef Struct_(Binds_FloorTri) { + U4 PrimCursor; + V3_S2* FaceCursor; + V3_S2* VertBase; + U4* OtBase; +}; +internal +MipsAtom_(rbind_floor_f3_face) atom_info(atom_bind(Binds_FloorTri), atom_phase(floor_f3) + , atom_reads(R_TapePtr) + , atom_writes(R_PrimCursor, R_FaceCursor, R_VertBase, R_OtBase) +){ + /* 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_self( R_TapePtr, S_(Binds_FloorTri)), + mac_yield() +}; + +// atom_dbg_skip +internal +MipsAtom_(floor_f3_face) atom_info(atom_phase(floor_f3) + , atom_reads( R_PrimCursor, R_FaceCursor, R_VertBase, R_OtBase) + , atom_writes(R_PrimCursor, R_FaceCursor) +) { + mac_load_tri_indices( R_T0, R_T1, R_T2), + mac_gte_load_tri_verts(R_T0, R_T1, R_T2), + nop2, gte_cmdw_rotate_translate_perspective_triple, // 2 nops retire the final cpu -> gte writes before RTPT + gte_cmdw_nclip, + + /* Culling (Branch forward if Backface) */ + gte_mv_from_data_r(R_T0, C2_MAC0), + nop, branch_le_zero(R_T0, atom_offset(culling, floor_f3_face_exit)), nop, // required gte -> cpu load-delay slot. + /* Format Primitive */ + mac_gte_store_f3(), + + /* Calculate Depth */ + gte_avg_sort_z3, + gte_mv_from_data_r(R_T1, C2_OTZ), + /* Bounds Check OTZ < 2048 (Branch forward to skip insertion) */ + add_ui( R_AT, R_0, OrderingTbl_Len), + set_lt_u( R_AT, R_T1, R_AT), + branch_equal(R_AT, R_0, atom_offset(bounds_chk, floor_f3_face_exit)), nop, + mac_format_f3_color(0xFF, 0xFF, 0xFF), // RGB-form (R=FF, G=FF, B=FF = white) + mac_insert_ot_tag_f3(), /* Insert into Ordering Table Linked List */ + add_ui_self(R_PrimCursor, S_(Poly_F3)), /* Advance Prim Cursor (5 words) */ + // Note(Ed): No bounds checking, should be checked before atom runs. + +/* Advance Input Cursor & Yield (Both branch targets land here) */ +atom_label(floor_f3_face_exit) + add_ui_self(R_FaceCursor, S_(S2) * 4), /* Advance Face Cursor (4 * S2 = 8 bytes) */ + mac_yield() +}; + +typedef Struct_(Binds_SyncPrimitiveArena) { U4 used; U4 cursor; }; +internal MipsAtom_(sync_primitive_arena) atom_info(atom_bind(Binds_SyncPrimitiveArena) + , atom_reads( R_TapePtr, R_PrimCursor) + , atom_writes(R_TapePtr) +){ + load_word(R_AT, R_TapePtr, O_(Binds_SyncPrimitiveArena,used)), + load_word(R_T0, R_TapePtr, O_(Binds_SyncPrimitiveArena,cursor)), + add_ui_self( 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 - binds.cursor + store_word(R_T0, R_AT, 0), // R_AT[0] = R_T0 + mac_yield() +}; + +#pragma endregion Baked Atoms diff --git a/scripts/build_psyq.ps1 b/scripts/build_psyq.ps1 index 6f66c58..cea53fc 100644 --- a/scripts/build_psyq.ps1 +++ b/scripts/build_psyq.ps1 @@ -458,8 +458,112 @@ function build-hello_gte { } } } -build-hello_gte +# build-hello_gte +function build-hello_joypad { + $includes += @() + + $path_module = join-path $path_code 'hello_joypad' + $path_duffle = join-path $path_code 'duffle' + $path_atom_metadata = join-path $path_duffle 'word_count.metadata.h' + $path_build_gen = join-path $path_build 'gen' + + $src_c = join-path $path_module 'hello_joypad.c' + ps1-meta -unity_root $src_c -metadata $path_atom_metadata -out_root $path_build_gen + + $assemble_args = @() + $assemble_args += $f_debug + $assemble_args += $f_optimize_none + $assemble_args += ($f_include + $path_code) + + $src_asm_crt = join-path $path_nugget_common 'crt0/crt0.s' + $module_asm_crt = join-path $path_build 'crt0.o' + assemble-unit $src_asm_crt $module_asm_crt $includes $assemble_args + + $module_c = join-path $path_build 'hello_joypad_c.o' + + $compile_args = @() + $compile_args += $f_debug + $compile_args += $f_optimize_none + # $compile_args += $f_optimize_intrinsics + # $compile_args += $f_optimize_size + # $compile_args += $f_optimize_debug + $compile_args += ($f_include + $path_code) + compile-unit $src_c $module_c $includes $compile_args + + $elf = join-path $path_build 'hello_joypad.elf' + $exe = join-path $path_build 'hello_joypad.ps-exe' + + $link_args = @() + $link_args += $f_debug + # $link_args += $f_optimize_size + $link_modules = @( + $module_asm_crt, + $module_c + ) + link-modules $link_modules $elf $link_args + make-binary $elf $exe + + # Post-link: gdb-runtime + dwarf-injection in a single Lua invocation (one luajit cold start). + ps1-meta -unity_root $src_c -metadata $path_atom_metadata -out_root $path_build_gen -passes @('--post-link') ` -extra_args @('--elf', $elf) + + $dwarfLineBin = join-path $path_build_gen 'hello_joypad.dwarf_line.bin' + $dwarfArangesBin = join-path $path_build_gen 'hello_joypad.dwarf_aranges.bin' + $dwarfRnglistsBin = join-path $path_build_gen 'hello_joypad.dwarf_rnglists.bin' + $injectElf = join-path $path_build 'hello_joypad.dwarf-injected.elf' + if ((Test-Path $dwarfLineBin) -and (Test-Path $dwarfArangesBin) -and (Test-Path $dwarfRnglistsBin)) + { + Write-Host "[build] DWARF-injecting $elf -> $injectElf" + Copy-Item -LiteralPath $elf -Destination $injectElf -Force + # Objcopy call: 3x --update-section for (line, aranges, rnglists). + $f_args = @( + "--update-section=.debug_line=$dwarfLineBin", + "--update-section=.debug_aranges=$dwarfArangesBin", + "--update-section=.debug_rnglists=$dwarfRnglistsBin" + ) + & $Objcopy @f_args $injectElf 2>&1 | Out-Null + if ($LASTEXITCODE -ne 0) { + Write-Warning "[build] objcopy F' splice failed (exit $LASTEXITCODE); removing $injectElf" + Remove-Item -LiteralPath $injectElf -ErrorAction SilentlyContinue + return; + } + + $dwarfInfoBin = join-path $path_build_gen 'hello_joypad.dwarf_info.bin' + $dwarfAbbrevBin = join-path $path_build_gen 'hello_joypad.dwarf_abbrev.bin' + $dwarfStrBin = join-path $path_build_gen 'hello_joypad.dwarf_str.bin' + $dwarfLocBin = join-path $path_build_gen 'hello_joypad.dwarf_loc.bin' + $dwarfLoclistsBin = join-path $path_build_gen 'hello_joypad.dwarf_loclists.bin' + $g_args = @( + "--update-section=.debug_info=$dwarfInfoBin", + "--update-section=.debug_abbrev=$dwarfAbbrevBin", + "--update-section=.debug_str=$dwarfStrBin", + "--add-section=.debug_loc=$dwarfLocBin", + "--add-section=.debug_loclists=$dwarfLoclistsBin" + ) + & $Objcopy @g_args $injectElf 2>&1 | Out-Null + if ($LASTEXITCODE -ne 0) { + Write-Warning "[build] objcopy G' splice failed (exit $LASTEXITCODE); removing $injectElf" + Remove-Item -LiteralPath $injectElf -ErrorAction SilentlyContinue + return; + } + + # Baked atoms execute from RAM but are emitted as C data arrays, so their ELF sections lack SHF_EXECINSTR. + # GDB discards line rows for non-code sections. Mark only the debug-copy sections executable. + # The original ELF and PS-EXE remain byte/flag unchanged. + & $Objcopy ` + --set-section-flags ".rodata=alloc,load,readonly,code,contents" ` + --set-section-flags ".data=alloc,load,data,code,contents" ` + $injectElf 2>&1 | Out-Null + if ($LASTEXITCODE -ne 0) { + Write-Warning "[build] atom-section flag update failed (exit $LASTEXITCODE); removing $injectElf" + Remove-Item -LiteralPath $injectElf -ErrorAction SilentlyContinue + } + else { + Write-Host "[build] DWARF-injected ELF: $injectElf" + } + } +} +build-hello_joypad # NO idea if this works yet... function Send-ToEmulator { param( [string]$exePath ) diff --git a/scripts/passes/dwarf_injection.lua b/scripts/passes/dwarf_injection.lua index 6d36af0..3ec6594 100644 --- a/scripts/passes/dwarf_injection.lua +++ b/scripts/passes/dwarf_injection.lua @@ -153,6 +153,7 @@ local DW_AT_inline = 0x20 -- DWARF5 §7.7.1: DW_AT_inline (used by a local DW_AT_decl_file = 0x3A -- DWARF5 §7.7.1: DW_AT_decl_file (1-based file index into the CU's file table) local DW_AT_decl_line = 0x3B -- DWARF5 §7.7.1: DW_AT_decl_line +-- TODO(Ed): Remove this HARDCODE -- File index lookup table for the existing main line unit (Unit 2). -- Provenance paths come back with mixed slashes; we normalize to basename and look up against the line unit's actual file table. -- Current scope has two provenance basenames: hello_gte_tape.c (the atom's call site) and lottes_tape.h (the component definition). @@ -162,7 +163,8 @@ local DW_AT_decl_line = 0x3B -- DWARF5 §7.7.1: DW_AT_decl_line -- headers that are pure macros/typedefs (dsl.h, memory.h, math.h, mips.h, gp.h, gte.h, etc.) never appear. -- lottes_tape.h is the FIRST include that emits line entries (MipsAtomComp_ declarations), so it is the FIRST entry after the primary file. local PROVENANCE_BASENAME_TO_FILE_INDEX = { - ["hello_gte_tape.c"] = ATOM_SOURCE_FILE_INDEX, -- = 11 + ["hello_joypad.tape.c"] = ATOM_SOURCE_FILE_INDEX, -- = 11 + ["hello_gte.tape.c"] = ATOM_SOURCE_FILE_INDEX, -- = 11 ["lottes_tape.h"] = 2, }