Level Complete
The game detects when all creatures have been saved or lost. A new STATE_LOST handles creatures that fall off the screen, and the panel changes colour to show the result — green for a win, red for a loss.
Creatures walk, fall, and reach the exit — but the game never ends. Some creatures fall off the bottom of the screen and vanish into nothing. In this unit, we add a proper end condition: when every creature has been saved or lost, the level is complete.

The panel shows “0/3” and the game runs until all three creatures are accounted for. When the level ends, the panel turns green (all saved) or red (some lost).
Lost Creatures
Until now, creatures that fell below the screen height simply resumed walking — a placeholder behaviour. Now they get a proper state:
STATE_LOST equ 3
When a falling creature’s y position exceeds SCREEN_HEIGHT - SPRITE_HEIGHT, it transitions to STATE_LOST. Its sprite is hidden (just like saved creatures) and a lost_count variable increments:
.do_fall:
move.w CR_Y(a2),d0
add.w #FALL_SPEED,d0
cmp.w #SCREEN_HEIGHT-SPRITE_HEIGHT,d0
blt.s .fall_ok
; Fell below screen — creature is lost
move.w #STATE_LOST,CR_STATE(a2)
move.w #0,CR_Y(a2)
addq.w #1,lost_count
bra.s .done
The creature’s y is reset to 0 and its state prevents any further processing. write_sprite_pos already hides sprites for saved creatures — it now also hides lost ones.
Level Complete Detection
Every frame, the main loop checks whether all creatures have been accounted for:
; --- Check level complete ---
move.w game_state,d0
cmp.w #GAME_COMPLETE,d0
beq.s .skip_check
move.w saved_count,d0
add.w lost_count,d0
cmp.w #NUM_CREATURES,d0
blt.s .skip_check
; All creatures accounted for
move.w #GAME_COMPLETE,game_state
bsr draw_result
.skip_check:
The check is simple arithmetic: saved_count + lost_count >= NUM_CREATURES. Every creature must end up in one category or the other. Once the total matches, the game state changes to GAME_COMPLETE and the result is displayed.
The game_state check at the top prevents draw_result from being called every frame after completion — it only fires once.
Runtime Copper Patching
The result is communicated visually by changing the panel’s background colour. This works by writing directly to a value inside the Copper list:
;──────────────────────────────────────────────────────────────
; draw_result — Change panel colour based on outcome
;──────────────────────────────────────────────────────────────
draw_result:
move.w saved_count,d0
cmp.w #NUM_CREATURES,d0
bne.s .not_win
; All saved — green
move.w #COLOUR_WIN,panel_bg_val
rts
.not_win:
; Some lost — red
move.w #COLOUR_LOSE,panel_bg_val
rts
The Copper list contains a labelled value for the panel background:
dc.w COLOR00
panel_bg_val:
dc.w COLOUR_PANEL_BG ; Patched at runtime
Writing a new colour value to panel_bg_val changes what the Copper outputs next frame. No register writes, no timing tricks — the Copper reads the new value on its next pass. Green ($0362) for a win, red ($0622) for a loss.
This is runtime Copper patching: the CPU modifies the Copper program while it runs. The Copper re-reads its list every frame, so changes take effect immediately.
Game States
The game now has two states:
GAME_PLAYING equ 0
GAME_COMPLETE equ 1
During GAME_PLAYING, creatures are processed normally. When GAME_COMPLETE is set, the creature loop still runs (sprites need position updates) but the level-complete check is skipped. The result colours remain on screen until the program is restarted.
The Complete Code
;──────────────────────────────────────────────────────────────
; EXODUS - A terrain puzzle for the Commodore Amiga
; Unit 14: Level Complete
;
; The game detects when all creatures have been saved or lost.
; A result message appears in the panel when the level ends.
;──────────────────────────────────────────────────────────────
;══════════════════════════════════════════════════════════════
; TWEAKABLE VALUES
;══════════════════════════════════════════════════════════════
COLOUR_SKY_DEEP equ $0016
COLOUR_SKY_UPPER equ $0038
COLOUR_SKY_MID equ $005B
COLOUR_SKY_LOWER equ $007D
COLOUR_SKY_HORIZON equ $009E
COLOUR_TERRAIN equ $0741
COLOUR_PANEL_BG equ $0223
COLOUR_PANEL_FG equ $0FFF
COLOUR_WIN equ $0362
COLOUR_LOSE equ $0622
COLOUR_SPR0_1 equ $0FFF
COLOUR_SPR0_2 equ $0F80
COLOUR_SPR0_3 equ $0000
CREATURE_SPEED equ 1
FALL_SPEED equ 2
FOOT_OFFSET_X equ 8
FOOT_OFFSET_Y equ 12
STEP_PERIOD equ 400
STEP_VOLUME equ 48
NUM_CREATURES equ 3
; Creature data table offsets
CR_X equ 0
CR_Y equ 2
CR_DX equ 4
CR_STATE equ 6
CR_STEP equ 8
CR_SIZE equ 10
STATE_WALKING equ 0
STATE_FALLING equ 1
STATE_SAVED equ 2
STATE_LOST equ 3
; Game states
GAME_PLAYING equ 0
GAME_COMPLETE equ 1
; Exit zone
EXIT_X equ 280
EXIT_Y equ 108
EXIT_W equ 24
EXIT_H equ 12
; Panel
PANEL_Y equ 200 ; Scanline where panel starts
PANEL_SAVED_X equ 144 ; X position for saved digit (byte-aligned)
PANEL_SLASH_X equ 152 ; X position for slash
PANEL_TOTAL_X equ 160 ; X position for total digit
PANEL_DIGIT_Y equ 4 ; Y offset within panel bitplane
; Terrain
GROUND_L_X equ 0
GROUND_L_Y equ 152
GROUND_L_W equ 128
GROUND_L_H equ 48
GROUND_R_X equ 128
GROUND_R_Y equ 120
GROUND_R_W equ 192
GROUND_R_H equ 80
PLATFORM_X equ 24
PLATFORM_Y equ 104
PLATFORM_W equ 72
PLATFORM_H equ 8
;══════════════════════════════════════════════════════════════
; DISPLAY CONSTANTS
;══════════════════════════════════════════════════════════════
SCREEN_WIDTH equ 320
SCREEN_HEIGHT equ 200 ; Game area only (panel below)
BYTES_PER_ROW equ SCREEN_WIDTH/8
BITPLANE_SIZE equ BYTES_PER_ROW*SCREEN_HEIGHT
PANEL_HEIGHT equ 56
PANEL_SIZE equ BYTES_PER_ROW*PANEL_HEIGHT
SPRITE_HEIGHT equ 12
STEP_INTERVAL equ 8
;══════════════════════════════════════════════════════════════
; HARDWARE REGISTERS
;══════════════════════════════════════════════════════════════
CUSTOM equ $dff000
DMACON equ $096
INTENA equ $09a
INTREQ equ $09c
COP1LC equ $080
COPJMP1 equ $088
BPLCON0 equ $100
BPLCON1 equ $102
BPLCON2 equ $104
BPL1MOD equ $108
DIWSTRT equ $08e
DIWSTOP equ $090
DDFSTRT equ $092
DDFSTOP equ $094
BPL1PTH equ $0e0
BPL1PTL equ $0e2
SPR0PTH equ $120
SPR0PTL equ $122
SPR1PTH equ $124
SPR1PTL equ $126
SPR2PTH equ $128
SPR2PTL equ $12a
COLOR00 equ $180
COLOR01 equ $182
COLOR17 equ $1a2
COLOR18 equ $1a4
COLOR19 equ $1a6
VPOSR equ $004
AUD0LC equ $0a0
AUD0LEN equ $0a4
AUD0PER equ $0a6
AUD0VOL equ $0a8
;══════════════════════════════════════════════════════════════
; CODE (Chip RAM)
;══════════════════════════════════════════════════════════════
section code,code_c
start:
lea CUSTOM,a5
move.w #$7fff,INTENA(a5)
move.w #$7fff,INTREQ(a5)
move.w #$7fff,DMACON(a5)
; --- Initialise creatures ---
lea creatures,a0
move.w #80,CR_X(a0)
move.w #92,CR_Y(a0)
move.w #CREATURE_SPEED,CR_DX(a0)
move.w #STATE_WALKING,CR_STATE(a0)
move.w #0,CR_STEP(a0)
move.w #16,CR_X+CR_SIZE(a0)
move.w #140,CR_Y+CR_SIZE(a0)
move.w #CREATURE_SPEED,CR_DX+CR_SIZE(a0)
move.w #STATE_WALKING,CR_STATE+CR_SIZE(a0)
move.w #4,CR_STEP+CR_SIZE(a0)
move.w #160,CR_X+CR_SIZE*2(a0)
move.w #108,CR_Y+CR_SIZE*2(a0)
move.w #CREATURE_SPEED,CR_DX+CR_SIZE*2(a0)
move.w #STATE_WALKING,CR_STATE+CR_SIZE*2(a0)
move.w #2,CR_STEP+CR_SIZE*2(a0)
move.w #0,saved_count
move.w #0,lost_count
move.w #GAME_PLAYING,game_state
; --- Draw terrain ---
move.w #GROUND_L_X,d0
move.w #GROUND_L_Y,d1
move.w #GROUND_L_W,d2
move.w #GROUND_L_H,d3
bsr draw_rect
move.w #GROUND_R_X,d0
move.w #GROUND_R_Y,d1
move.w #GROUND_R_W,d2
move.w #GROUND_R_H,d3
bsr draw_rect
move.w #PLATFORM_X,d0
move.w #PLATFORM_Y,d1
move.w #PLATFORM_W,d2
move.w #PLATFORM_H,d3
bsr draw_rect
; Exit marker
move.w #EXIT_X,d0
move.w #EXIT_Y,d1
move.w #EXIT_W,d2
move.w #1,d3
bsr draw_rect
move.w #EXIT_X,d0
move.w #EXIT_Y+EXIT_H-1,d1
move.w #EXIT_W,d2
move.w #1,d3
bsr draw_rect
; --- Draw initial digit in panel ---
bsr draw_saved_count
; --- Patch bitplane pointer ---
lea bitplane,a0
move.l a0,d0
swap d0
lea bpl1pth_val,a1
move.w d0,(a1)
swap d0
lea bpl1ptl_val,a1
move.w d0,(a1)
; --- Patch panel bitplane pointer ---
lea panel_bitplane,a0
move.l a0,d0
swap d0
lea panel_bpl1pth_val,a1
move.w d0,(a1)
swap d0
lea panel_bpl1ptl_val,a1
move.w d0,(a1)
; --- Patch sprite pointers ---
lea sprite0_data,a0
move.l a0,d0
swap d0
lea spr0pth_val,a1
move.w d0,(a1)
swap d0
lea spr0ptl_val,a1
move.w d0,(a1)
lea sprite1_data,a0
move.l a0,d0
swap d0
lea spr1pth_val,a1
move.w d0,(a1)
swap d0
lea spr1ptl_val,a1
move.w d0,(a1)
lea sprite2_data,a0
move.l a0,d0
swap d0
lea spr2pth_val,a1
move.w d0,(a1)
swap d0
lea spr2ptl_val,a1
move.w d0,(a1)
; --- Install Copper list ---
lea copperlist,a0
move.l a0,COP1LC(a5)
move.w d0,COPJMP1(a5)
; --- Enable DMA ---
move.w #$83a1,DMACON(a5)
; === Main Loop ===
mainloop:
move.l #$1ff00,d1
.vbwait:
move.l VPOSR(a5),d0
and.l d1,d0
bne.s .vbwait
; --- Process all creatures ---
lea creatures,a2
moveq #NUM_CREATURES-1,d7
.creature_loop:
move.w saved_count,d6 ; Remember count before update
bsr update_creature
add.w #CR_SIZE,a2
dbra d7,.creature_loop
; --- Redraw count if changed ---
move.w saved_count,d0
cmp.w d6,d0
beq.s .no_redraw
bsr draw_saved_count
.no_redraw:
; --- Check level complete ---
move.w game_state,d0
cmp.w #GAME_COMPLETE,d0
beq.s .skip_check
move.w saved_count,d0
add.w lost_count,d0
cmp.w #NUM_CREATURES,d0
blt.s .skip_check
; All creatures accounted for
move.w #GAME_COMPLETE,game_state
bsr draw_result
.skip_check:
; --- Update sprites ---
lea creatures,a2
lea sprite0_data,a0
bsr write_sprite_pos
lea creatures+CR_SIZE,a2
lea sprite1_data,a0
bsr write_sprite_pos
lea creatures+CR_SIZE*2,a2
lea sprite2_data,a0
bsr write_sprite_pos
btst #6,$bfe001
bne mainloop
.halt:
bra.s .halt
;──────────────────────────────────────────────────────────────
; update_creature — Update one creature (A2 = creature data)
;──────────────────────────────────────────────────────────────
update_creature:
move.w CR_STATE(a2),d0
cmp.w #STATE_SAVED,d0
beq .done
cmp.w #STATE_LOST,d0
beq .done
cmp.w #STATE_FALLING,d0
beq .do_fall
; --- Walking ---
move.w CR_X(a2),d0
add.w CR_DX(a2),d0
add.w #FOOT_OFFSET_X,d0
move.w CR_Y(a2),d1
add.w #FOOT_OFFSET_Y,d1
bsr check_pixel
tst.b d0
beq.s .walk_no_floor
move.w CR_X(a2),d0
add.w CR_DX(a2),d0
move.w d0,CR_X(a2)
bsr check_exit
move.w CR_STEP(a2),d0
subq.w #1,d0
bgt.s .no_step
bsr play_step
move.w #STEP_INTERVAL,d0
.no_step:
move.w d0,CR_STEP(a2)
move.w CR_X(a2),d0
add.w #FOOT_OFFSET_X,d0
move.w CR_Y(a2),d1
add.w #FOOT_OFFSET_Y,d1
bsr check_pixel
tst.b d0
bne.s .done
move.w #STATE_FALLING,CR_STATE(a2)
bra.s .done
.walk_no_floor:
neg.w CR_DX(a2)
bra.s .done
; --- Falling ---
.do_fall:
move.w CR_Y(a2),d0
add.w #FALL_SPEED,d0
cmp.w #SCREEN_HEIGHT-SPRITE_HEIGHT,d0
blt.s .fall_ok
; Fell below screen — creature is lost
move.w #STATE_LOST,CR_STATE(a2)
move.w #0,CR_Y(a2)
addq.w #1,lost_count
bra.s .done
.fall_ok:
move.w d0,CR_Y(a2)
move.w CR_X(a2),d0
add.w #FOOT_OFFSET_X,d0
move.w CR_Y(a2),d1
add.w #FOOT_OFFSET_Y,d1
bsr check_pixel
tst.b d0
beq.s .done
move.w #STATE_WALKING,CR_STATE(a2)
.done:
rts
;──────────────────────────────────────────────────────────────
; check_exit — Test if creature is inside the exit zone
;──────────────────────────────────────────────────────────────
check_exit:
move.w CR_X(a2),d0
add.w #FOOT_OFFSET_X,d0
cmp.w #EXIT_X,d0
blt.s .not_in
cmp.w #EXIT_X+EXIT_W,d0
bge.s .not_in
move.w CR_Y(a2),d1
add.w #FOOT_OFFSET_Y,d1
cmp.w #EXIT_Y,d1
blt.s .not_in
cmp.w #EXIT_Y+EXIT_H,d1
bge.s .not_in
move.w #STATE_SAVED,CR_STATE(a2)
addq.w #1,saved_count
move.w #0,CR_Y(a2)
.not_in:
rts
;──────────────────────────────────────────────────────────────
; draw_score — Draw "saved / total" in the panel
;──────────────────────────────────────────────────────────────
draw_saved_count:
; Draw saved digit
move.w saved_count,d0
move.w #PANEL_SAVED_X,d1
bsr draw_panel_digit
; Draw slash
lea font_slash,a1
move.w #PANEL_SLASH_X,d1
bsr draw_panel_glyph
; Draw total digit
move.w #NUM_CREATURES,d0
move.w #PANEL_TOTAL_X,d1
bsr draw_panel_digit
rts
;──────────────────────────────────────────────────────────────
; draw_panel_digit — Draw digit D0 at x position D1
;──────────────────────────────────────────────────────────────
draw_panel_digit:
cmp.w #9,d0
ble.s .ok
move.w #9,d0
.ok:
lsl.w #3,d0 ; * 8 bytes per glyph
lea font_digits,a1
add.w d0,a1
; Fall through to draw_panel_glyph
;──────────────────────────────────────────────────────────────
; draw_panel_glyph — Draw 8x8 glyph at A1, x position D1
;──────────────────────────────────────────────────────────────
draw_panel_glyph:
lea panel_bitplane,a0
add.w #PANEL_DIGIT_Y*BYTES_PER_ROW,a0
move.w d1,d2
lsr.w #3,d2
add.w d2,a0
moveq #7,d3
.draw:
move.b (a1)+,(a0)
add.w #BYTES_PER_ROW,a0
dbra d3,.draw
rts
;──────────────────────────────────────────────────────────────
; draw_result — Change panel colour based on outcome
;──────────────────────────────────────────────────────────────
draw_result:
move.w saved_count,d0
cmp.w #NUM_CREATURES,d0
bne.s .not_win
; All saved — green
move.w #COLOUR_WIN,panel_bg_val
rts
.not_win:
; Some lost — red
move.w #COLOUR_LOSE,panel_bg_val
rts
;──────────────────────────────────────────────────────────────
; write_sprite_pos — Write position from creature data to sprite
;──────────────────────────────────────────────────────────────
write_sprite_pos:
move.w CR_STATE(a2),d0
cmp.w #STATE_SAVED,d0
beq.s .hide
cmp.w #STATE_LOST,d0
beq.s .hide
move.w CR_Y(a2),d0
add.w #$2c,d0
move.w d0,d1
add.w #SPRITE_HEIGHT,d1
move.w CR_X(a2),d2
add.w #$80,d2
move.b d0,d3
lsl.w #8,d3
move.w d2,d4
lsr.w #1,d4
or.b d4,d3
move.w d3,(a0)+
move.b d1,d3
lsl.w #8,d3
moveq #0,d4
btst #8,d0
beq.s .no_vs8
bset #2,d4
.no_vs8:
btst #8,d1
beq.s .no_ve8
bset #1,d4
.no_ve8:
btst #0,d2
beq.s .no_h0
bset #0,d4
.no_h0:
or.b d4,d3
move.w d3,(a0)
rts
.hide:
move.w #0,(a0)+
move.w #0,(a0)
rts
;──────────────────────────────────────────────────────────────
; play_step — Trigger the footstep sample on Paula channel 0
;──────────────────────────────────────────────────────────────
play_step:
lea CUSTOM,a6
lea step_sample,a0
move.l a0,AUD0LC(a6)
move.w #STEP_SAMPLE_LEN/2,AUD0LEN(a6)
move.w #STEP_PERIOD,AUD0PER(a6)
move.w #STEP_VOLUME,AUD0VOL(a6)
move.w #$8201,DMACON(a6)
rts
;──────────────────────────────────────────────────────────────
; check_pixel — Test a single pixel in the bitplane
;──────────────────────────────────────────────────────────────
check_pixel:
lea bitplane,a0
mulu #BYTES_PER_ROW,d1
add.l d1,a0
move.w d0,d2
lsr.w #3,d2
add.w d2,a0
not.w d0
and.w #7,d0
btst d0,(a0)
sne d0
and.b #1,d0
rts
;──────────────────────────────────────────────────────────────
; draw_rect — Fill a byte-aligned rectangle in the bitplane
;──────────────────────────────────────────────────────────────
draw_rect:
lea bitplane,a0
mulu #BYTES_PER_ROW,d1
add.l d1,a0
lsr.w #3,d0
add.w d0,a0
lsr.w #3,d2
subq.w #1,d3
.row:
move.w d2,d5
subq.w #1,d5
move.l a0,a1
.col:
move.b #$ff,(a1)+
dbra d5,.col
add.w #BYTES_PER_ROW,a0
dbra d3,.row
rts
;══════════════════════════════════════════════════════════════
; FONT — 8x8 digit glyphs (0-9)
;══════════════════════════════════════════════════════════════
font_digits:
; 0
dc.b %00111100
dc.b %01100110
dc.b %01101110
dc.b %01110110
dc.b %01100110
dc.b %01100110
dc.b %00111100
dc.b %00000000
; 1
dc.b %00011000
dc.b %00111000
dc.b %00011000
dc.b %00011000
dc.b %00011000
dc.b %00011000
dc.b %01111110
dc.b %00000000
; 2
dc.b %00111100
dc.b %01100110
dc.b %00000110
dc.b %00001100
dc.b %00110000
dc.b %01100000
dc.b %01111110
dc.b %00000000
; 3
dc.b %00111100
dc.b %01100110
dc.b %00000110
dc.b %00011100
dc.b %00000110
dc.b %01100110
dc.b %00111100
dc.b %00000000
; 4
dc.b %00001100
dc.b %00011100
dc.b %00101100
dc.b %01001100
dc.b %01111110
dc.b %00001100
dc.b %00001100
dc.b %00000000
; 5
dc.b %01111110
dc.b %01100000
dc.b %01111100
dc.b %00000110
dc.b %00000110
dc.b %01100110
dc.b %00111100
dc.b %00000000
; 6
dc.b %00111100
dc.b %01100110
dc.b %01100000
dc.b %01111100
dc.b %01100110
dc.b %01100110
dc.b %00111100
dc.b %00000000
; 7
dc.b %01111110
dc.b %01100110
dc.b %00001100
dc.b %00011000
dc.b %00011000
dc.b %00011000
dc.b %00011000
dc.b %00000000
; 8
dc.b %00111100
dc.b %01100110
dc.b %01100110
dc.b %00111100
dc.b %01100110
dc.b %01100110
dc.b %00111100
dc.b %00000000
; 9
dc.b %00111100
dc.b %01100110
dc.b %01100110
dc.b %00111110
dc.b %00000110
dc.b %01100110
dc.b %00111100
dc.b %00000000
font_slash:
dc.b %00000010
dc.b %00000100
dc.b %00001000
dc.b %00010000
dc.b %00100000
dc.b %01000000
dc.b %10000000
dc.b %00000000
;══════════════════════════════════════════════════════════════
; COPPER LIST
;══════════════════════════════════════════════════════════════
copperlist:
dc.w DIWSTRT,$2c81
dc.w DIWSTOP,$2cc1
dc.w DDFSTRT,$0038
dc.w DDFSTOP,$00d0
dc.w BPLCON0,$1200
dc.w BPLCON1,$0000
dc.w BPLCON2,$0000
dc.w BPL1MOD,$0000
dc.w BPL1PTH
bpl1pth_val:
dc.w $0000
dc.w BPL1PTL
bpl1ptl_val:
dc.w $0000
dc.w SPR0PTH
spr0pth_val:
dc.w $0000
dc.w SPR0PTL
spr0ptl_val:
dc.w $0000
dc.w SPR1PTH
spr1pth_val:
dc.w $0000
dc.w SPR1PTL
spr1ptl_val:
dc.w $0000
dc.w SPR2PTH
spr2pth_val:
dc.w $0000
dc.w SPR2PTL
spr2ptl_val:
dc.w $0000
dc.w COLOR00,COLOUR_SKY_DEEP
dc.w COLOR01,COLOUR_TERRAIN
dc.w COLOR17,COLOUR_SPR0_1
dc.w COLOR18,COLOUR_SPR0_2
dc.w COLOR19,COLOUR_SPR0_3
dc.w $3401,$fffe
dc.w COLOR00,COLOUR_SKY_UPPER
dc.w $4401,$fffe
dc.w COLOR00,COLOUR_SKY_MID
dc.w $5401,$fffe
dc.w COLOR00,COLOUR_SKY_LOWER
dc.w $6001,$fffe
dc.w COLOR00,COLOUR_SKY_HORIZON
dc.w $6801,$fffe
dc.w COLOR00,$0000
; --- Panel screen split ---
; Wait for panel scanline ($2c + 200 = $E4)
dc.w $e401,$fffe
dc.w COLOR00
panel_bg_val:
dc.w COLOUR_PANEL_BG
dc.w COLOR01,COLOUR_PANEL_FG
dc.w BPL1PTH
panel_bpl1pth_val:
dc.w $0000
dc.w BPL1PTL
panel_bpl1ptl_val:
dc.w $0000
dc.w $ffff,$fffe
;══════════════════════════════════════════════════════════════
; SPRITE DATA
;══════════════════════════════════════════════════════════════
even
sprite0_data:
dc.w $0000,$0000
dc.w %0000011111100000,%0000000000000000
dc.w %0000111111110000,%0000011111100000
dc.w %0001111111111000,%0000111111110000
dc.w %0001101110111000,%0001111111111000
dc.w %0001111111111000,%0000111111110000
dc.w %0000111001110000,%0000011111100000
dc.w %0000011111100000,%0000000000000000
dc.w %0001111111111000,%0000011111100000
dc.w %0001111111111000,%0000111111110000
dc.w %0001111111111000,%0000111111110000
dc.w %0000110000110000,%0000000000000000
dc.w %0000110000110000,%0000000000000000
dc.w $0000,$0000
even
sprite1_data:
dc.w $0000,$0000
dc.w %0000011111100000,%0000000000000000
dc.w %0000111111110000,%0000011111100000
dc.w %0001111111111000,%0000111111110000
dc.w %0001101110111000,%0001111111111000
dc.w %0001111111111000,%0000111111110000
dc.w %0000111001110000,%0000011111100000
dc.w %0000011111100000,%0000000000000000
dc.w %0001111111111000,%0000011111100000
dc.w %0001111111111000,%0000111111110000
dc.w %0001111111111000,%0000111111110000
dc.w %0000110000110000,%0000000000000000
dc.w %0000110000110000,%0000000000000000
dc.w $0000,$0000
even
sprite2_data:
dc.w $0000,$0000
dc.w %0000011111100000,%0000000000000000
dc.w %0000111111110000,%0000011111100000
dc.w %0001111111111000,%0000111111110000
dc.w %0001101110111000,%0001111111111000
dc.w %0001111111111000,%0000111111110000
dc.w %0000111001110000,%0000011111100000
dc.w %0000011111100000,%0000000000000000
dc.w %0001111111111000,%0000011111100000
dc.w %0001111111111000,%0000111111110000
dc.w %0001111111111000,%0000111111110000
dc.w %0000110000110000,%0000000000000000
dc.w %0000110000110000,%0000000000000000
dc.w $0000,$0000
;══════════════════════════════════════════════════════════════
; STEP SAMPLE
;══════════════════════════════════════════════════════════════
even
step_sample:
dc.b $60,$40,$20,$10,$08,$04,$02,$01
dc.b $fe,$fc,$f8,$f0,$e0,$d0,$c0,$b0
dc.b $50,$30,$18,$0c,$06,$03,$01,$00
dc.b $ff,$fd,$fa,$f4,$e8,$d8,$c8,$b8
STEP_SAMPLE_LEN equ *-step_sample
;══════════════════════════════════════════════════════════════
; VARIABLES
;══════════════════════════════════════════════════════════════
even
creatures:
dcb.b CR_SIZE*NUM_CREATURES,0
saved_count:
dc.w 0
lost_count:
dc.w 0
game_state:
dc.w 0
;══════════════════════════════════════════════════════════════
; BITPLANE DATA
;══════════════════════════════════════════════════════════════
even
bitplane:
dcb.b BITPLANE_SIZE,0
even
panel_bitplane:
dcb.b PANEL_SIZE,0
If It Doesn’t Work
- Creatures vanish but level never completes? Check that
lost_countincrements in the falling code. The level-complete check needssaved_count + lost_count >= NUM_CREATURES. - Panel doesn’t change colour? Make sure
panel_bg_valis a label inside the Copper list, positioned after the panel WAIT but before the end marker. The CPU writes to this address directly. - Always shows red/green immediately? Check that
game_statestarts atGAME_PLAYING(0) and the level-complete check tests it before acting.
What You’ve Learnt
- Lost state — creatures that fall off screen transition to
STATE_LOST, are hidden, and counted separately from saved creatures. - Level completion — a simple arithmetic check (
saved + lost >= total) determines when all creatures are accounted for. - Runtime Copper patching — writing values directly into the Copper list changes display properties next frame. The Copper re-reads its program every frame.
- Game states —
GAME_PLAYINGandGAME_COMPLETEcontrol which parts of the main loop execute. State machines scale naturally as game logic grows.
What’s Next
The game plays and ends, but there’s no way to start it. In Unit 15, we’ll add a title screen — the game begins with “EXODUS” displayed and waits for the fire button before starting.