diff --git a/code/hello_joypad/gen/hello_joypad.offsets.h b/code/hello_joypad/gen/hello_joypad.offsets.h index f9831b6..359b63f 100644 --- a/code/hello_joypad/gen/hello_joypad.offsets.h +++ b/code/hello_joypad/gen/hello_joypad.offsets.h @@ -25,17 +25,17 @@ enum { atom_offset_bounds_chk_floor_f3_face_exit = _atom_offset_bounds_chk_floor_f3_face_exit, }; -// --- atom: pad_bios_snapshot (88 words) --- +// --- atom: pad_bios_snapshot (78 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_snap_root_skip_disconnected 8 +#define _atom_offset_disconnected_snap_end 60 +#define _atom_offset_case_2_id_dispatch 8 +#define _atom_offset_pending_snap_end 50 +#define _atom_offset_id_dispatch_try_analog_stick 11 +#define _atom_offset_id_dispatch_snap_end 37 +#define _atom_offset_try_analog_stick_try_analog_pad 12 +#define _atom_offset_analog_stick_snap_end 23 +#define _atom_offset_try_analog_pad_try_unsupported 11 #define _atom_offset_analog_pad_snap_end 9 enum { diff --git a/code/hello_joypad/hello_joypad.c b/code/hello_joypad/hello_joypad.c index 85ba583..9c97b52 100644 --- a/code/hello_joypad/hello_joypad.c +++ b/code/hello_joypad/hello_joypad.c @@ -278,16 +278,16 @@ void update(PrimitiveArena* pa, U4* ordering_buf) tb.used = 0; tb_scope_run(& tb) { /* BIOS-owned polling: per-frame snapshot of both ports. */ tb_emit_(pad_bios_snapshot); - tb_data_(Binds_PadBiosSnapshot.raw, & smem.pad_raw[0]); - tb_data_(Binds_PadBiosSnapshot.state, & smem.pad[0]); + tb_data_(raw, & smem.pad_raw[0]); + tb_data_(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]); + tb_data_(raw, & smem.pad_raw[1]); + tb_data_(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); + tb_data_(state, & smem.pad[0]); + tb_data_(cube_rot, & smem.cube.rot); + tb_data_(floor_rot, & smem.floor.rot); } } diff --git a/code/hello_joypad/hello_joypad.tape.c b/code/hello_joypad/hello_joypad.tape.c index ab7f1b6..7cc7159 100644 --- a/code/hello_joypad/hello_joypad.tape.c +++ b/code/hello_joypad/hello_joypad.tape.c @@ -437,20 +437,22 @@ internal MipsAtom_(pad_bios_snapshot) atom_info(atom_bind(Binds_PadBiosSnapshot) , atom_reads( R_PadRaw, R_PadState, R_RawStatus, R_RawId, R_T4, R_T5, R_TapePtr) , atom_writes(R_PadRaw, R_PadState, R_RawStatus, R_RawId, R_T4, R_T5, R_TapePtr) ) { - /* === Bind consumption (3 words): T0 = raw, T1 = state, advance R_TapePtr by 8. */ + /* === Bind consumption: T0 = raw, T1 = state, advance R_TapePtr by 8. */ load_word(R_PadRaw, R_TapePtr, O_(Binds_PadBiosSnapshot,raw)), load_word(R_PadState, R_TapePtr, O_(Binds_PadBiosSnapshot,state)), add_ui_self( R_TapePtr, S_(Binds_PadBiosSnapshot)), - /* === Read raw[0] (status) + raw[1] (id) — 3 words. */ + /* === Read raw[0] (status) + raw[1] (id) */ load_byte_u(R_RawStatus, R_PadRaw, 0), load_byte_u(R_RawId, R_PadRaw, 1), -atom_label(snap_root) /* === Case 1: Disconnected (status == 0xFF) — 3 words. */ - add_ui(R_T4, R_0, 0xFF), branch_ne(R_RawStatus, R_T4, atom_offset(snap_root, skip_disconnected)), nop, +atom_label(snap_root) /* === Case 1: Disconnected (status == 0xFF). */ + add_ui(R_T4, R_0, 0xFF), branch_ne(R_RawStatus, R_T4, atom_offset(snap_root, skip_disconnected)), + /* BD-slot: pre-compute PadStatus_Disconnected. Branch reads R_T4=0xFF in EX before this WB completes. + * If branch NOT taken (fall through to pending/id_dispatch), R_T4 is overwritten by the next case body's add_ui — harmless. */ -atom_label(disconnected) /* === Disconnected body — 9 words. */ - add_ui(R_T4, R_0, PadStatus_Disconnected), +atom_label(disconnected) /* === Disconnected body. */ + /* R_T4 = PadStatus_Disconnected from snap_root BD-slot. */ store_word(R_T4, R_PadState, O_(PadState,status)), store_half(R_0, R_PadState, O_(PadState,buttons)), /* axes = 0x80808080 (centered) — single sw writes the 4-byte axes block at offset 8 (left_x, left_y, right_x, right_y). */ @@ -462,13 +464,15 @@ atom_label(disconnected) /* === Disconnected body — 9 words. */ // jump(atom_offset(disconnected, snap_end)), nop, atom_label(skip_disconnected) - /* === Case 2: Pending (status == 0 && id == 0) — 3 words. + /* === Case 2: Pending (status == 0 && id == 0) * Combined check: if (status | id) != 0 then skip to id_dispatch. * Falls through to the Pending case only when both are zero. */ - or_u_self(R_RawStatus, R_RawId), branch_ne(R_RawStatus, R_0, atom_offset(case_2, id_dispatch)), nop, + or_u_self(R_RawStatus, R_RawId), branch_ne(R_RawStatus, R_0, atom_offset(case_2, id_dispatch)), + /* BD-slot: pre-compute PadStatus_Pending. Branch reads R_RawStatus in EX before this WB completes. + * If branch NOT taken (fall through to id_dispatch), R_T4 is overwritten by the digital/analog body add_ui — harmless. */ -atom_label(pending) /* === Pending body — 9 words. */ - add_ui( R_T4, R_0, PadStatus_Pending), +atom_label(pending) /* === Pending body */ + /* R_T4 = PadStatus_Pending from case_2 BD-slot. */ store_word(R_T4, R_PadState, O_(PadState,status)), store_half(R_0, R_PadState, O_(PadState,buttons)), /* axes = 0x80808080 (centered) — single sw writes the 4-byte axes block at offset 8 (left_x, left_y, right_x, right_y). */ @@ -479,21 +483,23 @@ atom_label(pending) /* === Pending body — 9 words. */ // 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, +atom_label(id_dispatch) /* === Case 3-6: ID dispatch */ + add_ui(R_T4, R_0, 0x41), branch_ne(R_RawId, R_T4, atom_offset(id_dispatch, try_analog_stick)), + /* BD-slot: pre-compute PadStatus_Digital. Branch reads R_RawId in EX before this WB completes. + * If branch NOT taken (fall through to try_analog_stick), R_T4 is overwritten by the analog body add_ui. */ - /* === Digital body — 15 words (status, buttons normalize, axes=0x80, id, branch). */ - add_ui( R_T4, R_0, PadStatus_Digital), + /* === Digital body (status, buttons normalize, axes=0x80, id, branch. */ + /* R_T4 = PadStatus_Digital from id_dispatch BD-slot. */ store_word( R_T4, R_PadState, O_(PadState,status)), load_half_u(R_T4, R_PadRaw, 2 * S_(U1)), - nop, + /* Fill R_T4's load-delay slot with the 0x80808080 axes constant into R_T5 + * (R_T5 is dead on this path; it's only consumed at the analog_pad range check). */ + load_upper_i(R_T5, 0x8080), or_i_self(R_T5, 0x8080), nor_u( R_T4, R_T4, R_0), /* raw_buttons is already in host bit order; no swap needed */ store_half( R_T4, R_PadState, O_(PadState,buttons)), /* axes = 0x80808080 (centered) — single sw writes the 4-byte axes block at offset 8 (left_x, left_y, right_x, right_y). */ - load_upper_i(R_T4, 0x8080), - or_i_self( R_T4, 0x8080), - store_word( R_T4, R_PadState, O_(PadState,left_x)), + store_word( R_T5, R_PadState, O_(PadState,left_x)), add_ui( R_T4, R_0, 0x41), store_byte( R_T4, R_PadState, O_(PadState,id)), @@ -501,49 +507,47 @@ atom_label(id_dispatch) /* === Case 3-6: ID dispatch — 3 words (id == 0x41 che // 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(try_analog_stick) /* === Case 4: AnalogStick (id == 0x53)*/ + add_ui(R_T4, R_0, 0x53), branch_ne(R_RawId, R_T4, atom_offset(try_analog_stick, try_analog_pad)), + /* BD-slot: pre-compute PadStatus_AnalogStick. Branch reads R_RawId in EX before this WB completes. + * If branch NOT taken (fall through to try_analog_pad), R_T4 is overwritten by the analog_pad body add_ui. */ -atom_label(analog_stick) /* === AnalogStick body — 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), +atom_label(analog_stick) /* === AnalogStick body + * Axes are loaded as two halfwords: raw[6..7] → left_xy (sh at offset 8), raw[4..5] → right_xy (sh at offset 10). + * R_T5 holds left_xy / id-value in turn (it's dead on this path — only consumed at the analog_pad range check). */ + /* R_T4 = PadStatus_AnalogStick from try_analog_stick BD-slot. */ store_word( R_T4, R_PadState, O_(PadState,status)), - load_half_u( R_T4, R_PadRaw, 2 * S_(U1)), - nop, - nor_u( R_T4, R_T4, R_0), /* raw_buttons is already in host bit order; no swap needed */ + load_half_u( R_T4, R_PadRaw, 2 * S_(U1)), /* R_T4 = raw_buttons */ + load_half_u( R_T5, R_PadRaw, 6 * S_(U1)), /* R_T5 = left_xy; fills R_T4's load-delay slot (doesn't read R_T4) */ + nor_u( R_T4, R_T4, R_0), /* R_T4 = ~raw_buttons */ store_half( R_T4, R_PadState, O_(PadState,buttons)), - load_half_u( R_T4, R_PadRaw, 6 * S_(U1)), /* raw[6..7] = left_x | (left_y << 8) */ - nop, - 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) */ - nop, - 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)), + load_half_u( R_T4, R_PadRaw, 4 * S_(U1)), /* R_T4 = right_xy; fills R_T5's load-delay slot */ + store_half( R_T5, R_PadState, O_(PadState,left_x)), /* R_T5 settled, store left_xy */ + store_half( R_T4, R_PadState, O_(PadState,right_x)), + add_ui( R_T5, R_0, 0x53), /* R_T5 = id value (clobbers left_xy, already stored) */ + store_byte( R_T5, R_PadState, O_(PadState,id)), 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. */ +atom_label(try_analog_pad) /* === Case 5-6: AnalogPad (id & 0xF0 == 0x70) */ and_i( R_T4, R_RawId, 0xF0), - add_ui( R_T5, R_0, 0x70), - branch_ne(R_T4, R_T5, atom_offset(try_analog_pad, try_unsupported)), nop, + add_ui( R_T5, R_0, 0x70), + branch_ne(R_T4, R_T5, atom_offset(try_analog_pad, try_unsupported)), + /* BD-slot: pre-compute PadStatus_AnalogPad. Branch reads R_T4 in EX before this WB completes. + * If branch NOT taken (fall through to try_unsupported), R_T4 is overwritten by the unsupported body add_ui. */ -atom_label(analog_pad) /* === AnalogPad body — 21 words (same shape as AnalogStick with AnalogPad status). */ - add_ui( R_T4, R_0, PadStatus_AnalogPad), +atom_label(analog_pad) /* === AnalogPad body + * Same shape as AnalogStick with AnalogPad status. R_T5 holds left_xy (it's dead on this path). */ + /* R_T4 = PadStatus_AnalogPad from try_analog_pad BD-slot. */ store_word( R_T4, R_PadState, O_(PadState,status)), - load_half_u(R_T4, R_PadRaw, 2 * S_(U1)), - nop, - nor_u( R_T4, R_T4, R_0), /* raw_buttons is already in host bit order; no swap needed */ + load_half_u(R_T4, R_PadRaw, 2 * S_(U1)), /* R_T4 = raw_buttons */ + load_half_u(R_T5, R_PadRaw, 6 * S_(U1)), /* R_T5 = left_xy; fills R_T4's load-delay slot */ + nor_u( R_T4, R_T4, R_0), /* R_T4 = ~raw_buttons */ store_half( R_T4, R_PadState, O_(PadState,buttons)), - load_half_u(R_T4, R_PadRaw, 6 * S_(U1)), /* raw[6..7] = left_x | (left_y << 8) */ - nop, - 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) */ - nop, - store_half( R_T4, R_PadState, O_(PadState,right_x)), /* sh at offset 10 → right_x @ 10, right_y @ 11 */ + load_half_u(R_T4, R_PadRaw, 4 * S_(U1)), /* R_T4 = right_xy; fills R_T5's load-delay slot */ + store_half( R_T5, R_PadState, O_(PadState,left_x)), /* R_T5 settled, store left_xy */ + store_half( R_T4, R_PadState, O_(PadState,right_x)), store_byte( R_RawId, R_PadState, O_(PadState,id)), branch_equal(R_0, R_0, atom_offset(analog_pad, snap_end)), nop, @@ -555,8 +559,7 @@ atom_label(try_unsupported) /* === Case 7: Unsupported — fall through from the 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), + 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)), @@ -568,7 +571,7 @@ atom_label(snap_end) /* ----- pad_apply_input ----- * Reads pad[0].buttons + pad[0].left_x; - * Applies the input-semantics deltas to cube_rot.y + floor_rot.y per spec §"Input semantics": + * Applies the input-semantics deltas to cube_rot.y + floor_rot.y: * - D-pad Left: cube_rot.y += 30, floor_rot.y += 5 * - D-pad Right: cube_rot.y -= 30, floor_rot.y -= 5 * - Analog stick X (dead zone 0x70..0x90): @@ -630,7 +633,7 @@ internal MipsAtom_(pad_apply_input) atom_info(atom_bind(Binds_PadApplyInput) 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). + /* Analog left-stick X: dead zone 0x70..0x90. * Cube delta = (0x80 - left_x) >> 2; floor delta = (0x80 - left_x) >> 5. */ load_byte_u(R_T3, R_PadStateT5, O_(PadState,left_x)), @@ -653,9 +656,10 @@ atom_label(dead_check_upper) // 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 */ + /* R_T3 = left_x (from line 632 lbu; not clobbered between dead_zone_low_check branch + its BD-slot `add_ui R_T4, 0x80`). + * The earlier `load_byte_u(R_T3, ...)` reload was redundant and introduced a load-use hazard on the next `sub_u`. + * R_T4 = 0x80 from the BD-slot of `dead_zone_low_check`'s branch_ne. */ + sub_u( R_T3, R_T4, R_T3), /* R_T3 = 0x80 - left_x */ /* delta = 0x80 - left_x (positive). */ /* R_T4 = cube_delta */ @@ -664,10 +668,10 @@ atom_label(dead_low_active) nop, 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), + /* R_T4 = floor_delta — moved into the load-delay slot of the floor load below (fills the 1-instruction gap; + * doesn't read R_T0; R_T4 settles by the subsequent add_u). */ load_half( R_T0, R_FloorRot, O_(V3_S2,y)), - nop, + shift_aright(R_T4, R_T3, 5), add_u( R_T0, R_T0, R_T4), store_half( R_T0, R_FloorRot, O_(V3_S2,y)), @@ -676,9 +680,10 @@ atom_label(dead_low_active) // 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), + /* R_T3 = left_x (from line 641 lbu in dead_check_upper; not clobbered between dead_zone_high_check branch + its BD-slot `add_ui R_T4, 0x80`). + * The earlier `load_byte_u(R_T3, ...)` reload was redundant and introduced a load-use hazard on the next `sub_u`. + * R_T4 = 0x80 from the BD-slot of `dead_zone_high_check`'s branch_ne. */ + sub_u( R_T3, R_T4, R_T3), /* delta = 0x80 - left_x (signed negative). */ shift_aright(R_T4, R_T3, 2), /* R_T4 = cube_delta (signed) */ @@ -687,9 +692,9 @@ atom_label(dead_high_active) 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) */ + /* R_T4 = floor_delta (signed) — moved into the load-delay slot of the floor load below. */ load_half( R_T0, R_FloorRot, O_(V3_S2,y)), - nop, + shift_aright(R_T4, R_T3, 5), add_u( R_T0, R_T0, R_T4), store_half( R_T0, R_FloorRot, O_(V3_S2,y)),