From 6f2eff920daf8692ce6e62e39c6f415e105b1acf Mon Sep 17 00:00:00 2001 From: Ed_ Date: Wed, 5 Aug 2026 02:00:41 -0400 Subject: [PATCH] some more review before bed. --- code/duffle/gen/macs.h | 1 + code/duffle/gen/offsets.h | 27 ++ code/duffle/lottes_tape.h | 84 ++--- code/duffle/pad.atom.c | 182 ++++++++++- code/hello_camera/gen/offsets.h | 62 ++-- code/hello_camera/hello_camera.atom.c | 438 ++++++++------------------ code/hello_camera/hello_camera.c | 117 +------ 7 files changed, 408 insertions(+), 503 deletions(-) diff --git a/code/duffle/gen/macs.h b/code/duffle/gen/macs.h index 3c33ef1..b15e20e 100644 --- a/code/duffle/gen/macs.h +++ b/code/duffle/gen/macs.h @@ -19,6 +19,7 @@ // source: C:\projects\Pikuma\ps1\code\duffle\mips.atom.c // source: C:\projects\Pikuma\ps1\code\duffle\gte.atom.c // source: C:\projects\Pikuma\ps1\code\duffle\gp.atom.c +// source: C:\projects\Pikuma\ps1\code\duffle\pad.atom.c // source: C:\projects\Pikuma\ps1\code\duffle\psyq.atom.c // Component atoms (MipsAtomComp_(ac_*)) -> macro variants (mac_*) diff --git a/code/duffle/gen/offsets.h b/code/duffle/gen/offsets.h index 667aaea..0f7d193 100644 --- a/code/duffle/gen/offsets.h +++ b/code/duffle/gen/offsets.h @@ -16,11 +16,38 @@ // source: C:\projects\Pikuma\ps1\code\duffle\mips.atom.c // source: C:\projects\Pikuma\ps1\code\duffle\gte.atom.c // source: C:\projects\Pikuma\ps1\code\duffle\gp.atom.c +// source: C:\projects\Pikuma\ps1\code\duffle\pad.atom.c // source: C:\projects\Pikuma\ps1\code\duffle\psyq.atom.c #pragma once #pragma region duffle +// --- atom: pad_bios_snapshot (78 words) --- + +#define _atom_offset_snap_root_skip_disconnected 8 +#define _atom_offset_disconnected_snap_end 61 +#define _atom_offset_case_2_id_dispatch 8 +#define _atom_offset_pending_snap_end 51 +#define _atom_offset_id_dispatch_try_analog_stick 11 +#define _atom_offset_id_dispatch_snap_end 38 +#define _atom_offset_try_analog_stick_try_analog_pad 12 +#define _atom_offset_analog_stick_snap_end 24 +#define _atom_offset_try_analog_pad_try_unsupported 11 +#define _atom_offset_analog_pad_snap_end 10 + +enum { + atom_offset_snap_root_skip_disconnected = _atom_offset_snap_root_skip_disconnected, + atom_offset_disconnected_snap_end = _atom_offset_disconnected_snap_end, + atom_offset_case_2_id_dispatch = _atom_offset_case_2_id_dispatch, + atom_offset_pending_snap_end = _atom_offset_pending_snap_end, + atom_offset_id_dispatch_try_analog_stick = _atom_offset_id_dispatch_try_analog_stick, + atom_offset_id_dispatch_snap_end = _atom_offset_id_dispatch_snap_end, + atom_offset_try_analog_stick_try_analog_pad = _atom_offset_try_analog_stick_try_analog_pad, + atom_offset_analog_stick_snap_end = _atom_offset_analog_stick_snap_end, + atom_offset_try_analog_pad_try_unsupported = _atom_offset_try_analog_pad_try_unsupported, + atom_offset_analog_pad_snap_end = _atom_offset_analog_pad_snap_end, +}; + #pragma endregion duffle diff --git a/code/duffle/lottes_tape.h b/code/duffle/lottes_tape.h index 36dae7d..5e2e3a1 100644 --- a/code/duffle/lottes_tape.h +++ b/code/duffle/lottes_tape.h @@ -11,39 +11,11 @@ # include "dsl.atom.h" #endif -typedef U4 const MipsCode; // Underlying type to mips asm words. -typedef Slice_(MipsCode); - -typedef U4 const MipsAtom; // Underlying type to an array of mips asm words that must terminate with an ac_yield. -#define MipsAtom_(sym) MipsCode sym [] align_(4) = - -// Used for components with no args (e.g., ac_load_tri_indices) or identifier-args (hardcoded register names). -// MipsAtomComp_(ac_X) { body } -// expands to: -// MipsCode ac_X[] align_(4) = { body }; -#define MipsAtomComp_(sym) MipsCode sym [] align_(4) = - -// Used for components with value-args (e.g., ac_format_f3_color). -// FI_ Slice_MipsCode ac_X(args) MipsAtomComp_Proc_(ac_X, { body }) -// expands to: -// FI_ Slice_MipsCode ac_X(args) { MipsCode ac_X[] align_(4) = { body }; return slice_from_array(MipsCode, ac_X); } -#define MipsAtomComp_Proc_(sym, ...) { MipsCode sym [] align_(4) = __VA_ARGS__; return slice_from_array(MipsCode, sym); } - -/* Line-table anchor: gcc only adds a file to the .debug_line file table when the - file contains line-numbered content. Files containing only: - - `MipsAtomComp_` static-array declarations, or - - `MipsAtomComp_Proc_` (force-inline) function bodies whose line info gets - attributed to the call site at the include point are otherwise omitted from the file table, - which breaks the DWARF injection when it tries to resolve atom-component provenance paths. - - Place `ATOM_FILE_LINE_MARKER();` once at file scope in any `.atom.c` that defines atoms. - The macro expands to a file-scope `internal U4 const` declaration keeps the file in the line table. - The constant is in `.rodata` and unreferenced; the linker may eliminate it. - The two-level concat + `__LINE__` suffix makes the identifier unique per call site - (the identifier embeds the source line, so duplicates across `#include`d files don't collide). */ -#define ATOM_FILE_DEBUGGER_LINE_MARKER(file_name) internal U4 const tmpl(atom_file_debugger_line_marker,file_name) = 0 - -/* Register aliases */ +#pragma region Tape Drive +/* --------------------------------------------------------------------------- + * TAPE DRIVE ABI + * ---------------------------------------------------------------------------*/ +/* Register Allocation Info */ enum { R_AtomJmp = R_T8 atom_reg, /* debug-visible; tape yield handshake scratch */ R_TapePtr = R_T9 atom_reg, /* The Instruction Stream Pointer */ @@ -79,16 +51,46 @@ enum { // S 0-7 }; -#pragma region Tape Drive -/* --------------------------------------------------------------------------- - * TAPE DRIVE ABI & REGISTER ALIASES (the enum moved earlier; see below) - * ---------------------------------------------------------------------------*/ -typedef Slice_(MipsAtom); typedef Slice_MipsAtom Tape; +typedef U4 const MipsCode; // Underlying type to mips asm words. +typedef Slice_(MipsCode); + +typedef U4 const MipsAtom; // Underlying type to an array of mips asm words that must terminate with an ac_yield. +#define MipsAtom_(sym) MipsCode sym [] align_(4) = + +// Used for components with no args (e.g., ac_load_tri_indices) or identifier-args (hardcoded register names). +// MipsAtomComp_(ac_X) { body } +// expands to: +// MipsCode ac_X[] align_(4) = { body }; +#define MipsAtomComp_(sym) MipsCode sym [] align_(4) = + +// Used for components with value-args (e.g., ac_format_f3_color). +// FI_ Slice_MipsCode ac_X(args) MipsAtomComp_Proc_(ac_X, { body }) +// expands to: +// FI_ Slice_MipsCode ac_X(args) { MipsCode ac_X[] align_(4) = { body }; return slice_from_array(MipsCode, ac_X); } +#define MipsAtomComp_Proc_(sym, ...) { MipsCode sym [] align_(4) = __VA_ARGS__; return slice_from_array(MipsCode, sym); } + +/* Line-table anchor: gcc only adds a file to the .debug_line file table when the + file contains line-numbered content. Files containing only: + - `MipsAtomComp_` static-array declarations, or + - `MipsAtomComp_Proc_` (force-inline) function bodies whose line info gets + attributed to the call site at the include point are otherwise omitted from the file table, + which breaks the DWARF injection when it tries to resolve atom-component provenance paths. + + Place `ATOM_FILE_LINE_MARKER();` once at file scope in any `.atom.c` that defines atoms. + The macro expands to a file-scope `internal U4 const` declaration keeps the file in the line table. + The constant is in `.rodata` and unreferenced; the linker may eliminate it. + The two-level concat + `__LINE__` suffix makes the identifier unique per call site + (the identifier embeds the source line, so duplicates across `#include`d files don't collide). */ +#define ATOM_FILE_DEBUGGER_LINE_MARKER(file_name) internal U4 const tmpl(atom_file_debugger_line_marker,file_name) = 0 + + typedef Slice_(MipsAtom); typedef Slice_MipsAtom Tape; /* The 'Exit' Atom */ atom_dbg_skip MipsAtom_(tape_exit) { jump_reg(rret_addr), nop }; -/* Generalized Tape Engine Runner */ +// TODO(Ed): When we have a substantial workload/throughput, profile each of these to see impact at ABI boundaries. + +/* Tape Runner (Default) */ FI_ void tape_run(Tape tape) { register U4* tape_ptr rgcc(R_TapePtr) = u4_r(tape.ptr); asm volatile( asm_words( load_word( R_AtomJmp, R_TapePtr, 0) /* Bootstrap the first jump */ @@ -99,13 +101,13 @@ FI_ void tape_run(Tape tape) { register U4* tape_ptr rgcc(R_TapePtr) = u4_r(tape asm_rpins, r_use(tape_ptr) asm_clobber: rlit(R_AT), - rlit(R_V0), rlit(R_V1), + rlit(R_V0), rlit(R_V1), // We clobber these for GTE ACs (that don't expose register selection, might expose them in the future...) rlit(R_T0), rlit(R_T1), rlit(R_T2), rlit(R_T3), rlit(R_T4), rlit(R_T5), rlit(R_T6), rlit(R_T7), rlit(R_T8), clb_mem_drain ); } -/* Fully Clobbered Tape */ +/* Tape Runner (Static and Arg Clobbers) */ FI_ void tape_run_a02_s07(Tape tape) { register U4* tape_ptr rgcc(R_TapePtr) = u4_r(tape.ptr); asm volatile( asm_words( load_word( R_AtomJmp, R_TapePtr, 0) /* Bootstrap the first jump */ diff --git a/code/duffle/pad.atom.c b/code/duffle/pad.atom.c index 12688a8..1a0396c 100644 --- a/code/duffle/pad.atom.c +++ b/code/duffle/pad.atom.c @@ -1,8 +1,184 @@ -#ifdef INTELLISSENSE_DIRECTIVES -# include "gen/macs.h" -# include "gen/offsets.h" +#ifdef INTELLISENSE_DIRECTIVES +# include "gen/macs.h" +# include "gen/offsets.h" +# include "mips.h" +# include "dsl.atom.h" +# include "lottes_tape.h" +# include "pad.h" #endif ATOM_FILE_DEBUGGER_LINE_MARKER(pad_atom_c); +#pragma region Baked Atoms +/* ----- pad_bios_snapshot ----- + * Per-frame snapshot of one BIOS pad buffer into PadState. + * Decoder (branch ladder on raw[0] status + raw[1] id): + * 1. raw[0] == 0xFF -> Disconnected (buttons=0, axes=0x80) + * 2. raw[0]==0 && raw[1]==0 -> Pending (buttons=0, axes=0x80) + * 3. raw[1] == 0x41 -> Digital (buttons normalized; axes=0x80) + * 4. raw[1] == 0x53 -> AnalogStick (buttons normalized; axes from raw[4..7]) + * 5. raw[1] in 0x7x -> AnalogPad (buttons normalized; axes from raw[4..7]) + * 6. else -> Unsupported (buttons=0, axes=0x80) + * + * Buttons normalization: byte_swap16((~raw_buttons) & 0xFFFF). + * raw_buttons = load_half_u(raw, 2) = raw[2] | (raw[3] << 8). + * byte_swap16(x) = (x >> 8) | (x << 8); nor(x, R_0) = ~x. store_half truncates to 16 bits so the upper-16 mask is implicit in the store. + * + * Register use (atom-local; no wave-context touched): + * R_T0 = raw base (kept throughout; axes loads read raw[4..7] from R_T0) + * R_T1 = state base (kept throughout; all stores go through R_T1) + * R_T2 = raw[0] status (alive across the disc/pending/id dispatch, then dead) + * R_T3 = raw[1] id (alive across the id dispatch, then dead) + * R_T4 = scratch (shifts, compares, immediate loads, store values) + * R_T5 = scratch (parallel lui+ori for the 0x80808080 axes constant + byte-swap target) + */ +enum { + R_PadRaw = R_T0 atom_reg atom_type(U1), + R_PadState = R_T1 atom_reg, + R_RawStatus = R_T2 atom_reg, + R_RawId = R_T3 atom_reg, +}; +typedef Struct_(Binds_PadBiosSnapshot) { + PadBiosRaw* raw; + PadState* state; +}; +internal MipsAtom_(pad_bios_snapshot) atom_info(atom_bind(Binds_PadBiosSnapshot) +, atom_reads( R_PadRaw, R_PadState, R_RawStatus, R_RawId, R_T4, R_T5, R_TapePtr) +, atom_writes(R_PadRaw, R_PadState, R_RawStatus, R_RawId, R_T4, R_T5, R_TapePtr) +) { + /* === Bind consumption: T0 = raw, T1 = state, advance R_TapePtr by 8. */ + load_word(R_PadRaw, R_TapePtr, O_(Binds_PadBiosSnapshot,raw)), + load_word(R_PadState, R_TapePtr, O_(Binds_PadBiosSnapshot,state)), + add_ui_self( R_TapePtr, S_(Binds_PadBiosSnapshot)), + + /* === Read raw[0] (status) + raw[1] (id) */ + load_byte_u(R_RawStatus, R_PadRaw, 0), + load_byte_u(R_RawId, R_PadRaw, 1), + +atom_label(snap_root) /* === Case 1: Disconnected (status == 0xFF). */ + add_ui(R_T4, R_0, 0xFF), branch_ne(R_RawStatus, R_T4, atom_offset(snap_root, skip_disconnected)), + /* BD-slot: pre-compute PadStatus_Disconnected. Branch reads R_T4=0xFF in EX before this WB completes. + * If branch NOT taken (fall through to pending/id_dispatch), R_T4 is overwritten by the next case body's add_ui — harmless. */ + +atom_label(disconnected) /* === Disconnected body. */ + /* R_T4 = PadStatus_Disconnected from snap_root BD-slot. */ + store_word(R_T4, R_PadState, O_(PadState,status)), + store_half(R_0, R_PadState, O_(PadState,buttons)), + /* axes = 0x80808080 (centered) — single sw writes the 4-byte axes block at offset 8 (left_x, left_y, right_x, right_y). */ + load_upper_i(R_T4, 0x8080), or_i_self(R_T4, 0x8080), + store_word( R_T4, R_PadState, O_(PadState,left_x)), + store_byte( R_RawId, R_PadState, O_(PadState,id)), + jump_rel(atom_offset(disconnected, snap_end)), + /* BD-slot: load next atom's entry point (replaces the nop). + * The unconditional branch always jumps to snap_end, where mac_yield_tail() + * transfers control to R_AtomJmp without re-loading it. */ + mac_yield_load(), +atom_label(skip_disconnected) + + /* === Case 2: Pending (status == 0 && id == 0) + * Combined check: if (status | id) != 0 then skip to id_dispatch. + * Falls through to the Pending case only when both are zero. */ + or_u_self(R_RawStatus, R_RawId), branch_ne(R_RawStatus, R_0, atom_offset(case_2, id_dispatch)), + /* BD-slot: pre-compute PadStatus_Pending. Branch reads R_RawStatus in EX before this WB completes. + * If branch NOT taken (fall through to id_dispatch), R_T4 is overwritten by the digital/analog body add_ui — harmless. */ + +atom_label(pending) /* === Pending body */ + /* R_T4 = PadStatus_Pending from case_2 BD-slot. */ + store_word(R_T4, R_PadState, O_(PadState,status)), + store_half(R_0, R_PadState, O_(PadState,buttons)), + /* axes = 0x80808080 (centered) — single sw writes the 4-byte axes block at offset 8 (left_x, left_y, right_x, right_y). */ + load_upper_i(R_T4, 0x8080), or_i_self(R_T4, 0x8080), + store_word( R_T4, R_PadState, O_(PadState,left_x)), + store_byte( R_RawId, R_PadState, O_(PadState,id)), + jump_rel(atom_offset(pending, snap_end)), + mac_yield_load(), + +atom_label(id_dispatch) /* === Case 3-6: ID dispatch */ + add_ui(R_T4, R_0, 0x41), branch_ne(R_RawId, R_T4, atom_offset(id_dispatch, try_analog_stick)), + /* BD-slot: pre-compute PadStatus_Digital. Branch reads R_RawId in EX before this WB completes. + * If branch NOT taken (fall through to try_analog_stick), R_T4 is overwritten by the analog body add_ui. */ + + /* === Digital body (status, buttons normalize, axes=0x80, id, branch. */ + /* R_T4 = PadStatus_Digital from id_dispatch BD-slot. */ + store_word( R_T4, R_PadState, O_(PadState,status)), + load_half_u(R_T4, R_PadRaw, 2 * S_(U1)), + /* Fill R_T4's load-delay slot with the 0x80808080 axes constant into R_T5 + * (R_T5 is dead on this path; it's only consumed at the analog_pad range check). */ + load_upper_i(R_T5, 0x8080), or_i_self(R_T5, 0x8080), + nor_u( R_T4, R_T4, R_0), /* raw_buttons is already in host bit order; no swap needed */ + store_half( R_T4, R_PadState, O_(PadState,buttons)), + + /* axes = 0x80808080 (centered) — single sw writes the 4-byte axes block at offset 8 (left_x, left_y, right_x, right_y). */ + store_word( R_T5, R_PadState, O_(PadState,left_x)), + add_ui( R_T4, R_0, 0x41), + store_byte( R_T4, R_PadState, O_(PadState,id)), + + jump_rel(atom_offset(id_dispatch, snap_end)), + mac_yield_load(), + +atom_label(try_analog_stick) /* === Case 4: AnalogStick (id == 0x53)*/ + add_ui(R_T4, R_0, 0x53), branch_ne(R_RawId, R_T4, atom_offset(try_analog_stick, try_analog_pad)), + /* BD-slot: pre-compute PadStatus_AnalogStick. Branch reads R_RawId in EX before this WB completes. + * If branch NOT taken (fall through to try_analog_pad), R_T4 is overwritten by the analog_pad body add_ui. */ + +atom_label(analog_stick) /* === AnalogStick body + * Axes are loaded as two halfwords: raw[6..7] → left_xy (sh at offset 8), raw[4..5] → right_xy (sh at offset 10). + * R_T5 holds left_xy / id-value in turn (it's dead on this path — only consumed at the analog_pad range check). */ + /* R_T4 = PadStatus_AnalogStick from try_analog_stick BD-slot. */ + store_word( R_T4, R_PadState, O_(PadState,status)), + load_half_u( R_T4, R_PadRaw, 2 * S_(U1)), /* R_T4 = raw_buttons */ + load_half_u( R_T5, R_PadRaw, 6 * S_(U1)), /* R_T5 = left_xy; fills R_T4's load-delay slot (doesn't read R_T4) */ + nor_u( R_T4, R_T4, R_0), /* R_T4 = ~raw_buttons */ + store_half( R_T4, R_PadState, O_(PadState,buttons)), + load_half_u( R_T4, R_PadRaw, 4 * S_(U1)), /* R_T4 = right_xy; fills R_T5's load-delay slot */ + store_half( R_T5, R_PadState, O_(PadState,left_x)), /* R_T5 settled, store left_xy */ + store_half( R_T4, R_PadState, O_(PadState,right_x)), + add_ui( R_T5, R_0, 0x53), /* R_T5 = id value (clobbers left_xy, already stored) */ + store_byte( R_T5, R_PadState, O_(PadState,id)), + jump_rel(atom_offset(analog_stick, snap_end)), + mac_yield_load(), + +atom_label(try_analog_pad) /* === Case 5-6: AnalogPad (id & 0xF0 == 0x70) */ + and_i( R_T4, R_RawId, 0xF0), + add_ui( R_T5, R_0, 0x70), + branch_ne(R_T4, R_T5, atom_offset(try_analog_pad, try_unsupported)), + /* BD-slot: pre-compute PadStatus_AnalogPad. Branch reads R_T4 in EX before this WB completes. + * If branch NOT taken (fall through to try_unsupported), R_T4 is overwritten by the unsupported body add_ui. */ + +atom_label(analog_pad) /* === AnalogPad body + * Same shape as AnalogStick with AnalogPad status. R_T5 holds left_xy (it's dead on this path). */ + /* R_T4 = PadStatus_AnalogPad from try_analog_pad BD-slot. */ + store_word( R_T4, R_PadState, O_(PadState,status)), + load_half_u(R_T4, R_PadRaw, 2 * S_(U1)), /* R_T4 = raw_buttons */ + load_half_u(R_T5, R_PadRaw, 6 * S_(U1)), /* R_T5 = left_xy; fills R_T4's load-delay slot */ + nor_u( R_T4, R_T4, R_0), /* R_T4 = ~raw_buttons */ + store_half( R_T4, R_PadState, O_(PadState,buttons)), + load_half_u(R_T4, R_PadRaw, 4 * S_(U1)), /* R_T4 = right_xy; fills R_T5's load-delay slot */ + store_half( R_T5, R_PadState, O_(PadState,left_x)), /* R_T5 settled, store left_xy */ + store_half( R_T4, R_PadState, O_(PadState,right_x)), + store_byte( R_RawId, R_PadState, O_(PadState,id)), + + jump_rel(atom_offset(analog_pad, snap_end)), + mac_yield_load(), + +atom_label(try_unsupported) /* === Case 7: Unsupported — fall through from the AnalogPad range-check miss. */ + add_ui( R_T4, R_0, PadStatus_Unsupported), + store_word(R_T4, R_PadState, O_(PadState,status)), + store_half(R_0, R_PadState, O_(PadState,buttons)), + /* axes = 0x80808080 (centered) — single sw writes the 4-byte axes block at offset 8 (left_x, left_y, right_x, right_y). */ + load_upper_i(R_T4, 0x8080), or_i_self(R_T4, 0x8080), + store_word( R_T4, R_PadState, O_(PadState,left_x)), + add_ui( R_T4, R_0, 0xFF), /* 0xFF sentinel: "unknown id" */ + store_byte( R_T4, R_PadState, O_(PadState,id)), + /* Fall through to snap_end. */ + +atom_label(no_jump_fallthrough) + mac_yield_load(), + +atom_label(snap_end) + /* NOT mac_yield() — R_AtomJmp was already loaded in the BD-slot of the case-exit branch. */ + mac_yield_tail(), +}; + +#pragma endregion Baked Atoms diff --git a/code/hello_camera/gen/offsets.h b/code/hello_camera/gen/offsets.h index 33df1f3..c04cf3b 100644 --- a/code/hello_camera/gen/offsets.h +++ b/code/hello_camera/gen/offsets.h @@ -8,6 +8,24 @@ #pragma region hello_camera +// --- atom: pad_apply_input (60 words) --- + +#define _atom_offset_dpad_left_exit_dpad_left 6 +#define _atom_offset_dpad_right_exit_dpad_right 6 +#define _atom_offset_dead_zone_low_check_dead_low_active 8 +#define _atom_offset_dead_zone_high_check_dead_high_active 15 +#define _atom_offset_dead_zone_skip_exit_stick 24 +#define _atom_offset_end_low_exit_stick 12 + +enum { + atom_offset_dpad_left_exit_dpad_left = _atom_offset_dpad_left_exit_dpad_left, + atom_offset_dpad_right_exit_dpad_right = _atom_offset_dpad_right_exit_dpad_right, + atom_offset_dead_zone_low_check_dead_low_active = _atom_offset_dead_zone_low_check_dead_low_active, + atom_offset_dead_zone_high_check_dead_high_active = _atom_offset_dead_zone_high_check_dead_high_active, + atom_offset_dead_zone_skip_exit_stick = _atom_offset_dead_zone_skip_exit_stick, + atom_offset_end_low_exit_stick = _atom_offset_end_low_exit_stick, +}; + // --- atom: cube_g4_face (76 words) --- #define _atom_offset_cull_cube_g4_face_exit 41 @@ -28,49 +46,5 @@ enum { atom_offset_bounds_chk_floor_f3_face_exit = _atom_offset_bounds_chk_floor_f3_face_exit, }; -// --- atom: pad_bios_snapshot (78 words) --- - -#define _atom_offset_snap_root_skip_disconnected 8 -#define _atom_offset_disconnected_snap_end 61 -#define _atom_offset_case_2_id_dispatch 8 -#define _atom_offset_pending_snap_end 51 -#define _atom_offset_id_dispatch_try_analog_stick 11 -#define _atom_offset_id_dispatch_snap_end 38 -#define _atom_offset_try_analog_stick_try_analog_pad 12 -#define _atom_offset_analog_stick_snap_end 24 -#define _atom_offset_try_analog_pad_try_unsupported 11 -#define _atom_offset_analog_pad_snap_end 10 - -enum { - atom_offset_snap_root_skip_disconnected = _atom_offset_snap_root_skip_disconnected, - atom_offset_disconnected_snap_end = _atom_offset_disconnected_snap_end, - atom_offset_case_2_id_dispatch = _atom_offset_case_2_id_dispatch, - atom_offset_pending_snap_end = _atom_offset_pending_snap_end, - atom_offset_id_dispatch_try_analog_stick = _atom_offset_id_dispatch_try_analog_stick, - atom_offset_id_dispatch_snap_end = _atom_offset_id_dispatch_snap_end, - atom_offset_try_analog_stick_try_analog_pad = _atom_offset_try_analog_stick_try_analog_pad, - atom_offset_analog_stick_snap_end = _atom_offset_analog_stick_snap_end, - atom_offset_try_analog_pad_try_unsupported = _atom_offset_try_analog_pad_try_unsupported, - atom_offset_analog_pad_snap_end = _atom_offset_analog_pad_snap_end, -}; - -// --- atom: pad_apply_input (60 words) --- - -#define _atom_offset_dpad_left_exit_dpad_left 6 -#define _atom_offset_dpad_right_exit_dpad_right 6 -#define _atom_offset_dead_zone_low_check_dead_low_active 8 -#define _atom_offset_dead_zone_high_check_dead_high_active 15 -#define _atom_offset_dead_zone_skip_exit_stick 24 -#define _atom_offset_end_low_exit_stick 12 - -enum { - atom_offset_dpad_left_exit_dpad_left = _atom_offset_dpad_left_exit_dpad_left, - atom_offset_dpad_right_exit_dpad_right = _atom_offset_dpad_right_exit_dpad_right, - atom_offset_dead_zone_low_check_dead_low_active = _atom_offset_dead_zone_low_check_dead_low_active, - atom_offset_dead_zone_high_check_dead_high_active = _atom_offset_dead_zone_high_check_dead_high_active, - atom_offset_dead_zone_skip_exit_stick = _atom_offset_dead_zone_skip_exit_stick, - atom_offset_end_low_exit_stick = _atom_offset_end_low_exit_stick, -}; - #pragma endregion hello_camera diff --git a/code/hello_camera/hello_camera.atom.c b/code/hello_camera/hello_camera.atom.c index 4fcc4c0..29b0ad6 100644 --- a/code/hello_camera/hello_camera.atom.c +++ b/code/hello_camera/hello_camera.atom.c @@ -180,6 +180,140 @@ internal MipsAtom_(gp_screen_init) atom_info(atom_phase(screen_init), atom_reads mac_yield(), }; +/* ----- pad_apply_input ----- + * Reads pad[0].buttons + pad[0].left_x; + * Applies the input-semantics deltas to cube_rot.y + floor_rot.y: + * - D-pad Left: cube_rot.y += 30, floor_rot.y += 5 + * - D-pad Right: cube_rot.y -= 30, floor_rot.y -= 5 + * - Analog stick X (dead zone 0x70..0x90): + * cube delta = (0x80 - left_x) >> 2 (range approx -32..+32) + * floor delta = (0x80 - left_x) >> 5 (range approx -4..+4) + * - D-pad + analog deltas add when used together. + * + * Convention: + * pad_state = 0 means no buttons active. + * The fail-safe zero-button value flows through unchanged, so a disconnected/fresh pad produces no rotation. + * The branch_le_zero pattern below matches the existing pad_input_demo convention (atom body lines 248/257). + * + * Signed-delta trick: + * load_byte_u zero-extends left_x to 32 bits; sub_u from 0x80 wraps to a SIGNED two's-complement value in the negative range; + * shift_aright (sra) then correctly sign-extends the shift for both positive (left_x < 0x80) and negative (left_x > 0x80) cases. + * Digital pads publish left_x = 0x80 → delta = 0 → no rotation, so the analog step is naturally a no-op for digital controllers. + */ +typedef Struct_(Binds_PadApplyInput) { + PadState* state; + V3_S2* cube_rot; + V3_S2* floor_rot; +}; +enum { + R_PadStateT5 = R_T5 atom_reg, + R_CubeRot = R_T1 atom_reg, + R_FloorRot = R_T2 atom_reg, +}; +internal MipsAtom_(pad_apply_input) atom_info(atom_bind(Binds_PadApplyInput) +, atom_reads(R_T0, R_CubeRot, R_FloorRot, R_T3, R_T4, R_PadStateT5, R_TapePtr) +, atom_writes( R_CubeRot, R_FloorRot) +) { + /* Pop Binds from tape (state, cube_rot, floor_rot) */ + load_word(R_PadStateT5, R_TapePtr, O_(Binds_PadApplyInput,state)), + load_word(R_CubeRot, R_TapePtr, O_(Binds_PadApplyInput,cube_rot)), + load_word(R_FloorRot, R_TapePtr, O_(Binds_PadApplyInput,floor_rot)), + add_ui_self( R_TapePtr, S_(Binds_PadApplyInput)), + + /* Load pad[0].buttons into R_T0. */ + load_word(R_T0, R_PadStateT5, O_(PadState,buttons)), nop, + // Note(Ed): Potential op with delay slot? + + /* D-pad Left: cube_rot.y += 30, floor_rot.y += 5. */ + and_i(R_T3, R_T0, pad0_(Pad_Left)), branch_le_zero(R_T3, atom_offset(dpad_left, exit_dpad_left)), + load_half( R_T4, R_CubeRot, O_(V3_S2,y)), /* BD-slot */ + load_half( R_T3, R_FloorRot, O_(V3_S2,y)), + add_si( R_T4, R_T4, 30), + add_si( R_T3, R_T3, 5), + store_half(R_T4, R_CubeRot, O_(V3_S2,y)), + store_half(R_T3, R_FloorRot, O_(V3_S2,y)), + atom_label(exit_dpad_left) + + /* D-pad Right: cube_rot.y -= 30, floor_rot.y -= 5. */ + and_i(R_T3, R_T0, pad0_(Pad_Right)), branch_le_zero(R_T3, atom_offset(dpad_right, exit_dpad_right)), + load_half( R_T4, R_CubeRot, O_(V3_S2,y)), /* BD-slot */ + load_half( R_T3, R_FloorRot, O_(V3_S2,y)), + add_si( R_T4, R_T4, -30), + add_si( R_T3, R_T3, -5), + store_half(R_T4, R_CubeRot, O_(V3_S2,y)), + store_half(R_T3, R_FloorRot, O_(V3_S2,y)), + atom_label(exit_dpad_right) + + /* Analog left-stick X: dead zone 0x70..0x90. + * Cube delta = (0x80 - left_x) >> 2; floor delta = (0x80 - left_x) >> 5. */ + load_byte_u(R_T3, R_PadStateT5, O_(PadState,left_x)), + + /* Dead-zone check: skip analog if left_x in [0x70, 0x90] inclusive. Outside dead zone on LOW side: left_x < 0x70 (strictly). + * set_lt_u(R_T4, R_T3, R_T4=0x70) → R_T4 = (left_x < 0x70) ? 1 : 0. */ + add_ui(R_T4, R_0, 0x70), set_lt_u(R_T4, R_T3, R_T4), branch_ne(R_T4, R_0, atom_offset(dead_zone_low_check, dead_low_active)), + add_ui(R_T4, R_0, 0x80), /* BD-slot: pre-load 0x80 for dead_low_active */ + +atom_label(dead_check_upper) + /* left_x >= 0x70 → check upper bound. */ + load_byte_u(R_T3, R_PadStateT5, O_(PadState,left_x)), /* reload */ + add_ui( R_T4, R_0, 0x90), + + /* R_T4 = (0x90 < left_x) ? 1 : 0 → (left_x > 0x90) ? 1 : 0 */ + set_lt_u(R_T4, R_T4, R_T3), branch_ne(R_T4, R_0, atom_offset(dead_zone_high_check, dead_high_active)), + add_ui( R_T4, R_0, 0x80), /* BD-slot: pre-load 0x80 for dead_high_active */ + jump_rel(atom_offset(dead_zone_skip, exit_stick)), + mac_yield_load(), + +atom_label(dead_low_active) + /* R_T3 = left_x (from line 632 lbu; not clobbered between dead_zone_low_check branch + its BD-slot `add_ui R_T4, 0x80`). + * The earlier `load_byte_u(R_T3, ...)` reload was redundant and introduced a load-use hazard on the next `sub_u`. + * R_T4 = 0x80 from the BD-slot of `dead_zone_low_check`'s branch_ne. */ + sub_u( R_T3, R_T4, R_T3), /* R_T3 = 0x80 - left_x */ + /* delta = 0x80 - left_x (positive). */ + + /* R_T4 = cube_delta */ + shift_aright(R_T4, R_T3, 2), + load_half( R_T0, R_CubeRot, O_(V3_S2,y)), + nop, + add_u( R_T0, R_T0, R_T4), + store_half( R_T0, R_CubeRot, O_(V3_S2,y)), + /* R_T4 = floor_delta — moved into the load-delay slot of the floor load below (fills the 1-instruction gap; + * doesn't read R_T0; R_T4 settles by the subsequent add_u). */ + load_half( R_T0, R_FloorRot, O_(V3_S2,y)), + shift_aright(R_T4, R_T3, 5), + add_u( R_T0, R_T0, R_T4), + store_half( R_T0, R_FloorRot, O_(V3_S2,y)), + + jump_rel(atom_offset(end_low, exit_stick)), + mac_yield_load(), + +atom_label(dead_high_active) + /* R_T3 = left_x (from line 641 lbu in dead_check_upper; not clobbered between dead_zone_high_check branch + its BD-slot `add_ui R_T4, 0x80`). + * The earlier `load_byte_u(R_T3, ...)` reload was redundant and introduced a load-use hazard on the next `sub_u`. + * R_T4 = 0x80 from the BD-slot of `dead_zone_high_check`'s branch_ne. */ + sub_u( R_T3, R_T4, R_T3), + /* delta = 0x80 - left_x (signed negative). */ + + shift_aright(R_T4, R_T3, 2), /* R_T4 = cube_delta (signed) */ + load_half( R_T0, R_CubeRot, O_(V3_S2,y)), + nop, + add_u( R_T0, R_T0, R_T4), + store_half( R_T0, R_CubeRot, O_(V3_S2,y)), + + /* R_T4 = floor_delta (signed) — moved into the load-delay slot of the floor load below. */ + load_half( R_T0, R_FloorRot, O_(V3_S2,y)), + shift_aright(R_T4, R_T3, 5), + add_u( R_T0, R_T0, R_T4), + store_half( R_T0, R_FloorRot, O_(V3_S2,y)), + +atom_label(no_jump_fallthrough) + mac_yield_load(), + +atom_label(exit_stick) + /* NOT mac_yield() — R_AtomJmp was already loaded in the BD-slot of the dead-zone/exit branch. */ + mac_yield_tail(), +}; + enum { R_PrimCursor = R_T7 atom_reg atom_type(U4*), /* VRAM output cursor (primitive buffer) */ R_FaceCursor = R_T4 atom_reg atom_type(V4_S2*), /* Cube face-index cursor (V4_S2*); floor context switches to V3_S2* via atom_phase */ @@ -331,308 +465,4 @@ internal MipsAtom_(sync_primitive_arena) atom_info(atom_bind(Binds_SyncPrimitive mac_yield() }; -/* ----- pad_bios_snapshot ----- - * Per-frame snapshot of one BIOS pad buffer into PadState. - * Decoder (branch ladder on raw[0] status + raw[1] id): - * 1. raw[0] == 0xFF -> Disconnected (buttons=0, axes=0x80) - * 2. raw[0]==0 && raw[1]==0 -> Pending (buttons=0, axes=0x80) - * 3. raw[1] == 0x41 -> Digital (buttons normalized; axes=0x80) - * 4. raw[1] == 0x53 -> AnalogStick (buttons normalized; axes from raw[4..7]) - * 5. raw[1] in 0x7x -> AnalogPad (buttons normalized; axes from raw[4..7]) - * 6. else -> Unsupported (buttons=0, axes=0x80) - * - * Buttons normalization: byte_swap16((~raw_buttons) & 0xFFFF). - * raw_buttons = load_half_u(raw, 2) = raw[2] | (raw[3] << 8). - * byte_swap16(x) = (x >> 8) | (x << 8); nor(x, R_0) = ~x. store_half truncates to 16 bits so the upper-16 mask is implicit in the store. - * - * Register use (atom-local; no wave-context touched): - * R_T0 = raw base (kept throughout; axes loads read raw[4..7] from R_T0) - * R_T1 = state base (kept throughout; all stores go through R_T1) - * R_T2 = raw[0] status (alive across the disc/pending/id dispatch, then dead) - * R_T3 = raw[1] id (alive across the id dispatch, then dead) - * R_T4 = scratch (shifts, compares, immediate loads, store values) - * R_T5 = scratch (parallel lui+ori for the 0x80808080 axes constant + byte-swap target) - */ -enum { - R_PadRaw = R_T0 atom_reg atom_type(U1), - R_PadState = R_T1 atom_reg, - R_RawStatus = R_T2 atom_reg, - R_RawId = R_T3 atom_reg, -}; -typedef Struct_(Binds_PadBiosSnapshot) { - PadBiosRaw* raw; - PadState* state; -}; -internal MipsAtom_(pad_bios_snapshot) atom_info(atom_bind(Binds_PadBiosSnapshot) -, atom_reads( R_PadRaw, R_PadState, R_RawStatus, R_RawId, R_T4, R_T5, R_TapePtr) -, atom_writes(R_PadRaw, R_PadState, R_RawStatus, R_RawId, R_T4, R_T5, R_TapePtr) -) { - /* === Bind consumption: T0 = raw, T1 = state, advance R_TapePtr by 8. */ - load_word(R_PadRaw, R_TapePtr, O_(Binds_PadBiosSnapshot,raw)), - load_word(R_PadState, R_TapePtr, O_(Binds_PadBiosSnapshot,state)), - add_ui_self( R_TapePtr, S_(Binds_PadBiosSnapshot)), - - /* === Read raw[0] (status) + raw[1] (id) */ - load_byte_u(R_RawStatus, R_PadRaw, 0), - load_byte_u(R_RawId, R_PadRaw, 1), - -atom_label(snap_root) /* === Case 1: Disconnected (status == 0xFF). */ - add_ui(R_T4, R_0, 0xFF), branch_ne(R_RawStatus, R_T4, atom_offset(snap_root, skip_disconnected)), - /* BD-slot: pre-compute PadStatus_Disconnected. Branch reads R_T4=0xFF in EX before this WB completes. - * If branch NOT taken (fall through to pending/id_dispatch), R_T4 is overwritten by the next case body's add_ui — harmless. */ - -atom_label(disconnected) /* === Disconnected body. */ - /* R_T4 = PadStatus_Disconnected from snap_root BD-slot. */ - store_word(R_T4, R_PadState, O_(PadState,status)), - store_half(R_0, R_PadState, O_(PadState,buttons)), - /* axes = 0x80808080 (centered) — single sw writes the 4-byte axes block at offset 8 (left_x, left_y, right_x, right_y). */ - load_upper_i(R_T4, 0x8080), or_i_self(R_T4, 0x8080), - store_word( R_T4, R_PadState, O_(PadState,left_x)), - store_byte( R_RawId, R_PadState, O_(PadState,id)), - jump_rel(atom_offset(disconnected, snap_end)), - /* BD-slot: load next atom's entry point (replaces the nop). - * The unconditional branch always jumps to snap_end, where mac_yield_tail() - * transfers control to R_AtomJmp without re-loading it. */ - mac_yield_load(), -atom_label(skip_disconnected) - - /* === Case 2: Pending (status == 0 && id == 0) - * Combined check: if (status | id) != 0 then skip to id_dispatch. - * Falls through to the Pending case only when both are zero. */ - or_u_self(R_RawStatus, R_RawId), branch_ne(R_RawStatus, R_0, atom_offset(case_2, id_dispatch)), - /* BD-slot: pre-compute PadStatus_Pending. Branch reads R_RawStatus in EX before this WB completes. - * If branch NOT taken (fall through to id_dispatch), R_T4 is overwritten by the digital/analog body add_ui — harmless. */ - -atom_label(pending) /* === Pending body */ - /* R_T4 = PadStatus_Pending from case_2 BD-slot. */ - store_word(R_T4, R_PadState, O_(PadState,status)), - store_half(R_0, R_PadState, O_(PadState,buttons)), - /* axes = 0x80808080 (centered) — single sw writes the 4-byte axes block at offset 8 (left_x, left_y, right_x, right_y). */ - load_upper_i(R_T4, 0x8080), or_i_self(R_T4, 0x8080), - store_word( R_T4, R_PadState, O_(PadState,left_x)), - store_byte( R_RawId, R_PadState, O_(PadState,id)), - jump_rel(atom_offset(pending, snap_end)), - mac_yield_load(), - -atom_label(id_dispatch) /* === Case 3-6: ID dispatch */ - add_ui(R_T4, R_0, 0x41), branch_ne(R_RawId, R_T4, atom_offset(id_dispatch, try_analog_stick)), - /* BD-slot: pre-compute PadStatus_Digital. Branch reads R_RawId in EX before this WB completes. - * If branch NOT taken (fall through to try_analog_stick), R_T4 is overwritten by the analog body add_ui. */ - - /* === Digital body (status, buttons normalize, axes=0x80, id, branch. */ - /* R_T4 = PadStatus_Digital from id_dispatch BD-slot. */ - store_word( R_T4, R_PadState, O_(PadState,status)), - load_half_u(R_T4, R_PadRaw, 2 * S_(U1)), - /* Fill R_T4's load-delay slot with the 0x80808080 axes constant into R_T5 - * (R_T5 is dead on this path; it's only consumed at the analog_pad range check). */ - load_upper_i(R_T5, 0x8080), or_i_self(R_T5, 0x8080), - nor_u( R_T4, R_T4, R_0), /* raw_buttons is already in host bit order; no swap needed */ - store_half( R_T4, R_PadState, O_(PadState,buttons)), - - /* axes = 0x80808080 (centered) — single sw writes the 4-byte axes block at offset 8 (left_x, left_y, right_x, right_y). */ - store_word( R_T5, R_PadState, O_(PadState,left_x)), - add_ui( R_T4, R_0, 0x41), - store_byte( R_T4, R_PadState, O_(PadState,id)), - - jump_rel(atom_offset(id_dispatch, snap_end)), - mac_yield_load(), - -atom_label(try_analog_stick) /* === Case 4: AnalogStick (id == 0x53)*/ - add_ui(R_T4, R_0, 0x53), branch_ne(R_RawId, R_T4, atom_offset(try_analog_stick, try_analog_pad)), - /* BD-slot: pre-compute PadStatus_AnalogStick. Branch reads R_RawId in EX before this WB completes. - * If branch NOT taken (fall through to try_analog_pad), R_T4 is overwritten by the analog_pad body add_ui. */ - -atom_label(analog_stick) /* === AnalogStick body - * Axes are loaded as two halfwords: raw[6..7] → left_xy (sh at offset 8), raw[4..5] → right_xy (sh at offset 10). - * R_T5 holds left_xy / id-value in turn (it's dead on this path — only consumed at the analog_pad range check). */ - /* R_T4 = PadStatus_AnalogStick from try_analog_stick BD-slot. */ - store_word( R_T4, R_PadState, O_(PadState,status)), - load_half_u( R_T4, R_PadRaw, 2 * S_(U1)), /* R_T4 = raw_buttons */ - load_half_u( R_T5, R_PadRaw, 6 * S_(U1)), /* R_T5 = left_xy; fills R_T4's load-delay slot (doesn't read R_T4) */ - nor_u( R_T4, R_T4, R_0), /* R_T4 = ~raw_buttons */ - store_half( R_T4, R_PadState, O_(PadState,buttons)), - load_half_u( R_T4, R_PadRaw, 4 * S_(U1)), /* R_T4 = right_xy; fills R_T5's load-delay slot */ - store_half( R_T5, R_PadState, O_(PadState,left_x)), /* R_T5 settled, store left_xy */ - store_half( R_T4, R_PadState, O_(PadState,right_x)), - add_ui( R_T5, R_0, 0x53), /* R_T5 = id value (clobbers left_xy, already stored) */ - store_byte( R_T5, R_PadState, O_(PadState,id)), - jump_rel(atom_offset(analog_stick, snap_end)), - mac_yield_load(), - -atom_label(try_analog_pad) /* === Case 5-6: AnalogPad (id & 0xF0 == 0x70) */ - and_i( R_T4, R_RawId, 0xF0), - add_ui( R_T5, R_0, 0x70), - branch_ne(R_T4, R_T5, atom_offset(try_analog_pad, try_unsupported)), - /* BD-slot: pre-compute PadStatus_AnalogPad. Branch reads R_T4 in EX before this WB completes. - * If branch NOT taken (fall through to try_unsupported), R_T4 is overwritten by the unsupported body add_ui. */ - -atom_label(analog_pad) /* === AnalogPad body - * Same shape as AnalogStick with AnalogPad status. R_T5 holds left_xy (it's dead on this path). */ - /* R_T4 = PadStatus_AnalogPad from try_analog_pad BD-slot. */ - store_word( R_T4, R_PadState, O_(PadState,status)), - load_half_u(R_T4, R_PadRaw, 2 * S_(U1)), /* R_T4 = raw_buttons */ - load_half_u(R_T5, R_PadRaw, 6 * S_(U1)), /* R_T5 = left_xy; fills R_T4's load-delay slot */ - nor_u( R_T4, R_T4, R_0), /* R_T4 = ~raw_buttons */ - store_half( R_T4, R_PadState, O_(PadState,buttons)), - load_half_u(R_T4, R_PadRaw, 4 * S_(U1)), /* R_T4 = right_xy; fills R_T5's load-delay slot */ - store_half( R_T5, R_PadState, O_(PadState,left_x)), /* R_T5 settled, store left_xy */ - store_half( R_T4, R_PadState, O_(PadState,right_x)), - store_byte( R_RawId, R_PadState, O_(PadState,id)), - - jump_rel(atom_offset(analog_pad, snap_end)), - mac_yield_load(), - -atom_label(try_unsupported) /* === Case 7: Unsupported — fall through from the AnalogPad range-check miss. */ - add_ui( R_T4, R_0, PadStatus_Unsupported), - store_word(R_T4, R_PadState, O_(PadState,status)), - store_half(R_0, R_PadState, O_(PadState,buttons)), - /* axes = 0x80808080 (centered) — single sw writes the 4-byte axes block at offset 8 (left_x, left_y, right_x, right_y). */ - load_upper_i(R_T4, 0x8080), or_i_self(R_T4, 0x8080), - store_word( R_T4, R_PadState, O_(PadState,left_x)), - add_ui( R_T4, R_0, 0xFF), /* 0xFF sentinel: "unknown id" */ - store_byte( R_T4, R_PadState, O_(PadState,id)), - /* Fall through to snap_end. */ - -atom_label(no_jump_fallthrough) - mac_yield_load(), - -atom_label(snap_end) - /* NOT mac_yield() — R_AtomJmp was already loaded in the BD-slot of the case-exit branch. */ - mac_yield_tail(), -}; - -/* ----- pad_apply_input ----- - * Reads pad[0].buttons + pad[0].left_x; - * Applies the input-semantics deltas to cube_rot.y + floor_rot.y: - * - D-pad Left: cube_rot.y += 30, floor_rot.y += 5 - * - D-pad Right: cube_rot.y -= 30, floor_rot.y -= 5 - * - Analog stick X (dead zone 0x70..0x90): - * cube delta = (0x80 - left_x) >> 2 (range approx -32..+32) - * floor delta = (0x80 - left_x) >> 5 (range approx -4..+4) - * - D-pad + analog deltas add when used together. - * - * Convention: - * pad_state = 0 means no buttons active. - * The fail-safe zero-button value flows through unchanged, so a disconnected/fresh pad produces no rotation. - * The branch_le_zero pattern below matches the existing pad_input_demo convention (atom body lines 248/257). - * - * Signed-delta trick: - * load_byte_u zero-extends left_x to 32 bits; sub_u from 0x80 wraps to a SIGNED two's-complement value in the negative range; - * shift_aright (sra) then correctly sign-extends the shift for both positive (left_x < 0x80) and negative (left_x > 0x80) cases. - * Digital pads publish left_x = 0x80 → delta = 0 → no rotation, so the analog step is naturally a no-op for digital controllers. - */ -typedef Struct_(Binds_PadApplyInput) { - PadState* state; - V3_S2* cube_rot; - V3_S2* floor_rot; -}; -enum { - R_PadStateT5 = R_T5 atom_reg, - R_CubeRot = R_T1 atom_reg, - R_FloorRot = R_T2 atom_reg, -}; -internal MipsAtom_(pad_apply_input) atom_info(atom_bind(Binds_PadApplyInput) -, atom_reads(R_T0, R_CubeRot, R_FloorRot, R_T3, R_T4, R_PadStateT5, R_TapePtr) -, atom_writes( R_CubeRot, R_FloorRot) -) { - /* Pop Binds from tape (state, cube_rot, floor_rot) */ - load_word(R_PadStateT5, R_TapePtr, O_(Binds_PadApplyInput,state)), - load_word(R_CubeRot, R_TapePtr, O_(Binds_PadApplyInput,cube_rot)), - load_word(R_FloorRot, R_TapePtr, O_(Binds_PadApplyInput,floor_rot)), - add_ui_self( R_TapePtr, S_(Binds_PadApplyInput)), - - /* Load pad[0].buttons into R_T0. */ - load_word(R_T0, R_PadStateT5, O_(PadState,buttons)), nop, - // Note(Ed): Potential op with delay slot? - - /* D-pad Left: cube_rot.y += 30, floor_rot.y += 5. */ - and_i(R_T3, R_T0, pad0_(Pad_Left)), branch_le_zero(R_T3, atom_offset(dpad_left, exit_dpad_left)), - load_half( R_T4, R_CubeRot, O_(V3_S2,y)), /* BD-slot */ - load_half( R_T3, R_FloorRot, O_(V3_S2,y)), - add_si( R_T4, R_T4, 30), - add_si( R_T3, R_T3, 5), - store_half(R_T4, R_CubeRot, O_(V3_S2,y)), - store_half(R_T3, R_FloorRot, O_(V3_S2,y)), - atom_label(exit_dpad_left) - - /* D-pad Right: cube_rot.y -= 30, floor_rot.y -= 5. */ - and_i(R_T3, R_T0, pad0_(Pad_Right)), branch_le_zero(R_T3, atom_offset(dpad_right, exit_dpad_right)), - load_half( R_T4, R_CubeRot, O_(V3_S2,y)), /* BD-slot */ - load_half( R_T3, R_FloorRot, O_(V3_S2,y)), - add_si( R_T4, R_T4, -30), - add_si( R_T3, R_T3, -5), - store_half(R_T4, R_CubeRot, O_(V3_S2,y)), - store_half(R_T3, R_FloorRot, O_(V3_S2,y)), - atom_label(exit_dpad_right) - - /* Analog left-stick X: dead zone 0x70..0x90. - * Cube delta = (0x80 - left_x) >> 2; floor delta = (0x80 - left_x) >> 5. */ - load_byte_u(R_T3, R_PadStateT5, O_(PadState,left_x)), - - /* Dead-zone check: skip analog if left_x in [0x70, 0x90] inclusive. Outside dead zone on LOW side: left_x < 0x70 (strictly). - * set_lt_u(R_T4, R_T3, R_T4=0x70) → R_T4 = (left_x < 0x70) ? 1 : 0. */ - add_ui(R_T4, R_0, 0x70), set_lt_u(R_T4, R_T3, R_T4), branch_ne(R_T4, R_0, atom_offset(dead_zone_low_check, dead_low_active)), - add_ui(R_T4, R_0, 0x80), /* BD-slot: pre-load 0x80 for dead_low_active */ - -atom_label(dead_check_upper) - /* left_x >= 0x70 → check upper bound. */ - load_byte_u(R_T3, R_PadStateT5, O_(PadState,left_x)), /* reload */ - add_ui( R_T4, R_0, 0x90), - - /* R_T4 = (0x90 < left_x) ? 1 : 0 → (left_x > 0x90) ? 1 : 0 */ - set_lt_u(R_T4, R_T4, R_T3), branch_ne(R_T4, R_0, atom_offset(dead_zone_high_check, dead_high_active)), - add_ui( R_T4, R_0, 0x80), /* BD-slot: pre-load 0x80 for dead_high_active */ - jump_rel(atom_offset(dead_zone_skip, exit_stick)), - mac_yield_load(), - -atom_label(dead_low_active) - /* R_T3 = left_x (from line 632 lbu; not clobbered between dead_zone_low_check branch + its BD-slot `add_ui R_T4, 0x80`). - * The earlier `load_byte_u(R_T3, ...)` reload was redundant and introduced a load-use hazard on the next `sub_u`. - * R_T4 = 0x80 from the BD-slot of `dead_zone_low_check`'s branch_ne. */ - sub_u( R_T3, R_T4, R_T3), /* R_T3 = 0x80 - left_x */ - /* delta = 0x80 - left_x (positive). */ - - /* R_T4 = cube_delta */ - shift_aright(R_T4, R_T3, 2), - load_half( R_T0, R_CubeRot, O_(V3_S2,y)), - nop, - add_u( R_T0, R_T0, R_T4), - store_half( R_T0, R_CubeRot, O_(V3_S2,y)), - /* R_T4 = floor_delta — moved into the load-delay slot of the floor load below (fills the 1-instruction gap; - * doesn't read R_T0; R_T4 settles by the subsequent add_u). */ - load_half( R_T0, R_FloorRot, O_(V3_S2,y)), - shift_aright(R_T4, R_T3, 5), - add_u( R_T0, R_T0, R_T4), - store_half( R_T0, R_FloorRot, O_(V3_S2,y)), - - jump_rel(atom_offset(end_low, exit_stick)), - mac_yield_load(), - -atom_label(dead_high_active) - /* R_T3 = left_x (from line 641 lbu in dead_check_upper; not clobbered between dead_zone_high_check branch + its BD-slot `add_ui R_T4, 0x80`). - * The earlier `load_byte_u(R_T3, ...)` reload was redundant and introduced a load-use hazard on the next `sub_u`. - * R_T4 = 0x80 from the BD-slot of `dead_zone_high_check`'s branch_ne. */ - sub_u( R_T3, R_T4, R_T3), - /* delta = 0x80 - left_x (signed negative). */ - - shift_aright(R_T4, R_T3, 2), /* R_T4 = cube_delta (signed) */ - load_half( R_T0, R_CubeRot, O_(V3_S2,y)), - nop, - add_u( R_T0, R_T0, R_T4), - store_half( R_T0, R_CubeRot, O_(V3_S2,y)), - - /* R_T4 = floor_delta (signed) — moved into the load-delay slot of the floor load below. */ - load_half( R_T0, R_FloorRot, O_(V3_S2,y)), - shift_aright(R_T4, R_T3, 5), - add_u( R_T0, R_T0, R_T4), - store_half( R_T0, R_FloorRot, O_(V3_S2,y)), - -atom_label(no_jump_fallthrough) - mac_yield_load(), - -atom_label(exit_stick) - /* NOT mac_yield() — R_AtomJmp was already loaded in the BD-slot of the dead-zone/exit branch. */ - mac_yield_tail(), -}; - #pragma endregion Baked Atoms diff --git a/code/hello_camera/hello_camera.c b/code/hello_camera/hello_camera.c index a3aac89..4fa7021 100644 --- a/code/hello_camera/hello_camera.c +++ b/code/hello_camera/hello_camera.c @@ -34,15 +34,16 @@ #include "duffle/mips.atom.c" #include "duffle/gte.atom.c" #include "duffle/gp.atom.c" +#include "duffle/pad.atom.c" #include "duffle/psyq.atom.c" #pragma endregion Duffle TUs -#pragma region Joypade Headers +#pragma region Hello Camera Headers # include "gen/macs.h" # include "gen/offsets.h" #include "hello_camera.h" -#pragma region Joypad Headers +#pragma endregion Hello Camera Headers #pragma region Hello Joypad TUs #include "hello_camera.atom.c" @@ -159,19 +160,7 @@ void update(PrimitiveArena* pa, U4* ordering_buf) { TapeBuilder tb = tb_make(slice_ut_arr(smem.MemTape)); - if (0) // Pad Input (dead — kept for the source-as-written record; references the deleted `pad_state` field) - { - (void)Pad_Left; (void)Pad_Right; /* suppress unused-token warnings */ - if (false) { - smem.cube.rot.y += 30; - smem.floor.rot.y += 5; - } - if (false) { - smem.cube.rot.y -= 30; - smem.floor.rot.y -= 5; - } - } - if (1) // Pad Input (Tape version) + if (1) // Pad Input { tb.used = 0; tb_scope_run(& tb) { /* BIOS-owned polling: per-frame snapshot of both ports. */ @@ -213,47 +202,7 @@ void update(PrimitiveArena* pa, U4* ordering_buf) S4 flag; //???? - // 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 + // Draw cube if (1) { m3s2_rotation (& smem.cube.rot, & smem.tform_world); @@ -285,61 +234,7 @@ void update(PrimitiveArena* pa, U4* ordering_buf) // 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 + // Draw floor if (1) { m3s2_rotation (& smem.floor.rot, & smem.tform_world);