Skip to content
Game 1 Unit 10 of 64 1 hr learning time

Home Zones

Reach the top. Fill the homes. Score points. Respawn and repeat.

16% of Signal

What You’re Building

Goals.

Stakes without a goal is just endless punishment. This unit adds 5 home zones at the top of the screen. Reach an empty home, it fills in, you score 50 points, and you respawn. Fill all 5 homes to complete a round and start again.

By the end of this unit:

  • 5 home zones at top of screen
  • Reaching an empty home fills it
  • 50 points per successful docking
  • Landing on filled home = death
  • Landing between homes = death
  • Fill all 5 to reset for next round

Unit 10 Screenshot

Home Zone Layout

The top row has 5 home slots, evenly spaced across the grid:

; Home Zone Constants
; 5 homes at top row, must fill all to complete round

; Home zone configuration
NUM_HOMES       equ 5
HOME_ROW        equ 0           ; Top row of grid
POINTS_PER_HOME equ 50

; Home X positions (grid coordinates)
; Each home is 2 cells wide
HOME_0_X        equ 2
HOME_1_X        equ 6
HOME_2_X        equ 10
HOME_3_X        equ 14
HOME_4_X        equ 18

; Colours
COLOUR_HOME     equ $0282       ; Dark green (gaps between homes)
COLOUR_HOME_LIT equ $03a3       ; Light green (empty home)
COLOUR_HOME_FILLED equ $0f80    ; Orange (filled home)

; Home state tracking (0 = empty, 1 = filled)
home_filled:    dc.w    0,0,0,0,0

Each home is 2 grid cells wide. The gaps between homes are dangerous—landing there kills the frog. Using words instead of bytes keeps alignment clean and makes comparisons straightforward.

The Check Home Routine

When the frog reaches row 0, we determine which home (if any) it landed on:

; Check Home Zone
; Detect which home frog has reached (if any)

check_home:
            ; Only check if at top row
            move.w  frog_grid_y,d0
            tst.w   d0
            bne     .not_home

            ; At top row - determine which home
            move.w  frog_grid_x,d0

            ; Check home 0 (X = 2-3)
            cmp.w   #HOME_0_X,d0
            blt.s   .not_home
            cmp.w   #HOME_0_X+2,d0
            blt.s   .home_0

            ; Check home 1 (X = 6-7)
            cmp.w   #HOME_1_X,d0
            blt.s   .not_home
            cmp.w   #HOME_1_X+2,d0
            blt.s   .home_1

            ; (continue for homes 2, 3, 4...)

            ; Between homes - death!
            bra     .death_between

.home_0:    moveq   #0,d1
            bra.s   .check_filled
.home_1:    moveq   #1,d1
            bra.s   .check_filled
            ; (etc...)

.check_filled:
            ; D1 = home index (0-4)
            lea     home_filled,a0
            add.w   d1,d1               ; *2 for word offset
            tst.w   0(a0,d1.w)
            bne.s   .death_filled       ; Already filled - death!

            ; Home is empty - fill it!
            move.w  #1,0(a0,d1.w)
            add.l   #POINTS_PER_HOME,score
            bsr     update_home_display
            bsr     reset_frog
            rts

.death_filled:
.death_between:
            bsr     trigger_death
            rts

.not_home:
            rts

The logic is straightforward: check if the X position falls within each home’s range. If it’s between homes, the frog dies. Landing on an already-filled home also kills the frog—this prevents players from repeatedly using the same safe spot.

Score Tracking

We use a 32-bit long word for the score:

score:          dc.l    0
POINTS_PER_HOME equ 50

; Adding points
add.l   #POINTS_PER_HOME,score

The score isn’t displayed yet—that comes in a later unit. For now, we just track it.

Round Completion

When all 5 homes are filled, we reset them for the next round:

check_round_complete:
    lea     home_filled,a0
    moveq   #NUM_HOMES-1,d0
    moveq   #0,d1               ; Count filled homes

.loop:
    tst.w   (a0)+
    beq.s   .not_complete
    addq.w  #1,d1
    dbf     d0,.loop

    ; All homes filled?
    cmp.w   #NUM_HOMES,d1
    bne.s   .not_complete

    ; Clear all homes for next round
    lea     home_filled,a0
    moveq   #NUM_HOMES-1,d0
.clear:
    clr.w   (a0)+
    dbf     d0,.clear

    bsr     update_home_display

.not_complete:
    rts

In a complete game, this is where you’d increase difficulty—faster cars, more obstacles. For now, we just reset.

Visual Feedback via Copper

We modify the Copper list to show filled homes in a different colour. Each home zone has its own colour value that we update:

; Update Home Display
; Modify Copper list colours to show filled homes

update_home_display:
            lea     home_filled,a0
            lea     home_colour_0,a1

            ; Home 0
            tst.w   (a0)+
            beq.s   .home0_empty
            move.w  #COLOUR_HOME_FILLED,(a1)
            bra.s   .home1
.home0_empty:
            move.w  #COLOUR_HOME_LIT,(a1)

.home1:
            lea     home_colour_1,a1
            tst.w   (a0)+
            beq.s   .home1_empty
            move.w  #COLOUR_HOME_FILLED,(a1)
            bra.s   .home2
.home1_empty:
            move.w  #COLOUR_HOME_LIT,(a1)

            ; (continue for homes 2, 3, 4...)

            rts

; Copper list with modifiable home colours:
copperlist:
            ; ... other setup ...

            ; Home zone row with horizontal colour changes
            dc.w    $2c07,$fffe
            dc.w    COLOR00,COLOUR_HOME     ; Gap

            dc.w    $2c4f,$fffe             ; Wait for home 0 X position
            dc.w    COLOR00
home_colour_0:
            dc.w    COLOUR_HOME_LIT         ; Modified at runtime

            dc.w    $2c5f,$fffe             ; End of home 0
            dc.w    COLOR00,COLOUR_HOME     ; Gap

            ; (continue for homes 1-4...)

The Copper changes colours mid-scanline to create distinct home zones. Filled homes appear orange, empty ones stay light green. The wait value $2c4f means: scanline $2c, horizontal position $4f. This lets us change colours within a single line.

Game Initialisation

We add an init_game routine that resets everything for a new game:

init_game:
    move.w  #START_LIVES,lives
    clr.l   score

    ; Clear all homes
    lea     home_filled,a0
    moveq   #NUM_HOMES-1,d0
.clear_homes:
    clr.w   (a0)+
    dbf     d0,.clear_homes

    bsr     update_home_display
    bsr     reset_frog
    rts

This is called at startup and when restarting after game over.

When to Check Homes

We only check for home docking when the frog is idle (not hopping or dying):

cmp.w   #STATE_DYING,frog_state
beq.s   .skip_collision
cmp.w   #STATE_IDLE,frog_state
bne.s   .skip_collision
bsr     check_home
bsr     check_collision
.skip_collision:

This ensures we only check at the moment the frog lands on a cell, not during animation.

Key Takeaways

  • Home zones create a goal beyond survival
  • State tracking per home enables progress
  • Round completion creates replay loops
  • Horizontal Copper waits enable per-home colours
  • Score variable tracks player progress
  • Filled home death prevents camping

The Code

;==============================================================================
; SIGNAL - A Frogger-style game for the Commodore Amiga
; Unit 10: Home Zones
;
; Stakes alone aren't enough—you need a goal. This unit adds 5 home zones at
; the top of the screen. Reach one, it fills in, you respawn. Fill all 5 to
; complete a round. Each successful docking scores 50 points.
;==============================================================================

;==============================================================================
; CONSTANTS
;==============================================================================

