Category : Music and Digitized Voice
Archive   : PAS-SDK1.ZIP
Filename : MIXERS.ASM

 
Output of file : MIXERS.ASM contained in archive : PAS-SDK1.ZIP
;$Author: DCODY $
;$Date: 28 Jun 1992 11:36:42 $
;$Header: W:/sccs/mixers/mixers.asv 1.2 28 Jun 1992 11:36:42 DCODY $
;$Log: W:/sccs/mixers/mixers.asv $
;
; Rev 1.2 28 Jun 1992 11:36:42 DCODY
; fixed FM split, and made SB channel assigment changes on entry to SETMIXER
;
; Rev 1.1 23 Jun 1992 16:45:28 DCODY
; PAS2 update
;
; Rev 1.0 15 Jun 1992 09:41:36 BCRANE
; Initial revision.
;$Logfile: W:/sccs/mixers/mixers.asv $
;$Modtimes$
;$Revision: 1.2 $

Title MIXERS -- Games Authors interface to MIXER/VOLUME/CROSS/FILTER
page 64,131

; /*\
;---|*|----====< MIXERS >====----
;---|*|
;---|*| This module contains the code for supporting the mixers,filter,
;---|*| volume control, and PCM I/O.
;---|*|
; |*| Media Vision, Inc. Copyright (c) 1991, All Rights Reserved
;---|*|
; \*/
;
if1
%out ***********************************************************
%out * *
%out * ----====< Programming Caveat >====---- *
%out * *
%out * This code is provided as documentation ONLY. Attempting *
%out * to use this in your program will make you hardware *
%out * dependent!!!. Please use the identical API located in *
%out * in MVHxLIB for mixer control. Here are the library *
%out * function names: *
%out * *
%out * int (far *MVSetMixerFunction) (); *
%out * int (far *MVSetVolumeFunction) (); *
%out * int (far *MVSetFilterFunction) (); *
%out * int (far *MVSetCrossChannel) (); *
%out * int (far *MVGetMixerFunction) (); *
%out * int (far *MVGetVolumeFunction) (); *
%out * int (far *MVGetFilterFunction) (); *
%out * int (far *MVGetCrossChannel) (); *
%out * int (far *MVRealSoundSwitch) (); *
%out * int (far *MVFMSplitSwitch) (); *
%out * *
%out ***********************************************************
.err
endif

;
; /*\
;---|*|
;---|*|----=======================================================---------
;---|*|----====< MVSOUND.SYS function table entry conditions:
;---|*|----=======================================================---------
;---|*|
;---|*| This function returns a segment:offset to the table of 10 functions
;---|*| The table is made up of 32 bit pointers to the ten far routines.
;---|*| Each routine has different register requirements:
;---|*|
;---|*| Function #1 Set a Mixer.
;---|*|
;---|*| Entry Condition:
;---|*| BX is the setting (0% - 100%)
;---|*| CX is the mixer select (BI_OUTPUTMIXER | BI_INPUTMIXER)
;---|*| DX is the channel (ex: BI_L_FM, BI_R_FM, etc.)
;---|*|
;---|*| Exit Condition:
;---|*| All other registers are unchanged.
;---|*|
;---|*| Function #2 Set the Volume/Equalizer device.
;---|*|
;---|*| Entry Condition:
;---|*| BX is the setting (0% - 100%)
;---|*| CX is the volume channel select:
;---|*|
;---|*| BI_VOLLOUD
;---|*| BI_VOLENHANCE
;---|*| BI_VOLBASS
;---|*| BI_VOLTREBLE
;---|*| BI_VOLLEFT
;---|*| BI_VOLRIGHT
;---|*|
;---|*| Since some of the devices are swithes, a 0% turns it
;---|*| off, and a 100% turns it on. The switches are:
;---|*|
;---|*| BI_VOLLOUD -- ENHANCED STEREO switch
;---|*| BI_VOLENHANCE -- LOUDNESS switch
;---|*|
;---|*| Exit Condition:
;---|*| All other registers are unchanged.
;---|*|
;---|*| Function #3 Set the Filter.
;---|*|
;---|*| Entry Condition:
;---|*| BX is a new setting (0% - 100%)
;---|*|
;---|*| 0% filters out anything higher than 0k hz (is mute)
;---|*| 100% filters out anything higher than 20 khz
;---|*|
;---|*| Exit Condition:
;---|*| All other registers are unchanged.
;---|*|
;---|*| Function #4 Set the Cross Channel.
;---|*|
;---|*| Entry Condition:
;---|*| BX holds the new cross channel mask bits:
;---|*|
;---|*| 00000001b Right to Right
;---|*| 00000010b Left to Right
;---|*| 00000100b Right to Left
;---|*| 00001000b Left to Left
;---|*|
;---|*| A set bit turns ON the connection.
;---|*| A clear bit turns OFF the connection.
;---|*|
;---|*| Exit Condition:
;---|*| All other registers are unchanged.
;---|*|
;---|*| Function #5 Get a Mixer setting.
;---|*|
;---|*| Entry Condition:
;---|*| CX is the mixer select (BI_OUTPUTMIXER | BI_INPUTMIXER)
;---|*| DX is the channel (ex: BI_L_FM, BI_R_FM, etc.)
;---|*|
;---|*| Exit Condition:
;---|*| BL is the setting (0% - 100%)
;---|*| BH is 0 for off, FF for on.
;---|*| All other registers are unchanged.
;---|*|
;---|*| Function #6 Get the Volume/Equalizer setting.
;---|*|
;---|*| Entry Condition:
;---|*| CX is the volume channel select:
;---|*|
;---|*| BI_VOLLOUD
;---|*| BI_VOLENHANCE
;---|*| BI_VOLBASS
;---|*| BI_VOLTREBLE
;---|*| BI_VOLLEFT
;---|*| BI_VOLRIGHT
;---|*| BI_VOLMODE
;---|*|
;---|*| Since some of the devices are swithes, a 0% turns it
;---|*| off, and a 100% turns it on. The switches are:
;---|*|
;---|*| BI_VOLLOUD -- ENHANCED STEREO switch
;---|*| BI_VOLENHANCE -- LOUDNESS switch
;---|*|
;---|*| Exit Condition:
;---|*| BX is the setting (0% - 100%)
;---|*| All other registers are unchanged.
;---|*|
;---|*| Function #7 Get the Filter setting.
;---|*|
;---|*| Entry Condition:
;---|*| None
;---|*|
;---|*| Exit Condition:
;---|*| BX is a new setting (0% - 100%)
;---|*| 0% filters out anything higher than 0k hz (is mute)
;---|*| 100% filters out anything higher than 20 khz
;---|*|
;---|*| All other registers are unchanged.
;---|*|
;---|*| Function #8 Get the Cross Channel setting
;---|*|
;---|*| Entry Condition:
;---|*| None
;---|*|
;---|*| Exit Condition:
;---|*| BX holds the new cross channel mask bits:
;---|*|
;---|*| 00000001b Right to Right
;---|*| 00000010b Left to Right
;---|*| 00000100b Right to Left
;---|*| 00001000b Left to Left
;---|*|
;---|*| A set bit turns ON the connection.
;---|*| A clear bit turns OFF the connection.
;---|*|
;---|*| All other registers are unchanged.
;---|*|
;---|*| Function #9 Get/Set the Real Sound bit.
;---|*|
;---|*| Entry Condition:
;---|*| BX may hold 0 to turn off realsound, 100 to turn it on.
;---|*| CX determines if the switch is read or written. If cx
;---|*| is 0, the switch is read. If cx is not zero, the
;---|*| switch is written.
;---|*|
;---|*| Exit Condition:
;---|*| If the switch is read, BX holds 0 for off, 100 for on.
;---|*| All other registers are unchanged.
;---|*|
;---|*| Function #10 Get/Set the Split FM chips bit.
;---|*|
;---|*| Entry Condition:
;---|*| BX may hold 0 to split the chips, 100 to have mono chips
;---|*| CX determines if the switch is read or written. If cx
;---|*| is 0, the switch is read. If cx is not zero, the
;---|*| switch is written.
;---|*|
;---|*| Exit Condition:
;---|*| If the switch is read, BX holds 0 for split, 100 for mono.
;---|*| All other registers are unchanged.
;---|*|
; \*/

