/***************************************************************************
*	NAME:  SAMPLE.C
**	COPYRIGHT:
**	"Copyright (c) 1992, by FORTE
**
**       "This software is furnished under a license and may be used,
**       copied, or disclosed only in accordance with the terms of such
**       license and with the inclusion of the above copyright notice.
**       This software or any other copies thereof may not be provided or
**       otherwise made available to any other person. No title to and
**       ownership of the software is hereby transfered."
****************************************************************************
*  CREATION DATE: 11/18/92
*--------------------------------------------------------------------------*
*     VERSION	DATE	   NAME		DESCRIPTION
*>	1.0	11/18/92		Original
***************************************************************************/

#include <stdio.h>
#include <dos.h>
#include <conio.h>

#include "forte.h"
#include "gf1proto.h"
#include "osproto.h"
#include "gf1hware.h"
#include "gf1os.h"
#include "ultraerr.h"
#include "dma.h"	/* Hardware defs for PC's dma controllers */

extern DMA_ENTRY _gf1_dma[]; /* Structure that holds data on PC's dma chan */
extern ULTRA_DATA _gf1_data;

int
UltraRecordDmaBusy(void)
{
return(_gf1_dma[_gf1_data.adc_dma_chan-1].flags & DMA_PENDING);
}

void
UltraWaitRecordDma(void)
{
	_gf1_data.flags &= ~ADC_DMA_NOWAIT;

	ENTER_CRITICAL_ON;

	while (_gf1_data.flags & ADC_DMA_BUSY)	/* wait for irq to clear this */
		{
		}

	LEAVE_CRITICAL_ON;
}

void
UltraSetRecordFrequency(unsigned long rate)
{
unsigned char adsr;

		/* First calculate as if its for a record */
		/* Use the formula:
			rate = CLOCK_RATE / (16*(ADRS+2))
		*/
		adsr = ((CLOCK_RATE>>4)/rate)-2;

ENTER_CRITICAL;

		outp(_gf1_data.reg_select,SET_SAMPLE_RATE);
		outp(_gf1_data.data_hi,(unsigned char)adsr);
LEAVE_CRITICAL;

}

int
UltraPrimeRecord(void far *pc_ptr,unsigned int size,int repeat)
{
int mode;
int val;

if (repeat)
	mode = INDEF_READ;
else
	mode = READ_DMA;

/* Make sure the channel is not busy (recording or playback ... )*/
val = PrimeDma(pc_ptr,mode,size,_gf1_data.adc_dma_chan);
return(val);
}

int
UltraGoRecord(unsigned char control)
{
DMA_ENTRY *tdma;

tdma  = &_gf1_dma[_gf1_data.adc_dma_chan-1];		/* point to this dma data */

/* Set flag that irq handler clears when the xfer is complete */
_gf1_data.flags |= ADC_DMA_BUSY;

/* Now tell GF1 to start xfer ... */
tdma->cur_control = control;

UltraStartRecordDma(control);

return(ULTRA_OK);
}

int
UltraRecordData(void far *pc_ptr,unsigned char control,unsigned int size,int wait,int repeat)
{
int ret;


ret=UltraPrimeRecord(pc_ptr,size,repeat);
if (ret != ULTRA_OK)
	return(ret);

UltraGoRecord(control);

/* if required, wait till dma is done ... */
if (wait)
	UltraWaitRecordDma();
else
	_gf1_data.flags |= ADC_DMA_NOWAIT;

return(ULTRA_OK);
}


#ifdef NEVER
// This code will hang if there is a memory manager that virtualizes
// the dma controller. It takes too much time and we will never read
// the same count twice ....
// Use the other GetRecordDmaPos routine ....

unsigned int
GetRecordDmaPos(int chan_num)
{
DMA_ENTRY *tdma;
unsigned int val1,val2;
unsigned int low1,low2;
unsigned int high1,high2;

	tdma  = &_gf1_dma[chan_num-1]; /* point to this dma data */

	ENTER_CRITICAL;

	while (TRUE)
		{
		outp(tdma->clear_ff,0);						/* clear f/f */
		low1  = (unsigned int)inp( tdma->count );
		high1 = (unsigned int)inp( tdma->count );

		outp(tdma->clear_ff,0);						/* clear f/f */
		low2  = (unsigned int)inp( tdma->count );
		high2 = (unsigned int)inp( tdma->count );

		val1 = (high1<<8) + low1;
		val2 = (high2<<8) + low2;

		if( val1 < val2 )
			val1 = val2 - val1;
		else
			val1 = val1 - val2;
		if( val1 < 10 )
			break;
		}

	LEAVE_CRITICAL;

	return(val2);
}

#endif
unsigned int
GetRecordDmaPos(int chan_num)
{
DMA_ENTRY *tdma;
unsigned int val1,val2;
unsigned int low1,low2;
unsigned int high1,high2;
static unsigned int threshold[MAX_DMA+1] = {30,30,30,30,30,30,30,30};
int i=5;

	tdma  = &_gf1_dma[chan_num-1]; /* point to this dma data */

	ENTER_CRITICAL;

	if (tdma->flags & CALIB_COUNT)
		{

// This code is necessary to accomodate a virtualized DMA controller.
// If something (like emm386) virtualizes the DMA controller, we have
// to adjust our threshold point to accomodate the rollover problem
// in the DMA controller. The problem is when you read the low byte
// and the high byte rolls over before we get a chance to read it.
// A virtualized DMA controller aggravates the problem since it will
// take longer between reads ...

		tdma->flags &= ~CALIB_COUNT;
		while(i-- > 0)	/* try up to 5 times, then use default */
			{
			outp(tdma->clear_ff,0);						/* clear f/f */
			low1  = (unsigned int)inp( tdma->count );
			high1 = (unsigned int)inp( tdma->count );
			low2  = (unsigned int)inp( tdma->count );
			high2 = (unsigned int)inp( tdma->count );
			if (high1 == high2)
				{
				threshold[chan_num] = (low1 - low2)/2 + 2;
				break;
				}
			}
		}

	val2 = 1;	/* first time thru condition ... */
	
	while (TRUE)
		{
		outp(tdma->clear_ff,0);						/* clear f/f */
		low1  = (unsigned int)inp( tdma->count );
		high1 = (unsigned int)inp( tdma->count );

		val1 = (high1<<8) + low1;

		/* If count is not about to roll over, use this count */
		/* or if count equals 0xffff (dma complete) use this count */
		if ((low1 > threshold[chan_num] && low1 != 0xff) || (val1 == 0xffff))
			{
			val2 = val1;
			break;
			}
		if (val2 == val1)
			break;
		val2 = val1;
		}

	LEAVE_CRITICAL;

	return(val2+1);
}

unsigned int
UltraReadRecordPosition(void)
{
DMA_ENTRY *tdma;
unsigned int actual_dma;
unsigned int this_size;
unsigned int total_size;

	tdma  = &_gf1_dma[_gf1_data.adc_dma_chan-1]; /* point to this dma data */

	actual_dma = GetRecordDmaPos(_gf1_data.adc_dma_chan);

	/* Since it counts backwards, subtract this from size of transfer */
	this_size = tdma->cur_size - actual_dma;

	/* Now add in the amount sent (in case it crosses page) */
	total_size = tdma->amnt_sent + this_size;

	if ( _gf1_data.adc_dma_chan >= 4)
		total_size <<= 1;

return(total_size);
}

