developer-toolkit-v1.01/installed/WAVE.C

UTF-8 rendering of a DOS-encoded (CP437 / CRLF) file. Byte-for-byte original: WAVE.C.


/*
	WAVE.C

	MMA Sampling driver.

	Marc Savary, sept-91
*/

/***************************************************************************
 										Includes
 ***************************************************************************/
#include "stdio.h"
#include "stdlib.h"
#include "dos.h"

#include "global.h"
#include "dma.h"
#include "control.h"
#include "interr.H"
#include "wave.h"

/***************************************************************************
 										defines
 ***************************************************************************/

#define MAJOR_VER		0
#define MINOR_VER		1

#define DEVICE_CLOSED	1
#define DEVICE_INPUT	2
#define DEVICE_OUTPUT	4

#define STATUS_WORKING		0	/* device currently working */
#define STATUS_STOPPED		1	/* device stopped by user */
#define STATUS_PAUSED		2	/* device paused by user */
#define STATUS_OUT_OF_BUFFER 3	/* no more buffer while playing/recording */

#define WAVE_OUT_MAGIC		23917	/* magic number output */
#define WAVE_IN_MAGIC		23918	/* magic number input */

#define PLAY_FIFO_SELECT	5		/* flag enabled when less than 32 bytes in fifo */
#define PLAY_FIFO_SIZE		96		/* transfer size, play-back */
#define REC_FIFO_SELECT	3		/* interrupt when more than 64 bytes in FIFO */
#define REC_FIFO_SIZE		64		/* transfer size, record */

#define BREAK_LOOP		1
#define NO_BREAK		0

#define DEF_VOLUME		0xffffffffL
#define DEF_STEREO	WAVE_STEREO_CENTER

/***************************************************************************
 										macros
 ***************************************************************************/

#undef inportb
#undef outportb

#ifdef DRVRES
void        	_Cdecl outportb( int __portid, unsigned char __value );
unsigned char 	_Cdecl inportb( int __portid );

#define	outp(a,d)				outportb(a,(unsigned char)d)	
#define	inp(p)					inportb(p)		
#endif

#define IntOff()	{ _asm{ pushf } \
					_asm{ cli } \
					 }
#define IntOn()	{ _asm { popf }}

#define ResetSampling( n) { \
	WriteMMA( n, 9, 0x80);	\
	inp( 0x21);	/* delay */	\
	inp( 0x21);				\
	WriteMMA( n, 9, 0);		\
	}


/***************************************************************************
 										Structures
 ***************************************************************************/


typedef
	struct {
		Word	wMagic;			/* magic number to verify validity */
		Word deviceId;			/* the first entry of array is 0,
									the second entry is 1 */
		Word wDeviceMode;		/* device mode:
									DEVICE_CLOSED		closed
									DEVICE_INPUT		open for input
									DEVICE_OUTPUT		open for output
			
					*/
		Word wDeviceStatus;		/* device status:
								STATUS_WORKING device currently working
								STATUS_STOPPED device stopped by user
								STATUS_PAUSED device paused by user
								STATUS_OUT_OF_BUFFER no more buffer while playing/recording
								*/

		Word	format;			/* waveform format: 
									WAVE_FORMAT_ADPCM4
									WAVE_FORMAT_PCM8
									WAVE_FORMAT_PCM12
									WAVE_FORMAT_PCM16
								*/
		Word	stereo;			/* mono = 0, stereo = 1 */
		Word	freqCode;		/* frequency select #, from 0 to 3 incl. */
		Word	transferMode;	/*
									WAVE_TRANSF_POLLING
									WAVE_TRANSF_INTERRUPT
									WAVE_TRANSF_DMA
								*/
		Word	leftRight;		/* WAVE_STEREO_LEFT,
									WAVE_STEREO_CENTER,
									WAVE_STEREO_RIGHT
								*/

		int (far * dwCallBack)();	/* user call back procedure */
		DWord dwCallBackData;	/* user data for call back proc. */
		Word	wDmaNr;			/* if DMA mode, DMA channel # to use */

		LpWaveHdr lpBlockList;	/* list of data block to be processed */
		LpWaveHdr lpFrstLpBlk;	/* first looping block ptr */
		Word	wLoopCount;		/* loop down counter */
		Word	started;		/* flag to indicate that PCM/ADPCM has been started */
		long	bytesToProcess;	/* # of byte still to play/record for the
									current block */
		WaveDataP processData;	/* ptr to current data in play/record buffer */
		DWord	dmaTrSize;		/* last programmed DMA transfer size */
		DWord	dwVolume;		/* wave volume: high word == left channel,
									low word == right channel;
									if mono, only right channel is defined */
		DWord	position;		/* the total byte count processed since Open()... */
	} WDev, far * DevicePtr;

/***************************************************************************
 										Variables
 ***************************************************************************/


static WDev	near waveDevices[ 2];

static int int_index;				/* audio interrupt # (Gold #), -1 if none */
static unsigned char oldIrqMask;
static unsigned intCAddr;
static int near intAT;
static unsigned near mma_io;
static unsigned near ctrl_io;
static unsigned char mmaVolL;		/* MMA mixer volume */

static unsigned char near reg9[ 2], near regA[ 2], near regC[ 2];


/* code interne du MMA pour les frequence d'echantillonage en fonction
	du mode PCM ou ADPCM; -1 signifie que la combinaison est impossible */
								  /* 44  22  11   7   5   Khz */
static signed char freqsAdpcm[] = { -1,  0,  1,  2,  3 };		/* ADPCM */
static signed char freqsPcm[] =   {  0,  1,  2,  3, -1 };		/* PCM */



	/* harware interrupt #: */
static char ints[] = { 3, 4, 5, 7, 10, 11, 12, 15 };
	/* hardware interrupt mask for 8259 controler: */
static unsigned char intMasks[] = { 1 << 3, 1 << 4, 1 << 5, 1 << 7,
							1 << 10 -8, 1 << 11 -8, 1 << 12 -8, 1 << 15 -8};
	/* software interrupt #: */
static unsigned char vecs[] = { 11, 12, 13, 15, 0x72, 0x73, 0x74, 0x77 };

static	void (interrupt far * oldVect)();	/* vecteur intial */
static int volatile dmaStatus;


/***************************************************************************
							Function prototypes
 ***************************************************************************/
static CallbackProc	IrqAudioProc();
static void StopSampling(DevicePtr dev);
static void OutOfBuffSampling(DevicePtr dev);
static void PauseSampling(DevicePtr dev);
static void	StartPlaying(DevicePtr dev);
static void PlayPolling(DevicePtr dev);
static void PlayBlockPolling(DevicePtr dev);
static void StartPlayRecInt(DevicePtr dev, int play);
static void	RecordPolling(DevicePtr dev);
static void RecordBlockPolling(DevicePtr dev);
static int FindFreqCode(DWord freq);
static void PrepareForSampling(int channel, int freq, int play, int format, 
								       int dmaMode, int stereo);