;
; The following equate sets up the compiled model size for this code:
;
MODELSIZE equ 0

include model.inc
include masm.inc ; masm equates, etc
include common.inc ; hardware equates module
include state.inc ; state table
include binary.inc ; int2f binary interface module

.code
assume ds:@code
assume es:@code

;
; The following address is the entrypoint into the static mixer driver.
; This driver was provided as the static linked mixer driver embedded in
; MIXERC.C. This code compiles into a .COM format, then is translated into
; a header file to be included into MIXERC.C.
;
org 0h ; The original driver worked
; with a zero offset.
public StartingAddress ; The driver was initialized by
StartingAddress label far ; a far call to offset 0 of the driver.

jmp near ptr doinit ; go do it...

db 0 ; filler to align on next dword

signature db "MVPROAS01" ; 8 byte signature


; /*\
;---|*|--------------------====< Data Section >====--------------------
; \*/

FunctionTable label near
smixerfunc dd offset dosetmixer ; set the mixers
svolumefunc dd offset dosetvolume ; set the volume
sfilterfunc dd offset dosetfilter ; set the filter
scrossfunc dd offset dosetcross ; set the cross channel
gmixerfunc dd offset dogetmixer ; get the mixers
gvolumefunc dd offset dogetvolume ; get the volume
gfilterfunc dd offset dogetfilter ; get the filter
gcrossfunc dd offset dogetcross ; get the cross channel
realsound dd offset dorealsound ; get/set the realsound bit
fmsplit dd offset dofmsplit ; get/set the FM split bit

TheDMAChannel db DEFAULTDMA ; default DMA channel
TheIRQChannel db DEFAULTIRQ ; default IRQ channel

;
; These variable hold the current mixer hardware state
;
_inmixertbl dw 16 dup(0f0fh) ; input mixer indexes, settings
_outmixertbl dw 16 dup(0f0fh) ; output mixer indexes, settings
_currmixer db 16 dup(0fh) ; pointer to the other tables
_voltbl dw 8 dup(000fh) ; total volume indexes, settings
_filtidx db 0 ; filter index (0 - 6)

HardwareShadowTable MVState <0>
mvhwShadowPointer dd 0 ; far pointer to the state table
mvhwFunctionTable dd 0 ; far pointer to the function table

HardwareType dw 0 ; hardware feature bits
ORIGPAS equ 0000000000000000b ; all feature bits are zero
bMVA508 equ 0000000000000001b ; new mixer chip
CDPC equ 0000000100000000b ; CDPC installed at original address
PAS16 equ 0000001000000000b ; PAS16 installed at original address
PAS8 equ 0000010000000000b ; PAS8 installed at original address

sbleft db 0 ; SB left mixer connection
sbright db 0 ; SB right mixer connection

;
; a table of maximum physical settings for each of the volume control device.
;
_voltblmax db 02
db 03
db EQUALIZERMAX
db EQUALIZERMAX
_volleftmax db ? ; volume max changes between
_volritmax db ? ; mixer chips
db 06
db 00
;
; a linear table of filter values - from mute to high for the 8 bit filter
;
tableof label byte
db 000000b ; mute - goes to PC speaker
db 100100b ; 20hz to 2.9khz
db 111001b ; 20hz to 5.9khz
db 110001b ; 20hz to 8.9khz
db 101001b ; 20hz to 11.9khz
db 100010b ; 20hz to 15.9khz
db 100001b ; 20hz to 17.8khz

;
; National Semi's Mixer Volume settings (0 - 12)
;
mixersettings label byte
db 0 ; level 0
db 20H ; level 1
db 10H ; level 2
db 08H ; level 3
db 04H ; level 4
db 02H ; level 5
db 12H ; level 6
db 2AH ; level 7
db 16H ; level 8
db 01H ; level 9
db 29H ; level A
db 1DH ; level B
db 2FH ; level C


