
;	/*\
;---|*|----====< MIXERC.C >====----
;---|*|
;---|*| Initialize and setup the access to the mixers, volume, filter, etc
;---|*|
;---|*| Copyright (c) 1991, Media Vision, Inc. All rights reserved
;---|*|
;	\*/

#include <stdio.h>
#include <stdlib.h>
#include "mixbin.h"                 // the actual mixer code
#include "state.h"                  // the state structure

;   /*\
;---|*|---------------====< Programming Caveat >====--------------\
;---|*|                                                           |
;---|*| This code is provided for documentation ONLY. It has been |
;---|*| replaced by MIXERC.C. Please use the newer body of code   |
;---|*| for all your development, thus avoiding hardware depend-  |
;---|*| encies. 												  |
;---|*|                                                           |
;---|*|---------------====< Programming Caveat >====--------------/
;	\*/

		int  ThePASDMAChannel = 0;	// DMA channel
		int  ThePASIRQChannel = 0;	// IRQ channel

		static int dummyroutine 	   ();

		int (far *MVSetMixerFunction)  () = 0;
		int (far *MVSetVolumeFunction) () = 0;
		int (far *MVSetFilterFunction) () = 0;
		int (far *MVSetCrossChannel)   () = 0;
		int (far *MVGetMixerFunction)  () = 0;
		int (far *MVGetVolumeFunction) () = 0;
		int (far *MVGetFilterFunction) () = 0;
		int (far *MVGetCrossChannel)   () = 0;
		int (far *MVRealSoundSwitch)   () = 0;
		int (far *MVFMSplitSwitch)	   () = 0;

#define STATICDRIVER	0x0000		// The static driver is loaded.
#define DOSDRIVER		0x0001		// The DOS driver is loaded.
#define DISKDRIVER		0x0002		// The Disk resident driver is loaded.

        extern struct MVState far *mvhwShadowPointer;
		static struct MVState far *(far *MVMixerHWStateFunction)()	   = 0;
			   struct MVState far *MVMixerHWState(struct MVState far *);

////
////;	/*\
////;---|*|----====< test code >====----
////;---|*|
////;---|*| this is test code to be removed
////;---|*|
////;	\*/
//// int main()
//// {
////
////	// In each of the following calls, MVInitMixerCode will search for
////	// the DOS driver, "MVSOUND.SYS". If found, all succeeding calls to
////	// the mixers will be routed through MVSOUND.SYS. If the DOS driver
////	// is not found, then either the DISK based version, or the static
////	// version will be used. To select the DISK based version, pass a
////	// path to MVInitMixerCode. Any pointer (other than a NULL) will tell
////	// the routine to load the driver from the disk. NOTE: If loading from
////	// the disk fails, then the static driver will be used.
////
////	//
////	// EXAMPLE #1 - The following will attempt to
////	//				load the driver from the disk.
////
////		MVInitMixerCode ( "." );
////
////	//
////	// EXAMPLE #2 - the following will use the
////	//				static built-in driver.
////
////		MVInitMixerCode ( 0 );
//// }
////

