;$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	<pmADDRSELECT+pmMIXERSELECT>

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

