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

Sound Effects

Paula plays samples. Hop sound. Death sound. Home sound. Audio presence.

17% of Signal

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

Unit 11 Screenshot

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:

  1. At startup, include audio DMA in the initial DMACON setup:
move.w  #$87e1,DMACON(a5)   ; Enable Copper, Blitter, Sprites, AUD0
  1. 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

Unit 10 → Unit 11
+142-98
11 ;==============================================================================
22 ; SIGNAL - A Frogger-style game for the Commodore Amiga
3-; Unit 10: Home Zones
3+; Unit 11: Sound Effects
44 ;
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.
88 ;==============================================================================
99
1010 ;==============================================================================
...
5050
5151 ; Lives system
5252 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
5853
5954 ; Home zones
6055 NUM_HOMES equ 5
61-HOME_ROW equ 0 ; Top row of grid
56+HOME_ROW equ 0
6257 POINTS_PER_HOME equ 50
6358
64-; Home X positions (grid coordinates): 2, 6, 10, 14, 18
65-; Each home is 2 cells wide (even column = start of home)
6659 HOME_0_X equ 2
6760 HOME_1_X equ 6
6861 HOME_2_X equ 10
6962 HOME_3_X equ 14
7063 HOME_4_X equ 18
64+
65+; Audio constants
66+SND_HOP equ 0
67+SND_DEATH equ 1
68+SND_HOME equ 2
7169
7270 ; Colours
7371 COLOUR_BLACK equ $0000
7472 COLOUR_HOME equ $0282
7573 COLOUR_HOME_LIT equ $03a3
76-COLOUR_HOME_FILLED equ $0f80 ; Orange for filled homes
74+COLOUR_HOME_FILLED equ $0f80
7775 COLOUR_WATER1 equ $0148
7876 COLOUR_WATER2 equ $026a
7977 COLOUR_MEDIAN equ $0383
...
8583 COLOUR_EYES equ $0ff0
8684 COLOUR_OUTLINE equ $0000
8785 COLOUR_CAR equ $0f00
88-COLOUR_LIFE equ $00f0
8986
9087 ;==============================================================================
9188 ; HARDWARE REGISTERS
...
123120 SPR1PTL equ $126
124121 SPR2PTH equ $128
125122 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)
126129
127130 ;==============================================================================
128131 ; CODE SECTION
...
162165 lea sprptl_val,a1
163166 move.w d0,(a1)
164167
165- ; Set up life icon sprites (sprites 1, 2, 3)
168+ ; Set up life icon sprites
166169 bsr setup_life_sprites
167170
168171 bsr update_sprite
...
173176 move.l a0,COP1LC(a5)
174177 move.w d0,COPJMP1(a5)
175178
176- ; Enable DMA
177- move.w #$87e0,DMACON(a5)
179+ ; Enable DMA (including audio channel 0)
180+ move.w #$87e1,DMACON(a5) ; +AUD0
178181
179182 ;==============================================================================
180183 ; MAIN LOOP
...
206209 bra mainloop
207210
208211 .game_over_loop:
209- ; Game over - wait for fire button to restart
210212 btst #7,$bfe001
211213 bne.s mainloop
212214
...
216218 bsr clear_screen
217219
218220 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
219277
220278 ;==============================================================================
221279 ; GAME INITIALISATION
...
225283 move.w #START_LIVES,lives
226284 clr.l score
227285
228- ; Clear all homes
229286 lea home_filled,a0
230287 moveq #NUM_HOMES-1,d0
231288 .clear_homes:
...
240297 ; HOME ZONE DETECTION
241298 ;==============================================================================
242299
243-;------------------------------------------------------------------------------
244-; CHECK_HOME - Check if frog has reached a home zone
245-;------------------------------------------------------------------------------
246300 check_home:
247- ; Only check if at top row
248301 move.w frog_grid_y,d0
249302 tst.w d0
250303 bne .not_home
251304
252- ; At top row - determine which home
253305 move.w frog_grid_x,d0
254306
255- ; Check home 0 (X = 2-3)
256307 cmp.w #HOME_0_X,d0
257308 blt.s .not_home
258309 cmp.w #HOME_0_X+2,d0
259310 blt.s .home_0
260311
261- ; Check home 1 (X = 6-7)
262312 cmp.w #HOME_1_X,d0
263313 blt.s .not_home
264314 cmp.w #HOME_1_X+2,d0
265315 blt.s .home_1
266316
267- ; Check home 2 (X = 10-11)
268317 cmp.w #HOME_2_X,d0
269318 blt.s .not_home
270319 cmp.w #HOME_2_X+2,d0
271320 blt.s .home_2
272321
273- ; Check home 3 (X = 14-15)
274322 cmp.w #HOME_3_X,d0
275323 blt.s .not_home
276324 cmp.w #HOME_3_X+2,d0
277325 blt.s .home_3
278326
279- ; Check home 4 (X = 18-19)
280327 cmp.w #HOME_4_X,d0
281328 blt.s .not_home
282329 cmp.w #HOME_4_X+2,d0
283330 blt.s .home_4
284331
285- ; Between homes - death!
286332 bra .death_between
287333
288334 .home_0: moveq #0,d1
...
296342 .home_4: moveq #4,d1
297343
298344 .check_filled:
299- ; D1 = home index (0-4)
300345 lea home_filled,a0
301- add.w d1,d1 ; *2 for word offset
346+ add.w d1,d1
302347 tst.w 0(a0,d1.w)
303- bne.s .death_filled ; Already filled - death!
348+ bne.s .death_filled
304349
305- ; Home is empty - fill it!
306350 move.w #1,0(a0,d1.w)
307-
308- ; Add points
309351 add.l #POINTS_PER_HOME,score
310352
311- ; Update display
312- bsr update_home_display
353+ ; Play success sound
354+ move.w #SND_HOME,d0
355+ bsr play_sound
313356
314- ; Check if all homes filled
357+ bsr update_home_display
315358 bsr check_round_complete
316-
317- ; Respawn frog
318359 bsr reset_frog
319360 rts
320361
321362 .death_filled:
322- ; Landed on filled home - death
323- bsr trigger_death
324- rts
325-
326363 .death_between:
327- ; Landed between homes (on lily pad gaps) - death
328364 bsr trigger_death
329365 rts
330366
331367 .not_home:
332368 rts
333369
334-;------------------------------------------------------------------------------
335-; CHECK_ROUND_COMPLETE - Check if all 5 homes are filled
336-;------------------------------------------------------------------------------
337370 check_round_complete:
338371 lea home_filled,a0
339372 moveq #NUM_HOMES-1,d0
340- moveq #0,d1 ; Count filled homes
373+ moveq #0,d1
341374
342375 .loop:
343376 tst.w (a0)+
...
345378 addq.w #1,d1
346379 dbf d0,.loop
347380
348- ; All homes filled - reset for next round
349381 cmp.w #NUM_HOMES,d1
350382 bne.s .not_complete
351383
352- ; Clear all homes for next round
353384 lea home_filled,a0
354385 moveq #NUM_HOMES-1,d0
355386 .clear:
...
357388 dbf d0,.clear
358389
359390 bsr update_home_display
360- ; Could add bonus points here, increase speed, etc.
361391
362392 .not_complete:
363393 rts
364394
365-;------------------------------------------------------------------------------
366-; UPDATE_HOME_DISPLAY - Update Copper colours to show filled homes
367-;------------------------------------------------------------------------------
368395 update_home_display:
369396 lea home_filled,a0
370397 lea home_colour_0,a1
371398
372- ; Home 0
373399 tst.w (a0)+
374400 beq.s .home0_empty
375401 move.w #COLOUR_HOME_FILLED,(a1)
...
419445 ;==============================================================================
420446
421447 setup_life_sprites:
422- ; Sprite 1
423448 lea life_icon_1,a0
424449 move.l a0,d0
425450 swap d0
...
429454 lea spr1ptl_val,a1
430455 move.w d0,(a1)
431456
432- ; Sprite 2
433457 lea life_icon_2,a0
434458 move.l a0,d0
435459 swap d0
...
439463 lea spr2ptl_val,a1
440464 move.w d0,(a1)
441465
442- ; Sprite 3
443466 lea life_icon_3,a0
444467 move.l a0,d0
445468 swap d0
...
452475 rts
453476
454477 update_life_display:
455- ; Life 1 (always show if lives >= 1)
456478 lea life_icon_1,a0
457479 move.w lives,d0
458480 cmp.w #1,d0
...
531553 move.w #DEATH_FRAMES,death_timer
532554 move.w #FLASH_COLOUR,flash_colour
533555
534- ; Decrement lives
556+ ; Play death sound
557+ move.w #SND_DEATH,d0
558+ bsr play_sound
559+
535560 subq.w #1,lives
536561 bsr update_life_display
537562
...
644669 subq.w #1,death_timer
645670 bgt.s .still_dying
646671
647- ; Death complete - check for game over
648672 tst.w lives
649673 beq.s .game_over
650674
651- ; Still have lives - respawn
652675 bsr reset_frog
653676 bra .done
654677
...
705728 .start_hop:
706729 move.w #STATE_HOPPING,frog_state
707730 clr.w frog_anim_frame
731+
732+ ; Play hop sound
733+ move.w #SND_HOP,d0
734+ bsr play_sound
735+
708736 bra.s .done
709737
710738 .hopping:
...
858886 lives: dc.w 3
859887 score: dc.l 0
860888
861-; Home zone state (0 = empty, 1 = filled)
862889 home_filled: dc.w 0,0,0,0,0
863890
864891 car_data:
...
896923 dc.w $0180,$0000
897924 dc.w $0182,COLOUR_CAR
898925
899- ; Sprite 0-1 palette (frog and life icons share this)
900926 dc.w $01a2,COLOUR_FROG
901927 dc.w $01a4,COLOUR_EYES
902928 dc.w $01a6,COLOUR_OUTLINE
903929
904- ; Sprite 0 pointer (main frog)
905930 dc.w SPR0PTH
906931 sprpth_val: dc.w $0000
907932 dc.w SPR0PTL
908933 sprptl_val: dc.w $0000
909934
910- ; Sprite 1 pointer (life icon 1)
911935 dc.w SPR1PTH
912936 spr1pth_val: dc.w $0000
913937 dc.w SPR1PTL
914938 spr1ptl_val: dc.w $0000
915939
916- ; Sprite 2 pointer (life icon 2)
917940 dc.w SPR2PTH
918941 spr2pth_val: dc.w $0000
919942 dc.w SPR2PTL
920943 spr2ptl_val: dc.w $0000
921944
922- ; Sprite 3 pointer (life icon 3)
923- dc.w $0126 ; SPR3PTH
945+ dc.w $0126
924946 spr3pth_val: dc.w $0000
925- dc.w $012a ; SPR3PTL
947+ dc.w $012a
926948 spr3ptl_val: dc.w $0000
927949
928- ; Home zone row - 5 distinct zones for visual feedback
929- ; Each home changes colour when filled
950+ ; Home zone row
930951 dc.w $2c07,$fffe
931- dc.w COLOR00,COLOUR_HOME ; Gaps between homes
952+ dc.w COLOR00,COLOUR_HOME
932953
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
935955 dc.w COLOR00
936956 home_colour_0:
937957 dc.w COLOUR_HOME_LIT
938958
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
941961
942- ; Home 1 (columns 6-7)
943- dc.w $2c8f,$fffe ; Wait for X=143
962+ dc.w $2c8f,$fffe
944963 dc.w COLOR00
945964 home_colour_1:
946965 dc.w COLOUR_HOME_LIT
947966
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
950969
951- ; Home 2 (columns 10-11)
952- dc.w $2ccf,$fffe ; Wait for X=207
970+ dc.w $2ccf,$fffe
953971 dc.w COLOR00
954972 home_colour_2:
955973 dc.w COLOUR_HOME_LIT
956974
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
959977
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
962979 dc.w COLOR00
963980 home_colour_3:
964981 dc.w COLOUR_HOME_LIT
965982
966983 dc.w $2d17,$fffe
967- dc.w COLOR00,COLOUR_HOME ; Gap
984+ dc.w COLOR00,COLOUR_HOME
968985
969- ; Home 4 (columns 18-19)
970986 dc.w $2d47,$fffe
971987 dc.w COLOR00
972988 home_colour_4:
973989 dc.w COLOUR_HOME_LIT
974990
975991 dc.w $2d57,$fffe
976- dc.w COLOR00,COLOUR_HOME ; End
992+ dc.w COLOR00,COLOUR_HOME
977993
978- ; Rest of home row - solid colour
979994 dc.w $3407,$fffe
980995 dc.w COLOR00,COLOUR_HOME_LIT
981996 dc.w $3807,$fffe
982997 dc.w COLOR00,COLOUR_HOME
983998
984- ; Water rows
985999 dc.w $3c07,$fffe
9861000 dc.w COLOR00,COLOUR_WATER1
9871001 dc.w $4c07,$fffe
...
9931007 dc.w $7c07,$fffe
9941008 dc.w COLOR00,COLOUR_WATER1
9951009
996- ; Median
9971010 dc.w $8c07,$fffe
9981011 dc.w COLOR00,COLOUR_MEDIAN
9991012
1000- ; Road rows
10011013 dc.w $9c07,$fffe
10021014 dc.w COLOR00,COLOUR_ROAD1
10031015 dc.w $ac07,$fffe
...
10091021 dc.w $dc07,$fffe
10101022 dc.w COLOR00,COLOUR_ROAD1
10111023
1012- ; Start zone
10131024 dc.w $ec07,$fffe
10141025 dc.w COLOR00,COLOUR_START
10151026 dc.w $f407,$fffe
...
10641075
10651076 dc.w $0000,$0000
10661077
1067-; Life icon sprites (small frog shape, 8 pixels tall)
10681078 even
10691079 life_icon_1:
10701080 dc.w $0000,$0000
...
11031113 dc.w $1800,$0000
11041114 dc.w $0000,$0000
11051115 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
11061150
11071151 ;==============================================================================
11081152 ; SCREEN BUFFER