static void TerminateSampling(DevicePtr dev);
static void DoInputInterrupt(DevicePtr dev);
static void	DoPlayInterrupt(DevicePtr dev);
static int DoDataDMA(DevicePtr dev);
static void	GoToNextBlock(DevicePtr dev, int breakFlag);
static void WriteMMA( int channel, int reg, int data);
static int  ReadMMA(int channel, int reg);
static int 	ReadStatusMM1(void );
static int	FIFO_int(Word wFifoNr);

/***************************************************************************
 										Implementation
 ***************************************************************************/

/*
	To be called once in order to execute low level Wave Driver
	initialisations.
*/
WORD InitWaveDriver()
	{
	unsigned char byte;
	int dmaNr;

   /*
    *  We must tell the control chip driver what is our entry routine
    *  address.
    */

	SetDriverCallback(ADLIB_WAVE_DRIVER_ID, IrqAudioProc);

	/* 
	 * Now, lets initialize our stuff 
	 */

	dmaStatus = 0;

	mma_io = CtGetRelocationAddress() + 4;
	if (mma_io == 4) mma_io = 0x38c;

	WriteMMA( 0, 8, 0x70);		/* STAND-BY off, T2 T1 T0 masked */
	WriteMMA( 0, 0xD, 0x35);	/* mask all in D register */

	ResetSampling( 0);
	ResetSampling( 1);

	inp( mma_io);				/* clear status flags */

	regC[ 0] = 0;
	regC[ 1] = 0;
	WriteMMA( 0, 0xC, regC[ 0]);		/* clear channel 0 register C */
	WriteMMA( 1, 0xC, regC[ 1]);		/* clear channel 1 register C */

	reg9[ 0] = 0x60;			/* device 0 au centre */
	reg9[ 1] = 0x60;			/* device 1 au centre */
	WriteMMA( 0, 9, reg9[ 0]);
	WriteMMA( 1, 9, reg9[ 1]);
	WriteMMA( 0, 10, 0xff);		/* volume maximum */
	WriteMMA( 1, 10, 0xff);		/* volume maximum */

	waveDevices[ 0].wDeviceMode = DEVICE_CLOSED;
	waveDevices[ 0].wDeviceStatus = STATUS_STOPPED;
	waveDevices[ 0].deviceId = 0;
	waveDevices[ 0].wMagic = 0;

	if (CtGetEnabDisabDMA0SampChan()) {
		dmaNr = CtGetDMA0ChannelSampChan();
		}
	else dmaNr = -1;

	waveDevices[ 0].wDmaNr = dmaNr;

	waveDevices[ 1].wDeviceMode = DEVICE_CLOSED;
	waveDevices[ 1].wDeviceStatus = STATUS_STOPPED;
	waveDevices[ 1].deviceId = 1;
	waveDevices[ 1].wMagic = 0;

	if (CtGetEnabDisabDMA1SampChan()) {
		dmaNr = CtGetDMA1ChannelSampChan();
		}
	else dmaNr = -1;

	waveDevices[ 1].wDmaNr = dmaNr;
   return(0);
	}



/*
	To be called before returning to DOS if InitWaveDriver() has
	been called.
*/
WORD CloseWaveDriver()
	{
	ResetSampling( 0);
	ResetSampling( 1);
	ResetDriverCallback(ADLIB_WAVE_DRIVER_ID);
   return(0);
	}


/*
	This routine is called by hardware (interrupt) to manage interrupt
	dispatching of Adllib Gold Card
*/

static CallbackProc	IrqAudioProc()
	{
	unsigned char 	mmaStatus;

	_asm	mov		mmaStatus, bl
   _asm	push	ds
	_asm	push	es

	_asm 	mov		ax, ds
	_asm 	mov		es, ax

	if (mmaStatus & 0x01) FIFO_int(0);
	else if(mmaStatus & 0x02)  FIFO_int(1);

	_asm pop es
   _asm pop ds
	}




/*
	This function open a specified waveform output device for playback.

	Use 'WaveOutGetNumDevs()' to determine the number of waveform output
	devices present in the system. The device id specified by 'wDeviceId'
	varies from 0 to one less than the number of devices present.

	The format of the call back function 'dwCallBack()' is
	"int CallBack( HWaveOut dev, LpWaveHdr block, DWord dwCallBackData)".
	This function is called by the driver each time it has finished
	with a buffer.
*/
Word WaveOutOpen( lphWaveOut, wDeviceId, lpFormat, dwCallBack,
					dwCallBackData, dwFlags)
	HWaveOut far * lphWaveOut;
	Word wDeviceId;		/* device number (channel number): 0 to 1 */
	LpWaveFormat lpFormat;
	CallbackOutPtr dwCallBack;
	DWord dwCallBackData;
	DWord dwFlags;	/* flags for opening device:
						WAVE_FORMAT_QUERY:
							if this flag is specified, the device driver will
							determine if it supports the given format, but will
							not actually open the device. */
	{
	DevicePtr dev;
	unsigned freq;
	int code;

	if( wDeviceId > 1)
		return WERR_BADDEVICEID;
	dev = &waveDevices[ wDeviceId];
	if( dev->wDeviceMode != DEVICE_CLOSED)
		return WERR_ALLOCATED;
	if( lpFormat->nChannels > 1)
		if( wDeviceId != 0)
			return WERR_STEREOBADCHANNEL;
		else if( waveDevices[ 1].wDeviceMode != DEVICE_CLOSED)
			return WERR_STEREONEED2FREECHNL;

	freq = FindFreqCode( lpFormat->samplingFreq);
	if( freq > WAVE_LAST_FREQ)
		return WERR_UNSUPPORTEDFORMAT;
	dev->format = lpFormat->wFormatTag;
	if( (unsigned)dev->format > WAVE_LAST_FORMAT)
		return WERR_UNSUPPORTEDFORMAT;
	dev->stereo = lpFormat->nChannels > 1 ? 1 : 0;
	if( dev->format == WAVE_FORMAT_ADPCM4)
		code = freqsAdpcm[ freq];
	else
		code = freqsPcm[ freq];
	if( code == -1)
		return WERR_UNSUPPORTEDFORMAT;
	if( lpFormat->wTransMode > WAVE_LAST_TRANSF)
		return WERR_BADTRANSFERMODE;
	if( dwFlags & WAVE_FORMAT_QUERY)
		return 0;	/* Just return succes code */

	dev->transferMode = lpFormat->wTransMode;
	dev->freqCode = code;
	dev->wDeviceMode = DEVICE_OUTPUT;
	dev->wDeviceStatus = STATUS_OUT_OF_BUFFER;
	dev->dwCallBack = dwCallBack;
	dev->dwCallBackData = dwCallBackData;
	dev->lpBlockList = NULL;
	dev->wLoopCount = 0;
	dev->wMagic = WAVE_OUT_MAGIC;
	dev->dmaTrSize = 0;
	dev->position = 0;

	WaveOutSetVolume( dev, DEF_VOLUME);
	WaveOutSetLeftRight( dev, DEF_STEREO);
	*lphWaveOut = dev;
	return 0;
	}




