2 ; Sound driver for the Atari Lynx.
4 ; Karri Kaksonen and Bjoern Spruck, 11.12.2012
8 .include "zeropage.inc"
10 .export _lynx_snd_init
11 .export _lynx_snd_active
12 .export _lynx_snd_play
13 .export _lynx_snd_stop
14 .export _lynx_snd_pause
15 .export _lynx_snd_continue
16 .interruptor lynx_snd_handler
20 ;----------------------------------------------------------------------------
21 ; ZP variables that go into APPZP
24 .segment "APPZP" : zeropage
31 ;----------------------------------------------------------------------------
47 SndNotePlaying: .res 4
59 SndEnvVolParts: .res 4
60 SndEnvVolParts2: .res 4
65 SndEnvFrqParts: .res 4
66 SndEnvFrqParts2: .res 4
69 SndEnvWaveLoop: .res 4
70 SndEnvWaveParts: .res 4
71 SndEnvWaveParts2: .res 4
73 MAX_INSTRUMENTS .set 64
74 SndEnvVolPtrLo: .res MAX_INSTRUMENTS
75 SndEnvVolPtrHi: .res MAX_INSTRUMENTS
76 SndEnvFrqPtrLo: .res MAX_INSTRUMENTS
77 SndEnvFrqPtrHi: .res MAX_INSTRUMENTS
78 SndEnvWavePtrLo: .res MAX_INSTRUMENTS
79 SndEnvWavePtrHi: .res MAX_INSTRUMENTS
83 SndOffsets: .byte $00,$08,$10,$18
85 ;----------------------------------------------------------------------------
93 if_count .set if_count +1
94 nest_count .set nest_count +1
95 beq .ident (.sprintf ("else%04d", if_count))
96 .ident (.sprintf ("push%04d", nest_count)) .set if_count
100 if_count .set if_count +1
101 nest_count .set nest_count +1
102 bne .ident (.sprintf ("else%04d", if_count))
103 .ident (.sprintf ("push%04d", nest_count)) .set if_count
107 if_count .set if_count +1
108 nest_count .set nest_count +1
109 bpl .ident (.sprintf ("else%04d", if_count))
110 .ident (.sprintf ("push%04d", nest_count)) .set if_count
114 if_count .set if_count +1
115 nest_count .set nest_count +1
116 bmi .ident (.sprintf ("else%04d", if_count))
117 .ident (.sprintf ("push%04d", nest_count)) .set if_count
121 if_count .set if_count +1
122 nest_count .set nest_count +1
123 bcc .ident (.sprintf ("else%04d", if_count))
124 .ident (.sprintf ("push%04d", nest_count)) .set if_count
128 if_count .set if_count +1
129 nest_count .set nest_count +1
130 bcc .ident (.sprintf ("else%04d", if_count))
131 .ident (.sprintf ("push%04d", nest_count)) .set if_count
135 if_count .set if_count +1
136 nest_count .set nest_count +1
137 bcs .ident (.sprintf ("else%04d", if_count))
138 .ident (.sprintf ("push%04d", nest_count)) .set if_count
142 bra .ident (.sprintf ("endif%04d", .ident (.sprintf ("push%04d", nest_count))))
143 .ident (.sprintf ("else%04d", .ident (.sprintf ("push%04d", nest_count)))) := *
147 .if .not .defined( .ident (.sprintf ("else%04d", .ident (.sprintf ("push%04d", nest_count)))))
148 .ident (.sprintf ("else%04d", .ident (.sprintf ("push%04d", nest_count)))) := *
150 .ident (.sprintf ("endif%04d", .ident (.sprintf ("push%04d", nest_count)))) := *
151 nest_count .set nest_count -1
156 ;----------------------------------------------------------------------------
157 ; void lynx_snd_init() will initialize the sound engine.
165 lda #%10011000|_31250Hz
168 sta STIMBKUP ; set up a 240Hz IRQ
175 stz $fd44 ; all channels full volume / no attenuation
187 init0: stz SndActive,x
202 ;----------------------------------------------------------------------------
203 ; lynx_snd_handler is run at every sound interrupt
217 ; *NOW* set all values which were "pre-set" in last interrupt
221 lda SndRetAFlag ; reset the return flag, but save it first
262 ;----------------------------------------------------------------------------
263 ; A process table with addresses to sound functions
269 .byte <((SndPause)-1)
270 .byte <((SndNoteOff)-1)
271 .byte <((SndSetInstr)-1)
272 .byte <((SndNewNote2)-1)
273 .byte <((SndCallPattern)-1)
274 .byte <((SndRetToSong)-1)
275 .byte <((SndDefEnvVol)-1)
276 .byte <((SndSetEnvVol)-1)
277 .byte <((SndDefEnvFrq)-1)
278 .byte <((SndSetEnvFrq)-1)
279 .byte <((SndDefEnvWave)-1)
280 .byte <((SndSetEnvWave)-1)
281 .byte <((SndSetStereo)-1)
282 .byte <((SndSetAttenuationOn)-1)
283 .byte <((SndSetChnAttenution)-1)
284 .byte <((SndPlayerFreq)-1)
285 .byte <((SndReturnAll)-1)
290 .byte >((SndPause)-1)
291 .byte >((SndNoteOff)-1)
292 .byte >((SndSetInstr)-1)
293 .byte >((SndNewNote2)-1)
294 .byte >((SndCallPattern)-1)
295 .byte >((SndRetToSong)-1)
296 .byte >((SndDefEnvVol)-1)
297 .byte >((SndSetEnvVol)-1)
298 .byte >((SndDefEnvFrq)-1)
299 .byte >((SndSetEnvFrq)-1)
300 .byte >((SndDefEnvWave)-1)
301 .byte >((SndSetEnvWave)-1)
302 .byte >((SndSetStereo)-1)
303 .byte >((SndSetAttenuationOn)-1)
304 .byte >((SndSetChnAttenution)-1)
305 .byte >((SndPlayerFreq)-1)
306 .byte >((SndReturnAll)-1)
308 ;----------------------------------------------------------------------------
309 ; Get next sound command from stream
325 ;;; force the direct continue return
328 bne cmd991 ;; check special case
335 cmd0: lda (SndPtrTmp)
358 ;; now check if delay is only 1 AND next one is return all.
363 ;; NOW read ahead ONE
370 cmp #$92 ;; Return all
372 sta SndRetAFlag ; just set !=0
376 ;----------------------------------------------------------------------------
377 ; Call function pointed to by y
388 ;----------------------------------------------------------------------------
389 ; Stop sound on one channel
404 ;----------------------------------------------------------------------------
405 ; Send a new note, length, volume triplet
446 ;----------------------------------------------------------------------------
447 ; Start a loop with count
473 ;----------------------------------------------------------------------------
474 ; Sound volume envelope
479 lda (SndPtrTmp),y ; env #
487 sta SndEnvVolPtrHi,x ; Ptr to [cnt,inc]
494 lda (SndPtrTmp),y ; # env
498 sta SndEnvVol,x ; save
514 sta SndEnvVolLoop,x ; here is the loop-start
521 sta SndEnvVolParts2,x
530 ;----------------------------------------------------------------------------
531 ; Sound frequency envelope
536 lda (SndPtrTmp),y ; env #
544 sta SndEnvFrqPtrHi,x ; Ptr to [inc,cnt]
550 lda (SndPtrTmp),y ; # env
554 sta SndEnvFrq,x ; save
577 sta SndEnvFrqParts2,x
586 ;----------------------------------------------------------------------------
587 ; Sound frequency envelope
593 lda (SndPtrTmp),y ; env #
598 sta SndEnvWavePtrLo,x
601 sta SndEnvWavePtrHi,x ; Ptr to [inc,cnt]
607 lda (SndPtrTmp),y ; # env
611 sta SndEnvWave,x ; save
619 lda SndEnvWavePtrLo,y
621 lda SndEnvWavePtrHi,y
633 sta SndEnvWaveParts,x
636 sta SndEnvWaveParts2,x
645 ;----------------------------------------------------------------------------
654 ;;;* This set the new Player Freq instantanious!!!
740 ;;; Note,length,volume
745 lda (SndPtrTmp),y ; reload
748 lda (SndPtrTmp),y ; prescale
751 lda (SndPtrTmp),y ; laenge
820 lda SndEnvVolParts2,x
830 vol1v: lda (SndEnvPtr),y
865 ;; NEU: switch Tremolo off
872 ;; NEU: switch Tremolo off
878 ora #1 ;; if already -1 ... no effect
903 lda SndEnvFrqParts2,x
913 frq1f: lda (SndEnvPtr),y
995 ora #1 ;; if already -1 -> no effect
1000 ;; Ab hier x Kanal 0-3, y Environment
1004 lda SndNotePlaying,x
1008 lda SndEnvWavePtrLo,y
1010 lda SndEnvWavePtrHi,y
1015 dec SndEnvWaveParts,x
1017 lda SndEnvWaveLoop,x
1020 ;; Ab hier x Kanal 0-3, y Offset im Environment
1021 lda SndEnvWaveParts2,x
1022 sta SndEnvWaveParts,x
1026 ora #$80 ;; beende Env
1031 ;; Ab hier x Kanal 0-3, y Offset im Environment
1032 wav1v: lda (SndEnvPtr),y
1037 ;; Ab hier x Kanal (0-3)*8, y Offset im Environment
1041 sta SndChannel+3,x ; Shift LO
1044 sta SndChannel+7,x ; Shift HI
1047 sta SndChannel+1,x ; Feedback
1051 ;; Ab hier x Kanal (0-3)*8, y Kanal 0-3
1053 lda #$FF ; =-1 ;; stop timer to set new values...
1062 set0: ldy SndOffsets,x
1064 _IFNE ; flag == 0 => don`t set
1069 sta $fd25,y ; stop sound timer
1072 sta $fd23,y ; shifter 1
1075 ;;ora SndChannel+7,y ; shifter 2
1076 lda SndChannel+7,y ; shifter 2
1079 sta $fd21,y ; feedback
1083 sta $fd20,y ; volume
1088 sta $fd24,y ; reload
1090 ora #%00011000 ;;; #%01011000
1092 sta $fd25,y ; re-enable timer
1096 sta SndChannel+2,y ; clear flag
1119 start0: lda SndActive,x
1143 lda SndActive,x ; check default
1144 beq start20 ; inactive => ok
1146 ldx #3 ; search free channel
1149 beq start22 ; found =>
1153 dec SndReqStop,x ; stop default-channel
1165 jsr SndStartSoundx ; launch new sound
1173 stop0: dec SndReqStop,x
1176 stop1: lda SndActive,x
1183 _lynx_snd_stop_channel:
1189 stopc1: lda SndActive,x
1197 act0: ldy SndActive,x
1227 lda #0 ; Selbsmodifizierter Code!!!
1230 lda #0 ; Selbsmodifizierter Code!!!
1248 .byte $00,$06,$06,$06,$06,$05,$05,$05,$05,$05,$05,$05,$04,$04,$04,$04
1249 .byte $04,$04,$04,$04,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$02,$02
1250 .byte $02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$01,$01,$01,$01,$01
1251 .byte $01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$00,$00
1252 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
1253 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
1254 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
1255 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
1258 .byte $00,$9A,$96,$8F,$86,$FA,$E5,$D1,$BE,$AC,$9C,$8D,$00,$E8,$D3,$C0
1259 .byte $AF,$A0,$93,$87,$FA,$E7,$D6,$C6,$B8,$AC,$A1,$96,$8D,$84,$FA,$EB
1260 .byte $DE,$D2,$C7,$BC,$B3,$AA,$A1,$9A,$93,$8C,$86,$00,$F5,$EB,$E1,$D8
1261 .byte $CF,$C7,$C0,$B9,$B2,$AB,$A5,$A0,$9A,$95,$90,$8B,$87,$82,$FD,$F5
1262 .byte $EE,$E7,$E0,$D9,$D3,$CD,$C8,$C2,$BD,$B8,$B3,$AE,$AA,$A5,$A1,$9D
1263 .byte $99,$96,$92,$8F,$8B,$88,$85,$82,$7F,$7C,$79,$77,$74,$72,$6F,$6D
1264 .byte $6B,$69,$67,$64,$63,$61,$5F,$5D,$5B,$59,$58,$56,$55,$53,$51,$50
1265 .byte $4F,$4D,$4C,$4B,$49,$48,$47,$46,$44,$43,$42,$41,$40,$3F,$3E,$3D