From 9eb696ece804f6b7858d8ed2e48fa6b184a84293 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Sun, 2 Aug 2026 21:58:57 -0400 Subject: [PATCH] drafting --- code/duffle/lottes_tape.h | 14 +--- code/duffle/pad.h | 105 ++++++++++++++++++++++++++ code/hello_joypad/hello_joypad.h | 26 +++++++ code/hello_joypad/hello_joypad.tape.c | 45 +++++++++-- 4 files changed, 175 insertions(+), 15 deletions(-) diff --git a/code/duffle/lottes_tape.h b/code/duffle/lottes_tape.h index 7f0c0ce..363ed82 100644 --- a/code/duffle/lottes_tape.h +++ b/code/duffle/lottes_tape.h @@ -81,18 +81,12 @@ atom_dbg_skip MipsAtom_(tape_exit) { jump_reg(rret_addr), nop }; //TODO(Ed): Do we backup R_S0-7 here? Have it in a heavier tape run as a opt-in? Same with V0-1 and A0-3? /* Generalized Tape Engine Runner */ FI_ void tape_run(Slice_MipsCode tape) { register U4* tape_ptr rgcc(R_TapePtr) = u4_r(tape.ptr); asm volatile( - // Removed, not needed - /*add_ui( R_SP, R_SP, -MipsStackAlignment)*/ /* Allocate stack space */ - /*, store_word( R_RA, R_SP, 0)*/ /* Safely backup $ra to the stack */ asm_words( - load_word( R_AtomJmp, R_TapePtr, 0) /* Bootstrap the first jump */ - , add_ui_self(R_TapePtr, S_(MipsCode)) /* Advance tape */ - , call_reg( R_AtomJmp) /* jalr $t9 */ - , nop /* Branch delay slot */ + load_word( R_AtomJmp, R_TapePtr, 0) /* Bootstrap the first jump */ + , add_ui_self(R_TapePtr, S_(MipsCode)) /* Advance tape */ + , call_reg( R_AtomJmp) /* jalr $t9 */ + , nop /* Branch delay slot */ ) - // Removed, not needed - /*, load_word( R_RA, R_SP, 0)* /* Restore $ra from stack */ - /*, add_ui_self(R_SP, MipsStackAlignment)*/ /* Deallocate stack space */ asm_rpins, r_use(tape_ptr) asm_clobber: rlit(R_AT) diff --git a/code/duffle/pad.h b/code/duffle/pad.h index c1d9da7..a121f60 100644 --- a/code/duffle/pad.h +++ b/code/duffle/pad.h @@ -36,3 +36,108 @@ enum { void pad_init(U4 mode) asm("PadInit"); U4 pad_read(U4 id) asm("PadRead"); +/* ============================================================ + * SIO0 raw controller polling surface (Phase 1 scaffolding) + * Source of truth: docs/psx-spx/docs/serialinterfacessio.md + * docs/psx-spx/docs/controllersandmemorycards.md + * https://github.com/Lameguy64/PSn00bSDK (spi.c) + * ============================================================ */ + +enum { + /* SIO0 register offsets relative to IOBASE 0xBF800000 */ + pad_SIO_DATA_OFFSET = 0x1040, /* 8-bit data (TX write / RX read) */ + pad_SIO_STAT_OFFSET = 0x1044, /* 16-bit status */ + pad_SIO_MODE_OFFSET = 0x1048, /* 16-bit mode */ + pad_SIO_CTRL_OFFSET = 0x104A, /* 16-bit control */ + pad_SIO_BAUD_OFFSET = 0x104E, /* 16-bit baud */ + + /* SIO_CTRL bit flags */ + pad_SIO_CTRL_RESET = 0x0040, /* reset most SIO registers */ + pad_SIO_CTRL_TX_ENABLE = 0x0001, /* TX enable */ + pad_SIO_CTRL_DTR_CS = 0x0002, /* DTR drives /CS on controller */ + pad_SIO_CTRL_ACK = 0x0010, /* acknowledge / clear status */ + pad_SIO_CTRL_DSR_IRQ = 0x1000, /* enable /ACK IRQ (unused; polling) */ + + /* SIO_STAT bit flags */ + pad_SIO_STAT_TX_READY = 0x0001, + pad_SIO_STAT_RX_NOT_EMPTY = 0x0002, + pad_SIO_STAT_TX_IDLE = 0x0004, + pad_SIO_STAT_DSR_ACK = 0x0080, + + /* SIO init values (per docs/psx-spx/docs/serialinterfacessio.md) */ + pad_SIO_MODE_INIT = 0x000D, /* MUL1, 8-bit, no parity, idle-high */ + pad_SIO_BAUD_INIT = 0x0088, /* ~250 kHz standard rate */ + pad_SIO_CTRL_CLEANUP = 0x0010, /* raise /CS + clear stale status */ + + /* Controller protocol bytes (per docs/psx-spx/docs/controllersandmemorycards.md) */ + pad_PROTO_ADDR = 0x01, + pad_PROTO_CMD_READ = 0x42, + pad_PROTO_CMD_ENTERCFG = 0x43, + pad_PROTO_CMD_SETLED = 0x44, + pad_PROTO_PREFIX = 0x5A, + pad_PROTO_PREFIX_QUIRK = 0x00, /* DualShock: Analog-button swap after cfg */ + + /* Controller response ID nibble (low 4 bits of ID byte) */ + pad_PROTO_ID_DIGITAL = 0x41, + pad_PROTO_ID_ANALOG_STK = 0x53, + pad_PROTO_ID_ANALOG = 0x73, + pad_PROTO_ID_CONFIG = 0xF3, + + /* Per-state constants */ + pad_SIO_SETTLE_BEFORE_TX = 1000, /* iterations after CTRL=0x0010 */ + pad_SIO_SETTLE_AFTER_TX = 2000, /* iterations after CTRL=port_select */ + pad_SIO_WAIT_BUDGET = 4096, /* max iterations per /ACK or RX wait */ + pad_SIO_CFG_MAX_ATTEMPTS = 3, /* DualShock config retries before reject */ +}; + +/* Pad status enum (per-port outcome) */ +typedef Enum_(U4, PadSioStatus) { + PadSioStatus_Disconnected, + PadSioStatus_Digital, + PadSioStatus_AnalogStick, + PadSioStatus_Analog, + PadSioStatus_ConfigRejected, + PadSioStatus_Invalid, + PadSioStatus_TxTimeout, + PadSioStatus_RxTimeout, + PadSioStatus_AckTimeout, +}; + +/* PadState — per-port runtime state */ +typedef Struct_(PadState) { + PadSioStatus status; + U4 buttons; + U1 left_x; + U1 left_y; + U1 right_x; + U1 right_y; + U4 attempt; +}; + +/* PadSioInit — boot-time C-side pointer table passed into the tape */ +typedef Struct_(PadSioInit) { + U4 sio_base_addr[2]; + PadState* pad_state_ptr[2]; +}; + +/* Register aliases for the new atoms (post-2026-07-10 ABI) */ +#ifndef atom_reg +#define atom_reg /* atom_reg: opt the preceding enum entry into the DWARF registry */ +#endif + +enum { + R_PadSioBase = R_T6 atom_reg, /* caller-pinned IO_BASE_ADDR */ + R_PadState = R_T7 atom_reg, /* caller-pinned PadState* */ + R_PadStatus = R_T4 atom_reg, /* scratch for status reads */ + R_PadCountdown = R_T5 atom_reg, /* scratch for countdown budget */ +#define R_PadSioBase_Code R_T6_Code +#define R_PadState_Code R_T7_Code +#define R_PadStatus_Code R_T4_Code +#define R_PadCountdown_Code R_T5_Code +}; + +/* Address of SIO0 block in KSEG1 (the PS1-side uncached mirror) */ +enum { + pad_IO_KSEG1_BASE = 0xBF800000, +}; + diff --git a/code/hello_joypad/hello_joypad.h b/code/hello_joypad/hello_joypad.h index 72ae480..4189084 100644 --- a/code/hello_joypad/hello_joypad.h +++ b/code/hello_joypad/hello_joypad.h @@ -21,3 +21,29 @@ enum { }; #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); diff --git a/code/hello_joypad/hello_joypad.tape.c b/code/hello_joypad/hello_joypad.tape.c index cde981d..22b1c8a 100644 --- a/code/hello_joypad/hello_joypad.tape.c +++ b/code/hello_joypad/hello_joypad.tape.c @@ -199,7 +199,7 @@ internal MipsAtom_(gp_screen_init) atom_info(atom_phase(screen_init), atom_reads }; enum { - R_PadState = R_T4 atom_reg atom_type(U4), + R_PadInState = R_T4 atom_reg atom_type(U4), R_PadSignal = R_T0 atom_reg atom_type(U4), R_CubeRot = R_T1 atom_reg atom_type(V3_S2*), R_FloorRot = R_T2 atom_reg atom_type(V3_S2*), @@ -210,15 +210,15 @@ typedef Struct_(Binds_PadInputDemo) { V3_S2* floor_rot; }; internal MipsAtom_(pad_input_demo) atom_info(atom_bind(Binds_PadInputDemo) -, atom_reads(R_PadState, R_CubeRot, R_FloorRot) +, atom_reads(R_PadInState, R_CubeRot, R_FloorRot) , atom_writes(R_CubeRot, R_FloorRot) ) { - load_word(R_PadState, R_TapePtr, O_(Binds_PadInputDemo,pad_state)), + load_word(R_PadInState, R_TapePtr, O_(Binds_PadInputDemo,pad_state)), load_word(R_CubeRot, R_TapePtr, O_(Binds_PadInputDemo,cube_rot)), load_word(R_FloorRot, R_TapePtr, O_(Binds_PadInputDemo,floor_rot)), add_ui_self( R_TapePtr, S_(Binds_PadInputDemo)), - and_i(R_PadSignal, R_PadState, pad0_(Pad_Left)), branch_le_zero(R_PadSignal, atom_offset(pad_left, exit_pad_left)), + and_i(R_PadSignal, R_PadInState, pad0_(Pad_Left)), branch_le_zero(R_PadSignal, atom_offset(pad_left, exit_pad_left)), load_half( R_T5, R_CubeRot, O_(V3_S2,y)), // BD-Slot occupied load_half( R_T6, R_FloorRot, O_(V3_S2,y)), add_si( R_T5, R_T5, 30), @@ -227,7 +227,7 @@ internal MipsAtom_(pad_input_demo) atom_info(atom_bind(Binds_PadInputDemo) store_half(R_T6, R_FloorRot, O_(V3_S2,y)), atom_label(exit_pad_left) - and_i(R_PadSignal, R_PadState, pad0_(Pad_Right)), branch_le_zero(R_PadSignal, atom_offset(pad_right, exit_pad_right)), + and_i(R_PadSignal, R_PadInState, pad0_(Pad_Right)), branch_le_zero(R_PadSignal, atom_offset(pad_right, exit_pad_right)), load_half( R_T5, R_CubeRot, O_(V3_S2,y)), // BD-Slot occupied load_half( R_T6, R_FloorRot, O_(V3_S2,y)), add_si( R_T5, R_T5, -30), @@ -376,4 +376,39 @@ internal MipsAtom_(sync_primitive_arena) atom_info(atom_bind(Binds_SyncPrimitive mac_yield() }; +/* ----- 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.) + */ +internal MipsAtom_(pad_sio_init) atom_info(atom_phase(pad_init) +, atom_reads(R_T5, R_PadSioBase) +, atom_writes(R_T5) +) { + mac_yield(), +}; + +/* ----- 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]. (Phase 3 fills the body.) + */ +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) +) { + 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. (Phase 4 fills the body.) + */ +internal MipsAtom_(pad_apply_input) atom_info(atom_bind(Binds_PadApplyInput) +, atom_reads(R_T0, R_T1, R_T2, R_T5) +, atom_writes(R_T1, R_T2) +) { + mac_yield(), +}; + #pragma endregion Baked Atoms