/*
	This function closes the specified waveform output device.

	If the device is still playing a waveform, the close operation will fail.
	Use WaveOutReset() to terminate waveform playback before calling 
	WaveOutClose().
*/
Word WaveOutClose( hWaveOut)
	HWaveOut hWaveOut;
	{
	DevicePtr wDev;

	wDev = hWaveOut;
	if( wDev->wMagic != WAVE_OUT_MAGIC)
		return WERR_INVALIDHANDLE;
	if( wDev->lpBlockList != NULL)
		return WERR_STILLPLAYING;

	wDev->wDeviceMode = DEVICE_CLOSED;
	wDev->wMagic = 0;
	return 0;
	}



/*
	This function retrieves the number of waveform output devices present
	in the system.
*/
Word WaveOutGetNumDevs()
	{
	return 2;
	}



/*
	This function sends a data block to the specified waveform
	output device.

	Unless the devices is paused by calling 'WaveOutPause()', playback
	begin when the first data block is esnt to the device.
*/
Word WaveOutWrite( hWaveOut, lpWaveOutHdr, wSize)
	HWaveOut hWaveOut;		/* Handle to a opened waveform device */
	LpWaveHdr lpWaveOutHdr;	/* far ptr to a 'WaveHdr' structure containing
								information about the data block */
	Word wSize;			/* specifies the size of the 'WaveHdr' structure */
	{
	DevicePtr dev;
	LpWaveHdr prev, wave;

	(void)wSize;

	dev = hWaveOut;
	if( dev->wMagic != WAVE_OUT_MAGIC)
		return WERR_INVALIDHANDLE;

	IntOff();
	if( dev->lpBlockList == NULL) {
		dev->lpBlockList = lpWaveOutHdr;
		dev->bytesToProcess = dev->lpBlockList->dwBufferLength;
		dev->processData = dev->lpBlockList->lpData;
		}
	else {
       for( prev = wave = dev->lpBlockList; wave != NULL;
										prev = wave, wave = wave->lpNext)
			;
		prev->lpNext = lpWaveOutHdr;
		}
	lpWaveOutHdr->lpNext = NULL;
	IntOn();

	if( dev->wDeviceStatus == STATUS_OUT_OF_BUFFER)
		StartPlaying( dev);
	return 0;
	}



/*
	This function sets the volume of a waveform output device.
*/
Word WaveOutSetVolume( hWaveOut, dwVolume)
	HWaveOut hWaveOut;		/* Identifies the waveform output device */
	DWord dwVolume;			/*
							Specifies the new volume setting. The high order
							word contains the left channel setting, and the
							low order word contains the right channel setting.
							0xffff represent full volume, 0 is silence. If a
							device does not support both left and right volume,
							only the right channel value is used.
							*/
	{
	DevicePtr wDev;
	
	wDev = hWaveOut;
	if( wDev->wMagic != WAVE_OUT_MAGIC)
		return WERR_INVALIDHANDLE;

	wDev->dwVolume = dwVolume;
	if( !wDev->stereo) {
		regA[ wDev->deviceId] = dwVolume >> 8;
		WriteMMA( wDev->deviceId, 0xA, regA[ wDev->deviceId]);
		}
	else {
		regA[ 0] = dwVolume >> 24;		/* left volume */
		regA[ 1] = dwVolume >> 8;		/* right volume */
		WriteMMA( 0, 0xA, regA[ 0]);
		WriteMMA( 1, 0xA, regA[ 1]);
		}
	return 0;
	}



/*
	This function queries the current volume setting of a waveform 
	output device.
*/
Word WaveOutGetVolume( hWaveOut, lpdwVolume)
	HWaveOut hWaveOut;
	LPDWord lpdwVolume;
	{
	DevicePtr wDev;
	
	wDev = hWaveOut;
	if( wDev->wMagic != WAVE_OUT_MAGIC)
		return WERR_INVALIDHANDLE;

	*lpdwVolume = wDev->dwVolume;
	return 0;
	}



/*
	This function select on which side the channel will
	output. This is possible only for monophonic channel. Stereophonic
	channel are always outputed on left & right.
*/	
Word WaveOutSetLeftRight( hWaveOut, leftRight)
	HWaveOut hWaveOut;
	Word leftRight;			/* WAVE_STEREO_LEFT,
								WAVE_STEREO_CENTER,
								WAVE_STEREO_RIGHT */
	{
	DevicePtr dev;
	unsigned char byte;
								/*  center left right */
	static unsigned char bits[] = { 0x60, 0x20, 0x40 };
	
	dev = hWaveOut;
	if( dev->wMagic != WAVE_OUT_MAGIC)
		return WERR_INVALIDHANDLE;

	if( leftRight > WAVE_LAST_STEREO)
		return WERR_BADPOSITION;

	IntOff();
	if( dev->stereo) {
		leftRight = WAVE_STEREO_CENTER;
		byte = reg9[ 0] & ~0x60;
		byte |= 0x20;
		reg9[ 0] = byte;
		WriteMMA( 0, 9, byte);

		byte = reg9[ 1] & ~0x60;
		byte |= 0x40;
		reg9[ 1] = byte;
		WriteMMA( 1, 9, byte);
		}
	else {
		byte = reg9[ dev->deviceId] & ~0x60;
		byte |= bits[ leftRight];
		reg9[ dev->deviceId] = byte;
		WriteMMA( dev->deviceId, 9, byte);
		}
	IntOn();
	dev->leftRight = leftRight;
	return 0;
	}


/*
	This function stops playback on a given waveform output device and reset
	the current position to 0. All pending playback buffers are marked as
	done and returned to the application.
*/
Word WaveOutReset( hWaveOut)
	HWaveOut hWaveOut;		/* Specifies a handle to the waveform output
							device that is to be reset */
	{
	DevicePtr wDev;
	LpWaveHdr block;
	
	wDev = hWaveOut;
	if( wDev->wMagic != WAVE_OUT_MAGIC)
		return WERR_INVALIDHANDLE;

	StopSampling( wDev);
	if( wDev->transferMode == WAVE_TRANSF_DMA)
		/* mask DMA controler channel: */
		outp( dmaSingleMaskRegister, dmaSingleMaskSet | wDev->wDmaNr);

	/* mark data buffer as DONE and return to application: */
	for( block = wDev->lpBlockList; block != NULL; block = block->lpNext) {
		block->dwFlags |= WHDR_DONE;
		(*wDev->dwCallBack)( (HWaveOut)wDev, block, wDev->dwCallBackData);
		}
	wDev->lpBlockList = NULL;

	return 0;
	}



/*
	This function breaks a loop on a given waveform output device and allow
	playback to continue with the next block in the driver list.
*/
Word WaveOutBreakLoop( hWaveOut)
	HWaveOut hWaveOut;
	{
	DevicePtr dev;
	LpWaveHdr block;
	
	dev = hWaveOut;
	if( dev->wMagic != WAVE_OUT_MAGIC)
		return WERR_INVALIDHANDLE;
	if( dev->wLoopCount == 0)		/* nothing is looping */
		return 0;

	IntOff();

	TerminateSampling( dev);
	GoToNextBlock( dev, BREAK_LOOP);
	StartPlaying( dev);

	IntOn();
	return 0;
	}



