Collision Detection
Hit a car, die. Flash screen. Respawn. Consequences.
What You’re Building
Consequences.
Traffic that you can walk through isn’t traffic—it’s decoration. This unit adds collision detection: touch a car and the screen flashes red, then you respawn at the start. Now the game has stakes.
By the end of this unit:
- Frog-car collisions detected
- Screen flashes red on death
- Brief pause (0.6 seconds)
- Frog respawns at starting position

Bounding Box Collision
The simplest collision detection: treat both objects as rectangles and check for overlap.
Two rectangles overlap if ALL of these are true:
- Frog’s right edge > Car’s left edge
- Car’s right edge > Frog’s left edge
- Frog’s bottom edge > Car’s top edge
- Car’s bottom edge > Frog’s top edge
Since our frog and cars are on a grid, we can simplify: only check cars in the same row as the frog.
First, the constants that define collision boundaries:
; Collision Detection Constants
; Frog and car dimensions for bounding box checks
; Frog dimensions
FROG_HEIGHT equ 16
FROG_WIDTH equ 16
; Car dimensions
CAR_WIDTH_PX equ 32 ; Pixels (not words!)
CAR_HEIGHT equ 12
; Death animation
DEATH_FRAMES equ 30 ; 0.6 seconds at 50fps
FLASH_COLOUR equ $0f00 ; Red flash
; Road boundaries (grid rows)
ROAD_ROW_FIRST equ 7
ROAD_ROW_LAST equ 11
; Frog states
STATE_IDLE equ 0
STATE_HOPPING equ 1
STATE_DYING equ 2 ; New state for death animation
The Collision Test
The full collision check filters by row first, then tests X overlap using AABB (Axis-Aligned Bounding Box) logic:
; Bounding Box Collision Test
; AABB overlap: two boxes collide if all four edges overlap
check_collision:
; Only check if frog is on a road row
move.w frog_grid_y,d0
cmp.w #ROAD_ROW_FIRST,d0
blt .no_collision
cmp.w #ROAD_ROW_LAST,d0
bgt .no_collision
; Frog is on road - check against all cars
lea car_data,a2
moveq #NUM_CARS-1,d7
.loop:
; Check if car is in same row
move.w 2(a2),d1 ; Car row
cmp.w d0,d1
bne.s .next_car
; Same row - check X overlap using AABB test
move.w frog_pixel_x,d2 ; Frog X
move.w (a2),d3 ; Car X
; Test 1: frog_right > car_left
move.w d2,d4
add.w #FROG_WIDTH,d4 ; d4 = frog right edge
cmp.w d3,d4
ble.s .next_car ; Frog right <= car left: no collision
; Test 2: car_right > frog_left
move.w d3,d4
add.w #CAR_WIDTH_PX,d4 ; d4 = car right edge
cmp.w d2,d4
ble.s .next_car ; Car right <= frog left: no collision
; COLLISION! Both tests passed
bsr trigger_death
bra.s .no_collision ; Exit early
.next_car:
lea CAR_STRUCT_SIZE(a2),a2
dbf d7,.loop
.no_collision:
rts
The checks use ble (branch if less or equal) rather than blt because we want to skip if edges are exactly touching—that’s not a collision.
The Death State
We add a third state to our frog state machine. When dying, the frog ignores input and counts down a timer while flashing the screen:
; Death State and Flash Effect
; Screen flashes red by modifying Copper colour register
trigger_death:
move.w #STATE_DYING,frog_state
move.w #DEATH_FRAMES,death_timer
move.w #FLASH_COLOUR,flash_colour
rts
; In update_frog, handle dying state:
.dying:
; Count down death timer
subq.w #1,death_timer
bgt.s .still_dying
; Death complete - respawn
bsr respawn_frog
bra .done
.still_dying:
; Flash effect: alternate colour every 4 frames
move.w death_timer,d0
and.w #4,d0 ; Bit 2 toggles every 4 frames
beq.s .flash_off
move.w #FLASH_COLOUR,flash_colour
bra .done
.flash_off:
move.w #COLOUR_BLACK,flash_colour
bra .done
; At end of update_frog, copy flash colour to Copper list:
.done:
move.w flash_colour,flash_copper
rts
The flash effect modifies the Copper list’s background colour. We alternate the colour every 4 frames, creating a flashing effect at about 6Hz—noticeable but not seizure-inducing.
Respawn Logic
When death is complete, we reset the frog:
respawn_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
; Clear flash
move.w #COLOUR_BLACK,flash_colour
bsr grid_to_pixels
rts
Note that we clear joy_prev—this prevents a held joystick direction from immediately moving the respawned frog.
When to Check Collisions
We only check collisions when the frog is alive:
mainloop:
bsr wait_vblank
bsr update_frog
bsr update_sprite
bsr erase_all_cars
bsr move_all_cars
bsr draw_all_cars
; Only check if not already dying
cmp.w #STATE_DYING,frog_state
beq.s .skip_collision
bsr check_collision
.skip_collision:
bra mainloop
Checking during death would immediately trigger another death on respawn if a car happened to be at the start position.
Collision Timing
An important subtlety: we check collision after updating cars and after the frog has finished moving. This means:
- Frog starts hop
- Frog animates through hop (8 frames)
- Frog lands on new cell
- Collision check happens
- If car overlaps, death triggers
The frog can hop “through” a fast car if it passes during the hop animation. This is actually how classic Frogger works—you’re only vulnerable when stationary.
Key Takeaways
- Bounding box collision checks rectangle overlap
- Row filtering reduces unnecessary checks
- Death state prevents input during animation
- Copper modification creates screen flash effect
- State cleanup on respawn prevents input glitches
- Collision timing affects gameplay feel
The Code
;══════════════════════════════════════════════════════════════════════════════
; SIGNAL - A Frogger-style game for the Commodore Amiga
; Unit 8: Collision Detection
;
; Traffic is meaningless if you can walk through it. This unit adds collision
; detection: hit a car, the screen flashes red, and you respawn at the start.
; Now it's actually a game with consequences!
;══════════════════════════════════════════════════════════════════════════════
;══════════════════════════════════════════════════════════════════════════════
; 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 ; NEW: Death state
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 animation
DEATH_FRAMES equ 30 ; How long death lasts (0.6 seconds at 50fps)
FLASH_COLOUR equ $0f00 ; Red flash
NUM_CARS equ 10
CAR_WIDTH equ 2
CAR_WIDTH_PX equ 32 ; Pixels
CAR_HEIGHT equ 12
CAR_STRUCT_SIZE equ 8
; Collision threshold (pixels of overlap required)
COLLISION_THRESHOLD equ 8
; Road rows (grid rows 7-11, which are rows 8-12 in 1-based counting)
ROAD_ROW_FIRST equ 7
ROAD_ROW_LAST equ 11
; Zone colours
COLOUR_BLACK equ $0000
COLOUR_HOME equ $0282
COLOUR_HOME_LIT equ $03a3
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
;══════════════════════════════════════════════════════════════════════════════
; 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
;══════════════════════════════════════════════════════════════════════════════
; 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 frog
bsr respawn_frog
; Set sprite pointer
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)
bsr update_sprite
; 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
; Update frog (handles all states including death)
bsr update_frog
bsr update_sprite
; Update cars
bsr erase_all_cars
bsr move_all_cars
bsr draw_all_cars
; Check collisions (only if frog is alive)
cmp.w #STATE_DYING,frog_state
beq.s .skip_collision
bsr check_collision
.skip_collision:
bra mainloop
;══════════════════════════════════════════════════════════════════════════════
; COLLISION DETECTION
;══════════════════════════════════════════════════════════════════════════════
;------------------------------------------------------------------------------
; CHECK_COLLISION - Test frog against all cars
;------------------------------------------------------------------------------
check_collision:
; Only check if frog is on a road row
move.w frog_grid_y,d0
cmp.w #ROAD_ROW_FIRST,d0
blt .no_collision
cmp.w #ROAD_ROW_LAST,d0
bgt .no_collision
; Frog is on road - check against all cars in this row
lea car_data,a2
moveq #NUM_CARS-1,d7
.loop:
; Check if car is in same row
move.w 2(a2),d1 ; Car row
cmp.w d0,d1 ; Compare with frog row
bne.s .next_car
; Same row - check X overlap
move.w frog_pixel_x,d2 ; Frog X
move.w (a2),d3 ; Car X
; Check: frog_x + frog_width > car_x
move.w d2,d4
add.w #FROG_WIDTH,d4
cmp.w d3,d4
ble.s .next_car ; Frog right edge <= car left edge
; Check: car_x + car_width > frog_x
move.w d3,d4
add.w #CAR_WIDTH_PX,d4
cmp.w d2,d4
ble.s .next_car ; Car right edge <= frog left edge
; COLLISION! Trigger death
bsr trigger_death
bra.s .no_collision ; Exit early
.next_car:
lea CAR_STRUCT_SIZE(a2),a2
dbf d7,.loop
.no_collision:
rts
;------------------------------------------------------------------------------
; TRIGGER_DEATH - Start death sequence
;------------------------------------------------------------------------------
trigger_death:
move.w #STATE_DYING,frog_state
move.w #DEATH_FRAMES,death_timer
; Flash screen red by changing Copper colour
move.w #FLASH_COLOUR,flash_colour
rts
;------------------------------------------------------------------------------
; RESPAWN_FROG - Reset frog to starting position
;------------------------------------------------------------------------------
respawn_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
; Reset flash colour to black
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:
; Count down death timer
subq.w #1,death_timer
bgt.s .still_dying
; Death complete - respawn
bsr respawn_frog
bra .done
.still_dying:
; Flash effect: alternate colour
move.w death_timer,d0
and.w #4,d0 ; Flash every 4 frames
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:
; Update flash colour in Copper list
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
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 ; This gets modified for flash effect
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
dc.w $01a2,COLOUR_FROG
dc.w $01a4,COLOUR_EYES
dc.w $01a6,COLOUR_OUTLINE
dc.w SPR0PTH
sprpth_val: dc.w $0000
dc.w SPR0PTL
sprptl_val: dc.w $0000
dc.w $2c07,$fffe
dc.w COLOR00,COLOUR_HOME
dc.w $3407,$fffe
dc.w COLOR00,COLOUR_HOME_LIT
dc.w $3807,$fffe
dc.w COLOR00,COLOUR_HOME
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
dc.w $8c07,$fffe
dc.w COLOR00,COLOUR_MEDIAN
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
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
;══════════════════════════════════════════════════════════════════════════════
; 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
Death happens, but there’s no limit. In Unit 9, we’ll add a lives system—three lives before game over.
What Changed
| 1 | 1 | ;══════════════════════════════════════════════════════════════════════════════ | |
| 2 | 2 | ; SIGNAL - A Frogger-style game for the Commodore Amiga | |
| 3 | - | ; Unit 7: Cars on the Road | |
| 3 | + | ; Unit 8: Collision Detection | |
| 4 | 4 | ; | |
| 5 | - | ; One car isn't traffic. This unit adds multiple cars across all five road | |
| 6 | - | ; lanes, each moving at different speeds. Some go left, some go right. | |
| 7 | - | ; The Blitter draws them all efficiently using a loop. | |
| 5 | + | ; Traffic is meaningless if you can walk through it. This unit adds collision | |
| 6 | + | ; detection: hit a car, the screen flashes red, and you respawn at the start. | |
| 7 | + | ; Now it's actually a game with consequences! | |
| 8 | 8 | ;══════════════════════════════════════════════════════════════════════════════ | |
| 9 | 9 | | |
| 10 | 10 | ;══════════════════════════════════════════════════════════════════════════════ | |
| 11 | 11 | ; CONSTANTS | |
| 12 | 12 | ;══════════════════════════════════════════════════════════════════════════════ | |
| 13 | 13 | | |
| 14 | - | ; Screen setup | |
| 15 | 14 | SCREEN_W equ 40 | |
| 16 | 15 | SCREEN_H equ 256 | |
| 17 | 16 | PLANE_SIZE equ SCREEN_W*SCREEN_H | |
| 18 | 17 | | |
| 19 | - | ; Grid constants | |
| 20 | 18 | GRID_COLS equ 20 | |
| 21 | 19 | GRID_ROWS equ 13 | |
| 22 | 20 | CELL_SIZE equ 16 | |
| ... | |||
| 25 | 23 | START_GRID_X equ 9 | |
| 26 | 24 | START_GRID_Y equ 12 | |
| 27 | 25 | | |
| 28 | - | ; Frog animation | |
| 29 | 26 | HOP_FRAMES equ 8 | |
| 30 | 27 | PIXELS_PER_FRAME equ 2 | |
| 31 | 28 | STATE_IDLE equ 0 | |
| 32 | 29 | STATE_HOPPING equ 1 | |
| 30 | + | STATE_DYING equ 2 ; NEW: Death state | |
| 33 | 31 | DIR_UP equ 0 | |
| 34 | 32 | DIR_DOWN equ 1 | |
| 35 | 33 | DIR_LEFT equ 2 | |
| 36 | 34 | DIR_RIGHT equ 3 | |
| 37 | 35 | FROG_HEIGHT equ 16 | |
| 36 | + | FROG_WIDTH equ 16 | |
| 38 | 37 | | |
| 39 | - | ; Car constants | |
| 40 | - | NUM_CARS equ 10 ; Total cars | |
| 41 | - | CAR_WIDTH equ 2 ; 32 pixels = 2 words | |
| 38 | + | ; Death animation | |
| 39 | + | DEATH_FRAMES equ 30 ; How long death lasts (0.6 seconds at 50fps) | |
| 40 | + | FLASH_COLOUR equ $0f00 ; Red flash | |
| 41 | + | | |
| 42 | + | NUM_CARS equ 10 | |
| 43 | + | CAR_WIDTH equ 2 | |
| 44 | + | CAR_WIDTH_PX equ 32 ; Pixels | |
| 42 | 45 | CAR_HEIGHT equ 12 | |
| 43 | - | CAR_STRUCT_SIZE equ 8 ; Bytes per car: x(2), row(2), speed(2), pad(2) | |
| 46 | + | CAR_STRUCT_SIZE equ 8 | |
| 47 | + | | |
| 48 | + | ; Collision threshold (pixels of overlap required) | |
| 49 | + | COLLISION_THRESHOLD equ 8 | |
| 50 | + | | |
| 51 | + | ; Road rows (grid rows 7-11, which are rows 8-12 in 1-based counting) | |
| 52 | + | ROAD_ROW_FIRST equ 7 | |
| 53 | + | ROAD_ROW_LAST equ 11 | |
| 44 | 54 | | |
| 45 | 55 | ; Zone colours | |
| 46 | 56 | COLOUR_BLACK equ $0000 | |
| ... | |||
| 53 | 63 | COLOUR_ROAD2 equ $0444 | |
| 54 | 64 | COLOUR_START equ $0262 | |
| 55 | 65 | COLOUR_START_LIT equ $0373 | |
| 56 | - | | |
| 57 | - | ; Sprite and car colours | |
| 58 | 66 | COLOUR_FROG equ $00f0 | |
| 59 | 67 | COLOUR_EYES equ $0ff0 | |
| 60 | 68 | COLOUR_OUTLINE equ $0000 | |
| ... | |||
| 102 | 110 | start: | |
| 103 | 111 | lea CUSTOM,a5 | |
| 104 | 112 | | |
| 105 | - | ; --- System takeover --- | |
| 106 | 113 | move.w #$7fff,INTENA(a5) | |
| 107 | 114 | move.w #$7fff,INTREQ(a5) | |
| 108 | 115 | move.w #$7fff,DMACON(a5) | |
| 109 | 116 | | |
| 110 | - | ; --- Clear screen --- | |
| 111 | 117 | bsr clear_screen | |
| 112 | 118 | | |
| 113 | - | ; --- Set up bitplane pointer --- | |
| 119 | + | ; Set up bitplane pointer | |
| 114 | 120 | lea screen_plane,a0 | |
| 115 | 121 | move.l a0,d0 | |
| 116 | 122 | swap d0 | |
| ... | |||
| 120 | 126 | lea bplptl_val,a1 | |
| 121 | 127 | move.w d0,(a1) | |
| 122 | 128 | | |
| 123 | - | ; --- Initialise frog --- | |
| 124 | - | move.w #START_GRID_X,frog_grid_x | |
| 125 | - | move.w #START_GRID_Y,frog_grid_y | |
| 126 | - | move.w #STATE_IDLE,frog_state | |
| 127 | - | clr.w joy_prev | |
| 128 | - | bsr grid_to_pixels | |
| 129 | + | ; Initialise frog | |
| 130 | + | bsr respawn_frog | |
| 129 | 131 | | |
| 130 | - | ; --- Set sprite pointer --- | |
| 132 | + | ; Set sprite pointer | |
| 131 | 133 | lea frog_data,a0 | |
| 132 | 134 | move.l a0,d0 | |
| 133 | 135 | swap d0 | |
| ... | |||
| 138 | 140 | move.w d0,(a1) | |
| 139 | 141 | bsr update_sprite | |
| 140 | 142 | | |
| 141 | - | ; --- Install copper list --- | |
| 143 | + | ; Install copper list | |
| 142 | 144 | lea copperlist,a0 | |
| 143 | 145 | move.l a0,COP1LC(a5) | |
| 144 | 146 | move.w d0,COPJMP1(a5) | |
| 145 | 147 | | |
| 146 | - | ; --- Enable DMA --- | |
| 148 | + | ; Enable DMA | |
| 147 | 149 | move.w #$87e0,DMACON(a5) | |
| 148 | 150 | | |
| 149 | 151 | ;══════════════════════════════════════════════════════════════════════════════ | |
| ... | |||
| 153 | 155 | mainloop: | |
| 154 | 156 | bsr wait_vblank | |
| 155 | 157 | | |
| 156 | - | ; --- Update frog --- | |
| 158 | + | ; Update frog (handles all states including death) | |
| 157 | 159 | bsr update_frog | |
| 158 | 160 | bsr update_sprite | |
| 159 | 161 | | |
| 160 | - | ; --- Update cars --- | |
| 162 | + | ; Update cars | |
| 161 | 163 | bsr erase_all_cars | |
| 162 | 164 | bsr move_all_cars | |
| 163 | 165 | bsr draw_all_cars | |
| 166 | + | | |
| 167 | + | ; Check collisions (only if frog is alive) | |
| 168 | + | cmp.w #STATE_DYING,frog_state | |
| 169 | + | beq.s .skip_collision | |
| 170 | + | bsr check_collision | |
| 171 | + | .skip_collision: | |
| 164 | 172 | | |
| 165 | 173 | bra mainloop | |
| 166 | 174 | | |
| 167 | 175 | ;══════════════════════════════════════════════════════════════════════════════ | |
| 168 | - | ; CAR MANAGEMENT | |
| 176 | + | ; COLLISION DETECTION | |
| 169 | 177 | ;══════════════════════════════════════════════════════════════════════════════ | |
| 170 | 178 | | |
| 171 | 179 | ;------------------------------------------------------------------------------ | |
| 172 | - | ; ERASE_ALL_CARS - Clear all cars from screen | |
| 180 | + | ; CHECK_COLLISION - Test frog against all cars | |
| 173 | 181 | ;------------------------------------------------------------------------------ | |
| 174 | - | erase_all_cars: | |
| 182 | + | check_collision: | |
| 183 | + | ; Only check if frog is on a road row | |
| 184 | + | move.w frog_grid_y,d0 | |
| 185 | + | cmp.w #ROAD_ROW_FIRST,d0 | |
| 186 | + | blt .no_collision | |
| 187 | + | cmp.w #ROAD_ROW_LAST,d0 | |
| 188 | + | bgt .no_collision | |
| 189 | + | | |
| 190 | + | ; Frog is on road - check against all cars in this row | |
| 175 | 191 | lea car_data,a2 | |
| 176 | 192 | moveq #NUM_CARS-1,d7 | |
| 177 | 193 | | |
| 178 | 194 | .loop: | |
| 179 | - | move.w (a2),d0 ; X position | |
| 180 | - | move.w 2(a2),d1 ; Row | |
| 195 | + | ; Check if car is in same row | |
| 196 | + | move.w 2(a2),d1 ; Car row | |
| 197 | + | cmp.w d0,d1 ; Compare with frog row | |
| 198 | + | bne.s .next_car | |
| 199 | + | | |
| 200 | + | ; Same row - check X overlap | |
| 201 | + | move.w frog_pixel_x,d2 ; Frog X | |
| 202 | + | move.w (a2),d3 ; Car X | |
| 203 | + | | |
| 204 | + | ; Check: frog_x + frog_width > car_x | |
| 205 | + | move.w d2,d4 | |
| 206 | + | add.w #FROG_WIDTH,d4 | |
| 207 | + | cmp.w d3,d4 | |
| 208 | + | ble.s .next_car ; Frog right edge <= car left edge | |
| 209 | + | | |
| 210 | + | ; Check: car_x + car_width > frog_x | |
| 211 | + | move.w d3,d4 | |
| 212 | + | add.w #CAR_WIDTH_PX,d4 | |
| 213 | + | cmp.w d2,d4 | |
| 214 | + | ble.s .next_car ; Car right edge <= frog left edge | |
| 215 | + | | |
| 216 | + | ; COLLISION! Trigger death | |
| 217 | + | bsr trigger_death | |
| 218 | + | bra.s .no_collision ; Exit early | |
| 219 | + | | |
| 220 | + | .next_car: | |
| 221 | + | lea CAR_STRUCT_SIZE(a2),a2 | |
| 222 | + | dbf d7,.loop | |
| 223 | + | | |
| 224 | + | .no_collision: | |
| 225 | + | rts | |
| 226 | + | | |
| 227 | + | ;------------------------------------------------------------------------------ | |
| 228 | + | ; TRIGGER_DEATH - Start death sequence | |
| 229 | + | ;------------------------------------------------------------------------------ | |
| 230 | + | trigger_death: | |
| 231 | + | move.w #STATE_DYING,frog_state | |
| 232 | + | move.w #DEATH_FRAMES,death_timer | |
| 233 | + | | |
| 234 | + | ; Flash screen red by changing Copper colour | |
| 235 | + | move.w #FLASH_COLOUR,flash_colour | |
| 236 | + | rts | |
| 237 | + | | |
| 238 | + | ;------------------------------------------------------------------------------ | |
| 239 | + | ; RESPAWN_FROG - Reset frog to starting position | |
| 240 | + | ;------------------------------------------------------------------------------ | |
| 241 | + | respawn_frog: | |
| 242 | + | move.w #START_GRID_X,frog_grid_x | |
| 243 | + | move.w #START_GRID_Y,frog_grid_y | |
| 244 | + | move.w #STATE_IDLE,frog_state | |
| 245 | + | clr.w frog_anim_frame | |
| 246 | + | clr.w joy_prev | |
| 247 | + | | |
| 248 | + | ; Reset flash colour to black | |
| 249 | + | move.w #COLOUR_BLACK,flash_colour | |
| 250 | + | | |
| 251 | + | bsr grid_to_pixels | |
| 252 | + | rts | |
| 253 | + | | |
| 254 | + | ;══════════════════════════════════════════════════════════════════════════════ | |
| 255 | + | ; CAR MANAGEMENT | |
| 256 | + | ;══════════════════════════════════════════════════════════════════════════════ | |
| 257 | + | | |
| 258 | + | erase_all_cars: | |
| 259 | + | lea car_data,a2 | |
| 260 | + | moveq #NUM_CARS-1,d7 | |
| 181 | 261 | | |
| 262 | + | .loop: | |
| 263 | + | move.w (a2),d0 | |
| 264 | + | move.w 2(a2),d1 | |
| 182 | 265 | bsr calc_screen_addr | |
| 183 | 266 | bsr wait_blit | |
| 184 | 267 | | |
| ... | |||
| 192 | 275 | dbf d7,.loop | |
| 193 | 276 | rts | |
| 194 | 277 | | |
| 195 | - | ;------------------------------------------------------------------------------ | |
| 196 | - | ; MOVE_ALL_CARS - Update all car positions | |
| 197 | - | ;------------------------------------------------------------------------------ | |
| 198 | 278 | move_all_cars: | |
| 199 | 279 | lea car_data,a2 | |
| 200 | 280 | moveq #NUM_CARS-1,d7 | |
| 201 | 281 | | |
| 202 | 282 | .loop: | |
| 203 | - | move.w (a2),d0 ; X position | |
| 204 | - | move.w 4(a2),d1 ; Speed (signed) | |
| 205 | - | add.w d1,d0 ; Move | |
| 283 | + | move.w (a2),d0 | |
| 284 | + | move.w 4(a2),d1 | |
| 285 | + | add.w d1,d0 | |
| 206 | 286 | | |
| 207 | - | ; --- Wrap at screen edges --- | |
| 208 | - | tst.w d1 ; Check direction | |
| 287 | + | tst.w d1 | |
| 209 | 288 | bmi.s .check_left | |
| 210 | 289 | | |
| 211 | - | ; Moving right | |
| 212 | 290 | cmp.w #320,d0 | |
| 213 | 291 | blt.s .store | |
| 214 | - | sub.w #320+32,d0 ; Wrap from right to left | |
| 292 | + | sub.w #320+32,d0 | |
| 215 | 293 | bra.s .store | |
| 216 | 294 | | |
| 217 | 295 | .check_left: | |
| 218 | - | ; Moving left | |
| 219 | 296 | cmp.w #-32,d0 | |
| 220 | 297 | bgt.s .store | |
| 221 | - | add.w #320+32,d0 ; Wrap from left to right | |
| 298 | + | add.w #320+32,d0 | |
| 222 | 299 | | |
| 223 | 300 | .store: | |
| 224 | - | move.w d0,(a2) ; Store new X | |
| 225 | - | | |
| 301 | + | move.w d0,(a2) | |
| 226 | 302 | lea CAR_STRUCT_SIZE(a2),a2 | |
| 227 | 303 | dbf d7,.loop | |
| 228 | 304 | rts | |
| 229 | 305 | | |
| 230 | - | ;------------------------------------------------------------------------------ | |
| 231 | - | ; DRAW_ALL_CARS - Draw all cars to screen | |
| 232 | - | ;------------------------------------------------------------------------------ | |
| 233 | 306 | draw_all_cars: | |
| 234 | 307 | lea car_data,a2 | |
| 235 | 308 | moveq #NUM_CARS-1,d7 | |
| 236 | 309 | | |
| 237 | 310 | .loop: | |
| 238 | - | move.w (a2),d0 ; X position | |
| 239 | - | move.w 2(a2),d1 ; Row | |
| 311 | + | move.w (a2),d0 | |
| 312 | + | move.w 2(a2),d1 | |
| 240 | 313 | | |
| 241 | - | ; Skip if offscreen | |
| 242 | 314 | cmp.w #-32,d0 | |
| 243 | 315 | blt.s .next | |
| 244 | 316 | cmp.w #320,d0 | |
| ... | |||
| 263 | 335 | rts | |
| 264 | 336 | | |
| 265 | 337 | ;══════════════════════════════════════════════════════════════════════════════ | |
| 266 | - | ; BLITTER UTILITIES | |
| 338 | + | ; FROG ROUTINES | |
| 267 | 339 | ;══════════════════════════════════════════════════════════════════════════════ | |
| 268 | - | | |
| 269 | - | wait_blit: | |
| 270 | - | btst #6,DMACONR(a5) | |
| 271 | - | bne.s wait_blit | |
| 272 | - | rts | |
| 273 | 340 | | |
| 274 | - | clear_screen: | |
| 275 | - | bsr.s wait_blit | |
| 276 | - | move.l #screen_plane,BLTDPTH(a5) | |
| 277 | - | move.w #0,BLTDMOD(a5) | |
| 278 | - | move.w #$0100,BLTCON0(a5) | |
| 279 | - | move.w #0,BLTCON1(a5) | |
| 280 | - | move.w #(SCREEN_H<<6)|SCREEN_W/2,BLTSIZE(a5) | |
| 281 | - | rts | |
| 341 | + | update_frog: | |
| 342 | + | move.w frog_state,d0 | |
| 282 | 343 | | |
| 283 | - | calc_screen_addr: | |
| 284 | - | ; D0 = pixel X, D1 = row number -> A0 = screen address | |
| 285 | - | lea screen_plane,a0 | |
| 344 | + | cmp.w #STATE_DYING,d0 | |
| 345 | + | beq .dying | |
| 286 | 346 | | |
| 287 | - | move.w d1,d2 | |
| 288 | - | mulu #CELL_SIZE,d2 | |
| 289 | - | add.w #GRID_ORIGIN_Y,d2 | |
| 290 | - | mulu #SCREEN_W,d2 | |
| 291 | - | add.l d2,a0 | |
| 347 | + | tst.w d0 | |
| 348 | + | beq .idle | |
| 349 | + | bra .hopping | |
| 292 | 350 | | |
| 293 | - | move.w d0,d2 | |
| 294 | - | lsr.w #3,d2 | |
| 295 | - | ext.l d2 | |
| 296 | - | add.l d2,a0 | |
| 297 | - | rts | |
| 351 | + | .dying: | |
| 352 | + | ; Count down death timer | |
| 353 | + | subq.w #1,death_timer | |
| 354 | + | bgt.s .still_dying | |
| 298 | 355 | | |
| 299 | - | ;══════════════════════════════════════════════════════════════════════════════ | |
| 300 | - | ; FROG ROUTINES | |
| 301 | - | ;══════════════════════════════════════════════════════════════════════════════ | |
| 356 | + | ; Death complete - respawn | |
| 357 | + | bsr respawn_frog | |
| 358 | + | bra .done | |
| 302 | 359 | | |
| 303 | - | update_frog: | |
| 304 | - | tst.w frog_state | |
| 305 | - | beq .idle | |
| 306 | - | bra .hopping | |
| 360 | + | .still_dying: | |
| 361 | + | ; Flash effect: alternate colour | |
| 362 | + | move.w death_timer,d0 | |
| 363 | + | and.w #4,d0 ; Flash every 4 frames | |
| 364 | + | beq.s .flash_off | |
| 365 | + | move.w #FLASH_COLOUR,flash_colour | |
| 366 | + | bra .done | |
| 367 | + | .flash_off: | |
| 368 | + | move.w #COLOUR_BLACK,flash_colour | |
| 369 | + | bra .done | |
| 307 | 370 | | |
| 308 | 371 | .idle: | |
| 309 | 372 | bsr read_joystick_edge | |
| ... | |||
| 394 | 457 | move.w #STATE_IDLE,frog_state | |
| 395 | 458 | | |
| 396 | 459 | .done: | |
| 460 | + | ; Update flash colour in Copper list | |
| 461 | + | move.w flash_colour,flash_copper | |
| 397 | 462 | rts | |
| 463 | + | | |
| 464 | + | ;══════════════════════════════════════════════════════════════════════════════ | |
| 465 | + | ; UTILITY ROUTINES | |
| 466 | + | ;══════════════════════════════════════════════════════════════════════════════ | |
| 398 | 467 | | |
| 399 | 468 | grid_to_pixels: | |
| 400 | 469 | move.w frog_grid_x,d0 | |
| ... | |||
| 428 | 497 | move.l VPOSR(a5),d0 | |
| 429 | 498 | and.l d1,d0 | |
| 430 | 499 | bne.s .wait | |
| 500 | + | rts | |
| 501 | + | | |
| 502 | + | wait_blit: | |
| 503 | + | btst #6,DMACONR(a5) | |
| 504 | + | bne.s wait_blit | |
| 505 | + | rts | |
| 506 | + | | |
| 507 | + | clear_screen: | |
| 508 | + | bsr.s wait_blit | |
| 509 | + | move.l #screen_plane,BLTDPTH(a5) | |
| 510 | + | move.w #0,BLTDMOD(a5) | |
| 511 | + | move.w #$0100,BLTCON0(a5) | |
| 512 | + | move.w #0,BLTCON1(a5) | |
| 513 | + | move.w #(SCREEN_H<<6)|SCREEN_W/2,BLTSIZE(a5) | |
| 514 | + | rts | |
| 515 | + | | |
| 516 | + | calc_screen_addr: | |
| 517 | + | lea screen_plane,a0 | |
| 518 | + | move.w d1,d2 | |
| 519 | + | mulu #CELL_SIZE,d2 | |
| 520 | + | add.w #GRID_ORIGIN_Y,d2 | |
| 521 | + | mulu #SCREEN_W,d2 | |
| 522 | + | add.l d2,a0 | |
| 523 | + | move.w d0,d2 | |
| 524 | + | lsr.w #3,d2 | |
| 525 | + | ext.l d2 | |
| 526 | + | add.l d2,a0 | |
| 431 | 527 | rts | |
| 432 | 528 | | |
| 433 | 529 | update_sprite: | |
| ... | |||
| 459 | 555 | frog_anim_frame: dc.w 0 | |
| 460 | 556 | joy_prev: dc.w 0 | |
| 461 | 557 | | |
| 462 | - | ; Car data: X position, Row, Speed, (padding) | |
| 463 | - | ; Row 7 = road lane 1 (rows 7-11 are road lanes) | |
| 558 | + | death_timer: dc.w 0 | |
| 559 | + | flash_colour: dc.w 0 | |
| 560 | + | | |
| 464 | 561 | car_data: | |
| 465 | - | ; Lane 1 (row 7) - moving right, speed 1 | |
| 466 | 562 | dc.w 0,7,1,0 | |
| 467 | 563 | dc.w 160,7,1,0 | |
| 468 | - | | |
| 469 | - | ; Lane 2 (row 8) - moving left, speed -2 | |
| 470 | 564 | dc.w 100,8,-2,0 | |
| 471 | 565 | dc.w 250,8,-2,0 | |
| 472 | - | | |
| 473 | - | ; Lane 3 (row 9) - moving right, speed 2 | |
| 474 | 566 | dc.w 50,9,2,0 | |
| 475 | 567 | dc.w 200,9,2,0 | |
| 476 | - | | |
| 477 | - | ; Lane 4 (row 10) - moving left, speed -1 | |
| 478 | 568 | dc.w 80,10,-1,0 | |
| 479 | 569 | dc.w 220,10,-1,0 | |
| 480 | - | | |
| 481 | - | ; Lane 5 (row 11) - moving right, speed 3 | |
| 482 | 570 | dc.w 30,11,3,0 | |
| 483 | 571 | dc.w 180,11,3,0 | |
| 484 | 572 | | |
| ... | |||
| 487 | 575 | ;══════════════════════════════════════════════════════════════════════════════ | |
| 488 | 576 | | |
| 489 | 577 | copperlist: | |
| 490 | - | dc.w COLOR00,COLOUR_BLACK | |
| 578 | + | dc.w COLOR00 | |
| 579 | + | flash_copper: | |
| 580 | + | dc.w COLOUR_BLACK ; This gets modified for flash effect | |
| 491 | 581 | | |
| 492 | 582 | dc.w $0100,$1200 | |
| 493 | 583 | dc.w $0102,$0000 | |
| ... | |||
| 512 | 602 | dc.w SPR0PTL | |
| 513 | 603 | sprptl_val: dc.w $0000 | |
| 514 | 604 | | |
| 515 | - | ; ROW 1: HOME ZONE | |
| 516 | 605 | dc.w $2c07,$fffe | |
| 517 | 606 | dc.w COLOR00,COLOUR_HOME | |
| 518 | 607 | dc.w $3407,$fffe | |
| ... | |||
| 520 | 609 | dc.w $3807,$fffe | |
| 521 | 610 | dc.w COLOR00,COLOUR_HOME | |
| 522 | 611 | | |
| 523 | - | ; ROWS 2-6: WATER ZONE | |
| 524 | 612 | dc.w $3c07,$fffe | |
| 525 | 613 | dc.w COLOR00,COLOUR_WATER1 | |
| 526 | 614 | dc.w $4c07,$fffe | |
| ... | |||
| 532 | 620 | dc.w $7c07,$fffe | |
| 533 | 621 | dc.w COLOR00,COLOUR_WATER1 | |
| 534 | 622 | | |
| 535 | - | ; ROW 7: MEDIAN | |
| 536 | 623 | dc.w $8c07,$fffe | |
| 537 | 624 | dc.w COLOR00,COLOUR_MEDIAN | |
| 538 | 625 | | |
| 539 | - | ; ROWS 8-12: ROAD ZONE | |
| 540 | 626 | dc.w $9c07,$fffe | |
| 541 | 627 | dc.w COLOR00,COLOUR_ROAD1 | |
| 542 | 628 | dc.w $ac07,$fffe | |
| ... | |||
| 548 | 634 | dc.w $dc07,$fffe | |
| 549 | 635 | dc.w COLOR00,COLOUR_ROAD1 | |
| 550 | 636 | | |
| 551 | - | ; ROW 13: START ZONE | |
| 552 | 637 | dc.w $ec07,$fffe | |
| 553 | 638 | dc.w COLOR00,COLOUR_START | |
| 554 | 639 | dc.w $f407,$fffe |