Skip to content
Game 1 Unit 13 of 128 1 hr learning time

Score

The panel now shows 'saved / total' — three glyphs drawn by a shared subroutine. A slash glyph joins two digits, giving the player a clear goal.

10% of Exodus

The panel shows a single digit, but there’s no context. How many creatures are there in total? In this unit, the display expands from one digit to a “saved / total” readout — three glyphs drawn by a shared subroutine.

Exodus Unit 13

The panel now reads “0/3” — zero saved out of three. As creatures reach the exit, the first digit updates. The slash and total are drawn once at the start and never change.

Three Glyphs, One Subroutine

The previous unit drew a single digit with a dedicated routine. Now we need three glyphs at different positions — a digit, a slash, and another digit. Rather than duplicating the drawing code, we split it into two layers:

;──────────────────────────────────────────────────────────────
; 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_panel_digit converts a number into a glyph pointer, then falls through to draw_panel_glyph. The fall-through is a common 68000 pattern — when one subroutine’s last action is to call another, you skip the BSR and let execution flow directly into the next routine. No stack overhead, no wasted cycles.

draw_panel_glyph takes a glyph pointer in A1 and an x position in D1. It works with any 8x8 glyph — digits, letters, symbols. The same 8-byte copy loop draws everything.

The Score Display

The draw_saved_count subroutine calls the drawing routines three times:

;──────────────────────────────────────────────────────────────
; draw_saved_count — 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

Three positions are defined as constants:

PANEL_SAVED_X       equ 144         ; Saved digit
PANEL_SLASH_X       equ 152         ; Slash separator
PANEL_TOTAL_X       equ 160         ; Total digit

Each glyph is 8 pixels wide, so they’re spaced 8 pixels apart. The total digit uses NUM_CREATURES directly — it never changes, so there’s no variable to track.

The Slash Glyph

A new font entry draws the separator:

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

One diagonal line of pixels, bottom-left to top-right. Simple, readable, and drawn by the same draw_panel_glyph routine that handles digits.

The Complete Code

;──────────────────────────────────────────────────────────────
; EXODUS - A terrain puzzle for the Commodore Amiga
; Unit 13: Score Display
;
; The panel shows "saved / total" as two digits with a slash.
; The score updates live as creatures reach the exit.
;──────────────────────────────────────────────────────────────

;══════════════════════════════════════════════════════════════
; 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_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

; 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

            ; --- 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:

            ; --- 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_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
            move.w  #SCREEN_HEIGHT-SPRITE_HEIGHT,d0
            move.w  d0,CR_Y(a2)
            move.w  #STATE_WALKING,CR_STATE(a2)
            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

;──────────────────────────────────────────────────────────────
; 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

            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,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

;══════════════════════════════════════════════════════════════
; BITPLANE DATA
;══════════════════════════════════════════════════════════════

            even
bitplane:
            dcb.b   BITPLANE_SIZE,0

            even
panel_bitplane:
            dcb.b   PANEL_SIZE,0

If It Doesn’t Work

  • Only one digit shows? Check that all three bsr calls use the correct x positions. The saved digit, slash, and total must be at different PANEL_*_X values.
  • Slash doesn’t appear? Make sure font_slash is defined after font_digits and contains exactly 8 bytes.
  • Total shows wrong number? The total digit draws NUM_CREATURES, not saved_count. Check that NUM_CREATURES is 3.

What You’ve Learnt

  • Fall-through subroutines — when one routine’s last action is calling another, skip the BSR and let execution flow directly. Saves stack operations and keeps code compact.
  • Generic drawing routinesdraw_panel_glyph handles any 8x8 glyph. The same code draws digits, slashes, and any future characters.
  • Multi-glyph displays — combining several glyph draws at different x positions to build a readable status readout.

What’s Next

The score shows progress, but the game never ends. In Unit 14, we’ll add level completion — when every creature has been saved or lost, the result is displayed and the game state changes.