/*
	This function pauses playback on the specified waveform output device.
	The current playback position is saved. Use WaveOutRestart() to resume
	playback from the current playback position.

	Calling this function when the output is already paused will have no
	effect 0 will be returned.
*/
Word WaveOutPause( hWaveOut)
	HWaveOut hWaveOut;
	{
	DevicePtr dev;
	Word count;
	int dmaOff;
	
	dev = hWaveOut;
	if( dev->wMagic != WAVE_OUT_MAGIC)
		return WERR_INVALIDHANDLE;
	if( dev->wDeviceStatus == STATUS_WORKING)
		PauseSampling( dev);

	return 0;
	}




/*
	This function restarts a paused waveform output device.
*/
Word WaveOutRestart( hWaveOut)
	HWaveOut hWaveOut;
	{
	DevicePtr dev;
	
	dev = hWaveOut;
	if( dev->wMagic != WAVE_OUT_MAGIC)
		return WERR_INVALIDHANDLE;
	if( dev->wDeviceStatus == STATUS_PAUSED)
		StartPlaying( dev);
	return 0;
	}



/*
	This function sends an input buffer to a waveform input device. When
	the buffer is filled, it is sent back to the application.
*/
Word WaveInAddBuffer( hWaveIn, lpWaveInHdr, wSize)
	HWaveIn hWaveIn;	/* Handle to the input waveform device */
	LpWaveHdr lpWaveInHdr;	/* far ptr to struct. that identifies buffer */
	Word wSize;			/* Specifies the size of the WaveHdr struture */
	{
	DevicePtr dev;
	LpWaveHdr wave;

	(void) wSize;	
	dev = hWaveIn;
	if( dev->wMagic != WAVE_IN_MAGIC)
		return WERR_INVALIDHANDLE;

	IntOff();
	if( dev->lpBlockList == NULL) {
		dev->lpBlockList = lpWaveInHdr;
		dev->bytesToProcess = dev->lpBlockList->dwBufferLength;
		dev->processData = dev->lpBlockList->lpData;
		}
	else {
		for(wave = dev->lpBlockList; wave->lpNext != NULL; wave= wave->lpNext);
		wave->lpNext = lpWaveInHdr;
		}
	lpWaveInHdr->lpNext = NULL;
	lpWaveInHdr->dwBytesRecorded = 0;
	IntOn();

	if( dev->wDeviceStatus == STATUS_OUT_OF_BUFFER)
		return WaveInStart( hWaveIn);
	return 0;
	}



/*
	This function close the specified waveform input device.

	If there are input buffers that have been sent with WaveInAddBuffer(),
	and haven't been returned to the application, the close operstion will
	fail. Call WaveInReset() to mark all pending buffers as done.
*/	 
Word WaveInClose( hWaveIn)
	HWaveIn hWaveIn;
	{
	DevicePtr dev;
	
	dev = hWaveIn;
	if( dev->wMagic != WAVE_IN_MAGIC)
		return WERR_INVALIDHANDLE;
	if( dev->lpBlockList != NULL)
		return WERR_STILLPLAYING;

	dev->wDeviceMode = DEVICE_CLOSED;
	dev->wMagic = 0;
	return 0;
	}



/*
	This function queries a specified waveform input device to determine
	its capabilities.

	Use WaveInGetNumDevs to determine the number of waveform input devices
	present in the system. The device ID specified by wDeviceID varies from
	zero to one less than the number of devices present. Only wSize bytes
	(or less) of information will be copied to the location pointed to by
	lpCaps.
*/
Word WaveInGetDevCaps( wDeviceID, lpCaps, wSize)
	Word wDeviceID;	/* waveform input devive to be queried */
	LpWaveInCaps lpCaps;	/* Ptr to a WaveInCaps structure. This structure
							is filled with information about teh capabilities
							of the device. */
	Word wSize;		/* size of the WaveInCaps structure */
	{
	WaveInCaps wc;

	if( wDeviceID > 1)
		return WERR_BADDEVICEID;
	wc.wMid = 0;
	wc.wPid = 0;
	wc.vDriverVersion = (MAJOR_VER << 8) + MINOR_VER;
	strcpy( wc.szPname, "ADLIB-GOLD");
	wc.dwFormats = -1;
	wc.wChannels = 2;
	memmove( (char *)lpCaps, (char *) &wc, min( wSize, sizeof wc));
	return 0;
	}



/*
	This function return the number of waveform input device.
*/
Word WaveInGetNumDevs()
	{
	return 2;
	}
	



/*
	This function returns the number of samples recorded since last WaveInOpen
	or WaveInReset()...
*/
Word WaveInGetPosition( hWaveIn, wavePos)
	HWaveIn hWaveIn;
	LPDWord wavePos;
	{
	DevicePtr dev;
	long pos, delta, samples;
	int dmaOff;
	unsigned count;
	
	dev = hWaveIn;
	if( dev->wMagic != WAVE_IN_MAGIC)
		return WERR_INVALIDHANDLE;

	pos = dev->position;
	delta = 0;
	if( dev->wDeviceStatus == STATUS_WORKING) {
		if( dev->transferMode == WAVE_TRANSF_INTERRUPT)
			delta = dev->lpBlockList->dwBufferLength - dev->bytesToProcess;
		else if( dev->transferMode == WAVE_TRANSF_DMA) {
			dmaOff = dev->wDmaNr << 1;
			IntOff();
			outp( dmaBytePointerRegister, 0);	/* clear byte pointer */
			count = inp( dmaWordCountRegister +dmaOff);	/* get low byte count down reg. */
			count += inp( dmaWordCountRegister +dmaOff) << 8;	/* high byte c. d. reg */
			count++;
			delta = dev->dmaTrSize - count;
			IntOn();
			}
		}
	pos += delta;

	switch( dev->format) {
		case WAVE_FORMAT_ADPCM4:
				samples = pos << 1;
				break;
		case WAVE_FORMAT_PCM8:
				samples = pos;
				break;
		case WAVE_FORMAT_PCM12:
		case WAVE_FORMAT_PCM16:
				samples = pos >> 1;
		}
	*wavePos = samples;

	return 0;
	}