;	/*\
;---|*|----====< MVInitMixerCode >====----
;---|*|
;---|*| Load link to the DOS driver, or load the mixer code
;---|*|
;---|*| Entry Conditions:
;---|*| 	char *path is the path to the disk resident driver.
;---|*|
;---|*| Return Value:
;---|*| 	 0, the mixer code is using the static driver.
;---|*| 	 1, the mixer code is using DOS driver.
;---|*| 	 2, the mixer code is using Disk resident driver.
;---|*|
;	\*/
int MVInitMixerCode(path)
	char *path;
{
char *p,*p1;
int n,mx,tablelen;
long far *bfp;			// buffer far pointer
long far *tfp;			// table far pointer
long mvver; 			// MVSOUND.SYS version #

static int retry = 0;	// reentry flag. We will only execute once
static int retcode = 0;

	// exit if this code has already been executed

		if (retry++)
			return(retcode);

    // flush some variables

		mx = 0;

	_asm {

		; fixup the linked in code to be on a paragraph alignment

            push    es
			push	di
			push	si

			mov 	bx,ds
			mov 	es,bx

			mov 	di,offset code_block	; get the start of the block
			mov 	si,17					; # of leading zeros in code_block
			add 	si,di					; si points to the start
			and 	di,0xfff0				; knock out the low nibble
			add 	di,0x10 				; move back up to a starting place

            sbb     ax,ax                   ; overflow carries into the segment
			and 	ax,1000h
			add 	bx,ax

			mov 	ax,di					; calculate a new segment:offset
			mov 	cl,4
			shr 	ax,cl
			add 	bx,ax					; address is bx:0000

            mov     cx,2048/2
			cld
			rep movsw

			mov 	[bfp+0],cx				; save the new offset
			mov 	[bfp+2],bx				; save the new segment

			call	far ptr [bfp]			; go get the table pointer.

			mov 	[tfp+0],ax				; save the function table ptr
			mov 	[tfp+2],dx
			mov 	byte ptr ThePASDMAChannel,bl	; save the DMA channel
			mov 	byte ptr ThePASIRQChannel,cl	; save the IRQ channel

			pop 	si
			pop 	di
			pop 	es

		}

		_asm {

		; for small model, we have to update the segments

			mov 	bx, offset dummyroutine
			mov 	word ptr [MVSetMixerFunction+0],bx
            mov     word ptr [MVSetMixerFunction+2],cs

			mov 	word ptr [MVSetVolumeFunction+0],bx
            mov     word ptr [MVSetVolumeFunction+2],cs

			mov 	word ptr [MVSetFilterFunction+0],bx
            mov     word ptr [MVSetFilterFunction+2],cs

			mov 	word ptr [MVSetCrossChannel+0],bx
            mov     word ptr [MVSetCrossChannel+2],cs

			mov 	word ptr [MVGetMixerFunction+0],bx
            mov     word ptr [MVGetMixerFunction+2],cs

			mov 	word ptr [MVGetVolumeFunction+0],bx
            mov     word ptr [MVGetVolumeFunction+2],cs

			mov 	word ptr [MVGetFilterFunction+0],bx
            mov     word ptr [MVGetFilterFunction+2],cs

			mov 	word ptr [MVGetCrossChannel+0],bx
            mov     word ptr [MVGetCrossChannel+2],cs

			mov 	word ptr [MVRealSoundSwitch+0],bx
            mov     word ptr [MVRealSoundSwitch+2],cs

			mov 	word ptr [MVFMSplitSwitch+0],bx
			mov 	word ptr [MVFMSplitSwitch+2],cs

        ; deterine if the DOS driver is loaded

			mov 	ax,0xbc00				; function 0 is for ID
			mov 	bx,0x3f3f
			sub 	cx,cx
			sub 	dx,dx
			int 	2fh 					; go get it...
			xor 	bx,cx					; combine all registers
			xor 	bx,dx
			cmp 	bx,0x4d56				; to form 'MV'
			jnz 	mvininodosdriver

			mov 	ax,0xbc01				; get the version
			int 	2fh

			mov 	word ptr [mvver+0],bx	; save the version
			mov 	word ptr [mvver+2],cx

		; the DOS driver is loaded, use it's vectors, data, etc

			sub 	cx,cx					; comes back 0 or the entry length
			mov 	ax,0xbc03				; get the vector table
			int 	0x2f					; from the DOS driver

			mov 	word ptr [tfp+0],bx 	; save the function pointer offset
			mov 	word ptr [tfp+2],dx 	; and segment
			mov 	[tablelen],cx

			mov 	ax,0bc04h				; get the DMA & IRQ
			int 	0x2f					; from the DOS driver

			mov 	byte ptr ThePASDMAChannel,bl	; DMA channel
			mov 	byte ptr ThePASIRQChannel,cl	; IRQ channel

          mvininodosdriver:

		}

	// if the above code did not find the DOS driver, we will attempt to
	// load the DOS mixer driver program located in the caller's path.

        if (retcode != DOSDRIVER) { // no dos driver try the disk version

			if (path) { 			// if there is a path, try to load it

				// copy the path to working storage

					p1 = p = &code_block[2048-64];// build it at the end of buffer
					while ((*p++ = *path++) != 0);

				// try to append a '\' to the path

					if (*(p-2) != '\\')
						*(p-1) = '\\';

				// move the file name

					n = 0;
					while ((*p++ = "mixer.drv"[n++]) != 0) ;

				// attempt to open the file and read in the driver

					_asm {

						mov 	dx,[p1] 		; ds:dx points to the file
						mov 	ax,0x3d00		; open a file for reading
						int 	0x21

						jc		nofilehere		; skip out if not found

						push	ds				; save...

                        lds     dx,bfp          ; get the storage pointer
                        mov     bx,ax
						mov 	cx,2048 		; our max size
						mov 	ah,0x3f
						int 	0x21			; load the driver

						pop 	ds				; ...restore

						pushf

						mov 	ah,0x3e 		; close the file
						int 	0x21

						popf					; get the success flag
						jc		nofilehere

						call	far ptr bfp 	; init the code

						mov 	[tfp+0],ax		; save the function table ptr
						mov 	[tfp+2],dx

						mov 	byte ptr ThePASDMAChannel,bl ; DMA channel
						mov 	byte ptr ThePASIRQChannel,cl ; IRQ channel

						mov 	retcode,DISKDRIVER ; the disk driver is loaded.

					nofilehere:

					}
			}
        }

		/* check the static driver to see if we can change the state pointer */

		_asm {

			push	es

			les 	bx,bfp
			inc 	bx
            inc     bx
			cmp 	es:[bx+1],0
			jz		nostatefunc 			; no, skip the pointer save
			mov 	[MVMixerHWStateFunction+0],bx ; pointer to state table fetch
			mov 	[MVMixerHWStateFunction+2],es ; or set function
		;
		nostatefunc:
			pop 	es

        }

	// we have the pointer to some table. Now, load each function pointer into
	// the individual far pointers.

		if (mvver > 0x30313032) 		// ASCII version 0102
			retcode = DOSDRIVER;		// indicate the DOS driver is loaded

	// set the first 4 (1st rev of MVSOUND just had 4 functions)

        (long) MVSetMixerFunction  = (long) *tfp++;
		(long) MVSetVolumeFunction = (long) *tfp++;
		(long) MVSetFilterFunction = (long) *tfp++;
		(long) MVSetCrossChannel   = (long) *tfp++;

	// set the next entries (later revs of MVSOUND had 10 functions)

		if (tablelen > 4) {
			(long) MVGetMixerFunction  = (long) *tfp++;
			(long) MVGetVolumeFunction = (long) *tfp++;
			(long) MVGetFilterFunction = (long) *tfp++;
			(long) MVGetCrossChannel   = (long) *tfp++;
			(long) MVRealSoundSwitch   = (long) *tfp++;
			(long) MVFMSplitSwitch	   = (long) *tfp;
		}

    // setup the local drivers state table pointer

		if (retcode != DOSDRIVER)
			MVMixerHWState(mvhwShadowPointer);

	// return the code

	return retcode;
}