SCREEN_W        equ 40
SCREEN_H        equ 256
PLANE_SIZE      equ SCREEN_W*SCREEN_H

GRID_COLS       equ 20
GRID_ROWS       equ 13
CELL_SIZE       equ 16
GRID_ORIGIN_X   equ 48
GRID_ORIGIN_Y   equ 44
START_GRID_X    equ 9
START_GRID_Y    equ 12

HOP_FRAMES      equ 8
PIXELS_PER_FRAME equ 2
STATE_IDLE      equ 0
STATE_HOPPING   equ 1
STATE_DYING     equ 2
STATE_GAMEOVER  equ 3
DIR_UP          equ 0
DIR_DOWN        equ 1
DIR_LEFT        equ 2
DIR_RIGHT       equ 3
FROG_HEIGHT     equ 16
FROG_WIDTH      equ 16

DEATH_FRAMES    equ 30
FLASH_COLOUR    equ $0f00

NUM_CARS        equ 10
CAR_WIDTH       equ 2
CAR_WIDTH_PX    equ 32
CAR_HEIGHT      equ 12
CAR_STRUCT_SIZE equ 8

ROAD_ROW_FIRST  equ 7
ROAD_ROW_LAST   equ 11

; Lives system
START_LIVES     equ 3
LIFE_ICON_W     equ 1
LIFE_ICON_H     equ 8
LIFE_ICON_X     equ 8
LIFE_ICON_Y     equ 260
LIFE_ICON_SPACING equ 20

; Home zones
NUM_HOMES       equ 5
HOME_ROW        equ 0           ; Top row of grid
POINTS_PER_HOME equ 50

; Home X positions (grid coordinates): 2, 6, 10, 14, 18
; Each home is 2 cells wide (even column = start of home)
HOME_0_X        equ 2
HOME_1_X        equ 6
HOME_2_X        equ 10
HOME_3_X        equ 14
HOME_4_X        equ 18

; Colours
COLOUR_BLACK    equ $0000
COLOUR_HOME     equ $0282
COLOUR_HOME_LIT equ $03a3
COLOUR_HOME_FILLED equ $0f80    ; Orange for filled homes
COLOUR_WATER1   equ $0148
COLOUR_WATER2   equ $026a
COLOUR_MEDIAN   equ $0383
COLOUR_ROAD1    equ $0333
COLOUR_ROAD2    equ $0444
COLOUR_START    equ $0262
COLOUR_START_LIT equ $0373
COLOUR_FROG     equ $00f0
COLOUR_EYES     equ $0ff0
COLOUR_OUTLINE  equ $0000
COLOUR_CAR      equ $0f00
COLOUR_LIFE     equ $00f0

;==============================================================================
; HARDWARE REGISTERS
;==============================================================================

CUSTOM      equ $dff000

DMACONR     equ $002
VPOSR       equ $004
JOY1DAT     equ $00c

BLTCON0     equ $040
BLTCON1     equ $042
BLTAFWM     equ $044
BLTALWM     equ $046
BLTCPTH     equ $048
BLTBPTH     equ $04c
BLTAPTH     equ $050
BLTDPTH     equ $054
BLTSIZE     equ $058
BLTCMOD     equ $060
BLTBMOD     equ $062
BLTAMOD     equ $064
BLTDMOD     equ $066

COP1LC      equ $080
COPJMP1     equ $088
DMACON      equ $096
INTENA      equ $09a
INTREQ      equ $09c
COLOR00     equ $180
SPR0PTH     equ $120
SPR0PTL     equ $122
SPR1PTH     equ $124
SPR1PTL     equ $126
SPR2PTH     equ $128
SPR2PTL     equ $12a

;==============================================================================
; CODE SECTION
;==============================================================================

            section code,code_c

start:
            lea     CUSTOM,a5

            move.w  #$7fff,INTENA(a5)
            move.w  #$7fff,INTREQ(a5)
            move.w  #$7fff,DMACON(a5)

            bsr     clear_screen

            ; Set up bitplane pointer
            lea     screen_plane,a0
            move.l  a0,d0
            swap    d0
            lea     bplpth_val,a1
            move.w  d0,(a1)
            swap    d0
            lea     bplptl_val,a1
            move.w  d0,(a1)

            ; Initialise game
            bsr     init_game

            ; Set main sprite pointer (sprite 0 = frog)
            lea     frog_data,a0
            move.l  a0,d0
            swap    d0
            lea     sprpth_val,a1
            move.w  d0,(a1)
            swap    d0
            lea     sprptl_val,a1
            move.w  d0,(a1)

            ; Set up life icon sprites (sprites 1, 2, 3)
            bsr     setup_life_sprites

            bsr     update_sprite
            bsr     update_life_display

            ; Install copper list
            lea     copperlist,a0
            move.l  a0,COP1LC(a5)
            move.w  d0,COPJMP1(a5)

            ; Enable DMA
            move.w  #$87e0,DMACON(a5)

;==============================================================================
; MAIN LOOP
;==============================================================================

mainloop:
            bsr     wait_vblank

            ; Check game state
            cmp.w   #STATE_GAMEOVER,frog_state
            beq     .game_over_loop

            ; Normal game loop
            bsr     update_frog
            bsr     update_sprite

            bsr     erase_all_cars
            bsr     move_all_cars
            bsr     draw_all_cars

            cmp.w   #STATE_DYING,frog_state
            beq.s   .skip_collision
            cmp.w   #STATE_IDLE,frog_state
            bne.s   .skip_collision
            bsr     check_home
            bsr     check_collision
.skip_collision:

            bra     mainloop

.game_over_loop:
            ; Game over - wait for fire button to restart
            btst    #7,$bfe001
            bne.s   mainloop

            ; Restart game
            bsr     init_game
            bsr     update_life_display
            bsr     clear_screen

            bra     mainloop

;==============================================================================
; GAME INITIALISATION
;==============================================================================

init_game:
            move.w  #START_LIVES,lives
            clr.l   score

            ; Clear all homes
            lea     home_filled,a0
            moveq   #NUM_HOMES-1,d0
.clear_homes:
            clr.w   (a0)+
            dbf     d0,.clear_homes

            bsr     update_home_display
            bsr     reset_frog
            rts

;==============================================================================
; HOME ZONE DETECTION
;==============================================================================

;------------------------------------------------------------------------------
; CHECK_HOME - Check if frog has reached a home zone
;------------------------------------------------------------------------------
check_home:
            ; Only check if at top row
            move.w  frog_grid_y,d0
            tst.w   d0
            bne     .not_home

            ; At top row - determine which home
            move.w  frog_grid_x,d0

            ; Check home 0 (X = 2-3)
            cmp.w   #HOME_0_X,d0
            blt.s   .not_home
            cmp.w   #HOME_0_X+2,d0
            blt.s   .home_0

            ; Check home 1 (X = 6-7)
            cmp.w   #HOME_1_X,d0
            blt.s   .not_home
            cmp.w   #HOME_1_X+2,d0
            blt.s   .home_1

            ; Check home 2 (X = 10-11)
            cmp.w   #HOME_2_X,d0
            blt.s   .not_home
            cmp.w   #HOME_2_X+2,d0
            blt.s   .home_2

            ; Check home 3 (X = 14-15)
            cmp.w   #HOME_3_X,d0
            blt.s   .not_home
            cmp.w   #HOME_3_X+2,d0
            blt.s   .home_3

            ; Check home 4 (X = 18-19)
            cmp.w   #HOME_4_X,d0
            blt.s   .not_home
            cmp.w   #HOME_4_X+2,d0
            blt.s   .home_4

            ; Between homes - death!
            bra     .death_between

