Applecorn/mainmem.mock.s

200 lines
6.7 KiB
ArmAsm

* MAINMEM.MOCK.S
* (c) Bobbi 2022 GPLv3
*
* Mockingboard Driver.
*
*
* 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
* TODO: STOP VIA INTERRUPT & DEALLOC_INTERRUPT before we QUIT
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 MOCKWRT
DEX
BPL :L0
LDA #$38 ; Turn off noise
LDX #07
JSR MOCKWRT
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 ; Noise channel
BEQ :DONE ; TODO: IGNORE NOISE FOR NOW
TXA
DEC A ; Subtract 1
ASL ; Double to get fine register
TAX
LDA MFREQLOW,Y ; LSB of divider
JSR MOCKWRT ; Write value to AY-3 register
INX ; Add one for course register
LDA MFREQHIGH,Y ; MSB of divider
JSR MOCKWRT ; Write value to AY-3 register
:DONE PLX
RTS
* 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 :DONE ; Has no amplitude
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 MOCKWRT ; Write value to AY-3 register
:DONE PLX
RTS
* Mockingboard interrupt service routine - just calls generic audio ISR
MOCKISR CLD
* TODO: Check whether interrupt is from Mockingboard or not
BIT MOCK_6522_T1CL ; Clear interrupt
JSR AUDIOISR
CLC ; CC indicates we serviced irq
RTS
**
** Private functions follow (ie: not part of driver API)
**
* Write to both AY-3s
* On entry: A - value, X - register
* On exit: All regs preserved.
MOCKWRT PHY
STX MOCK_6522_ORA1 ; Latch the address
STX MOCK_6522_ORA2
LDY #MOCK_AY_LATCH_ADDR
STY MOCK_6522_ORB1
STY MOCK_6522_ORB2
LDY #MOCK_AY_INACTIVE ; Go inactive
STY MOCK_6522_ORB1
STY MOCK_6522_ORB2
STA MOCK_6522_ORA1 ; Write data
STA MOCK_6522_ORA2
LDY #MOCK_AY_WRITE
STY MOCK_6522_ORB1
STY MOCK_6522_ORB2
LDY #MOCK_AY_INACTIVE ; Go inactive
STY MOCK_6522_ORB1
STY MOCK_6522_ORB2
PLY
RTS