Home Zones
Reach the top. Fill the homes. Score points. Respawn and repeat.
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

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
| 1 | - | ;══════════════════════════════════════════════════════════════════════════════ | |
| 1 | + | ;============================================================================== | |
| 2 | 2 | ; SIGNAL - A Frogger-style game for the Commodore Amiga | |
| 3 | - | ; Unit 9: Lives System | |
| 3 | + | ; Unit 10: Home Zones | |
| 4 | 4 | ; | |
| 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 | + | ;============================================================================== | |
| 9 | 9 | | |
| 10 | - | ;══════════════════════════════════════════════════════════════════════════════ | |
| 10 | + | ;============================================================================== | |
| 11 | 11 | ; CONSTANTS | |
| 12 | - | ;══════════════════════════════════════════════════════════════════════════════ | |
| 12 | + | ;============================================================================== | |
| 13 | 13 | | |
| 14 | 14 | SCREEN_W equ 40 | |
| 15 | 15 | SCREEN_H equ 256 | |
| ... | |||
| 28 | 28 | STATE_IDLE equ 0 | |
| 29 | 29 | STATE_HOPPING equ 1 | |
| 30 | 30 | STATE_DYING equ 2 | |
| 31 | - | STATE_GAMEOVER equ 3 ; NEW: Game over state | |
| 31 | + | STATE_GAMEOVER equ 3 | |
| 32 | 32 | DIR_UP equ 0 | |
| 33 | 33 | DIR_DOWN equ 1 | |
| 34 | 34 | DIR_LEFT equ 2 | |
| ... | |||
| 50 | 50 | | |
| 51 | 51 | ; Lives system | |
| 52 | 52 | START_LIVES equ 3 | |
| 53 | - | LIFE_ICON_W equ 1 ; 16 pixels = 1 word | |
| 53 | + | LIFE_ICON_W equ 1 | |
| 54 | 54 | 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 | |
| 58 | 71 | | |
| 59 | 72 | ; Colours | |
| 60 | 73 | COLOUR_BLACK equ $0000 | |
| 61 | 74 | COLOUR_HOME equ $0282 | |
| 62 | 75 | COLOUR_HOME_LIT equ $03a3 | |
| 76 | + | COLOUR_HOME_FILLED equ $0f80 ; Orange for filled homes | |
| 63 | 77 | COLOUR_WATER1 equ $0148 | |
| 64 | 78 | COLOUR_WATER2 equ $026a | |
| 65 | 79 | COLOUR_MEDIAN equ $0383 | |
| ... | |||
| 71 | 85 | COLOUR_EYES equ $0ff0 | |
| 72 | 86 | COLOUR_OUTLINE equ $0000 | |
| 73 | 87 | COLOUR_CAR equ $0f00 | |
| 74 | - | COLOUR_LIFE equ $00f0 ; Green for life icons | |
| 88 | + | COLOUR_LIFE equ $00f0 | |
| 75 | 89 | | |
| 76 | - | ;══════════════════════════════════════════════════════════════════════════════ | |
| 90 | + | ;============================================================================== | |
| 77 | 91 | ; HARDWARE REGISTERS | |
| 78 | - | ;══════════════════════════════════════════════════════════════════════════════ | |
| 92 | + | ;============================================================================== | |
| 79 | 93 | | |
| 80 | 94 | CUSTOM equ $dff000 | |
| 81 | 95 | | |
| ... | |||
| 110 | 124 | SPR2PTH equ $128 | |
| 111 | 125 | SPR2PTL equ $12a | |
| 112 | 126 | | |
| 113 | - | ;══════════════════════════════════════════════════════════════════════════════ | |
| 127 | + | ;============================================================================== | |
| 114 | 128 | ; CODE SECTION | |
| 115 | - | ;══════════════════════════════════════════════════════════════════════════════ | |
| 129 | + | ;============================================================================== | |
| 116 | 130 | | |
| 117 | 131 | section code,code_c | |
| 118 | 132 | | |
| ... | |||
| 136 | 150 | move.w d0,(a1) | |
| 137 | 151 | | |
| 138 | 152 | ; Initialise game | |
| 139 | - | move.w #START_LIVES,lives | |
| 140 | - | bsr reset_frog | |
| 153 | + | bsr init_game | |
| 141 | 154 | | |
| 142 | 155 | ; Set main sprite pointer (sprite 0 = frog) | |
| 143 | 156 | lea frog_data,a0 | |
| ... | |||
| 163 | 176 | ; Enable DMA | |
| 164 | 177 | move.w #$87e0,DMACON(a5) | |
| 165 | 178 | | |
| 166 | - | ;══════════════════════════════════════════════════════════════════════════════ | |
| 179 | + | ;============================================================================== | |
| 167 | 180 | ; MAIN LOOP | |
| 168 | - | ;══════════════════════════════════════════════════════════════════════════════ | |
| 181 | + | ;============================================================================== | |
| 169 | 182 | | |
| 170 | 183 | mainloop: | |
| 171 | 184 | bsr wait_vblank | |
| ... | |||
| 184 | 197 | | |
| 185 | 198 | cmp.w #STATE_DYING,frog_state | |
| 186 | 199 | beq.s .skip_collision | |
| 200 | + | cmp.w #STATE_IDLE,frog_state | |
| 201 | + | bne.s .skip_collision | |
| 202 | + | bsr check_home | |
| 187 | 203 | bsr check_collision | |
| 188 | 204 | .skip_collision: | |
| 189 | 205 | | |
| ... | |||
| 191 | 207 | | |
| 192 | 208 | .game_over_loop: | |
| 193 | 209 | ; 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 | |
| 196 | 212 | | |
| 197 | 213 | ; Restart game | |
| 198 | - | move.w #START_LIVES,lives | |
| 199 | - | bsr reset_frog | |
| 214 | + | bsr init_game | |
| 200 | 215 | bsr update_life_display | |
| 201 | 216 | bsr clear_screen | |
| 202 | 217 | | |
| 203 | 218 | bra mainloop | |
| 204 | 219 | | |
| 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 | + | ;============================================================================== | |
| 208 | 242 | | |
| 209 | 243 | ;------------------------------------------------------------------------------ | |
| 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 | |
| 211 | 367 | ;------------------------------------------------------------------------------ | |
| 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 | + | | |
| 212 | 421 | setup_life_sprites: | |
| 213 | 422 | ; Sprite 1 | |
| 214 | 423 | lea life_icon_1,a0 | |
| ... | |||
| 242 | 451 | | |
| 243 | 452 | rts | |
| 244 | 453 | | |
| 245 | - | ;------------------------------------------------------------------------------ | |
| 246 | - | ; UPDATE_LIFE_DISPLAY - Show/hide life icons based on lives count | |
| 247 | - | ;------------------------------------------------------------------------------ | |
| 248 | 454 | 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 | - | | |
| 253 | 455 | ; Life 1 (always show if lives >= 1) | |
| 254 | 456 | lea life_icon_1,a0 | |
| 255 | 457 | move.w lives,d0 | |
| 256 | 458 | cmp.w #1,d0 | |
| 257 | 459 | 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) | |
| 260 | 462 | bra.s .life2 | |
| 261 | 463 | .hide_life1: | |
| 262 | - | clr.l (a0) ; Hide sprite | |
| 464 | + | clr.l (a0) | |
| 263 | 465 | | |
| 264 | 466 | .life2: | |
| 265 | 467 | lea life_icon_2,a0 | |
| 266 | 468 | cmp.w #2,d0 | |
| 267 | 469 | blt.s .hide_life2 | |
| 268 | - | move.w #$1e28,$0(a0) ; VSTART=30, HSTART=80 | |
| 470 | + | move.w #$1e28,$0(a0) | |
| 269 | 471 | move.w #$2600,$2(a0) | |
| 270 | 472 | bra.s .life3 | |
| 271 | 473 | .hide_life2: | |
| ... | |||
| 275 | 477 | lea life_icon_3,a0 | |
| 276 | 478 | cmp.w #3,d0 | |
| 277 | 479 | blt.s .hide_life3 | |
| 278 | - | move.w #$1e30,$0(a0) ; VSTART=30, HSTART=96 | |
| 480 | + | move.w #$1e30,$0(a0) | |
| 279 | 481 | move.w #$2600,$2(a0) | |
| 280 | 482 | rts | |
| 281 | 483 | .hide_life3: | |
| 282 | 484 | clr.l (a0) | |
| 283 | 485 | rts | |
| 284 | 486 | | |
| 285 | - | ;══════════════════════════════════════════════════════════════════════════════ | |
| 487 | + | ;============================================================================== | |
| 286 | 488 | ; COLLISION AND DEATH | |
| 287 | - | ;══════════════════════════════════════════════════════════════════════════════ | |
| 489 | + | ;============================================================================== | |
| 288 | 490 | | |
| 289 | 491 | check_collision: | |
| 290 | 492 | move.w frog_grid_y,d0 | |
| ... | |||
| 335 | 537 | | |
| 336 | 538 | rts | |
| 337 | 539 | | |
| 338 | - | ;------------------------------------------------------------------------------ | |
| 339 | - | ; RESET_FROG - Reset frog position (called on respawn and game start) | |
| 340 | - | ;------------------------------------------------------------------------------ | |
| 341 | 540 | reset_frog: | |
| 342 | 541 | move.w #START_GRID_X,frog_grid_x | |
| 343 | 542 | move.w #START_GRID_Y,frog_grid_y | |
| ... | |||
| 348 | 547 | bsr grid_to_pixels | |
| 349 | 548 | rts | |
| 350 | 549 | | |
| 351 | - | ;══════════════════════════════════════════════════════════════════════════════ | |
| 550 | + | ;============================================================================== | |
| 352 | 551 | ; CAR MANAGEMENT | |
| 353 | - | ;══════════════════════════════════════════════════════════════════════════════ | |
| 552 | + | ;============================================================================== | |
| 354 | 553 | | |
| 355 | 554 | erase_all_cars: | |
| 356 | 555 | lea car_data,a2 | |
| ... | |||
| 428 | 627 | dbf d7,.loop | |
| 429 | 628 | rts | |
| 430 | 629 | | |
| 431 | - | ;══════════════════════════════════════════════════════════════════════════════ | |
| 630 | + | ;============================================================================== | |
| 432 | 631 | ; FROG ROUTINES | |
| 433 | - | ;══════════════════════════════════════════════════════════════════════════════ | |
| 632 | + | ;============================================================================== | |
| 434 | 633 | | |
| 435 | 634 | update_frog: | |
| 436 | 635 | move.w frog_state,d0 | |
| ... | |||
| 559 | 758 | move.w flash_colour,flash_copper | |
| 560 | 759 | rts | |
| 561 | 760 | | |
| 562 | - | ;══════════════════════════════════════════════════════════════════════════════ | |
| 761 | + | ;============================================================================== | |
| 563 | 762 | ; UTILITY ROUTINES | |
| 564 | - | ;══════════════════════════════════════════════════════════════════════════════ | |
| 763 | + | ;============================================================================== | |
| 565 | 764 | | |
| 566 | 765 | grid_to_pixels: | |
| 567 | 766 | move.w frog_grid_x,d0 | |
| ... | |||
| 640 | 839 | move.w d0,2(a0) | |
| 641 | 840 | rts | |
| 642 | 841 | | |
| 643 | - | ;══════════════════════════════════════════════════════════════════════════════ | |
| 842 | + | ;============================================================================== | |
| 644 | 843 | ; VARIABLES | |
| 645 | - | ;══════════════════════════════════════════════════════════════════════════════ | |
| 844 | + | ;============================================================================== | |
| 646 | 845 | | |
| 647 | 846 | frog_grid_x: dc.w 9 | |
| 648 | 847 | frog_grid_y: dc.w 12 | |
| ... | |||
| 657 | 856 | flash_colour: dc.w 0 | |
| 658 | 857 | | |
| 659 | 858 | 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 | |
| 660 | 863 | | |
| 661 | 864 | car_data: | |
| 662 | 865 | dc.w 0,7,1,0 | |
| ... | |||
| 670 | 873 | dc.w 30,11,3,0 | |
| 671 | 874 | dc.w 180,11,3,0 | |
| 672 | 875 | | |
| 673 | - | ;══════════════════════════════════════════════════════════════════════════════ | |
| 876 | + | ;============================================================================== | |
| 674 | 877 | ; COPPER LIST | |
| 675 | - | ;══════════════════════════════════════════════════════════════════════════════ | |
| 878 | + | ;============================================================================== | |
| 676 | 879 | | |
| 677 | 880 | copperlist: | |
| 678 | 881 | dc.w COLOR00 | |
| ... | |||
| 719 | 922 | ; Sprite 3 pointer (life icon 3) | |
| 720 | 923 | dc.w $0126 ; SPR3PTH | |
| 721 | 924 | spr3pth_val: dc.w $0000 | |
| 722 | - | dc.w $0128 ; SPR3PTL (note: this is wrong, should be $012a) | |
| 925 | + | dc.w $012a ; SPR3PTL | |
| 723 | 926 | spr3ptl_val: dc.w $0000 | |
| 724 | 927 | | |
| 725 | - | ; Zone colours | |
| 928 | + | ; Home zone row - 5 distinct zones for visual feedback | |
| 929 | + | ; Each home changes colour when filled | |
| 726 | 930 | 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 | |
| 728 | 979 | dc.w $3407,$fffe | |
| 729 | 980 | dc.w COLOR00,COLOUR_HOME_LIT | |
| 730 | 981 | dc.w $3807,$fffe | |
| 731 | 982 | dc.w COLOR00,COLOUR_HOME | |
| 732 | 983 | | |
| 984 | + | ; Water rows | |
| 733 | 985 | dc.w $3c07,$fffe | |
| 734 | 986 | dc.w COLOR00,COLOUR_WATER1 | |
| 735 | 987 | dc.w $4c07,$fffe | |
| ... | |||
| 741 | 993 | dc.w $7c07,$fffe | |
| 742 | 994 | dc.w COLOR00,COLOUR_WATER1 | |
| 743 | 995 | | |
| 996 | + | ; Median | |
| 744 | 997 | dc.w $8c07,$fffe | |
| 745 | 998 | dc.w COLOR00,COLOUR_MEDIAN | |
| 746 | 999 | | |
| 1000 | + | ; Road rows | |
| 747 | 1001 | dc.w $9c07,$fffe | |
| 748 | 1002 | dc.w COLOR00,COLOUR_ROAD1 | |
| 749 | 1003 | dc.w $ac07,$fffe | |
| ... | |||
| 755 | 1009 | dc.w $dc07,$fffe | |
| 756 | 1010 | dc.w COLOR00,COLOUR_ROAD1 | |
| 757 | 1011 | | |
| 1012 | + | ; Start zone | |
| 758 | 1013 | dc.w $ec07,$fffe | |
| 759 | 1014 | dc.w COLOR00,COLOUR_START | |
| 760 | 1015 | dc.w $f407,$fffe | |
| ... | |||
| 767 | 1022 | | |
| 768 | 1023 | dc.w $ffff,$fffe | |
| 769 | 1024 | | |
| 770 | - | ;══════════════════════════════════════════════════════════════════════════════ | |
| 1025 | + | ;============================================================================== | |
| 771 | 1026 | ; GRAPHICS DATA | |
| 772 | - | ;══════════════════════════════════════════════════════════════════════════════ | |
| 1027 | + | ;============================================================================== | |
| 773 | 1028 | | |
| 774 | 1029 | even | |
| 775 | 1030 | car_gfx: | |
| ... | |||
| 812 | 1067 | ; Life icon sprites (small frog shape, 8 pixels tall) | |
| 813 | 1068 | even | |
| 814 | 1069 | life_icon_1: | |
| 815 | - | dc.w $0000,$0000 ; Control words (set by code) | |
| 816 | 1070 | 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 ; ..##............ | |
| 823 | 1071 | 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 | |
| 825 | 1080 | | |
| 826 | 1081 | even | |
| 827 | 1082 | life_icon_2: | |
| ... | |||
| 849 | 1104 | dc.w $0000,$0000 | |
| 850 | 1105 | dc.w $0000,$0000 | |
| 851 | 1106 | | |
| 852 | - | ;══════════════════════════════════════════════════════════════════════════════ | |
| 1107 | + | ;============================================================================== | |
| 853 | 1108 | ; SCREEN BUFFER | |
| 854 | - | ;══════════════════════════════════════════════════════════════════════════════ | |
| 1109 | + | ;============================================================================== | |
| 855 | 1110 | | |
| 856 | 1111 | section chipbss,bss_c | |
| 857 | 1112 | |