; /*\
;---|*|--------------------====< Code Section >====--------------------
; \*/

; /*\
;---|*|------------------------==================-------------------------
;---|*|------------------------====< doinit >====-------------------------
;---|*|------------------------==================-------------------------
;---|*|
;---|*| This routine is only called if the DOS driver is not loaded, so
;---|*| we must init the various stuff...
;---|*|
;---|*| Entry Conditions:
;---|*| None
;---|*|
;---|*| Exit Conditions:
;---|*| DX:AX point to the function table
;---|*| BX holds the DMA channel #
;---|*| CX holds the IRQ channel #
;---|*|
; \*/

invmso_done db 0 ; perform this code just once!

public doinit
doinit proc far
push ds

push cs ; all data is stored locally.
pop ds
;
; perform the hardware code initialization
; this code can only be processed once
;
cmp [invmso_done],0 ; have we been here?
jjnz inmv_done ; yes, don't do this again
inc [invmso_done] ; no, so pass by just once
;
; determine the hardware and setup for default stand-alone operation
;
mov bx,ORIGPAS ; bx holds the flags

mov dx,INTRCTLR ; check the board rev bits
in al,dx
test al,fICrevbits ; non-zero means we have new hardware
jz foundthehardware

mov bx,CDPC
mov dx,MASTERMODRD ; check for the CDPC
in al,dx
test al,bMMRDmsmd ; master/slave bit set?
jnz foundthehardware

mov bx,PAS8 ; check for the 8 bit card
mov dx,SLAVEMODRD ; check for the CDPC
in al,dx
test al,bSMRDdactyp ; 16 bit DAC?
jz foundthehardware ; no, so it's the PAS16 card
mov bx,PAS16+bMVA508 ; check for the 8 bit card
;
foundthehardware:
mov [HardwareType],bx ; save the type

test bx,PAS16 ; is a PAS16 with the new mixer?

mov bl,MVVOLUMEMAX ; (default to new mixer)
mov ax,(L_FREE*256)+R_FREE ; SB is FREE for mva508 types

jnz @F

mov bl,NSVOLUMEMAX ; no, use the NS mixer data
mov ax,(L_SPEAKER*256)+R_SPEAKER ; SB is SPEAKER for others
;
@@:
mov [_volleftmax],bl ; save the maximum volume setting
mov [_volritmax],bl
mov [sbleft],ah ; save the sb connections
mov [sbright],al
;
; we must complete the 32 bit addresses in the vector table
;
push es
push di

mov ax,cs ; ax holds the segment for storing
lea bx,FunctionTable
mov wptr [mvhwFunctionTable+0],bx ; save a copy of the table
mov wptr [mvhwFunctionTable+2],ax ; pointer in our code

mov es,ax ; es:di points to the local table
mov di,bx

mov cx,10 ; update just ten entries
cld
;
@@:
inc di ; pass the offset
inc di
stosw ; store the segment
loop @B

pop di
pop es
;
; setup a pointer to our local hardware state table
;
lea bx,HardwareShadowTable
mov wptr [mvhwShadowPointer+0],bx
mov wptr [mvhwShadowPointer+2],cs
;
; find the int 2F interface
;
mov ax,0bc00h ; load the registers to check if
mov bx,'??' ; MVSOUND.SYS is loaded.
sub cx,cx ; this is a unique enough approach
sub dx,dx

int 2fh ; get the response

xor bx,cx ; merge the response into BX
xor bx,dx
cmp bx,'MV' ; is the int 2F interface here?
jnz inmv_done ; no, exit home
;
; the int 2F interface is there, so get the state table.
;
mov ax,0bc02h ; get the state table pointer
int 2fh
mov wptr [mvhwShadowPointer+0],bx
mov wptr [mvhwShadowPointer+2],dx

mov ax,0bc03h ; get the function pointer

int 2fh
mov wptr [mvhwFunctionTable+0],bx
mov wptr [mvhwFunctionTable+2],dx

mov ax,0bc04h ; get the DMA and IRQ numbers
int 2fh
mov TheDMAChannel,bl ; save the correct DMA & IRQ
mov TheIRQChannel,cl
;
inmv_done:
mov ax,wptr [mvhwFunctionTable+0]
mov dx,wptr [mvhwFunctionTable+2]

pop ds
retf

doinit endp

;
; /*\
;---|*|-------------------------===================-------------------------
;---|*|-----------------------====< dogetcross >====-------------------------
;---|*|-------------------------===================-------------------------
;---|*|
;---|*| Return the Cross channel mask
;---|*|
;---|*| Entry Conditions:
;---|*| None
;---|*|
;---|*| Exit Conditions:
;---|*| BX holds the old cross channel mask bits:
;---|*|
;---|*| 00000001b Right to Right
;---|*| 00000010b Right to Left
;---|*| 00000100b Left to Right
;---|*| 00001000b Left to Left
;---|*|
;---|*| A set bit means the connection is ON.
;---|*| A clear bit means the connection is OFF.
;---|*|
; \*/

public dogetcross
dogetcross proc far

push ax ; save everything
push cx
push dx
push ds

push es
push di

les di,cs:[mvhwShadowPointer] ; get the pointer
mov bl,es:[di._crosschannel]
and bx,0000fh

pop di
pop es
;
far_return_less_bx label near
pop ds
pop dx
pop cx
pop ax
retf

dogetcross endp

;
; /*\
;---|*|-------------------------===================-------------------------
;---|*|-----------------------====< dosetcross >====-------------------------
;---|*|-------------------------===================-------------------------
;---|*|
;---|*| Load a new cross channel mask
;---|*|
;---|*| Entry Conditions:
;---|*| BX holds the new cross channel mask bits:
;---|*|
;---|*| 00000001b Right to Right
;---|*| 00000010b Right to Left
;---|*| 00000100b Left to Right
;---|*| 00001000b Left to Left
;---|*|
;---|*| A set bit turns ON the connection.
;---|*| A clear bit turns OFF the connection.
;---|*|
;---|*| Exit Conditions:
;---|*| None
;---|*|
; \*/

public dosetcross
dosetcross proc far

