mirror of
https://github.com/Ed94/pikuma_ps1.git
synced 2026-08-08 08:38:50 +00:00
wip: input was working... messed it up (bios snapshot reads)
This commit is contained in:
@@ -9,6 +9,12 @@
|
||||
#define WORD_COUNT(name, count) enum { words_##name = (count) };
|
||||
#endif
|
||||
|
||||
/* atom_dbg_skip */
|
||||
#define mac_load_v2s2(rs_x, rs_y, r_base, offset) \
|
||||
load_half( rs_x, r_base, O_(V3_S2,x)) \
|
||||
, load_half( rs_y, r_base, O_(V3_S2,y))
|
||||
WORD_COUNT(mac_load_v2s2, 2)
|
||||
|
||||
/* atom_dbg_skip */
|
||||
#define mac_store_v2s2(rt_x, rt_y, base, offset) \
|
||||
store_half(rt_x, base, offset + O_(V2_S2,x)) \
|
||||
@@ -63,17 +69,3 @@ WORD_COUNT(mac_put_disp_env, 15)
|
||||
, mac_gcmd_push(gp0_word_nop(), reg_transfer, reg_base, port)
|
||||
WORD_COUNT(mac_put_draw_env, 48)
|
||||
|
||||
#define mac_pad_sio_write_pad_state(status_val, state_ptr_reg, scratch_reg) \
|
||||
add_ui(scratch_reg, R_0, status_val) \
|
||||
, store_word(scratch_reg, state_ptr_reg, O_(PadState,status)) /* FIX 2026-08-02: buttons = 0x0000FFFF = "no buttons pressed" in
|
||||
* libetc convention. Build it with LUI + ORI so addiu does not
|
||||
* sign-extend 0xFFFF to 0xFFFFFFFF. */ \
|
||||
, load_upper_i(scratch_reg, 0x0000) \
|
||||
, or_i(scratch_reg, scratch_reg, 0xFFFF) \
|
||||
, store_word(scratch_reg, state_ptr_reg, O_(PadState,buttons)) \
|
||||
, add_ui(scratch_reg, R_0, 0x80808080) \
|
||||
, store_word(scratch_reg, state_ptr_reg, O_(PadState,left_x)) \
|
||||
, add_ui(scratch_reg, R_0, 0) \
|
||||
, store_word(scratch_reg, state_ptr_reg, O_(PadState,attempt))
|
||||
WORD_COUNT(mac_pad_sio_write_pad_state, 9)
|
||||
|
||||
|
||||
@@ -5,16 +5,6 @@
|
||||
#pragma region hello_joypad.tape
|
||||
|
||||
|
||||
// --- atom: pad_input_demo (24 words) ---
|
||||
|
||||
#define _atom_offset_pad_left_exit_pad_left 6
|
||||
#define _atom_offset_pad_right_exit_pad_right 6
|
||||
|
||||
enum {
|
||||
atom_offset_pad_left_exit_pad_left = _atom_offset_pad_left_exit_pad_left,
|
||||
atom_offset_pad_right_exit_pad_right = _atom_offset_pad_right_exit_pad_right,
|
||||
};
|
||||
|
||||
// --- atom: cube_g4_face (77 words) ---
|
||||
|
||||
#define _atom_offset_cull_cube_g4_face_exit 42
|
||||
@@ -35,126 +25,30 @@ enum {
|
||||
atom_offset_bounds_chk_floor_f3_face_exit = _atom_offset_bounds_chk_floor_f3_face_exit,
|
||||
};
|
||||
|
||||
// --- atom: pad_sio_step (398 words) ---
|
||||
// --- atom: pad_bios_snapshot (88 words) ---
|
||||
|
||||
#define _atom_offset_settle_pre_port0_settle_pre_port0 -3
|
||||
#define _atom_offset_settle_post_port0_settle_post_port0 -3
|
||||
#define _atom_offset_wait_ack0_port0_ack0_received_port0 12
|
||||
#define _atom_offset_continue_wait_ack0_port0_wait_ack0_port0 -6
|
||||
#define _atom_offset_skip_port0_from_ack0_port1_start 160
|
||||
#define _atom_offset_wait_ackrel0_port0_ack_released_port0 12
|
||||
#define _atom_offset_continue_wait_ackrel0_port0_wait_ackrel0_port0 -6
|
||||
#define _atom_offset_skip_port0_from_ackrel0_port1_start 142
|
||||
#define _atom_offset_wait_ack1_port0_ack1_received_port0 12
|
||||
#define _atom_offset_continue_wait_ack1_port0_wait_ack1_port0 -6
|
||||
#define _atom_offset_skip_port0_from_ack1_port1_start 123
|
||||
#define _atom_offset_wait_ackrel1_port0_ack_released1_port0 12
|
||||
#define _atom_offset_continue_wait_ackrel1_port0_wait_ackrel1_port0 -6
|
||||
#define _atom_offset_skip_port0_from_ackrel1_port1_start 105
|
||||
#define _atom_offset_wait_ack2_port0_ack2_received_port0 12
|
||||
#define _atom_offset_continue_wait_ack2_port0_wait_ack2_port0 -6
|
||||
#define _atom_offset_skip_port0_from_ack2_port1_start 86
|
||||
#define _atom_offset_wait_ackrel2_port0_ack_released2_port0 12
|
||||
#define _atom_offset_continue_wait_ackrel2_port0_wait_ackrel2_port0 -6
|
||||
#define _atom_offset_skip_port0_from_ackrel2_port1_start 68
|
||||
#define _atom_offset_wait_ack3_port0_ack3_received_port0 12
|
||||
#define _atom_offset_continue_wait_ack3_port0_wait_ack3_port0 -6
|
||||
#define _atom_offset_skip_port0_from_ack3_port1_start 49
|
||||
#define _atom_offset_wait_ackrel3_port0_ack_released3_port0 12
|
||||
#define _atom_offset_continue_wait_ackrel3_port0_wait_ackrel3_port0 -6
|
||||
#define _atom_offset_skip_port0_from_ackrel3_port1_start 31
|
||||
#define _atom_offset_wait_rx4_port0_rx4_received_port0 12
|
||||
#define _atom_offset_continue_wait_rx4_port0_wait_rx4_port0 -6
|
||||
#define _atom_offset_skip_port0_from_rx4_port1_start 12
|
||||
#define _atom_offset_settle_pre_port1_settle_pre_port1 -3
|
||||
#define _atom_offset_settle_post_port1_settle_post_port1 -3
|
||||
#define _atom_offset_wait_ack0_port1_ack0_received_port1 12
|
||||
#define _atom_offset_continue_wait_ack0_port1_wait_ack0_port1 -6
|
||||
#define _atom_offset_skip_port1_from_ack0_end_atom 160
|
||||
#define _atom_offset_wait_ackrel0_port1_ack_released_port1 12
|
||||
#define _atom_offset_continue_wait_ackrel0_port1_wait_ackrel0_port1 -6
|
||||
#define _atom_offset_skip_port1_from_ackrel0_end_atom 142
|
||||
#define _atom_offset_wait_ack1_port1_ack1_received_port1 12
|
||||
#define _atom_offset_continue_wait_ack1_port1_wait_ack1_port1 -6
|
||||
#define _atom_offset_skip_port1_from_ack1_end_atom 123
|
||||
#define _atom_offset_wait_ackrel1_port1_ack_released1_port1 12
|
||||
#define _atom_offset_continue_wait_ackrel1_port1_wait_ackrel1_port1 -6
|
||||
#define _atom_offset_skip_port1_from_ackrel1_end_atom 105
|
||||
#define _atom_offset_wait_ack2_port1_ack2_received_port1 12
|
||||
#define _atom_offset_continue_wait_ack2_port1_wait_ack2_port1 -6
|
||||
#define _atom_offset_skip_port1_from_ack2_end_atom 86
|
||||
#define _atom_offset_wait_ackrel2_port1_ack_released2_port1 12
|
||||
#define _atom_offset_continue_wait_ackrel2_port1_wait_ackrel2_port1 -6
|
||||
#define _atom_offset_skip_port1_from_ackrel2_end_atom 68
|
||||
#define _atom_offset_wait_ack3_port1_ack3_received_port1 12
|
||||
#define _atom_offset_continue_wait_ack3_port1_wait_ack3_port1 -6
|
||||
#define _atom_offset_skip_port1_from_ack3_end_atom 49
|
||||
#define _atom_offset_wait_ackrel3_port1_ack_released3_port1 12
|
||||
#define _atom_offset_continue_wait_ackrel3_port1_wait_ackrel3_port1 -6
|
||||
#define _atom_offset_skip_port1_from_ackrel3_end_atom 31
|
||||
#define _atom_offset_wait_rx4_port1_rx4_received_port1 12
|
||||
#define _atom_offset_continue_wait_rx4_port1_wait_rx4_port1 -6
|
||||
#define _atom_offset_skip_port1_from_rx4_end_atom 12
|
||||
#define _atom_offset_snap_root_skip_disconnected 10
|
||||
#define _atom_offset_disconnected_snap_end 68
|
||||
#define _atom_offset_case_2_id_dispatch 10
|
||||
#define _atom_offset_pending_snap_end 56
|
||||
#define _atom_offset_id_dispatch_try_analog_stick 13
|
||||
#define _atom_offset_id_dispatch_snap_end 41
|
||||
#define _atom_offset_try_analog_stick_try_analog_pad 14
|
||||
#define _atom_offset_analog_stick_snap_end 25
|
||||
#define _atom_offset_try_analog_pad_try_unsupported 13
|
||||
#define _atom_offset_analog_pad_snap_end 9
|
||||
|
||||
enum {
|
||||
atom_offset_settle_pre_port0_settle_pre_port0 = _atom_offset_settle_pre_port0_settle_pre_port0,
|
||||
atom_offset_settle_post_port0_settle_post_port0 = _atom_offset_settle_post_port0_settle_post_port0,
|
||||
atom_offset_wait_ack0_port0_ack0_received_port0 = _atom_offset_wait_ack0_port0_ack0_received_port0,
|
||||
atom_offset_continue_wait_ack0_port0_wait_ack0_port0 = _atom_offset_continue_wait_ack0_port0_wait_ack0_port0,
|
||||
atom_offset_skip_port0_from_ack0_port1_start = _atom_offset_skip_port0_from_ack0_port1_start,
|
||||
atom_offset_wait_ackrel0_port0_ack_released_port0 = _atom_offset_wait_ackrel0_port0_ack_released_port0,
|
||||
atom_offset_continue_wait_ackrel0_port0_wait_ackrel0_port0 = _atom_offset_continue_wait_ackrel0_port0_wait_ackrel0_port0,
|
||||
atom_offset_skip_port0_from_ackrel0_port1_start = _atom_offset_skip_port0_from_ackrel0_port1_start,
|
||||
atom_offset_wait_ack1_port0_ack1_received_port0 = _atom_offset_wait_ack1_port0_ack1_received_port0,
|
||||
atom_offset_continue_wait_ack1_port0_wait_ack1_port0 = _atom_offset_continue_wait_ack1_port0_wait_ack1_port0,
|
||||
atom_offset_skip_port0_from_ack1_port1_start = _atom_offset_skip_port0_from_ack1_port1_start,
|
||||
atom_offset_wait_ackrel1_port0_ack_released1_port0 = _atom_offset_wait_ackrel1_port0_ack_released1_port0,
|
||||
atom_offset_continue_wait_ackrel1_port0_wait_ackrel1_port0 = _atom_offset_continue_wait_ackrel1_port0_wait_ackrel1_port0,
|
||||
atom_offset_skip_port0_from_ackrel1_port1_start = _atom_offset_skip_port0_from_ackrel1_port1_start,
|
||||
atom_offset_wait_ack2_port0_ack2_received_port0 = _atom_offset_wait_ack2_port0_ack2_received_port0,
|
||||
atom_offset_continue_wait_ack2_port0_wait_ack2_port0 = _atom_offset_continue_wait_ack2_port0_wait_ack2_port0,
|
||||
atom_offset_skip_port0_from_ack2_port1_start = _atom_offset_skip_port0_from_ack2_port1_start,
|
||||
atom_offset_wait_ackrel2_port0_ack_released2_port0 = _atom_offset_wait_ackrel2_port0_ack_released2_port0,
|
||||
atom_offset_continue_wait_ackrel2_port0_wait_ackrel2_port0 = _atom_offset_continue_wait_ackrel2_port0_wait_ackrel2_port0,
|
||||
atom_offset_skip_port0_from_ackrel2_port1_start = _atom_offset_skip_port0_from_ackrel2_port1_start,
|
||||
atom_offset_wait_ack3_port0_ack3_received_port0 = _atom_offset_wait_ack3_port0_ack3_received_port0,
|
||||
atom_offset_continue_wait_ack3_port0_wait_ack3_port0 = _atom_offset_continue_wait_ack3_port0_wait_ack3_port0,
|
||||
atom_offset_skip_port0_from_ack3_port1_start = _atom_offset_skip_port0_from_ack3_port1_start,
|
||||
atom_offset_wait_ackrel3_port0_ack_released3_port0 = _atom_offset_wait_ackrel3_port0_ack_released3_port0,
|
||||
atom_offset_continue_wait_ackrel3_port0_wait_ackrel3_port0 = _atom_offset_continue_wait_ackrel3_port0_wait_ackrel3_port0,
|
||||
atom_offset_skip_port0_from_ackrel3_port1_start = _atom_offset_skip_port0_from_ackrel3_port1_start,
|
||||
atom_offset_wait_rx4_port0_rx4_received_port0 = _atom_offset_wait_rx4_port0_rx4_received_port0,
|
||||
atom_offset_continue_wait_rx4_port0_wait_rx4_port0 = _atom_offset_continue_wait_rx4_port0_wait_rx4_port0,
|
||||
atom_offset_skip_port0_from_rx4_port1_start = _atom_offset_skip_port0_from_rx4_port1_start,
|
||||
atom_offset_settle_pre_port1_settle_pre_port1 = _atom_offset_settle_pre_port1_settle_pre_port1,
|
||||
atom_offset_settle_post_port1_settle_post_port1 = _atom_offset_settle_post_port1_settle_post_port1,
|
||||
atom_offset_wait_ack0_port1_ack0_received_port1 = _atom_offset_wait_ack0_port1_ack0_received_port1,
|
||||
atom_offset_continue_wait_ack0_port1_wait_ack0_port1 = _atom_offset_continue_wait_ack0_port1_wait_ack0_port1,
|
||||
atom_offset_skip_port1_from_ack0_end_atom = _atom_offset_skip_port1_from_ack0_end_atom,
|
||||
atom_offset_wait_ackrel0_port1_ack_released_port1 = _atom_offset_wait_ackrel0_port1_ack_released_port1,
|
||||
atom_offset_continue_wait_ackrel0_port1_wait_ackrel0_port1 = _atom_offset_continue_wait_ackrel0_port1_wait_ackrel0_port1,
|
||||
atom_offset_skip_port1_from_ackrel0_end_atom = _atom_offset_skip_port1_from_ackrel0_end_atom,
|
||||
atom_offset_wait_ack1_port1_ack1_received_port1 = _atom_offset_wait_ack1_port1_ack1_received_port1,
|
||||
atom_offset_continue_wait_ack1_port1_wait_ack1_port1 = _atom_offset_continue_wait_ack1_port1_wait_ack1_port1,
|
||||
atom_offset_skip_port1_from_ack1_end_atom = _atom_offset_skip_port1_from_ack1_end_atom,
|
||||
atom_offset_wait_ackrel1_port1_ack_released1_port1 = _atom_offset_wait_ackrel1_port1_ack_released1_port1,
|
||||
atom_offset_continue_wait_ackrel1_port1_wait_ackrel1_port1 = _atom_offset_continue_wait_ackrel1_port1_wait_ackrel1_port1,
|
||||
atom_offset_skip_port1_from_ackrel1_end_atom = _atom_offset_skip_port1_from_ackrel1_end_atom,
|
||||
atom_offset_wait_ack2_port1_ack2_received_port1 = _atom_offset_wait_ack2_port1_ack2_received_port1,
|
||||
atom_offset_continue_wait_ack2_port1_wait_ack2_port1 = _atom_offset_continue_wait_ack2_port1_wait_ack2_port1,
|
||||
atom_offset_skip_port1_from_ack2_end_atom = _atom_offset_skip_port1_from_ack2_end_atom,
|
||||
atom_offset_wait_ackrel2_port1_ack_released2_port1 = _atom_offset_wait_ackrel2_port1_ack_released2_port1,
|
||||
atom_offset_continue_wait_ackrel2_port1_wait_ackrel2_port1 = _atom_offset_continue_wait_ackrel2_port1_wait_ackrel2_port1,
|
||||
atom_offset_skip_port1_from_ackrel2_end_atom = _atom_offset_skip_port1_from_ackrel2_end_atom,
|
||||
atom_offset_wait_ack3_port1_ack3_received_port1 = _atom_offset_wait_ack3_port1_ack3_received_port1,
|
||||
atom_offset_continue_wait_ack3_port1_wait_ack3_port1 = _atom_offset_continue_wait_ack3_port1_wait_ack3_port1,
|
||||
atom_offset_skip_port1_from_ack3_end_atom = _atom_offset_skip_port1_from_ack3_end_atom,
|
||||
atom_offset_wait_ackrel3_port1_ack_released3_port1 = _atom_offset_wait_ackrel3_port1_ack_released3_port1,
|
||||
atom_offset_continue_wait_ackrel3_port1_wait_ackrel3_port1 = _atom_offset_continue_wait_ackrel3_port1_wait_ackrel3_port1,
|
||||
atom_offset_skip_port1_from_ackrel3_end_atom = _atom_offset_skip_port1_from_ackrel3_end_atom,
|
||||
atom_offset_wait_rx4_port1_rx4_received_port1 = _atom_offset_wait_rx4_port1_rx4_received_port1,
|
||||
atom_offset_continue_wait_rx4_port1_wait_rx4_port1 = _atom_offset_continue_wait_rx4_port1_wait_rx4_port1,
|
||||
atom_offset_skip_port1_from_rx4_end_atom = _atom_offset_skip_port1_from_rx4_end_atom,
|
||||
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) ---
|
||||
@@ -175,25 +69,5 @@ enum {
|
||||
atom_offset_end_low_exit_stick = _atom_offset_end_low_exit_stick,
|
||||
};
|
||||
|
||||
// --- atom: pad_sio_diag_byte_exchange (53 words) ---
|
||||
|
||||
#define _atom_offset_diag_wait_ack0_diag_ack0_done 5
|
||||
#define _atom_offset_diag_wait_ack0_diag_wait_ack0 -6
|
||||
#define _atom_offset_diag_timeout_ack0_diag_timeout 24
|
||||
#define _atom_offset_diag_wait_ack1_diag_ack1_done 5
|
||||
#define _atom_offset_diag_wait_ack1_diag_wait_ack1 -6
|
||||
#define _atom_offset_diag_timeout_ack1_diag_timeout 11
|
||||
#define _atom_offset_diag_success_diag_done 7
|
||||
|
||||
enum {
|
||||
atom_offset_diag_wait_ack0_diag_ack0_done = _atom_offset_diag_wait_ack0_diag_ack0_done,
|
||||
atom_offset_diag_wait_ack0_diag_wait_ack0 = _atom_offset_diag_wait_ack0_diag_wait_ack0,
|
||||
atom_offset_diag_timeout_ack0_diag_timeout = _atom_offset_diag_timeout_ack0_diag_timeout,
|
||||
atom_offset_diag_wait_ack1_diag_ack1_done = _atom_offset_diag_wait_ack1_diag_ack1_done,
|
||||
atom_offset_diag_wait_ack1_diag_wait_ack1 = _atom_offset_diag_wait_ack1_diag_wait_ack1,
|
||||
atom_offset_diag_timeout_ack1_diag_timeout = _atom_offset_diag_timeout_ack1_diag_timeout,
|
||||
atom_offset_diag_success_diag_done = _atom_offset_diag_success_diag_done,
|
||||
};
|
||||
|
||||
#pragma endregion hello_joypad.tape
|
||||
|
||||
|
||||
@@ -123,25 +123,13 @@ typedef Struct_(SMemory) {
|
||||
Ent_Cube cube;
|
||||
Ent_Floor floor;
|
||||
|
||||
U4 pad_state;
|
||||
|
||||
PadState pad[2]; /* raw_sio_pad_poll_20260802 — per-port */
|
||||
PadSioInit pad_sio_init; /* raw_sio_pad_poll_20260802 — boot init */
|
||||
PadBiosRaw pad_raw[2];
|
||||
PadState pad[2];
|
||||
|
||||
U4_V scratchpad; // d-cache
|
||||
};
|
||||
global SMemory smem;
|
||||
extern SMemory smem;
|
||||
U4 scratch_for_atom_diag_pin; /* raw_sio_pad_poll_20260802 — diag atom scratch */
|
||||
|
||||
#define pad0_signal_(btn_id) smem.pad_state & pad0_(btn_id)
|
||||
#define pad1_signal_(btn_id) smem.pad_state & pad1_(btn_id)
|
||||
|
||||
|
||||
// TODO(Ed):
|
||||
FI_ U4* spad_warm(MipsAtom atom) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
I_ B1* prim__alloc(U4 type_width, Str8 type_name) {
|
||||
gknown PrimitiveArena* pa = & smem.primitives;
|
||||
@@ -153,6 +141,80 @@ I_ B1* prim__alloc(U4 type_width, Str8 type_name) {
|
||||
}
|
||||
#define prim_alloc(type) (type*)prim__alloc(S_(type), slit( stringify(type)))
|
||||
|
||||
/* Uses ONE 8-byte frame allocated via the compiler's standard prologue.
|
||||
* The 4 wasted-arg words for B(12h) InitPAD2 live at [SP+0..15] but are not explicitly allocated.
|
||||
* The compiler handles the MIPS O32 "wasted stack" convention for us by treating the B-call as a 4-arg call.
|
||||
*
|
||||
* The buffer pointers are passed as arguments so the compiler keeps them in callee-saved registers;
|
||||
* The B(12h) asm volatile block does NOT clobber those registers (it clobbers only the volatile GPRs + the B-table arg registers explicitly).
|
||||
* The C-level writes after the call re-load the pointers from their callee-saved homes.
|
||||
*
|
||||
* The clobber list for both B-calls names the full BIOS destroy set documented in kernelbios.md:167-174 (R1..R15, R24..R25, R31, HI/LO).
|
||||
* The kernel-ABI "volatile GPRs" subset is clb_system; the rest of the destroy set is enumerated explicitly here. */
|
||||
NI_ void pad_bios_init_start(PadBiosRaw* raw0, PadBiosRaw* raw1)
|
||||
{
|
||||
/* Pin raw0 + raw1 to $a0 + $a1 via rgcc; the B(12h) call uses these directly.
|
||||
* The `(void)` casts mark them as unread after the call so the compiler doesn't need to move them back. */
|
||||
register PadBiosRaw* p0 rgcc(R_A0) = raw0;
|
||||
register PadBiosRaw* p1 rgcc(R_A1) = raw1;
|
||||
(void)p0; (void)p1;
|
||||
|
||||
// TODO(Ed): Properly annotate the raw values in the inline asm instructions.
|
||||
// Use enums.
|
||||
|
||||
/* B(12h) InitPAD2(raw0, 0x22, raw1, 0x22)
|
||||
* $a0 = raw0 (rgcc-bound; survives the sequence below)
|
||||
* $a1 = raw1 (preserved into $a2 before $a1 is overwritten)
|
||||
* $a2 = raw1 (moved from $a1; survives $a1's overwrite)
|
||||
* $a3 = 0x22 (immediate)
|
||||
* $t1 = 0x12 (function number)
|
||||
* $t2 = 0xB0 (BIOS B-table address) */
|
||||
asm volatile(
|
||||
asm_words(
|
||||
or_u( rarg_2, rarg_1, rdiscard), /* $a2 = $a1 = raw1 */
|
||||
add_ui( rarg_1, rdiscard, 0x22), /* $a1 = 0x22 */
|
||||
add_ui( rarg_3, rdiscard, 0x22), /* $a3 = 0x22 */
|
||||
add_ui( rtmp_1, rdiscard, 0x12), /* $t1 = 0x12 */
|
||||
add_ui( rtmp_2, rdiscard, 0xB0), /* $t2 = 0xB0 */
|
||||
call_reg(rtmp_2), /* jalr $t2, $ra */
|
||||
nop /* BD slot */
|
||||
)
|
||||
asm_rpins, r_use(p0), r_use(p1)
|
||||
asm_clobber:
|
||||
rlit(R_AT),
|
||||
rlit(R_V0), rlit(R_V1),
|
||||
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), rlit(R_T9),
|
||||
rlit(R_RA),
|
||||
clb_mem_drain
|
||||
);
|
||||
|
||||
/* BIOS clobbered $a0..$a3, $ra, and the volatile GPRs above.
|
||||
* The compiler keeps raw0 + raw1 in callee-saved registers (or the outer frame's saved slots)
|
||||
* because the asm volatile blocks only clobber the volatile GPRs above.
|
||||
* The C-level writes re-load the pointers via the parameter names and write 0xFF to each
|
||||
* buffer's status byte to mark the initial-state hazard documented in kernelbios.md:1621-1624. */
|
||||
u1_v(raw0)[0] = 0xFF;
|
||||
u1_v(raw1)[0] = 0xFF;
|
||||
|
||||
/* B(13h) StartPAD2() — no args. The BIOS preserves $sp. */
|
||||
asm volatile(
|
||||
asm_words(
|
||||
add_ui( rtmp_1, rdiscard, 0x13), /* $t1 = 0x13 */
|
||||
add_ui( rtmp_2, rdiscard, 0xB0), /* $t2 = 0xB0 (re-load) */
|
||||
call_reg(rtmp_2), /* jalr $t2, $ra */
|
||||
nop /* BD slot */
|
||||
)
|
||||
asm_clobber:
|
||||
rlit(R_AT),
|
||||
rlit(R_V0), rlit(R_V1),
|
||||
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), rlit(R_T9),
|
||||
rlit(R_RA),
|
||||
clb_mem_drain
|
||||
);
|
||||
}
|
||||
|
||||
void gp_screen_init_c11(DoubleBuffer* screen_buf, S4* active_buf_id)
|
||||
{
|
||||
reset_graph(0);
|
||||
@@ -197,21 +259,19 @@ void gp_display_frame(DoubleBuffer* screen_buf, S4* active_buf_id, U4* ordering_
|
||||
active_buf_id[0] = ! active_buf_id[0]; // Swap current buffer
|
||||
}
|
||||
|
||||
void render(void) {
|
||||
}
|
||||
|
||||
GCC_OPTIMIZATION_DISABLE
|
||||
void update(PrimitiveArena* pa, U4* ordering_buf)
|
||||
{
|
||||
TapeBuilder tb = tb_make(slice_ut_arr(smem.MemTape));
|
||||
|
||||
if (0) // Pad Input
|
||||
if (0) // Pad Input (dead — kept for the source-as-written record; references the deleted `pad_state` field)
|
||||
{
|
||||
if (pad0_signal_(Pad_Left)) {
|
||||
(void)Pad_Left; (void)Pad_Right; /* suppress unused-token warnings */
|
||||
if (false) {
|
||||
smem.cube.rot.y += 30;
|
||||
smem.floor.rot.y += 5;
|
||||
}
|
||||
if (pad0_signal_(Pad_Right)) {
|
||||
if (false) {
|
||||
smem.cube.rot.y -= 30;
|
||||
smem.floor.rot.y -= 5;
|
||||
}
|
||||
@@ -219,18 +279,18 @@ void update(PrimitiveArena* pa, U4* ordering_buf)
|
||||
if (1) // Pad Input (Tape version)
|
||||
{
|
||||
tb.used = 0; tb_scope_run(& tb) {
|
||||
/* Per-frame SIO0 poll: pad_sio_step polls both ports; pad_apply_input applies rotation */
|
||||
tb_emit(& tb, pad_sio_init);
|
||||
tb_emit(& tb, pad_sio_step);
|
||||
tb_data(& tb, u4_(& smem.pad[0]));
|
||||
tb_data(& tb, u4_(& smem.pad[1]));
|
||||
tb_data(& tb, u4_(smem.pad_sio_init.sio_base_addr[0]));
|
||||
tb_data(& tb, u4_(smem.pad_sio_init.sio_base_addr[1]));
|
||||
/* BIOS-owned polling: per-frame snapshot of both ports. */
|
||||
tb_emit_(pad_bios_snapshot);
|
||||
tb_data_(Binds_PadBiosSnapshot.raw, & smem.pad_raw[0]);
|
||||
tb_data_(Binds_PadBiosSnapshot.state, & smem.pad[0]);
|
||||
tb_emit_(pad_bios_snapshot);
|
||||
tb_data_(Binds_PadBiosSnapshot.raw, & smem.pad_raw[1]);
|
||||
tb_data_(Binds_PadBiosSnapshot.state, & smem.pad[1]);
|
||||
/* Per-frame rotation apply: consume pad[0].buttons + pad[0].left_x */
|
||||
tb_emit(& tb, pad_apply_input);
|
||||
tb_data(& tb, u4_(& smem.pad[0]));
|
||||
tb_data(& tb, u4_(& smem.cube.rot));
|
||||
tb_data(& tb, u4_(& smem.floor.rot));
|
||||
tb_emit_(pad_apply_input);
|
||||
tb_data_(Binds_PadApplyInput.state, & smem.pad[0]);
|
||||
tb_data_(Binds_PadApplyInput.cube_rot, & smem.cube.rot);
|
||||
tb_data_(Binds_PadApplyInput.floor_rot, & smem.floor.rot);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -440,6 +500,9 @@ void update(PrimitiveArena* pa, U4* ordering_buf)
|
||||
}
|
||||
GCC_OPTIMIZATION_ENABLE
|
||||
|
||||
void render(void) {
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
smem = (SMemory){0};
|
||||
@@ -450,7 +513,6 @@ int main(void)
|
||||
ent_cube128_init(& smem.cube.verts, & smem.cube.faces); {
|
||||
Ent_Cube* cube = & smem.cube;
|
||||
cube->rot = v3s2(0, 0, 0);
|
||||
// cube->pos = v3s4(0, 0, 900);
|
||||
cube->scale = v3s4_fp_one();
|
||||
cube->accel = v3s4(0, 1, 0);
|
||||
cube->pos = v3s4(0, -400, 1800);
|
||||
@@ -464,25 +526,14 @@ int main(void)
|
||||
}
|
||||
TapeBuilder tb = tb_make(slice_ut_arr(smem.MemTape)); {
|
||||
reset_graph(0);
|
||||
pad_sio_init_setup(& smem.pad_sio_init, & smem.pad[0], & smem.pad[1]);
|
||||
/* raw_sio_pad_poll_20260802 — pin the SIO base for the boot atom.
|
||||
* Use the KSEG1 constant directly (not via pad_sio_init.sio_base_addr[0])
|
||||
* because rgcc binds the register to the variable's storage, not the
|
||||
* value-at-call-site. Reading the field at tape-emit time would require
|
||||
* an extra load, which the atom body can't do implicitly. */
|
||||
register U4 pad_io_base rgcc(R_PadSioBase) = pad_IO_KSEG1_BASE;
|
||||
register U4* io_base_addr rgcc(R_IO_BaseAddr) = u4_r(IO_BASE_ADDR);
|
||||
register U4* r_diag_scratch rgcc(R_DiagPinScratch) = & scratch_for_atom_diag_pin;
|
||||
register DoubleBuffer* screen_buf rgcc(R_ScreenBuf) = & smem.screen_buf;
|
||||
/* Direct BIOS: poll both ports during VBlank. */
|
||||
pad_bios_init_start(& smem.pad_raw[0], & smem.pad_raw[1]);
|
||||
/* Pinned registers for the GPU init atom. */
|
||||
register U4* io_base_addr rgcc(R_IO_BaseAddr) = u4_r(IO_BASE_ADDR);
|
||||
register DoubleBuffer* screen_buf rgcc(R_ScreenBuf) = & smem.screen_buf;
|
||||
tb.used = 0; tb_scope_run(& tb) {
|
||||
tb_emit(& tb, screen_env_init);
|
||||
tb_emit(& tb, gp_screen_init);
|
||||
tb_emit(& tb, pad_sio_init);
|
||||
tb_emit(& tb, pad_sio_step); /* initial baseline */
|
||||
tb_data(& tb, u4_(& smem.pad[0]));
|
||||
tb_data(& tb, u4_(& smem.pad[1]));
|
||||
tb_data(& tb, u4_(smem.pad_sio_init.sio_base_addr[0]));
|
||||
tb_data(& tb, u4_(smem.pad_sio_init.sio_base_addr[1]));
|
||||
}
|
||||
}
|
||||
while (1) {
|
||||
@@ -495,13 +546,3 @@ int main(void)
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* raw_sio_pad_poll_20260802 — populate the boot-time pad SIO context.
|
||||
* Populates the two PadState pointers + the KSEG1 SIO base address. */
|
||||
void pad_sio_init_setup(PadSioInit* init, PadState* s0, PadState* s1)
|
||||
{
|
||||
init->sio_base_addr[0] = pad_IO_KSEG1_BASE;
|
||||
init->sio_base_addr[1] = pad_IO_KSEG1_BASE;
|
||||
init->pad_state_ptr[0] = s0;
|
||||
init->pad_state_ptr[1] = s1;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
# include "duffle/dsl.h"
|
||||
# include "duffle/math.h"
|
||||
# include "duffle/gp.h"
|
||||
# include "duffle/pad.h"
|
||||
#endif
|
||||
|
||||
enum {
|
||||
@@ -10,40 +11,16 @@ enum {
|
||||
OrderingTbl_Len = 2048
|
||||
};
|
||||
|
||||
#define ScreenRes_X 320
|
||||
#define ScreenRes_Y 240
|
||||
#define ScreenZ 320
|
||||
#define ScreenRes_CenterX (ScreenRes_X >> 1)
|
||||
#define ScreenRes_CenterY (ScreenRes_Y >> 1)
|
||||
enum {
|
||||
ScreenRes_X = 320,
|
||||
ScreenRes_Y = 240,
|
||||
ScreenZ = 320,
|
||||
ScreenRes_CenterX = (ScreenRes_X >> 1),
|
||||
ScreenRes_CenterY = (ScreenRes_Y >> 1),
|
||||
};
|
||||
|
||||
enum {
|
||||
fp_one = (1 << 12),
|
||||
};
|
||||
|
||||
#define v3s4_fp_one() v3s4(fp_one, fp_one, fp_one)
|
||||
|
||||
#ifdef INTELLISENSE_DIRECTIVES
|
||||
# include "duffle/pad.h"
|
||||
#endif
|
||||
|
||||
/* ============================================================
|
||||
* Raw SIO0 pad subsystem (raw_sio_pad_poll_20260802)
|
||||
* ============================================================ */
|
||||
|
||||
typedef Struct_(Binds_PadSioStep) {
|
||||
PadState* state0;
|
||||
PadState* state1;
|
||||
U4 sio_base_addr0;
|
||||
U4 sio_base_addr1;
|
||||
};
|
||||
|
||||
typedef Struct_(Binds_PadApplyInput) {
|
||||
PadState* state;
|
||||
V3_S2* cube_rot;
|
||||
V3_S2* floor_rot;
|
||||
};
|
||||
|
||||
/* Populates smem.pad_sio_init with the two PadState pointers and the
|
||||
* KSEG1 SIO0 base. The caller (main()) runs this once at boot before
|
||||
* the first tb_emit(pad_sio_init). */
|
||||
void pad_sio_init_setup(PadSioInit* init, PadState* s0, PadState* s1);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,659 @@
|
||||
|
||||
#if 0 /* ac_pad_sio_write_pad_state — superseded by pad_bios_snapshot */
|
||||
|
||||
/* ============================================================
|
||||
* raw_sio_pad_poll_20260802 — superseded by bios_pad_buffer_snapshot_20260803.
|
||||
* The doomed raw-SIO production atoms (ac_pad_sio_write_pad_state,
|
||||
* pad_sio_init, pad_sio_step, pad_sio_diag_pin, pad_sio_diag_byte_exchange)
|
||||
* reference symbols that were removed from code/duffle/pad.h during
|
||||
* Phase 1. Each is wrapped in a narrow `#if 0` so the C compile skips
|
||||
* the body while the source-as-written text stays in place for the
|
||||
* Phase 5.1 deletion pass. The wrap is removed (and the bodies are
|
||||
* deleted) by Phase 5.1 of this track.
|
||||
* ============================================================ */
|
||||
|
||||
* Writes the per-port PadState in 5 instructions plus 4 store_word calls (status,
|
||||
* buttons, left_x/y/right_x/right_y packed, attempt). The provisional decode publishes
|
||||
* 0x0000FFFF buttons + centered axes on every path until response-byte decode lands.
|
||||
*
|
||||
* Args:
|
||||
* status_val - the PadSioStatus enum value to publish
|
||||
* state_ptr_reg - the PadState* base (R_PadState at the call site)
|
||||
* scratch_reg - scratch register for the value being stored (e.g., R_T0)
|
||||
*
|
||||
* Emits 9 instructions (status/buttons/axes/attempt stores plus the
|
||||
* two-instruction zero-extended buttons load).
|
||||
*/
|
||||
FI_ Slice_MipsCode ac_pad_sio_write_pad_state(U4 status_val, U4 state_ptr_reg, U4 scratch_reg)
|
||||
MipsAtomComp_Proc_(ac_pad_sio_write_pad_state, {
|
||||
add_ui(scratch_reg, R_0, status_val),
|
||||
store_word(scratch_reg, state_ptr_reg, O_(PadState,status)),
|
||||
/* FIX 2026-08-02: buttons = 0x0000FFFF = "no buttons pressed" in
|
||||
* libetc convention. Build it with LUI + ORI so addiu does not
|
||||
* sign-extend 0xFFFF to 0xFFFFFFFF. */
|
||||
load_upper_i(scratch_reg, 0x0000),
|
||||
or_i(scratch_reg, scratch_reg, 0xFFFF),
|
||||
store_word(scratch_reg, state_ptr_reg, O_(PadState,buttons)),
|
||||
add_ui(scratch_reg, R_0, 0x80808080),
|
||||
store_word(scratch_reg, state_ptr_reg, O_(PadState,left_x)),
|
||||
add_ui(scratch_reg, R_0, 0),
|
||||
store_word(scratch_reg, state_ptr_reg, O_(PadState,attempt))
|
||||
})
|
||||
#endif /* end ac_pad_sio_write_pad_state wrap */
|
||||
|
||||
/* ----- pad_sio_init -----
|
||||
* Boot-time SIO0 init. Caller pins R_T6 = sio_base_addr0.
|
||||
* Issues SIO CTRL=0x0040 (reset), MODE=0x000D, BAUD=0x0088.
|
||||
* (Phase 2 fills the body.)
|
||||
*/
|
||||
#if 0 /* pad_sio_init — superseded by pad_bios_init_start (Phase 1.3) */
|
||||
internal MipsAtom_(pad_sio_init) atom_info(atom_phase(pad_init)
|
||||
, atom_reads(R_T5, R_T6)
|
||||
, atom_writes(R_T5, R_T6)
|
||||
) {
|
||||
/* FIX 2026-08-02: explicitly load the KSEG1 base into R_T6 at the top of
|
||||
* the atom body. The rgcc(R_PadSioBase) binding in main() pins R_T6 = base
|
||||
* when main() runs, but $12 is caller-saved per the O32 ABI — when tape_run
|
||||
* is invoked, R_T6 is fair game. The atom body cannot rely on the value. */
|
||||
load_upper_i(R_T6, pad_IO_KSEG1_BASE >> 16), /* R_T6 high 16 = 0xBF80 */
|
||||
or_i(R_T6, R_T6, pad_IO_KSEG1_BASE & 0xFFFF), /* R_T6 = 0xBF800000 */
|
||||
|
||||
/* SIO CTRL = 0x0040 (reset) */
|
||||
add_ui(R_T5, R_0, pad_SIO_CTRL_RESET),
|
||||
store_half(R_T5, R_T6, pad_SIO_CTRL_OFFSET),
|
||||
/* SIO MODE = 0x000D (MUL1, 8-bit, no parity, idle-high) */
|
||||
add_ui(R_T5, R_0, pad_SIO_MODE_INIT),
|
||||
store_half(R_T5, R_T6, pad_SIO_MODE_OFFSET),
|
||||
/* SIO BAUD = 0x0088 (~250 kHz) */
|
||||
add_ui(R_T5, R_0, pad_SIO_BAUD_INIT),
|
||||
store_half(R_T5, R_T6, pad_SIO_BAUD_OFFSET),
|
||||
mac_yield(),
|
||||
};
|
||||
#endif /* end pad_sio_init wrap */
|
||||
|
||||
/* ----- pad_sio_step -----
|
||||
* Per-frame bounded raw-SIO transaction. Reads PadState pointers + SIO
|
||||
* base addresses from Binds_PadSioStep; writes per-port status +
|
||||
* buttons + axes into smem.pad[0..1].
|
||||
* Body shape (per spec §"Transaction model (per port, per pad_sio_step)"):
|
||||
* port 0: CTRL=CLEANUP → settle → CTRL=port-select → settle → exchange 5
|
||||
* bytes (addr + 0x42 0x00 0x00 0x00) → decode → write PadState[0]
|
||||
* → CTRL=CLEANUP.
|
||||
* port 1: swap scratch regs (sio_base_addr1 → R_PadSioBase, state1 →
|
||||
* R_PadState) → mirror port 0 sequence.
|
||||
*
|
||||
* Bounded-loop semantics: every countdown is wrapped in
|
||||
* add_ui_self(R_T1, -1) + branch_ne(R_T1, R_0, ...)
|
||||
* with a known maximum (pad_SIO_SETTLE_BEFORE_TX=1000, pad_SIO_SETTLE_AFTER_TX=2000,
|
||||
* pad_SIO_WAIT_BUDGET=4096). The static-analysis pass currently reports
|
||||
* has_loops = true; the follow-up metaprogram track that learns modeled-bounded
|
||||
* loops is out of scope here (per spec §"Risks").
|
||||
*
|
||||
* Scratch register strategy:
|
||||
* R_PadStatus = R_T4 — RESERVED for port-1 swap (holds state1)
|
||||
* R_PadCountdown = R_T5 — RESERVED for port-1 swap (holds sio_base_addr1)
|
||||
* R_T0 — byte-exchange value + STAT read (clobbered freely)
|
||||
* R_T1 — countdown budget (clobbered freely)
|
||||
* R_PadState = R_T7 — PadState* (preserved for PadState writes)
|
||||
* R_PadSioBase = R_T6 — SIO base (preserved through the port)
|
||||
*
|
||||
* Response decode (Task 3.1 teaching scope):
|
||||
* - status = PadSioStatus_Digital (hardcoded)
|
||||
* - buttons = 0xFFFF (no buttons pressed in the provisional libetc
|
||||
* convention; full response-byte decode is follow-up)
|
||||
* - axes = 0x80808080 (centered: left_x=0x80, left_y=0x80,
|
||||
* right_x=0x80, right_y=0x80)
|
||||
* - attempt = 0
|
||||
* - DualShock handshake (0x43 0x01 → 0x44 0x01 0x03 → 0x43 0x00) is
|
||||
* follow-up scope; the hardcoded digital decode is a placeholder.
|
||||
*
|
||||
* Both ports raise /CS (CTRL = pad_SIO_CTRL_CLEANUP) before exit. Both ports
|
||||
* treat response timeout as PadSioStatus_Disconnected per the spec §"Failure
|
||||
* handling" + the canonical per-port timeout semantics.
|
||||
*/
|
||||
#if 0 /* pad_sio_step — superseded by pad_bios_snapshot (Phase 2.1) */
|
||||
internal MipsAtom_(pad_sio_step) atom_info(atom_bind(Binds_PadSioStep)
|
||||
, atom_reads(R_TapePtr, R_PadSioBase, R_PadState, R_PadStatus, R_PadCountdown)
|
||||
, atom_writes(R_PadStatus, R_PadCountdown)
|
||||
) {
|
||||
/* FIX 2026-08-02: explicitly load KSEG1 base into R_PadSioBase (R_T6) at the
|
||||
* top. The rgcc() binding in main() does NOT survive the tape_run call
|
||||
* because R_T6 is caller-saved per the O32 ABI. The pad_sio_init atom
|
||||
* (also in the per-frame tape) reloads R_T6 separately. */
|
||||
load_upper_i(R_PadSioBase, pad_IO_KSEG1_BASE >> 16),
|
||||
or_i(R_PadSioBase, R_PadSioBase, pad_IO_KSEG1_BASE & 0xFFFF),
|
||||
|
||||
/* Pop Binds from tape (in Binds_PadSioStep declaration order) */
|
||||
load_word(R_PadState, R_TapePtr, O_(Binds_PadSioStep,state0)),
|
||||
load_word(R_PadStatus, R_TapePtr, O_(Binds_PadSioStep,state1)), /* reserved for port-1 swap */
|
||||
load_word(R_PadSioBase, R_TapePtr, O_(Binds_PadSioStep,sio_base_addr0)),
|
||||
load_word(R_PadCountdown, R_TapePtr, O_(Binds_PadSioStep,sio_base_addr1)), /* reserved for port-1 swap */
|
||||
add_ui_self(R_TapePtr, S_(Binds_PadSioStep)),
|
||||
|
||||
/* ============== PORT 0 TRANSACTION ============== */
|
||||
/* Use R_T0 (byte value / STAT read) + R_T1 (countdown) as scratch.
|
||||
* R_PadStatus (state1) + R_PadCountdown (sio_base_addr1) are preserved
|
||||
* through the port-0 body and swapped into R_PadSioBase + R_PadState
|
||||
* at atom_offset(port1_start, ...) below. */
|
||||
|
||||
/* 1. Cleanup: CTRL = 0x0010 (raise /CS, clear stale status) */
|
||||
add_ui(R_T0, R_0, pad_SIO_CTRL_CLEANUP),
|
||||
store_half(R_T0, R_PadSioBase, pad_SIO_CTRL_OFFSET),
|
||||
/* Bounded by pad_SIO_SETTLE_BEFORE_TX = 1000 iterations. */
|
||||
add_ui(R_T1, R_0, pad_SIO_SETTLE_BEFORE_TX),
|
||||
atom_label(settle_pre_port0)
|
||||
nop, /* BD slot */
|
||||
add_ui_self(R_T1, -1),
|
||||
branch_ne(R_T1, R_0, atom_offset(settle_pre_port0, settle_pre_port0)),
|
||||
|
||||
/* 2. Port-select: CTRL = 0x0003 (TX enable + DTR /CS) for port 0 */
|
||||
add_ui(R_T0, R_0, pad_SIO_CTRL_TX_ENABLE),
|
||||
or_i(R_T0, R_T0, pad_SIO_CTRL_DTR_CS), /* set /CS line low */
|
||||
store_half(R_T0, R_PadSioBase, pad_SIO_CTRL_OFFSET),
|
||||
/* Bounded by pad_SIO_SETTLE_AFTER_TX = 2000 iterations. */
|
||||
add_ui(R_T1, R_0, pad_SIO_SETTLE_AFTER_TX),
|
||||
atom_label(settle_post_port0)
|
||||
nop,
|
||||
add_ui_self(R_T1, -1),
|
||||
branch_ne(R_T1, R_0, atom_offset(settle_post_port0, settle_post_port0)),
|
||||
|
||||
/* 3. Address byte (0x01) — send + RX-ready wait + read response + RX-drain confirmation */
|
||||
add_ui(R_T0, R_0, pad_PROTO_ADDR),
|
||||
store_byte(R_T0, R_PadSioBase, pad_SIO_DATA_OFFSET),
|
||||
|
||||
/* Bounded by pad_SIO_WAIT_BUDGET = 4096 iterations. */
|
||||
add_ui(R_T1, R_0, pad_SIO_WAIT_BUDGET),
|
||||
atom_label(wait_ack0_port0)
|
||||
load_half_u(R_T0, R_PadSioBase, pad_SIO_STAT_OFFSET),
|
||||
nop,
|
||||
and_i(R_T0, R_T0, pad_SIO_STAT_RX_NOT_EMPTY),
|
||||
branch_ne(R_T0, R_0, atom_offset(wait_ack0_port0, ack0_received_port0)),
|
||||
add_ui_self(R_T1, -1),
|
||||
atom_label(continue_wait_ack0_port0)
|
||||
branch_ne(R_T1, R_0, atom_offset(continue_wait_ack0_port0, wait_ack0_port0)),
|
||||
/* RX timeout → mark disconnected; skip to port 1 */
|
||||
mac_pad_sio_write_pad_state(PadSioStatus_Disconnected, R_PadState, R_T0),
|
||||
atom_label(skip_port0_from_ack0)
|
||||
branch_equal(R_0, R_0, atom_offset(skip_port0_from_ack0, port1_start)),
|
||||
|
||||
atom_label(ack0_received_port0)
|
||||
/* Read open-bus response byte 0 — discard per docs/psx-spx §controllersandmemorycards.md */
|
||||
load_byte_u(R_T0, R_PadSioBase, pad_SIO_DATA_OFFSET),
|
||||
|
||||
/* Confirm RX FIFO drained before sending byte 1. Bounded by pad_SIO_WAIT_BUDGET = 4096 iterations. */
|
||||
add_ui(R_T1, R_0, pad_SIO_WAIT_BUDGET),
|
||||
atom_label(wait_ackrel0_port0)
|
||||
load_half_u(R_T0, R_PadSioBase, pad_SIO_STAT_OFFSET),
|
||||
nop,
|
||||
and_i(R_T0, R_T0, pad_SIO_STAT_RX_NOT_EMPTY),
|
||||
branch_equal(R_T0, R_0, atom_offset(wait_ackrel0_port0, ack_released_port0)),
|
||||
add_ui_self(R_T1, -1),
|
||||
atom_label(continue_wait_ackrel0_port0)
|
||||
branch_ne(R_T1, R_0, atom_offset(continue_wait_ackrel0_port0, wait_ackrel0_port0)),
|
||||
/* RX-drain timeout → disconnected; skip to port 1 */
|
||||
mac_pad_sio_write_pad_state(PadSioStatus_Disconnected, R_PadState, R_T0),
|
||||
atom_label(skip_port0_from_ackrel0)
|
||||
branch_equal(R_0, R_0, atom_offset(skip_port0_from_ackrel0, port1_start)),
|
||||
|
||||
atom_label(ack_released_port0)
|
||||
|
||||
/* === Byte 1 (port 0): send 0x42 (cmd read) + RX-ready wait + read response + RX-drain confirmation === */
|
||||
/* Bounded by pad_SIO_WAIT_BUDGET = 4096 iterations. */
|
||||
add_ui(R_T0, R_0, pad_PROTO_CMD_READ),
|
||||
store_byte(R_T0, R_PadSioBase, pad_SIO_DATA_OFFSET),
|
||||
add_ui(R_T1, R_0, pad_SIO_WAIT_BUDGET),
|
||||
atom_label(wait_ack1_port0)
|
||||
load_half_u(R_T0, R_PadSioBase, pad_SIO_STAT_OFFSET),
|
||||
nop,
|
||||
and_i(R_T0, R_T0, pad_SIO_STAT_RX_NOT_EMPTY),
|
||||
branch_ne(R_T0, R_0, atom_offset(wait_ack1_port0, ack1_received_port0)),
|
||||
add_ui_self(R_T1, -1),
|
||||
atom_label(continue_wait_ack1_port0)
|
||||
branch_ne(R_T1, R_0, atom_offset(continue_wait_ack1_port0, wait_ack1_port0)),
|
||||
mac_pad_sio_write_pad_state(PadSioStatus_Disconnected, R_PadState, R_T0),
|
||||
atom_label(skip_port0_from_ack1)
|
||||
branch_equal(R_0, R_0, atom_offset(skip_port0_from_ack1, port1_start)),
|
||||
|
||||
atom_label(ack1_received_port0)
|
||||
/* Read response ID byte — discarded for teaching scope (decode hardcoded). */
|
||||
load_byte_u(R_T0, R_PadSioBase, pad_SIO_DATA_OFFSET),
|
||||
|
||||
/* RX FIFO drain wait. Bounded by pad_SIO_WAIT_BUDGET = 4096 iterations. */
|
||||
add_ui(R_T1, R_0, pad_SIO_WAIT_BUDGET),
|
||||
atom_label(wait_ackrel1_port0)
|
||||
load_half_u(R_T0, R_PadSioBase, pad_SIO_STAT_OFFSET),
|
||||
nop,
|
||||
and_i(R_T0, R_T0, pad_SIO_STAT_RX_NOT_EMPTY),
|
||||
branch_equal(R_T0, R_0, atom_offset(wait_ackrel1_port0, ack_released1_port0)),
|
||||
add_ui_self(R_T1, -1),
|
||||
atom_label(continue_wait_ackrel1_port0)
|
||||
branch_ne(R_T1, R_0, atom_offset(continue_wait_ackrel1_port0, wait_ackrel1_port0)),
|
||||
mac_pad_sio_write_pad_state(PadSioStatus_Disconnected, R_PadState, R_T0),
|
||||
atom_label(skip_port0_from_ackrel1)
|
||||
branch_equal(R_0, R_0, atom_offset(skip_port0_from_ackrel1, port1_start)),
|
||||
|
||||
atom_label(ack_released1_port0)
|
||||
|
||||
/* === Byte 2 (port 0): send 0x00 + RX-ready wait + read response + RX-drain confirmation === */
|
||||
/* Bounded by pad_SIO_WAIT_BUDGET = 4096 iterations. */
|
||||
add_ui(R_T0, R_0, 0x00),
|
||||
store_byte(R_T0, R_PadSioBase, pad_SIO_DATA_OFFSET),
|
||||
add_ui(R_T1, R_0, pad_SIO_WAIT_BUDGET),
|
||||
atom_label(wait_ack2_port0)
|
||||
load_half_u(R_T0, R_PadSioBase, pad_SIO_STAT_OFFSET),
|
||||
nop,
|
||||
and_i(R_T0, R_T0, pad_SIO_STAT_RX_NOT_EMPTY),
|
||||
branch_ne(R_T0, R_0, atom_offset(wait_ack2_port0, ack2_received_port0)),
|
||||
add_ui_self(R_T1, -1),
|
||||
atom_label(continue_wait_ack2_port0)
|
||||
branch_ne(R_T1, R_0, atom_offset(continue_wait_ack2_port0, wait_ack2_port0)),
|
||||
mac_pad_sio_write_pad_state(PadSioStatus_Disconnected, R_PadState, R_T0),
|
||||
atom_label(skip_port0_from_ack2)
|
||||
branch_equal(R_0, R_0, atom_offset(skip_port0_from_ack2, port1_start)),
|
||||
|
||||
atom_label(ack2_received_port0)
|
||||
load_byte_u(R_T0, R_PadSioBase, pad_SIO_DATA_OFFSET),
|
||||
|
||||
/* Bounded by pad_SIO_WAIT_BUDGET = 4096 iterations. */
|
||||
add_ui(R_T1, R_0, pad_SIO_WAIT_BUDGET),
|
||||
atom_label(wait_ackrel2_port0)
|
||||
load_half_u(R_T0, R_PadSioBase, pad_SIO_STAT_OFFSET),
|
||||
nop,
|
||||
and_i(R_T0, R_T0, pad_SIO_STAT_RX_NOT_EMPTY),
|
||||
branch_equal(R_T0, R_0, atom_offset(wait_ackrel2_port0, ack_released2_port0)),
|
||||
add_ui_self(R_T1, -1),
|
||||
atom_label(continue_wait_ackrel2_port0)
|
||||
branch_ne(R_T1, R_0, atom_offset(continue_wait_ackrel2_port0, wait_ackrel2_port0)),
|
||||
mac_pad_sio_write_pad_state(PadSioStatus_Disconnected, R_PadState, R_T0),
|
||||
atom_label(skip_port0_from_ackrel2)
|
||||
branch_equal(R_0, R_0, atom_offset(skip_port0_from_ackrel2, port1_start)),
|
||||
|
||||
atom_label(ack_released2_port0)
|
||||
|
||||
/* === Byte 3 (port 0): send 0x00 + RX-ready wait + read response + RX-drain confirmation === */
|
||||
/* Bounded by pad_SIO_WAIT_BUDGET = 4096 iterations. */
|
||||
add_ui(R_T0, R_0, 0x00),
|
||||
store_byte(R_T0, R_PadSioBase, pad_SIO_DATA_OFFSET),
|
||||
add_ui(R_T1, R_0, pad_SIO_WAIT_BUDGET),
|
||||
atom_label(wait_ack3_port0)
|
||||
load_half_u(R_T0, R_PadSioBase, pad_SIO_STAT_OFFSET),
|
||||
nop,
|
||||
and_i(R_T0, R_T0, pad_SIO_STAT_RX_NOT_EMPTY),
|
||||
branch_ne(R_T0, R_0, atom_offset(wait_ack3_port0, ack3_received_port0)),
|
||||
add_ui_self(R_T1, -1),
|
||||
atom_label(continue_wait_ack3_port0)
|
||||
branch_ne(R_T1, R_0, atom_offset(continue_wait_ack3_port0, wait_ack3_port0)),
|
||||
mac_pad_sio_write_pad_state(PadSioStatus_Disconnected, R_PadState, R_T0),
|
||||
atom_label(skip_port0_from_ack3)
|
||||
branch_equal(R_0, R_0, atom_offset(skip_port0_from_ack3, port1_start)),
|
||||
|
||||
atom_label(ack3_received_port0)
|
||||
load_byte_u(R_T0, R_PadSioBase, pad_SIO_DATA_OFFSET),
|
||||
|
||||
/* Bounded by pad_SIO_WAIT_BUDGET = 4096 iterations. */
|
||||
add_ui(R_T1, R_0, pad_SIO_WAIT_BUDGET),
|
||||
atom_label(wait_ackrel3_port0)
|
||||
load_half_u(R_T0, R_PadSioBase, pad_SIO_STAT_OFFSET),
|
||||
nop,
|
||||
and_i(R_T0, R_T0, pad_SIO_STAT_RX_NOT_EMPTY),
|
||||
branch_equal(R_T0, R_0, atom_offset(wait_ackrel3_port0, ack_released3_port0)),
|
||||
add_ui_self(R_T1, -1),
|
||||
atom_label(continue_wait_ackrel3_port0)
|
||||
branch_ne(R_T1, R_0, atom_offset(continue_wait_ackrel3_port0, wait_ackrel3_port0)),
|
||||
mac_pad_sio_write_pad_state(PadSioStatus_Disconnected, R_PadState, R_T0),
|
||||
atom_label(skip_port0_from_ackrel3)
|
||||
branch_equal(R_0, R_0, atom_offset(skip_port0_from_ackrel3, port1_start)),
|
||||
|
||||
atom_label(ack_released3_port0)
|
||||
|
||||
/* === Byte 4 (FINAL, port 0): send 0x00 + RX-not-empty wait + read final byte === */
|
||||
/* Bounded by pad_SIO_WAIT_BUDGET = 4096 iterations. */
|
||||
add_ui(R_T0, R_0, 0x00),
|
||||
store_byte(R_T0, R_PadSioBase, pad_SIO_DATA_OFFSET),
|
||||
add_ui(R_T1, R_0, pad_SIO_WAIT_BUDGET),
|
||||
atom_label(wait_rx4_port0)
|
||||
load_half_u(R_T0, R_PadSioBase, pad_SIO_STAT_OFFSET),
|
||||
nop,
|
||||
and_i(R_T0, R_T0, pad_SIO_STAT_RX_NOT_EMPTY),
|
||||
branch_ne(R_T0, R_0, atom_offset(wait_rx4_port0, rx4_received_port0)),
|
||||
add_ui_self(R_T1, -1),
|
||||
atom_label(continue_wait_rx4_port0)
|
||||
branch_ne(R_T1, R_0, atom_offset(continue_wait_rx4_port0, wait_rx4_port0)),
|
||||
mac_pad_sio_write_pad_state(PadSioStatus_Disconnected, R_PadState, R_T0),
|
||||
atom_label(skip_port0_from_rx4)
|
||||
branch_equal(R_0, R_0, atom_offset(skip_port0_from_rx4, port1_start)),
|
||||
|
||||
atom_label(rx4_received_port0)
|
||||
load_byte_u(R_T0, R_PadSioBase, pad_SIO_DATA_OFFSET), /* discard final byte */
|
||||
|
||||
/* === RESPONSE DECODE (hardcoded for teaching scope) ===
|
||||
* Per the plan §"Phase 3 task 3.1" + spec §"Architecture":
|
||||
* - Full decode (buttons/axes from response bytes) is follow-up scope.
|
||||
* - Teaching scope: hardcode digital poll response.
|
||||
* status = PadSioStatus_Digital
|
||||
* buttons = 0x0000FFFF (no buttons pressed — placeholder)
|
||||
* axes = 0x80808080 (left_x=0x80, left_y=0x80, right_x=0x80, right_y=0x80)
|
||||
* attempt = 0
|
||||
*/
|
||||
atom_label(decode_port0)
|
||||
mac_pad_sio_write_pad_state(PadSioStatus_Digital, R_PadState, R_T0),
|
||||
|
||||
/* /CS cleanup: raise /CS, clear stale status before exiting port 0. */
|
||||
add_ui(R_T0, R_0, pad_SIO_CTRL_CLEANUP),
|
||||
store_half(R_T0, R_PadSioBase, pad_SIO_CTRL_OFFSET),
|
||||
|
||||
/* ============== PORT 1 SETUP ============== */
|
||||
/* Swap: R_PadCountdown holds sio_base_addr1; R_PadStatus holds state1. */
|
||||
atom_label(port1_start)
|
||||
add_u(R_PadSioBase, R_0, R_PadCountdown), /* sio_base_addr1 → R_PadSioBase */
|
||||
add_u(R_PadState, R_0, R_PadStatus), /* state1 → R_PadState */
|
||||
|
||||
/* ============== PORT 1 TRANSACTION (mirror of port 0) ============== */
|
||||
/* R_PadStatus + R_PadCountdown are no longer reserved (port 1 is the
|
||||
* last transaction); we still use R_T0/R_T1 as scratch to match port 0. */
|
||||
|
||||
/* 1. Cleanup: CTRL = 0x0010 (raise /CS, clear stale status) */
|
||||
add_ui(R_T0, R_0, pad_SIO_CTRL_CLEANUP),
|
||||
store_half(R_T0, R_PadSioBase, pad_SIO_CTRL_OFFSET),
|
||||
/* Bounded by pad_SIO_SETTLE_BEFORE_TX = 1000 iterations. */
|
||||
add_ui(R_T1, R_0, pad_SIO_SETTLE_BEFORE_TX),
|
||||
atom_label(settle_pre_port1)
|
||||
nop,
|
||||
add_ui_self(R_T1, -1),
|
||||
branch_ne(R_T1, R_0, atom_offset(settle_pre_port1, settle_pre_port1)),
|
||||
|
||||
/* 2. Port-select: CTRL = 0x0003 | (1 << 13) (port 1 select) */
|
||||
add_ui(R_T0, R_0, pad_SIO_CTRL_TX_ENABLE),
|
||||
or_i(R_T0, R_T0, pad_SIO_CTRL_DTR_CS),
|
||||
or_i(R_T0, R_T0, 1 << 13), /* port 1 select bit (CTRL bit 13 = port select) */
|
||||
store_half(R_T0, R_PadSioBase, pad_SIO_CTRL_OFFSET),
|
||||
/* Bounded by pad_SIO_SETTLE_AFTER_TX = 2000 iterations. */
|
||||
add_ui(R_T1, R_0, pad_SIO_SETTLE_AFTER_TX),
|
||||
atom_label(settle_post_port1)
|
||||
nop,
|
||||
add_ui_self(R_T1, -1),
|
||||
branch_ne(R_T1, R_0, atom_offset(settle_post_port1, settle_post_port1)),
|
||||
|
||||
/* 3. Address byte (0x01) — send + RX-ready wait + read response + RX-drain confirmation */
|
||||
add_ui(R_T0, R_0, pad_PROTO_ADDR),
|
||||
store_byte(R_T0, R_PadSioBase, pad_SIO_DATA_OFFSET),
|
||||
|
||||
/* Bounded by pad_SIO_WAIT_BUDGET = 4096 iterations. */
|
||||
add_ui(R_T1, R_0, pad_SIO_WAIT_BUDGET),
|
||||
atom_label(wait_ack0_port1)
|
||||
load_half_u(R_T0, R_PadSioBase, pad_SIO_STAT_OFFSET),
|
||||
nop,
|
||||
and_i(R_T0, R_T0, pad_SIO_STAT_RX_NOT_EMPTY),
|
||||
branch_ne(R_T0, R_0, atom_offset(wait_ack0_port1, ack0_received_port1)),
|
||||
add_ui_self(R_T1, -1),
|
||||
atom_label(continue_wait_ack0_port1)
|
||||
branch_ne(R_T1, R_0, atom_offset(continue_wait_ack0_port1, wait_ack0_port1)),
|
||||
mac_pad_sio_write_pad_state(PadSioStatus_Disconnected, R_PadState, R_T0),
|
||||
atom_label(skip_port1_from_ack0)
|
||||
branch_equal(R_0, R_0, atom_offset(skip_port1_from_ack0, end_atom)),
|
||||
|
||||
atom_label(ack0_received_port1)
|
||||
load_byte_u(R_T0, R_PadSioBase, pad_SIO_DATA_OFFSET),
|
||||
|
||||
/* Bounded by pad_SIO_WAIT_BUDGET = 4096 iterations. */
|
||||
add_ui(R_T1, R_0, pad_SIO_WAIT_BUDGET),
|
||||
atom_label(wait_ackrel0_port1)
|
||||
load_half_u(R_T0, R_PadSioBase, pad_SIO_STAT_OFFSET),
|
||||
nop,
|
||||
and_i(R_T0, R_T0, pad_SIO_STAT_RX_NOT_EMPTY),
|
||||
branch_equal(R_T0, R_0, atom_offset(wait_ackrel0_port1, ack_released_port1)),
|
||||
add_ui_self(R_T1, -1),
|
||||
atom_label(continue_wait_ackrel0_port1)
|
||||
branch_ne(R_T1, R_0, atom_offset(continue_wait_ackrel0_port1, wait_ackrel0_port1)),
|
||||
mac_pad_sio_write_pad_state(PadSioStatus_Disconnected, R_PadState, R_T0),
|
||||
atom_label(skip_port1_from_ackrel0)
|
||||
branch_equal(R_0, R_0, atom_offset(skip_port1_from_ackrel0, end_atom)),
|
||||
|
||||
atom_label(ack_released_port1)
|
||||
|
||||
/* === Byte 1 (port 1): send 0x42 (cmd read) + RX-ready wait + read response + RX-drain confirmation === */
|
||||
/* Bounded by pad_SIO_WAIT_BUDGET = 4096 iterations. */
|
||||
add_ui(R_T0, R_0, pad_PROTO_CMD_READ),
|
||||
store_byte(R_T0, R_PadSioBase, pad_SIO_DATA_OFFSET),
|
||||
add_ui(R_T1, R_0, pad_SIO_WAIT_BUDGET),
|
||||
atom_label(wait_ack1_port1)
|
||||
load_half_u(R_T0, R_PadSioBase, pad_SIO_STAT_OFFSET),
|
||||
nop,
|
||||
and_i(R_T0, R_T0, pad_SIO_STAT_RX_NOT_EMPTY),
|
||||
branch_ne(R_T0, R_0, atom_offset(wait_ack1_port1, ack1_received_port1)),
|
||||
add_ui_self(R_T1, -1),
|
||||
atom_label(continue_wait_ack1_port1)
|
||||
branch_ne(R_T1, R_0, atom_offset(continue_wait_ack1_port1, wait_ack1_port1)),
|
||||
mac_pad_sio_write_pad_state(PadSioStatus_Disconnected, R_PadState, R_T0),
|
||||
atom_label(skip_port1_from_ack1)
|
||||
branch_equal(R_0, R_0, atom_offset(skip_port1_from_ack1, end_atom)),
|
||||
|
||||
atom_label(ack1_received_port1)
|
||||
load_byte_u(R_T0, R_PadSioBase, pad_SIO_DATA_OFFSET),
|
||||
|
||||
/* Bounded by pad_SIO_WAIT_BUDGET = 4096 iterations. */
|
||||
add_ui(R_T1, R_0, pad_SIO_WAIT_BUDGET),
|
||||
atom_label(wait_ackrel1_port1)
|
||||
load_half_u(R_T0, R_PadSioBase, pad_SIO_STAT_OFFSET),
|
||||
nop,
|
||||
and_i(R_T0, R_T0, pad_SIO_STAT_RX_NOT_EMPTY),
|
||||
branch_equal(R_T0, R_0, atom_offset(wait_ackrel1_port1, ack_released1_port1)),
|
||||
add_ui_self(R_T1, -1),
|
||||
atom_label(continue_wait_ackrel1_port1)
|
||||
branch_ne(R_T1, R_0, atom_offset(continue_wait_ackrel1_port1, wait_ackrel1_port1)),
|
||||
mac_pad_sio_write_pad_state(PadSioStatus_Disconnected, R_PadState, R_T0),
|
||||
atom_label(skip_port1_from_ackrel1)
|
||||
branch_equal(R_0, R_0, atom_offset(skip_port1_from_ackrel1, end_atom)),
|
||||
|
||||
atom_label(ack_released1_port1)
|
||||
|
||||
/* === Byte 2 (port 1): send 0x00 + RX-ready wait + read response + RX-drain confirmation === */
|
||||
/* Bounded by pad_SIO_WAIT_BUDGET = 4096 iterations. */
|
||||
add_ui(R_T0, R_0, 0x00),
|
||||
store_byte(R_T0, R_PadSioBase, pad_SIO_DATA_OFFSET),
|
||||
add_ui(R_T1, R_0, pad_SIO_WAIT_BUDGET),
|
||||
atom_label(wait_ack2_port1)
|
||||
load_half_u(R_T0, R_PadSioBase, pad_SIO_STAT_OFFSET),
|
||||
nop,
|
||||
and_i(R_T0, R_T0, pad_SIO_STAT_RX_NOT_EMPTY),
|
||||
branch_ne(R_T0, R_0, atom_offset(wait_ack2_port1, ack2_received_port1)),
|
||||
add_ui_self(R_T1, -1),
|
||||
atom_label(continue_wait_ack2_port1)
|
||||
branch_ne(R_T1, R_0, atom_offset(continue_wait_ack2_port1, wait_ack2_port1)),
|
||||
mac_pad_sio_write_pad_state(PadSioStatus_Disconnected, R_PadState, R_T0),
|
||||
atom_label(skip_port1_from_ack2)
|
||||
branch_equal(R_0, R_0, atom_offset(skip_port1_from_ack2, end_atom)),
|
||||
|
||||
atom_label(ack2_received_port1)
|
||||
load_byte_u(R_T0, R_PadSioBase, pad_SIO_DATA_OFFSET),
|
||||
|
||||
/* Bounded by pad_SIO_WAIT_BUDGET = 4096 iterations. */
|
||||
add_ui(R_T1, R_0, pad_SIO_WAIT_BUDGET),
|
||||
atom_label(wait_ackrel2_port1)
|
||||
load_half_u(R_T0, R_PadSioBase, pad_SIO_STAT_OFFSET),
|
||||
nop,
|
||||
and_i(R_T0, R_T0, pad_SIO_STAT_RX_NOT_EMPTY),
|
||||
branch_equal(R_T0, R_0, atom_offset(wait_ackrel2_port1, ack_released2_port1)),
|
||||
add_ui_self(R_T1, -1),
|
||||
atom_label(continue_wait_ackrel2_port1)
|
||||
branch_ne(R_T1, R_0, atom_offset(continue_wait_ackrel2_port1, wait_ackrel2_port1)),
|
||||
mac_pad_sio_write_pad_state(PadSioStatus_Disconnected, R_PadState, R_T0),
|
||||
atom_label(skip_port1_from_ackrel2)
|
||||
branch_equal(R_0, R_0, atom_offset(skip_port1_from_ackrel2, end_atom)),
|
||||
|
||||
atom_label(ack_released2_port1)
|
||||
|
||||
/* === Byte 3 (port 1): send 0x00 + RX-ready wait + read response + RX-drain confirmation === */
|
||||
/* Bounded by pad_SIO_WAIT_BUDGET = 4096 iterations. */
|
||||
add_ui(R_T0, R_0, 0x00),
|
||||
store_byte(R_T0, R_PadSioBase, pad_SIO_DATA_OFFSET),
|
||||
add_ui(R_T1, R_0, pad_SIO_WAIT_BUDGET),
|
||||
atom_label(wait_ack3_port1)
|
||||
load_half_u(R_T0, R_PadSioBase, pad_SIO_STAT_OFFSET),
|
||||
nop,
|
||||
and_i(R_T0, R_T0, pad_SIO_STAT_RX_NOT_EMPTY),
|
||||
branch_ne(R_T0, R_0, atom_offset(wait_ack3_port1, ack3_received_port1)),
|
||||
add_ui_self(R_T1, -1),
|
||||
atom_label(continue_wait_ack3_port1)
|
||||
branch_ne(R_T1, R_0, atom_offset(continue_wait_ack3_port1, wait_ack3_port1)),
|
||||
mac_pad_sio_write_pad_state(PadSioStatus_Disconnected, R_PadState, R_T0),
|
||||
atom_label(skip_port1_from_ack3)
|
||||
branch_equal(R_0, R_0, atom_offset(skip_port1_from_ack3, end_atom)),
|
||||
|
||||
atom_label(ack3_received_port1)
|
||||
load_byte_u(R_T0, R_PadSioBase, pad_SIO_DATA_OFFSET),
|
||||
|
||||
/* Bounded by pad_SIO_WAIT_BUDGET = 4096 iterations. */
|
||||
add_ui(R_T1, R_0, pad_SIO_WAIT_BUDGET),
|
||||
atom_label(wait_ackrel3_port1)
|
||||
load_half_u(R_T0, R_PadSioBase, pad_SIO_STAT_OFFSET),
|
||||
nop,
|
||||
and_i(R_T0, R_T0, pad_SIO_STAT_RX_NOT_EMPTY),
|
||||
branch_equal(R_T0, R_0, atom_offset(wait_ackrel3_port1, ack_released3_port1)),
|
||||
add_ui_self(R_T1, -1),
|
||||
atom_label(continue_wait_ackrel3_port1)
|
||||
branch_ne(R_T1, R_0, atom_offset(continue_wait_ackrel3_port1, wait_ackrel3_port1)),
|
||||
mac_pad_sio_write_pad_state(PadSioStatus_Disconnected, R_PadState, R_T0),
|
||||
atom_label(skip_port1_from_ackrel3)
|
||||
branch_equal(R_0, R_0, atom_offset(skip_port1_from_ackrel3, end_atom)),
|
||||
|
||||
atom_label(ack_released3_port1)
|
||||
|
||||
/* === Byte 4 (FINAL, port 1): send 0x00 + RX-not-empty wait + read final byte === */
|
||||
/* Bounded by pad_SIO_WAIT_BUDGET = 4096 iterations. */
|
||||
add_ui(R_T0, R_0, 0x00),
|
||||
store_byte(R_T0, R_PadSioBase, pad_SIO_DATA_OFFSET),
|
||||
add_ui(R_T1, R_0, pad_SIO_WAIT_BUDGET),
|
||||
atom_label(wait_rx4_port1)
|
||||
load_half_u(R_T0, R_PadSioBase, pad_SIO_STAT_OFFSET),
|
||||
nop,
|
||||
and_i(R_T0, R_T0, pad_SIO_STAT_RX_NOT_EMPTY),
|
||||
branch_ne(R_T0, R_0, atom_offset(wait_rx4_port1, rx4_received_port1)),
|
||||
add_ui_self(R_T1, -1),
|
||||
atom_label(continue_wait_rx4_port1)
|
||||
branch_ne(R_T1, R_0, atom_offset(continue_wait_rx4_port1, wait_rx4_port1)),
|
||||
mac_pad_sio_write_pad_state(PadSioStatus_Disconnected, R_PadState, R_T0),
|
||||
atom_label(skip_port1_from_rx4)
|
||||
branch_equal(R_0, R_0, atom_offset(skip_port1_from_rx4, end_atom)),
|
||||
|
||||
atom_label(rx4_received_port1)
|
||||
load_byte_u(R_T0, R_PadSioBase, pad_SIO_DATA_OFFSET), /* discard final byte */
|
||||
|
||||
/* === RESPONSE DECODE (port 1) === */
|
||||
atom_label(decode_port1)
|
||||
mac_pad_sio_write_pad_state(PadSioStatus_Digital, R_PadState, R_T0),
|
||||
|
||||
/* /CS cleanup: raise /CS, clear stale status before exiting port 1. */
|
||||
add_ui(R_T0, R_0, pad_SIO_CTRL_CLEANUP),
|
||||
store_half(R_T0, R_PadSioBase, pad_SIO_CTRL_OFFSET),
|
||||
|
||||
atom_label(end_atom)
|
||||
mac_yield(),
|
||||
};
|
||||
#endif /* end pad_sio_step wrap */
|
||||
|
||||
/* ----- pad_sio_diag_pin -----
|
||||
* Per-frame diagnostic counter. The caller binds R_DiagPinScratch to
|
||||
* scratch_for_atom_diag_pin for temporary gdb verification.
|
||||
*/
|
||||
#if 0 /* pad_sio_diag_pin — superseded (raw-SIO phase removed) */
|
||||
internal MipsAtom_(pad_sio_diag_pin) atom_info(atom_phase(pad_init)
|
||||
, atom_reads(R_T0, R_T1, R_DiagPinScratch)
|
||||
, atom_writes(R_T0, R_T1, R_DiagPinScratch)
|
||||
) {
|
||||
/* FIX 2026-08-02: explicitly reload R_DiagPinScratch (R_T3 = $t3). Caller-saved
|
||||
* per O32 ABI; the rgcc binding in main() does not survive tape_run. */
|
||||
load_upper_i(R_DiagPinScratch, 0x8001),
|
||||
or_i(R_DiagPinScratch, R_DiagPinScratch, 0xC800),
|
||||
|
||||
/* High half = 0xD1A6; low half increments once per atom invocation. */
|
||||
load_word(R_T1, R_DiagPinScratch, 0),
|
||||
nop,
|
||||
add_ui(R_T1, R_T1, 1),
|
||||
and_i(R_T0, R_T1, 0xFFFF),
|
||||
load_upper_i(R_T1, 0xD1A6),
|
||||
or_i(R_T1, R_T1, 0),
|
||||
or_u(R_T1, R_T1, R_T0),
|
||||
store_word(R_T1, R_DiagPinScratch, 0),
|
||||
mac_yield(),
|
||||
};
|
||||
#endif /* end pad_sio_diag_pin wrap */
|
||||
|
||||
/* ----- pad_sio_diag_byte_exchange -----
|
||||
* Temporary two-byte wire probe: sends 0x01 and 0x42, then stores the
|
||||
* open-bus byte and response ID in scratch_for_atom_diag_pin.
|
||||
*/
|
||||
#if 0 /* pad_sio_diag_byte_exchange — superseded (raw-SIO phase removed) */
|
||||
internal MipsAtom_(pad_sio_diag_byte_exchange) atom_info(atom_phase(pad_init)
|
||||
, atom_reads(R_T0, R_T1, R_T2, R_PadSioBase, R_DiagPinScratch)
|
||||
, atom_writes(R_T0, R_T1, R_T2, R_PadSioBase, R_DiagPinScratch)
|
||||
) {
|
||||
/* FIX 2026-08-02: explicitly reload R_DiagPinScratch (R_T3 = $t3). Caller-saved
|
||||
* per O32 ABI; the rgcc binding in main() does not survive tape_run. */
|
||||
load_upper_i(R_DiagPinScratch, 0x8001),
|
||||
or_i(R_DiagPinScratch, R_DiagPinScratch, 0xC800),
|
||||
|
||||
/* FIX 2026-08-02: explicitly load KSEG1 base into R_PadSioBase (R_T6) at the
|
||||
* top. The rgcc() binding in main() does NOT survive the tape_run call
|
||||
* because R_T6 is caller-saved per the O32 ABI. */
|
||||
load_upper_i(R_PadSioBase, pad_IO_KSEG1_BASE >> 16),
|
||||
or_i(R_PadSioBase, R_PadSioBase, pad_IO_KSEG1_BASE & 0xFFFF),
|
||||
|
||||
add_ui(R_T0, R_0, pad_SIO_CTRL_CLEANUP),
|
||||
store_half(R_T0, R_PadSioBase, pad_SIO_CTRL_OFFSET),
|
||||
add_ui(R_T0, R_0, pad_SIO_CTRL_TX_ENABLE),
|
||||
or_i(R_T0, R_T0, pad_SIO_CTRL_DTR_CS),
|
||||
store_half(R_T0, R_PadSioBase, pad_SIO_CTRL_OFFSET),
|
||||
|
||||
add_ui(R_T0, R_0, pad_PROTO_ADDR),
|
||||
store_byte(R_T0, R_PadSioBase, pad_SIO_DATA_OFFSET),
|
||||
add_ui(R_T1, R_0, pad_SIO_WAIT_BUDGET),
|
||||
atom_label(diag_wait_ack0)
|
||||
load_half_u(R_T0, R_PadSioBase, pad_SIO_STAT_OFFSET),
|
||||
nop,
|
||||
and_i(R_T0, R_T0, pad_SIO_STAT_RX_NOT_EMPTY),
|
||||
branch_ne(R_T0, R_0, atom_offset(diag_wait_ack0, diag_ack0_done)),
|
||||
add_ui_self(R_T1, -1),
|
||||
branch_ne(R_T1, R_0, atom_offset(diag_wait_ack0, diag_wait_ack0)),
|
||||
add_ui(R_T0, R_0, 0xDEADAC01),
|
||||
store_word(R_T0, R_DiagPinScratch, 0),
|
||||
branch_equal(R_0, R_0, atom_offset(diag_timeout_ack0, diag_timeout)),
|
||||
atom_label(diag_ack0_done)
|
||||
load_byte_u(R_T2, R_PadSioBase, pad_SIO_DATA_OFFSET),
|
||||
add_ui(R_T0, R_0, pad_PROTO_CMD_READ),
|
||||
store_byte(R_T0, R_PadSioBase, pad_SIO_DATA_OFFSET),
|
||||
add_ui(R_T1, R_0, pad_SIO_WAIT_BUDGET),
|
||||
atom_label(diag_wait_ack1)
|
||||
load_half_u(R_T0, R_PadSioBase, pad_SIO_STAT_OFFSET),
|
||||
nop,
|
||||
and_i(R_T0, R_T0, pad_SIO_STAT_RX_NOT_EMPTY),
|
||||
branch_ne(R_T0, R_0, atom_offset(diag_wait_ack1, diag_ack1_done)),
|
||||
add_ui_self(R_T1, -1),
|
||||
branch_ne(R_T1, R_0, atom_offset(diag_wait_ack1, diag_wait_ack1)),
|
||||
add_ui(R_T0, R_0, 0xDEADAC02),
|
||||
store_word(R_T0, R_DiagPinScratch, 0),
|
||||
branch_equal(R_0, R_0, atom_offset(diag_timeout_ack1, diag_timeout)),
|
||||
atom_label(diag_ack1_done)
|
||||
load_byte_u(R_T0, R_PadSioBase, pad_SIO_DATA_OFFSET),
|
||||
nop,
|
||||
shift_lleft(R_T0, R_T0, 8),
|
||||
or_u(R_T2, R_T2, R_T0),
|
||||
store_word(R_T2, R_DiagPinScratch, 0),
|
||||
atom_label(diag_success)
|
||||
branch_equal(R_0, R_0, atom_offset(diag_success, diag_done)),
|
||||
nop,
|
||||
atom_label(diag_timeout_ack0)
|
||||
add_ui(R_T0, R_0, 0xDEADAC01),
|
||||
store_word(R_T0, R_DiagPinScratch, 0),
|
||||
atom_label(diag_timeout_ack1)
|
||||
add_ui(R_T0, R_0, 0xDEADAC02),
|
||||
store_word(R_T0, R_DiagPinScratch, 0),
|
||||
atom_label(diag_timeout)
|
||||
add_ui(R_T0, R_0, 0xDEADACFF),
|
||||
store_word(R_T0, R_DiagPinScratch, 0),
|
||||
atom_label(diag_done)
|
||||
add_ui(R_T0, R_0, pad_SIO_CTRL_CLEANUP),
|
||||
store_half(R_T0, R_PadSioBase, pad_SIO_CTRL_OFFSET),
|
||||
mac_yield(),
|
||||
};
|
||||
#endif /* end pad_sio_diag_byte_exchange wrap */
|
||||
Reference in New Issue
Block a user