AUDF0 = $d200 AUDC0 = $d201 AUDCTL = $d208 .data Chan0dur: .res 4 ; current note duration channel 0 Chan0note: .res 4 ; current note pitch channel 0 Chan0duty: .res 4 ; current duty bits channel 0 DurationTimer: .res 1 ; duration until next cmd CurChannel: .res 1 ; next channel to add note Volume: .res 1 ; initial volume of note (0-31) ; set_irq() saves/restores entire zeropage segment ; which we don't want! ; so we have to set this with an equate SongPtr = $fe ;.segment "EXTZP" ;SongPtr: .res 2 ; ptr to next song byte .code ; Update channel pitch in AUDF0 ; 8-bit rotation of duty cycle bits .export _music_duty _music_duty: ldx #3 ldy #0 @loop: lda Chan0dur,x beq :++ lda Chan0duty,x asl bcc :+ ora #1 : sta Chan0duty,x lda Chan0note,x beq :+ ; If next bit is set, add 1 to AUDF0 adc #0 sta AUDF0,y : iny iny dex bpl @loop rts ; Decrement the volumes for each channel .export _music_tick _music_tick: ldx #3 ldy #0 @loop1: lda Chan0dur,x beq :+ lsr ora #$a0 sta AUDC0,y dec Chan0dur,x : iny iny dex bpl @loop1 ; Also decrement next-note timer, fetch next note lda DurationTimer bmi @Done beq @NextData dec DurationTimer rts ; Timer ran out, so fetch next note @NextData: ldx #0 lda (SongPtr,x) bmi @LoadDuration ; < $80, play next note ldx CurChannel ; next channel tay jsr music_do_note inx txa and #3 sta CurChannel ; inc next channel jsr @IncDataPtr jmp @NextData ; >= $80, load next duration @LoadDuration: cmp #$ff ; $ff = end of song bne @NoResetTrack sta DurationTimer jmp music_done @NoResetTrack: and #$7f ; asl sta DurationTimer ; store duration * 2 @IncDataPtr: ; increment song pointer inc SongPtr bne @Done inc SongPtr+1 @Done: rts music_do_note: ; Play a note ; X = channel (0,1) ; Y = note index (0-63) lda FREQZ,y sta Chan0note,x lda DUTYZ,y sta Chan0duty,x lda Volume sta Chan0dur,x rts music_done: rts .export _music_start _music_start: sta SongPtr stx SongPtr+1 lda #24 sta Volume lda #0 sta DurationTimer sta CurChannel ldx #3 : sta Chan0dur,x dex bpl :- lda #$01 sta AUDCTL rts .export _music_get_ptr _music_get_ptr: lda SongPtr ldx SongPtr+1 rts .export _music_is_done _music_is_done: ldx #0 lda (SongPtr,x) cmp #$ff php pla lsr and #1 rts ; Table of AUDF base values for each note FREQZ: .byte 254, 254, 254, 255, 240, 227, 214, 202, 190, 180, 169, 160, 151, 142, 134, 127, 119, 113, 106, 100, 94, 89, 84, 79, 75, 70, 66, 63, 59, 56, 52, 49, 47, 44, 41, 39, 37, 34, 32, 31, 29, 27, 25, 24, 23, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 11, 10, 9, 9, 8, 8, 7, 7 ; Table of duty-cycle bits for each note DUTYZ: .byte 0, 0, 0, 0, 181, 1, 17, 1, 219, 0, 239, 17, 17, 181, 181, 0, 239, 0, 181, 181, 239, 85, 73, 181, 1, 239, 219, 0, 73, 0, 239, 219, 0, 17, 219, 73, 0, 239, 239, 0, 17, 85, 239, 73, 0, 181, 73, 1, 0, 0, 0, 0, 1, 17, 85, 219, 0, 73, 181, 1, 85, 0, 85, 0