push ax
push bx
push cx
push dx
push ds

push es
push di
pushf

les di,cs:[mvhwShadowPointer] ; get the pointer
mov dx,CROSSCHANNEL
xchg ax,bx

cli ; kill ints until popf
mov ah,es:[di._crosschannel]
and ax,0f00fh
or al,ah
mov es:[di._crosschannel],al
out dx,al

popf
pop di
pop es

jmp far_return

dosetcross endp

;
; /*\
;---|*|------------------------====================------------------------
;---|*|----------------------====< dogetfilter >====------------------------
;---|*|------------------------====================------------------------
;---|*|
;---|*| Return the current filter settings, from 0 to 100 %, where
;---|*| 0 is mute and 100 % is CD quality
;---|*|
;---|*| Entry Conditions:
;---|*| None
;---|*|
;---|*| Exit Conditions:
;---|*| BX holds the value
;---|*|
; \*/

public dogetfilter
dogetfilter proc far

push ax ; save everything
push cx
push dx
push ds

push es
push di
les di,cs:[mvhwShadowPointer] ; get the pointer
mov al,es:[di._audiofilt]
pop di
pop es

sub bx,bx ; ax starts with mute
test al,20h ; mute bit clear
jz dogefi_done ; yes, we have the value

mov bx,17
cmp al,24h ; male voice?
jz dogefi_done

mov bx,34
cmp al,39h ; Telephone?
jz dogefi_done

mov bx,50
cmp al,31h ; AM Radio?
jz dogefi_done

mov bx,67
cmp al,29h ; FM Radio?
jz dogefi_done

mov bx,84
cmp al,22h ; Cassette?
jz dogefi_done

mov bx,100 ; CD = 100 %

dogefi_done:
jmp short far_return_less_bx

dogetfilter endp

;
; /*\
;---|*|------------------------====================------------------------
;---|*|----------------------====< dosetfilter >====------------------------
;---|*|------------------------====================------------------------
;---|*|
;---|*| Set the filter to a setting from 0 to 100 %, where 0 is mute, 100 %
;---|*| is no filtering at all.
;---|*|
;---|*| Entry Conditions:
;---|*| BX holds the value
;---|*|
;---|*| Exit Conditions:
;---|*| None
;---|*|
; \*/

public dosetfilter
dosetfilter proc far
push ax ; save everything
push bx
push cx
push dx
push ds

push cs ; all data is stored locally.
pop ds

mov ax,6 ; translate 0-100 % into 0-6
mov bh,ah ; flush the top half
xchg ax,bx
or ax,ax ; if mute, skip the math...
jz @F
mul bx
mov bl,100
div bx ; ax holds the new setting
;
@@:
push ax
call SetFilter
pop ax
jmp far_return

dosetfilter endp

;
; /*\
;---|*|------------------------===================------------------------
;---|*|----------------------====< dogetmixer >====------------------------
;---|*|------------------------===================------------------------
;---|*|
;---|*| Return the channel setting in either the input or output mixer.
;---|*| The return value will be 0 - 100 %
;---|*|
;---|*| Entry Conditions:
;---|*| CX holds the mixer select (BI_OUTPUTMIXER or BI_INPUTMIXER)
;---|*| DX holds the channel (ex: L_LEFTFM, R_LEFTFM, etc.)
;---|*|
;---|*| Exit Conditions:
;---|*| BX returns the volume (0 - 100%)
;---|*|
; \*/

public dogetmixer
dogetmixer proc far
push ax ; save everything
push cx
push dx
push ds

push cs ; all data is stored locally.
pop ds

mov bx,dx
and bx,0fh ; make sure the channel is legit
lea ax,_inmixertbl
cmp cx,BI_INPUTMIXER
jz @F
lea ax,_outmixertbl
;
@@:
sub cl,_currmixer[bx] ; get the current channel selection
add bx,ax
mov al,[bx] ; get the current value

mov bx,100 ; translate 0-31 into 0-100%
cbw
mul bx
or ax,ax
jz @F
mov bl,31
div bx ; ax holds the new setting
;
@@:
call rounduppct ; psuedo round up
xchg ax,bx ; bl holds the result
cmp cl,1 ; carry set if Z, clear if NZ
sbb bh,bh ; bh holds the ON/OFF flag
jmp far_return_less_bx

dogetmixer endp

;
; /*\
;---|*|------------------------===================------------------------
;---|*|----------------------====< dosetmixer >====------------------------
;---|*|------------------------===================------------------------
;---|*|
;---|*| Set an input or output mixer channel from 0 to 100%
;---|*|
;---|*| Entry Conditions:
;---|*| BX holds the volume (0 - 100%)
;---|*| CX holds the mixer select (BI_OUTPUTMIXER or BI_INPUTMIXER)
;---|*| DX holds the channel (ex: L_LEFTFM, R_LEFTFM, etc.)
;---|*|
;---|*| Exit Conditions:
;---|*| None
;---|*|
; \*/

public dosetmixer
dosetmixer proc far

push ax ; save everything
push bx
push cx
push dx
push ds

push cs ; all data is stored locally.
pop ds
;
; special case the variable channel assignments here...
;
cmp cl,BI_L_SBDAC ; if left SB
jnz @F
mov cl,[sbleft] ; force it to be true
jmp chgdone
;
@@:
cmp cl,BI_R_SBDAC ; if right SB
jnz @F
mov cl,[sbright] ; force it to be true
;
chgdone:
;
; perform the mixer settings now...
;
push dx ; save the channels
push dx ; and one more copy

mov ax,31 ; translate 0-100% into 0-31
mov bh,ah ; flush the top half
xchg ax,bx
or ax,ax ; if mute, skip the math...
jz @F
mul bx
mov bl,100
div bx ; ax holds the new setting
;
@@:
pop bx ; get the channel
and bx,0fh ; make sure the channel is legit

lea dx,_inmixertbl
mov ch,INPUTMIXER
cmp cl,BI_INPUTMIXER
jz @F
lea dx,_outmixertbl
mov ch,OUTPUTMIXER
;
@@:
mov _currmixer[bx],cl ; save the channel selection
add bx,dx

