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_play
12 .export _lynx_snd_stop
13 .export _lynx_snd_pause
14 .export _lynx_snd_continue
15 .interruptor lynx_snd_handler
19 ;----------------------------------------------------------------------------
20 ; ZP variables that go into APPZP
23 .segment "APPZP" : zeropage
30 ;----------------------------------------------------------------------------
46 SndNotePlaying: .res 4
58 SndEnvVolParts: .res 4
59 SndEnvVolParts2: .res 4
64 SndEnvFrqParts: .res 4
65 SndEnvFrqParts2: .res 4
68 SndEnvWaveLoop: .res 4
69 SndEnvWaveParts: .res 4
70 SndEnvWaveParts2: .res 4
72 MAX_INSTRUMENTS .set 64
73 SndEnvVolPtrLo: .res MAX_INSTRUMENTS
74 SndEnvVolPtrHi: .res MAX_INSTRUMENTS
75 SndEnvFrqPtrLo: .res MAX_INSTRUMENTS
76 SndEnvFrqPtrHi: .res MAX_INSTRUMENTS
77 SndEnvWavePtrLo: .res MAX_INSTRUMENTS
78 SndEnvWavePtrHi: .res MAX_INSTRUMENTS
82 SndOffsets: .byte $00,$08,$10,$18
84 ;----------------------------------------------------------------------------
92 if_count .set if_count +1
93 nest_count .set nest_count +1
94 beq .ident (.sprintf ("else%04d", if_count))
95 .ident (.sprintf ("push%04d", nest_count)) .set if_count
99 if_count .set if_count +1
100 nest_count .set nest_count +1
101 bne .ident (.sprintf ("else%04d", if_count))
102 .ident (.sprintf ("push%04d", nest_count)) .set if_count
106 if_count .set if_count +1
107 nest_count .set nest_count +1
108 bpl .ident (.sprintf ("else%04d", if_count))
109 .ident (.sprintf ("push%04d", nest_count)) .set if_count
113 if_count .set if_count +1
114 nest_count .set nest_count +1
115 bmi .ident (.sprintf ("else%04d", if_count))
116 .ident (.sprintf ("push%04d", nest_count)) .set if_count
120 if_count .set if_count +1
121 nest_count .set nest_count +1
122 bcc .ident (.sprintf ("else%04d", if_count))
123 .ident (.sprintf ("push%04d", nest_count)) .set if_count
127 if_count .set if_count +1
128 nest_count .set nest_count +1
129 bcc .ident (.sprintf ("else%04d", if_count))
130 .ident (.sprintf ("push%04d", nest_count)) .set if_count
134 if_count .set if_count +1
135 nest_count .set nest_count +1
136 bcs .ident (.sprintf ("else%04d", if_count))
137 .ident (.sprintf ("push%04d", nest_count)) .set if_count
141 bra .ident (.sprintf ("endif%04d", .ident (.sprintf ("push%04d", nest_count))))
142 .ident (.sprintf ("else%04d", .ident (.sprintf ("push%04d", nest_count)))) := *
146 .if .not .defined( .ident (.sprintf ("else%04d", .ident (.sprintf ("push%04d", nest_count)))))
147 .ident (.sprintf ("else%04d", .ident (.sprintf ("push%04d", nest_count)))) := *
149 .ident (.sprintf ("endif%04d", .ident (.sprintf ("push%04d", nest_count)))) := *
150 nest_count .set nest_count -1
155 ;----------------------------------------------------------------------------
156 ; void lynx_snd_init() will initialize the sound engine.
164 lda #%10011000|_31250Hz
167 sta STIMBKUP ; set up a 240Hz IRQ
174 stz $fd44 ; all channels full volume / no attenuation
186 init0: stz SndActive,x
201 ;----------------------------------------------------------------------------
202 ; lynx_snd_handler is run at every sound interrupt
216 ; *NOW* set all values which were "pre-set" in last interrupt
220 lda SndRetAFlag ; reset the return flag, but save it first
261 ;----------------------------------------------------------------------------
262 ; A process table with addresses to sound functions
268 .byte <((SndPause)-1)
269 .byte <((SndNoteOff)-1)
270 .byte <((SndSetInstr)-1)
271 .byte <((SndNewNote2)-1)
272 .byte <((SndCallPattern)-1)
273 .byte <((SndRetToSong)-1)
274 .byte <((SndDefEnvVol)-1)
275 .byte <((SndSetEnvVol)-1)
276 .byte <((SndDefEnvFrq)-1)
277 .byte <((SndSetEnvFrq)-1)
278 .byte <((SndDefEnvWave)-1)
279 .byte <((SndSetEnvWave)-1)
280 .byte <((SndSetStereo)-1)
281 .byte <((SndSetAttenuationOn)-1)
282 .byte <((SndSetChnAttenution)-1)
283 .byte <((SndPlayerFreq)-1)
284 .byte <((SndReturnAll)-1)
289 .byte >((SndPause)-1)
290 .byte >((SndNoteOff)-1)
291 .byte >((SndSetInstr)-1)
292 .byte >((SndNewNote2)-1)
293 .byte >((SndCallPattern)-1)
294 .byte >((SndRetToSong)-1)
295 .byte >((SndDefEnvVol)-1)
296 .byte >((SndSetEnvVol)-1)
297 .byte >((SndDefEnvFrq)-1)
298 .byte >((SndSetEnvFrq)-1)
299 .byte >((SndDefEnvWave)-1)
300 .byte >((SndSetEnvWave)-1)
301 .byte >((SndSetStereo)-1)
302 .byte >((SndSetAttenuationOn)-1)
303 .byte >((SndSetChnAttenution)-1)
304 .byte >((SndPlayerFreq)-1)
305 .byte >((SndReturnAll)-1)
307 ;----------------------------------------------------------------------------
308 ; Get next sound command from stream
324 ;;; force the direct continue return
327 bne cmd991 ;; check special case
334 cmd0: lda (SndPtrTmp)
357 ;; now check if delay is only 1 AND next one is return all.
362 ;; NOW read ahead ONE
369 cmp #$92 ;; Return all
371 sta SndRetAFlag ; just set !=0
375 ;----------------------------------------------------------------------------
376 ; Call function pointed to by y
387 ;----------------------------------------------------------------------------
388 ; Stop sound on one channel
403 ;----------------------------------------------------------------------------
404 ; Send a new note, length, volume triplet
445 ;----------------------------------------------------------------------------
446 ; Start a loop with count
472 ;----------------------------------------------------------------------------
473 ; Sound volume envelope
478 lda (SndPtrTmp),y ; env #
486 sta SndEnvVolPtrHi,x ; Ptr to [cnt,inc]
493 lda (SndPtrTmp),y ; # env
497 sta SndEnvVol,x ; save
513 sta SndEnvVolLoop,x ; here is the loop-start
520 sta SndEnvVolParts2,x
529 ;----------------------------------------------------------------------------
530 ; Sound frequency envelope
535 lda (SndPtrTmp),y ; env #
543 sta SndEnvFrqPtrHi,x ; Ptr to [inc,cnt]
549 lda (SndPtrTmp),y ; # env
553 sta SndEnvFrq,x ; save
576 sta SndEnvFrqParts2,x
585 ;----------------------------------------------------------------------------
586 ; Sound frequency envelope
592 lda (SndPtrTmp),y ; env #
597 sta SndEnvWavePtrLo,x
600 sta SndEnvWavePtrHi,x ; Ptr to [inc,cnt]
606 lda (SndPtrTmp),y ; # env
610 sta SndEnvWave,x ; save
618 lda SndEnvWavePtrLo,y
620 lda SndEnvWavePtrHi,y
632 sta SndEnvWaveParts,x
635 sta SndEnvWaveParts2,x
644 ;----------------------------------------------------------------------------
653 ;;;* This set the new Player Freq instantanious!!!
739 ;;; Note,length,volume
744 lda (SndPtrTmp),y ; reload
747 lda (SndPtrTmp),y ; prescale
750 lda (SndPtrTmp),y ; laenge
819 lda SndEnvVolParts2,x
829 vol1v: lda (SndEnvPtr),y
864 ;; NEU: switch Tremolo off
871 ;; NEU: switch Tremolo off
877 ora #1 ;; if already -1 ... no effect
902 lda SndEnvFrqParts2,x
912 frq1f: lda (SndEnvPtr),y
994 ora #1 ;; if already -1 -> no effect
999 ;; Ab hier x Kanal 0-3, y Environment
1003 lda SndNotePlaying,x
1007 lda SndEnvWavePtrLo,y
1009 lda SndEnvWavePtrHi,y
1014 dec SndEnvWaveParts,x
1016 lda SndEnvWaveLoop,x
1019 ;; Ab hier x Kanal 0-3, y Offset im Environment
1020 lda SndEnvWaveParts2,x
1021 sta SndEnvWaveParts,x
1025 ora #$80 ;; beende Env
1030 ;; Ab hier x Kanal 0-3, y Offset im Environment
1031 wav1v: lda (SndEnvPtr),y
1036 ;; Ab hier x Kanal (0-3)*8, y Offset im Environment
1040 sta SndChannel+3,x ; Shift LO
1043 sta SndChannel+7,x ; Shift HI
1046 sta SndChannel+1,x ; Feedback
1050 ;; Ab hier x Kanal (0-3)*8, y Kanal 0-3
1052 lda #$FF ; =-1 ;; stop timer to set new values...
1061 set0: ldy SndOffsets,x
1063 _IFNE ; flag == 0 => don`t set
1068 sta $fd25,y ; stop sound timer
1071 sta $fd23,y ; shifter 1
1074 ;;ora SndChannel+7,y ; shifter 2
1075 lda SndChannel+7,y ; shifter 2
1078 sta $fd21,y ; feedback
1082 sta $fd20,y ; volume
1087 sta $fd24,y ; reload
1089 ora #%00011000 ;;; #%01011000
1091 sta $fd25,y ; re-enable timer
1095 sta SndChannel+2,y ; clear flag
1118 start0: lda SndActive,x
1142 lda SndActive,x ; check default
1143 beq start20 ; inactive => ok
1145 ldx #3 ; search free channel
1148 beq start22 ; found =>
1152 dec SndReqStop,x ; stop default-channel
1164 jsr SndStartSoundx ; launch new sound
1172 stop0: dec SndReqStop,x
1175 stop1: lda SndActive,x
1182 _lynx_snd_stop_channel:
1188 stopc1: lda SndActive,x
1196 act0: ldy SndActive,x
1226 lda #0 ; Selbsmodifizierter Code!!!
1229 lda #0 ; Selbsmodifizierter Code!!!
1247 .byte $00,$06,$06,$06,$06,$05,$05,$05,$05,$05,$05,$05,$04,$04,$04,$04
1248 .byte $04,$04,$04,$04,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$02,$02
1249 .byte $02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$01,$01,$01,$01,$01
1250 .byte $01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$00,$00
1251 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$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
1257 .byte $00,$9A,$96,$8F,$86,$FA,$E5,$D1,$BE,$AC,$9C,$8D,$00,$E8,$D3,$C0
1258 .byte $AF,$A0,$93,$87,$FA,$E7,$D6,$C6,$B8,$AC,$A1,$96,$8D,$84,$FA,$EB
1259 .byte $DE,$D2,$C7,$BC,$B3,$AA,$A1,$9A,$93,$8C,$86,$00,$F5,$EB,$E1,$D8
1260 .byte $CF,$C7,$C0,$B9,$B2,$AB,$A5,$A0,$9A,$95,$90,$8B,$87,$82,$FD,$F5
1261 .byte $EE,$E7,$E0,$D9,$D3,$CD,$C8,$C2,$BD,$B8,$B3,$AE,$AA,$A5,$A1,$9D
1262 .byte $99,$96,$92,$8F,$8B,$88,$85,$82,$7F,$7C,$79,$77,$74,$72,$6F,$6D
1263 .byte $6B,$69,$67,$64,$63,$61,$5F,$5D,$5B,$59,$58,$56,$55,$53,$51,$50
1264 .byte $4F,$4D,$4C,$4B,$49,$48,$47,$46,$44,$43,$42,$41,$40,$3F,$3E,$3D