Title Screen
A three-state game loop: title, playing, and complete. The title draws 'EXODUS' on the bitplane using letter glyphs. Fire starts the game, fire after completion restarts it.
The game plays to completion, but it starts immediately and can’t restart. In this unit, we add a title screen and a complete game loop: title, play, and result — with fire button transitions between each.

“EXODUS” appears in the game area against the gradient sky. The panel sits below, empty and waiting. Press fire to begin.
Three Game States
The game state machine expands to three states:
GAME_TITLE equ 0
GAME_PLAYING equ 1
GAME_COMPLETE equ 2
The main loop checks the state and branches accordingly:
; --- Check game state ---
move.w game_state,d0
cmp.w #GAME_TITLE,d0
beq .title_update
cmp.w #GAME_COMPLETE,d0
beq .complete_update
; --- PLAYING: Process all creatures ---
; ...creature processing...
bra .frame_end
; --- TITLE: wait for fire ---
.title_update:
btst #6,$bfe001
bne .frame_end
bsr start_game
bra .frame_end
; --- COMPLETE: wait for fire to restart ---
.complete_update:
btst #6,$bfe001
bne .frame_end
bsr clear_bitplane
bsr draw_title
move.w #GAME_TITLE,game_state
move.w #COLOUR_PANEL_BG,panel_bg_val
.frame_end:
In the title state, the loop does nothing except check the fire button. When fire is pressed, start_game sets up everything and switches to playing. When the level completes, the result shows until fire is pressed again — then the bitplane is cleared, the title is redrawn, and the state returns to title.
This is a complete game loop. The program never exits; it cycles through title, play, and result indefinitely.
Drawing the Title
Six letter glyphs spell “EXODUS” on the game bitplane:
;──────────────────────────────────────────────────────────────
; draw_title — Draw "EXODUS" on the game bitplane
;──────────────────────────────────────────────────────────────
draw_title:
lea font_E,a1
move.w #128,d0
move.w #80,d1
bsr draw_bitplane_glyph
lea font_X,a1
move.w #136,d0
move.w #80,d1
bsr draw_bitplane_glyph
lea font_O,a1
move.w #144,d0
move.w #80,d1
bsr draw_bitplane_glyph
lea font_D,a1
move.w #152,d0
move.w #80,d1
bsr draw_bitplane_glyph
lea font_U,a1
move.w #160,d0
move.w #80,d1
bsr draw_bitplane_glyph
lea font_S,a1
move.w #168,d0
move.w #80,d1
bsr draw_bitplane_glyph
rts
Each letter is an 8x8 glyph, drawn at 8-pixel intervals starting at x=128, y=80. The draw_bitplane_glyph subroutine works identically to draw_panel_glyph, but writes to the game bitplane instead of the panel bitplane.
The letter font data uses the same format as the digit font — 8 bytes per character:
font_E:
dc.b %01111110
dc.b %01100000
dc.b %01100000
dc.b %01111100
dc.b %01100000
dc.b %01100000
dc.b %01111110
dc.b %00000000
The bitmap font system now handles digits 0-9, a slash, and the letters E, X, O, D, U, S. Adding more characters is trivial — define 8 bytes and call draw_bitplane_glyph.
The start_game Subroutine
When fire is pressed on the title screen, start_game handles the entire transition:
;──────────────────────────────────────────────────────────────
; start_game — Transition from title to gameplay
;──────────────────────────────────────────────────────────────
start_game:
bsr clear_bitplane
; Reset 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)
; ...more creatures...
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
; ...more terrain...
; Draw initial score
bsr draw_saved_count
rts
It clears the title text, resets all creatures to their starting positions, resets the score counters, changes the game state, draws the terrain and exit markers, and draws the initial score display. Everything the game needs is initialised in one place.
The bitplane clear uses a tight loop:
clear_bitplane:
lea bitplane,a0
move.w #BITPLANE_SIZE/4-1,d0
.clr:
clr.l (a0)+
dbra d0,.clr
rts
CLR.L zeros 4 bytes at a time. For an 8,000-byte bitplane, that’s 2,000 iterations — fast enough to complete within the vertical blank.
Restart Flow
After the level completes and the result is shown, pressing fire triggers a restart:
.complete_update:
btst #6,$bfe001
bne .frame_end
bsr clear_bitplane
bsr draw_title
move.w #GAME_TITLE,game_state
move.w #COLOUR_PANEL_BG,panel_bg_val
The panel background is restored to its default colour (clearing the green/red result), the bitplane is cleared, and the title is redrawn. The game is ready for another round.
The Complete Code
;──────────────────────────────────────────────────────────────
; EXODUS - A terrain puzzle for the Commodore Amiga
; Unit 15: Title Screen
;
; A title screen shows "EXODUS" before gameplay starts.
; Press fire to begin. The game flow: title → play → result.
;──────────────────────────────────────────────────────────────
;══════════════════════════════════════════════════════════════
; 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_TITLE equ 0
GAME_PLAYING equ 1
GAME_COMPLETE equ 2
; 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_TITLE,game_state
; --- Draw title screen ---
bsr draw_title
; --- 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
; --- Check game state ---
move.w game_state,d0
cmp.w #GAME_TITLE,d0
beq .title_update
cmp.w #GAME_COMPLETE,d0
beq .complete_update
; --- PLAYING: Process all creatures ---
lea creatures,a2
moveq #NUM_CREATURES-1,d7
.creature_loop:
move.w saved_count,d6
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 saved_count,d0
add.w lost_count,d0
cmp.w #NUM_CREATURES,d0
blt.s .not_done
move.w #GAME_COMPLETE,game_state
bsr draw_result
.not_done:
; 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
bra .frame_end
; --- TITLE: wait for fire ---
.title_update:
btst #6,$bfe001
bne .frame_end
; Fire pressed — start game
bsr start_game
bra .frame_end
; --- COMPLETE: wait for fire to restart ---
.complete_update:
btst #6,$bfe001
bne .frame_end
; Fire pressed — restart
bsr clear_bitplane
bsr draw_title
move.w #GAME_TITLE,game_state
move.w #COLOUR_PANEL_BG,panel_bg_val
.frame_end:
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
;──────────────────────────────────────────────────────────────
; start_game — Transition from title to gameplay
;──────────────────────────────────────────────────────────────
start_game:
bsr clear_bitplane
; Reset 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 score
bsr draw_saved_count
rts
;──────────────────────────────────────────────────────────────
; draw_title — Draw "EXODUS" on the game bitplane
;──────────────────────────────────────────────────────────────
draw_title:
; "EXODUS" at row 80, starting at byte 16 (x=128)
lea font_E,a1
move.w #128,d0
move.w #80,d1
bsr draw_bitplane_glyph
lea font_X,a1
move.w #136,d0
move.w #80,d1
bsr draw_bitplane_glyph
lea font_O,a1
move.w #144,d0
move.w #80,d1
bsr draw_bitplane_glyph
lea font_D,a1
move.w #152,d0
move.w #80,d1
bsr draw_bitplane_glyph
lea font_U,a1
move.w #160,d0
move.w #80,d1
bsr draw_bitplane_glyph
lea font_S,a1
move.w #168,d0
move.w #80,d1
bsr draw_bitplane_glyph
rts
;──────────────────────────────────────────────────────────────
; draw_bitplane_glyph — Draw 8x8 glyph onto game bitplane
; A1 = glyph data, D0 = x (byte-aligned), D1 = y
;──────────────────────────────────────────────────────────────
draw_bitplane_glyph:
lea bitplane,a0
mulu #BYTES_PER_ROW,d1
add.l d1,a0
lsr.w #3,d0
add.w d0,a0
moveq #7,d3
.draw:
move.b (a1)+,(a0)
add.w #BYTES_PER_ROW,a0
dbra d3,.draw
rts
;──────────────────────────────────────────────────────────────
; clear_bitplane — Zero the game bitplane
;──────────────────────────────────────────────────────────────
clear_bitplane:
lea bitplane,a0
move.w #BITPLANE_SIZE/4-1,d0
.clr:
clr.l (a0)+
dbra d0,.clr
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
; Letters for "EXODUS"
font_E:
dc.b %01111110
dc.b %01100000
dc.b %01100000
dc.b %01111100
dc.b %01100000
dc.b %01100000
dc.b %01111110
dc.b %00000000
font_X:
dc.b %01100110
dc.b %01100110
dc.b %00111100
dc.b %00011000
dc.b %00111100
dc.b %01100110
dc.b %01100110
dc.b %00000000
font_O:
dc.b %00111100
dc.b %01100110
dc.b %01100110
dc.b %01100110
dc.b %01100110
dc.b %01100110
dc.b %00111100
dc.b %00000000
font_D:
dc.b %01111100
dc.b %01100110
dc.b %01100110
dc.b %01100110
dc.b %01100110
dc.b %01100110
dc.b %01111100
dc.b %00000000
font_U:
dc.b %01100110
dc.b %01100110
dc.b %01100110
dc.b %01100110
dc.b %01100110
dc.b %01100110
dc.b %00111100
dc.b %00000000
font_S:
dc.b %00111100
dc.b %01100110
dc.b %01100000
dc.b %00111100
dc.b %00000110
dc.b %01100110
dc.b %00111100
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
- Title doesn’t appear? Check that
draw_titleis called before the main loop starts, and that the bitplane pointer is patched before the Copper list is installed. - Fire doesn’t start the game? The fire button test reads
$BFE001bit 6. Low means pressed. Make sure the title state checksGAME_TITLE(0), not some other value. - Game doesn’t restart? The complete state must clear the bitplane, redraw the title, reset
game_statetoGAME_TITLE, and restorepanel_bg_valtoCOLOUR_PANEL_BG. - Terrain appears on title screen? Make sure
start_gamecallsclear_bitplanebefore drawing terrain. If terrain is drawn without clearing, it’ll overlap with the title text.
What You’ve Learnt
- Three-state game loop — title, playing, and complete form a cycle. Each state has its own update logic and transition trigger.
- Bitplane text rendering — 8x8 letter glyphs drawn directly to the game bitplane. The same technique works for digits, letters, and symbols.
- Game initialisation subroutine —
start_gameencapsulates all setup in one callable routine, making restart trivial. - State restoration — returning to the title means clearing the display and resetting Copper values. Every visual element must be explicitly restored.
What’s Next
The game works end-to-end with three creatures. In Unit 16, we’ll increase to five creatures — more hardware sprites, staggered spawning, and the final polish that completes Phase 1.