call FetchMixLevel ; on the National chip, convert to 0-12
mov [bx],ax ; save the new value

xchg ah,al ; let ax hold the converted value
cbw

mov cl,ch ; cl holds the parameter
mov bx,ax ; bx holds the channel setting
pop ax ; ax holds the channel #
call SetMixer ; set the mixer
;
far_return label near ; here for convienience sake
pop ds
pop dx
pop cx
pop bx
pop ax
retf

dosetmixer endp

;
; /*\
;---|*|------------------------====================------------------------
;---|*|----------------------====< dogetvolume >====------------------------
;---|*|------------------------====================------------------------
;---|*|
;---|*| Return with one of the volume register settings
;---|*|
;---|*| Entry Conditions:
;---|*| CX holds the channel
;---|*|
;---|*| Exit Conditions:
;---|*| BX holds the value
;---|*|
; \*/
;
.errnz VOLMUTE-40h ; volume devices have bit 6 set
.errnz BI_VOLENHANCE-BI_VOLLOUD-1 ; LOUDNESS=0, ENHANCED=1

public dogetvolume
dogetvolume proc far

push ax ; save everything
push cx
push dx
push ds

push cs ; all data is stored locally.
pop ds
;
; convert the channel number to the table index
;
and cx,07h ; make it legit
mov bx,cx ; load the index into bx
cmp cl,1 ; if it's BI_VOLLOUD, make it BI_VOLEHN...
sbb ax,ax ; ax = ffff if 0, 0000 if 1
adc bl,0 ; if bl = 40, bl++
shl bx,1 ; word indexing...

mov bx,[_voltbl+bx] ; get the setting
;
; convert the volume settings into 0 - 100 % values
;
dec cx ; if 1, then it's loudness, enhanced
jle dogvo_loudness ; go do it...
mov al,12
sub cl,2 ; if LE,then it's BASS or TREBLE scale
jle dogvo_scale
mov al,40
;
; volume or equalizer settings
;
dogvo_scale:
xchg ax,bx ; ax = the current setting, bx the max
or ax,ax
jz dogvo_done
mov cx,100
mul cx
div bx
jmp short dogvo_done
;
; loudness and enhanced buttons
;
dogvo_loudness:
not ax
and al,1 ; al = 0 for volloud, 1 for enhanced
inc al ; al = 1 for volloud, 2 for enhanced

and al,bl ; al holds the bit
neg al ; set carry on zero
sbb al,al
and ax,100 ; ax = 0 for off, 100 for on
;
dogvo_done:
call rounduppct ; psuedo rounding
xchg ax,bx ; bx holds the result
jmp far_return_less_bx

dogetvolume endp

;
; /*\
;---|*|------------------------====================------------------------
;---|*|----------------------====< dosetvolume >====------------------------
;---|*|------------------------====================------------------------
;---|*|
;---|*| Set one of the volume register with stufff...
;---|*|
;---|*| Entry Conditions:
;---|*| BX holds the value
;---|*| CX holds the channel
;---|*|
;---|*| Exit Conditions:
;---|*| None
;---|*|
; \*/
;
.errnz VOLMUTE-40h ; volume devices have bit 6 set
.errnz BI_VOLENHANCE-BI_VOLLOUD-1 ; LOUDNESS=0, ENHANCED=1

public dosetvolume
dosetvolume proc far

push ax ; save everything
push bx
push cx
push dx
push ds

push cs ; all data is stored locally.
pop ds

sub bh,bh ; make sure its a low value
;
; convert the channel number to something close
;
or cl,40h ; add in the National device select bit
;
; volume settings
;
cmp cl,BI_VOLLEFT+40H
jz dovo_volume
cmp cl,BI_VOLRIGHT+40H
jnz dovo_notvolume
;
dovo_volume:
mov al,[_volleftmax] ; xlat 0-100% to the physical max
cbw
xchg ax,bx
or ax,ax
jz dovo_sendit
mul bx
mov bl,100
div bx
jmp short dovo_sendit
;
dovo_notvolume:
;
; bass/treble settings
;
cmp cl,BI_VOLBASS+40H
jz dovo_bass
cmp cl,BI_VOLTREBLE+40H
jnz dovo_not_basstreble
;
dovo_bass:
mov ax,12 ; xlat 0-100% to 0-12
xchg ax,bx
or ax,ax
jz dovo_sendit
mul bx
mov bl,100
div bx
jmp short dovo_sendit
;
dovo_not_basstreble:
;
; loudness and enhanced buttons
;
mov ax,0fe01h ; load a mask for stripping/saving bits
cmp cl,BI_VOLLOUD+40H ; loudness?
jz dovo_loudness ; yes, use it
cmp cl,BI_VOLENHANCE+40H ; enhanced?
jnz dovo_bad ; no, bomb out...
mov ax,0fd02h ; load a mask for stripping/saving bits
;
dovo_loudness:

or cl,01 ; calc the true volume register
.errnz (VOLLOUDENH AND 0BFh) - 1 ; make sure the above ADD works

and ah,byte ptr [_voltbl+2] ; get all other bits
.errnz VOLLOUDENH-41h ; must be entry #2

neg bl ; sets carry if not zero
sbb bl,bl ; bl = 0000 if 0, else ffff for 1
and al,bl
or al,ah ; al holds the new value
;
dovo_sendit:
sub ch,ch ; guarrenteed goodness...
sub ah,ah ; guarrenteed goodness...
mov bx,cx ; bx holds the channel, ax the value
xchg ax,bx ; for keeping the parameters straight...
call SetVolume
;
dovo_bad:
jmp far_return

dosetvolume endp

;
; /*\
;---|*|------------------===============================-----------------------
;---|*|----------------====< dorealsound/dofmsplit >======---------------------
;---|*|------------------===============================-----------------------
;---|*|
;---|*| Set/Get the realsound and FM bit settings
;---|*|
;---|*| Entry Conditions:
;---|*| BX holds the 0 for off, ~0 for ON
;---|*| CX holds 0 to get, 1 to set the bit
;---|*|
;---|*| Exit Conditions:
;---|*| BX may return the value
;---|*|
; \*/
;
public dorealsound
dorealsound proc far

