-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathmainmem.mock.s
252 lines (221 loc) · 8.95 KB
/
mainmem.mock.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
* MAINMEM.MOCK.S
* (c) Bobbi 2022 GPLv3
*
* Mockingboard Driver.
* Simulates Hitachi SN76489 sound generator chip found in BBC Micro.
*
*
* I borrowed some ideas from Deater:
* https://github.com/deater/dos33fsprogs/blob/master/music/pt3_lib/pt3_lib_mockingboard_setup.s
*
* Mockingboard control registers
* ASSUMES SLOT 4
MOCK_6522_ORB1 EQU $C400 ; 6522 #1 port b data
MOCK_6522_ORA1 EQU $C401 ; 6522 #1 port a data
MOCK_6522_DDRB1 EQU $C402 ; 6522 #1 data direction port B
MOCK_6522_DDRA1 EQU $C403 ; 6522 #1 data direction port A
MOCK_6522_T1CL EQU $C404 ; 6522 #1 t1 low order latches
MOCK_6522_T1CH EQU $C405 ; 6522 #1 t1 high order counter
MOCK_6522_T1LL EQU $C406 ; 6522 #1 t1 low order latches
MOCK_6522_T1LH EQU $C407 ; 6522 #1 t1 high order latches
MOCK_6522_T2CL EQU $C408 ; 6522 #1 t2 low order latches
MOCK_6522_T2CH EQU $C409 ; 6522 #1 t2 high order counters
MOCK_6522_SR EQU $C40A ; 6522 #1 shift register
MOCK_6522_ACR EQU $C40B ; 6522 #1 auxilliary control register
MOCK_6522_PCR EQU $C40C ; 6522 #1 peripheral control register
MOCK_6522_IFR EQU $C40D ; 6522 #1 interrupt flag register
MOCK_6522_IER EQU $C40E ; 6522 #1 interrupt enable register
MOCK_6522_ORANH EQU $C40F ; 6522 #1 port a data no handshake
MOCK_6522_ORB2 EQU $C480 ; 6522 #2 port b data
MOCK_6522_ORA2 EQU $C481 ; 6522 #2 port a data
MOCK_6522_DDRB2 EQU $C482 ; 6522 #2 data direction port B
MOCK_6522_DDRA2 EQU $C483 ; 6522 #2 data direction port A
; AY-3-8910 commands on port B
MOCK_AY_RESET EQU $0
MOCK_AY_INACTIVE EQU $4
MOCK_AY_READ EQU $5
MOCK_AY_WRITE EQU $6
MOCK_AY_LATCH_ADDR EQU $7
* Initialize Mockingboard
MOCKINIT LDA #$FF ; All VIA pins output
STA MOCK_6522_DDRB1
STA MOCK_6522_DDRA1
STA MOCK_6522_DDRB2
STA MOCK_6522_DDRA2
LDA #MOCK_AY_RESET ; Reset left AY-3
STA MOCK_6522_ORB1
LDA #MOCK_AY_INACTIVE
STA MOCK_6522_ORB1
LDA #MOCK_AY_RESET ; Reset right AY-3
STA MOCK_6522_ORB2
LDA #MOCK_AY_INACTIVE
STA MOCK_6522_ORB2
LDA #<MOCKISR ; Set up ISR with ALLOC_INTERRUPT
STA ALLOCPL+2
LDA #>MOCKISR
STA ALLOCPL+3
JSR MLI
DB ALLOCCMD
DW ALLOCPL
PHP
SEI
LDA #$40 ; Configure VIA interrupt
STA MOCK_6522_ACR
LDA #$7F ; Clear all bits
STA MOCK_6522_IER
LDA #$C0 ; Set bit 6
STA MOCK_6522_IFR
STA MOCK_6522_IER
LDA #$F4 ; $27F4 => 100Hz
STA MOCK_6522_T1CL
LDA #$27
STA MOCK_6522_T1CH
PLP
* Silence all channels
MOCKSILENT LDX #13 ; Clear all 14 AY-3 regs
LDA #$00
:L0 JSR MOCKWRT1
JSR MOCKWRT2
DEX
BPL :L0
LDA #$38 ; Turn off noise
LDX #07
JSR MOCKWRT1 ; First AY-3
LDA #$14 ; A,B=tone; C=noise
JSR MOCKWRT2 ; Second AY-3
RTS
* Stop Mockingboard interrupt
MOCKSTOP JSR MOCKSILENT
LDA #$7F ; Clear all bits
STA MOCK_6522_IER
LDA ALLOCPL+1 ; Interrupt number
STA DEALLOCPL+1
JSR MLI ; Deallocate ISR
DB DEALLOCCMD
DW DEALLOCPL
RTS
* Configure a Mockingboard oscillator to play a note
* On entry: X - oscillator number 0-3, A - frequency, Y - amplitude
* Preserves all registers
MOCKNOTE PHA
PHY
TAY
JSR MOCKFREQ ; Set frequency
PLY
JSR MOCKAMP ; Set amplitude
PLA
RTS
* Adjust frequency of note already playing
* On entry: X - oscillator number 0-3, Y - frequency to set
* Preserves X & Y
MOCKFREQ PHX
CPX #$00 ; Oscillator 0 is noise channel
BEQ :NOISE
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 TXA
DEC A ; Subtract 1
ASL ; Double to get fine register
TAX
LDA MFREQLOW,Y ; LSB of divider
JSR MOCKWRT1 ; Write value to AY-3 register
INX ; Add one for course register
LDA MFREQHIGH,Y ; MSB of divider
JSR MOCKWRT1 ; Write value to AY-3 register
PLX
RTS
:NOISE TYA ; Parameter P
AND #$03 ; Keep least significant 2 bits
TAY
LDA :NOISENOTE,Y ; Get value of note
EOR #$FF ; Negate
INC A
CLC ; Add 255
ADC #$FF
LSR ; Convert 0..255 to 0..31
LSR
LSR
LDX #6 ; Noise period register
JSR MOCKWRT2 ; Write value to AY-3 register
PLX
RTS
:NOISENOTE DB $FF ; BBC Micro note P=0 or 4
DB $80 ; BBC Micro note P=1 or 5
DB $00 ; BBC Micro note P=2 or 6
:CH1NOTE DB $00 ; Note on channel 1
* Adjust amplitude of note already playing
* On entry: X - oscillator number 0-3, Y - amplitude to set
* Preserves X & Y
MOCKAMP PHX
CPX #$00 ; Noise channel
BEQ :NOISE
TXA ; Add 7 to get register
CLC
ADC #7
TAX
TYA ; Amplitude 0..255
LSR ; Divide by 16
LSR
LSR
LSR ; Now 0..15
JSR MOCKWRT1 ; Write value to AY-3 register
PLX
RTS
:NOISE LDX #10 ; Chan C
TYA ; Amplitude 0..255
LSR ; Divide by 16
LSR
LSR
LSR ; Now 0..15
JSR MOCKWRT2 ; Write value to AY-3 register
PLX
RTS
* Mockingboard interrupt service routine - just calls generic audio ISR
MOCKISR CLD
LDA MOCK_6522_IFR ; Interrupt flag register
AND #$40 ; Bit 6
BEQ :NOTMB ; Wasn't Mockingboard IRQ
BIT MOCK_6522_T1CL ; Clear interrupt
JSR AUDIOISR
CLC ; CC indicates we serviced irq
RTS
:NOTMB SEC ; We did not handle irq
RTS
**
** Private functions follow (ie: not part of driver API)
**
* Write to first AY-3
* On entry: A - value, X - register
* On exit: All regs preserved.
MOCKWRT1 PHY
STX MOCK_6522_ORA1 ; Latch the address
LDY #MOCK_AY_LATCH_ADDR
STY MOCK_6522_ORB1
LDY #MOCK_AY_INACTIVE ; Go inactive
STY MOCK_6522_ORB1
STA MOCK_6522_ORA1 ; Write data
LDY #MOCK_AY_WRITE
STY MOCK_6522_ORB1
LDY #MOCK_AY_INACTIVE ; Go inactive
STY MOCK_6522_ORB1
PLY
RTS
* Write to second AY-3
* On entry: A - value, X - register
* On exit: All regs preserved.
MOCKWRT2 PHY
STX MOCK_6522_ORA2 ; Latch the address
LDY #MOCK_AY_LATCH_ADDR
STY MOCK_6522_ORB2
LDY #MOCK_AY_INACTIVE ; Go inactive
STY MOCK_6522_ORB2
STA MOCK_6522_ORA2 ; Write data
LDY #MOCK_AY_WRITE
STY MOCK_6522_ORB2
LDY #MOCK_AY_INACTIVE ; Go inactive
STY MOCK_6522_ORB2
PLY
RTS