Build the complete caller with make -C sega-master-system/patterns/assembly/input/read-action. Press button 1 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.

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
in a,($dc)
cpl
and $10
ld b,aIN ($DC), complement and mask $10 returns button 1 as 0/$10 in B. AF/B change. The caller does not change pad I/O direction mid-read.
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=$C001; turn=$C000, field clock=$C002/$C003; stack below $C3F0. 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
Combine direction/button bits into one sample before calculating edges. Light guns and paddles 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
; Original capability caller: owns VDP, one audio channel and declared RAM.
VDP_DATA equ $be
VDP_CTRL equ $bf
TURNS equ $c000
SOUND_TICKS equ $c005
org 0
jp start
defs $66-$,0
retn
defs $100-$,0
start:
di
ld sp,$c3f0
xor a
ld ($c000),a
ld ($c001),a
ld ($c002),a
ld ($c003),a
call init_picture
main:
call wait_frame
ld hl,($c002)
inc hl
ld ($c002),hl
include "../sample.inc"
ld a,($c001)
ld a,b
nop
jr z,no_action
ld a,($c000)
inc a
ld ($c000),a
call trigger_sound
no_action:
ld a,b
ld ($c001),a
call draw_picture
call service_sound
jp main
wait_frame:
in a,($bf)
wait_new:
in a,($bf)
and $80
jr z,wait_new
ret
include "../picture.inc"
include "../sound.inc"
defs $7ff0-$,0
db "TMR SEGA"
db 0,0,0,0,0,0,0,$4c
The complete caller
Working setup and integration
; Original capability caller: owns VDP, one audio channel and declared RAM.
VDP_DATA equ $be
VDP_CTRL equ $bf
TURNS equ $c000
SOUND_TICKS equ $c005
org 0
jp start
defs $66-$,0
retn
defs $100-$,0
start:
di
ld sp,$c3f0
xor a
ld ($c000),a
ld ($c001),a
ld ($c002),a
ld ($c003),a
call init_picture
main:
call wait_frame
ld hl,($c002)
inc hl
ld ($c002),hl
include "sample.inc"
ld a,($c001)
cpl
and b
jr z,no_action
ld a,($c000)
inc a
ld ($c000),a
call trigger_sound
no_action:
ld a,b
ld ($c001),a
call draw_picture
call service_sound
jp main
wait_frame:
in a,($bf)
wait_new:
in a,($bf)
and $80
jr z,wait_new
ret
include "picture.inc"
include "sound.inc"
defs $7ff0-$,0
db "TMR SEGA"
db 0,0,0,0,0,0,0,$4cFor more responsive controls, compare polling, buffering and press edges in input choices.
Verification and sources
Target: NTSC stock Master System cartridge configuration. 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.
Sega, Master System Software Reference Manual: I/O port $DC, controller interface, VDP status and PSG.