mirror of
https://github.com/Ed94/pikuma_ps1.git
synced 2026-08-12 10:38:15 +00:00
Compare commits
2
Commits
9eb696ece8
...
f17fa9165e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f17fa9165e | ||
|
|
8282f8e902 |
@@ -61,7 +61,7 @@ WORD_COUNT(mac_gte_load_tri_verts, 18)
|
|||||||
, load_word( R_AT, R_T1, O_(PolyTag,code)) /* AT = old_ot_head */ \
|
, load_word( R_AT, R_T1, O_(PolyTag,code)) /* AT = old_ot_head */ \
|
||||||
, load_upper_i(R_V0, (S_(Poly_F3)/S_(U4) - S_(PolyTag)/S_(U4)) << PolyTag_len_bits) /* V0 = (5 - 1) << 24 = 4 << 24 */ \
|
, load_upper_i(R_V0, (S_(Poly_F3)/S_(U4) - S_(PolyTag)/S_(U4)) << PolyTag_len_bits) /* V0 = (5 - 1) << 24 = 4 << 24 */ \
|
||||||
, mask_upper( R_AT, R_AT, S_(PolyTag_len_bits)) /* Strip upper 8 bits (length from prev cell) → keep only low 24 */ \
|
, mask_upper( R_AT, R_AT, S_(PolyTag_len_bits)) /* Strip upper 8 bits (length from prev cell) → keep only low 24 */ \
|
||||||
, or( R_AT, R_AT, R_V0) /* Merge length */ \
|
, or_u( R_AT, R_AT, R_V0) /* Merge length */ \
|
||||||
, store_word( R_AT, R_PrimCursor, O_(PolyTag,code)) /* prim->tag = packed(prim_length, old_addr) */ \
|
, store_word( R_AT, R_PrimCursor, O_(PolyTag,code)) /* prim->tag = packed(prim_length, old_addr) */ \
|
||||||
, shift_lleft( R_AT, R_PrimCursor, S_(PolyTag_len_bits)) /* AT = (prim_length << 24) | old_addr */ \
|
, shift_lleft( R_AT, R_PrimCursor, S_(PolyTag_len_bits)) /* AT = (prim_length << 24) | old_addr */ \
|
||||||
, shift_lright(R_AT, R_AT, S_(PolyTag_len_bits)) \
|
, shift_lright(R_AT, R_AT, S_(PolyTag_len_bits)) \
|
||||||
@@ -76,7 +76,7 @@ WORD_COUNT(mac_insert_ot_tag_f3, 11)
|
|||||||
, load_word( R_AT, R_T1, O_(PolyTag,code)) /* AT = old_ot_head */ \
|
, load_word( R_AT, R_T1, O_(PolyTag,code)) /* AT = old_ot_head */ \
|
||||||
, load_upper_i(R_V0, (S_(Poly_G4)/S_(U4) - S_(PolyTag)/S_(U4)) << PolyTag_len_bits) /* V0 = (9 - 1) << 24 = 8 << 24 */ \
|
, load_upper_i(R_V0, (S_(Poly_G4)/S_(U4) - S_(PolyTag)/S_(U4)) << PolyTag_len_bits) /* V0 = (9 - 1) << 24 = 8 << 24 */ \
|
||||||
, mask_upper( R_AT, R_AT, S_(PolyTag_len_bits)) /* Strip upper 8 bits (length from prev cell) → keep only low 24 */ \
|
, mask_upper( R_AT, R_AT, S_(PolyTag_len_bits)) /* Strip upper 8 bits (length from prev cell) → keep only low 24 */ \
|
||||||
, or( R_AT, R_AT, R_V0) /* Merge length */ \
|
, or_u( R_AT, R_AT, R_V0) /* Merge length */ \
|
||||||
, store_word( R_AT, R_PrimCursor, O_(PolyTag,code)) /* prim->tag = packed(prim_length, old_addr) */ \
|
, store_word( R_AT, R_PrimCursor, O_(PolyTag,code)) /* prim->tag = packed(prim_length, old_addr) */ \
|
||||||
, shift_lleft( R_AT, R_PrimCursor, S_(PolyTag_len_bits)) /* AT = (prim_length << 24) | old_addr */ \
|
, shift_lleft( R_AT, R_PrimCursor, S_(PolyTag_len_bits)) /* AT = (prim_length << 24) | old_addr */ \
|
||||||
, shift_lright(R_AT, R_AT, S_(PolyTag_len_bits)) \
|
, shift_lright(R_AT, R_AT, S_(PolyTag_len_bits)) \
|
||||||
|
|||||||
+27
-86
@@ -10,10 +10,10 @@
|
|||||||
# include "gen/duffle.offsets.h"
|
# include "gen/duffle.offsets.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef U4 const MipsCode;
|
typedef U4 const MipsCode; // Underlying type to mips asm words.
|
||||||
typedef Slice_(MipsCode);
|
typedef Slice_(MipsCode);
|
||||||
typedef Slice_MipsCode MipsAtom;
|
|
||||||
|
|
||||||
|
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) =
|
#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).
|
// Used for components with no args (e.g., ac_load_tri_indices) or identifier-args (hardcoded register names).
|
||||||
@@ -23,9 +23,9 @@ typedef Slice_MipsCode MipsAtom;
|
|||||||
#define MipsAtomComp_(sym) MipsCode sym [] align_(4) =
|
#define MipsAtomComp_(sym) MipsCode sym [] align_(4) =
|
||||||
|
|
||||||
// Used for components with value-args (e.g., ac_format_f3_color).
|
// Used for components with value-args (e.g., ac_format_f3_color).
|
||||||
// FI_ MipsAtom ac_X(args) MipsAtomComp_Proc_(ac_X, { body })
|
// FI_ Slice_MipsCode ac_X(args) MipsAtomComp_Proc_(ac_X, { body })
|
||||||
// expands to:
|
// expands to:
|
||||||
// FI_ MipsAtom ac_X(args) { MipsCode ac_X[] align_(4) = { body }; return slice_from_array(MipsCode, ac_X); }
|
// 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); }
|
#define MipsAtomComp_Proc_(sym, ...) { MipsCode sym [] align_(4) = __VA_ARGS__; return slice_from_array(MipsCode, sym); }
|
||||||
|
|
||||||
// Auto-generated component macros (<module>/gen/<dir>/<dir>.macs.h) are included manually by the unity build.
|
// Auto-generated component macros (<module>/gen/<dir>/<dir>.macs.h) are included manually by the unity build.
|
||||||
@@ -59,12 +59,12 @@ enum {
|
|||||||
R_TScratch6 = R_T6,
|
R_TScratch6 = R_T6,
|
||||||
R_TScratch7 = R_T7,
|
R_TScratch7 = R_T7,
|
||||||
R_TScratch8 = R_T8,
|
R_TScratch8 = R_T8,
|
||||||
|
R_TScratch10 = R_V0,
|
||||||
|
R_TScratch11 = R_V1,
|
||||||
// Note(Ed): We can technically clobber these, but don't unless we hit a bottleneck.
|
// Note(Ed): We can technically clobber these, but don't unless we hit a bottleneck.
|
||||||
// R_TScratch10 = R_V0,
|
// R_TScratch12 = R_A0,
|
||||||
// R_TScratch10 = R_V1,
|
// R_TScratch13 = R_A1,
|
||||||
// R_TScratch11 = R_A0,
|
// R_TScratch14 = R_A3,
|
||||||
// R_TScratch12 = R_A1,
|
|
||||||
// R_TScratch13 = R_A3,
|
|
||||||
// TODO(Ed): Review S0-S7, they are technically avaialble, we just have to snapshot them at the ABI boundary.
|
// TODO(Ed): Review S0-S7, they are technically avaialble, we just have to snapshot them at the ABI boundary.
|
||||||
// TODO(Ed): This is technically a waste of cycles for most work? so maybe only do this for expensive atoms on-demand or atom phases.
|
// TODO(Ed): This is technically a waste of cycles for most work? so maybe only do this for expensive atoms on-demand or atom phases.
|
||||||
// TODO(Ed): Sort out the other available registers... (Not sure how much is left avail)
|
// TODO(Ed): Sort out the other available registers... (Not sure how much is left avail)
|
||||||
@@ -74,25 +74,27 @@ enum {
|
|||||||
/* ---------------------------------------------------------------------------
|
/* ---------------------------------------------------------------------------
|
||||||
* TAPE DRIVE ABI & REGISTER ALIASES (the enum moved earlier; see below)
|
* TAPE DRIVE ABI & REGISTER ALIASES (the enum moved earlier; see below)
|
||||||
* ---------------------------------------------------------------------------*/
|
* ---------------------------------------------------------------------------*/
|
||||||
|
typedef Slice_(MipsAtom); typedef Slice_MipsAtom Tape;
|
||||||
|
|
||||||
/* The 'Exit' Atom */
|
/* The 'Exit' Atom */
|
||||||
atom_dbg_skip MipsAtom_(tape_exit) { jump_reg(rret_addr), nop };
|
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?
|
//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 */
|
/* Generalized Tape Engine Runner */
|
||||||
FI_ void tape_run(Slice_MipsCode tape) { register U4* tape_ptr rgcc(R_TapePtr) = u4_r(tape.ptr); asm volatile(
|
FI_ void tape_run(Tape tape) { register U4* tape_ptr rgcc(R_TapePtr) = u4_r(tape.ptr); asm volatile(
|
||||||
asm_words(
|
asm_words(
|
||||||
load_word( R_AtomJmp, R_TapePtr, 0) /* Bootstrap the first jump */
|
load_word( R_AtomJmp, R_TapePtr, 0) /* Bootstrap the first jump */
|
||||||
, add_ui_self(R_TapePtr, S_(MipsCode)) /* Advance tape */
|
, add_ui_self(R_TapePtr, S_(MipsAtom)) /* Advance tape */
|
||||||
, call_reg( R_AtomJmp) /* jalr $t9 */
|
, call_reg( R_AtomJmp) /* jalr $t9 */
|
||||||
, nop /* Branch delay slot */
|
, nop /* Branch delay slot */
|
||||||
)
|
)
|
||||||
asm_rpins, r_use(tape_ptr)
|
asm_rpins, r_use(tape_ptr)
|
||||||
asm_clobber:
|
asm_clobber:
|
||||||
rlit(R_AT)
|
rlit(R_AT),
|
||||||
, rlit(R_T0), rlit(R_T1), rlit(R_T2), rlit(R_T3), rlit(R_T4)
|
rlit(R_V0), rlit(R_V1),
|
||||||
, rlit(R_T5), rlit(R_T6), rlit(R_T7), rlit(R_T8)
|
rlit(R_T0), rlit(R_T1), rlit(R_T2), rlit(R_T3), rlit(R_T4),
|
||||||
, clb_mem_drain
|
rlit(R_T5), rlit(R_T6), rlit(R_T7), rlit(R_T8),
|
||||||
|
clb_mem_drain
|
||||||
); }
|
); }
|
||||||
|
|
||||||
typedef Relative_(FArena) Struct_(TapeBuilder) { U4 ptr; U4 capacity; U4 used; };
|
typedef Relative_(FArena) Struct_(TapeBuilder) { U4 ptr; U4 capacity; U4 used; };
|
||||||
@@ -100,12 +102,13 @@ FI_ void tb_init(TapeBuilder* tb, FArena* arena) { tb->ptr = arena->start
|
|||||||
FI_ TapeBuilder tb_make_old( FArena* arena) { return (TapeBuilder){ arena->start, 0 }; }
|
FI_ TapeBuilder tb_make_old( FArena* arena) { return (TapeBuilder){ arena->start, 0 }; }
|
||||||
FI_ TapeBuilder tb_make(Slice mem) { return (TapeBuilder){ mem.ptr, mem.len, 0 }; }
|
FI_ TapeBuilder tb_make(Slice mem) { return (TapeBuilder){ mem.ptr, mem.len, 0 }; }
|
||||||
|
|
||||||
#define tb_emit_(tb, atom) tb_emit(tb, atom)
|
|
||||||
FI_ void tb_emit(TapeBuilder* tb, MipsCode* atom) { u4_r(tb->ptr)[tb->used] = u4_(atom); ++ tb->used; }
|
FI_ void tb_emit(TapeBuilder* tb, MipsCode* atom) { u4_r(tb->ptr)[tb->used] = u4_(atom); ++ tb->used; }
|
||||||
FI_ void tb_data(TapeBuilder* tb, U4 data) { u4_r(tb->ptr)[tb->used] = u4_(data); ++ tb->used; }
|
FI_ void tb_data(TapeBuilder* tb, U4 data) { u4_r(tb->ptr)[tb->used] = u4_(data); ++ tb->used; }
|
||||||
|
#define tb_emit_(atom) tb_emit(& tb, atom)
|
||||||
|
#define tb_data_(field, data) tb_data(& tb, u4_(data))
|
||||||
|
|
||||||
FI_ Slice_MipsCode tb_end (TapeBuilder* tb) { tb_emit(tb,tape_exit); return (Slice_MipsCode){ C_(U4*,tb->ptr), tb->used }; }
|
FI_ Tape tb_end (TapeBuilder* tb) { tb_emit(tb,tape_exit); return (Tape){ C_(U4*,tb->ptr), tb->used }; }
|
||||||
FI_ Slice_MipsCode tb_slice(TapeBuilder tb) { return (Slice_MipsCode){ C_(U4*,tb.ptr), tb.used }; }
|
FI_ Tape tb_slice(TapeBuilder tb) { return (Tape){ C_(U4*,tb.ptr), tb.used }; }
|
||||||
#define tb_scope(tb) for(U4 tbs_once=0;tbs_once==0;++tbs_once,tb_emit(tb,tape_exit))
|
#define tb_scope(tb) for(U4 tbs_once=0;tbs_once==0;++tbs_once,tb_emit(tb,tape_exit))
|
||||||
|
|
||||||
FI_ void tb_scope_run_end(TapeBuilder* tb) { tb_emit(tb,tape_exit); tape_run(tb_slice(tb[0])); }
|
FI_ void tb_scope_run_end(TapeBuilder* tb) { tb_emit(tb,tape_exit); tape_run(tb_slice(tb[0])); }
|
||||||
@@ -159,7 +162,7 @@ MipsAtomComp_(ac_insert_ot_tag_f3) {
|
|||||||
load_word( R_AT, R_T1, O_(PolyTag,code)), // AT = old_ot_head
|
load_word( R_AT, R_T1, O_(PolyTag,code)), // AT = old_ot_head
|
||||||
load_upper_i(R_V0, (S_(Poly_F3)/S_(U4) - S_(PolyTag)/S_(U4)) << PolyTag_len_bits), // V0 = (5 - 1) << 24 = 4 << 24
|
load_upper_i(R_V0, (S_(Poly_F3)/S_(U4) - S_(PolyTag)/S_(U4)) << PolyTag_len_bits), // V0 = (5 - 1) << 24 = 4 << 24
|
||||||
mask_upper( R_AT, R_AT, S_(PolyTag_len_bits)), // Strip upper 8 bits (length from prev cell) → keep only low 24
|
mask_upper( R_AT, R_AT, S_(PolyTag_len_bits)), // Strip upper 8 bits (length from prev cell) → keep only low 24
|
||||||
or( R_AT, R_AT, R_V0), // Merge length
|
or_u( R_AT, R_AT, R_V0), // Merge length
|
||||||
store_word( R_AT, R_PrimCursor, O_(PolyTag,code)), // prim->tag = packed(prim_length, old_addr)
|
store_word( R_AT, R_PrimCursor, O_(PolyTag,code)), // prim->tag = packed(prim_length, old_addr)
|
||||||
shift_lleft( R_AT, R_PrimCursor, S_(PolyTag_len_bits)), // AT = (prim_length << 24) | old_addr
|
shift_lleft( R_AT, R_PrimCursor, S_(PolyTag_len_bits)), // AT = (prim_length << 24) | old_addr
|
||||||
shift_lright(R_AT, R_AT, S_(PolyTag_len_bits)),
|
shift_lright(R_AT, R_AT, S_(PolyTag_len_bits)),
|
||||||
@@ -174,7 +177,7 @@ MipsAtomComp_(ac_insert_ot_tag_g4) {
|
|||||||
load_word( R_AT, R_T1, O_(PolyTag,code)), // AT = old_ot_head
|
load_word( R_AT, R_T1, O_(PolyTag,code)), // AT = old_ot_head
|
||||||
load_upper_i(R_V0, (S_(Poly_G4)/S_(U4) - S_(PolyTag)/S_(U4)) << PolyTag_len_bits), // V0 = (9 - 1) << 24 = 8 << 24
|
load_upper_i(R_V0, (S_(Poly_G4)/S_(U4) - S_(PolyTag)/S_(U4)) << PolyTag_len_bits), // V0 = (9 - 1) << 24 = 8 << 24
|
||||||
mask_upper( R_AT, R_AT, S_(PolyTag_len_bits)), // Strip upper 8 bits (length from prev cell) → keep only low 24
|
mask_upper( R_AT, R_AT, S_(PolyTag_len_bits)), // Strip upper 8 bits (length from prev cell) → keep only low 24
|
||||||
or( R_AT, R_AT, R_V0), // Merge length
|
or_u( R_AT, R_AT, R_V0), // Merge length
|
||||||
store_word( R_AT, R_PrimCursor, O_(PolyTag,code)), // prim->tag = packed(prim_length, old_addr)
|
store_word( R_AT, R_PrimCursor, O_(PolyTag,code)), // prim->tag = packed(prim_length, old_addr)
|
||||||
shift_lleft( R_AT, R_PrimCursor, S_(PolyTag_len_bits)), // AT = (prim_length << 24) | old_addr
|
shift_lleft( R_AT, R_PrimCursor, S_(PolyTag_len_bits)), // AT = (prim_length << 24) | old_addr
|
||||||
shift_lright(R_AT, R_AT, S_(PolyTag_len_bits)),
|
shift_lright(R_AT, R_AT, S_(PolyTag_len_bits)),
|
||||||
@@ -183,7 +186,7 @@ MipsAtomComp_(ac_insert_ot_tag_g4) {
|
|||||||
|
|
||||||
/* Words: 3; Emits one (cmd|color) word to R_PrimCursor at the given
|
/* Words: 3; Emits one (cmd|color) word to R_PrimCursor at the given
|
||||||
* byte offset. Internal helper used by the *_format_*_color macros. */
|
* byte offset. Internal helper used by the *_format_*_color macros. */
|
||||||
FI_ MipsAtom ac_pack_color_word(U4 off, U4 cmd, U1 r, U1 g, U1 b)
|
FI_ Slice_MipsCode ac_pack_color_word(U4 off, U4 cmd, U1 r, U1 g, U1 b)
|
||||||
atom_dbg_skip MipsAtomComp_Proc_(ac_pack_color_word, {
|
atom_dbg_skip MipsAtomComp_Proc_(ac_pack_color_word, {
|
||||||
load_upper_i(R_AT, (cmd) << 8 | (b)),
|
load_upper_i(R_AT, (cmd) << 8 | (b)),
|
||||||
or_i_self( R_AT, ((g) << 8) | (r)),
|
or_i_self( R_AT, ((g) << 8) | (r)),
|
||||||
@@ -192,7 +195,7 @@ atom_dbg_skip MipsAtomComp_Proc_(ac_pack_color_word, {
|
|||||||
|
|
||||||
/* Words: 3; Emits the F3 command+color word (cmd byte | BLUE | GREEN | RED)
|
/* Words: 3; Emits the F3 command+color word (cmd byte | BLUE | GREEN | RED)
|
||||||
* Args: _r, _g, _b are 8-bit RGB byte values (not raw 16-bit fields). */
|
* Args: _r, _g, _b are 8-bit RGB byte values (not raw 16-bit fields). */
|
||||||
FI_ MipsAtom ac_format_f3_color(U1 r, U1 g, U1 b)
|
FI_ Slice_MipsCode ac_format_f3_color(U1 r, U1 g, U1 b)
|
||||||
atom_dbg_skip MipsAtomComp_Proc_(ac_format_f3_color, { mac_pack_color_word(O_(Poly_F3,color), gp0_cmd_poly_f3, r, g, b) })
|
atom_dbg_skip MipsAtomComp_Proc_(ac_format_f3_color, { mac_pack_color_word(O_(Poly_F3,color), gp0_cmd_poly_f3, r, g, b) })
|
||||||
|
|
||||||
/* Words: 3; Stores the 3 transformed (V2_S2 screen) vertices to the F3.
|
/* Words: 3; Stores the 3 transformed (V2_S2 screen) vertices to the F3.
|
||||||
@@ -205,7 +208,7 @@ atom_dbg_skip MipsAtomComp_(ac_gte_store_f3) {
|
|||||||
|
|
||||||
/* Words: 12; Emits the four (code|color) words of a Poly_G4.
|
/* Words: 12; Emits the four (code|color) words of a Poly_G4.
|
||||||
* Args: rN,gN,bN are 8-bit RGB byte values for each of the 4 vertices. */
|
* Args: rN,gN,bN are 8-bit RGB byte values for each of the 4 vertices. */
|
||||||
FI_ MipsAtom ac_format_g4_color(
|
FI_ Slice_MipsCode ac_format_g4_color(
|
||||||
U1 r0, U1 g0, U1 b0,
|
U1 r0, U1 g0, U1 b0,
|
||||||
U1 r1, U1 g1, U1 b1,
|
U1 r1, U1 g1, U1 b1,
|
||||||
U1 r2, U1 g2, U1 b2,
|
U1 r2, U1 g2, U1 b2,
|
||||||
@@ -259,7 +262,7 @@ FI_ void atombuilder_end(MipsAtomBuilder_R ab) {
|
|||||||
mem_bump(ab->start, ab->capacity, & ab->used, S_(ac_yield));
|
mem_bump(ab->start, ab->capacity, & ab->used, S_(ac_yield));
|
||||||
}
|
}
|
||||||
|
|
||||||
#define mipsatom_from_builder(ab) (MipsAtom){ab.start, ab.used}
|
#define mipsatom_from_builder(ab) (Slice_MipsCode){ab.start, ab.used}
|
||||||
|
|
||||||
#pragma endregion Mips Atom Builder
|
#pragma endregion Mips Atom Builder
|
||||||
|
|
||||||
@@ -313,66 +316,4 @@ internal MipsAtom_(set_gte_world) atom_info(
|
|||||||
mac_yield()
|
mac_yield()
|
||||||
};
|
};
|
||||||
|
|
||||||
/* DIAGNOSTIC 1: Pure tape loop test */
|
|
||||||
internal MipsAtom_(diag_yield) { mac_yield() };
|
|
||||||
|
|
||||||
/* DIAGNOSTIC 2: Pure memory test (No GTE). Draws a fixed cyan triangle. */
|
|
||||||
internal MipsAtom_(diag_color) {
|
|
||||||
store_word( R_0, R_T7, 0),
|
|
||||||
load_upper_i(R_AT, gp0_cmd_poly_f3 << 8 | 0xFF), /* High: MipsCode Poly_F3(0x20) + Color B:FF */
|
|
||||||
or_i_self( R_AT, 0xFF00), /* Low: Color G:FF, R:00 (Cyan) */
|
|
||||||
store_word( R_AT, R_T7, 4),
|
|
||||||
|
|
||||||
/* Fake coordinates - Swapped winding order to prevent GPU culling! */
|
|
||||||
load_upper_i(R_AT, 0x0010), or_i_self(R_AT, 0x0010), store_word(R_AT, R_T7, 8), /* (16, 16) */
|
|
||||||
load_upper_i(R_AT, 0x0050), or_i_self(R_AT, 0x0010), store_word(R_AT, R_T7, 12), /* (80, 16) */
|
|
||||||
load_upper_i(R_AT, 0x0010), or_i_self(R_AT, 0x0050), store_word(R_AT, R_T7, 16), /* (16, 80) */
|
|
||||||
|
|
||||||
add_ui( R_T1, R_0, 10),
|
|
||||||
shift_lleft_self(R_T1, S_(U4)/2),
|
|
||||||
add_u_self( R_T1, R_T6),
|
|
||||||
|
|
||||||
load_word( R_AT, R_T1, 0),
|
|
||||||
load_upper_i(R_V0, (S_(Poly_F3)/S_(U4) - S_(PolyTag)/S_(U4)) << PolyTag_len_bits),
|
|
||||||
store_word( R_AT, R_T7, 0),
|
|
||||||
shift_lleft(R_AT, R_T7, S_(PolyTag_len_bits)), shift_lright(R_AT, R_AT, S_(PolyTag_len_bits)),
|
|
||||||
or_u_self( R_AT, R_V0),
|
|
||||||
store_word( R_AT, R_T1, 0),
|
|
||||||
|
|
||||||
add_ui(R_T7, R_T7, 20),
|
|
||||||
|
|
||||||
mac_yield()
|
|
||||||
};
|
|
||||||
|
|
||||||
/* DIAGNOSTIC 3: Pure GTE test (No Memory Writes) */
|
|
||||||
internal MipsAtom_(diag_gte) {
|
|
||||||
/* Load 3 indices */
|
|
||||||
load_half_u(R_T0, R_T4, 0),
|
|
||||||
load_half_u(R_T1, R_T4, 2),
|
|
||||||
load_half_u(R_T2, R_T4, 4),
|
|
||||||
|
|
||||||
/* Load Vertices into GTE */
|
|
||||||
shift_lleft( R_AT, R_T0, 3), add_u( R_AT, R_AT, R_T5),
|
|
||||||
load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4),
|
|
||||||
gte_mv_to_data_r(R_V0, C2_VXY0), gte_mv_to_data_r(R_V1, C2_VZ0),
|
|
||||||
|
|
||||||
shift_lleft( R_AT, R_T1, 3), add_u(R_AT, R_AT, R_T5),
|
|
||||||
load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4),
|
|
||||||
gte_mv_to_data_r(R_V0, C2_VXY1), gte_mv_to_data_r(R_V1, C2_VZ1),
|
|
||||||
|
|
||||||
shift_lleft(R_AT, R_T2, 3), add_u(R_AT, R_AT, R_T5),
|
|
||||||
load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4),
|
|
||||||
gte_mv_to_data_r(R_V0, C2_VXY2), gte_mv_to_data_r(R_V1, C2_VZ2),
|
|
||||||
|
|
||||||
/* Run Math */
|
|
||||||
nop2, gte_cmdw_rtpt,
|
|
||||||
nop2, gte_cmdw_nclip,
|
|
||||||
nop2,
|
|
||||||
|
|
||||||
/* Advance Face Cursor and Yield */
|
|
||||||
add_ui(R_T4, R_T4, 8),
|
|
||||||
|
|
||||||
mac_yield()
|
|
||||||
};
|
|
||||||
|
|
||||||
#pragma endregion Baked Mips Atoms
|
#pragma endregion Baked Mips Atoms
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ enum {
|
|||||||
v3s2_byteoff = 3, // log2(8), used with shift_left_logical op for index via byte offset.
|
v3s2_byteoff = 3, // log2(8), used with shift_left_logical op for index via byte offset.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef Array_(U1, 2);
|
||||||
typedef Array_(U4, 2);
|
typedef Array_(U4, 2);
|
||||||
typedef Array_(S2, 2);
|
typedef Array_(S2, 2);
|
||||||
typedef Array_(S2, 3);
|
typedef Array_(S2, 3);
|
||||||
@@ -22,6 +23,7 @@ typedef S2 A3x3_S2[3][3];
|
|||||||
typedef Struct_(Extent2_S2) { S2 width; S2 height; };
|
typedef Struct_(Extent2_S2) { S2 width; S2 height; };
|
||||||
typedef Struct_(Extent2_S4) { S4 width; S4 height; };
|
typedef Struct_(Extent2_S4) { S4 width; S4 height; };
|
||||||
|
|
||||||
|
typedef Struct_(V2_U1) { U1 x; U1 y; };
|
||||||
typedef Struct_(V2_S2) { S2 x; S2 y; };
|
typedef Struct_(V2_S2) { S2 x; S2 y; };
|
||||||
typedef Struct_(V2_S4) { S4 x; S4 y; };
|
typedef Struct_(V2_S4) { S4 x; S4 y; };
|
||||||
typedef Struct_(V3_S2) { S2 x; S2 y; S2 z; S2 pad; };
|
typedef Struct_(V3_S2) { S2 x; S2 y; S2 z; S2 pad; };
|
||||||
|
|||||||
+4
-4
@@ -336,10 +336,10 @@ enum { _BitOffsets = 0
|
|||||||
|
|
||||||
/* Logic Opcodes */
|
/* Logic Opcodes */
|
||||||
|
|
||||||
#define and(rd, rs, rt) enc_r(op_special, (rs), (rt), (rd), 0, fc_and)
|
#define and_u(rd, rs, rt) enc_r(op_special, (rs), (rt), (rd), 0, fc_and)
|
||||||
#define or(rd, rs, rt) enc_r(op_special, (rs), (rt), (rd), 0, fc_or)
|
#define or_u(rd, rs, rt) enc_r(op_special, (rs), (rt), (rd), 0, fc_or)
|
||||||
#define xor(rd, rs, rt) enc_r(op_special, (rs), (rt), (rd), 0, fc_xor)
|
#define xor_u(rd, rs, rt) enc_r(op_special, (rs), (rt), (rd), 0, fc_xor)
|
||||||
#define nor(rd, rs, rt) enc_r(op_special, (rs), (rt), (rd), 0, fc_nor)
|
#define nor_u(rd, rs, rt) enc_r(op_special, (rs), (rt), (rd), 0, fc_nor)
|
||||||
|
|
||||||
#define or_u_self(rd_rs, rt) enc_r(op_special, (rd_rs), (rt), (rd_rs), 0, fc_or)
|
#define or_u_self(rd_rs, rt) enc_r(op_special, (rd_rs), (rt), (rd_rs), 0, fc_or)
|
||||||
|
|
||||||
|
|||||||
+45
-115
@@ -3,24 +3,26 @@
|
|||||||
# include "dsl.h"
|
# include "dsl.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* PSX button bit positions — 1:1 with PSX-SPX docs at docs/psx-spx/docs/controllersandmemorycards.md:405-421.
|
||||||
|
* Wire is active-low (0 = pressed).
|
||||||
|
* The decoder atom computes buttons = (~raw_buttons) & 0xFFFF; the active-low-to-active-high inversion is applied bit-by-bit. */
|
||||||
enum {
|
enum {
|
||||||
Bit_(Pad_L2, 0),
|
Bit_(Pad_Select, 0),
|
||||||
Bit_(Pad_R2, 1),
|
Bit_(Pad_L3, 1),
|
||||||
Bit_(Pad_L1, 2),
|
Bit_(Pad_R3, 2),
|
||||||
Bit_(Pad_R1, 3),
|
Bit_(Pad_Start, 3),
|
||||||
Bit_(Pad_Triangle, 4),
|
Bit_(Pad_Up, 4),
|
||||||
Bit_(Pad_Circle, 5),
|
Bit_(Pad_Right, 5),
|
||||||
Bit_(Pad_Cross, 6),
|
Bit_(Pad_Down, 6),
|
||||||
Bit_(Pad_Square, 7),
|
Bit_(Pad_Left, 7),
|
||||||
Bit_(Pad_Select, 8),
|
Bit_(Pad_L2, 8),
|
||||||
Bit_(Unused_PadI, 9),
|
Bit_(Pad_R2, 9),
|
||||||
Bit_(Unused_PadJ, 10),
|
Bit_(Pad_L1, 10),
|
||||||
Bit_(Pad_Start, 11),
|
Bit_(Pad_R1, 11),
|
||||||
Bit_(Pad_Up, 12),
|
Bit_(Pad_Triangle, 12),
|
||||||
Bit_(Pad_Right, 13),
|
Bit_(Pad_Circle, 13),
|
||||||
Bit_(Pad_Down, 14),
|
Bit_(Pad_Cross, 14),
|
||||||
Bit_(Pad_Left, 15),
|
Bit_(Pad_Square, 15),
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@@ -33,111 +35,39 @@ enum {
|
|||||||
#define pad0_(btn_id) (btn_id << Pad0)
|
#define pad0_(btn_id) (btn_id << Pad0)
|
||||||
#define pad1_(btn_id) (btn_id << Pad1)
|
#define pad1_(btn_id) (btn_id << Pad1)
|
||||||
|
|
||||||
void pad_init(U4 mode) asm("PadInit");
|
|
||||||
U4 pad_read(U4 id) asm("PadRead");
|
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
* SIO0 raw controller polling surface (Phase 1 scaffolding)
|
* BIOS pad-buffer subsystem: docs/psx-spx/docs/kernelbios.md (B(12h) + B(13h))
|
||||||
* Source of truth: docs/psx-spx/docs/serialinterfacessio.md
|
|
||||||
* docs/psx-spx/docs/controllersandmemorycards.md
|
|
||||||
* https://github.com/Lameguy64/PSn00bSDK (spi.c)
|
|
||||||
* ============================================================ */
|
* ============================================================ */
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
/* SIO0 register offsets relative to IOBASE 0xBF800000 */
|
PAD_BIOS_RAW_SIZE = 0x22,
|
||||||
pad_SIO_DATA_OFFSET = 0x1040, /* 8-bit data (TX write / RX read) */
|
};
|
||||||
pad_SIO_STAT_OFFSET = 0x1044, /* 16-bit status */
|
typedef Struct_(PadBiosRaw) {
|
||||||
pad_SIO_MODE_OFFSET = 0x1048, /* 16-bit mode */
|
U1 bytes[PAD_BIOS_RAW_SIZE];
|
||||||
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, PadStatus) {
|
||||||
typedef Enum_(U4, PadSioStatus) {
|
PadStatus_Disconnected,
|
||||||
PadSioStatus_Disconnected,
|
PadStatus_Digital,
|
||||||
PadSioStatus_Digital,
|
PadStatus_AnalogStick,
|
||||||
PadSioStatus_AnalogStick,
|
PadStatus_AnalogPad,
|
||||||
PadSioStatus_Analog,
|
PadStatus_Unsupported,
|
||||||
PadSioStatus_ConfigRejected,
|
PadStatus_Pending,
|
||||||
PadSioStatus_Invalid,
|
PadStatus_Invalid,
|
||||||
PadSioStatus_TxTimeout,
|
|
||||||
PadSioStatus_RxTimeout,
|
|
||||||
PadSioStatus_AckTimeout,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* PadState — per-port runtime state */
|
/* PadState — per-port normalized runtime state.
|
||||||
|
* Field order is chosen so that the 4 axes (left_x, left_y, right_x, right_y)
|
||||||
|
* form a contiguous 4-byte block at offset 8, allowing a single `store_word` to clear-or-write all 4 axes in one MIPS instruction.
|
||||||
|
* The struct size stays 12 bytes (unchanged from the prior order,
|
||||||
|
* which left the C compiler to insert 1 byte of trailing pad to reach the 4-byte struct alignment). */
|
||||||
typedef Struct_(PadState) {
|
typedef Struct_(PadState) {
|
||||||
PadSioStatus status;
|
PadStatus status; /* offset 0, size 4 (U4) */
|
||||||
U4 buttons;
|
U2 buttons; /* offset 4, size 2 */
|
||||||
U1 left_x;
|
U1 id; /* offset 6, size 1 */
|
||||||
U1 left_y;
|
U1 pad; /* offset 7, size 1 — explicit pad to align the axes block */
|
||||||
U1 right_x;
|
U1 left_x; /* offset 8, size 1 — store_word target (4-byte aligned) */
|
||||||
U1 right_y;
|
U1 left_y; /* offset 9, size 1 */
|
||||||
U4 attempt;
|
U1 right_x; /* offset 10, size 1 */
|
||||||
|
U1 right_y; /* offset 11, size 1 */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* 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,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ WORD_COUNT(call_reg, 1)
|
|||||||
WORD_COUNT(call_addr, 1)
|
WORD_COUNT(call_addr, 1)
|
||||||
WORD_COUNT(branch_le_zero, 1)
|
WORD_COUNT(branch_le_zero, 1)
|
||||||
WORD_COUNT(branch_equal, 1)
|
WORD_COUNT(branch_equal, 1)
|
||||||
|
WORD_COUNT(branch_ne, 1)
|
||||||
WORD_COUNT(add_ui, 1)
|
WORD_COUNT(add_ui, 1)
|
||||||
WORD_COUNT(set_lt_u, 1)
|
WORD_COUNT(set_lt_u, 1)
|
||||||
WORD_COUNT(set_lt_s, 1)
|
WORD_COUNT(set_lt_s, 1)
|
||||||
@@ -29,7 +30,9 @@ WORD_COUNT(set_lt_si, 1)
|
|||||||
WORD_COUNT(set_lt_ui, 1)
|
WORD_COUNT(set_lt_ui, 1)
|
||||||
WORD_COUNT(load_word, 1)
|
WORD_COUNT(load_word, 1)
|
||||||
WORD_COUNT(load_half_u, 1)
|
WORD_COUNT(load_half_u, 1)
|
||||||
|
WORD_COUNT(load_byte_u, 1)
|
||||||
WORD_COUNT(store_word, 1)
|
WORD_COUNT(store_word, 1)
|
||||||
|
WORD_COUNT(store_byte, 1)
|
||||||
WORD_COUNT(add_ui_self, 1)
|
WORD_COUNT(add_ui_self, 1)
|
||||||
WORD_COUNT(add_u_self, 1)
|
WORD_COUNT(add_u_self, 1)
|
||||||
WORD_COUNT(add_u, 1)
|
WORD_COUNT(add_u, 1)
|
||||||
@@ -37,6 +40,7 @@ WORD_COUNT(or_i, 1)
|
|||||||
WORD_COUNT(or_i_self, 1)
|
WORD_COUNT(or_i_self, 1)
|
||||||
WORD_COUNT(or_u, 1)
|
WORD_COUNT(or_u, 1)
|
||||||
WORD_COUNT(or_u_self, 1)
|
WORD_COUNT(or_u_self, 1)
|
||||||
|
WORD_COUNT(nor_u, 1)
|
||||||
WORD_COUNT(shift_lleft, 1)
|
WORD_COUNT(shift_lleft, 1)
|
||||||
WORD_COUNT(shift_lleft_self, 1)
|
WORD_COUNT(shift_lleft_self, 1)
|
||||||
WORD_COUNT(shift_lright, 1)
|
WORD_COUNT(shift_lright, 1)
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ global SMemory smem;
|
|||||||
extern SMemory smem;
|
extern SMemory smem;
|
||||||
|
|
||||||
// TODO(Ed):
|
// TODO(Ed):
|
||||||
FI_ U4* spad_warm(MipsAtom atom) {
|
FI_ U4* spad_warm(Slice_MipsCode atom) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,68 @@
|
|||||||
|
|
||||||
#pragma region Baked Atoms
|
#pragma region Baked Atoms
|
||||||
|
|
||||||
|
/* DIAGNOSTIC 1: Pure tape loop test */
|
||||||
|
internal MipsAtom_(diag_yield) { mac_yield() };
|
||||||
|
|
||||||
|
/* DIAGNOSTIC 2: Pure memory test (No GTE). Draws a fixed cyan triangle. */
|
||||||
|
internal MipsAtom_(diag_color) {
|
||||||
|
store_word( R_0, R_T7, 0),
|
||||||
|
load_upper_i(R_AT, gp0_cmd_poly_f3 << 8 | 0xFF), /* High: MipsCode Poly_F3(0x20) + Color B:FF */
|
||||||
|
or_i_self( R_AT, 0xFF00), /* Low: Color G:FF, R:00 (Cyan) */
|
||||||
|
store_word( R_AT, R_T7, 4),
|
||||||
|
|
||||||
|
/* Fake coordinates - Swapped winding order to prevent GPU culling! */
|
||||||
|
load_upper_i(R_AT, 0x0010), or_i_self(R_AT, 0x0010), store_word(R_AT, R_T7, 8), /* (16, 16) */
|
||||||
|
load_upper_i(R_AT, 0x0050), or_i_self(R_AT, 0x0010), store_word(R_AT, R_T7, 12), /* (80, 16) */
|
||||||
|
load_upper_i(R_AT, 0x0010), or_i_self(R_AT, 0x0050), store_word(R_AT, R_T7, 16), /* (16, 80) */
|
||||||
|
|
||||||
|
add_ui( R_T1, R_0, 10),
|
||||||
|
shift_lleft_self(R_T1, S_(U4)/2),
|
||||||
|
add_u_self( R_T1, R_T6),
|
||||||
|
|
||||||
|
load_word( R_AT, R_T1, 0),
|
||||||
|
load_upper_i(R_V0, (S_(Poly_F3)/S_(U4) - S_(PolyTag)/S_(U4)) << PolyTag_len_bits),
|
||||||
|
store_word( R_AT, R_T7, 0),
|
||||||
|
shift_lleft(R_AT, R_T7, S_(PolyTag_len_bits)), shift_lright(R_AT, R_AT, S_(PolyTag_len_bits)),
|
||||||
|
or_u_self( R_AT, R_V0),
|
||||||
|
store_word( R_AT, R_T1, 0),
|
||||||
|
|
||||||
|
add_ui(R_T7, R_T7, 20),
|
||||||
|
|
||||||
|
mac_yield()
|
||||||
|
};
|
||||||
|
|
||||||
|
/* DIAGNOSTIC 3: Pure GTE test (No Memory Writes) */
|
||||||
|
internal MipsAtom_(diag_gte) {
|
||||||
|
/* Load 3 indices */
|
||||||
|
load_half_u(R_T0, R_T4, 0),
|
||||||
|
load_half_u(R_T1, R_T4, 2),
|
||||||
|
load_half_u(R_T2, R_T4, 4),
|
||||||
|
|
||||||
|
/* Load Vertices into GTE */
|
||||||
|
shift_lleft( R_AT, R_T0, 3), add_u( R_AT, R_AT, R_T5),
|
||||||
|
load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4),
|
||||||
|
gte_mv_to_data_r(R_V0, C2_VXY0), gte_mv_to_data_r(R_V1, C2_VZ0),
|
||||||
|
|
||||||
|
shift_lleft( R_AT, R_T1, 3), add_u(R_AT, R_AT, R_T5),
|
||||||
|
load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4),
|
||||||
|
gte_mv_to_data_r(R_V0, C2_VXY1), gte_mv_to_data_r(R_V1, C2_VZ1),
|
||||||
|
|
||||||
|
shift_lleft(R_AT, R_T2, 3), add_u(R_AT, R_AT, R_T5),
|
||||||
|
load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4),
|
||||||
|
gte_mv_to_data_r(R_V0, C2_VXY2), gte_mv_to_data_r(R_V1, C2_VZ2),
|
||||||
|
|
||||||
|
/* Run Math */
|
||||||
|
nop2, gte_cmdw_rtpt,
|
||||||
|
nop2, gte_cmdw_nclip,
|
||||||
|
nop2,
|
||||||
|
|
||||||
|
/* Advance Face Cursor and Yield */
|
||||||
|
add_ui(R_T4, R_T4, 8),
|
||||||
|
|
||||||
|
mac_yield()
|
||||||
|
};
|
||||||
|
|
||||||
typedef Struct_(Binds_CubeTri) {
|
typedef Struct_(Binds_CubeTri) {
|
||||||
U4 PrimCursor;
|
U4 PrimCursor;
|
||||||
V4_S2* FaceCursor;
|
V4_S2* FaceCursor;
|
||||||
|
|||||||
@@ -9,6 +9,12 @@
|
|||||||
#define WORD_COUNT(name, count) enum { words_##name = (count) };
|
#define WORD_COUNT(name, count) enum { words_##name = (count) };
|
||||||
#endif
|
#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 */
|
/* atom_dbg_skip */
|
||||||
#define mac_store_v2s2(rt_x, rt_y, base, offset) \
|
#define mac_store_v2s2(rt_x, rt_y, base, offset) \
|
||||||
store_half(rt_x, base, offset + O_(V2_S2,x)) \
|
store_half(rt_x, base, offset + O_(V2_S2,x)) \
|
||||||
|
|||||||
@@ -5,16 +5,6 @@
|
|||||||
#pragma region hello_joypad.tape
|
#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) ---
|
// --- atom: cube_g4_face (77 words) ---
|
||||||
|
|
||||||
#define _atom_offset_cull_cube_g4_face_exit 42
|
#define _atom_offset_cull_cube_g4_face_exit 42
|
||||||
@@ -35,5 +25,49 @@ enum {
|
|||||||
atom_offset_bounds_chk_floor_f3_face_exit = _atom_offset_bounds_chk_floor_f3_face_exit,
|
atom_offset_bounds_chk_floor_f3_face_exit = _atom_offset_bounds_chk_floor_f3_face_exit,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// --- atom: pad_bios_snapshot (88 words) ---
|
||||||
|
|
||||||
|
#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_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 23
|
||||||
|
#define _atom_offset_end_low_exit_stick 11
|
||||||
|
|
||||||
|
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_joypad.tape
|
#pragma endregion hello_joypad.tape
|
||||||
|
|
||||||
|
|||||||
@@ -123,22 +123,14 @@ typedef Struct_(SMemory) {
|
|||||||
Ent_Cube cube;
|
Ent_Cube cube;
|
||||||
Ent_Floor floor;
|
Ent_Floor floor;
|
||||||
|
|
||||||
U4 pad_state;
|
PadBiosRaw pad_raw[2];
|
||||||
|
PadState pad[2];
|
||||||
|
|
||||||
U4_V scratchpad; // d-cache
|
U4_V scratchpad; // d-cache
|
||||||
};
|
};
|
||||||
global SMemory smem;
|
global SMemory smem;
|
||||||
extern SMemory smem;
|
extern SMemory smem;
|
||||||
|
|
||||||
#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) {
|
I_ B1* prim__alloc(U4 type_width, Str8 type_name) {
|
||||||
gknown PrimitiveArena* pa = & smem.primitives;
|
gknown PrimitiveArena* pa = & smem.primitives;
|
||||||
gknown B1* buf = (B1*) r_(smem.primitives.buf)[smem.active_buf_id];
|
gknown B1* buf = (B1*) r_(smem.primitives.buf)[smem.active_buf_id];
|
||||||
@@ -149,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)))
|
#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)
|
void gp_screen_init_c11(DoubleBuffer* screen_buf, S4* active_buf_id)
|
||||||
{
|
{
|
||||||
reset_graph(0);
|
reset_graph(0);
|
||||||
@@ -193,23 +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
|
active_buf_id[0] = ! active_buf_id[0]; // Swap current buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
void render(void) {
|
|
||||||
}
|
|
||||||
|
|
||||||
GCC_OPTIMIZATION_DISABLE
|
GCC_OPTIMIZATION_DISABLE
|
||||||
void update(PrimitiveArena* pa, U4* ordering_buf)
|
void update(PrimitiveArena* pa, U4* ordering_buf)
|
||||||
{
|
{
|
||||||
TapeBuilder tb = tb_make(slice_ut_arr(smem.MemTape));
|
TapeBuilder tb = tb_make(slice_ut_arr(smem.MemTape));
|
||||||
|
|
||||||
smem.pad_state = pad_read(0);
|
if (0) // Pad Input (dead — kept for the source-as-written record; references the deleted `pad_state` field)
|
||||||
|
|
||||||
if (0) // Pad Input
|
|
||||||
{
|
{
|
||||||
if (pad0_signal_(Pad_Left)) {
|
(void)Pad_Left; (void)Pad_Right; /* suppress unused-token warnings */
|
||||||
|
if (false) {
|
||||||
smem.cube.rot.y += 30;
|
smem.cube.rot.y += 30;
|
||||||
smem.floor.rot.y += 5;
|
smem.floor.rot.y += 5;
|
||||||
}
|
}
|
||||||
if (pad0_signal_(Pad_Right)) {
|
if (false) {
|
||||||
smem.cube.rot.y -= 30;
|
smem.cube.rot.y -= 30;
|
||||||
smem.floor.rot.y -= 5;
|
smem.floor.rot.y -= 5;
|
||||||
}
|
}
|
||||||
@@ -217,10 +279,18 @@ void update(PrimitiveArena* pa, U4* ordering_buf)
|
|||||||
if (1) // Pad Input (Tape version)
|
if (1) // Pad Input (Tape version)
|
||||||
{
|
{
|
||||||
tb.used = 0; tb_scope_run(& tb) {
|
tb.used = 0; tb_scope_run(& tb) {
|
||||||
tb_emit(& tb, pad_input_demo);
|
/* BIOS-owned polling: per-frame snapshot of both ports. */
|
||||||
tb_data(& tb, smem.pad_state);
|
tb_emit_(pad_bios_snapshot);
|
||||||
tb_data(& tb, u4_(& smem.cube.rot));
|
tb_data_(Binds_PadBiosSnapshot.raw, & smem.pad_raw[0]);
|
||||||
tb_data(& tb, u4_(& smem.floor.rot));
|
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_(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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,6 +500,9 @@ void update(PrimitiveArena* pa, U4* ordering_buf)
|
|||||||
}
|
}
|
||||||
GCC_OPTIMIZATION_ENABLE
|
GCC_OPTIMIZATION_ENABLE
|
||||||
|
|
||||||
|
void render(void) {
|
||||||
|
}
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
smem = (SMemory){0};
|
smem = (SMemory){0};
|
||||||
@@ -440,7 +513,6 @@ int main(void)
|
|||||||
ent_cube128_init(& smem.cube.verts, & smem.cube.faces); {
|
ent_cube128_init(& smem.cube.verts, & smem.cube.faces); {
|
||||||
Ent_Cube* cube = & smem.cube;
|
Ent_Cube* cube = & smem.cube;
|
||||||
cube->rot = v3s2(0, 0, 0);
|
cube->rot = v3s2(0, 0, 0);
|
||||||
// cube->pos = v3s4(0, 0, 900);
|
|
||||||
cube->scale = v3s4_fp_one();
|
cube->scale = v3s4_fp_one();
|
||||||
cube->accel = v3s4(0, 1, 0);
|
cube->accel = v3s4(0, 1, 0);
|
||||||
cube->pos = v3s4(0, -400, 1800);
|
cube->pos = v3s4(0, -400, 1800);
|
||||||
@@ -454,13 +526,15 @@ int main(void)
|
|||||||
}
|
}
|
||||||
TapeBuilder tb = tb_make(slice_ut_arr(smem.MemTape)); {
|
TapeBuilder tb = tb_make(slice_ut_arr(smem.MemTape)); {
|
||||||
reset_graph(0);
|
reset_graph(0);
|
||||||
|
/* 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 U4* io_base_addr rgcc(R_IO_BaseAddr) = u4_r(IO_BASE_ADDR);
|
||||||
register DoubleBuffer* screen_buf rgcc(R_ScreenBuf) = & smem.screen_buf;
|
register DoubleBuffer* screen_buf rgcc(R_ScreenBuf) = & smem.screen_buf;
|
||||||
tb.used = 0; tb_scope_run(& tb) {
|
tb.used = 0; tb_scope_run(& tb) {
|
||||||
tb_emit(& tb, screen_env_init);
|
tb_emit(& tb, screen_env_init);
|
||||||
tb_emit(& tb, gp_screen_init);
|
tb_emit(& tb, gp_screen_init);
|
||||||
}
|
}
|
||||||
pad_init(0);
|
|
||||||
}
|
}
|
||||||
while (1) {
|
while (1) {
|
||||||
gknown S4* active_buf_id = & smem.active_buf_id;
|
gknown S4* active_buf_id = & smem.active_buf_id;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
# include "duffle/dsl.h"
|
# include "duffle/dsl.h"
|
||||||
# include "duffle/math.h"
|
# include "duffle/math.h"
|
||||||
# include "duffle/gp.h"
|
# include "duffle/gp.h"
|
||||||
|
# include "duffle/pad.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@@ -10,40 +11,16 @@ enum {
|
|||||||
OrderingTbl_Len = 2048
|
OrderingTbl_Len = 2048
|
||||||
};
|
};
|
||||||
|
|
||||||
#define ScreenRes_X 320
|
enum {
|
||||||
#define ScreenRes_Y 240
|
ScreenRes_X = 320,
|
||||||
#define ScreenZ 320
|
ScreenRes_Y = 240,
|
||||||
#define ScreenRes_CenterX (ScreenRes_X >> 1)
|
ScreenZ = 320,
|
||||||
#define ScreenRes_CenterY (ScreenRes_Y >> 1)
|
ScreenRes_CenterX = (ScreenRes_X >> 1),
|
||||||
|
ScreenRes_CenterY = (ScreenRes_Y >> 1),
|
||||||
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
fp_one = (1 << 12),
|
fp_one = (1 << 12),
|
||||||
};
|
};
|
||||||
|
|
||||||
#define v3s4_fp_one() v3s4(fp_one, fp_one, fp_one)
|
#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);
|
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
# include "duffle/gen/duffle.macs.h"
|
# include "duffle/gen/duffle.macs.h"
|
||||||
# include "duffle/gen/duffle.offsets.h"
|
# include "duffle/gen/duffle.offsets.h"
|
||||||
# include "duffle/atom_dsl.h"
|
# include "duffle/atom_dsl.h"
|
||||||
# include "duffle/pad.h"
|
|
||||||
# include "duffle/lottes_tape.h"
|
# include "duffle/lottes_tape.h"
|
||||||
# include "duffle/mips.h"
|
# include "duffle/mips.h"
|
||||||
# include "duffle/gte.h"
|
# include "duffle/gte.h"
|
||||||
# include "duffle/gp.h"
|
# include "duffle/gp.h"
|
||||||
|
# include "duffle/pad.h"
|
||||||
# include "duffle/word_count.metadata.h"
|
# include "duffle/word_count.metadata.h"
|
||||||
# include "psyq.h"
|
# include "psyq.h"
|
||||||
# include "gen/hello_joypad.offsets.h"
|
# include "gen/hello_joypad.offsets.h"
|
||||||
@@ -16,33 +16,37 @@
|
|||||||
|
|
||||||
#pragma region MACs (Mips Atom components)
|
#pragma region MACs (Mips Atom components)
|
||||||
|
|
||||||
|
FI_ Slice_MipsCode ac_load_v2s2(U4 rs_x, U4 rs_y, U4 r_base, U4 offset) atom_dbg_skip MipsAtomComp_Proc_(ac_load_v2s2, {
|
||||||
|
load_half( rs_x, r_base, O_(V3_S2,x)),
|
||||||
|
load_half( rs_y, r_base, O_(V3_S2,y)),
|
||||||
|
})
|
||||||
|
|
||||||
FI_ MipsAtom ac_store_v2s2(U4 rt_x, U4 rt_y, U4 base, U4 offset) atom_dbg_skip MipsAtomComp_Proc_(ac_store_v2s2, {
|
FI_ Slice_MipsCode ac_store_v2s2(U4 rt_x, U4 rt_y, U4 base, U4 offset) atom_dbg_skip MipsAtomComp_Proc_(ac_store_v2s2, {
|
||||||
store_half(rt_x, base, offset + O_(V2_S2,x)),
|
store_half(rt_x, base, offset + O_(V2_S2,x)),
|
||||||
store_half(rt_y, base, offset + O_(V2_S2,y)),
|
store_half(rt_y, base, offset + O_(V2_S2,y)),
|
||||||
})
|
})
|
||||||
|
|
||||||
FI_ MipsAtom ac_store_rects2(U4 rt_x, U4 rt_y, U4 rt_width, U4 rt_height, U4 base, U4 offset) atom_dbg_skip MipsAtomComp_Proc_(ac_store_rects2, {
|
FI_ Slice_MipsCode ac_store_rects2(U4 rt_x, U4 rt_y, U4 rt_width, U4 rt_height, U4 base, U4 offset) atom_dbg_skip MipsAtomComp_Proc_(ac_store_rects2, {
|
||||||
store_half(rt_x, base, offset + O_(Rect_S2,x)),
|
store_half(rt_x, base, offset + O_(Rect_S2,x)),
|
||||||
store_half(rt_y, base, offset + O_(Rect_S2,y)),
|
store_half(rt_y, base, offset + O_(Rect_S2,y)),
|
||||||
store_half(rt_width, base, offset + O_(Rect_S2,width)),
|
store_half(rt_width, base, offset + O_(Rect_S2,width)),
|
||||||
store_half(rt_height, base, offset + O_(Rect_S2,height)),
|
store_half(rt_height, base, offset + O_(Rect_S2,height)),
|
||||||
})
|
})
|
||||||
|
|
||||||
FI_ MipsAtom ac_store_rgb8(U1 rr, U1 rg, U1 rb, U4 base, U4 offset) atom_dbg_skip MipsAtomComp_Proc_(ac_store_rgb8, {
|
FI_ Slice_MipsCode ac_store_rgb8(U1 rr, U1 rg, U1 rb, U4 base, U4 offset) atom_dbg_skip MipsAtomComp_Proc_(ac_store_rgb8, {
|
||||||
store_byte(rr, base, offset + O_(DrawEnv,initial_bg_color.r)),
|
store_byte(rr, base, offset + O_(DrawEnv,initial_bg_color.r)),
|
||||||
store_byte(rg, base, offset + O_(DrawEnv,initial_bg_color.g)),
|
store_byte(rg, base, offset + O_(DrawEnv,initial_bg_color.g)),
|
||||||
store_byte(rb, base, offset + O_(DrawEnv,initial_bg_color.b)),
|
store_byte(rb, base, offset + O_(DrawEnv,initial_bg_color.b)),
|
||||||
})
|
})
|
||||||
|
|
||||||
FI_ MipsAtom ac_gcmd_push(U4 cmd, U4 reg_transfer, U4 reg_base, U2 port)
|
FI_ Slice_MipsCode ac_gcmd_push(U4 cmd, U4 reg_transfer, U4 reg_base, U2 port)
|
||||||
MipsAtomComp_Proc_(ac_gcmd_push, {
|
MipsAtomComp_Proc_(ac_gcmd_push, {
|
||||||
load_upper_i(reg_transfer, cmd >> 16),
|
load_upper_i(reg_transfer, cmd >> 16),
|
||||||
or_i_self( reg_transfer, cmd & 0xFFFF),
|
or_i_self( reg_transfer, cmd & 0xFFFF),
|
||||||
store_word( reg_transfer, reg_base, port),
|
store_word( reg_transfer, reg_base, port),
|
||||||
})
|
})
|
||||||
|
|
||||||
FI_ MipsAtom ac_put_disp_env(U4 reg_transfer, U4 reg_base, U2 port)
|
FI_ Slice_MipsCode ac_put_disp_env(U4 reg_transfer, U4 reg_base, U2 port)
|
||||||
MipsAtomComp_Proc_(ac_put_disp_env, {
|
MipsAtomComp_Proc_(ac_put_disp_env, {
|
||||||
// Emits 5 GP0 commands for buffer 0 (display_area = (0,0,320,240)).
|
// Emits 5 GP0 commands for buffer 0 (display_area = (0,0,320,240)).
|
||||||
// Sequence per libpsyx PutDispEnv: DrawArea TL → DrawArea BR → Mask → DrawArea TL → DrawArea BR
|
// Sequence per libpsyx PutDispEnv: DrawArea TL → DrawArea BR → Mask → DrawArea TL → DrawArea BR
|
||||||
@@ -53,7 +57,7 @@ MipsAtomComp_Proc_(ac_put_disp_env, {
|
|||||||
mac_gcmd_push(gp0_word_draw_area_bottom_right_320x240, reg_transfer, reg_base, port),
|
mac_gcmd_push(gp0_word_draw_area_bottom_right_320x240, reg_transfer, reg_base, port),
|
||||||
})
|
})
|
||||||
|
|
||||||
FI_ MipsAtom ac_put_draw_env(U4 reg_transfer, U4 reg_base, U2 port)
|
FI_ Slice_MipsCode ac_put_draw_env(U4 reg_transfer, U4 reg_base, U2 port)
|
||||||
MipsAtomComp_Proc_(ac_put_draw_env, {
|
MipsAtomComp_Proc_(ac_put_draw_env, {
|
||||||
/*
|
/*
|
||||||
* ORIGIN: each code word corresponds to the EXACT value libpsyx's PutDrawEnv function would compute for the same DrawEnv settings.
|
* ORIGIN: each code word corresponds to the EXACT value libpsyx's PutDrawEnv function would compute for the same DrawEnv settings.
|
||||||
@@ -110,6 +114,68 @@ MipsAtomComp_Proc_(ac_put_draw_env, {
|
|||||||
|
|
||||||
#pragma region Baked Atoms
|
#pragma region Baked Atoms
|
||||||
|
|
||||||
|
/* DIAGNOSTIC 1: Pure tape loop test */
|
||||||
|
internal MipsAtom_(diag_yield) { mac_yield() };
|
||||||
|
|
||||||
|
/* DIAGNOSTIC 2: Pure memory test (No GTE). Draws a fixed cyan triangle. */
|
||||||
|
internal MipsAtom_(diag_color) {
|
||||||
|
store_word( R_0, R_T7, 0),
|
||||||
|
load_upper_i(R_AT, gp0_cmd_poly_f3 << 8 | 0xFF), /* High: MipsCode Poly_F3(0x20) + Color B:FF */
|
||||||
|
or_i_self( R_AT, 0xFF00), /* Low: Color G:FF, R:00 (Cyan) */
|
||||||
|
store_word( R_AT, R_T7, 4),
|
||||||
|
|
||||||
|
/* Fake coordinates - Swapped winding order to prevent GPU culling! */
|
||||||
|
load_upper_i(R_AT, 0x0010), or_i_self(R_AT, 0x0010), store_word(R_AT, R_T7, 8), /* (16, 16) */
|
||||||
|
load_upper_i(R_AT, 0x0050), or_i_self(R_AT, 0x0010), store_word(R_AT, R_T7, 12), /* (80, 16) */
|
||||||
|
load_upper_i(R_AT, 0x0010), or_i_self(R_AT, 0x0050), store_word(R_AT, R_T7, 16), /* (16, 80) */
|
||||||
|
|
||||||
|
add_ui( R_T1, R_0, 10),
|
||||||
|
shift_lleft_self(R_T1, S_(U4)/2),
|
||||||
|
add_u_self( R_T1, R_T6),
|
||||||
|
|
||||||
|
load_word( R_AT, R_T1, 0),
|
||||||
|
load_upper_i(R_V0, (S_(Poly_F3)/S_(U4) - S_(PolyTag)/S_(U4)) << PolyTag_len_bits),
|
||||||
|
store_word( R_AT, R_T7, 0),
|
||||||
|
shift_lleft(R_AT, R_T7, S_(PolyTag_len_bits)), shift_lright(R_AT, R_AT, S_(PolyTag_len_bits)),
|
||||||
|
or_u_self( R_AT, R_V0),
|
||||||
|
store_word( R_AT, R_T1, 0),
|
||||||
|
|
||||||
|
add_ui(R_T7, R_T7, 20),
|
||||||
|
|
||||||
|
mac_yield()
|
||||||
|
};
|
||||||
|
|
||||||
|
/* DIAGNOSTIC 3: Pure GTE test (No Memory Writes) */
|
||||||
|
internal MipsAtom_(diag_gte) {
|
||||||
|
/* Load 3 indices */
|
||||||
|
load_half_u(R_T0, R_T4, 0),
|
||||||
|
load_half_u(R_T1, R_T4, 2),
|
||||||
|
load_half_u(R_T2, R_T4, 4),
|
||||||
|
|
||||||
|
/* Load Vertices into GTE */
|
||||||
|
shift_lleft( R_AT, R_T0, 3), add_u( R_AT, R_AT, R_T5),
|
||||||
|
load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4),
|
||||||
|
gte_mv_to_data_r(R_V0, C2_VXY0), gte_mv_to_data_r(R_V1, C2_VZ0),
|
||||||
|
|
||||||
|
shift_lleft( R_AT, R_T1, 3), add_u(R_AT, R_AT, R_T5),
|
||||||
|
load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4),
|
||||||
|
gte_mv_to_data_r(R_V0, C2_VXY1), gte_mv_to_data_r(R_V1, C2_VZ1),
|
||||||
|
|
||||||
|
shift_lleft(R_AT, R_T2, 3), add_u(R_AT, R_AT, R_T5),
|
||||||
|
load_word(R_V0, R_AT, 0), load_word(R_V1, R_AT, 4),
|
||||||
|
gte_mv_to_data_r(R_V0, C2_VXY2), gte_mv_to_data_r(R_V1, C2_VZ2),
|
||||||
|
|
||||||
|
/* Run Math */
|
||||||
|
nop2, gte_cmdw_rtpt,
|
||||||
|
nop2, gte_cmdw_nclip,
|
||||||
|
nop2,
|
||||||
|
|
||||||
|
/* Advance Face Cursor and Yield */
|
||||||
|
add_ui(R_T4, R_T4, 8),
|
||||||
|
|
||||||
|
mac_yield()
|
||||||
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
R_ScreenX = R_T5 atom_reg atom_type(U2),
|
R_ScreenX = R_T5 atom_reg atom_type(U2),
|
||||||
R_ScreenY = R_T6 atom_reg atom_type(U2),
|
R_ScreenY = R_T6 atom_reg atom_type(U2),
|
||||||
@@ -198,47 +264,6 @@ internal MipsAtom_(gp_screen_init) atom_info(atom_phase(screen_init), atom_reads
|
|||||||
mac_yield(),
|
mac_yield(),
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
|
||||||
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*),
|
|
||||||
};
|
|
||||||
typedef Struct_(Binds_PadInputDemo) {
|
|
||||||
U4 pad_state;
|
|
||||||
V3_S2* cube_rot;
|
|
||||||
V3_S2* floor_rot;
|
|
||||||
};
|
|
||||||
internal MipsAtom_(pad_input_demo) atom_info(atom_bind(Binds_PadInputDemo)
|
|
||||||
, atom_reads(R_PadInState, R_CubeRot, R_FloorRot)
|
|
||||||
, atom_writes(R_CubeRot, R_FloorRot)
|
|
||||||
) {
|
|
||||||
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_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),
|
|
||||||
add_si( R_T6, R_T6, 5),
|
|
||||||
store_half(R_T5, R_CubeRot, O_(V3_S2,y)),
|
|
||||||
store_half(R_T6, R_FloorRot, O_(V3_S2,y)),
|
|
||||||
atom_label(exit_pad_left)
|
|
||||||
|
|
||||||
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),
|
|
||||||
add_si( R_T6, R_T6, -5),
|
|
||||||
store_half(R_T5, R_CubeRot, O_(V3_S2,y)),
|
|
||||||
store_half(R_T6, R_FloorRot, O_(V3_S2,y)),
|
|
||||||
atom_label(exit_pad_right)
|
|
||||||
|
|
||||||
mac_yield(),
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef Struct_(Binds_CubeTri) {
|
typedef Struct_(Binds_CubeTri) {
|
||||||
U4 PrimCursor;
|
U4 PrimCursor;
|
||||||
V4_S2* FaceCursor;
|
V4_S2* FaceCursor;
|
||||||
@@ -376,38 +401,288 @@ internal MipsAtom_(sync_primitive_arena) atom_info(atom_bind(Binds_SyncPrimitive
|
|||||||
mac_yield()
|
mac_yield()
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ----- pad_sio_init -----
|
/* ----- pad_bios_snapshot -----
|
||||||
* Boot-time SIO0 init. Caller pins R_T6 = sio_base_addr0.
|
* Per-frame snapshot of one BIOS pad buffer into PadState.
|
||||||
* Issues SIO CTRL=0x0040 (reset), MODE=0x000D, BAUD=0x0088.
|
* Decoder (branch ladder on raw[0] status + raw[1] id):
|
||||||
* (Phase 2 fills the body.)
|
* 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)
|
||||||
*/
|
*/
|
||||||
internal MipsAtom_(pad_sio_init) atom_info(atom_phase(pad_init)
|
enum {
|
||||||
, atom_reads(R_T5, R_PadSioBase)
|
R_PadRaw = R_T0 atom_reg atom_type(U1),
|
||||||
, atom_writes(R_T5)
|
R_PadState = R_T1 atom_reg,
|
||||||
) {
|
R_RawStatus = R_T2 atom_reg,
|
||||||
mac_yield(),
|
R_RawId = R_T3 atom_reg,
|
||||||
};
|
};
|
||||||
|
typedef Struct_(Binds_PadBiosSnapshot) {
|
||||||
/* ----- pad_sio_step -----
|
PadBiosRaw* raw;
|
||||||
* Per-frame bounded raw-SIO transaction. Reads PadState pointers + SIO
|
PadState* state;
|
||||||
* base addresses from Binds_PadSioStep; writes per-port status +
|
};
|
||||||
* buttons + axes into smem.pad[0..1]. (Phase 3 fills the body.)
|
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)
|
||||||
internal MipsAtom_(pad_sio_step) atom_info(atom_bind(Binds_PadSioStep)
|
, atom_writes(R_PadRaw, R_PadState, R_RawStatus, R_RawId, R_T4, R_T5, R_TapePtr)
|
||||||
, atom_reads(R_TapePtr, R_PadSioBase, R_PadState, R_PadStatus, R_PadCountdown)
|
|
||||||
, atom_writes(R_PadStatus, R_PadCountdown)
|
|
||||||
) {
|
) {
|
||||||
|
/* === Bind consumption (3 words): 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) — 3 words. */
|
||||||
|
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) — 3 words. */
|
||||||
|
add_ui(R_T4, R_0, 0xFF), branch_ne(R_RawStatus, R_T4, atom_offset(snap_root, skip_disconnected)), nop,
|
||||||
|
|
||||||
|
atom_label(disconnected) /* === Disconnected body — 9 words. */
|
||||||
|
add_ui(R_T4, R_0, PadStatus_Disconnected),
|
||||||
|
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)),
|
||||||
|
branch_equal(R_0, R_0, atom_offset(disconnected, snap_end)), nop,
|
||||||
|
// TODO(Ed): Lua metaprogram: Support jump instruction here..
|
||||||
|
// jump(atom_offset(disconnected, snap_end)), nop,
|
||||||
|
atom_label(skip_disconnected)
|
||||||
|
|
||||||
|
/* === Case 2: Pending (status == 0 && id == 0) — 3 words.
|
||||||
|
* 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)), nop,
|
||||||
|
|
||||||
|
atom_label(pending) /* === Pending body — 9 words. */
|
||||||
|
add_ui( R_T4, R_0, PadStatus_Pending),
|
||||||
|
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)),
|
||||||
|
branch_equal(R_0, R_0, atom_offset(pending, snap_end)), nop,
|
||||||
|
// TODO(Ed): Lua metaprogram: Support jump instruction here..
|
||||||
|
// jump(atom_offset(pending, snap_end)), nop,
|
||||||
|
|
||||||
|
atom_label(id_dispatch) /* === Case 3-6: ID dispatch — 3 words (id == 0x41 check). */
|
||||||
|
add_ui(R_T4, R_0, 0x41), branch_ne(R_RawId, R_T4, atom_offset(id_dispatch, try_analog_stick)), nop,
|
||||||
|
|
||||||
|
/* === Digital body — 15 words (status, buttons normalize, axes=0x80, id, branch). */
|
||||||
|
add_ui( R_T4, R_0, PadStatus_Digital),
|
||||||
|
store_word( R_T4, R_PadState, O_(PadState,status)),
|
||||||
|
load_half_u(R_T4, R_PadRaw, 2 * S_(U1)),
|
||||||
|
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). */
|
||||||
|
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, 0x41),
|
||||||
|
store_byte( R_T4, R_PadState, O_(PadState,id)),
|
||||||
|
|
||||||
|
branch_equal(R_0, R_0, atom_offset(id_dispatch, snap_end)), nop,
|
||||||
|
// TODO(Ed): Lua metaprogram: Support jump instruction here..
|
||||||
|
// jump(atom_offset(id_dispatch, snap_end)), nop,
|
||||||
|
|
||||||
|
atom_label(try_analog_stick) /* === Case 4: AnalogStick (id == 0x53) — 3 words dispatch. */
|
||||||
|
add_ui(R_T4, R_0, 0x53),
|
||||||
|
branch_ne(R_RawId, R_T4, atom_offset(try_analog_stick, try_analog_pad)), nop,
|
||||||
|
|
||||||
|
atom_label(analog_stick) /* === AnalogStick body — 21 words.
|
||||||
|
* Axes are loaded as two halfwords: raw[6..7] → left_xy (sh at offset 8), raw[4..5] → right_xy (sh at offset 10).
|
||||||
|
* Each lhu is followed by a nop in its MIPS-I load delay slot; the sh consumes the halfword safely. */
|
||||||
|
add_ui( R_T4, R_0, PadStatus_AnalogStick),
|
||||||
|
store_word( R_T4, R_PadState, O_(PadState,status)),
|
||||||
|
load_half_u( R_T4, R_PadRaw, 2 * S_(U1)),
|
||||||
|
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)),
|
||||||
|
load_half_u( R_T4, R_PadRaw, 6 * S_(U1)), /* raw[6..7] = left_x | (left_y << 8) */
|
||||||
|
store_half( R_T4, R_PadState, O_(PadState,left_x)), /* sh at offset 8 → left_x @ 8, left_y @ 9 */
|
||||||
|
load_half_u( R_T4, R_PadRaw, 4 * S_(U1)), /* raw[4..5] = right_x | (right_y << 8) */
|
||||||
|
store_half( R_T4, R_PadState, O_(PadState,right_x)), /* sh at offset 10 → right_x @ 10, right_y @ 11 */
|
||||||
|
add_ui( R_T4, R_0, 0x53),
|
||||||
|
store_byte( R_T4, R_PadState, O_(PadState,id)),
|
||||||
|
branch_equal(R_0, R_0, atom_offset(analog_stick, snap_end)), nop,
|
||||||
|
// TODO(Ed): Lua metaprogram: Support jump instruction here..
|
||||||
|
// jump(atom_offset(analog_stick, snap_end)), nop,
|
||||||
|
|
||||||
|
atom_label(try_analog_pad) /* === Case 5-6: AnalogPad (id & 0xF0 == 0x70) — 4 words dispatch. */
|
||||||
|
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)), nop,
|
||||||
|
|
||||||
|
atom_label(analog_pad) /* === AnalogPad body — 21 words (same shape as AnalogStick with AnalogPad status). */
|
||||||
|
add_ui( R_T4, R_0, PadStatus_AnalogPad),
|
||||||
|
store_word( R_T4, R_PadState, O_(PadState,status)),
|
||||||
|
load_half_u(R_T4, R_PadRaw, 2 * S_(U1)),
|
||||||
|
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)),
|
||||||
|
load_half_u(R_T4, R_PadRaw, 6 * S_(U1)), /* raw[6..7] = left_x | (left_y << 8) */
|
||||||
|
store_half( R_T4, R_PadState, O_(PadState,left_x)), /* sh at offset 8 → left_x @ 8, left_y @ 9 */
|
||||||
|
load_half_u(R_T4, R_PadRaw, 4 * S_(U1)), /* raw[4..5] = right_x | (right_y << 8) */
|
||||||
|
store_half( R_T4, R_PadState, O_(PadState,right_x)), /* sh at offset 10 → right_x @ 10, right_y @ 11 */
|
||||||
|
store_byte( R_RawId, R_PadState, O_(PadState,id)),
|
||||||
|
|
||||||
|
branch_equal(R_0, R_0, atom_offset(analog_pad, snap_end)), nop,
|
||||||
|
// TODO(Ed): Lua metaprogram: Support jump instruction here..
|
||||||
|
// jump(atom_offset(analog_pad, snap_end)), nop,
|
||||||
|
|
||||||
|
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(snap_end)
|
||||||
mac_yield(),
|
mac_yield(),
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ----- pad_apply_input -----
|
/* ----- pad_apply_input -----
|
||||||
* Reads pad[0].buttons + pad[0].left_x; applies the input-semantics
|
* Reads pad[0].buttons + pad[0].left_x;
|
||||||
* deltas to cube_rot.y + floor_rot.y. (Phase 4 fills the body.)
|
* Applies the input-semantics deltas to cube_rot.y + floor_rot.y per spec §"Input semantics":
|
||||||
|
* - 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 (per spec line 137 + Task 3.1b buttons-fix):
|
||||||
|
* 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)
|
internal MipsAtom_(pad_apply_input) atom_info(atom_bind(Binds_PadApplyInput)
|
||||||
, atom_reads(R_T0, R_T1, R_T2, R_T5)
|
, atom_reads(R_T0, R_CubeRot, R_FloorRot, R_T3, R_T4, R_PadStateT5, R_TapePtr)
|
||||||
, atom_writes(R_T1, R_T2)
|
, 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 (per spec line 89).
|
||||||
|
* 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 */
|
||||||
|
branch_equal(R_0, R_0, atom_offset(dead_zone_skip, exit_stick)), nop,
|
||||||
|
/* Fall-through = left_x in [0x70, 0x90] (dead zone); skip analog entirely. */
|
||||||
|
// TODO(Ed): Lua metaprogram: Support jump instruction here..
|
||||||
|
// jump(atom_offset(dead_zone_skip, exit_stick)), nop,
|
||||||
|
|
||||||
|
atom_label(dead_low_active)
|
||||||
|
load_byte_u(R_T3, R_PadStateT5, O_(PadState,left_x)),
|
||||||
|
// add_ui(R_T4, R_0, 0x80), // R_T4 = 0x80 (from BD-slot: dead_zone_low_check).
|
||||||
|
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)),
|
||||||
|
add_u( R_T0, R_T0, R_T4),
|
||||||
|
store_half( R_T0, R_CubeRot, O_(V3_S2,y)),
|
||||||
|
/* R_T4 = floor_delta */
|
||||||
|
shift_aright(R_T4, R_T3, 5),
|
||||||
|
load_half( R_T0, R_FloorRot, O_(V3_S2,y)),
|
||||||
|
add_u( R_T0, R_T0, R_T4),
|
||||||
|
store_half( R_T0, R_FloorRot, O_(V3_S2,y)),
|
||||||
|
|
||||||
|
branch_equal(R_0, R_0, atom_offset(end_low, exit_stick)), nop,
|
||||||
|
// TODO(Ed): Lua metaprogram: Support jump instruction here..
|
||||||
|
// jump(atom_offset(end_low, exit_stick)), nop,
|
||||||
|
|
||||||
|
atom_label(dead_high_active)
|
||||||
|
load_byte_u(R_T3, R_PadStateT5, O_(PadState,left_x)),
|
||||||
|
// add_ui(R_T4, R_0, 0x80), // R_T4 = 0x80 (from BD-slot: dead_zone_high_check)
|
||||||
|
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)),
|
||||||
|
add_u( R_T0, R_T0, R_T4),
|
||||||
|
store_half( R_T0, R_CubeRot, O_(V3_S2,y)),
|
||||||
|
|
||||||
|
shift_aright(R_T4, R_T3, 5), /* R_T4 = floor_delta (signed) */
|
||||||
|
load_half( R_T0, R_FloorRot, O_(V3_S2,y)),
|
||||||
|
add_u( R_T0, R_T0, R_T4),
|
||||||
|
store_half( R_T0, R_FloorRot, O_(V3_S2,y)),
|
||||||
|
|
||||||
|
atom_label(exit_stick)
|
||||||
mac_yield(),
|
mac_yield(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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 */
|
||||||
+7
-18
@@ -180,29 +180,18 @@ function link-modules { param([string[]]$link_modules, [string] $elf, [string[]
|
|||||||
$link_args += ($f_link_pass_through_prefix + $f_link_mapfile + $map)
|
$link_args += ($f_link_pass_through_prefix + $f_link_mapfile + $map)
|
||||||
|
|
||||||
$link_args += ($f_link_pass_through_prefix + $f_link_start_group)
|
$link_args += ($f_link_pass_through_prefix + $f_link_start_group)
|
||||||
|
# raw_sio_pad_poll_20260802 — Task 5.1c surgical library-list trim.
|
||||||
|
# The 16 removed entries (c2, card, cd, comb, ds, gs, gun, hmd, math,
|
||||||
|
# mcrd, mcx, press, sio, snd, spu, tap) had LOAD lines in the map but
|
||||||
|
# ZERO .o files pulled in — they were unused. The 5 kept libraries
|
||||||
|
# (api, c, etc, gpu, gte) are required by the C-side calls in
|
||||||
|
# hello_joypad.c (reset_graph, draw_sync, vsync, etc.).
|
||||||
$libraries = @(
|
$libraries = @(
|
||||||
"api",
|
"api",
|
||||||
"c",
|
"c",
|
||||||
"c2",
|
|
||||||
"card",
|
|
||||||
"cd",
|
|
||||||
"comb",
|
|
||||||
"ds",
|
|
||||||
"etc",
|
"etc",
|
||||||
"gpu",
|
"gpu",
|
||||||
"gs",
|
"gte"
|
||||||
"gte",
|
|
||||||
"gun",
|
|
||||||
"hmd",
|
|
||||||
"math",
|
|
||||||
"mcrd",
|
|
||||||
"mcx",
|
|
||||||
"pad",
|
|
||||||
"press",
|
|
||||||
"sio",
|
|
||||||
"snd",
|
|
||||||
"spu",
|
|
||||||
"tap"
|
|
||||||
)
|
)
|
||||||
foreach ($lib in $libraries) {
|
foreach ($lib in $libraries) {
|
||||||
$link_args += ($f_link_lib + $lib)
|
$link_args += ($f_link_lib + $lib)
|
||||||
|
|||||||
+16
-8
@@ -925,13 +925,21 @@ function M.tokenize_body(body)
|
|||||||
if c == BYTE_COMMA then break end
|
if c == BYTE_COMMA then break end
|
||||||
if c == BYTE_NEWLINE then break end
|
if c == BYTE_NEWLINE then break end
|
||||||
if c == BYTE_SEMI then break end
|
if c == BYTE_SEMI then break end
|
||||||
|
-- Line-comment '// ... \n' (0x2F 0x2F): skip to (and past) the next newline, or to end-of-body.
|
||||||
|
if c == BYTE_SLASH and body:byte(scan + 1) == BYTE_SLASH then
|
||||||
|
local nl = M.find_byte(body, BYTE_NEWLINE, scan)
|
||||||
|
scan = nl and (nl + 1) or (len + 1)
|
||||||
|
-- Block-comment '/* ... */' (0x2F 0x2A): skip to (and past) the matching '*/', or to end-of-body.
|
||||||
|
elseif c == BYTE_SLASH and body:byte(scan + 1) == BYTE_STAR then
|
||||||
|
local close = body:find("*/", scan + 2, true)
|
||||||
|
scan = close and (close + 2) or (len + 1)
|
||||||
-- Group opener bytes (consume the balanced group via the matching reader): '(' = 0x28, '{' = 0x7B, '[' = 0x5B.
|
-- Group opener bytes (consume the balanced group via the matching reader): '(' = 0x28, '{' = 0x7B, '[' = 0x5B.
|
||||||
if c == BYTE_OPEN_PAREN then local _, a = M.read_parens (body, scan); scan = a
|
elseif c == BYTE_OPEN_PAREN then local _, a = M.read_parens (body, scan); scan = a
|
||||||
elseif c == BYTE_OPEN_BRACE then local _, a = M.read_braces (body, scan); scan = a
|
elseif c == BYTE_OPEN_BRACE then local _, a = M.read_braces (body, scan); scan = a
|
||||||
elseif c == BYTE_OPEN_BRACK then local _, a = M.read_brackets (body, scan); scan = a
|
elseif c == BYTE_OPEN_BRACK then local _, a = M.read_brackets (body, scan); scan = a
|
||||||
-- String-literal byte ('"' = 0x22 or '\'' = 0x27): skip past the quoted region in one shot.
|
-- String-literal byte ('"' = 0x22 or '\'' = 0x27): skip past the quoted region in one shot.
|
||||||
elseif c == BYTE_DQUOTE or c == BYTE_SQUOTE then
|
elseif c == BYTE_DQUOTE or c == BYTE_SQUOTE then
|
||||||
scan = M.skip_str_or_cmt(body, scan) + 1
|
scan = (M.skip_str_or_cmt(body, scan) or scan) + 1
|
||||||
else
|
else
|
||||||
scan = scan + 1
|
scan = scan + 1
|
||||||
end
|
end
|
||||||
@@ -1316,13 +1324,13 @@ M.OPERAND_READ_POSITIONS = {
|
|||||||
["sub_s"] = {1, 2, 3},
|
["sub_s"] = {1, 2, 3},
|
||||||
["sub_u"] = {1, 2, 3},
|
["sub_u"] = {1, 2, 3},
|
||||||
["and_i"] = {1, 2},
|
["and_i"] = {1, 2},
|
||||||
["and_u"] = {1, 2, 3},
|
["and"] = {1, 2, 3},
|
||||||
["or_i"] = {1, 2},
|
["or_i"] = {1, 2},
|
||||||
["or_i_self"] = {1},
|
["or_i_self"] = {1},
|
||||||
["or_u"] = {1, 2, 3},
|
["or"] = {1, 2, 3},
|
||||||
["or_u_self"] = {1, 2},
|
["or_self"] = {1, 2},
|
||||||
["xor_i"] = {1, 2},
|
["xor_i"] = {1, 2},
|
||||||
["xor_u"] = {1, 2, 3},
|
["xor"] = {1, 2, 3},
|
||||||
["slt_s"] = {1, 2, 3},
|
["slt_s"] = {1, 2, 3},
|
||||||
["slt_u"] = {1, 2, 3},
|
["slt_u"] = {1, 2, 3},
|
||||||
["slt_si"] = {1, 2},
|
["slt_si"] = {1, 2},
|
||||||
@@ -1473,7 +1481,7 @@ M.INSTRUCTION_LATENCY = {
|
|||||||
["add_s"] = 1, ["add_si"] = 1,
|
["add_s"] = 1, ["add_si"] = 1,
|
||||||
["add_u"] = 1, ["add_u_self"] = 1,
|
["add_u"] = 1, ["add_u_self"] = 1,
|
||||||
["sub_u"] = 1, ["sub_s"] = 1,
|
["sub_u"] = 1, ["sub_s"] = 1,
|
||||||
["and_i"] = 1, ["and_u"] = 1,
|
["and_i"] = 1, ["and"] = 1,
|
||||||
["or_i"] = 1, ["or_i_self"] = 1,
|
["or_i"] = 1, ["or_i_self"] = 1,
|
||||||
["or_u"] = 1, ["or_u_self"] = 1,
|
["or_u"] = 1, ["or_u_self"] = 1,
|
||||||
["xor_i"] = 1, ["xor_u"] = 1,
|
["xor_i"] = 1, ["xor_u"] = 1,
|
||||||
@@ -1952,7 +1960,7 @@ M.GPR_VALUE_RULES = {
|
|||||||
-- Present register-form self variants. They are included here so a
|
-- Present register-form self variants. They are included here so a
|
||||||
-- known value is not needlessly lost when these encoders are used.
|
-- known value is not needlessly lost when these encoders are used.
|
||||||
add_u_self = { op = "add_u", dest = 1, sources = {1, 2}, },
|
add_u_self = { op = "add_u", dest = 1, sources = {1, 2}, },
|
||||||
or_u_self = { op = "or_u", dest = 1, sources = {1, 2}, },
|
or_u_self = { op = "or", dest = 1, sources = {1, 2}, },
|
||||||
shift_lleft_self = { op = "shift_lleft", dest = 1, source = 1, immediate = 2, },
|
shift_lleft_self = { op = "shift_lleft", dest = 1, source = 1, immediate = 2, },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
--- Scanner owns `declaration_comment` and `debug_skip` on each declaration record; this pass projects both forward.
|
--- Scanner owns `declaration_comment` and `debug_skip` on each declaration record; this pass projects both forward.
|
||||||
---
|
---
|
||||||
--- Reads the pre-scanned SourceScan payload from `duffle.scan_source` for `MipsAtomComp_(ac_X)` and `MipsAtomComp_Proc_(ac_X, { body })` declarations,
|
--- Reads the pre-scanned SourceScan payload from `duffle.scan_source` for `MipsAtomComp_(ac_X)` and `MipsAtomComp_Proc_(ac_X, { body })` declarations,
|
||||||
--- then resolves the function-args string from the preceding `FI_ MipsAtom ac_X(...)` declaration via a backward walk.
|
--- then resolves the function-args string from the preceding `FI_ Slice_MipsCode ac_X(...)` declaration via a backward walk.
|
||||||
---
|
---
|
||||||
--- Emits one `<dir_basename>.macs.h` per source with `#define mac_X(sig) \` macros plus `WORD_COUNT(mac_X, N)` entries for downstream offset computation.
|
--- Emits one `<dir_basename>.macs.h` per source with `#define mac_X(sig) \` macros plus `WORD_COUNT(mac_X, N)` entries for downstream offset computation.
|
||||||
---
|
---
|
||||||
@@ -29,7 +29,7 @@ local duffle = dofile(_bootstrap_dir .. "../duffle_paths.lua")
|
|||||||
|
|
||||||
-- Atom component declaration identifiers.
|
-- Atom component declaration identifiers.
|
||||||
local ATOM_COMP_PROC = "MipsAtomComp_Proc_"
|
local ATOM_COMP_PROC = "MipsAtomComp_Proc_"
|
||||||
local MIPS_ATOM = "MipsAtom" -- prefix on the function declaration that wraps an AtomComp_Proc_
|
local MIPS_ATOM = "Slice_MipsCode" -- prefix on the function declaration that wraps an AtomComp_Proc_
|
||||||
|
|
||||||
-- Component-name prefixes.
|
-- Component-name prefixes.
|
||||||
local AC_PREFIX = "ac_" -- arg to MipsAtomComp_(ac_X); the X is the atom name
|
local AC_PREFIX = "ac_" -- arg to MipsAtomComp_(ac_X); the X is the atom name
|
||||||
@@ -97,9 +97,9 @@ local M = {}
|
|||||||
--- Returns the args string (e.g., `"U4 off, U4 code, U1 r, U1 g, U1 b"`) or nil if no function declaration is found.
|
--- Returns the args string (e.g., `"U4 off, U4 code, U1 r, U1 g, U1 b"`) or nil if no function declaration is found.
|
||||||
---
|
---
|
||||||
--- Convention: function form is
|
--- Convention: function form is
|
||||||
--- `FI_ MipsAtom ac_X(args) MipsAtomComp_Proc_(ac_X, { body })`
|
--- `FI_ Slice_MipsCode ac_X(args) MipsAtomComp_Proc_(ac_X, { body })`
|
||||||
--- We find the LAST occurrence of `"ac_X("` before `before_pos` and extract the args from inside the parens.
|
--- We find the LAST occurrence of `"ac_X("` before `before_pos` and extract the args from inside the parens.
|
||||||
--- We then verify the preceding context ends with `MipsAtom`
|
--- We then verify the preceding context ends with `Slice_MipsCode`
|
||||||
--- (the function-decl keyword with possible qualifiers between).
|
--- (the function-decl keyword with possible qualifiers between).
|
||||||
---
|
---
|
||||||
--- @param source string
|
--- @param source string
|
||||||
|
|||||||
Reference in New Issue
Block a user