Skip to content

Read a button on Atari 2600

Reuse the actual machine-specific reader, with its caller, state, ownership and a diagnosed input mistake.

Build the complete caller with make -C atari-2600/patterns/assembly/input/read-action. Press fire once, keep it held, then release and press again. The piece moves on each new press and returns to its starting position after two; holding does not repeat the action. Load the cartridge produced by the Makefile in the named configuration; retain its reset/header setup.

Atari 2600 native caller after accepted input.
Captured from the running caller in the configuration below.

Take the reader

sample.inc is an inline hardware sampler: include it where the caller samples input. It has no RET/RTS and must not be called as a subroutine. It supplies the held state described below; the caller supplies previous-state storage and decides what a new press does.

The reusable reader

sample.inc
    lda $0c                   ; INPT4, active-low fire
    and #$80
    eor #$80
    sta $84                   ; current active-high sample

INPT4 at $0C uses active-low bit 7. The inline sampler normalises it to 0/$80 in A and current byte $84. It changes A and flags; X/Y are untouched.

Calculate a new-press mask as current AND NOT previous, then store current as previous on every update, including release. Test held state for continuous movement and the new-press mask for an action that should happen once. A key already held at the caller’s first sample counts as a press because previous starts at zero.

Caller storage: previous=$81, current=$84; turn=$80, field clock=$82/$83. The sample’s display initialiser and any interrupts have their own ownership requirements; keep them with this complete example.

Change it and recognise a mistake

Change the input mask only with the correct electrical source. Paddle inputs and a second fire arrangement need different readers.

The broken caller feeds held state into the action decision. Keeping the button down advances repeatedly; the native fault run reaches 32 instead of remaining at one. Restore the previous-state comparison and update previous on release as well.

Deliberately broken input/update path

no-edge.asm
; Capability caller: original input/frame service, explicit drawing and sound.
; NTSC 4 KiB cartridge. Display servicing runs every field, but only a new
; fire press advances the turn. Owns all TIA state and RIOT RAM $80-$84.
* = $f000
start:
    sei
    cld
    ldx #$ff
    txs
    lda #0
    ldx #$7f
clear_ram:
    sta $80,x
    dex
    bpl clear_ram
    lda #2
    sta $01                   ; VBLANK: blank, button latching disabled
    jsr init_picture
frame:
    lda #2
    sta $00                   ; VSYNC
    sta $02
    sta $02
    sta $02                   ; three sync lines
    lda #0
    sta $00
    ldx #37
blank_lines:
    sta $02                   ; WSYNC
    dex
    bne blank_lines
    lda #0
    sta $01
    ldx #192
visible_lines:
    sta $02
    dex
    bne visible_lines
    lda #2
    sta $01
    sta $02                   ; overscan line 1; next line contains short work
    !source "../sample.inc"
    lda $84
    nop
    nop
    nop
    nop
    beq no_action
    inc $80                   ; byte turn counter, wraps naturally
    jsr trigger_sound
no_action:
    lda $84
    sta $81
    inc $82                   ; field counter, low
    bne show
    inc $83
show:
    jsr draw_picture
    jsr service_sound
    sta $02                   ; finish overscan line 3 (bounded drawing/audio work)
    ldx #27
overscan:
    sta $02
    dex
    bne overscan              ; 30 overscan lines including the two-line work window
    jmp frame
!source "../picture.inc"
!source "../sound.inc"
    !fill $fffa-*,0
    !word start,start,start
Atari 2600 deliberately broken input caller.
Observed fault; use the working caller to repair it.

The complete caller

Working setup and integration

demo.asm
; Capability caller: original input/frame service, explicit drawing and sound.
; NTSC 4 KiB cartridge. Display servicing runs every field, but only a new
; fire press advances the turn. Owns all TIA state and RIOT RAM $80-$84.
* = $f000
start:
    sei
    cld
    ldx #$ff
    txs
    lda #0
    ldx #$7f
clear_ram:
    sta $80,x
    dex
    bpl clear_ram
    lda #2
    sta $01                   ; VBLANK: blank, button latching disabled
    jsr init_picture
frame:
    lda #2
    sta $00                   ; VSYNC
    sta $02
    sta $02
    sta $02                   ; three sync lines
    lda #0
    sta $00
    ldx #37
blank_lines:
    sta $02                   ; WSYNC
    dex
    bne blank_lines
    lda #0
    sta $01
    ldx #192
visible_lines:
    sta $02
    dex
    bne visible_lines
    lda #2
    sta $01
    sta $02                   ; overscan line 1; next line contains short work
    !source "sample.inc"
    lda $81
    eor #$ff
    and $84
    beq no_action
    inc $80                   ; byte turn counter, wraps naturally
    jsr trigger_sound
no_action:
    lda $84
    sta $81
    inc $82                   ; field counter, low
    bne show
    inc $83
show:
    jsr draw_picture
    jsr service_sound
    sta $02                   ; finish overscan line 3 (bounded drawing/audio work)
    ldx #27
overscan:
    sta $02
    dex
    bne overscan              ; 30 overscan lines including the two-line work window
    jmp frame
!source "picture.inc"
!source "sound.inc"
    !fill $fffa-*,0
    !word start,start,start

For more responsive controls, compare polling, buffering and press edges in input choices.

Verification and sources

Target: NTSC 4 KiB cartridge. These callers were executed with released Emu198x 0.44.1; assembly uses Asm198x 0.0.71. The sample folder contains reproduction instructions and retained observations. The independent checker binds source, native captures and actual output; its rejection tests exercise wrong state, missing files and false audio claims.

This qualifies the named emulator/firmware path. Pitch accuracy, original hardware and other peripherals require their own observations.

Atari, Stella Programmer’s Guide: INPT4, VBLANK/WSYNC and audio registers; MOS 6507/RIOT execution.