diff --git a/credits.asm b/credits.asm index 111dcd4..cfd118b 100644 --- a/credits.asm +++ b/credits.asm @@ -59,10 +59,16 @@ ci_col: lda #$ff sta SPR_EN + jsr music_init ; start the credits music + lda #0 + sta sfx_timer ; prevent SFX driver from touching voice 1 + +setupd credits_update rts credits_update: + jsr music_play ; advance the music one tick + ; ---- all the heavy drawing runs first, while the beam ; is still far above the scroller row ---- @@ -265,6 +271,8 @@ cu_input: beq cu_stay lda #$c8 ; leave with the scroll register clean sta XCTRL + jsr music_stop ; silence all SID voices + jsr sfx_init ; restore SFX driver volume jmp menu_init ; back to the menu cu_stay: diff --git a/defs.asm b/defs.asm index 0294439..e70dcf9 100644 --- a/defs.asm +++ b/defs.asm @@ -26,7 +26,8 @@ SPR_XEX = $d01d ; horizontal expand SPR_COL = $d027 ; sprite 0 color, +1 = sprite 1, ... -; ---- SID registers (voice 1 used for SFX) ---- +; ---- SID registers ---- +SID_BASE = $d400 SID_V1FL = $d400 ; voice 1 frequency low SID_V1FH = $d401 ; voice 1 frequency high SID_V1PL = $d402 ; voice 1 pulse width low @@ -34,6 +35,20 @@ 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_V2FL = $d407 ; voice 2 frequency low +SID_V2FH = $d408 ; voice 2 frequency high +SID_V2PL = $d409 ; voice 2 pulse width low +SID_V2PH = $d40a ; voice 2 pulse width high +SID_V2CR = $d40b ; voice 2 control +SID_V2AD = $d40c ; voice 2 attack/decay +SID_V2SR = $d40d ; voice 2 sustain/release +SID_V3FL = $d40e ; voice 3 frequency low +SID_V3FH = $d40f ; voice 3 frequency high +SID_V3PL = $d410 ; voice 3 pulse width low +SID_V3PH = $d411 ; voice 3 pulse width high +SID_V3CR = $d412 ; voice 3 control +SID_V3AD = $d413 ; voice 3 attack/decay +SID_V3SR = $d414 ; voice 3 sustain/release SID_VOL = $d418 ; filter mode + master volume ; ---- SFX IDs ---- diff --git a/main.asm b/main.asm index 3feedf5..0d9c027 100644 --- a/main.asm +++ b/main.asm @@ -124,4 +124,5 @@ wf_hit: !src "over.asm" !src "common.asm" !src "sound.asm" +!src "music.asm" !src "tables.asm" diff --git a/music.asm b/music.asm new file mode 100644 index 0000000..212fdfa --- /dev/null +++ b/music.asm @@ -0,0 +1,223 @@ +; ----------------------------------------------------------- +; music.asm - SID music driver (adapted from the c64-demo) +; voice 1: bass (pulse), voice 2: drums (noise), +; voice 3: echo lead (triangle) +; music_init: clear SID, set instruments, restart song +; music_play: call once per frame +; music_stop: silence everything +; ----------------------------------------------------------- + +; ---- waveform control values ---- +BASS_ON = $41 ; pulse + gate +BASS_OFF = $40 +DRUM_ON = $81 ; noise + gate +DRUM_OFF = $80 +LEAD_ON = $11 ; triangle + gate +LEAD_OFF = $10 + +SONG_LEN = 128 + +; ---- initialize SID and restart the song ---- +music_init: + ldx #24 + lda #0 +mi_clr: + sta SID_BASE,x + dex + bpl mi_clr + + lda #$00 ; bass: instant attack, full sustain + sta SID_V1AD + lda #$f3 + sta SID_V1SR + lda #$08 ; bass pulse width 50% + sta SID_V1PH + lda #$00 + sta SID_V1PL + lda #$04 ; drums: instant attack, ~110 ms decay + sta SID_V2AD + lda #$00 + sta SID_V2SR + lda #$06 ; lead: pluck, ~200 ms decay + sta SID_V3AD + lda #$00 + sta SID_V3SR + + lda #$0f ; master volume = 15 + sta SID_VOL + + lda #0 + sta mus_row + sta mus_spd_ix + sta mus_bcut + lda #1 + sta mus_tick + rts + +; ---- play one tick: called once per frame ---- +music_play: + pha + txa + pha + tya + pha + jsr mp_core + pla + tay + pla + tax + pla + rts + +mp_core: + lda mus_bcut + beq mp_tick + dec mus_bcut + bne mp_tick + lda #BASS_OFF + sta SID_V1CR +mp_tick: + dec mus_tick + beq mp_row + rts + +mp_row: + ldx mus_spd_ix + lda mus_spd_tbl,x + sta mus_tick + inx + cpx #6 + bne mp_spd_ok + ldx #0 +mp_spd_ok: + stx mus_spd_ix + + ldx mus_row + + ; ---- voice 1: bass ---- + lda bass_pat,x + beq mp_drums + cmp #1 + bne mp_bass_note + lda #BASS_OFF + sta SID_V1CR + jmp mp_drums +mp_bass_note: + and #$7f + tay + lda bass_flo,y + sta SID_V1FL + lda bass_fhi,y + sta SID_V1FH + lda #BASS_OFF + sta SID_V1CR + lda #BASS_ON + sta SID_V1CR + + ; ---- voice 2: drums ---- +mp_drums: + lda drum_pat,x + beq mp_lead + cmp #1 + bne mp_drum_note + lda #DRUM_OFF + sta SID_V2CR + jmp mp_lead +mp_drum_note: + and #$7f + tay + lda drum_flo,y + sta SID_V2FL + lda drum_fhi,y + sta SID_V2FH + lda #DRUM_OFF + sta SID_V2CR + lda #DRUM_ON + sta SID_V2CR + + ; ---- voice 3: lead echo ---- +mp_lead: + lda lead_pat,x + beq mp_next + and #$7f + tay + lda lead_flo,y + sta SID_V3FL + lda lead_fhi,y + sta SID_V3FH + lda #LEAD_OFF + sta SID_V3CR + lda #LEAD_ON + sta SID_V3CR + +mp_next: + inx + cpx #SONG_LEN + bne mp_store + ldx #0 + stx mus_spd_ix +mp_store: + stx mus_row + lda bass_pat,x + bpl mp_nocut + lda mus_tick + sec + sbc #2 + sta mus_bcut +mp_nocut: + rts + +; ---- stop: silence SID, restore for SFX use ---- +music_stop: + ldx #24 + lda #0 +ms_clr: + sta SID_BASE,x + dex + bpl ms_clr + rts + +; ---- state ---- +mus_row: !byte 0 +mus_tick: !byte 1 +mus_spd_ix: !byte 0 +mus_bcut: !byte 0 + +mus_spd_tbl: !byte 4,4,4,4,4,4 + +; ---- note tables (PAL SID) ---- +bass_flo: !byte $5a,$e0,$2d,$cf +bass_fhi: !byte $04,$03,$05,$05 +drum_flo: !byte $9c,$ff +drum_fhi: !byte $45,$ff +lead_flo: !byte $ce,$02,$64,$76,$9c,$04,$c8,$eb +lead_fhi: !byte $22,$1f,$29,$2e,$45,$3e,$52,$5c + +; ---- pattern data ---- +bass_pat: + !byte $80,$00,$00,$00,$01,$00,$80,$00,$01,$00,$00,$00,$81,$00,$80,$00 + !byte $01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + !byte $80,$00,$00,$00,$01,$00,$80,$00,$01,$00,$00,$00,$81,$00,$80,$00 + !byte $01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + !byte $80,$00,$00,$00,$01,$00,$80,$00,$01,$00,$00,$00,$81,$00,$80,$00 + !byte $01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + !byte $82,$00,$82,$00,$01,$00,$82,$00,$01,$00,$00,$00,$82,$00,$83,$00 + !byte $01,$00,$83,$00,$01,$00,$83,$00,$83,$00,$01,$00,$83,$00,$01,$00 +drum_pat: + !byte $80,$00,$00,$00,$00,$00,$00,$00,$81,$00,$00,$00,$00,$00,$00,$00 + !byte $80,$00,$00,$00,$00,$00,$80,$00,$81,$00,$00,$00,$00,$00,$00,$00 + !byte $80,$00,$00,$00,$00,$00,$00,$00,$81,$00,$00,$00,$00,$00,$00,$00 + !byte $80,$00,$00,$00,$00,$00,$80,$00,$81,$00,$00,$00,$00,$00,$00,$00 + !byte $80,$00,$00,$00,$00,$00,$00,$00,$81,$00,$00,$00,$00,$00,$00,$00 + !byte $80,$00,$00,$00,$00,$00,$80,$00,$81,$00,$00,$00,$00,$00,$00,$00 + !byte $80,$00,$00,$00,$00,$00,$00,$00,$81,$00,$00,$00,$00,$00,$00,$00 + !byte $80,$00,$00,$00,$00,$00,$80,$00,$81,$00,$00,$00,$00,$00,$00,$00 +lead_pat: + !byte $00,$00,$80,$00,$00,$00,$00,$00,$84,$00,$00,$00,$00,$00,$81,$00 + !byte $80,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + !byte $00,$00,$84,$00,$00,$00,$00,$00,$80,$00,$00,$00,$00,$00,$81,$00 + !byte $80,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + !byte $00,$00,$84,$00,$00,$00,$00,$00,$80,$00,$00,$00,$00,$00,$81,$00 + !byte $80,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + !byte $00,$00,$82,$00,$86,$00,$00,$00,$82,$00,$00,$00,$00,$00,$86,$00 + !byte $83,$00,$00,$00,$87,$00,$00,$00,$83,$00,$87,$00,$00,$00,$83,$00