.home_0:    moveq   #0,d1
            bra.s   .check_filled
.home_1:    moveq   #1,d1
            bra.s   .check_filled
.home_2:    moveq   #2,d1
            bra.s   .check_filled
.home_3:    moveq   #3,d1
            bra.s   .check_filled
.home_4:    moveq   #4,d1

.check_filled:
            ; D1 = home index (0-4)
            lea     home_filled,a0
            add.w   d1,d1               ; *2 for word offset
            tst.w   0(a0,d1.w)
            bne.s   .death_filled       ; Already filled - death!

            ; Home is empty - fill it!
            move.w  #1,0(a0,d1.w)

            ; Add points
            add.l   #POINTS_PER_HOME,score

            ; Update display
            bsr     update_home_display

            ; Check if all homes filled
            bsr     check_round_complete

            ; Respawn frog
            bsr     reset_frog
            rts

.death_filled:
            ; Landed on filled home - death
            bsr     trigger_death
            rts

.death_between:
            ; Landed between homes (on lily pad gaps) - death
            bsr     trigger_death
            rts

.not_home:
            rts

;------------------------------------------------------------------------------
; CHECK_ROUND_COMPLETE - Check if all 5 homes are filled
;------------------------------------------------------------------------------
check_round_complete:
            lea     home_filled,a0
            moveq   #NUM_HOMES-1,d0
            moveq   #0,d1               ; Count filled homes

.loop:
            tst.w   (a0)+
            beq.s   .not_complete
            addq.w  #1,d1
            dbf     d0,.loop

            ; All homes filled - reset for next round
            cmp.w   #NUM_HOMES,d1
            bne.s   .not_complete

            ; Clear all homes for next round
            lea     home_filled,a0
            moveq   #NUM_HOMES-1,d0
.clear:
            clr.w   (a0)+
            dbf     d0,.clear

            bsr     update_home_display
            ; Could add bonus points here, increase speed, etc.

.not_complete:
            rts

;------------------------------------------------------------------------------
; UPDATE_HOME_DISPLAY - Update Copper colours to show filled homes
;------------------------------------------------------------------------------
update_home_display:
            lea     home_filled,a0
            lea     home_colour_0,a1

            ; Home 0
            tst.w   (a0)+
            beq.s   .home0_empty
            move.w  #COLOUR_HOME_FILLED,(a1)
            bra.s   .home1
.home0_empty:
            move.w  #COLOUR_HOME_LIT,(a1)

.home1:
            lea     home_colour_1,a1
            tst.w   (a0)+
            beq.s   .home1_empty
            move.w  #COLOUR_HOME_FILLED,(a1)
            bra.s   .home2
.home1_empty:
            move.w  #COLOUR_HOME_LIT,(a1)

.home2:
            lea     home_colour_2,a1
            tst.w   (a0)+
            beq.s   .home2_empty
            move.w  #COLOUR_HOME_FILLED,(a1)
            bra.s   .home3
.home2_empty:
            move.w  #COLOUR_HOME_LIT,(a1)

.home3:
            lea     home_colour_3,a1
            tst.w   (a0)+
            beq.s   .home3_empty
            move.w  #COLOUR_HOME_FILLED,(a1)
            bra.s   .home4
.home3_empty:
            move.w  #COLOUR_HOME_LIT,(a1)

.home4:
            lea     home_colour_4,a1
            tst.w   (a0)+
            beq.s   .home4_empty
            move.w  #COLOUR_HOME_FILLED,(a1)
            rts
.home4_empty:
            move.w  #COLOUR_HOME_LIT,(a1)
            rts

;==============================================================================
; LIFE DISPLAY
;==============================================================================

setup_life_sprites:
            ; Sprite 1
            lea     life_icon_1,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)

            ; Sprite 2
            lea     life_icon_2,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)

            ; Sprite 3
            lea     life_icon_3,a0
            move.l  a0,d0
            swap    d0
            lea     spr3pth_val,a1
            move.w  d0,(a1)
            swap    d0
            lea     spr3ptl_val,a1
            move.w  d0,(a1)

            rts

update_life_display:
            ; Life 1 (always show if lives >= 1)
            lea     life_icon_1,a0
            move.w  lives,d0
            cmp.w   #1,d0
            blt.s   .hide_life1
            move.w  #$1e20,$0(a0)
            move.w  #$2600,$2(a0)
            bra.s   .life2
.hide_life1:
            clr.l   (a0)

.life2:
            lea     life_icon_2,a0
            cmp.w   #2,d0
            blt.s   .hide_life2
            move.w  #$1e28,$0(a0)
            move.w  #$2600,$2(a0)
            bra.s   .life3
.hide_life2:
            clr.l   (a0)

.life3:
            lea     life_icon_3,a0
            cmp.w   #3,d0
            blt.s   .hide_life3
            move.w  #$1e30,$0(a0)
            move.w  #$2600,$2(a0)
            rts
.hide_life3:
            clr.l   (a0)
            rts

;==============================================================================
; COLLISION AND DEATH
;==============================================================================

check_collision:
            move.w  frog_grid_y,d0
            cmp.w   #ROAD_ROW_FIRST,d0
            blt     .no_collision
            cmp.w   #ROAD_ROW_LAST,d0
            bgt     .no_collision

            lea     car_data,a2
            moveq   #NUM_CARS-1,d7

.loop:
            move.w  2(a2),d1
            cmp.w   d0,d1
            bne.s   .next_car

            move.w  frog_pixel_x,d2
            move.w  (a2),d3

            move.w  d2,d4
            add.w   #FROG_WIDTH,d4
            cmp.w   d3,d4
            ble.s   .next_car

            move.w  d3,d4
            add.w   #CAR_WIDTH_PX,d4
            cmp.w   d2,d4
            ble.s   .next_car

            bsr     trigger_death
            bra.s   .no_collision

.next_car:
            lea     CAR_STRUCT_SIZE(a2),a2
            dbf     d7,.loop

.no_collision:
            rts

trigger_death:
            move.w  #STATE_DYING,frog_state
            move.w  #DEATH_FRAMES,death_timer
            move.w  #FLASH_COLOUR,flash_colour

            ; Decrement lives
            subq.w  #1,lives
            bsr     update_life_display

            rts

reset_frog:
            move.w  #START_GRID_X,frog_grid_x
            move.w  #START_GRID_Y,frog_grid_y
            move.w  #STATE_IDLE,frog_state
            clr.w   frog_anim_frame
            clr.w   joy_prev
            move.w  #COLOUR_BLACK,flash_colour
            bsr     grid_to_pixels
            rts

;==============================================================================
; CAR MANAGEMENT
;==============================================================================

erase_all_cars:
            lea     car_data,a2
            moveq   #NUM_CARS-1,d7

.loop:
            move.w  (a2),d0
            move.w  2(a2),d1
            bsr     calc_screen_addr
            bsr     wait_blit

            move.l  a0,BLTDPTH(a5)
            move.w  #SCREEN_W-CAR_WIDTH*2,BLTDMOD(a5)
            move.w  #$0100,BLTCON0(a5)
            move.w  #0,BLTCON1(a5)
            move.w  #(CAR_HEIGHT<<6)|CAR_WIDTH,BLTSIZE(a5)

            lea     CAR_STRUCT_SIZE(a2),a2
            dbf     d7,.loop
            rts

move_all_cars:
            lea     car_data,a2
            moveq   #NUM_CARS-1,d7

.loop:
            move.w  (a2),d0
            move.w  4(a2),d1
            add.w   d1,d0

            tst.w   d1
            bmi.s   .check_left
            cmp.w   #320,d0
            blt.s   .store
            sub.w   #320+32,d0
            bra.s   .store