push ax ; save everything
push cx
push dx
push ds

mov ah,bMIspkrint ; realsound bit

push cs ; all data is stored locally.
pop ds

push es
push di
les di,mvhwShadowPointer ; get the pointer
;
finish_common_code:
jcxz dogetrealorfm ; if not set, go get the bit

push bx ; since this is a SET we can't flush bx
;
; create an AND / OR mask to set/clear the bit
;
neg bx ; set carry if nonzero
sbb al,al ; al = 0000 for 0, ffff for 1
and al,ah ; al holds the OR bit mask
not ah ; ah holds the AND bit mask

mov dx,AUDIOMIXR

pushf
cli

and ah,es:[di._audiomixr] ; get all but the realsound/fm bit

or al,ah ; merge into the one bit

out dx,al
mov es:[di._audiomixr],al ; save the new bit

popf
pop bx ; restore bx
jmp short dogetrealdone
;
dogetrealorfm:

and ah,es:[di._audiomixr] ; get the bit
neg ah ; carry set if nonzero
sbb bx,bx ; bx = ffff for ~0, 0000 for 0
and bx,100 ; bx = 0 or 100%
;
dogetrealdone:
pop di
pop es
jmp far_return_less_bx

;
;----====< dofmsplit entrypoint >====----
;

dofmsplit label far
push ax ; save everything
push cx
push dx
push ds

mov ah,bMImonofm ; fm bit

push cs ; all data is stored locally.
pop ds
;
; check the different hardware versions.
;
push es
push di
les di,mvhwShadowPointer ; get the pointer

test es:[di._intrctlr],fICidbits ; rev 2+ ?
jz finish_common_code ; no, handle as PAS1
jcxz finish_common_code ; also, GETs will go that way...
;
; OPL3 board, so do the switch at the OPL3 chip
;
mov ax,bx ; save BX
neg ax ; set carry to turn off
sbb ax,ax ; al = 0 for OFF, FF for ON
and ah,1 ; ah = 0 for OFF, 01 for ON
xor ah,1 ; flip for the OPL 3
;
; maintain compatibility with the old mixer setup.
;
and al,bMImonofm ; save just that bit

and es:[di._audiomixr],NOT bMImonofm ; flush the bit
or es:[di._audiomixr],al ; maybe set the bit

mov al,05h
mov dx,RFMADDR ; write to the new bit in the OPL3
out dx,al ; index first...
inc dx

in al,dx ; waste time...
in al,dx ; waste time...

mov al,ah ; data next...
out dx,al

jmp short dogetrealdone ; exit via common code

dorealsound endp

;
; /*\
;---|*|----====< int SetCrossChannel >====----
;---|*|
;---|*| Output New Cross Channel Select Bits
;---|*|
;---|*| Entry Conditions:
;---|*| AX holds the channel bit
;---|*| DX holds the state (0 or -1)
;---|*| DS holds our data segment
;---|*|
;---|*| Exit Conditions:
;---|*| AX,DX modified.
;---|*| h/w channel select modified
;---|*|
; \*/

public SetCrossChannel
SetCrossChannel proc
push es
push di
les di,mvhwShadowPointer ; get the pointer

mov ah,al ; ah = AND mask al = XOR mask
not ah
and al,dl ; al = 00 to clear, else 0X to set

mov dx,CROSSCHANNEL

disable

and ah,es:[di._crosschannel]
xor al,ah
out dx,al
mov es:[di._crosschannel],al ; save the new value, & return the old

enable

pop di
pop es
ret

SetCrossChannel endp

;
; /*\
;---|*|----====< void SetFilter (int setting ) >===----
;---|*|
;---|*| This routine selects a filter setting from mute to high freq filter.
;---|*|
;---|*| Entry Conditions:
;---|*| AX is a value from 0 - 6
;---|*| DS holds our data segment
;---|*|
;---|*| Exit Conditions:
;---|*| None
;---|*|
;---|*|
; \*/

public SetFilter
SetFilter proc
push es
push di
les di,mvhwShadowPointer ; get the pointer

mov bx,ax
cmp bx,6 ; validate the user index
ja sfbadval

mov _filtidx,bl
mov ah,tableof[bx] ; grab an appropriate value
mov dx,AUDIOFILT ; and toss it out...

disable

mov al,es:[di._audiofilt] ; get the shadowed gates

and al,not (fFIdatabits+fFImutebits) ; save the non-filter bits
or al,ah ; merge in the filter bits
out dx,al

mov es:[di._audiofilt],al ; shadow it too...

enable
;
sfbadval:
pop di
pop es
ret

SetFilter endp

;
; /*\
;---|*|----====< void SetMixer >====----
;---|*|
;---|*| Set the selected channel within the MVA508 Mixers & Volume device
;---|*|
;---|*| Entry Conditions:
;---|*| AX holds the channel (0 - 15)
;---|*| BX holds the volume (0 - 31)
;---|*| CL holds the device: OUTPUTMIXER or INPUTMIXER or DEFMIXER
;---|*| DS holds our data segment
;---|*|
;---|*| Exit Conditions:
;---|*| AX,BX,CX,DX modified
;---|*|
; \*/

public SetMixer
SetMixer proc near
push es
push di
les di,mvhwShadowPointer ; get the pointer

xchg bx,ax ; bx = channel #, al = volume
mov ah,al ; ah = real, al = simulated

test [hardwaretype],bMVA508
jjz nsSetMixer
;
; we scale down the mixer settings to make them
; sound more like the National part.
;
mov ch,ah ; multiply the value by 1.25 We take
shr ch,1 ; the value, divide in half, then
shr ch,1 ; the value, divide in half, then
add ah,ch ; add it back in.
cmp ah,1Fh ; not maxed out, just use it...
jbe @F
mov ah,1Fh ; max it out...
;
@@:
;
; send out the mixer channel #
;
mov al,cs:mv510table[bx] ; get the true mixer channel
mov dx,pAUDIOMIXR ; parallel mixer address
out dx,al
;
; convert the mixer select from the National definition to the MVA508
;
.errnz OUTPUTMIXER ; must always be zero
.erre INPUTMIXER ; must always be non-zero
.erre pmINPUTMIXER ; must always be non-zero

