Skip to content

Stop REST fire2/fire3 leaking onto the other joystick port - #880

Merged
chrisgleissner merged 2 commits into
GideonZ:masterfrom
chrisgleissner:fix/joystick-fire2-fire3-cross-port-leak
Sep 9, 2026
Merged

chrisgleissner merged 2 commits into
GideonZ:masterfrom
chrisgleissner:fix/joystick-fire2-fire3-cross-port-leak

Conversation

@chrisgleissner

@chrisgleissner chrisgleissner commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes #879.

JoystickOutput::apply() mirrors an asserted-low POTX/POTY value into port 1's paddle registers whenever port 2's fire2/fire3 is pressed, and OR's port 2's extra-button state into port 1's mouse-enable line. Holding fire2 or fire3 on one REST joystick port also reads as pressed on the other port's Anykey-style POT read, matching #879. The fix removes the mirror and the OR term; each port's paddle registers now reflect only that port's own fire2/fire3 state.

A second defect: apply_joystick_event() releases an input by updating persistent state only. A tap on the same input that's still mid-hold keeps its overlay bit pressed, which outranks the new persistent state in JoystickOutput::outputSnapshot()'s persistent & overlay combination, so the release has no visible effect for up to one 20ms timer tick. JoystickOutput::cancelRestPort1Overlay/cancelRestPort2Overlay, called from the release branch, clear the released bit's overlay and hold countdown immediately.

Changes

  • joystick_output.cc/.h: removes the cross-port mirror and mouse-enable OR term; adds cancelRestPortXOverlay + cancel_overlay_bits; replaces a literal 7 with a named JOYSTICK_BUTTON_COUNT constant matching input_api.h's INPUT_API_MAX_JOYSTICK_INPUTS.
  • route_input.cc: calls the new cancel methods on release.
  • input_api_state_test.cpp: two host unit tests for the release/tap fix.
  • input_test.py: Anykey-isolation checks collapsed into one parametrized loop; added a check holding fire2 on port 1 and fire3 on port 2 at once (a mirror can't satisfy this by chance the way it can a one-port-at-a-time check); added hardware checks for tap auto-release and for release-in-the-same-batch-as-tap; extended the round-trip and release_all checks to assert POT hardware, not just REST JSON and CIA state; read_joystick_pots now pauses the C64 like its siblings (unpaused, the KERNAL's keyboard-scan IRQ races the CIA1 write within ~20ms); all three pause-then-read helpers now attempt resume() even if pause() itself raises; paired-port checks now pause once via read_joystick_pots_both instead of twice.

Test plan

Both defects verified red/green on an Ultimate 64 Elite (firmware 3.15, real hardware):

  • Cross-port mirror: reverting only joystick_output.cc reproduces U64 REST Joystick input 2nd/3rd firebutton triggering on both joystick ports #879's exact symptom (expected fire2/fire3=False/False, got True/False); fixed, joystick 21/21, contract 7/7, keyboard 19/19 pass.
  • Release/tap race: disabling only the two cancelRestPortXOverlay calls fails the new same-batch check (response still shows fire2 pressed); restored, it passes. Also covered by 2 host unit tests (cd software/api/tests && make input-api-state, 30/30 pass).

u64 and u64ii both build cleanly with the full change set.

Related, not fixed here

usb_hid.cc's mouse-move handler unconditionally writes C64_PADDLE_1_X/C64_PADDLE_1_Y from raw USB mouse position right after JoystickOutput::apply() writes the same registers from REST state, so an attached USB mouse clobbers a REST-held port-1 fire2/fire3. Same register family, a different, uncoordinated writer. No USB mouse was available to verify a fix red/green.

apply() mirrored a pressed POTX/POTY into port 1's paddle registers
whenever port 2's fire2/fire3 was pressed, and OR'd port 2's extra-button
state into port 1's mouse-enable. Pressing fire2/fire3 on one REST
joystick port then showed as pressed on the other port's Anykey-style
POT read too. Fixes GideonZ#879.

Extends tests/e2e/api/input_test.py's joystick Anykey checks with the
cross-port case in both directions, and fixes read_joystick_pots to
pause the C64 before poking CIA1, matching its sibling helpers: left
running, the KERNAL's keyboard-scan IRQ overwrites the paddle-group
select within ~20ms of our write, which made the port argument race
against it. Verified red/green on real u64 hardware (192.168.1.13):
17/17 joystick checks plus contract and keyboard REST suites pass with
the fix, and the same joystick checks correctly fail on the reverted
source.
@chrisgleissner chrisgleissner self-assigned this Sep 9, 2026
@chrisgleissner chrisgleissner changed the title fix(u64): stop REST fire2/fire3 leaking onto the other joystick port Stop REST fire2/fire3 leaking onto the other joystick port Sep 9, 2026
…ystick input

Also hardens the GideonZ#879 regression tests: dedup, pot-hardware checks,
pause/resume safety, and a shared JOYSTICK_BUTTON_COUNT constant.
@chrisgleissner
chrisgleissner merged commit c72a9cd into GideonZ:master Sep 9, 2026
1 check passed
@chrisgleissner

Copy link
Copy Markdown
Collaborator Author

Confirmed by user to work. Merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

U64 REST Joystick input 2nd/3rd firebutton triggering on both joystick ports

1 participant