Sound Effects
Paula plays samples. Hop sound. Death sound. Home sound. Audio presence.
What You’re Building
Audio presence.
A silent game feels dead. This unit adds sound effects using Paula, the Amiga’s audio chip. Simple 8-bit waveforms play when you hop, die, or dock at a home zone. The result: immediate audio feedback that makes the game feel alive.
By the end of this unit:
- Hop sound when the frog jumps
- Death sound when hit by a car
- Success sound when reaching home
- Understanding of Paula’s sample playback

Paula: The Audio Chip
The Amiga’s Paula chip handles audio output. It has 4 independent channels, each capable of playing 8-bit signed PCM samples at variable rates. For our simple sound effects, we only need one channel.
; Paula Audio Registers
; Channel 0 registers for sound playback
; Paula audio registers (channel 0)
AUD0LC equ $0a0 ; Sample pointer (long)
AUD0LEN equ $0a4 ; Sample length in words
AUD0PER equ $0a6 ; Period (pitch): lower = higher
AUD0VOL equ $0a8 ; Volume: 0-64
; Audio DMA control
; DMACON bit 0 = AUD0 channel enable
; To disable: move.w #$0001,DMACON(a5)
; To enable: move.w #$8001,DMACON(a5)
; Period values (approximate):
; 200 = high pitch (chirp)
; 300 = medium pitch
; 500 = low pitch (death)
; Sample format:
; - 8-bit signed values (-128 to +127)
; - $7f = maximum positive, $80 = maximum negative
; - Must be in Chip RAM for DMA access
; - Length in words (bytes / 2)
Paula expects 8-bit signed samples. Values range from -128 ($80) to +127 ($7f). Sample data must reside in Chip RAM so the DMA can access it.
The Play Sound Routine
A single routine handles all sound playback:
; Play Sound Effect
; Select sound by number, configure Paula, trigger playback
; Sound effect numbers
SND_HOP equ 0
SND_DEATH equ 1
SND_HOME equ 2
play_sound:
; Get sound data based on D0
cmp.w #SND_HOP,d0
beq.s .play_hop
cmp.w #SND_DEATH,d0
beq.s .play_death
cmp.w #SND_HOME,d0
beq.s .play_home
rts
.play_hop:
lea sound_hop,a0
move.w #HOP_LEN/2,d1 ; Length in words
move.w #300,d2 ; Period (medium pitch)
bra.s .do_play
.play_death:
lea sound_death,a0
move.w #DEATH_LEN/2,d1
move.w #500,d2 ; Period (lower pitch)
bra.s .do_play
.play_home:
lea sound_home,a0
move.w #HOME_LEN/2,d1
move.w #200,d2 ; Period (higher pitch)
.do_play:
; Stop any playing sound first
move.w #$0001,DMACON(a5) ; Disable AUD0
; Configure channel
move.l a0,AUD0LC(a5) ; Sample pointer
move.w d1,AUD0LEN(a5) ; Length in words
move.w d2,AUD0PER(a5) ; Period (pitch)
move.w #64,AUD0VOL(a5) ; Maximum volume
; Start playback
move.w #$8001,DMACON(a5) ; Enable AUD0
rts
Audio DMA
Paula uses DMA to fetch sample data automatically. We enable it in two places:
- At startup, include audio DMA in the initial DMACON setup:
move.w #$87e1,DMACON(a5) ; Enable Copper, Blitter, Sprites, AUD0
- When triggering a sound, we toggle the channel’s DMA:
move.w #$0001,DMACON(a5) ; Disable AUD0 (clear bit 0)
; ... set up registers ...
move.w #$8001,DMACON(a5) ; Enable AUD0 (set bit 0)
Creating Sound Waveforms
Our sounds are simple square waves defined inline:
; Sound Data - 8-bit Signed Samples
; Simple square waves for game effects
even
; Hop sound - short square wave burst (32 bytes)
sound_hop:
dc.b $7f,$7f,$7f,$7f,$7f,$7f,$7f,$7f ; High (+127)
dc.b $80,$80,$80,$80,$80,$80,$80,$80 ; Low (-128)
dc.b $7f,$7f,$7f,$7f,$7f,$7f,$7f,$7f ; High
dc.b $80,$80,$80,$80,$80,$80,$80,$80 ; Low
HOP_LEN equ *-sound_hop
; Death sound - descending tone (variable wavelength)
sound_death:
dc.b $7f,$7f,$7f,$7f,$80,$80,$80,$80 ; Fast cycle (high)
dc.b $7f,$7f,$7f,$7f,$80,$80,$80,$80
dc.b $7f,$7f,$7f,$7f,$7f,$80,$80,$80,$80,$80 ; Slower
dc.b $7f,$7f,$7f,$7f,$7f,$7f,$80,$80,$80,$80,$80,$80 ; Even slower
dc.b $7f,$7f,$7f,$7f,$7f,$7f,$7f,$7f ; Slowest (low)
dc.b $80,$80,$80,$80,$80,$80,$80,$80
DEATH_LEN equ *-sound_death
; Home sound - bright chirp (ascending)
sound_home:
dc.b $7f,$80,$7f,$80,$7f,$80,$7f,$80 ; Very fast (high pitch)
dc.b $7f,$80,$7f,$80,$7f,$80,$7f,$80
dc.b $7f,$7f,$80,$80,$7f,$7f,$80,$80 ; Medium
dc.b $7f,$7f,$80,$80,$7f,$7f,$80,$80
dc.b $7f,$7f,$7f,$80,$80,$80,$7f,$7f,$7f,$80,$80,$80 ; Lower
dc.b $00,$00,$00,$00 ; Silence
HOME_LEN equ *-sound_home
The hop sound is a short burst, the death sound has varying cycle length for a descending effect, and the home sound is a bright chirp.
Triggering Sounds
We call play_sound at the appropriate moments:
On hop start:
.start_hop:
move.w #STATE_HOPPING,frog_state
clr.w frog_anim_frame
; Play hop sound
move.w #SND_HOP,d0
bsr play_sound
On death:
trigger_death:
move.w #STATE_DYING,frog_state
; ...
; Play death sound
move.w #SND_DEATH,d0
bsr play_sound
On reaching home:
; Home is empty - fill it!
move.w #1,0(a0,d1.w)
add.l #POINTS_PER_HOME,score
; Play success sound
move.w #SND_HOME,d0
bsr play_sound
Why Stop Before Playing?
We disable the channel before setting new values:
move.w #$0001,DMACON(a5) ; Disable AUD0
This ensures clean transitions. If a sound is already playing and we change the sample pointer, we might hear a glitch. Stopping first creates a clean break.
Sound Constants
We define symbolic constants for cleaner code:
SND_HOP equ 0
SND_DEATH equ 1
SND_HOME equ 2
This makes the calling code self-documenting:
move.w #SND_HOME,d0 ; Clear intent
bsr play_sound
Key Takeaways
- Paula plays 8-bit signed samples via DMA
- AUD0LC points to sample data in Chip RAM
- AUD0LEN is length in words, not bytes
- AUD0PER controls pitch (lower = higher)
- AUD0VOL sets volume (0-64)
- Audio DMA must be enabled for playback
- Stop before play prevents audio glitches
- Inline waveforms work for simple effects
The Code
;==============================================================================
; SIGNAL - A Frogger-style game for the Commodore Amiga
; Unit 11: Sound Effects
;
; A game without sound feels lifeless. This unit adds audio feedback using
; Paula, the Amiga's sound chip. Simple 8-bit waveforms play when you hop,
; die, or reach a home zone.
;==============================================================================
;==============================================================================
; 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
; Home zones
NUM_HOMES equ 5
HOME_ROW equ 0
POINTS_PER_HOME equ 50
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
; Audio constants
SND_HOP equ 0
SND_DEATH equ 1
SND_HOME equ 2
; Colours
COLOUR_BLACK equ $0000
COLOUR_HOME equ $0282
COLOUR_HOME_LIT equ $03a3
COLOUR_HOME_FILLED equ $0f80
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
SPR1PTH equ $124
SPR1PTL equ $126
SPR2PTH equ $128
SPR2PTL equ $12a
; Paula audio registers (channel 0)
AUD0LC equ $0a0 ; Sample pointer (long)
AUD0LEN equ $0a4 ; Sample length in words
AUD0PER equ $0a6 ; Period (lower = higher pitch)
AUD0VOL equ $0a8 ; Volume (0-64)
;==============================================================================
; 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
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 (including audio channel 0)
move.w #$87e1,DMACON(a5) ; +AUD0
;==============================================================================
; 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:
btst #7,$bfe001
bne.s mainloop
; Restart game
bsr init_game
bsr update_life_display
bsr clear_screen
bra mainloop
;==============================================================================
; SOUND ROUTINES
;==============================================================================
;------------------------------------------------------------------------------
; PLAY_SOUND - Play a sound effect
; Input: D0 = sound number (SND_HOP, SND_DEATH, SND_HOME)
;------------------------------------------------------------------------------
play_sound:
; Get sound data address and parameters
cmp.w #SND_HOP,d0
beq.s .play_hop
cmp.w #SND_DEATH,d0
beq.s .play_death
cmp.w #SND_HOME,d0
beq.s .play_home
rts
.play_hop:
lea sound_hop,a0
move.w #HOP_LEN/2,d1 ; Length in words
move.w #300,d2 ; Period (medium pitch)
bra.s .do_play
.play_death:
lea sound_death,a0
move.w #DEATH_LEN/2,d1
move.w #500,d2 ; Period (lower pitch)
bra.s .do_play
.play_home:
lea sound_home,a0
move.w #HOME_LEN/2,d1
move.w #200,d2 ; Period (higher pitch)
.do_play:
; Stop any playing sound first
move.w #$0001,DMACON(a5) ; Disable AUD0
; Set sample pointer
move.l a0,AUD0LC(a5)
; Set length (in words)
move.w d1,AUD0LEN(a5)
; Set period
move.w d2,AUD0PER(a5)
; Set volume (max = 64)
move.w #64,AUD0VOL(a5)
; Enable audio DMA
move.w #$8001,DMACON(a5) ; Enable AUD0
rts
;==============================================================================
; GAME INITIALISATION
;==============================================================================
init_game:
move.w #START_LIVES,lives
clr.l score
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:
move.w frog_grid_y,d0
tst.w d0
bne .not_home
move.w frog_grid_x,d0
cmp.w #HOME_0_X,d0
blt.s .not_home
cmp.w #HOME_0_X+2,d0
blt.s .home_0
cmp.w #HOME_1_X,d0
blt.s .not_home
cmp.w #HOME_1_X+2,d0
blt.s .home_1
cmp.w #HOME_2_X,d0
blt.s .not_home
cmp.w #HOME_2_X+2,d0
blt.s .home_2
cmp.w #HOME_3_X,d0
blt.s .not_home
cmp.w #HOME_3_X+2,d0
blt.s .home_3
cmp.w #HOME_4_X,d0
blt.s .not_home
cmp.w #HOME_4_X+2,d0
blt.s .home_4
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:
lea home_filled,a0
add.w d1,d1
tst.w 0(a0,d1.w)
bne.s .death_filled
move.w #1,0(a0,d1.w)
add.l #POINTS_PER_HOME,score
; Play success sound
move.w #SND_HOME,d0
bsr play_sound
bsr update_home_display
bsr check_round_complete
bsr reset_frog
rts
.death_filled:
.death_between:
bsr trigger_death
rts
.not_home:
rts
check_round_complete:
lea home_filled,a0
moveq #NUM_HOMES-1,d0
moveq #0,d1
.loop:
tst.w (a0)+
beq.s .not_complete
addq.w #1,d1
dbf d0,.loop
cmp.w #NUM_HOMES,d1
bne.s .not_complete
lea home_filled,a0
moveq #NUM_HOMES-1,d0
.clear:
clr.w (a0)+
dbf d0,.clear
bsr update_home_display
.not_complete:
rts
update_home_display:
lea home_filled,a0
lea home_colour_0,a1
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:
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)
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)
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:
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
; Play death sound
move.w #SND_DEATH,d0
bsr play_sound
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
tst.w lives
beq.s .game_over
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
; Play hop sound
move.w #SND_HOP,d0
bsr play_sound
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_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
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 SPR1PTH
spr1pth_val: dc.w $0000
dc.w SPR1PTL
spr1ptl_val: dc.w $0000
dc.w SPR2PTH
spr2pth_val: dc.w $0000
dc.w SPR2PTL
spr2ptl_val: dc.w $0000
dc.w $0126
spr3pth_val: dc.w $0000
dc.w $012a
spr3ptl_val: dc.w $0000
; Home zone row
dc.w $2c07,$fffe
dc.w COLOR00,COLOUR_HOME
dc.w $2c4f,$fffe
dc.w COLOR00
home_colour_0:
dc.w COLOUR_HOME_LIT
dc.w $2c5f,$fffe
dc.w COLOR00,COLOUR_HOME
dc.w $2c8f,$fffe
dc.w COLOR00
home_colour_1:
dc.w COLOUR_HOME_LIT
dc.w $2c9f,$fffe
dc.w COLOR00,COLOUR_HOME
dc.w $2ccf,$fffe
dc.w COLOR00
home_colour_2:
dc.w COLOUR_HOME_LIT
dc.w $2cdf,$fffe
dc.w COLOR00,COLOUR_HOME
dc.w $2d07,$fffe
dc.w COLOR00
home_colour_3:
dc.w COLOUR_HOME_LIT
dc.w $2d17,$fffe
dc.w COLOR00,COLOUR_HOME
dc.w $2d47,$fffe
dc.w COLOR00
home_colour_4:
dc.w COLOUR_HOME_LIT
dc.w $2d57,$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
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
;==============================================================================
; SOUND DATA (8-bit signed samples in Chip RAM)
;==============================================================================
even
; Hop sound - short square wave burst (32 bytes = 16 words)
sound_hop:
dc.b $7f,$7f,$7f,$7f,$7f,$7f,$7f,$7f ; High
dc.b $80,$80,$80,$80,$80,$80,$80,$80 ; Low
dc.b $7f,$7f,$7f,$7f,$7f,$7f,$7f,$7f ; High
dc.b $80,$80,$80,$80,$80,$80,$80,$80 ; Low
HOP_LEN equ *-sound_hop
; Death sound - descending tone (64 bytes = 32 words)
sound_death:
dc.b $7f,$7f,$7f,$7f,$80,$80,$80,$80 ; Fast cycle
dc.b $7f,$7f,$7f,$7f,$80,$80,$80,$80
dc.b $7f,$7f,$7f,$7f,$7f,$80,$80,$80,$80,$80 ; Slower
dc.b $7f,$7f,$7f,$7f,$7f,$7f,$80,$80,$80,$80,$80,$80 ; Slower still
dc.b $7f,$7f,$7f,$7f,$7f,$7f,$7f,$7f ; Even slower
dc.b $80,$80,$80,$80,$80,$80,$80,$80
DEATH_LEN equ *-sound_death
; Home sound - bright chirp (48 bytes = 24 words)
sound_home:
dc.b $7f,$80,$7f,$80,$7f,$80,$7f,$80 ; Fast high
dc.b $7f,$80,$7f,$80,$7f,$80,$7f,$80
dc.b $7f,$7f,$80,$80,$7f,$7f,$80,$80 ; Medium
dc.b $7f,$7f,$80,$80,$7f,$7f,$80,$80
dc.b $7f,$7f,$7f,$80,$80,$80,$7f,$7f,$7f,$80,$80,$80 ; Lower finish
dc.b $00,$00,$00,$00 ; Silence
HOME_LEN equ *-sound_home
;==============================================================================
; 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 sound, but no visible score. In Unit 12, we’ll add score display using bitmap fonts—finally showing the player their progress.
What Changed
| 1 | 1 | ;============================================================================== | |
| 2 | 2 | ; SIGNAL - A Frogger-style game for the Commodore Amiga | |
| 3 | - | ; Unit 10: Home Zones | |
| 3 | + | ; Unit 11: Sound Effects | |
| 4 | 4 | ; | |
| 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. | |
| 5 | + | ; A game without sound feels lifeless. This unit adds audio feedback using | |
| 6 | + | ; Paula, the Amiga's sound chip. Simple 8-bit waveforms play when you hop, | |
| 7 | + | ; die, or reach a home zone. | |
| 8 | 8 | ;============================================================================== | |
| 9 | 9 | | |
| 10 | 10 | ;============================================================================== | |
| ... | |||
| 50 | 50 | | |
| 51 | 51 | ; Lives system | |
| 52 | 52 | START_LIVES equ 3 | |
| 53 | - | LIFE_ICON_W equ 1 | |
| 54 | - | LIFE_ICON_H equ 8 | |
| 55 | - | LIFE_ICON_X equ 8 | |
| 56 | - | LIFE_ICON_Y equ 260 | |
| 57 | - | LIFE_ICON_SPACING equ 20 | |
| 58 | 53 | | |
| 59 | 54 | ; Home zones | |
| 60 | 55 | NUM_HOMES equ 5 | |
| 61 | - | HOME_ROW equ 0 ; Top row of grid | |
| 56 | + | HOME_ROW equ 0 | |
| 62 | 57 | POINTS_PER_HOME equ 50 | |
| 63 | 58 | | |
| 64 | - | ; Home X positions (grid coordinates): 2, 6, 10, 14, 18 | |
| 65 | - | ; Each home is 2 cells wide (even column = start of home) | |
| 66 | 59 | HOME_0_X equ 2 | |
| 67 | 60 | HOME_1_X equ 6 | |
| 68 | 61 | HOME_2_X equ 10 | |
| 69 | 62 | HOME_3_X equ 14 | |
| 70 | 63 | HOME_4_X equ 18 | |
| 64 | + | | |
| 65 | + | ; Audio constants | |
| 66 | + | SND_HOP equ 0 | |
| 67 | + | SND_DEATH equ 1 | |
| 68 | + | SND_HOME equ 2 | |
| 71 | 69 | | |
| 72 | 70 | ; Colours | |
| 73 | 71 | COLOUR_BLACK equ $0000 | |
| 74 | 72 | COLOUR_HOME equ $0282 | |
| 75 | 73 | COLOUR_HOME_LIT equ $03a3 | |
| 76 | - | COLOUR_HOME_FILLED equ $0f80 ; Orange for filled homes | |
| 74 | + | COLOUR_HOME_FILLED equ $0f80 | |
| 77 | 75 | COLOUR_WATER1 equ $0148 | |
| 78 | 76 | COLOUR_WATER2 equ $026a | |
| 79 | 77 | COLOUR_MEDIAN equ $0383 | |
| ... | |||
| 85 | 83 | COLOUR_EYES equ $0ff0 | |
| 86 | 84 | COLOUR_OUTLINE equ $0000 | |
| 87 | 85 | COLOUR_CAR equ $0f00 | |
| 88 | - | COLOUR_LIFE equ $00f0 | |
| 89 | 86 | | |
| 90 | 87 | ;============================================================================== | |
| 91 | 88 | ; HARDWARE REGISTERS | |
| ... | |||
| 123 | 120 | SPR1PTL equ $126 | |
| 124 | 121 | SPR2PTH equ $128 | |
| 125 | 122 | SPR2PTL equ $12a | |
| 123 | + | | |
| 124 | + | ; Paula audio registers (channel 0) | |
| 125 | + | AUD0LC equ $0a0 ; Sample pointer (long) | |
| 126 | + | AUD0LEN equ $0a4 ; Sample length in words | |
| 127 | + | AUD0PER equ $0a6 ; Period (lower = higher pitch) | |
| 128 | + | AUD0VOL equ $0a8 ; Volume (0-64) | |
| 126 | 129 | | |
| 127 | 130 | ;============================================================================== | |
| 128 | 131 | ; CODE SECTION | |
| ... | |||
| 162 | 165 | lea sprptl_val,a1 | |
| 163 | 166 | move.w d0,(a1) | |
| 164 | 167 | | |
| 165 | - | ; Set up life icon sprites (sprites 1, 2, 3) | |
| 168 | + | ; Set up life icon sprites | |
| 166 | 169 | bsr setup_life_sprites | |
| 167 | 170 | | |
| 168 | 171 | bsr update_sprite | |
| ... | |||
| 173 | 176 | move.l a0,COP1LC(a5) | |
| 174 | 177 | move.w d0,COPJMP1(a5) | |
| 175 | 178 | | |
| 176 | - | ; Enable DMA | |
| 177 | - | move.w #$87e0,DMACON(a5) | |
| 179 | + | ; Enable DMA (including audio channel 0) | |
| 180 | + | move.w #$87e1,DMACON(a5) ; +AUD0 | |
| 178 | 181 | | |
| 179 | 182 | ;============================================================================== | |
| 180 | 183 | ; MAIN LOOP | |
| ... | |||
| 206 | 209 | bra mainloop | |
| 207 | 210 | | |
| 208 | 211 | .game_over_loop: | |
| 209 | - | ; Game over - wait for fire button to restart | |
| 210 | 212 | btst #7,$bfe001 | |
| 211 | 213 | bne.s mainloop | |
| 212 | 214 | | |
| ... | |||
| 216 | 218 | bsr clear_screen | |
| 217 | 219 | | |
| 218 | 220 | bra mainloop | |
| 221 | + | | |
| 222 | + | ;============================================================================== | |
| 223 | + | ; SOUND ROUTINES | |
| 224 | + | ;============================================================================== | |
| 225 | + | | |
| 226 | + | ;------------------------------------------------------------------------------ | |
| 227 | + | ; PLAY_SOUND - Play a sound effect | |
| 228 | + | ; Input: D0 = sound number (SND_HOP, SND_DEATH, SND_HOME) | |
| 229 | + | ;------------------------------------------------------------------------------ | |
| 230 | + | play_sound: | |
| 231 | + | ; Get sound data address and parameters | |
| 232 | + | cmp.w #SND_HOP,d0 | |
| 233 | + | beq.s .play_hop | |
| 234 | + | cmp.w #SND_DEATH,d0 | |
| 235 | + | beq.s .play_death | |
| 236 | + | cmp.w #SND_HOME,d0 | |
| 237 | + | beq.s .play_home | |
| 238 | + | rts | |
| 239 | + | | |
| 240 | + | .play_hop: | |
| 241 | + | lea sound_hop,a0 | |
| 242 | + | move.w #HOP_LEN/2,d1 ; Length in words | |
| 243 | + | move.w #300,d2 ; Period (medium pitch) | |
| 244 | + | bra.s .do_play | |
| 245 | + | | |
| 246 | + | .play_death: | |
| 247 | + | lea sound_death,a0 | |
| 248 | + | move.w #DEATH_LEN/2,d1 | |
| 249 | + | move.w #500,d2 ; Period (lower pitch) | |
| 250 | + | bra.s .do_play | |
| 251 | + | | |
| 252 | + | .play_home: | |
| 253 | + | lea sound_home,a0 | |
| 254 | + | move.w #HOME_LEN/2,d1 | |
| 255 | + | move.w #200,d2 ; Period (higher pitch) | |
| 256 | + | | |
| 257 | + | .do_play: | |
| 258 | + | ; Stop any playing sound first | |
| 259 | + | move.w #$0001,DMACON(a5) ; Disable AUD0 | |
| 260 | + | | |
| 261 | + | ; Set sample pointer | |
| 262 | + | move.l a0,AUD0LC(a5) | |
| 263 | + | | |
| 264 | + | ; Set length (in words) | |
| 265 | + | move.w d1,AUD0LEN(a5) | |
| 266 | + | | |
| 267 | + | ; Set period | |
| 268 | + | move.w d2,AUD0PER(a5) | |
| 269 | + | | |
| 270 | + | ; Set volume (max = 64) | |
| 271 | + | move.w #64,AUD0VOL(a5) | |
| 272 | + | | |
| 273 | + | ; Enable audio DMA | |
| 274 | + | move.w #$8001,DMACON(a5) ; Enable AUD0 | |
| 275 | + | | |
| 276 | + | rts | |
| 219 | 277 | | |
| 220 | 278 | ;============================================================================== | |
| 221 | 279 | ; GAME INITIALISATION | |
| ... | |||
| 225 | 283 | move.w #START_LIVES,lives | |
| 226 | 284 | clr.l score | |
| 227 | 285 | | |
| 228 | - | ; Clear all homes | |
| 229 | 286 | lea home_filled,a0 | |
| 230 | 287 | moveq #NUM_HOMES-1,d0 | |
| 231 | 288 | .clear_homes: | |
| ... | |||
| 240 | 297 | ; HOME ZONE DETECTION | |
| 241 | 298 | ;============================================================================== | |
| 242 | 299 | | |
| 243 | - | ;------------------------------------------------------------------------------ | |
| 244 | - | ; CHECK_HOME - Check if frog has reached a home zone | |
| 245 | - | ;------------------------------------------------------------------------------ | |
| 246 | 300 | check_home: | |
| 247 | - | ; Only check if at top row | |
| 248 | 301 | move.w frog_grid_y,d0 | |
| 249 | 302 | tst.w d0 | |
| 250 | 303 | bne .not_home | |
| 251 | 304 | | |
| 252 | - | ; At top row - determine which home | |
| 253 | 305 | move.w frog_grid_x,d0 | |
| 254 | 306 | | |
| 255 | - | ; Check home 0 (X = 2-3) | |
| 256 | 307 | cmp.w #HOME_0_X,d0 | |
| 257 | 308 | blt.s .not_home | |
| 258 | 309 | cmp.w #HOME_0_X+2,d0 | |
| 259 | 310 | blt.s .home_0 | |
| 260 | 311 | | |
| 261 | - | ; Check home 1 (X = 6-7) | |
| 262 | 312 | cmp.w #HOME_1_X,d0 | |
| 263 | 313 | blt.s .not_home | |
| 264 | 314 | cmp.w #HOME_1_X+2,d0 | |
| 265 | 315 | blt.s .home_1 | |
| 266 | 316 | | |
| 267 | - | ; Check home 2 (X = 10-11) | |
| 268 | 317 | cmp.w #HOME_2_X,d0 | |
| 269 | 318 | blt.s .not_home | |
| 270 | 319 | cmp.w #HOME_2_X+2,d0 | |
| 271 | 320 | blt.s .home_2 | |
| 272 | 321 | | |
| 273 | - | ; Check home 3 (X = 14-15) | |
| 274 | 322 | cmp.w #HOME_3_X,d0 | |
| 275 | 323 | blt.s .not_home | |
| 276 | 324 | cmp.w #HOME_3_X+2,d0 | |
| 277 | 325 | blt.s .home_3 | |
| 278 | 326 | | |
| 279 | - | ; Check home 4 (X = 18-19) | |
| 280 | 327 | cmp.w #HOME_4_X,d0 | |
| 281 | 328 | blt.s .not_home | |
| 282 | 329 | cmp.w #HOME_4_X+2,d0 | |
| 283 | 330 | blt.s .home_4 | |
| 284 | 331 | | |
| 285 | - | ; Between homes - death! | |
| 286 | 332 | bra .death_between | |
| 287 | 333 | | |
| 288 | 334 | .home_0: moveq #0,d1 | |
| ... | |||
| 296 | 342 | .home_4: moveq #4,d1 | |
| 297 | 343 | | |
| 298 | 344 | .check_filled: | |
| 299 | - | ; D1 = home index (0-4) | |
| 300 | 345 | lea home_filled,a0 | |
| 301 | - | add.w d1,d1 ; *2 for word offset | |
| 346 | + | add.w d1,d1 | |
| 302 | 347 | tst.w 0(a0,d1.w) | |
| 303 | - | bne.s .death_filled ; Already filled - death! | |
| 348 | + | bne.s .death_filled | |
| 304 | 349 | | |
| 305 | - | ; Home is empty - fill it! | |
| 306 | 350 | move.w #1,0(a0,d1.w) | |
| 307 | - | | |
| 308 | - | ; Add points | |
| 309 | 351 | add.l #POINTS_PER_HOME,score | |
| 310 | 352 | | |
| 311 | - | ; Update display | |
| 312 | - | bsr update_home_display | |
| 353 | + | ; Play success sound | |
| 354 | + | move.w #SND_HOME,d0 | |
| 355 | + | bsr play_sound | |
| 313 | 356 | | |
| 314 | - | ; Check if all homes filled | |
| 357 | + | bsr update_home_display | |
| 315 | 358 | bsr check_round_complete | |
| 316 | - | | |
| 317 | - | ; Respawn frog | |
| 318 | 359 | bsr reset_frog | |
| 319 | 360 | rts | |
| 320 | 361 | | |
| 321 | 362 | .death_filled: | |
| 322 | - | ; Landed on filled home - death | |
| 323 | - | bsr trigger_death | |
| 324 | - | rts | |
| 325 | - | | |
| 326 | 363 | .death_between: | |
| 327 | - | ; Landed between homes (on lily pad gaps) - death | |
| 328 | 364 | bsr trigger_death | |
| 329 | 365 | rts | |
| 330 | 366 | | |
| 331 | 367 | .not_home: | |
| 332 | 368 | rts | |
| 333 | 369 | | |
| 334 | - | ;------------------------------------------------------------------------------ | |
| 335 | - | ; CHECK_ROUND_COMPLETE - Check if all 5 homes are filled | |
| 336 | - | ;------------------------------------------------------------------------------ | |
| 337 | 370 | check_round_complete: | |
| 338 | 371 | lea home_filled,a0 | |
| 339 | 372 | moveq #NUM_HOMES-1,d0 | |
| 340 | - | moveq #0,d1 ; Count filled homes | |
| 373 | + | moveq #0,d1 | |
| 341 | 374 | | |
| 342 | 375 | .loop: | |
| 343 | 376 | tst.w (a0)+ | |
| ... | |||
| 345 | 378 | addq.w #1,d1 | |
| 346 | 379 | dbf d0,.loop | |
| 347 | 380 | | |
| 348 | - | ; All homes filled - reset for next round | |
| 349 | 381 | cmp.w #NUM_HOMES,d1 | |
| 350 | 382 | bne.s .not_complete | |
| 351 | 383 | | |
| 352 | - | ; Clear all homes for next round | |
| 353 | 384 | lea home_filled,a0 | |
| 354 | 385 | moveq #NUM_HOMES-1,d0 | |
| 355 | 386 | .clear: | |
| ... | |||
| 357 | 388 | dbf d0,.clear | |
| 358 | 389 | | |
| 359 | 390 | bsr update_home_display | |
| 360 | - | ; Could add bonus points here, increase speed, etc. | |
| 361 | 391 | | |
| 362 | 392 | .not_complete: | |
| 363 | 393 | rts | |
| 364 | 394 | | |
| 365 | - | ;------------------------------------------------------------------------------ | |
| 366 | - | ; UPDATE_HOME_DISPLAY - Update Copper colours to show filled homes | |
| 367 | - | ;------------------------------------------------------------------------------ | |
| 368 | 395 | update_home_display: | |
| 369 | 396 | lea home_filled,a0 | |
| 370 | 397 | lea home_colour_0,a1 | |
| 371 | 398 | | |
| 372 | - | ; Home 0 | |
| 373 | 399 | tst.w (a0)+ | |
| 374 | 400 | beq.s .home0_empty | |
| 375 | 401 | move.w #COLOUR_HOME_FILLED,(a1) | |
| ... | |||
| 419 | 445 | ;============================================================================== | |
| 420 | 446 | | |
| 421 | 447 | setup_life_sprites: | |
| 422 | - | ; Sprite 1 | |
| 423 | 448 | lea life_icon_1,a0 | |
| 424 | 449 | move.l a0,d0 | |
| 425 | 450 | swap d0 | |
| ... | |||
| 429 | 454 | lea spr1ptl_val,a1 | |
| 430 | 455 | move.w d0,(a1) | |
| 431 | 456 | | |
| 432 | - | ; Sprite 2 | |
| 433 | 457 | lea life_icon_2,a0 | |
| 434 | 458 | move.l a0,d0 | |
| 435 | 459 | swap d0 | |
| ... | |||
| 439 | 463 | lea spr2ptl_val,a1 | |
| 440 | 464 | move.w d0,(a1) | |
| 441 | 465 | | |
| 442 | - | ; Sprite 3 | |
| 443 | 466 | lea life_icon_3,a0 | |
| 444 | 467 | move.l a0,d0 | |
| 445 | 468 | swap d0 | |
| ... | |||
| 452 | 475 | rts | |
| 453 | 476 | | |
| 454 | 477 | update_life_display: | |
| 455 | - | ; Life 1 (always show if lives >= 1) | |
| 456 | 478 | lea life_icon_1,a0 | |
| 457 | 479 | move.w lives,d0 | |
| 458 | 480 | cmp.w #1,d0 | |
| ... | |||
| 531 | 553 | move.w #DEATH_FRAMES,death_timer | |
| 532 | 554 | move.w #FLASH_COLOUR,flash_colour | |
| 533 | 555 | | |
| 534 | - | ; Decrement lives | |
| 556 | + | ; Play death sound | |
| 557 | + | move.w #SND_DEATH,d0 | |
| 558 | + | bsr play_sound | |
| 559 | + | | |
| 535 | 560 | subq.w #1,lives | |
| 536 | 561 | bsr update_life_display | |
| 537 | 562 | | |
| ... | |||
| 644 | 669 | subq.w #1,death_timer | |
| 645 | 670 | bgt.s .still_dying | |
| 646 | 671 | | |
| 647 | - | ; Death complete - check for game over | |
| 648 | 672 | tst.w lives | |
| 649 | 673 | beq.s .game_over | |
| 650 | 674 | | |
| 651 | - | ; Still have lives - respawn | |
| 652 | 675 | bsr reset_frog | |
| 653 | 676 | bra .done | |
| 654 | 677 | | |
| ... | |||
| 705 | 728 | .start_hop: | |
| 706 | 729 | move.w #STATE_HOPPING,frog_state | |
| 707 | 730 | clr.w frog_anim_frame | |
| 731 | + | | |
| 732 | + | ; Play hop sound | |
| 733 | + | move.w #SND_HOP,d0 | |
| 734 | + | bsr play_sound | |
| 735 | + | | |
| 708 | 736 | bra.s .done | |
| 709 | 737 | | |
| 710 | 738 | .hopping: | |
| ... | |||
| 858 | 886 | lives: dc.w 3 | |
| 859 | 887 | score: dc.l 0 | |
| 860 | 888 | | |
| 861 | - | ; Home zone state (0 = empty, 1 = filled) | |
| 862 | 889 | home_filled: dc.w 0,0,0,0,0 | |
| 863 | 890 | | |
| 864 | 891 | car_data: | |
| ... | |||
| 896 | 923 | dc.w $0180,$0000 | |
| 897 | 924 | dc.w $0182,COLOUR_CAR | |
| 898 | 925 | | |
| 899 | - | ; Sprite 0-1 palette (frog and life icons share this) | |
| 900 | 926 | dc.w $01a2,COLOUR_FROG | |
| 901 | 927 | dc.w $01a4,COLOUR_EYES | |
| 902 | 928 | dc.w $01a6,COLOUR_OUTLINE | |
| 903 | 929 | | |
| 904 | - | ; Sprite 0 pointer (main frog) | |
| 905 | 930 | dc.w SPR0PTH | |
| 906 | 931 | sprpth_val: dc.w $0000 | |
| 907 | 932 | dc.w SPR0PTL | |
| 908 | 933 | sprptl_val: dc.w $0000 | |
| 909 | 934 | | |
| 910 | - | ; Sprite 1 pointer (life icon 1) | |
| 911 | 935 | dc.w SPR1PTH | |
| 912 | 936 | spr1pth_val: dc.w $0000 | |
| 913 | 937 | dc.w SPR1PTL | |
| 914 | 938 | spr1ptl_val: dc.w $0000 | |
| 915 | 939 | | |
| 916 | - | ; Sprite 2 pointer (life icon 2) | |
| 917 | 940 | dc.w SPR2PTH | |
| 918 | 941 | spr2pth_val: dc.w $0000 | |
| 919 | 942 | dc.w SPR2PTL | |
| 920 | 943 | spr2ptl_val: dc.w $0000 | |
| 921 | 944 | | |
| 922 | - | ; Sprite 3 pointer (life icon 3) | |
| 923 | - | dc.w $0126 ; SPR3PTH | |
| 945 | + | dc.w $0126 | |
| 924 | 946 | spr3pth_val: dc.w $0000 | |
| 925 | - | dc.w $012a ; SPR3PTL | |
| 947 | + | dc.w $012a | |
| 926 | 948 | spr3ptl_val: dc.w $0000 | |
| 927 | 949 | | |
| 928 | - | ; Home zone row - 5 distinct zones for visual feedback | |
| 929 | - | ; Each home changes colour when filled | |
| 950 | + | ; Home zone row | |
| 930 | 951 | dc.w $2c07,$fffe | |
| 931 | - | dc.w COLOR00,COLOUR_HOME ; Gaps between homes | |
| 952 | + | dc.w COLOR00,COLOUR_HOME | |
| 932 | 953 | | |
| 933 | - | ; Home 0 (columns 2-3, pixels 48+32=80 to 48+48=96) | |
| 934 | - | dc.w $2c4f,$fffe ; Wait for X=79 | |
| 954 | + | dc.w $2c4f,$fffe | |
| 935 | 955 | dc.w COLOR00 | |
| 936 | 956 | home_colour_0: | |
| 937 | 957 | dc.w COLOUR_HOME_LIT | |
| 938 | 958 | | |
| 939 | - | dc.w $2c5f,$fffe ; Wait for X=95 | |
| 940 | - | dc.w COLOR00,COLOUR_HOME ; Gap | |
| 959 | + | dc.w $2c5f,$fffe | |
| 960 | + | dc.w COLOR00,COLOUR_HOME | |
| 941 | 961 | | |
| 942 | - | ; Home 1 (columns 6-7) | |
| 943 | - | dc.w $2c8f,$fffe ; Wait for X=143 | |
| 962 | + | dc.w $2c8f,$fffe | |
| 944 | 963 | dc.w COLOR00 | |
| 945 | 964 | home_colour_1: | |
| 946 | 965 | dc.w COLOUR_HOME_LIT | |
| 947 | 966 | | |
| 948 | - | dc.w $2c9f,$fffe ; Wait for X=159 | |
| 949 | - | dc.w COLOR00,COLOUR_HOME ; Gap | |
| 967 | + | dc.w $2c9f,$fffe | |
| 968 | + | dc.w COLOR00,COLOUR_HOME | |
| 950 | 969 | | |
| 951 | - | ; Home 2 (columns 10-11) | |
| 952 | - | dc.w $2ccf,$fffe ; Wait for X=207 | |
| 970 | + | dc.w $2ccf,$fffe | |
| 953 | 971 | dc.w COLOR00 | |
| 954 | 972 | home_colour_2: | |
| 955 | 973 | dc.w COLOUR_HOME_LIT | |
| 956 | 974 | | |
| 957 | - | dc.w $2cdf,$fffe ; Wait for X=223 | |
| 958 | - | dc.w COLOR00,COLOUR_HOME ; Gap | |
| 975 | + | dc.w $2cdf,$fffe | |
| 976 | + | dc.w COLOR00,COLOUR_HOME | |
| 959 | 977 | | |
| 960 | - | ; Home 3 (columns 14-15) - past X=223, need high bit | |
| 961 | - | dc.w $2d07,$fffe ; Wait for next pixel region | |
| 978 | + | dc.w $2d07,$fffe | |
| 962 | 979 | dc.w COLOR00 | |
| 963 | 980 | home_colour_3: | |
| 964 | 981 | dc.w COLOUR_HOME_LIT | |
| 965 | 982 | | |
| 966 | 983 | dc.w $2d17,$fffe | |
| 967 | - | dc.w COLOR00,COLOUR_HOME ; Gap | |
| 984 | + | dc.w COLOR00,COLOUR_HOME | |
| 968 | 985 | | |
| 969 | - | ; Home 4 (columns 18-19) | |
| 970 | 986 | dc.w $2d47,$fffe | |
| 971 | 987 | dc.w COLOR00 | |
| 972 | 988 | home_colour_4: | |
| 973 | 989 | dc.w COLOUR_HOME_LIT | |
| 974 | 990 | | |
| 975 | 991 | dc.w $2d57,$fffe | |
| 976 | - | dc.w COLOR00,COLOUR_HOME ; End | |
| 992 | + | dc.w COLOR00,COLOUR_HOME | |
| 977 | 993 | | |
| 978 | - | ; Rest of home row - solid colour | |
| 979 | 994 | dc.w $3407,$fffe | |
| 980 | 995 | dc.w COLOR00,COLOUR_HOME_LIT | |
| 981 | 996 | dc.w $3807,$fffe | |
| 982 | 997 | dc.w COLOR00,COLOUR_HOME | |
| 983 | 998 | | |
| 984 | - | ; Water rows | |
| 985 | 999 | dc.w $3c07,$fffe | |
| 986 | 1000 | dc.w COLOR00,COLOUR_WATER1 | |
| 987 | 1001 | dc.w $4c07,$fffe | |
| ... | |||
| 993 | 1007 | dc.w $7c07,$fffe | |
| 994 | 1008 | dc.w COLOR00,COLOUR_WATER1 | |
| 995 | 1009 | | |
| 996 | - | ; Median | |
| 997 | 1010 | dc.w $8c07,$fffe | |
| 998 | 1011 | dc.w COLOR00,COLOUR_MEDIAN | |
| 999 | 1012 | | |
| 1000 | - | ; Road rows | |
| 1001 | 1013 | dc.w $9c07,$fffe | |
| 1002 | 1014 | dc.w COLOR00,COLOUR_ROAD1 | |
| 1003 | 1015 | dc.w $ac07,$fffe | |
| ... | |||
| 1009 | 1021 | dc.w $dc07,$fffe | |
| 1010 | 1022 | dc.w COLOR00,COLOUR_ROAD1 | |
| 1011 | 1023 | | |
| 1012 | - | ; Start zone | |
| 1013 | 1024 | dc.w $ec07,$fffe | |
| 1014 | 1025 | dc.w COLOR00,COLOUR_START | |
| 1015 | 1026 | dc.w $f407,$fffe | |
| ... | |||
| 1064 | 1075 | | |
| 1065 | 1076 | dc.w $0000,$0000 | |
| 1066 | 1077 | | |
| 1067 | - | ; Life icon sprites (small frog shape, 8 pixels tall) | |
| 1068 | 1078 | even | |
| 1069 | 1079 | life_icon_1: | |
| 1070 | 1080 | dc.w $0000,$0000 | |
| ... | |||
| 1103 | 1113 | dc.w $1800,$0000 | |
| 1104 | 1114 | dc.w $0000,$0000 | |
| 1105 | 1115 | dc.w $0000,$0000 | |
| 1116 | + | | |
| 1117 | + | ;============================================================================== | |
| 1118 | + | ; SOUND DATA (8-bit signed samples in Chip RAM) | |
| 1119 | + | ;============================================================================== | |
| 1120 | + | | |
| 1121 | + | even | |
| 1122 | + | | |
| 1123 | + | ; Hop sound - short square wave burst (32 bytes = 16 words) | |
| 1124 | + | sound_hop: | |
| 1125 | + | dc.b $7f,$7f,$7f,$7f,$7f,$7f,$7f,$7f ; High | |
| 1126 | + | dc.b $80,$80,$80,$80,$80,$80,$80,$80 ; Low | |
| 1127 | + | dc.b $7f,$7f,$7f,$7f,$7f,$7f,$7f,$7f ; High | |
| 1128 | + | dc.b $80,$80,$80,$80,$80,$80,$80,$80 ; Low | |
| 1129 | + | HOP_LEN equ *-sound_hop | |
| 1130 | + | | |
| 1131 | + | ; Death sound - descending tone (64 bytes = 32 words) | |
| 1132 | + | sound_death: | |
| 1133 | + | dc.b $7f,$7f,$7f,$7f,$80,$80,$80,$80 ; Fast cycle | |
| 1134 | + | dc.b $7f,$7f,$7f,$7f,$80,$80,$80,$80 | |
| 1135 | + | dc.b $7f,$7f,$7f,$7f,$7f,$80,$80,$80,$80,$80 ; Slower | |
| 1136 | + | dc.b $7f,$7f,$7f,$7f,$7f,$7f,$80,$80,$80,$80,$80,$80 ; Slower still | |
| 1137 | + | dc.b $7f,$7f,$7f,$7f,$7f,$7f,$7f,$7f ; Even slower | |
| 1138 | + | dc.b $80,$80,$80,$80,$80,$80,$80,$80 | |
| 1139 | + | DEATH_LEN equ *-sound_death | |
| 1140 | + | | |
| 1141 | + | ; Home sound - bright chirp (48 bytes = 24 words) | |
| 1142 | + | sound_home: | |
| 1143 | + | dc.b $7f,$80,$7f,$80,$7f,$80,$7f,$80 ; Fast high | |
| 1144 | + | dc.b $7f,$80,$7f,$80,$7f,$80,$7f,$80 | |
| 1145 | + | dc.b $7f,$7f,$80,$80,$7f,$7f,$80,$80 ; Medium | |
| 1146 | + | dc.b $7f,$7f,$80,$80,$7f,$7f,$80,$80 | |
| 1147 | + | dc.b $7f,$7f,$7f,$80,$80,$80,$7f,$7f,$7f,$80,$80,$80 ; Lower finish | |
| 1148 | + | dc.b $00,$00,$00,$00 ; Silence | |
| 1149 | + | HOME_LEN equ *-sound_home | |
| 1106 | 1150 | | |
| 1107 | 1151 | ;============================================================================== | |
| 1108 | 1152 | ; SCREEN BUFFER |