mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2024-11-25 18:33:11 +00:00
165 lines
3.3 KiB
Plaintext
165 lines
3.3 KiB
Plaintext
|
|
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
|
|
|