2022-11-18 00:31:29 +00:00
|
|
|
* MAINMEM.ENSQ.S
|
|
|
|
* (c) Bobbi 2022 GPLv3
|
|
|
|
*
|
|
|
|
* Ensoniq DOC Driver for Apple IIGS.
|
2022-11-26 04:09:40 +00:00
|
|
|
* Simulates Hitachi SN76489 sound generator chip found in BBC Micro.
|
2022-11-18 00:31:29 +00:00
|
|
|
*
|
|
|
|
|
|
|
|
* 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
|
|
|
|
|
2022-11-26 00:19:09 +00:00
|
|
|
* One cycle of square wave for 256 samples
|
|
|
|
* Starts at address $0000 in DOC RAM
|
2022-11-18 00:31:29 +00:00
|
|
|
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
|
2022-11-26 00:19:09 +00:00
|
|
|
LDA #210 ; High value of square wave
|
2022-11-18 00:31:29 +00:00
|
|
|
LDX #$00
|
|
|
|
:L1 STA ENSQSNDDAT ; 128 cycles of high value
|
|
|
|
INX
|
|
|
|
CPX #128
|
|
|
|
BNE :L1
|
2022-11-26 00:19:09 +00:00
|
|
|
LDA #40 ; Low value of square wave
|
2022-11-18 00:31:29 +00:00
|
|
|
:L2 STA ENSQSNDDAT ; 128 cycles of low value
|
|
|
|
INX
|
|
|
|
CPX #0
|
|
|
|
BNE :L2
|
|
|
|
|
2022-11-26 00:19:09 +00:00
|
|
|
* One cycle of pulse wave for 256 samples
|
|
|
|
* Starts at $0100 in DOC RAM
|
|
|
|
LDA #210 ; High value of square wave
|
|
|
|
LDX #$00
|
2022-11-26 02:34:08 +00:00
|
|
|
:L3 STA ENSQSNDDAT ; 8 cycles of high value
|
2022-11-26 00:19:09 +00:00
|
|
|
INX
|
2022-11-26 02:34:08 +00:00
|
|
|
CPX #8
|
2022-11-26 00:19:09 +00:00
|
|
|
BNE :L3
|
|
|
|
LDA #40 ; Low value of square wave
|
2022-11-26 02:34:08 +00:00
|
|
|
:L4 STA ENSQSNDDAT ; 255-8 cycles of low value
|
2022-11-26 00:19:09 +00:00
|
|
|
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
|
|
|
|
|
2022-11-18 00:31:29 +00:00
|
|
|
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
|
|
|
|
|
|
|
|
|
2022-11-22 21:10:06 +00:00
|
|
|
* Stop Ensoniq interrupt
|
|
|
|
ENSQSTOP JSR ENSQSILENT
|
|
|
|
LDX #$A4 ; Control register for osc #4
|
|
|
|
LDY #$00 ; Free run, no IRQ, start
|
|
|
|
JSR ENSQWRTDOC
|
|
|
|
RTS
|
|
|
|
|
|
|
|
|
2022-11-18 00:31:29 +00:00
|
|
|
* 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
|
|
|
|
|
2022-11-26 00:19:09 +00:00
|
|
|
CPX #$00
|
|
|
|
BEQ :NOISE ; Oscillator 0 is noise channel
|
|
|
|
|
2022-11-26 04:09:40 +00:00
|
|
|
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
|
2022-11-18 00:31:29 +00:00
|
|
|
TAY
|
2022-11-21 21:27:45 +00:00
|
|
|
LDA EFREQLOW,Y
|
2022-11-18 00:31:29 +00:00
|
|
|
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
|
2022-11-21 21:27:45 +00:00
|
|
|
LDA EFREQHIGH,Y
|
2022-11-18 00:31:29 +00:00
|
|
|
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
|
|
|
|
|
2022-11-26 00:19:09 +00:00
|
|
|
LDY #$00 ; For 256 byte wavetable, res 0
|
|
|
|
LDA #$C0 ; DOC register base $C0 (WT size)
|
|
|
|
JSR ADDOSC ; Actual register in X
|
|
|
|
JSR ENSQWRTDOC
|
|
|
|
|
2022-11-18 00:31:29 +00:00
|
|
|
LDY #$00 ; Free run, no IRQ, start
|
|
|
|
LDA #$A0 ; DOC register base $A0 (Control)
|
|
|
|
JSR ADDOSC ; Actual register in X
|
|
|
|
JSR ENSQWRTDOC
|
|
|
|
|
2022-11-26 00:19:09 +00:00
|
|
|
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
|
2022-11-18 00:31:29 +00:00
|
|
|
JSR ENSQWRTDOC
|
|
|
|
|
2022-11-26 00:19:09 +00:00
|
|
|
PLA ; Restore P
|
|
|
|
AND #$03 ; Keep least significant 2 bits
|
2022-11-26 02:34:08 +00:00
|
|
|
TAY
|
|
|
|
LDA :NOISENOTE,Y ; BBC Micro note value
|
2022-11-26 00:19:09 +00:00
|
|
|
|
2022-11-26 02:34:08 +00:00
|
|
|
:S3 PHA ; Gonna need it again
|
|
|
|
TAY ; Computed note
|
|
|
|
LDA EFREQHIGH,Y
|
|
|
|
TAY ; Frequency value LS byte
|
2022-11-26 00:19:09 +00:00
|
|
|
LDX #$20 ; Freq Hi Register
|
|
|
|
JSR ENSQWRTDOC
|
|
|
|
|
2022-11-26 02:34:08 +00:00
|
|
|
PLY ; Get computed note back
|
|
|
|
LDA EFREQLOW,Y
|
|
|
|
TAY ; Frequency value LS byte
|
2022-11-26 00:19:09 +00:00
|
|
|
LDX #$00 ; Freq Lo Register
|
|
|
|
JSR ENSQWRTDOC
|
|
|
|
|
|
|
|
LDX #$40 ; Amplitude Register
|
2022-11-18 00:31:29 +00:00
|
|
|
PLY
|
2022-11-26 00:19:09 +00:00
|
|
|
PHY
|
|
|
|
JSR ENSQWRTDOC
|
|
|
|
|
|
|
|
LDX #$C0 ; Wavetable size Register
|
|
|
|
LDY #$04 ; Size 256, resolution 4
|
2022-11-26 02:34:08 +00:00
|
|
|
* LDY #$00 ; Size 256, resolution 1
|
2022-11-26 00:19:09 +00:00
|
|
|
JSR ENSQWRTDOC
|
|
|
|
|
|
|
|
LDX #$A0 ; Wavetable size Register
|
|
|
|
LDY #$00 ; Free run, no IRQ, start
|
|
|
|
JSR ENSQWRTDOC
|
|
|
|
|
|
|
|
:DONE PLY
|
2022-11-18 00:31:29 +00:00
|
|
|
PLX
|
|
|
|
PLA
|
|
|
|
RTS
|
2022-11-26 02:34:08 +00:00
|
|
|
: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
|
2022-11-26 04:09:40 +00:00
|
|
|
:CH1NOTE DB 00 ; Note on channel 1
|
2022-11-18 00:31:29 +00:00
|
|
|
|
|
|
|
|
|
|
|
* Adjust frequency of note already playing
|
2022-11-18 23:29:36 +00:00
|
|
|
* On entry: X - oscillator number 0-3, Y - frequency to set
|
2022-11-18 00:31:29 +00:00
|
|
|
* Preserves X & Y
|
|
|
|
ENSQFREQ PHX
|
|
|
|
PHY ; Gonna need it again
|
2022-11-21 21:27:45 +00:00
|
|
|
LDA EFREQLOW,Y
|
2022-11-18 00:31:29 +00:00
|
|
|
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
|
2022-11-21 21:27:45 +00:00
|
|
|
LDA EFREQHIGH,Y
|
2022-11-18 00:31:29 +00:00
|
|
|
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
|
2022-11-18 23:29:36 +00:00
|
|
|
* On entry: X - oscillator number 0-3, Y - amplitude to set
|
2022-11-18 00:31:29 +00:00
|
|
|
* 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
|
|
|
|
|
2022-11-18 03:26:46 +00:00
|
|
|
* Ensoniq interrupt service routine - just calls generic audio ISR
|
2022-11-18 23:29:36 +00:00
|
|
|
ENSQISR CLD
|
2022-11-19 00:26:20 +00:00
|
|
|
JSR AUDIOISR
|
|
|
|
RTL
|
2022-11-18 03:26:46 +00:00
|
|
|
|
|
|
|
|
2022-11-18 00:38:21 +00:00
|
|
|
**
|
|
|
|
** Private functions follow (ie: not part of driver API)
|
|
|
|
**
|
2022-11-18 00:31:29 +00:00
|
|
|
|
|
|
|
* 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
|
|
|
|
|