.check_left:
            cmp.w   #-32,d0
            bgt.s   .store
            add.w   #320+32,d0
.store:
            move.w  d0,(a2)
            lea     CAR_STRUCT_SIZE(a2),a2
            dbf     d7,.loop
            rts

draw_all_cars:
            lea     car_data,a2
            moveq   #NUM_CARS-1,d7

.loop:
            move.w  (a2),d0
            move.w  2(a2),d1

            cmp.w   #-32,d0
            blt.s   .next
            cmp.w   #320,d0
            bge.s   .next

            bsr     calc_screen_addr
            bsr     wait_blit

            move.l  #car_gfx,BLTAPTH(a5)
            move.l  a0,BLTDPTH(a5)
            move.w  #$ffff,BLTAFWM(a5)
            move.w  #$ffff,BLTALWM(a5)
            move.w  #0,BLTAMOD(a5)
            move.w  #SCREEN_W-CAR_WIDTH*2,BLTDMOD(a5)
            move.w  #$09f0,BLTCON0(a5)
            move.w  #0,BLTCON1(a5)
            move.w  #(CAR_HEIGHT<<6)|CAR_WIDTH,BLTSIZE(a5)

.next:
            lea     CAR_STRUCT_SIZE(a2),a2
            dbf     d7,.loop
            rts

;==============================================================================
; FROG ROUTINES
;==============================================================================

update_frog:
            move.w  frog_state,d0

            cmp.w   #STATE_DYING,d0
            beq     .dying
            tst.w   d0
            beq     .idle
            bra     .hopping

.dying:
            subq.w  #1,death_timer
            bgt.s   .still_dying

            ; Death complete - check for game over
            tst.w   lives
            beq.s   .game_over

            ; Still have lives - respawn
            bsr     reset_frog
            bra     .done

.game_over:
            move.w  #STATE_GAMEOVER,frog_state
            bra     .done

.still_dying:
            move.w  death_timer,d0
            and.w   #4,d0
            beq.s   .flash_off
            move.w  #FLASH_COLOUR,flash_colour
            bra     .done
.flash_off:
            move.w  #COLOUR_BLACK,flash_colour
            bra     .done

.idle:
            bsr     read_joystick_edge
            tst.w   d0
            beq     .done

            btst    #8,d0
            beq.s   .not_up
            move.w  frog_grid_y,d1
            tst.w   d1
            beq.s   .not_up
            move.w  #DIR_UP,frog_dir
            bra.s   .start_hop
.not_up:
            btst    #0,d0
            beq.s   .not_down
            move.w  frog_grid_y,d1
            cmp.w   #GRID_ROWS-1,d1
            beq.s   .not_down
            move.w  #DIR_DOWN,frog_dir
            bra.s   .start_hop
.not_down:
            btst    #9,d0
            beq.s   .not_left
            move.w  frog_grid_x,d1
            tst.w   d1
            beq.s   .not_left
            move.w  #DIR_LEFT,frog_dir
            bra.s   .start_hop
.not_left:
            btst    #1,d0
            beq     .done
            move.w  frog_grid_x,d1
            cmp.w   #GRID_COLS-1,d1
            beq     .done
            move.w  #DIR_RIGHT,frog_dir

.start_hop:
            move.w  #STATE_HOPPING,frog_state
            clr.w   frog_anim_frame
            bra.s   .done

.hopping:
            addq.w  #1,frog_anim_frame
            move.w  frog_dir,d0

            cmp.w   #DIR_UP,d0
            bne.s   .hop_not_up
            sub.w   #PIXELS_PER_FRAME,frog_pixel_y
            bra.s   .check_done
.hop_not_up:
            cmp.w   #DIR_DOWN,d0
            bne.s   .hop_not_down
            add.w   #PIXELS_PER_FRAME,frog_pixel_y
            bra.s   .check_done
.hop_not_down:
            cmp.w   #DIR_LEFT,d0
            bne.s   .hop_not_left
            sub.w   #PIXELS_PER_FRAME,frog_pixel_x
            bra.s   .check_done
.hop_not_left:
            add.w   #PIXELS_PER_FRAME,frog_pixel_x

.check_done:
            cmp.w   #HOP_FRAMES,frog_anim_frame
            blt.s   .done

            move.w  frog_dir,d0
            cmp.w   #DIR_UP,d0
            bne.s   .end_not_up
            subq.w  #1,frog_grid_y
            bra.s   .snap
.end_not_up:
            cmp.w   #DIR_DOWN,d0
            bne.s   .end_not_down
            addq.w  #1,frog_grid_y
            bra.s   .snap
.end_not_down:
            cmp.w   #DIR_LEFT,d0
            bne.s   .end_not_left
            subq.w  #1,frog_grid_x
            bra.s   .snap
.end_not_left:
            addq.w  #1,frog_grid_x

.snap:
            bsr     grid_to_pixels
            move.w  #STATE_IDLE,frog_state

.done:
            move.w  flash_colour,flash_copper
            rts

;==============================================================================
; UTILITY ROUTINES
;==============================================================================

grid_to_pixels:
            move.w  frog_grid_x,d0
            mulu    #CELL_SIZE,d0
            add.w   #GRID_ORIGIN_X,d0
            move.w  d0,frog_pixel_x

            move.w  frog_grid_y,d0
            mulu    #CELL_SIZE,d0
            add.w   #GRID_ORIGIN_Y,d0
            move.w  d0,frog_pixel_y
            rts

read_joystick_edge:
            move.w  JOY1DAT(a5),d0
            move.w  d0,d1
            lsr.w   #1,d1
            eor.w   d1,d0

            move.w  joy_prev,d1
            not.w   d1
            and.w   d0,d1
            move.w  d0,joy_prev

            move.w  d1,d0
            rts

wait_vblank:
            move.l  #$1ff00,d1
.wait:
            move.l  VPOSR(a5),d0
            and.l   d1,d0
            bne.s   .wait
            rts

wait_blit:
            btst    #6,DMACONR(a5)
            bne.s   wait_blit
            rts

clear_screen:
            bsr.s   wait_blit
            move.l  #screen_plane,BLTDPTH(a5)
            move.w  #0,BLTDMOD(a5)
            move.w  #$0100,BLTCON0(a5)
            move.w  #0,BLTCON1(a5)
            move.w  #(SCREEN_H<<6)|SCREEN_W/2,BLTSIZE(a5)
            rts

calc_screen_addr:
            lea     screen_plane,a0
            move.w  d1,d2
            mulu    #CELL_SIZE,d2
            add.w   #GRID_ORIGIN_Y,d2
            mulu    #SCREEN_W,d2
            add.l   d2,a0
            move.w  d0,d2
            lsr.w   #3,d2
            ext.l   d2
            add.l   d2,a0
            rts

update_sprite:
            lea     frog_data,a0
            move.w  frog_pixel_y,d0
            move.w  frog_pixel_x,d1

            move.w  d0,d2
            lsl.w   #8,d2
            lsr.w   #1,d1
            or.b    d1,d2
            move.w  d2,(a0)

            add.w   #FROG_HEIGHT,d0
            lsl.w   #8,d0
            move.w  d0,2(a0)
            rts

;==============================================================================
; VARIABLES
;==============================================================================

frog_grid_x:    dc.w    9
frog_grid_y:    dc.w    12
frog_pixel_x:   dc.w    0
frog_pixel_y:   dc.w    0
frog_state:     dc.w    0
frog_dir:       dc.w    0
frog_anim_frame: dc.w   0
joy_prev:       dc.w    0

death_timer:    dc.w    0
flash_colour:   dc.w    0