;	/*\
;---|*|----====< MVMixerHWState >====----
;---|*|
;---|*| fetch and/or load a pointer to the state table.
;---|*|
;---|*| Entry Conditions:
;---|*| 	MVState far *  --  pointer to another table. If NULL (0),
;---|*| 					   the current pointer is only returned. Only
;---|*| 					   non-zero table pointers will be loaded.
;---|*| Return Value:
;---|*| 	 Returns the pointer to the table currently
;---|*| 	 used by the mixer code.
;---|*|
;	\*/
struct MVState far *MVMixerHWState(tbl)
	struct MVState far *tbl;
{

	if (MVMixerHWStateFunction) {	// if there is a function, call it
		_asm {
			mov 	ax,[tbl+0]
			mov 	dx,[tbl+2]
			call	far ptr [MVMixerHWStateFunction]
			mov 	[tbl+0],ax
			mov 	[tbl+2],dx

		}
	}
	return (tbl);
}


;	/*\
;---|*|----====< static dummyroutine >====----
;---|*|
;---|*| This routine no-ops out the function calls in case MVSOUND.SYS
;---|*| is not loaded.
;---|*|
;---|*| Entry Conditions:
;---|*| 	none
;---|*|
;---|*| Return Value:
;---|*| 	 0
;---|*|
;	\*/

static int dummyroutine ()
{
	return 0;
}

	/*\
;---|*| End of MIXERC.C
	\*/