/*
	This function opens a specified waveform input device for recording.

	Use WaveInGetNumDevs() to determine the number of wavefomr input devices
	present in the system. THe device ID specified by wDeviceID varies from
	zero to one less the number of devices present.

*/
Word WaveInOpen( lphWaveIn, wDeviceID, lpFormat, dwCallBack, dwCallBackData,
				dwFlags)
	HWaveIn far * lphWaveIn;	/* ptr to ptr. This location is filled with
							a handle identifying the opened waveform input
							device. */
	Word wDeviceID;			/* Identifies the waveform input device to be
							opened. */
	LpWaveFormat lpFormat;	/* Specifies a ptr to a WaveFormat data structure
							that identifies the desired format for recording
							waveform data. */
	int (far *dwCallBack)();/* Specifies the address of a callback function
							that is called during waveform recording to precess
							messages related to the progress of recording. */
	DWord dwCallBackData;		/* Specifies a 32 bits user data that is passed
							to the callback function. */
	DWord dwFlags;			/* Specifies flags for opening device:

							WAVE_FORMAT_QUERRY:	If this flag is specified, the
								device will determine if it supports the given
								format, but will not actually open the device.
							*/
	{
	DevicePtr dev;
	int code;
	unsigned freq;

	if( wDeviceID > 1)
		return WERR_BADDEVICEID;
	dev = &waveDevices[ wDeviceID];
	if( dev->wDeviceMode != DEVICE_CLOSED)
		return WERR_ALLOCATED;
	if( lpFormat->nChannels > 1)
		if( wDeviceID != 0)
			return WERR_STEREOBADCHANNEL;
		else if( waveDevices[ 1].wDeviceMode != DEVICE_CLOSED)
			return WERR_STEREONEED2FREECHNL;

	freq = FindFreqCode( lpFormat->samplingFreq);
	if( freq > WAVE_LAST_FREQ)
		return WERR_UNSUPPORTEDFORMAT;
	dev->format = lpFormat->wFormatTag;
	if( (unsigned)dev->format > WAVE_LAST_FORMAT)
		return WERR_UNSUPPORTEDFORMAT;
	dev->stereo = lpFormat->nChannels > 1 ? 1 : 0;
	if( dev->format == WAVE_FORMAT_ADPCM4)
		code = freqsAdpcm[ freq];
	else
		code = freqsPcm[ freq];
	if( code == -1)
		return WERR_UNSUPPORTEDFORMAT;
	if( !( lpFormat->wTransMode == WAVE_TRANSF_INTERRUPT
	|| lpFormat->wTransMode == WAVE_TRANSF_DMA))
		return WERR_BADTRANSFERMODE;
	if( dwFlags & WAVE_FORMAT_QUERY)
		return 0;	/* Just return succes code */

	dev->freqCode = code;
	dev->transferMode = lpFormat->wTransMode;
	dev->wDeviceMode = DEVICE_INPUT;
	dev->wDeviceStatus = STATUS_STOPPED;
	dev->dwCallBack = dwCallBack;
	dev->dwCallBackData = dwCallBackData;
	dev->lpBlockList = NULL;
	dev->wLoopCount = 0;
	dev->wMagic = WAVE_IN_MAGIC;
	dev->dmaTrSize = 0;
	dev->position = 0;
	*lphWaveIn = dev;
	return 0;
	}
								


/*
	This function stops input on a given waveform input device and resets
	the current position to 0. All pending buffers are marked done and
	returned to the application.
*/
Word WaveInReset( hWaveIn)
	HWaveIn hWaveIn;
	{
	DevicePtr dev;
	LpWaveHdr block;
	Word count, recorded;
	int dmaOff;

	dev = hWaveIn;
	if( dev->wMagic != WAVE_IN_MAGIC)
		return WERR_INVALIDHANDLE;

	StopSampling( dev);

	/* mark data buffer as DONE and return to application: */
	for( block = dev->lpBlockList; block != NULL; block = block->lpNext) {
		block->dwFlags |= WHDR_DONE;
		(*dev->dwCallBack)( (HWaveOut)dev, block, dev->dwCallBackData);
		}
	dev->lpBlockList = NULL;
	dev->position = 0;

	return 0;
	}




/*
	This function starts input on a specified waveform input device.

	Buffers are returned to application when full or when WaveInReset()
	is called. "dwBytesRecorded" field in the header will contain the actual
	length of data. If there are no buffers in the queue, the data is
	thrown away without notification to the application and input will
	continue. Calling this function when input is already started will
	have no effect.
*/
Word WaveInStart( hWaveIn)
	HWaveIn hWaveIn;		/* Ptr to the waveform input device to be started */
	{
	DevicePtr dev;

	dev = hWaveIn;
	if( dev->wMagic != WAVE_IN_MAGIC)
		return WERR_INVALIDHANDLE;

	dev->wDeviceStatus = STATUS_WORKING;
	dev->started = 0;
	dev->bytesToProcess = dev->lpBlockList->dwBufferLength;
	PrepareForSampling( dev->deviceId, dev->freqCode, 0, dev->format,
						dev->transferMode == WAVE_TRANSF_DMA, dev->stereo);
	switch( dev->transferMode) {
		case WAVE_TRANSF_DMA:
			DoDataDMA( dev);
			break;
		case WAVE_TRANSF_INTERRUPT:
			StartPlayRecInt( dev, 0);
			break;
		default:	/* WAVE_TRANSF_POLLING */
			RecordPolling( dev);
			break;
		}	

	return 0;
	}




/*	The next function may be called by interrupt, so disable stack checking...*/
#pragma check_stack( off)

static 
void StopSampling(DevicePtr dev)

	{
	dev->wDeviceStatus = STATUS_STOPPED;
	TerminateSampling( dev);
	}


/*
	Arreter le sampling, et mettre le status a "STATUS_OUT_OF_BUFFER"
*/
static 
void OutOfBuffSampling(DevicePtr dev)
	{
	dev->wDeviceStatus = STATUS_OUT_OF_BUFFER;
	TerminateSampling( dev);
	}



#pragma check_stack()



static 
void PauseSampling(DevicePtr dev)
	{
	dev->wDeviceStatus = STATUS_PAUSED;
	TerminateSampling( dev);
	}


static void
StartPlaying(DevicePtr dev)
	{
	dev->wDeviceStatus = STATUS_WORKING;
	PrepareForSampling( dev->deviceId, dev->freqCode, 1, dev->format,
						dev->transferMode == WAVE_TRANSF_DMA, dev->stereo);
	switch( dev->transferMode) {
		case WAVE_TRANSF_DMA:
			DoDataDMA( dev);
			break;
		case WAVE_TRANSF_INTERRUPT:
			StartPlayRecInt( dev, 1);
			break;
		default:	/* WAVE_TRANSF_POLLING */
			PlayPolling( dev);
			break;
		}	
	}



/*

*/
static 
void PlayPolling(DevicePtr dev)
	{
	while( dev->lpBlockList != NULL) {
		PlayBlockPolling( dev);
		GoToNextBlock( dev, NO_BREAK);
		}

	OutOfBuffSampling( dev);
	}



