mirror of
https://github.com/Ed94/pikuma_ps1.git
synced 2026-08-05 15:18:49 +00:00
some more review before bed.
This commit is contained in:
@@ -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_*)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+43
-41
@@ -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 */
|
||||
|
||||
+179
-3
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user