lives:          dc.w    3
score:          dc.l    0

; Home zone state (0 = empty, 1 = filled)
home_filled:    dc.w    0,0,0,0,0

car_data:
            dc.w    0,7,1,0
            dc.w    160,7,1,0
            dc.w    100,8,-2,0
            dc.w    250,8,-2,0
            dc.w    50,9,2,0
            dc.w    200,9,2,0
            dc.w    80,10,-1,0
            dc.w    220,10,-1,0
            dc.w    30,11,3,0
            dc.w    180,11,3,0

;==============================================================================
; COPPER LIST
;==============================================================================

copperlist:
            dc.w    COLOR00
flash_copper:
            dc.w    COLOUR_BLACK

            dc.w    $0100,$1200
            dc.w    $0102,$0000
            dc.w    $0104,$0000
            dc.w    $0108,$0000
            dc.w    $010a,$0000

            dc.w    $00e0
bplpth_val: dc.w    $0000
            dc.w    $00e2
bplptl_val: dc.w    $0000

            dc.w    $0180,$0000
            dc.w    $0182,COLOUR_CAR

            ; Sprite 0-1 palette (frog and life icons share this)
            dc.w    $01a2,COLOUR_FROG
            dc.w    $01a4,COLOUR_EYES
            dc.w    $01a6,COLOUR_OUTLINE

            ; Sprite 0 pointer (main frog)
            dc.w    SPR0PTH
sprpth_val: dc.w    $0000
            dc.w    SPR0PTL
sprptl_val: dc.w    $0000

            ; Sprite 1 pointer (life icon 1)
            dc.w    SPR1PTH
spr1pth_val: dc.w   $0000
            dc.w    SPR1PTL
spr1ptl_val: dc.w   $0000

            ; Sprite 2 pointer (life icon 2)
            dc.w    SPR2PTH
spr2pth_val: dc.w   $0000
            dc.w    SPR2PTL
spr2ptl_val: dc.w   $0000

            ; Sprite 3 pointer (life icon 3)
            dc.w    $0126           ; SPR3PTH
spr3pth_val: dc.w   $0000
            dc.w    $012a           ; SPR3PTL
spr3ptl_val: dc.w   $0000

            ; Home zone row - 5 distinct zones for visual feedback
            ; Each home changes colour when filled
            dc.w    $2c07,$fffe
            dc.w    COLOR00,COLOUR_HOME     ; Gaps between homes

            ; Home 0 (columns 2-3, pixels 48+32=80 to 48+48=96)
            dc.w    $2c4f,$fffe             ; Wait for X=79
            dc.w    COLOR00
home_colour_0:
            dc.w    COLOUR_HOME_LIT

            dc.w    $2c5f,$fffe             ; Wait for X=95
            dc.w    COLOR00,COLOUR_HOME     ; Gap

            ; Home 1 (columns 6-7)
            dc.w    $2c8f,$fffe             ; Wait for X=143
            dc.w    COLOR00
home_colour_1:
            dc.w    COLOUR_HOME_LIT

            dc.w    $2c9f,$fffe             ; Wait for X=159
            dc.w    COLOR00,COLOUR_HOME     ; Gap

            ; Home 2 (columns 10-11)
            dc.w    $2ccf,$fffe             ; Wait for X=207
            dc.w    COLOR00
home_colour_2:
            dc.w    COLOUR_HOME_LIT

            dc.w    $2cdf,$fffe             ; Wait for X=223
            dc.w    COLOR00,COLOUR_HOME     ; Gap

            ; Home 3 (columns 14-15) - past X=223, need high bit
            dc.w    $2d07,$fffe             ; Wait for next pixel region
            dc.w    COLOR00
home_colour_3:
            dc.w    COLOUR_HOME_LIT

            dc.w    $2d17,$fffe
            dc.w    COLOR00,COLOUR_HOME     ; Gap

            ; Home 4 (columns 18-19)
            dc.w    $2d47,$fffe
            dc.w    COLOR00
home_colour_4:
            dc.w    COLOUR_HOME_LIT

            dc.w    $2d57,$fffe
            dc.w    COLOR00,COLOUR_HOME     ; End

            ; Rest of home row - solid colour
            dc.w    $3407,$fffe
            dc.w    COLOR00,COLOUR_HOME_LIT
            dc.w    $3807,$fffe
            dc.w    COLOR00,COLOUR_HOME

            ; Water rows
            dc.w    $3c07,$fffe
            dc.w    COLOR00,COLOUR_WATER1
            dc.w    $4c07,$fffe
            dc.w    COLOR00,COLOUR_WATER2
            dc.w    $5c07,$fffe
            dc.w    COLOR00,COLOUR_WATER1
            dc.w    $6c07,$fffe
            dc.w    COLOR00,COLOUR_WATER2
            dc.w    $7c07,$fffe
            dc.w    COLOR00,COLOUR_WATER1

            ; Median
            dc.w    $8c07,$fffe
            dc.w    COLOR00,COLOUR_MEDIAN

            ; Road rows
            dc.w    $9c07,$fffe
            dc.w    COLOR00,COLOUR_ROAD1
            dc.w    $ac07,$fffe
            dc.w    COLOR00,COLOUR_ROAD2
            dc.w    $bc07,$fffe
            dc.w    COLOR00,COLOUR_ROAD1
            dc.w    $cc07,$fffe
            dc.w    COLOR00,COLOUR_ROAD2
            dc.w    $dc07,$fffe
            dc.w    COLOR00,COLOUR_ROAD1

            ; Start zone
            dc.w    $ec07,$fffe
            dc.w    COLOR00,COLOUR_START
            dc.w    $f407,$fffe
            dc.w    COLOR00,COLOUR_START_LIT
            dc.w    $f807,$fffe
            dc.w    COLOR00,COLOUR_START

            dc.w    $fc07,$fffe
            dc.w    COLOR00,COLOUR_BLACK

            dc.w    $ffff,$fffe

;==============================================================================
; GRAPHICS DATA
;==============================================================================

            even
car_gfx:
            dc.w    $0ff0,$0ff0
            dc.w    $3ffc,$3ffc
            dc.w    $7ffe,$7ffe
            dc.w    $ffff,$ffff
            dc.w    $ffff,$ffff
            dc.w    $ffff,$ffff
            dc.w    $ffff,$ffff
            dc.w    $ffff,$ffff
            dc.w    $ffff,$ffff
            dc.w    $7ffe,$7ffe
            dc.w    $3c3c,$3c3c
            dc.w    $0000,$0000

            even
frog_data:
            dc.w    $ec50,$fc00

            dc.w    $0000,$0000
            dc.w    $07e0,$0000
            dc.w    $1ff8,$0420
            dc.w    $3ffc,$0a50
            dc.w    $7ffe,$1248
            dc.w    $7ffe,$1008
            dc.w    $ffff,$2004
            dc.w    $ffff,$0000
            dc.w    $ffff,$0000
            dc.w    $7ffe,$2004
            dc.w    $7ffe,$1008
            dc.w    $3ffc,$0810
            dc.w    $1ff8,$0420
            dc.w    $07e0,$0000
            dc.w    $0000,$0000
            dc.w    $0000,$0000

            dc.w    $0000,$0000

; Life icon sprites (small frog shape, 8 pixels tall)
            even
life_icon_1:
            dc.w    $0000,$0000
            dc.w    $0000,$0000
            dc.w    $1800,$0000
            dc.w    $3c00,$0000
            dc.w    $7e00,$0000
            dc.w    $7e00,$0000
            dc.w    $3c00,$0000
            dc.w    $1800,$0000
            dc.w    $0000,$0000
            dc.w    $0000,$0000

            even