static 
void PlayBlockPolling(DevicePtr dev)
	{
	WaveDataP pData;
	LpWaveHdr lpBlock;
	WaveSize dwBLen;
	Word channel;
	unsigned char data;
	int i, stat;

	lpBlock = dev->lpBlockList;
	channel = dev->deviceId;
	pData = lpBlock->lpData;
	dwBLen = lpBlock->dwBufferLength;

	if( !dev->started) {
		/* send 128 first bytes of signal: */
		for( i = 0; i < 128 && i < dwBLen; i++) {
			WriteMMA( channel, 0xB, *pData++);
			dwBLen--;
			if( dev->stereo) {
				WriteMMA( 1, 0xB, *pData++);
				dwBLen--;
				}
			}
		IntOff();
		reg9[ channel] |= 1;
		if( dev->stereo) {
			reg9[ 1] |= 1;
			WriteMMA( 1, 9, reg9[ 1]);
			}
		WriteMMA( channel, 9, reg9[ channel]);
		regC[ channel] &= ~2;		/* unmask flag */
		WriteMMA( channel, 0xC, regC[ channel]);
		IntOn();
		dev->started = 1;
		}

	while( dwBLen > 0) {
		stat = ReadStatusMM1();
		if( (stat & (1 << channel))) {	/* FIFO flag */
GetControlRegister(-1);
			for( i = 0; i < PLAY_FIFO_SIZE & dwBLen > 0; i++) {
				if( dev->stereo) {
					WriteMMA( 0, 0xB, *pData++);
					WriteMMA( 1, 0xB, *pData++);
					dwBLen -= 2;
					}
				else {
					WriteMMA( channel, 0xB, *pData++);
					dwBLen--;
					}
				}
			}
		}
	}






/*
	Start le sampling (ADP-ST = 1). Appele la routine d'interruption
	pour transferer la premiere batch de samples. Ensuite, l'interrupt
	genere par le MMA fera le reste.

	prepareForSampling() doit etre appele avant cette fonction.

	play specifies playback if 1, record if 0.
*/
static 
void StartPlayRecInt(DevicePtr dev, int play)
	{
	unsigned smpChnl;

	smpChnl = dev->deviceId;

	if( play)
		DoPlayInterrupt( dev);

	IntOff();
	reg9[ smpChnl] |= 1;
	if( dev->stereo) {
		reg9[ 1] |= 1;
		WriteMMA( 1, 9, reg9[ 1]);
		}
	WriteMMA( smpChnl, 9, reg9[ smpChnl]);

	regC[ smpChnl] &= ~2;		/* unmask FIFO */
	WriteMMA( smpChnl, 0xC, regC[ smpChnl]);

	dev->started = 1;
	IntOn();
	}





/*
	Cette fonction enregistre par polling tous les block contenus
	dans la liste du driver, et les retourne un a un a l,'application,
	au fur et a mesure de leur remplissage.
*/
static void
RecordPolling(DevicePtr dev)
	{

	while( dev->lpBlockList != NULL) {
		RecordBlockPolling( dev);
		GoToNextBlock( dev, NO_BREAK);
		}

	OutOfBuffSampling( dev);
	}



static 
void RecordBlockPolling(DevicePtr dev)
	{
	WaveDataP rData;
	LpWaveHdr lpBlock;
	WaveSize dwBLen;
	Word channel;
	unsigned char data;
	int i, stat;

	lpBlock = dev->lpBlockList;
	channel = dev->deviceId;
	rData = lpBlock->lpData;
	dwBLen = lpBlock->dwBufferLength;

	if( !dev->started) {
		IntOff();
		reg9[ channel] |= 1;
		if( dev->stereo) {
			reg9[ 1] |= 1;
			WriteMMA( 1, 9, reg9[ 1]);
			}
		WriteMMA( channel, 9, reg9[ channel]);
		IntOn();
		dev->started = 1;
		}

	while( dwBLen > 0) {
		stat = ReadStatusMM1();
		if( stat & 0x80) {
/*			printf( "\nOverrun!!! -record");		*/
			break;
			}
		else if( (stat & (1 << channel))) {	/* FIFO flag */
			for( i = 0; i < PLAY_FIFO_SIZE & dwBLen > 0; i++) {
				if( dev->stereo) {
					*rData++ = ReadMMA( 0, 0xB);
					*rData++ = ReadMMA( 1, 0xB);
					dwBLen -= 2;
					}
				else {
					*rData++ = ReadMMA( channel, 0xB);
					dwBLen--;
					}
				}
			}
		}
	}




/*
	Determine le code de frequence correspondant pour le chip MMA:

	retourne WAVE_FREQ44 ... WAVE_FREQ5
*/
static int FindFreqCode(DWord freq)
	{						 /* 44K    22K   11K    7K, 5 K */
	static DWord ranges[] = { 33075L, 16537, 9987, 6237 };
	int i;

	for( i = 0; i < 4; i++)
		if( freq >= ranges[ i])
			break;
	return i;
	}


	

/*
	INTERRUPT ROUTINES:

	These routines are subject to be called by interrupt.
*/


/* Disable stack checking for interrupt routines: */
#pragma check_stack( off)


/*
	Programmer le MMA pour le sampling (play & record). Il ne
	restera qu'a faire une start (ADP-ST) et demasker MSK-FIF si necessaire.
*/
static 
void PrepareForSampling(int channel, int freq, int play, int format, 
								int dmaMode, int stereo)
	{
	unsigned char byte, reg11;
	int i;

	ResetSampling( channel);
	if( stereo)
		ResetSampling( 1);

	byte = 2;		/* mask fifo */
	/* interrupt level selection: */
	byte |= play ? PLAY_FIFO_SELECT << 2 : REC_FIFO_SELECT << 2;
	byte |= ((format -1) & 3) << 5;		/* PCM format */
	if( dmaMode) {
		byte |= 1;
		if( stereo)
			byte |= 0x80;
		}
	regC[ channel] = byte;
	WriteMMA( channel, 0xC, regC[ channel]);
	for( i = 0; i < 8; i++)	/* pour eviter des problemes de dephasage 16 bits */
		WriteMMA( channel, 0xB, 0);
	if( stereo) {
		/* same as channel 0 but MSK-FIF = 1 & FIFO level select = 6 (16 bytes) */
		regC[ 1] = regC[ 0] | 2 | ( 6 << 2);
		WriteMMA( 1, 0xC, regC[ 1]);
		for( i = 0; i < 8; i++)	/* pour eviter des problemes de dephasage 16 bits */
			WriteMMA( 1, 0xB, 0);
		}

	reg9[ channel] &= ~0x9F;	/* clear: ADP-RST, FS1:FS0, PCM, PLY/REC-, ADP-ST */
	reg9[ channel] |= freq << 3;	/* set freq. bits */
	reg9[ channel] |= format == WAVE_FORMAT_ADPCM4 ? 0 : 4; /* PCM/ADPCM mode */
	if( play)
		reg9[ channel] |= 2;

	if( stereo) {
		reg9[ 1] = (reg9[ 0] & ~0x60) | (reg9[ 1] & 0x60);
		WriteMMA( 1, 9, reg9[ 1]);
		}
	WriteMMA( channel, 9, reg9[ channel]);

	if (stereo) CtStereoMonoAuxSamp(0);
	else CtStereoMonoAuxSamp(1);
	if (! play) {
		mmaVolL = CtGetMixerLevelForLeftSamplePb();
		CtSetMixerLevelForLeftSamplePb(0x80);
		CtSetChannel0FilterMode(1);
		CtSetChannel1FilterMode(1);
		}
	}



