The Joystick#

The standard CX40 joystick is the simplest input on the system: five switches — four directions and a fire button — and nothing analog. The four directions of both joysticks are read from a single RIOT port, SWCHA; the fire button comes in elsewhere (see Buttons & Console Switches).

One byte, two joysticks#

SWCHA packs both controllers into its eight bits — player 0 in the high nibble, player 1 in the low nibble — each nibble holding right/left/down/up:

Active-low: pressed reads as 0#

The single most important thing about reading switches on the VCS: they are active-low. A direction that’s held reads as 0; a direction that’s released reads as 1. This inverts the intuition — you branch on the bit being clear, not set. To test whether player 0 is pushing up (bit 4):

    lda SWCHA
    and #%00010000   ; isolate bit 4 (P0 up)
    bne NotUp        ; non-zero → bit was 1 → up is NOT pressed
    ; ...fall through: up IS pressed, move the player up
NotUp:

The same shape reads any direction: load SWCHA, mask the one bit, and branch on zero (pressed) versus non-zero (released). Because the bits are independent, diagonals just work — up and right are simply two bits that both happen to be 0.

In Practice#

  • Mask, don’t compare. There’s no “is the value 16?” here; you isolate the bit you care about with AND (or BIT) and branch on zero. Comparing SWCHA to a constant breaks the moment a second switch is also pressed.
  • Read once, reuse. SWCHA is live hardware (a memory-mapped read); load it into A or a variable once per frame and test that copy, rather than re-reading the port for every direction.
  • “Held” vs. “just pressed” are different questions. The port tells you what’s held right now. For a one-shot action — fire on the press, not every frame the button is down — you compare against last frame’s reading and act only on the change. That edge-detection pattern is shared with the buttons, and it’s covered next in Buttons & Console Switches.