-
Notifications
You must be signed in to change notification settings - Fork 8
/
mainmem.ensq.s
317 lines (276 loc) · 11.6 KB
/
mainmem.ensq.s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
* MAINMEM.ENSQ.S
* (c) Bobbi 2022 GPLv3
*
* Ensoniq DOC Driver for Apple IIGS.
* Simulates Hitachi SN76489 sound generator chip found in BBC Micro.
*
* Ensoniq control registers
ENSQSNDCTL EQU $C03C
ENSQSNDDAT EQU $C03D
ENSQADDRL EQU $C03E
ENSQADDRH EQU $C03F
* Initialize Ensoniq
* Setup wavetable - one period of a square wave
* Start timer on oscillator #4, silence oscillators 0 to 3
ENSQINIT LDX #3
LDA #$80 ; Initialize sound queues
:L0 STZ SND0STARTIDX,X
STZ SND0ENDIDX,X
DEX
BNE :L0
* One cycle of square wave for 256 samples
* Starts at address $0000 in DOC RAM
LDA ENSQSNDCTL ; Get settings
ORA #$60 ; DOC RAM, autoincrement on
STA ENSQSNDCTL ; Set it
LDA #$00
STA ENSQADDRL ; DOC RAM addr $0000
STA ENSQADDRH ; DOC RAM addr $0000
LDA #210 ; High value of square wave
LDX #$00
:L1 STA ENSQSNDDAT ; 128 cycles of high value
INX
CPX #128
BNE :L1
LDA #40 ; Low value of square wave
:L2 STA ENSQSNDDAT ; 128 cycles of low value
INX
CPX #0
BNE :L2
* One cycle of pulse wave for 256 samples
* Starts at $0100 in DOC RAM
LDA #210 ; High value of square wave
LDX #$00
:L3 STA ENSQSNDDAT ; 8 cycles of high value
INX
CPX #8
BNE :L3
LDA #40 ; Low value of square wave
:L4 STA ENSQSNDDAT ; 255-8 cycles of low value
INX
CPX #0
BNE :L4
* Random waveform for 256 samples
* Starts at $0200 in DOC RAM
LDY #$00
:L5 LDA $E000,Y ; Read ROM code as 'random' data
BNE :S1 ; Filter out zeros
LDA #$01 ; Change 0 -> 1
:S1 STA ENSQSNDDAT
INY
CPY #0
BNE :L5
* One cycle of pulse wave for 4096 samples
** Starts at address $1000 in DOC RAM
* LDA ENSQSNDCTL ; Get settings
* ORA #$60 ; DOC RAM, autoincrement on
* STA ENSQSNDCTL ; Set it
* LDA #$00
* STA ENSQADDRL ; DOC RAM addr $1000
* LDA #$10
* STA ENSQADDRH ; DOC RAM addr $1000
* LDA #210 ; High value of pulse wave
* LDX #$00
*:L3 STA ENSQSNDDAT ; 128 cycles of high value
* INX
* CPX #128
* BNE :L3
* LDA #40 ; Low value of pulse wave
*:L4 STA ENSQSNDDAT ; 128 cycles of low value
* INX
* CPX #0
* BNE :L4
* LDY #$00
*:L6 LDX #$00
*:L5 STA ENSQSNDDAT ; 256 cycles of low value
* INX
* CPX #0
* BNE :L5
* INY
* CPY #16 ; Do it 15 times
* BNE :L6
LDA #$5C ; GS IRQ.SOUND initialization
STAL $E1002C
LDA #<ENSQISR
STAL $E1002D
LDA #>ENSQISR
STAL $E1002E
LDA #$00 ; Bank $00
STAL $E1002F
LDX #$E1 ; DOC Osc Enable register $E1
LDY #10 ; Five oscillators enabled
JSR ENSQWRTDOC
LDY #$00 ; Amplitude for osc #4 (timer)
LDA #33+1 ; Freq G2+1/8 tone = 99.46Hz
LDX #$04
JSR ENSQNOTE ; Start oscillator 4
LDX #$A4 ; Control register for osc #4
LDY #$08 ; Free run, with IRQ, start
JSR ENSQWRTDOC
; Fall through
* Silence all channels
ENSQSILENT LDY #$00 ; Amplitude
LDA #$80 ; Frequency
LDX #$03
:L1 JSR ENSQNOTE ; Initialize channel Y
STZ CHANTIMES,X ; No note playing
DEX
BPL :L1
RTS
* Stop Ensoniq interrupt
ENSQSTOP JSR ENSQSILENT
LDX #$A4 ; Control register for osc #4
LDY #$00 ; Free run, no IRQ, start
JSR ENSQWRTDOC
RTS
* Configure an Ensoniq oscillator to play a note
* On entry: X - oscillator number 0-3 , A - frequency, Y - amplitude
* Preserves all registers
ENSQNOTE PHA
PHX
PHY
STX OSCNUM ; Stash oscillator number 0-3
CPX #$00
BEQ :NOISE ; Oscillator 0 is noise channel
CPX #$01 ; Oscillator 1 controls noise freq
BNE :S0 ; Not 1? Skip
CMP #$00 ; If frequency is zero ..
BEQ :S0 ; .. skip
STA :CH1NOTE ; Store frequency for noise gen
:S0 PHA ; Stash orig freq
TAY
LDA EFREQLOW,Y
TAY ; Frequency value LS byte
LDA #$00 ; DOC register base $00 (Freq Lo)
JSR ADDOSC ; Actual register in X
JSR ENSQWRTDOC
PLA ; Get orig freq back
TAY
LDA EFREQHIGH,Y
TAY ; Frequency value MS byte
LDA #$20 ; DOC register base $20 (Freq Hi)
JSR ADDOSC ; Actual register in X
JSR ENSQWRTDOC
PLY ; Amplitude value
PHY
LDA #$40 ; DOC register base $40 (Volume)
JSR ADDOSC ; Actual register in X
JSR ENSQWRTDOC
LDY #$00 ; Wavetable pointer $00
LDA #$80 ; DOC register base $80 (Wavetable)
JSR ADDOSC ; Actual register in X
JSR ENSQWRTDOC
LDY #$00 ; For 256 byte wavetable, res 0
LDA #$C0 ; DOC register base $C0 (WT size)
JSR ADDOSC ; Actual register in X
JSR ENSQWRTDOC
LDY #$00 ; Free run, no IRQ, start
LDA #$A0 ; DOC register base $A0 (Control)
JSR ADDOSC ; Actual register in X
JSR ENSQWRTDOC
BRA :DONE
:NOISE PHA ; Preserve value of parameter P
AND #$04 ; 'Periodic noise' or 'white noise'?
BEQ :S1
LDY #$02 ; DOC RAM $0200 - white noise
BRA :S2
:S1 LDY #$01 ; DOC RAM $0100 - periodic noise
:S2 LDX #$80 ; Wavetable pointer Register
JSR ENSQWRTDOC
PLA ; Restore P
AND #$03 ; Keep least significant 2 bits
TAY
LDA :NOISENOTE,Y ; BBC Micro note value
:S3 PHA ; Gonna need it again
TAY ; Computed note
LDA EFREQHIGH,Y
TAY ; Frequency value LS byte
LDX #$20 ; Freq Hi Register
JSR ENSQWRTDOC
PLY ; Get computed note back
LDA EFREQLOW,Y
TAY ; Frequency value LS byte
LDX #$00 ; Freq Lo Register
JSR ENSQWRTDOC
LDX #$40 ; Amplitude Register
PLY
PHY
JSR ENSQWRTDOC
LDX #$C0 ; Wavetable size Register
LDY #$04 ; Size 256, resolution 4
* LDY #$00 ; Size 256, resolution 1
JSR ENSQWRTDOC
LDX #$A0 ; Wavetable size Register
LDY #$00 ; Free run, no IRQ, start
JSR ENSQWRTDOC
:DONE PLY
PLX
PLA
RTS
:NOISENOTE DB 149 ; BBC Micro note P=0 or 4
DB 101 ; BBC Micro note P=1 or 5
DB 53 ; BBC Micro note P=2 or 6
:CH1NOTE DB 00 ; Note on channel 1
* Adjust frequency of note already playing
* On entry: X - oscillator number 0-3, Y - frequency to set
* Preserves X & Y
ENSQFREQ PHX
PHY ; Gonna need it again
LDA EFREQLOW,Y
TAY ; Frequency value LS byte
LDA #$00 ; DOC register base $00 (Freq Lo)
JSR ADDOSC ; Actual register in X
JSR ENSQWRTDOC
PLY ; Get freq back
PHY
LDA EFREQHIGH,Y
TAY ; Frequency value MS byte
LDA #$20 ; DOC register base $20 (Freq Hi)
JSR ADDOSC ; Actual register in X
JSR ENSQWRTDOC
PLY
PLX
RTS
* Adjust amplitude of note already playing
* On entry: X - oscillator number 0-3, Y - amplitude to set
* Preserves X & Y
ENSQAMP PHX
PHY ; Gonna need it again
LDA #$40 ; DOC register base $00 (Freq Lo)
JSR ADDOSC ; Actual register in X
JSR ENSQWRTDOC
PLY
PLX
RTS
* Ensoniq interrupt service routine - just calls generic audio ISR
ENSQISR CLD
JSR AUDIOISR
RTL
**
** Private functions follow (ie: not part of driver API)
**
* Add oscillator number to value in A, return sum in X
* Used by ENSQNOTE & ENSQFREQ
ADDOSC CLC
ADC OSCNUM
TAX
RTS
OSCNUM DB $00
* Wait for Ensoniq to be ready
ENSQWAIT LDA ENSQSNDCTL
BMI ENSQWAIT
RTS
* Write to a DOC register
* On entry: Value in Y, register in X
* Preserves all registers
ENSQWRTDOC PHA
JSR ENSQWAIT ; Wait for DOC to be ready
LDA ENSQSNDCTL
AND #$90 ; DOC register, no autoincr
ORA #$0F ; Master volume maximum
STA ENSQSNDCTL
STX ENSQADDRL ; Select DOC register
STZ ENSQADDRH
STY ENSQSNDDAT ; Write data
PLA
RTS