Move onto the star. The count changes from 0 to 1, and you hear a short sound. Stay there: the star has gone, so it cannot score again. Press restart and it returns.
Those few seconds bring several useful routines together. Here is the Commodore 64 version, with each part kept small enough to lift into a project of your own.

Run it first
Target: PAL C64. Controls: joystick port 2; Fire restarts. Reset the machine to leave.
With Asm198x available, keep the code-sample folders together and build:
make -C commodore-64/patterns/assembly/framework/collect-and-respondLoad the PRG and type RUN. The sample folder contains the complete program, build recipe and integration notes. The comparison explains the choices across all five versions.
The player moves one cell per six accepted updates. Hold a direction to keep moving. Restart reacts to a new press, so holding it will not trap the player at the starting position.
Connect the pieces
Read the controls once. Calculate the new and released bits from that sample. Update the world, then draw its new state. Call service_sound before the new update so an effect gets its full first frame. That order is visible in the caller:
The jobs in one update
main_loop:
jsr wait_frame
jsr joy_poll
lda joy2
sta controls
jsr detect_edges
jsr service_sound
jsr update_player
jsr draw_screen
inc updatesStartup owns the machine-specific work. It establishes the memory and hardware assumptions that the smaller routines rely on:
Initialise this caller
start:
sei
lda #$7f
sta $dc0d
sta $dd0d
lda $dc0d
lda $dd0d
lda #$35
sta $01 ; RAM at BASIC/KERNAL, I/O visible
lda #<nmi_return
sta $fffa
lda #>nmi_return
sta $fffb
lda $dd02
ora #3
sta $dd02
lda $dd00
ora #3
sta $dd00 ; VIC bank 0
lda #$14
sta $d018 ; screen $0400, character ROM $1000
lda #$1b
sta $d011 ; display on, normal text
lda #$08
sta $d016 ; 40 columns, single-colour text
lda #0
sta $d015
sta $d020
sta $d021
ldx #0
lda #32
clear_screen:
sta $0400, x
sta $0500, x
sta $0600, x
sta $06e8, x
inx
bne clear_screen
jsr joy_init
lda #15
sta $d418
lda #0
ldx #$a0
jsr sid_voice_setup
jsr reset_gameThis program owns CIA1, the screen, zero page $FB–$FD, SID voice 1 and master volume. The caller disables IRQs and banks the ROMs out. The routines together change A/X/Y and flags. Keep competing writers out of those resources; adding a music player or IRQ handler requires an explicit agreement about who writes what.
Move, test and consume
The playfield is 16 × 10 cells. The player begins at (2,5); the star occupies (10,5). The rules keep X between 0 and 15 and Y between 0 and 9. Diagonals move on both axes. Opposing directions are processed left/right, then up/down; choose cancellation or last-direction priority if that better suits your game.
previous, pressed and released belong to input sampling. player_x/player_y, move_delay, item_active and score belong to the rules. Reset preserves input history: clearing it while a button is held would invent another press.
The collision uses half-open boxes. A cell at (x,y) has edges (x,y,x+1,y+1), so merely touching the star’s edge leaves it alone. The routine can also compare larger rectangles or pixel coordinates, provided both boxes use the same units and edge arithmetic does not wrap.
Reusable box overlap
; Half-open boxes [left,top,right,bottom]; all edges unsigned bytes.
; Reject empty/reversed boxes and touching edges. Returns C=1 on overlap.
; Changes A/flags; preserves X/Y. No arithmetic inside the test.
boxes_overlap:
lda rect_a
cmp rect_a+2
bcs boxes_apart
lda rect_a+1
cmp rect_a+3
bcs boxes_apart
lda rect_b
cmp rect_b+2
bcs boxes_apart
lda rect_b+1
cmp rect_b+3
bcs boxes_apart
lda rect_a
cmp rect_b+2
bcs boxes_apart
lda rect_b
cmp rect_a+2
bcs boxes_apart
lda rect_a+1
cmp rect_b+3
bcs boxes_apart
lda rect_b+1
cmp rect_a+3
bcs boxes_apart
sec
rts
boxes_apart:
clc
rts
rect_a: !byte 0,0,0,0
rect_b: !byte 10,5,11,6The update consumes the star before starting feedback. That single state change is what prevents the next frame from collecting it again.
Input edges, movement and collection
; Writable byte state is allocated by demo.asm. Bounds: x=0..15, y=0..9.
; read_controls supplies controls; up/down/left/right/action = 1/2/4/8/16.
; All routines change A/X/Y/flags; no IRQ may write this state.
; Call edges -> service_sound -> update_player -> draw_screen once per frame.
detect_edges:
lda previous
eor #$ff
and controls
sta pressed
lda controls
eor #$ff
and previous
sta released
lda controls
sta previous
rts
reset_game:
lda #2
sta player_x
lda #5
sta player_y
lda #1
sta item_active
lda #0
sta score
sta move_delay
rts
update_player:
lda pressed
and #16
beq movement
jsr reset_game
rts
movement:
lda move_delay
beq move_now
dec move_delay
rts
move_now:
lda #5
sta move_delay
lda controls
and #4
beq move_right
lda player_x
beq move_right
dec player_x
move_right:
lda controls
and #8
beq move_up
lda player_x
cmp #15
beq move_up
inc player_x
move_up:
lda controls
and #1
beq move_down
lda player_y
beq move_down
dec player_y
move_down:
lda controls
and #2
beq test_collection
lda player_y
cmp #9
beq test_collection
inc player_y
test_collection:
lda item_active
beq collection_done
lda player_x
sta rect_a
clc
adc #1
sta rect_a+2
lda player_y
sta rect_a+1
clc
adc #1
sta rect_a+3
jsr boxes_overlap
bcc collection_done
lda #0
sta item_active ; consume BEFORE starting feedback
inc score
jsr trigger_sound
collection_done:
rtsFor this exact one-cell scene, coordinate equality would do less work. The general box routine earns its extra comparisons when objects have different sizes. Box collision and its alternatives explains when a more precise second test helps. A fast object can skip over a target between samples; that calls for a path or swept test as well.
Show the new state
This version uses screen codes and colour RAM. old_x/old_y remember which cell to erase. The item is drawn before the player, so the player wins the draw order when they overlap.
Draw, erase and show the count
; Draw one character cell. Input A=screen code, X=x, Y=y; destroys A/X/Y.
; Caller reserves zero page $fb-$fe. Screen $0400, character ROM, colour RAM.
put_cell:
pha
txa
clc
adc #10
sta $fd
lda #<$04f0 ; row 6 of the 40-column screen
sta $fb
lda #>$04f0
sta $fc
row_address:
cpy #0
beq row_ready
clc
lda $fb
adc #40
sta $fb
bcc row_no_carry
inc $fc
row_no_carry:
dey
bne row_address
row_ready:
ldy $fd
pla
sta ($fb),y
lda $fc
clc
adc #$d4
sta $fc
lda #1
sta ($fb),y
rts
draw_screen:
lda #32
ldx old_x
ldy old_y
jsr put_cell
ldx #10
ldy #5
lda #32
jsr put_cell
lda item_active
beq draw_player
lda #42 ; asterisk: collectible
ldx #10
ldy #5
jsr put_cell
draw_player:
lda #0 ; @ in the C64 screen-code set
ldx player_x
ldy player_y
jsr put_cell
lda player_x
sta old_x
lda player_y
sta old_y
lda score
clc
adc #48
sta $045a
lda #1
sta $d85a
rtsPainting a blank over the old position works because the background is empty. With a map behind the player, restore its tile. With overlapping objects, redraw the affected scene or choose another renderer. Keep that decision in the drawing routine so the rules can stay useful.
Make the event audible
On this machine, SID voice 1 plays while the loop continues; a frame counter releases its gate. The count also changes visibly, so sound carries useful feedback without being the only signal.
Collection sound
; Non-blocking SID voice-1 gate. Master volume belongs to demo startup.
; Changes A/X/flags. Call service_sound once per frame BEFORE update_player.
trigger_sound:
lda #$22
ldx #$4b
jsr sid_note_on
lda #8
sta sound_ticks
rts
service_sound:
lda sound_ticks
beq sound_done
dec sound_ticks
bne sound_done
jsr sid_note_off
sound_done:
rtsTake it into your game
Copy the routine you need, its state and its maintained includes. Read the declaration and caller together: the caller shows initialisation, call frequency and ownership. The sample README lists register effects and resource limits. Preserve the relative include layout, or update those paths deliberately when moving files.
These examples use whole-cell movement and a frame-based repeat counter. Fractional motion, scrolling, multiple items and sound priorities are useful next steps when a game needs them. A 50 Hz and a 60 Hz machine give this counter different wall-clock speeds.
Code status: assembled and emulator-executed for the named configuration. Checks drive real input, exercise all four bounds, reject edge-only contact, collect once, restart while held and capture the picture and PCM. A broken collection caller must fail the same guard. The execution record identifies the exact sources, binary, emulator and firmware; it establishes this caller rather than every feature of the machine.
Read the execution record · Understand the frame wait · Read the controls · Explore the sound routine
For several bullets, enemies or pickups at once, reusable object slots add bounded acquire/release operations and a lifetime pass without shifting live neighbours.
Game states and restart separate a fresh session from resuming a paused one, while preserving input history.