life_icon_2:
            dc.w    $0000,$0000
            dc.w    $0000,$0000
            dc.w    $1800,$0000
            dc.w    $3c00,$0000
            dc.w    $7e00,$0000
            dc.w    $7e00,$0000
            dc.w    $3c00,$0000
            dc.w    $1800,$0000
            dc.w    $0000,$0000
            dc.w    $0000,$0000

            even
life_icon_3:
            dc.w    $0000,$0000
            dc.w    $0000,$0000
            dc.w    $1800,$0000
            dc.w    $3c00,$0000
            dc.w    $7e00,$0000
            dc.w    $7e00,$0000
            dc.w    $3c00,$0000
            dc.w    $1800,$0000
            dc.w    $0000,$0000
            dc.w    $0000,$0000

;==============================================================================
; SCREEN BUFFER
;==============================================================================

            section chipbss,bss_c

            even
screen_plane:
            ds.b    PLANE_SIZE

Build It

vasmm68k_mot -Fhunkexe -kick1hunks -o signal signal.asm

What’s Next

The game has goals and stakes, but no audio feedback. In Unit 11, we’ll add sound effects—beeps and buzzes that give the game life.

What Changed

Unit 9 → Unit 10
+327-72
1-;══════════════════════════════════════════════════════════════════════════════
1+;==============================================================================
22 ; SIGNAL - A Frogger-style game for the Commodore Amiga
3-; Unit 9: Lives System
3+; Unit 10: Home Zones
44 ;
5-; Infinite respawns means no tension. This unit adds a lives system: start
6-; with 3 lives, lose one on each death, game over when empty. Small frog
7-; icons in the corner show your remaining chances.
8-;══════════════════════════════════════════════════════════════════════════════
5+; Stakes alone aren't enough—you need a goal. This unit adds 5 home zones at
6+; the top of the screen. Reach one, it fills in, you respawn. Fill all 5 to
7+; complete a round. Each successful docking scores 50 points.
8+;==============================================================================
99
10-;══════════════════════════════════════════════════════════════════════════════
10+;==============================================================================
1111 ; CONSTANTS
12-;══════════════════════════════════════════════════════════════════════════════
12+;==============================================================================
1313
1414 SCREEN_W equ 40
1515 SCREEN_H equ 256
...
2828 STATE_IDLE equ 0
2929 STATE_HOPPING equ 1
3030 STATE_DYING equ 2
31-STATE_GAMEOVER equ 3 ; NEW: Game over state
31+STATE_GAMEOVER equ 3
3232 DIR_UP equ 0
3333 DIR_DOWN equ 1
3434 DIR_LEFT equ 2
...
5050
5151 ; Lives system
5252 START_LIVES equ 3
53-LIFE_ICON_W equ 1 ; 16 pixels = 1 word
53+LIFE_ICON_W equ 1
5454 LIFE_ICON_H equ 8
55-LIFE_ICON_X equ 8 ; Left margin
56-LIFE_ICON_Y equ 260 ; Below visible area (we'll position in border)
57-LIFE_ICON_SPACING equ 20 ; Pixels between icons
55+LIFE_ICON_X equ 8
56+LIFE_ICON_Y equ 260
57+LIFE_ICON_SPACING equ 20
58+
59+; Home zones
60+NUM_HOMES equ 5
61+HOME_ROW equ 0 ; Top row of grid
62+POINTS_PER_HOME equ 50
63+
64+; Home X positions (grid coordinates): 2, 6, 10, 14, 18
65+; Each home is 2 cells wide (even column = start of home)
66+HOME_0_X equ 2
67+HOME_1_X equ 6
68+HOME_2_X equ 10
69+HOME_3_X equ 14
70+HOME_4_X equ 18
5871
5972 ; Colours
6073 COLOUR_BLACK equ $0000
6174 COLOUR_HOME equ $0282
6275 COLOUR_HOME_LIT equ $03a3
76+COLOUR_HOME_FILLED equ $0f80 ; Orange for filled homes
6377 COLOUR_WATER1 equ $0148
6478 COLOUR_WATER2 equ $026a
6579 COLOUR_MEDIAN equ $0383
...
7185 COLOUR_EYES equ $0ff0
7286 COLOUR_OUTLINE equ $0000
7387 COLOUR_CAR equ $0f00
74-COLOUR_LIFE equ $00f0 ; Green for life icons
88+COLOUR_LIFE equ $00f0
7589
76-;══════════════════════════════════════════════════════════════════════════════
90+;==============================================================================
7791 ; HARDWARE REGISTERS
78-;══════════════════════════════════════════════════════════════════════════════
92+;==============================================================================
7993
8094 CUSTOM equ $dff000
8195
...
110124 SPR2PTH equ $128
111125 SPR2PTL equ $12a
112126
113-;══════════════════════════════════════════════════════════════════════════════
127+;==============================================================================
114128 ; CODE SECTION
115-;══════════════════════════════════════════════════════════════════════════════
129+;==============================================================================
116130
117131 section code,code_c
118132
...
136150 move.w d0,(a1)
137151
138152 ; Initialise game
139- move.w #START_LIVES,lives
140- bsr reset_frog
153+ bsr init_game
141154
142155 ; Set main sprite pointer (sprite 0 = frog)
143156 lea frog_data,a0
...
163176 ; Enable DMA
164177 move.w #$87e0,DMACON(a5)
165178
166-;══════════════════════════════════════════════════════════════════════════════
179+;==============================================================================
167180 ; MAIN LOOP
168-;══════════════════════════════════════════════════════════════════════════════
181+;==============================================================================
169182
170183 mainloop:
171184 bsr wait_vblank
...
184197
185198 cmp.w #STATE_DYING,frog_state
186199 beq.s .skip_collision
200+ cmp.w #STATE_IDLE,frog_state
201+ bne.s .skip_collision
202+ bsr check_home
187203 bsr check_collision
188204 .skip_collision:
189205
...
191207
192208 .game_over_loop:
193209 ; Game over - wait for fire button to restart
194- btst #7,$bfe001 ; Check fire button (active low)
195- bne.s mainloop ; Not pressed, keep waiting
210+ btst #7,$bfe001
211+ bne.s mainloop
196212
197213 ; Restart game
198- move.w #START_LIVES,lives
199- bsr reset_frog
214+ bsr init_game
200215 bsr update_life_display
201216 bsr clear_screen
202217
203218 bra mainloop
204219
205-;══════════════════════════════════════════════════════════════════════════════
206-; LIFE DISPLAY
207-;══════════════════════════════════════════════════════════════════════════════
220+;==============================================================================
221+; GAME INITIALISATION
222+;==============================================================================
223+
224+init_game:
225+ move.w #START_LIVES,lives
226+ clr.l score
227+
228+ ; Clear all homes
229+ lea home_filled,a0
230+ moveq #NUM_HOMES-1,d0
231+.clear_homes:
232+ clr.w (a0)+
233+ dbf d0,.clear_homes
234+
235+ bsr update_home_display
236+ bsr reset_frog
237+ rts
238+
239+;==============================================================================
240+; HOME ZONE DETECTION
241+;==============================================================================
208242
209243 ;------------------------------------------------------------------------------
210-; SETUP_LIFE_SPRITES - Set sprite pointers for life icons
244+; CHECK_HOME - Check if frog has reached a home zone
245+;------------------------------------------------------------------------------
246+check_home:
247+ ; Only check if at top row
248+ move.w frog_grid_y,d0
249+ tst.w d0
250+ bne .not_home
251+
252+ ; At top row - determine which home
253+ move.w frog_grid_x,d0
254+
255+ ; Check home 0 (X = 2-3)
256+ cmp.w #HOME_0_X,d0
257+ blt.s .not_home
258+ cmp.w #HOME_0_X+2,d0
259+ blt.s .home_0
260+
261+ ; Check home 1 (X = 6-7)
262+ cmp.w #HOME_1_X,d0
263+ blt.s .not_home
264+ cmp.w #HOME_1_X+2,d0
265+ blt.s .home_1
266+
267+ ; Check home 2 (X = 10-11)
268+ cmp.w #HOME_2_X,d0
269+ blt.s .not_home
270+ cmp.w #HOME_2_X+2,d0
271+ blt.s .home_2
272+
273+ ; Check home 3 (X = 14-15)
274+ cmp.w #HOME_3_X,d0
275+ blt.s .not_home
276+ cmp.w #HOME_3_X+2,d0
277+ blt.s .home_3
278+
279+ ; Check home 4 (X = 18-19)
280+ cmp.w #HOME_4_X,d0
281+ blt.s .not_home
282+ cmp.w #HOME_4_X+2,d0
283+ blt.s .home_4
284+
285+ ; Between homes - death!
286+ bra .death_between
287+
288+.home_0: moveq #0,d1
289+ bra.s .check_filled
290+.home_1: moveq #1,d1
291+ bra.s .check_filled
292+.home_2: moveq #2,d1
293+ bra.s .check_filled
294+.home_3: moveq #3,d1
295+ bra.s .check_filled
296+.home_4: moveq #4,d1
297+
298+.check_filled:
299+ ; D1 = home index (0-4)
300+ lea home_filled,a0
301+ add.w d1,d1 ; *2 for word offset
302+ tst.w 0(a0,d1.w)
303+ bne.s .death_filled ; Already filled - death!
304+
305+ ; Home is empty - fill it!
306+ move.w #1,0(a0,d1.w)
307+
308+ ; Add points
309+ add.l #POINTS_PER_HOME,score
310+
311+ ; Update display
312+ bsr update_home_display
313+
314+ ; Check if all homes filled
315+ bsr check_round_complete
316+
317+ ; Respawn frog
318+ bsr reset_frog
319+ rts
320+
321+.death_filled:
322+ ; Landed on filled home - death
323+ bsr trigger_death
324+ rts
325+
326+.death_between:
327+ ; Landed between homes (on lily pad gaps) - death
328+ bsr trigger_death
329+ rts
330+
331+.not_home:
332+ rts
333+
334+;------------------------------------------------------------------------------
335+; CHECK_ROUND_COMPLETE - Check if all 5 homes are filled
336+;------------------------------------------------------------------------------
337+check_round_complete:
338+ lea home_filled,a0
339+ moveq #NUM_HOMES-1,d0
340+ moveq #0,d1 ; Count filled homes
341+
342+.loop:
343+ tst.w (a0)+
344+ beq.s .not_complete
345+ addq.w #1,d1
346+ dbf d0,.loop
347+
348+ ; All homes filled - reset for next round
349+ cmp.w #NUM_HOMES,d1
350+ bne.s .not_complete
351+
352+ ; Clear all homes for next round
353+ lea home_filled,a0
354+ moveq #NUM_HOMES-1,d0
355+.clear:
356+ clr.w (a0)+
357+ dbf d0,.clear
358+
359+ bsr update_home_display
360+ ; Could add bonus points here, increase speed, etc.
361+
362+.not_complete:
363+ rts
364+
365+;------------------------------------------------------------------------------
366+; UPDATE_HOME_DISPLAY - Update Copper colours to show filled homes
211367 ;------------------------------------------------------------------------------
368+update_home_display:
369+ lea home_filled,a0
370+ lea home_colour_0,a1
371+
372+ ; Home 0
373+ tst.w (a0)+
374+ beq.s .home0_empty
375+ move.w #COLOUR_HOME_FILLED,(a1)
376+ bra.s .home1
377+.home0_empty:
378+ move.w #COLOUR_HOME_LIT,(a1)
379+
380+.home1:
381+ lea home_colour_1,a1
382+ tst.w (a0)+
383+ beq.s .home1_empty
384+ move.w #COLOUR_HOME_FILLED,(a1)
385+ bra.s .home2
386+.home1_empty:
387+ move.w #COLOUR_HOME_LIT,(a1)
388+
389+.home2:
390+ lea home_colour_2,a1
391+ tst.w (a0)+
392+ beq.s .home2_empty
393+ move.w #COLOUR_HOME_FILLED,(a1)
394+ bra.s .home3
395+.home2_empty:
396+ move.w #COLOUR_HOME_LIT,(a1)
397+
398+.home3:
399+ lea home_colour_3,a1
400+ tst.w (a0)+
401+ beq.s .home3_empty
402+ move.w #COLOUR_HOME_FILLED,(a1)
403+ bra.s .home4
404+.home3_empty:
405+ move.w #COLOUR_HOME_LIT,(a1)
406+
407+.home4:
408+ lea home_colour_4,a1
409+ tst.w (a0)+
410+ beq.s .home4_empty
411+ move.w #COLOUR_HOME_FILLED,(a1)
412+ rts
413+.home4_empty:
414+ move.w #COLOUR_HOME_LIT,(a1)
415+ rts
416+
417+;==============================================================================
418+; LIFE DISPLAY
419+;==============================================================================
420+
212421 setup_life_sprites:
213422 ; Sprite 1
214423 lea life_icon_1,a0
...
242451
243452 rts
244453
245-;------------------------------------------------------------------------------
246-; UPDATE_LIFE_DISPLAY - Show/hide life icons based on lives count
247-;------------------------------------------------------------------------------
248454 update_life_display:
249- ; Position life icons at top of screen
250- ; VSTART = 30, VSTOP = 38 (8 pixels tall)
251- ; HSTART varies: 64, 80, 96 (sprite coords)
252-
253455 ; Life 1 (always show if lives >= 1)
254456 lea life_icon_1,a0
255457 move.w lives,d0
256458 cmp.w #1,d0
257459 blt.s .hide_life1
258- move.w #$1e20,$0(a0) ; VSTART=30, HSTART=64
259- move.w #$2600,$2(a0) ; VSTOP=38
460+ move.w #$1e20,$0(a0)
461+ move.w #$2600,$2(a0)
260462 bra.s .life2
261463 .hide_life1:
262- clr.l (a0) ; Hide sprite
464+ clr.l (a0)
263465
264466 .life2:
265467 lea life_icon_2,a0
266468 cmp.w #2,d0
267469 blt.s .hide_life2
268- move.w #$1e28,$0(a0) ; VSTART=30, HSTART=80
470+ move.w #$1e28,$0(a0)
269471 move.w #$2600,$2(a0)
270472 bra.s .life3
271473 .hide_life2:
...
275477 lea life_icon_3,a0
276478 cmp.w #3,d0
277479 blt.s .hide_life3
278- move.w #$1e30,$0(a0) ; VSTART=30, HSTART=96
480+ move.w #$1e30,$0(a0)
279481 move.w #$2600,$2(a0)
280482 rts
281483 .hide_life3:
282484 clr.l (a0)
283485 rts
284486
285-;══════════════════════════════════════════════════════════════════════════════
487+;==============================================================================
286488 ; COLLISION AND DEATH
287-;══════════════════════════════════════════════════════════════════════════════
489+;==============================================================================
288490
289491 check_collision:
290492 move.w frog_grid_y,d0
...
335537
336538 rts
337539
338-;------------------------------------------------------------------------------
339-; RESET_FROG - Reset frog position (called on respawn and game start)
340-;------------------------------------------------------------------------------
341540 reset_frog:
342541 move.w #START_GRID_X,frog_grid_x
343542 move.w #START_GRID_Y,frog_grid_y
...
348547 bsr grid_to_pixels
349548 rts
350549
351-;══════════════════════════════════════════════════════════════════════════════
550+;==============================================================================
352551 ; CAR MANAGEMENT
353-;══════════════════════════════════════════════════════════════════════════════
552+;==============================================================================
354553
355554 erase_all_cars:
356555 lea car_data,a2
...
428627 dbf d7,.loop
429628 rts
430629
431-;══════════════════════════════════════════════════════════════════════════════
630+;==============================================================================
432631 ; FROG ROUTINES
433-;══════════════════════════════════════════════════════════════════════════════
632+;==============================================================================
434633
435634 update_frog:
436635 move.w frog_state,d0
...
559758 move.w flash_colour,flash_copper
560759 rts
561760
562-;══════════════════════════════════════════════════════════════════════════════
761+;==============================================================================
563762 ; UTILITY ROUTINES
564-;══════════════════════════════════════════════════════════════════════════════
763+;==============================================================================
565764
566765 grid_to_pixels:
567766 move.w frog_grid_x,d0
...
640839 move.w d0,2(a0)
641840 rts
642841
643-;══════════════════════════════════════════════════════════════════════════════
842+;==============================================================================
644843 ; VARIABLES
645-;══════════════════════════════════════════════════════════════════════════════
844+;==============================================================================
646845
647846 frog_grid_x: dc.w 9
648847 frog_grid_y: dc.w 12
...
657856 flash_colour: dc.w 0
658857
659858 lives: dc.w 3
859+score: dc.l 0
860+
861+; Home zone state (0 = empty, 1 = filled)
862+home_filled: dc.w 0,0,0,0,0
660863
661864 car_data:
662865 dc.w 0,7,1,0
...
670873 dc.w 30,11,3,0
671874 dc.w 180,11,3,0
672875
673-;══════════════════════════════════════════════════════════════════════════════
876+;==============================================================================
674877 ; COPPER LIST
675-;══════════════════════════════════════════════════════════════════════════════
878+;==============================================================================
676879
677880 copperlist:
678881 dc.w COLOR00
...
719922 ; Sprite 3 pointer (life icon 3)
720923 dc.w $0126 ; SPR3PTH
721924 spr3pth_val: dc.w $0000
722- dc.w $0128 ; SPR3PTL (note: this is wrong, should be $012a)
925+ dc.w $012a ; SPR3PTL
723926 spr3ptl_val: dc.w $0000
724927
725- ; Zone colours
928+ ; Home zone row - 5 distinct zones for visual feedback
929+ ; Each home changes colour when filled
726930 dc.w $2c07,$fffe
727- dc.w COLOR00,COLOUR_HOME
931+ dc.w COLOR00,COLOUR_HOME ; Gaps between homes
932+
933+ ; Home 0 (columns 2-3, pixels 48+32=80 to 48+48=96)
934+ dc.w $2c4f,$fffe ; Wait for X=79
935+ dc.w COLOR00
936+home_colour_0:
937+ dc.w COLOUR_HOME_LIT
938+
939+ dc.w $2c5f,$fffe ; Wait for X=95
940+ dc.w COLOR00,COLOUR_HOME ; Gap
941+
942+ ; Home 1 (columns 6-7)
943+ dc.w $2c8f,$fffe ; Wait for X=143
944+ dc.w COLOR00
945+home_colour_1:
946+ dc.w COLOUR_HOME_LIT
947+
948+ dc.w $2c9f,$fffe ; Wait for X=159
949+ dc.w COLOR00,COLOUR_HOME ; Gap
950+
951+ ; Home 2 (columns 10-11)
952+ dc.w $2ccf,$fffe ; Wait for X=207
953+ dc.w COLOR00
954+home_colour_2:
955+ dc.w COLOUR_HOME_LIT
956+
957+ dc.w $2cdf,$fffe ; Wait for X=223
958+ dc.w COLOR00,COLOUR_HOME ; Gap
959+
960+ ; Home 3 (columns 14-15) - past X=223, need high bit
961+ dc.w $2d07,$fffe ; Wait for next pixel region
962+ dc.w COLOR00
963+home_colour_3:
964+ dc.w COLOUR_HOME_LIT
965+
966+ dc.w $2d17,$fffe
967+ dc.w COLOR00,COLOUR_HOME ; Gap
968+
969+ ; Home 4 (columns 18-19)
970+ dc.w $2d47,$fffe
971+ dc.w COLOR00
972+home_colour_4:
973+ dc.w COLOUR_HOME_LIT
974+
975+ dc.w $2d57,$fffe
976+ dc.w COLOR00,COLOUR_HOME ; End
977+
978+ ; Rest of home row - solid colour
728979 dc.w $3407,$fffe
729980 dc.w COLOR00,COLOUR_HOME_LIT
730981 dc.w $3807,$fffe
731982 dc.w COLOR00,COLOUR_HOME
732983
984+ ; Water rows
733985 dc.w $3c07,$fffe
734986 dc.w COLOR00,COLOUR_WATER1
735987 dc.w $4c07,$fffe
...
741993 dc.w $7c07,$fffe
742994 dc.w COLOR00,COLOUR_WATER1
743995
996+ ; Median
744997 dc.w $8c07,$fffe
745998 dc.w COLOR00,COLOUR_MEDIAN
746999
1000+ ; Road rows
7471001 dc.w $9c07,$fffe
7481002 dc.w COLOR00,COLOUR_ROAD1
7491003 dc.w $ac07,$fffe
...
7551009 dc.w $dc07,$fffe
7561010 dc.w COLOR00,COLOUR_ROAD1
7571011
1012+ ; Start zone
7581013 dc.w $ec07,$fffe
7591014 dc.w COLOR00,COLOUR_START
7601015 dc.w $f407,$fffe
...
7671022
7681023 dc.w $ffff,$fffe
7691024
770-;══════════════════════════════════════════════════════════════════════════════
1025+;==============================================================================
7711026 ; GRAPHICS DATA
772-;══════════════════════════════════════════════════════════════════════════════
1027+;==============================================================================
7731028
7741029 even
7751030 car_gfx:
...
8121067 ; Life icon sprites (small frog shape, 8 pixels tall)
8131068 even
8141069 life_icon_1:
815- dc.w $0000,$0000 ; Control words (set by code)
8161070 dc.w $0000,$0000
817- dc.w $1800,$0000 ; ..##............
818- dc.w $3c00,$0000 ; .####...........
819- dc.w $7e00,$0000 ; .######.........
820- dc.w $7e00,$0000 ; .######.........
821- dc.w $3c00,$0000 ; .####...........
822- dc.w $1800,$0000 ; ..##............
8231071 dc.w $0000,$0000
824- dc.w $0000,$0000 ; End marker
1072+ dc.w $1800,$0000
1073+ dc.w $3c00,$0000
1074+ dc.w $7e00,$0000
1075+ dc.w $7e00,$0000
1076+ dc.w $3c00,$0000
1077+ dc.w $1800,$0000
1078+ dc.w $0000,$0000
1079+ dc.w $0000,$0000
8251080
8261081 even
8271082 life_icon_2:
...
8491104 dc.w $0000,$0000
8501105 dc.w $0000,$0000
8511106
852-;══════════════════════════════════════════════════════════════════════════════
1107+;==============================================================================
8531108 ; SCREEN BUFFER
854-;══════════════════════════════════════════════════════════════════════════════
1109+;==============================================================================
8551110
8561111 section chipbss,bss_c
8571112