Skip to content

Joystick Edge Detection

Detect newly-pressed joystick directions, ignoring held inputs. Essential for grid-based movement and menu navigation.

Code status: the maintained reader is reused by executed Amiga callers; the edge adapter has a 1,024-case CPU/state check. The edge check consumes synthetic held/previous samples; hardware decoding uses the reader’s separate evidence. The surrounding movement examples are adapters; they need their own state, bounds and frame wait. The named callers establish physical port 2 under their A500 configuration, not every peripheral.

Audit execution record · Scope and remaining checks

Overview

Basic joystick reading returns the current state - fine for continuous movement, but problematic for grid-based games where holding a direction shouldn’t cause repeated steps. Edge detection returns only newly pressed directions by comparing current state to the previous frame.

Calling contract

Set A5 to $DFF000, configure CIA-A bit 7 as an input and own physical joystick port 2. Include framework/input-to-sound/input.inc once in writable memory. read_controls returns the held controls in D0.W and controls: up $0100, down $0001, left $0200, right $0002, Fire $0010. It changes D0/D1 and condition codes; other registers survive. Only the low word of D0 is defined.

Keep the horizontal bits from the original JOYDAT sample. The XOR with its shifted copy decodes the vertical bits; applying it to the whole result can turn leftover counter bits into false horizontal input. Physical port 1 uses JOY0DAT and CIA-A bit 6 for Fire; physical port 2 uses JOY1DAT and bit 7.

Code

edges.inc
; Include the maintained input-to-sound/input.inc once as well.
; A5=CUSTOM, CIA-A bit7 configured as input; caller owns physical joystick port2.
; read_joystick_edge samples once and returns new presses in D0.W.
; If already sampled this update, call detect_joystick_edges instead.
; Changes D0/D1/CCR. Other registers survive. State is writable WORDs.
; Zero initial history treats a held control as a first press. To suppress that,
; sample once and copy controls into joy_prev before entering the update loop.
read_joystick_edge:
    bsr read_controls
detect_joystick_edges:
    move.w joy_prev,d1
    not.w d1
    and.w controls,d1
    move.w controls,joy_prev
    move.w d1,d0
    rts
joy_prev: dc.w 0

Usage example (grid-based movement):

68000 assembly
update_player:
            bsr     read_joystick_edge
            tst.w   d0
            beq     .done               ; No new input

            ; Check each direction
            btst    #8,d0               ; Up?
            beq.s   .not_up
            subq.w  #1,player_grid_y    ; Move one cell up
            bra.s   .done
.not_up:
            btst    #0,d0               ; Down?
            beq.s   .not_down
            addq.w  #1,player_grid_y    ; Move one cell down
            bra.s   .done
.not_down:
            btst    #9,d0               ; Left?
            beq.s   .not_left
            subq.w  #1,player_grid_x
            bra.s   .done
.not_left:
            btst    #1,d0               ; Right?
            beq.s   .done
            addq.w  #1,player_grid_x
.done:
            rts

Trade-offs

Aspect Cost
CPU Measure the reader and edge adapter together in your caller
Memory Assembled reader/adapter plus a 2-byte history word
Limitation Requires per-frame update to work correctly

When to use: Grid-based movement, menu navigation, any single-step input.

When to avoid: Continuous movement (shooters, racing) - use basic joystick reading instead.

How It Works

  1. Read current joystick state (decoded)
  2. Invert previous state: NOT previous
  3. AND with current: current AND (NOT previous)
  4. Result: bits set only where current=1 AND previous=0

This gives you a pulse on the first frame of a press, then nothing until released and pressed again.

Comparison

Scenario Basic Read Edge Detection
Press right Returns “right” Returns “right”
Hold right Returns “right” Returns nothing
Release Returns nothing Returns nothing
Press again Returns “right” Returns “right”

Patterns: Joystick Reading, VBlank Game Loop

Vault: Commodore Amiga

History belongs to the caller

Include edges.inc alongside the input reader. read_joystick_edge samples once and returns new presses in D0.W, changing D0/D1 and condition codes. If the frame already sampled input, call detect_joystick_edges instead. The shared controls word supplies that sample.

With joy_prev initially zero, a held control counts as a first press. To suppress that, sample once during initialisation and copy controls to joy_prev. Keep the history while a button remains held; clearing it on a screen transition invents another press. This routine does not clamp the example’s grid coordinates or generate timed repeats.