You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

533 lines
12 KiB

;======================================================================
; SN76489 SOUND DRIVER
;
; WRITTEN BY: DEAN NETHERTON
;======================================================================
;
; SN74489 PSG CHIP NEEDS AN INPUT CLOCK FREQUENCY OF
; NO MORE THAN 4 MHZ. THE CLOSEST THING THERE IS TO A STANDARD
; IS THE MSX FREQ OF 3.579545 MHZ.
;
; TODO:
; 1. PROVIDE SUPPORT FOR NOISE CHANNEL
; 2. DOES THIS WORK FOR FASTER CPUS? ONLY BEEN TESTED ON A Z80 7MHZ UNIT
;
;======================================================================
; CONSTANTS
;======================================================================
;
DEVECHO "SN76489 MODE="
;
#IF (SNMODE == SNMODE_VGM)
SN76489_PORT_LEFT .EQU $C6 ; PORTS FOR ACCESSING THE SN76489 CHIP (LEFT)
SN76489_PORT_RIGHT .EQU $C7 ; PORTS FOR ACCESSING THE SN76489 CHIP (RIGHT)
DEVECHO "VGM"
#ENDIF
;
#IF (SNMODE == SNMODE_RC)
SN76489_PORT_LEFT .EQU $FF ; PORTS FOR ACCESSING THE SN76489 CHIP (LEFT)
SN76489_PORT_RIGHT .EQU $FB ; PORTS FOR ACCESSING THE SN76489 CHIP (RIGHT)
DEVECHO "RC"
#ENDIF
;
#IF (SNMODE == SNMODE_DUO)
SN76489_PORT_LEFT .EQU $BE ; PORTS FOR ACCESSING THE SN76489 CHIP (LEFT)
SN76489_PORT_RIGHT .EQU $BF ; PORTS FOR ACCESSING THE SN76489 CHIP (RIGHT)
DEVECHO "RC"
#ENDIF
;
DEVECHO ", IO_LEFT="
DEVECHO SN76489_PORT_LEFT
DEVECHO ", IO_RIGHT="
DEVECHO SN76489_PORT_RIGHT
DEVECHO ", CLOCK="
DEVECHO SN7CLK
DEVECHO " HZ\n"
;
SN7_IDAT .EQU 0
SN7_TONECNT .EQU 3 ; COUNT NUMBER OF TONE CHANNELS
SN7_NOISECNT .EQU 1 ; COUNT NUMBER OF NOISE CHANNELS
SN7_CHCNT .EQU SN7_TONECNT + SN7_NOISECNT
;
#INCLUDE "audio.inc"
;
; BLINDLY RESET THE PSG AS SOON AS WE CAN AFTER BOOT BECAUSE IT
; DOES NOT RESET ITSELF AT POWER ON AND MAKES UGLY NOISE.
;
SN76489_PREINIT:
JR SN7_RESET
;
SN76489_INIT:
LD IY, SN7_IDAT ; POINTER TO INSTANCE DATA
LD BC, SN7_FNTBL ; BC := FUNCTION TABLE ADDRESS
LD DE, SN7_IDAT ; DE := SN7 INSTANCE DATA PTR
CALL SND_ADDENT ; ADD ENTRY, A := UNIT ASSIGNED
LD DE,STR_MESSAGELT
CALL WRITESTR
LD A, SN76489_PORT_LEFT
CALL PRTHEXBYTE
LD DE,STR_MESSAGERT
CALL WRITESTR
LD A, SN76489_PORT_RIGHT
CALL PRTHEXBYTE
CALL SN7_RESET
XOR A ; SIGNAL SUCCESS
RET
;
;======================================================================
; INITIALIZE DEVICE
;======================================================================
;
SN7_INIT:
;
#IFDEF SBCV2004
LD A,(HB_RTCVAL)
OR %00001000 ; SBC-V2-004+ CHANGE
EZ80_IO
OUT (RTCIO),A ; TO HALF CLOCK SPEED
#ENDIF
;
LD HL,SN7_REG_INIT ; POINT TO REG INIT TBL
LD B,SN7_REG_CNT ; BYTES IN TABLE
SN7_INIT1
LD A,(HL)
INC HL ; BUMP FOR NEXT TIME
EZ80_IO
OUT (SN76489_PORT_LEFT), A ; WRITE LEFT PORT
EZ80_IO
OUT (SN76489_PORT_RIGHT), A ; WRITE RIGHT PORT
DJNZ SN7_INIT1 ; LOOP TILL DONE
;
#IFDEF SBCV2004
LD A,(HB_RTCVAL)
AND %11110111 ; SBC-V2-004+ CHANGE TO
EZ80_IO
OUT (RTCIO),A ; NORMAL CLOCK SPEED
#ENDIF
;
RET
;
SN7_REG_INIT:
.DB $80,$00 ; TONE 1 FREQ (2 BYTES)
.DB $9F ; TONE 1 ATTENTUATION
.DB $A0,$00 ; TONE 2 FREQ (2 BYTES)
.DB $BF ; TONE 2 ATTENTUATION
.DB $C0,$00 ; TONE 3 FREQ (2 BYTES)
.DB $DF ; TONE 3 ATTENTUATION
.DB $E0 ; NOISE CONTROL
.DB $FF ; NOISE ATTENTUATION
SN7_REG_CNT .EQU $ - SN7_REG_INIT
;
;======================================================================
; SN76489 DRIVER - SOUND ADAPTER (SND) FUNCTIONS
;======================================================================
;
SN7_RESET:
AUDTRACE(SNT_INIT)
;
CALL SN7_INIT ; HARDWARE INIT
;
; RESET DEFAULTS IN CASE OF AN IN-PLACE HBIOS RESTART
LD HL,0
LD (SN7_PENDING_PERIOD),HL
LD (SN7_PENDING_DURATION),HL
XOR A
LD (SN7_PENDING_VOLUME),A
;
XOR A
RET
; BIT MAPPING
; SET TONE:
; 1 CC 0 PPPP (LOW)
; 0 0 PPPPPP (HIGH)
; 1 CC 1 VVVV
SN7_VOLUME:
AUDTRACE(SNT_VOL)
AUDTRACE_L
AUDTRACE_CR
LD A, L
LD (SN7_PENDING_VOLUME), A
XOR A ; SIGNAL SUCCESS
RET
SN7_NOTE:
LD DE, SN7NOTETBL
CALL AUD_NOTE ; RETURNS PERIOD IN HL, FALL THRU
;
SN7_PERIOD:
AUDTRACE(SNT_PERIOD)
AUDTRACE_HL
AUDTRACE_CR
;
LD A,H ; IF ZERO - ERROR
OR L
JR Z,SN7_PERIOD1
;
LD A,H ; MAXIMUM TONE PERIOD IS 10-BITS
AND 11111100B ; ALLOWED RANGE IS 0001-03FF (1023)
JR NZ,SN7_PERIOD1 ; RETURN NZ IF NUMBER TOO LARGE
LD (SN7_PENDING_PERIOD),HL ; SAVE AND RETURN SUCCESSFUL
XOR A ; SET SUCCESS
RET
;
SN7_PERIOD1:
LD HL,$FFFF ; REQUESTED PERIOD IS LARGER
LD (SN7_PENDING_PERIOD),HL ; THAN PSG CAN SUPPORT, SO
OR $FF ; SET PERIOD TO $FFFF
RET ; AND RETURN FAILURE
;
SN7_PLAY:
AUDTRACE(SNT_PLAY)
AUDTRACE_D
AUDTRACE_CR
LD A, (SN7_PENDING_PERIOD + 1)
CP $FF
JR Z, SN7_PLAY1 ; PERIOD IS TOO LARGE, UNABLE TO PLAY
CALL SN7_APPLY_VOL
CALL SN7_APPLY_PRD
XOR A ; SIGNAL SUCCESS
RET
SN7_PLAY1: ; TURN CHANNEL VOL TO OFF AND STOP PLAYING
LD A, (SN7_PENDING_VOLUME)
PUSH AF
LD A, 0
LD (SN7_PENDING_VOLUME), A
CALL SN7_APPLY_VOL
POP AF
LD (SN7_PENDING_VOLUME), A
OR $FF ; SIGNAL FAILURE
RET
SN7_QUERY:
LD A, E
CP BF_SNDQ_CHCNT
JR Z, SN7_QUERY_CHCNT
CP BF_SNDQ_PERIOD
JR Z, SN7_QUERY_PERIOD
CP BF_SNDQ_VOLUME
JR Z, SN7_QUERY_VOLUME
CP BF_SNDQ_DEV
JR Z, SN7_QUERY_DEV
OR $FF ; SIGNAL FAILURE
RET
SN7_QUERY_CHCNT:
LD B, SN7_TONECNT
LD C, SN7_NOISECNT
XOR A
RET
SN7_QUERY_PERIOD:
LD HL, (SN7_PENDING_PERIOD)
XOR A
RET
SN7_QUERY_VOLUME:
LD A, (SN7_PENDING_VOLUME)
LD L, A
LD H, 0
XOR A
RET
SN7_QUERY_DEV:
LD B, SNDDEV_SN76489
LD DE, SN76489_PORT_LEFT ; E WITH LEFT PORT
LD HL, SN76489_PORT_RIGHT ; L WITH RIGHT PORT
XOR A
RET
;
; UTIL FUNCTIONS
;
SN7_APPLY_VOL: ; APPLY VOLUME TO BOTH LEFT AND RIGHT CHANNELS
PUSH BC ; D CONTAINS THE CHANNEL NUMBER
PUSH AF
LD A, D
AND $3
RLCA
RLCA
RLCA
RLCA
RLCA
OR $90
LD B, A
LD A, (SN7_PENDING_VOLUME)
RRCA
RRCA
RRCA
RRCA
AND $0F
LD C, A
LD A, $0F
SUB C
AND $0F
OR B ; A CONTAINS COMMAND TO SET VOLUME FOR CHANNEL
AUDTRACE(SNT_REGWR)
AUDTRACE_A
AUDTRACE_CR
#IFDEF SBCV2004
PUSH AF
LD A,(HB_RTCVAL)
OR %00001000 ; SBC-V2-004+ CHANGE
EZ80_IO
OUT (RTCIO),A ; TO HALF CLOCK SPEED
POP AF
#ENDIF
EZ80_IO
OUT (SN76489_PORT_LEFT), A
EZ80_IO
OUT (SN76489_PORT_RIGHT), A
#IFDEF SBCV2004
LD A,(HB_RTCVAL)
AND %11110111 ; SBC-V2-004+ CHANGE TO
EZ80_IO
OUT (RTCIO),A ; NORMAL CLOCK SPEED
#ENDIF
POP AF
POP BC
RET
SN7_APPLY_PRD:
PUSH DE
PUSH BC
PUSH AF
LD HL, (SN7_PENDING_PERIOD)
LD A, D
AND $3
RLCA
RLCA
RLCA
RLCA
RLCA
OR $80
LD B, A ; PERIOD COMMAND 1 - CONTAINS CHANNEL ONLY
LD A, L ; GET LOWER 4 BITS FOR COMMAND 1
AND $F
OR B ; A NOW CONTAINS FIRST PERIOD COMMAND
AUDTRACE(SNT_REGWR)
AUDTRACE_A
AUDTRACE_CR
#IFDEF SBCV2004
PUSH AF
LD A,(HB_RTCVAL)
OR %00001000 ; SBC-V2-004+ CHANGE
EZ80_IO
OUT (RTCIO),A ; TO HALF CLOCK SPEED
POP AF
#ENDIF
EZ80_IO
OUT (SN76489_PORT_LEFT), A
EZ80_IO
OUT (SN76489_PORT_RIGHT), A
#IFDEF SBCV2004
LD A,(HB_RTCVAL)
AND %11110111 ; SBC-V2-004+ CHANGE TO
EZ80_IO
OUT (RTCIO),A ; NORMAL CLOCK SPEED
#ENDIF
LD A, L ; RIGHT SHIFT OUT THE LOWER 4 BITS
RRCA
RRCA
RRCA
RRCA
AND $F
LD B, A
LD A, H
AND $3
RLCA
RLCA
RLCA
RLCA ; AND PLACE IN BITS 5 AND 6
OR B ; OR THE TWO SETS OF BITS TO MAKE 2ND PERIOD COMMAND
AUDTRACE(SNT_REGWR)
AUDTRACE_A
AUDTRACE_CR
#IFDEF SBCV2004
PUSH AF
LD A,(HB_RTCVAL)
OR %00001000 ; SBC-V2-004+ CHANGE
EZ80_IO
OUT (RTCIO),A ; TO HALF CLOCK SPEED
POP AF
#ENDIF
EZ80_IO
OUT (SN76489_PORT_LEFT), A
EZ80_IO
OUT (SN76489_PORT_RIGHT), A
#IFDEF SBCV2004
LD A,(HB_RTCVAL)
AND %11110111 ; SBC-V2-004+ CHANGE TO
EZ80_IO
OUT (RTCIO),A ; NORMAL CLOCK SPEED
#ENDIF
POP AF
POP BC
POP DE
RET
SN7_DURATION:
LD (SN7_PENDING_DURATION),HL ; SET TONE DURATION
XOR A
RET
SN7_DEVICE:
LD D,SNDDEV_SN76489 ; D := DEVICE TYPE
LD E,0 ; E := PHYSICAL UNIT
LD C,$00 ; C := DEVICE TYPE
LD H,0 ; H := 0, DRIVER HAS NO MODES
LD L,SN76489_PORT_LEFT ; L := BASE I/O ADDRESS
XOR A
RET
SN7_BEEP:
JP SND_BEEP ; DEFER TO GENERIC CODE IN HBIOS
SN7_FNTBL:
.DW SN7_RESET
.DW SN7_VOLUME
.DW SN7_PERIOD
.DW SN7_NOTE
.DW SN7_PLAY
.DW SN7_QUERY
.DW SN7_DURATION
.DW SN7_DEVICE
.DW SN7_BEEP
;
#IF (($ - SN7_FNTBL) != (SND_FNCNT * 2))
.ECHO "*** INVALID SND FUNCTION TABLE ***\n"
!!!!!
#ENDIF
SN7_PENDING_PERIOD .DW 0 ; PENDING PERIOD (10 BITS)
SN7_PENDING_VOLUME .DB 0 ; PENDING VOL (8 BITS -> DOWNCONVERTED TO 4 BITS AND INVERTED)
SN7_PENDING_DURATION .DW 0 ; PENDING DURATION (16 BITS)
STR_MESSAGELT .DB "\r\nSN76489: LEFT IO=0x$"
STR_MESSAGERT .DB ", RIGHT IO=0x$"
#IF AUDIOTRACE
SNT_INIT .DB "\r\nSN7_INIT\r\n$"
SNT_VOLOFF .DB "\r\nSN7_VOLUME OFF\r\n$"
SNT_VOL .DB "\r\nSN7_VOLUME: $"
SNT_NOTE .DB "\r\nSN7_NOTE: $"
SNT_PERIOD .DB "\r\nSN7_PERIOD: $"
SNT_PLAY .DB "\r\nSN7_PLAY CH: $"
SNT_REGWR .DB "\r\nOUT SN76489, $"
#ENDIF
;
;======================================================================
; EIGHTH TONE FREQUENCY TABLE
;======================================================================
;
; THE FOLLOWING TABLE MAPS A FULL OCTAVE OF EIGHTH-NOTES
; STARTING AT A# IN OCTAVE 0 TO THE CORRESPONDING PERIOD
; VALUE TO USE ON THE PSG TO ACHIEVE THE DESIRED NOTE FREQUENCY.
;
; THE FREQUENCY PRODUCED BY THE SN76489 IS:
; FREQ = CLOCK / 32 / PERIOD
;
; SO, TO MAP A DESIRED FREQUENCY TO A PERIOD, WE USE:
; PERIOD = CLOCK / 32 / FREQ
;
; IN ORDER TO IMPROVE THE RESOLUTION OF THE FREQUENCY
; VALUE USED, WE ALSO MULTPLY BOTH SIDES OF THE EQUATION
; BY 100:
; PERIOD * 100 = (CLOCK / 32 / FREQ) * 100
;
; THE RESULTING PERIOD VALUE CAN BE REPEATEDLY HALVED
; TO TO JUMP UP AS MANY OCTAVES AS DESIRED.
;
; THE FINAL VALUE IS SHIFTED BY AUD_SCALE BITS
; IN ORDER TO IMPROVE THE RESOLUTION. THIS FINAL SHIFT
; IS REMOVED WHEN IN THE AY_NOTE ROUTINE.
;
; ASSUMING A CLOCK OF 3.579545 MHZ, THE FIRST PLAYABLE
; NOTE WILL BE A2 (HBIOS NOTE CODE 92).
;
SN7RATIO .EQU (SN7CLK * 100) / (32 >> AUD_SCALE)
;
SN7NOTETBL:
.DW SN7RATIO / 2913 ; A0#/B0b
.DW SN7RATIO / 2956 ;
.DW SN7RATIO / 2999 ;
.DW SN7RATIO / 3042 ;
.DW SN7RATIO / 3086 ; B0
.DW SN7RATIO / 3131 ;
.DW SN7RATIO / 3177 ;
.DW SN7RATIO / 3223 ;
.DW SN7RATIO / 3270 ; C1
.DW SN7RATIO / 3318 ;
.DW SN7RATIO / 3366 ;
.DW SN7RATIO / 3415 ;
.DW SN7RATIO / 3464 ; C1#/D1b
.DW SN7RATIO / 3515 ;
.DW SN7RATIO / 3566 ;
.DW SN7RATIO / 3618 ;
.DW SN7RATIO / 3670 ; D1
.DW SN7RATIO / 3724 ;
.DW SN7RATIO / 3778 ;
.DW SN7RATIO / 3833 ;
.DW SN7RATIO / 3889 ; D1#/E1b
.DW SN7RATIO / 3945 ;
.DW SN7RATIO / 4003 ;
.DW SN7RATIO / 4061 ;
.DW SN7RATIO / 4120 ; E1
.DW SN7RATIO / 4180 ;
.DW SN7RATIO / 4241 ;
.DW SN7RATIO / 4302 ;
.DW SN7RATIO / 4365 ; F1
.DW SN7RATIO / 4428 ;
.DW SN7RATIO / 4493 ;
.DW SN7RATIO / 4558 ;
.DW SN7RATIO / 4624 ; F1#/G1b
.DW SN7RATIO / 4692 ;
.DW SN7RATIO / 4760 ;
.DW SN7RATIO / 4829 ;
.DW SN7RATIO / 4899 ; G1
.DW SN7RATIO / 4971 ;
.DW SN7RATIO / 5043 ;
.DW SN7RATIO / 5116 ;
.DW SN7RATIO / 5191 ; G1#/A1b
.DW SN7RATIO / 5266 ;
.DW SN7RATIO / 5343 ;
.DW SN7RATIO / 5421 ;
.DW SN7RATIO / 5499 ; A1
.DW SN7RATIO / 5579 ;
.DW SN7RATIO / 5661 ;
.DW SN7RATIO / 5743 ;