; VBISOUND.SRC, rev. 2.4
; By Chris Chabris, 7-14-83
; For ANTIC Magazine
; VBI routine to play constant tone/distortion music independent of mainline program execution
; Call from BASIC with:
; Q=USR(ADR(SET$))
; No additional calls are necessary except the following if you want to turn off the routine:
; Q=USR(ADR(RESET$))
; PROGRAM EQUATES FOLLOW:
TABLE EQU $00CB ; Address of current music table stored as a string
AUDF1 EQU $D200 ; Audio frequency (pitch)for voice #1
AUDC1 EQU $D201 ; Audio control (volume/distortion) for voice #1
XITVBV EQU $E462 ; Exit deferred VBI O.S. vector
SETVBV EQU $E45C ; Set VBI O.S. vector
AUDCTL EQU $D208 ; POKEY AUDio ConTroL register
; DEFERRED VBI ROUTINE FOLLOWS:
ORG $0600 ; This program is not relocatable
LDX #00 ; Initialize the X- and Y-registers
LDY #00
LOOP1 JSR MX2 ; Multiply the X-register by two for 2-byte table
LDA V0ADR,X ; Get address of next voice's table of notes and durations
STA TABLE ; Put it on page 0 for indirect indexed addressing
LDA V0ADR+1,X
STA TABLE+1
JSR DX2 ; Now divide X by two for the next table
LDA STATUS,X
BNE NEXT ; If status>0 (voice inactive) go do next voice
DEC DUR,X ; Decrement this voice's duration counter
BNE NEXT ; If >0, note or pause is still in progress, so go try the next voice
LDY COUNT,X ; Get the index into the music table to find next note, duration
LDA (TABLE),Y
CEND CMP #$FF ; $FF means end this voice until the status becomes zero again
BNE CREP ; If not $FF, check for the next command option
LDA #01 ; A one in the status register turns off the voice
STA STATUS,X
LDA #00
JSR MX2
STA AUDF1,X ; Turn off this voice by storing a zero in its Audio Frequency
STA AUDC1,X ; and Audio Control registers
JSR DX2
JMP NEXT ; And go on to try the next voice
CREP CMP #$FE ; $FE means immemdiately repeat the preceding music for this voice
BNE NOTE ; If not $FE, try to play the next note
LDA #00
STA COUNT,X ; Reset index to zero, indicating the start of the music table
LDA #01 ; Mark a duration of one so that the next VBI will restart the music
STA DUR,X
LDA #00
STA PAUSE,X ; Turn off a pause that might have been in progress
JMP NEXT ; Try the next voice
NOTE PHA ; Save the frequency value in the accumulator
LDA PAUSE,X ; Check to see whether a pause was in progress
BNE PLAY ; >0 indicates yes - a pause has just terminated
LDA #03 ; No - so set up a pause of 3/60 second
STA DUR,X ; Using the duration counter
STA PAUSE,X ; And setting the pause register
LDA #00
JSR MX2
STA AUDF1,X ; Don't forget to turn off the sound!
JSR DX2
PLA ; Pop stack
JMP NEXT ; to go on to the next voice
PLAY LDA #00 ; The pause is over - reset the pause register
STA PAUSE,X
PLA ; Retrieve the next frequency value
JSR MX2
STA AUDF1,X ; and put it into the Audio Frequency register for this voice
LDA #$A6 ; Give it a pure distortion (10) and volume of 6
STA AUDC1,X ; so put 16*distortion+volume into the Audio Control register
JSR DX2
INY ; Increment index to get this note's duration
LDA (TABLE),Y
STA DUR,X ; Store it to begin the countdown
INY ; Increment the index for the next VBI
TYA
STA COUNT,X ; and save it until then
NEXT INX ; Increment counter of # of voices processed
CPX NUMV ; Have we processed all voices specified?
BNE AGAIN ; No - go jump to the beginning (too far to branch)
JMP DONE ; Yes - so go and end the interrupt
AGAIN JMP LOOP1 ; Go back to the start of the loop to process the next voice
; SUBROUTINES FOLLOW:
MX2 PHA ; This routine simply multiplies the value in the
TXA ; X-register by two for various uses
ASL A
TAX
PLA
RTS
DX2 PHA ; This one reverses the multiply done in the
TXA ; routine above
LSR A
TAX
PLA
RTS
; EXIT DEFERRED VERTICAL BLANK INTERRUPT ROUTINE FOLLOWS:
DONE LDA AUD ; Get user AUDCTL setting
STA AUDCTL ; Make sure POKEY remembers it
JMP XITVBV ; Now let the O.S. take us out of the interrupt and back to main program
; VARIABLE STORAGE FOLLOWS:
NUMV DB $01
V0ADR DB $00,$00,$00,$00,$00,$00,$00,$00
DUR DB $01,$01,$01,$01
STATUS DB $00,$00,$00,$00
COUNT DB $00,$00,$00,$00
PAUSE DB $00,$00,$00,$00
AUD DB $00
; RELOCATABLE VBI INSERTION ROUTINE FOLLOWS:
ORG $6000
PLA ; Required when accessing from BASIC
LDY #$00 ; Lobyte of VBI routine start address goes in Y-register
LDX #$06 ; Hibyte of same goes in X-register
LDA #07 ; 7=Deferred VBI, 6=Immediate VBI
JSR SETVBV ; Let the O.S. insert the routine
RTS ; All done, so back to BASIC
; RELOCATABLE VBI RESET ROUTINE FOLLOWS:
ORG $6100
PLA ; This reverses the above routine by storing the
LDY #$62 ; address XITVBV in the deferred VBI vector
LDX #$E4 ; (actually, it lets the O.S. do it again)
LDA #07
JSR SETVBV
RTS
END