/*
	Termine le sampling (playback ou record) sur le canal de sampling
	correspondant au device 'dev'. S'il sagit d'un device stereo,
	les deux canaux sont stoppes.
*/
static 
void TerminateSampling(DevicePtr dev)
	{
	int channel;
	int dmaOff;
	unsigned count;
	long done;

	channel = dev->deviceId;

	IntOff();

	/* mask interrupt flag: */
	regC[ channel] |= 2;
	WriteMMA( channel, 0xC, regC[ channel]);

	/* stop MMA: */
	reg9[ channel] &= ~1;
	WriteMMA( channel, 9, reg9[ channel]);
	if( dev->stereo) {
		reg9[ 1] &= ~1;
		WriteMMA( 1, 9, reg9[ 1]);
		}
	IntOn();
	dev->started = 0;

	if( dev->transferMode == WAVE_TRANSF_DMA) {
	/* mask DMA controler channel: */
		outp( dmaSingleMaskRegister, dmaSingleMaskSet | dev->wDmaNr);
		dmaOff = dev->wDmaNr << 1;
	/* update byte count to process: */

		outp( dmaBytePointerRegister, 0);	/* clear byte pointer */
		count = inp( dmaWordCountRegister +dmaOff);	/* get low byte count down reg. */
		count += inp( dmaWordCountRegister +dmaOff) << 8;	/* high byte c. d. reg */
		count++;
		done = dev->dmaTrSize - count;
		dev->bytesToProcess -= done;
		if( DEVICE_INPUT == dev->wDeviceMode &&	dev->lpBlockList != NULL)
			dev->lpBlockList->dwBytesRecorded += done;
		dev->dmaTrSize = 0;
		dmaStatus |= inp( dmaStatusRegister);
		dmaStatus &= ~(1 << dev->wDmaNr);
		}

	if( DEVICE_INPUT == dev->wDeviceMode) {
		CtSetMixerLevelForLeftSamplePb(mmaVolL);
		CtSetChannel0FilterMode(0);
		CtSetChannel1FilterMode(0);
		}
	}




/*
	Cette fonction est responsable du transfert par interrupt
	en mode RECORD.
*/

static 
void DoInputInterrupt(DevicePtr dev)
	{
	int channel;
   int	dataReg;
   int count;
   int transferSize;
   char far *iData;

	channel = dev->deviceId;
	dataReg = mma_io + (channel ? 3: 1);
  
	/* Calculate the number of bytes to read */
	transferSize = REC_FIFO_SIZE;
   if (dev->stereo) transferSize <<= 1;
   if (transferSize > dev->bytesToProcess)
       transferSize = dev->bytesToProcess;

	// set fifo level to 0 temporary, to avoid false interrupt
   _asm	pushf   /*** DEBUG ***/
   _asm	cli     /*** DEBUG ***/
   outp(mma_io, 0x0C);
   outp(dataReg, regC[channel] | (0x07 << 2)); // select level 0 bytes 

	// Empty the MMA Buffer		
	iData = (char far *)dev->processData;
   outp(mma_io, 0x0B);

	// This case for 8-bit transfers
	if (dev->format  < WAVE_FORMAT_PCM12) {
   	if (dev->stereo) {
           count = transferSize >> 1;
       	while (count-=2) {
           	*iData++ = inp(dataReg);
               *iData++ = inp(dataReg + 2);
               }
			}
		else {
           count = transferSize;
			while(count--) *iData++ = inp(dataReg);
           }
		}
	// This case for 16 bit transfers
	else { /* dev->format  >= WAVE_FORMAT_PCM12 */
   	if (dev->stereo) {
           count = transferSize >> 2;
       	while (count--) {
           	*iData++ = inp(dataReg);
           	*iData++ = inp(dataReg);
               *iData++ = inp(dataReg + 2);
               *iData++ = inp(dataReg + 2);
               }
			}
		else {
           count = transferSize >> 1;
			while(count--) {
				*iData++ = inp(dataReg); 
				*iData++ = inp(dataReg);
               }
           }
		}

	// Update pointers

	dev->processData = iData;
	dev->lpBlockList->dwBytesRecorded += transferSize;
	dev->bytesToProcess -= transferSize;
		
	// restore interrupts
	_asm	popf    

	// Check if we need to prepare next block

	if( dev->bytesToProcess <= 0) {
		GoToNextBlock( dev, NO_BREAK);
		if( dev->lpBlockList == NULL) {
			OutOfBuffSampling( dev);
			}
       }


   _asm	pushf
   _asm	cli
   outp(mma_io, 0x0C);
   outp(dataReg, regC[channel]); // restore original FIFO level
   _asm 	popf
	}


static void
DoPlayInterrupt(DevicePtr dev)
	{
	int channel;
   int	dataReg;
   int count;
   int transferSize;
   char far *iData;

	channel = dev->deviceId;
	dataReg = mma_io + (channel ? 3: 1);
  
	/* Calculate the number of bytes to read */
	transferSize = PLAY_FIFO_SIZE;
   if (dev->stereo) transferSize <<= 1;
   if (transferSize > dev->bytesToProcess)
       transferSize = dev->bytesToProcess;

	// set fifo level to 0 temporary, to avoid false interrupt
   _asm	pushf   /*** DEBUG ***/
   _asm	cli     /*** DEBUG ***/
   outp(mma_io, 0x0C);
   outp(dataReg, regC[channel] | (0x07 << 2)); // select level 0 bytes 

	// Fill the MMA Buffer		
	iData = (char far *)dev->processData;
   outp(mma_io, 0x0B);

	// This case for 8-bit transfers
	if (dev->format  < WAVE_FORMAT_PCM12) {
   	if (dev->stereo) {
           count = transferSize >> 1;
       	while (count-=2) {
           	outp(dataReg, *iData++);
           	outp(dataReg+2, *iData++);
               }
			}
		else {
           count = transferSize;
			while(count--) outp(dataReg, *iData++);
           }
		}
	// This case for 16 bit transfers
	else { /* dev->format  >= WAVE_FORMAT_PCM12 */
   	if (dev->stereo) {
           count = transferSize >> 2;
       	while (count--) {
           	outp(dataReg, *iData++);
           	outp(dataReg, *iData++);
           	outp(dataReg+2, *iData++);
           	outp(dataReg+2, *iData++);
               }
			}
		else {
           count = transferSize >> 1;
			while(count--) {
           	outp(dataReg, *iData++);
           	outp(dataReg, *iData++);
               }
           }
		}

	// Update pointers

	dev->processData = iData;
	dev->bytesToProcess -= transferSize;
		
	// restore interrupts
	_asm	popf    

	// Check if we need to prepare next block

	if( dev->bytesToProcess <= 0) {
		GoToNextBlock( dev, NO_BREAK);
		if( dev->lpBlockList == NULL) {
			OutOfBuffSampling( dev);
			}
       }


   _asm	pushf
   _asm	cli
   outp(mma_io, 0x0C);
   outp(dataReg, regC[channel]); // restore original FIFO level
   _asm 	popf
	}