neg cl ; CY if INPUT mixer, NC for OUTPUT mixer
sbb al,al
and al,pmINPUTMIXER ; al now holds the proper bit field
or al,ah ; real mixer channel volume level
out dx,al

mov es:[di._paudiomixr],al ; shadow it...
;
semi_exit:
pop di
pop es
ret
;
; National Semi Mixer setting code
;
nsSetMixer:
push ax ; save the channel setting
push cx ; save the INPUTMIXER/OUTPUTMIXER bits
;
; send out the mixer channel #
;
test cs:[hardwaretype],CDPC ; CDPC mixer L/R connections are
jz @F ; backwards...
mov bl,cs:[cdpcmixer+bx] ; We will straighten them out.
@@:
mov ah,bl ; restore the register #
or ah,080h ; OR 080h d7=channel #
or ah,cl ; merge in the mixer select
call lmix ; (lvol looks for 80h)
;
; send out the mixer data
;
pop cx ; get the INPUTMIXER/OUTPUTMIXER
pop ax ; get the channel setting in AH
or ah,cl ; merge in the mixer select
call lmix
;
_semi_exit:
pop di
pop es
ret
;
; This table reverses the mixer settings so left is now right & visa versa.
;
cdpcmixer label byte
db 15 ; nothing
db 08 ; Left FM
db 09 ; Left input mixer
db 10 ; Left External
db 11 ; Left Internal
db 12 ; Left Mic
db 13 ; Left PCM
db 14 ; Left Speaker

db 0 ; nothing
db 01 ; Right FM
db 02 ; Right Input Mixer
db 03 ; Right External
db 04 ; Right Internal
db 05 ; Right Mic
db 06 ; Right PCM
db 07 ; Right Speaker

pmADMI equ

mv510table label byte ;Ch #; PAS ; MVA508
db 07+pmCHANNELL+pmADMI ; 0 ; L_FREE; 7 - Now the SB connect
db 00+pmCHANNELL+pmADMI ; 1 ; L_FM ; 0
db 01+pmCHANNELL+pmADMI ; 2 ; L_Mix ; 1
db 02+pmCHANNELL+pmADMI ; 3 ; L_Ext ; 2
db 03+pmCHANNELL+pmADMI ; 4 ; L_Int ; 4
db 04+pmCHANNELL+pmADMI ; 5 ; L_Mic ; 3
db 05+pmCHANNELL+pmADMI ; 6 ; L_PCM ; 5
db 06+pmCHANNELL+pmADMI ; 7 ; L_Spk ; 6
db 00+pmCHANNELR+pmADMI ; 8 ; R_FM ; 0
db 01+pmCHANNELR+pmADMI ; 9 ; R_Mix ; 1
db 02+pmCHANNELR+pmADMI ; A ; R_Ext ; 2
db 03+pmCHANNELR+pmADMI ; B ; R_Int ; 4
db 04+pmCHANNELR+pmADMI ; C ; R_Mic ; 3
db 05+pmCHANNELR+pmADMI ; D ; R_PCM ; 5
db 06+pmCHANNELR+pmADMI ; E ; R_Spk ; 6
db 07+pmCHANNELR+pmADMI ; F ; R_FREE; 7 - Now the SB connect

SetMixer endp

;
; /*\
;---|*|----====< void SetVolume >====----
;---|*|
;---|*| This routine outputs a new setting for a volume channel.
;---|*|
;---|*| Entry Conditions:
;---|*| AX is a register select value from 0 - 7
;---|*| BX is a value to be written to the control
;---|*| DS points to our data segment
;---|*|
;---|*| Exit Conditions:
;---|*| AX,BX,CX,DX may be modified
;---|*|
; \*/

public SetVolume
SetVolume proc
push es
push di
les di,mvhwShadowPointer ; get the pointer

test [hardwaretype],bMVA508
jjz _nsSetVolume

xchg ax,bx ; ax = data, bx = channel

and bl,10111111b ; knock out the Volume ID bit

shl bx,1
mov _voltbl[bx],ax ; save this channel's volume setting
shr bx,1 ; restore bx & go

mov ah,cs:mva508vol[bx] ; get the channel number for the MVA508
xchg ah,al ; al = channel, ah = data
cmp al,-1 ; is there an actual channel?
jz sevo_exit ; no, just skip it...
;
; special case the loudness and enhanced stereo switches for the MVA508
;
cmp bl,VOLLOUDENH AND 7 ; volume enhance switches?
jnz @F ; no, continue on...
shr ah,1 ; yes, swap the bit positions
sbb dl,dl
and dl,2

or ah,dl
shl ah,1
;
@@:
mov dx,pAUDIOMIXR ; parallel audio mixer interface
out dx,al ; write the index
xchg ah,al ; al = data, ah = channel
out dx,al ; write the data

mov es:[di._paudiomixr],al ; shadow it...
;
sevo_exit:
pop di
pop es
ret
;
; National Semi's total volume control
;
_nsSetVolume:
xchg ax,bx ; ax = data, bx = channel

and bl,10111111b ; knock out the Volume ID bit
shl bx,1

mov _voltbl[bx],ax ; save this channel's volume setting

shr bx,1 ; restore bx & go
or bl,01000000b ; replace the Volume ID bit
mov ah,al ; ah = data
call lvol ; call the read hardware routine

jmp short sevo_exit

;
; MVA580 mixer channel assignments
;
mva508vol label byte ; ch # ; PAS ; MVA508
db 02+pmADDRSELECT+pmCHANNELLR; 0 ; MUTE ; VOLB
db 05+pmADDRSELECT ; 1 ; LOUD/ENH ; 5
db 03+pmADDRSELECT ; 2 ; BASS ; 3
db 04+pmADDRSELECT ; 3 ; TREBLE ; 4
db 01+pmADDRSELECT+pmCHANNELL ; 4 ; L-VOL ; 1,CC=01
db 01+pmADDRSELECT+pmCHANNELR ; 5 ; R-VOl ; 1,CC=02
db -1 ; 6 ; MODE ; N/A
db -1 ; 7 ; N/A ; N/A

