From f2bf6ba6ade5ae8898ab86ccf0da79b96e7e97ba Mon Sep 17 00:00:00 2001 From: Zsolt Tasnadi Date: Thu, 16 Jul 2026 10:42:57 +0200 Subject: [PATCH] sounds, menu tweaks --- defs.asm | 18 ++++++++ main.asm | 3 ++ menu.asm | 21 +++++---- play.asm | 6 +++ sound.asm | 135 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 174 insertions(+), 9 deletions(-) create mode 100644 sound.asm diff --git a/defs.asm b/defs.asm index 600cdb1..0294439 100644 --- a/defs.asm +++ b/defs.asm @@ -26,6 +26,24 @@ SPR_XEX = $d01d ; horizontal expand SPR_COL = $d027 ; sprite 0 color, +1 = sprite 1, ... +; ---- SID registers (voice 1 used for SFX) ---- +SID_V1FL = $d400 ; voice 1 frequency low +SID_V1FH = $d401 ; voice 1 frequency high +SID_V1PL = $d402 ; voice 1 pulse width low +SID_V1PH = $d403 ; voice 1 pulse width high +SID_V1CR = $d404 ; voice 1 control (waveform + gate) +SID_V1AD = $d405 ; voice 1 attack/decay +SID_V1SR = $d406 ; voice 1 sustain/release +SID_VOL = $d418 ; filter mode + master volume + +; ---- SFX IDs ---- +SFX_SCORE = 0 ; rabbit dodged: high pling +SFX_MISS = 1 ; rabbit hit: noise crash +SFX_OVER = 2 ; game over: descending sweep +SFX_CURSOR = 3 ; menu cursor move: click +SFX_SELECT = 4 ; menu fire: rising tone +NSFX = 5 + ; ---- colors ---- BLACK = 0 WHITE = 1 diff --git a/main.asm b/main.asm index c038d9a..3feedf5 100644 --- a/main.asm +++ b/main.asm @@ -61,6 +61,7 @@ sprcol: sta SPR_YEX sta framectr sta fheld + jsr sfx_init !ifdef AUTOPLAY { ; test builds boot straight into the game jsr play_init @@ -78,6 +79,7 @@ main: sta BG inc framectr jsr rand8 ; stir the RNG once per frame + jsr sfx_update ; advance sound effects jsr call_update ; finishes long before the horizon line wait_grass: lda RASTER ; split the background at the horizon: @@ -121,4 +123,5 @@ wf_hit: !src "play.asm" !src "over.asm" !src "common.asm" +!src "sound.asm" !src "tables.asm" diff --git a/menu.asm b/menu.asm index 319a5b9..28c90ee 100644 --- a/menu.asm +++ b/menu.asm @@ -79,12 +79,16 @@ menu_update: lda menusel beq mu_udend dec menusel + lda #SFX_CURSOR + jsr sfx_play jmp mu_udend mu_down: lda menusel cmp #MENU_ITEMS-1 beq mu_udend inc menusel + lda #SFX_CURSOR + jsr sfx_play jmp mu_udend mu_udrel: lda #0 @@ -155,15 +159,9 @@ mu_keyup: sta k12held mu_keydone: - ; footer: the two control lines take turns - lda framectr - and #64 - beq mu_foot2 + ; footer: both control lines always visible +prtext 22, 9, LGREY, txt_info, 21 - jmp mu_footdone -mu_foot2: - +prtext 22, 9, LGREY, txt_info2, 21 -mu_footdone: + +prtext 23, 9, LGREY, txt_info2, 21 ; the menu items: the picked line is yellow ldx #0 @@ -206,6 +204,8 @@ mu_start: jsr start_edge beq mu_done menu_go: + lda #SFX_SELECT + jsr sfx_play lda menusel cmp #2 beq mg_credits @@ -238,8 +238,10 @@ draw_mitem: lda #YELLOW dm_store: sta pcolor + stx rtmp ; print_text trashes X jsr print_text - ldy #0 ; recolor the first letter (shortcut key) + ldx rtmp + ldy mitem_key,x ; recolor the shortcut letter lda #CYAN sta (collo),y rts @@ -248,6 +250,7 @@ dm_store: MENU_ITEMS = 4 mitem_lo: !byte txt_1p, >txt_2p, >txt_credits, >txt_story +mitem_key: !byte 0, 0, 0, 1 ; offset of the highlighted shortcut letter mitem_len: !byte 13, 11, 7, 5 mitem_row: !byte 14, 16, 18, 20 mcur_lo: ; cursor cells: column 12 of those rows diff --git a/play.asm b/play.asm index 90dfe1d..e6bf1b7 100644 --- a/play.asm +++ b/play.asm @@ -257,6 +257,8 @@ pu_rnext: lda miss cmp #MAX_MISS bcc pu_done + lda #SFX_OVER + jsr sfx_play jmp over_init ; its rts returns to the main loop pu_done: rts @@ -423,6 +425,8 @@ at_done: ; ---- score: bump the decimal digits and the difficulty byte ---- add_score: + lda #SFX_SCORE + jsr sfx_play lda score8 cmp #$ff beq as_digits @@ -461,6 +465,8 @@ draw_score: ; ---- miss: X mark on the HUD + blink the scooter ---- add_miss: + lda #SFX_MISS + jsr sfx_play inc miss ldy miss ; Y: the rabbit loop still needs X lda #24 ; 'x' diff --git a/sound.asm b/sound.asm new file mode 100644 index 0000000..fdcaf19 --- /dev/null +++ b/sound.asm @@ -0,0 +1,135 @@ +; ----------------------------------------------------------- +; sound.asm - minimal SID sound driver using voice 1 +; sfx_init - set master volume, silence voice 1 +; sfx_play - start effect A (SFX_SCORE, SFX_MISS, etc.) +; sfx_update - call once per frame to handle duration/slides +; ----------------------------------------------------------- + +; ---- one-time setup: master volume on, voice 1 silent ---- +sfx_init: + lda #$0f ; max volume, no filter + sta SID_VOL + lda #0 + sta SID_V1CR ; gate off, no waveform + sta sfx_timer + rts + +; ---- start playing effect A ---- +sfx_play: + tax + ; gate off first (retrigger envelope) + lda #0 + sta SID_V1CR + ; load parameters from the table + lda sfx_flo,x + sta SID_V1FL + lda sfx_fhi,x + sta SID_V1FH + lda sfx_ad,x + sta SID_V1AD + lda sfx_sr,x + sta SID_V1SR + lda #$00 ; pulse width mid-point for pulse wave + sta SID_V1PL + lda #$08 + sta SID_V1PH + lda sfx_wave,x ; waveform + gate bit + sta SID_V1CR + lda sfx_dur,x + sta sfx_timer + lda sfx_slide,x + sta sfx_sld + rts + +; ---- per-frame update: pitch slide + gate off when done ---- +sfx_update: + lda sfx_timer + beq sfu_done ; nothing playing + dec sfx_timer + bne sfu_slide + ; duration expired: gate off + lda #0 + sta SID_V1CR + rts +sfu_slide: + ; apply pitch slide (signed: positive = up, negative = down) + lda sfx_sld + beq sfu_done + bmi sfu_down + ; slide up + clc + adc SID_V1FH + sta SID_V1FH + rts +sfu_down: + ; slide down (sfx_sld is negative, e.g. $fe = -2) + clc + adc SID_V1FH ; adding a negative = subtracting + sta SID_V1FH + bcs sfu_done ; no underflow + lda #0 + sta SID_V1FH ; clamp at zero +sfu_done: + rts + +; ---- effect parameter tables (indexed by SFX ID) ---- + +; initial frequency low byte +sfx_flo: + !byte $00 ; SFX_SCORE: freq $1C00 + !byte $00 ; SFX_MISS: freq $0800 + !byte $00 ; SFX_OVER: freq $2000 + !byte $00 ; SFX_CURSOR: freq $0A00 + !byte $00 ; SFX_SELECT: freq $1000 + +; initial frequency high byte +sfx_fhi: + !byte $1c ; SFX_SCORE + !byte $08 ; SFX_MISS + !byte $20 ; SFX_OVER + !byte $0a ; SFX_CURSOR + !byte $10 ; SFX_SELECT + +; attack/decay ($XY: X=attack 0-F, Y=decay 0-F) +sfx_ad: + !byte $00 ; SFX_SCORE: instant attack, instant decay + !byte $00 ; SFX_MISS: instant attack, instant decay + !byte $09 ; SFX_OVER: instant attack, slow decay + !byte $00 ; SFX_CURSOR: instant attack, instant decay + !byte $00 ; SFX_SELECT: instant attack, instant decay + +; sustain/release ($XY: X=sustain 0-F, Y=release 0-F) +sfx_sr: + !byte $a8 ; SFX_SCORE: medium sustain, medium release + !byte $f5 ; SFX_MISS: full sustain, medium release + !byte $08 ; SFX_OVER: zero sustain, medium release + !byte $50 ; SFX_CURSOR: low sustain, instant release + !byte $98 ; SFX_SELECT: medium sustain, medium release + +; waveform + gate ($XY: bit7=noise, bit6=pulse, bit5=saw, bit4=tri, bit0=gate) +sfx_wave: + !byte $11 ; SFX_SCORE: triangle + gate + !byte $81 ; SFX_MISS: noise + gate + !byte $21 ; SFX_OVER: sawtooth + gate + !byte $41 ; SFX_CURSOR: pulse + gate + !byte $11 ; SFX_SELECT: triangle + gate + +; duration in frames +sfx_dur: + !byte 8 ; SFX_SCORE + !byte 10 ; SFX_MISS + !byte 50 ; SFX_OVER + !byte 3 ; SFX_CURSOR + !byte 8 ; SFX_SELECT + +; pitch slide per frame (signed: + = up, - = down, 0 = none) +sfx_slide: + !byte 0 ; SFX_SCORE: steady + !byte $fe ; SFX_MISS: slide down (-2) + !byte $fd ; SFX_OVER: slide down (-3) + !byte 0 ; SFX_CURSOR: steady + !byte 2 ; SFX_SELECT: slide up (+2) + +; ---- driver state ---- +sfx_timer: !byte 0 ; frames remaining for current effect +sfx_sld: !byte 0 ; current slide value