/*
	Cette routine, appelee par interruption, gere l'ecriture et la lecture
	de samples par DMA.
*/
static int 
DoDataDMA(DevicePtr dev)
	{
	int i;
	int cmdByte, page;
	unsigned char stat;
	unsigned long addr;
	unsigned int offset;
	int dmaChannel, dmaChnlOffset;
	char far * ptr;
	unsigned int size;		/* DMA transfer size */
   unsigned char highByte;
	unsigned long longSize;
	int smpChannel;

	static unsigned char dmaPages[] = {
		dmaPageRegister0, dmaPageRegister1, dmaPageRegister2, dmaPageRegister3 };

	dev->bytesToProcess -= dev->dmaTrSize;
	dev->processData += dev->dmaTrSize;

	if( dev->wDeviceMode == DEVICE_INPUT)
		dev->lpBlockList->dwBytesRecorded += dev->dmaTrSize;
	dev->dmaTrSize = 0;
	if( dev->bytesToProcess <= 0) {
		GoToNextBlock( dev, NO_BREAK);
		if( dev->lpBlockList == NULL) {
			OutOfBuffSampling( dev);
			return 0;
			}
		}

	ptr = (char far *)dev->processData;
	addr = ((unsigned long)FP_SEG( ptr) << 4) + FP_OFF( ptr);
	offset = addr & 0xffff;
	longSize = (long)~offset +1;		/* from start addr to end of page */
	if( longSize > dev->bytesToProcess) 
		longSize = dev->bytesToProcess;
	size = longSize -1;
	dev->dmaTrSize = longSize;

	page = addr >> 16;
	dmaChannel = dev->wDmaNr;
	dmaChnlOffset = dmaChannel << 1;

	/* disable dma controler: */

	outp( dmaSingleMaskRegister, dmaSingleMaskSet | dmaChannel);

	/* Program & start DMA controler: */
	cmdByte = dmaModeSingleTransfer | dmaChannel;
	cmdByte |= (dev->wDeviceMode == DEVICE_OUTPUT) ? dmaModeCycleRead : dmaModeCycleWrite;
	outp( dmaModeRegister, cmdByte);
	outp( dmaBytePointerRegister, 0);
	outp( dmaBaseAddressRegister +dmaChnlOffset, offset); 

   highByte = ((unsigned int)offset) >> 8;
	outp( dmaBaseAddressRegister +dmaChnlOffset, highByte); 
	outp( dmaPages[ dmaChannel], page);
	outp( dmaWordCountRegister +dmaChnlOffset, size); 

   highByte = ((unsigned int)size) >> 8;
	outp( dmaWordCountRegister +dmaChnlOffset, highByte); 

	/* Enable DMA controler... */
	outp( dmaSingleMaskRegister, dmaChannel);

	if( !dev->started) {
		IntOff();
		smpChannel = dev->deviceId;
		reg9[ smpChannel] |= 1;			/* ADP start = ON */
		WriteMMA( smpChannel, 9, reg9[ smpChannel]);

		regC[ smpChannel] &= ~2;		/* unmask FIFO */
		WriteMMA( smpChannel, 0xC, regC[ smpChannel]);

		dev->started = 1;
		IntOn();
		}
	return 1;		/* OK */
	}



/*
	Le block courant (lpBlockList) vient d'etre termine. On retourne
	a l'application le block, puis on avance au prochain.
	Tient compte des flags de looping. 
*/
static void
GoToNextBlock(DevicePtr dev, int breakFlag)
	/* breakFlag;		 termine la 'loop' si vrai */
	{
	DWord flags;
	LpWaveHdr block;
	int ok;
	int inputMode;

	flags = dev->lpBlockList->dwFlags;
	inputMode = dev->wDeviceMode == DEVICE_INPUT;
	if( inputMode)
		flags &= ~(WHDR_BEGINLOOP | WHDR_ENDLOOP);

	if( flags & WHDR_BEGINLOOP) {
		if( dev->wLoopCount == 0) {
			dev->wLoopCount = dev->lpBlockList->dwLoops;
			dev->lpFrstLpBlk = dev->lpBlockList;
			}
		}

	if( flags & WHDR_ENDLOOP || breakFlag) {
		if( --dev->wLoopCount > 0 && !breakFlag)
			dev->lpBlockList = dev->lpFrstLpBlk;
		else {
			for( ok = 1, block = dev->lpFrstLpBlk; ok; block = block->lpNext) {
				block->dwFlags |= WHDR_DONE;
				(*dev->dwCallBack)( (HWaveOut)dev, block, dev->dwCallBackData);
				ok = !(block->dwFlags & WHDR_ENDLOOP);
				}
			dev->lpBlockList = block;
			dev->wLoopCount = 0;
			}
		}
	else {
		if( dev->wLoopCount == 0) {
			dev->lpBlockList->dwFlags |= WHDR_DONE;
			(*dev->dwCallBack)( (HWaveOut)dev, dev->lpBlockList,
													dev->dwCallBackData);
			}
		dev->position += inputMode	? dev->lpBlockList->dwBytesRecorded
									: dev->lpBlockList->dwBufferLength;
		dev->lpBlockList = dev->lpBlockList->lpNext;
		}

	if( dev->lpBlockList != NULL) {
		dev->processData = dev->lpBlockList->lpData;
		dev->bytesToProcess = dev->lpBlockList->dwBufferLength;
		}
	}



/*
	Output data byte 'data' to register 'reg' of channel
	'channel' of MMA.
	
	No return error.
*/
static void
WriteMMA( int channel, int reg, int data)
	{
	int io;

	IntOff();
	io = mma_io + (channel << 1);
	outp( io++, reg);

	outp( io, data);

	IntOn();
	}


static int
ReadMMA(int channel, int reg)
	{
	int io;
   int res;

	io = mma_io + (channel << 1);
	IntOff();
	outp( io++, reg);

   res = inp(io);

	IntOn();
	return res;
	}



static int 
ReadStatusMM1(void )
	{
	inp( mma_io);				/* clear status flags */
	return inp( mma_io);
	}





/*
	Cette routine est appelee par le driver d'interruption
	lorsqu'un interruption provenant du MMA concerne les buffer
	de FIFO.
*/
static int
FIFO_int(Word wFifoNr)
	{
	DevicePtr dev;
	int bit;

	dev = &waveDevices[ wFifoNr];
	if( dev->wDeviceStatus !=  STATUS_WORKING)
		return 0;

	if( dev->transferMode == WAVE_TRANSF_INTERRUPT)
		switch( dev->wDeviceMode) {
			case DEVICE_OUTPUT:
				DoPlayInterrupt( dev);
				break;
			case DEVICE_INPUT:
				DoInputInterrupt( dev);
				break;
			}
	else if( dev->transferMode == WAVE_TRANSF_DMA) {	/* DMA mode */
		IntOff();
#ifdef TURBO
		dmaStatus |= inportb( dmaStatusRegister);
#else
		dmaStatus |= inp( dmaStatusRegister);
#endif
		bit = 1 << dev->wDmaNr;
		if( dmaStatus & bit) {
			dmaStatus ^= bit;
			DoDataDMA( dev);
			}
		else
			;	/* false interrupt */
		IntOn();
		}
	else
		;		/* polling */
   return 1;
	}

#pragma check_stack()