SetVolume endp

;
;---------------------====================================---------------------
;---------------------====< Local Subroutine Section >====---------------------
;---------------------====================================---------------------
;

; /*\
;---|*|----====< FetchMixLevel >====----
;---|*|
;---|*| Remap mixer level from 0-31 into 0-12
;---|*|
;---|*| Entry Conditions:
;---|*| AX holds the volume level - guarrenteed to be 0 - 31
;---|*| DS holds our data segment
;---|*|
;---|*| Exit Condtions:
;---|*| CY = 1, invalid volume level.
;---|*| CY = 0, good volume level.
;---|*| AH holds the converted value.
;---|*| AL the orig index.
;---|*|
; \*/

public FetchMixLevel
FetchMixLevel proc near
;
; ignore if the new mixer
;
mov ah,al ; ah may return the whole thing
test [HardwareType],bMVA508 ; if this is the MVA508
jnz @F
;
; do the converion for the old mixer
;
push bx
push dx
push ax
;
; remap 0-31 into 0-12: index = (index * 10) / 2.5
;
mov dl,10
mul dl
mov dl,25
div dl ; effectely divides 31 by 2.5
;
; read the table of hosed values
;
mov bl,al
sub bh,bh

pop ax ; al holds the original index
mov ah,mixersettings[bx] ; ah holds the real setting

pop dx
pop bx
;
@@:
clc
ret

FetchMixLevel endp


; /*\
;---|*|----====< lmix >====----
; \*/

;
; -- Load The Mixer
;
; Entry Conditions
; es:di point to the state table
; ah = index/data
; al = to mixer:
; al = INPUTMIXER (04h) - goes to input mixer
; al = OUTPUTMIXER (08h) - goes to output mixer
;
; Exit Conditions:
; No registers modified
;
public lmix
lmix proc near
push ax
push cx
push dx

disable ; make sure we clock in all data
;
; all clocks and ; strobes should be 1
;
mov dx,AUDIOMIXR ; load mixer port

mov al,es:[di._audiomixr] ; save the realsound & dual fm bits

and al,bMIspkrint+bMImonofm
or al,not (bMIspkrint+bMImonofm)

mov cx,8 ; load bit count
out dx,al ; set initial clocks

@@: xor al,bMIclock ; toggle output mixer clock
pause
out dx,al ; output clock is zero
ror al,1 ; make room for data
ror ah,1 ; get bit of data
adc al,al ; shift into register
out dx,al ; send data to mixer
xor al,bMIclock ; toggle clock again
pause
out dx,al ; clock sould be 1 now
loop @B ; do all 8 bits this way

xor al,bMImistrb ; toggle strobe low
out dx,al ; which loads data into mixer
pause
xor al,bMImistrb ; toggle strobe high
out dx,al ; loading complete

mov es:[di._audiomixr],al ; save the last state

enable

pop dx
pop cx
pop ax
ret

lmix endp

;
; /*\
;---|*|----====< lvol >====----
; \*/

;
; lvol -- Load Volume control Register
;
; Entry Conditions:
; bl = parameter register (volume control channel 0-7)
; ah = data to transfer (new channel setting)
;
; Exit Conditions:
; AX,BX,CX,DX modified
;
public lvol
lvol proc near
;
; pass everything, but left/right volume directly to the device
;
cmp bl,44h ; left & right volume are presented
jz @F ; to the logical level as 0 - 40,
cmp bl,45h ; where 0 is the lowest, and 40 is
jnz lvolout ; the highest. In reality, this
@@: sub ah,40 ; is backwards, that is, 40 is the
neg ah ; lowest, and 0 is the highest. We will
; ; correct the value here...
; Perform the volume control output
;
lvolout label near
mov dx,AUDIOMIXR ; load mixer port
;
; all 1s but volume enable and clock
;
mov al,es:[di._audiomixr] ; save the realsound & dual fm bits

and al,bMIspkrint+bMImonofm
or al,not (bMIspkrint+bMImonofm+bMIvol+bMIclock)

disable ; do it all at once, no interruptions

out dx,al ; set initial clocks
mov cx,8 ; load bit count for address
@@: xor al,bMIclock ; toggle volume control clock
ror al,1 ; make room for data
ror bl,1 ; get bit of data
adc al,al ; shift into register
out dx,al ; send data to volume control
pause
xor al,bMIclock ; toggle the clock again
out dx,al ; clock should be 1 now
loop @B ; do all 8 bits this way...
xor al,bMIvol ; toggle volume control enable
pause
out dx,al ; which starts data loading
mov cl,8 ; load bit count for data
@@: xor al,bMIclock ; toggle volume control clock
ror al,1 ; make room for data
ror ah,1 ; get bit of data
adc al,al ; shift into register
out dx,al ; send data to volume control
pause
xor al,bMIclock ; toggle clock again
out dx,al ; clock should be 1 now
loop @B ; doall a 8 bits this way...
pause
mov cl,12 ; load bit count for data
@@: out dx,al ; need to stall for 6 microseconds
pause
loop @B ; waiting...
xor al,bMIvol ; toggle volume control enable
out dx,al ; to prepare
pause
out dx,al ; need to stall
pause
out dx,al
pause
out dx,al
pause
xor al,bMIvol ; toggle volume control enable
out dx,al ; which loads data

mov es:[di._audiomixr],al ; save the last state

enable

ret

lvol endp

;
;----====< rounduppct >====----
;
rounduppct proc near
cmp al,0 ; skip out if less than zero
jle @F
cmp al,100 ; skip out if greater/equal to 100
jge @F
inc al
;
@@:
ret

rounduppct endp


; /*\
;---|*|----====< end of MIXERS.ASM >====----
; \*/

end



  3 Responses to “Category : Music and Digitized Voice
Archive   : PAS-SDK1.ZIP
Filename : MIXERS.ASM

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/