#include <dos.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h>
#include <malloc.h>
#include "iwdefs.h"
#include "iwprotos.h"
extern IWAVE iw;
//#########################################################################
// FILE: iwutil.c
//
// REMARKS: This file contains functions that have a support role within
//          the InterWave DDK.
//
// UPDATE: 6/5/95  --- Update for BCC32 support
//         6/19/95 --- Added IwaveCheckSum(...)
//         7/5/95  --- Added IwaveJoyPosition(...)
//#########################################################################
//
// FUNCTION: IwaveDmaMalloc
//
// PROFILE: This function allows a calling program to allocate memory
//          that lies within a DMA page. This utility is provided so
//          that applications can perform DMA transfers in auto init
//          mode. Use this function only if allocating space for this
//          type of transfers, otherwise use the regular routines that
//          come with your compiler.
//
//#########################################################################
#ifdef FLAT_MODEL
void *IwaveDmaMalloc(WORD buffsize)
{
 void   *buff_ptr;
 void   *buff_ptrs[10];
#else
void far *IwaveDmaMalloc(WORD buffsize)
{
 void   far *buff_ptr;
 void   far *buff_ptrs[10];
#endif
 DWORD physS;
 DWORD physE;

 #ifndef FLAT_MODEL
 WORD sseg;
 WORD soff;
 #endif
 WORD pageS;
 WORD pageE;
 WORD size;
 BYTE i, buff;

  for (i=0;i<10;i++)
       buff_ptrs[i]=NULL;               /* Initialize pointer array */

  for (buff=0;buff<10;buff++)
     {
		 buff_ptr=farmalloc(buffsize);
       
	  if (buff_ptr==NULL)
	  {
	  for (i=0;i<10;i++)             /* Free allocated buffer(s) */
			 if (buff_ptrs[i] != NULL)
				  farfree(buff_ptrs[i]);
#ifdef FLAT_MODEL
     return((void *)NULL);
#else
	  return((void far*)NULL);
#endif
	  }

#ifdef FLAT_MODEL
	  physS=(DWORD)buff_ptr;
#else
	  sseg= FP_SEG(buff_ptr);
	  soff= FP_OFF(buff_ptr);
	  physS= (((DWORD)sseg)<<4)+(DWORD)soff;
#endif
	  physE = physS+(DWORD)buffsize;
	  pageS = (WORD)(physS>>16);  /* Isolate Pages */
	  pageE = (WORD)(physE>>16);

	  if (pageS != pageE)
		 {
			farfree(buff_ptr);
			buff_ptr=NULL;
			size = (WORD)physS;
			size = (0xffff-size) + 1;
			buff_ptrs[buff]=farmalloc(size);
		 }
	  else
			break;

	}

  for (i=0; i<10; i++)
		 if (buff_ptrs[i] != NULL)
			  farfree(buff_ptrs[i]);

  return(buff_ptr);
}
#ifdef __WATCOMC__
//#########################################################################
//
// FUNCTION: WatcomDmaBuffer
//
// PROFILE: This function allows a WATCOM C protected program to allocate
//          memory that lies within a DMA page. This utility is provided so
//          that applications can perform DMA transfers in auto init
//          mode. Use this function only if allocating space for this
//          type of transfers, otherwise use the regular routines that
//          come with your compiler.
//
//#########################################################################
void *WatcomDmaBuffer(WORD buffsize,WORD *pselector)
{
 void   *buff_ptr;
 void   *buff_ptrs[10];
 WORD   psel[10], pseg;

 DWORD physS;
 DWORD physE;
 WORD pageS;
 WORD pageE;
 WORD size;
 BYTE i, buff;

  for (i=0;i<10;i++)
		{
		  buff_ptrs[i]=NULL;              // Initialize pointer array
		  psel[i]=0;                      // Initialize selector array
		}

  for (buff=0;buff<10;buff++)
	  {
		 buff_ptr=IwaveAllocDOS(buffsize,&pseg,&psel[buff]);
		 *pselector=psel[buff];

	  if (buff_ptr==NULL)
	  {
		 for (i=0;i<10;i++)             // Free allocated buffer(s)
			 if (buff_ptrs[i] != NULL)
				  IwaveFreeDOS(psel[i]);
		 return((void *)NULL);
	  }
	  physS=(DWORD)buff_ptr;
	  physE = physS+(DWORD)buffsize;
	  pageS = (WORD)(physS>>16);       // Isolate Pages
	  pageE = (WORD)(physE>>16);

	  if (pageS != pageE)
		 {
			IwaveFreeDOS(psel[buff]);
			buff_ptr=NULL;
			size = (WORD)physS;
			size = (0xffff-size) + 1;
			buff_ptrs[buff]=IwaveAllocDOS(size,&pseg,&psel[buff]);
			*pselector=psel[buff];
		 }
	  else
			break;

	}

  for (i=0; i<10; i++)
		 if (psel[i] != 0)
			  IwaveFreeDOS(psel[i]);
  return(buff_ptr);
}
//#########################################################################
//
// FUNCTION: IwaveAllocDOS
//
// PROFILE: This function allows WATCOM users to allocated real-mode or
//          DOS memory that a DMA controller needs in order to perform
//          transfers between PC memory and local memory. Use it in
//          protected mode programs requiring DMA capabilities.
//
//#########################################################################
void *IwaveAllocDOS(WORD nbytes,WORD *pseg,WORD *psel)
{
	union REGS regs;
	struct SREGS    sregs;

	WORD npara = (nbytes + 15)>>4;

	void *pprot;
	pprot = NULL;
	*pseg = 0;
	*psel = 0;

//###############################################
// DPMI call 100h allocates DOS memory
//###############################################
	segread(&sregs);
	memset(&sregs,0,sizeof(sregs));
	AX(regs) = 0x0100;              /* DPMI: Allocate DOS Memory */
	BX(regs) = npara;               /* number of paragraphs to alloc */

	int386x( DPMI_INT, &regs, &regs, &sregs);
	if (regs.w.cflag == 0)
//
	{
	  *pseg = AX(regs);             /* the real-mode segment */
	  *psel = DX(regs);             /* equivalent protected-mode selector */
	  pprot = (void *) ((WORD)*pseg << 4);
   }
   return(pprot);
}
//#########################################################################
//
// FUNCTION: IwaveFreeDOS
//
// PROFILE: This function allows WATCOM users to de-allocate real-mode or
//          DOS memory that was allocated with IwaveAllocDOS.
//
//#########################################################################
void IwaveFreeDOS(WORD sel)
{
   union REGS regs;

   AX(regs) = 0x0101;               /* DPMI free DOS memory */
   DX(regs) = sel;

   int386( DPMI_INT, &regs, &regs);
}
#endif
//#########################################################################
//
//  FUNCTION: IwaveAddrTrans
//
//  PROFILE: This function performs an address translation required for
//           16-bit DMA channels or 16-bit synthesizer memory accesses.
//           When in GUS mode (SGMI[ENH}=0) the translation consists of
//           shifting the first 17 bits to the right while preserving
//           bits 18 and 19. In enhanced mode, a right shift is required.
//           See page 125 of the functional spec.
//
//#########################################################################
ADDRESS IwaveAddrTrans(ADDRESS local)
{
  ADDRESS local_mem;

  if (iw.smode==GUS_MODE)
      local_mem= (local&0x000C0000L)|((local&0x0003FFFFL)>>1);
  else
      local_mem=local>>1;
  return(local_mem);
}
//#########################################################################
//
//  FUNCTION: IwaveRealAddr
//
//  PROFILE: This function translates the contents of the Synthesizer
//           address registers back into real addresses. This are the
//           actual addresses to local memory.
//
//#########################################################################
ADDRESS
IwaveRealAddr(ADDRESS logical, BYTE mode)
{
 if ((mode&VC_DATA_WIDTH) && (iw.smode==GUS_MODE))
	  logical=(logical&0xFC0000L)|((logical<<1)&0x3FFFFL);

 if ((mode&VC_DATA_WIDTH)&& (iw.smode==ENH_MODE))
	  logical<<=1;

 return(logical);
}
//#########################################################################
//
// FUNCTION: IwaveRegPeek
//
// PROFILE : This function returns the value stored in any readable
//           InterWave register. It takes as input a pointer to a
//           structure containing the addresses of the relocatable I/O
//           space as well as a register mnemonic. To correctly use this
//           function, the programmer must use the mnemonics defined in
//           "iwdefs.h". These mnemonics contain coded information used
//           by the function to properly access the desired register.
//
//           An attempt to read from a write-only register will return
//           meaningless data.
//
//#########################################################################
WORD IwaveRegPeek(DWORD reg_mnem)
{
	 BYTE index, val;
	 WORD reg_id, offset;

	 offset = (WORD)((BYTE)reg_mnem);
	 reg_id = (WORD)(reg_mnem>>16);
	 index  = (BYTE)(reg_mnem>>8);

//###################################################
// Logic to read registers in P2XR block & GMCR
//###################################################

	 if (reg_id>=0x0001 && reg_id<=0x001A)  /* UMCR to GMCR */
     {
	  	 if (reg_id<=0x000E)                 /* UMCR to USRR */
			  return((WORD)_peek(iw.p2xr+offset));

		 if (reg_id==0x0019)
			  return((WORD)_peek(iw.p201ar));

	  else                           /* GUS Hidden registers or GMCR */
	  {
		 BYTE iveri;

		 _poke(iw.igidxr,0x5B);              /* select IVERI */
		 iveri=_peek(iw.i8dp);               /* read IVERI */
		 _poke(iw.i8dp,(BYTE)(iveri|0x09));  /* set IVERI[3,0] */
		 if (reg_id==0x001A)                 /* GMCR */
			  {
				val=_peek(iw.p3xr);
				_poke(iw.i8dp,iveri);     /* restore IVERI */
			   return((WORD)val);
			  }
		 val = _peek(iw.p2xr+0x0F);     /* read URCR */
		 val = (val&0xF8)|index;        /* value for URCR[2:0] */
		 _poke(iw.p2xr+0x0F,val);       /* set URCR[2:0] */

		 if (reg_mnem==UDCI || reg_mnem==UICI)
			{
			  val=_peek(iw.p2xr);
			  if (reg_mnem==UDCI)
					_poke(iw.p2xr,(BYTE)(val&0xBF));
			  else
					_poke(iw.p2xr,(BYTE)(val|0x40));
			}
		 val=_peek(iw.p2xr+0x0B);
		 _poke(iw.igidxr,0x5B);         /* select IVERI */
		 _poke(iw.i8dp,iveri);          /* restore IVERI */
		 return((WORD)val);             /* read register */
		 }
     }

//###################################################
//  Logic to read registers in P3XR block
//###################################################

	 if (reg_id>=0x001B && reg_id<=0x005C)           /* GMSR to LMBDR */
	  {
		 if (reg_id==0x005C)                          /* LMBDR */
			  return((WORD)_peek(iw.lmbdr));

		 if (reg_id>=0x001B && reg_id<=0x0021)        /* GMSR to I8DP */
			  if (offset==0x04)
					return(_peekw(iw.i16dp));
			  else
					return((WORD)_peek(iw.p3xr+offset));
		 else                                        /* indexed registers */
		 {

		  if(reg_id<=0x003F)
			  index|=0x80;                            /* adjust for reading */

		  _poke(iw.igidxr,index);                    /* select register */

		  if (offset==0x04)
				return(_peekw(iw.i16dp));
		  else
				return((WORD)_peek(iw.i8dp));
	  }
	 }

//####################################################
// Logic to read registers in PCODAR block
//####################################################

	 if (reg_id>=0x005D && reg_id<=0x0081)         /* CIDXR to CLRCTI */
	 {
		 if (reg_id<=0x0061)
			  return((WORD)_peek(iw.pcodar+offset)); /* CRDR */

		 else                                       /* indexed registers */
		 {
			BYTE cidxr;

			cidxr = _peek(iw.pcodar);
			cidxr = (cidxr&0xE0)+index;
			_poke(iw.pcodar,cidxr);                  /* select register */
			return((WORD)_peek(iw.cdatap));
		 }
	 }
//#####################################################
// Logic to read the PnP registers
//#####################################################
	 if (reg_id>=0x0082 && reg_id<=0x00B7)        /* PCSNBR to PMITI */
	 {
		 if (reg_id==0x0085)
			  return((WORD)_peek(iw.pnprdp));

		 if (reg_id<0x0085)
			  return((WORD)_peek((WORD)reg_mnem));

		 else                                      /* indexed registers */
		 {
			if (reg_id>=0x008E&&reg_id<=0x00B7)
			  {
				 _poke(0x0279,0x07);                /* select PLDNI */
				 _poke(0xA79,(BYTE)offset);         /* select logical dev */
			  }
			_poke(0x0279,index);                   /* select the register */
			return((WORD)_peek(iw.pnprdp));
		 }
	 }
}
//#########################################################################
//
// FUNCTION: IwaveRegPoke
//
// PROFILE : This function writes a value to any writable
//           InterWave register. It takes as input a pointer to a
//           structure containing the addresses of the relocatable I/O
//           space as well as a register mnemonic. To correctly use this
//           function, the programmer must use the mnemonics defined in
//           "iwdefs.h". These mnemonics contain coded information used
//           by the function to properly access the desired register.
//
//           This function does not guard against writing to read-only
//           registers. It is the programmer's responsibility to ensure
//           that the writes are to valid registers.
//
//#########################################################################
void IwaveRegPoke(DWORD reg_mnem, WORD datum)
{
	 BYTE index;
	 BYTE val;
	 WORD reg_id;
	 WORD offset;

	 offset = (WORD)((BYTE)reg_mnem);
	 reg_id = (WORD)(reg_mnem>>16);
	 index  = (BYTE)(reg_mnem>>8);


//#######################################################
//  Logic to write to registers in P2XR block
//#######################################################
	 if (reg_id>=0x0001 && reg_id<=0x0019)  /* UMCR to GGCR */
     {
		 if (reg_id<=0x000E)                 /* UMCR to USRR */
		  {
			  _poke(iw.p2xr+offset,(BYTE)datum);
			  return;
		  }
		 if (reg_id==0x0019)
		  {
			  _poke(iw.p201ar,(BYTE)datum);
			  return;
		  }
		 else /* GUS Hidden registers */
		 {

		 BYTE iveri;

		 _poke(iw.igidxr,0x5B);             /* select IVERI */
		 iveri=_peek(iw.i8dp);              /* read IVERI */
		 _poke(iw.i8dp,(BYTE)(iveri|0x09)); /* set IVERI[3,0] */
		 val = _peek(iw.p2xr+0x0F);         /* read URCR */
		 val = (val&0xF8)|index;            /* value for URCR[2:0] */
		 _poke(iw.p2xr+0x0F,val);           /* set URCR[2:0] */

		 if (reg_mnem==UDCI || reg_mnem==UICI)
		 {
			val=_peek(iw.p2xr);                   /* read UMCR */
			if (reg_mnem==UDCI)
				 _poke(iw.p2xr,(BYTE)(val&0xBF));  /* set UMCR[6]=0 */
			else
				 _poke(iw.p2xr,(BYTE)(val|0x40));  /* set UMCR[6]=1 */
		 }
		 _poke(iw.p2xr+0x0B,(BYTE)datum);        /* write register */
		 _poke(iw.igidxr,0x5B);                  /* select IVERI */
		 _poke(iw.i8dp,iveri);                   /* restore IVERI */
		 return;
	  }
   }

//#############################################################
// Logic to write to registers in P3XR block
//#############################################################

	 if (reg_id>=0x001A && reg_id<=0x005C)        /* GMCR to LMBDR */
		{

		 if (reg_id==0x005C)                       /* LMBDR */
		  {
			 _poke(iw.lmbdr,(BYTE)datum);
			 return;
		  }
		 if (reg_id==0x001B)                        /* GMSR */
			  return;

		 if (reg_id >=0x001A && reg_id <= 0x0021)   /* GMCR to I8DP */
			  if (offset==0x04)
					_pokew(iw.i16dp,datum);
			  else
					_poke(iw.p3xr+offset,(BYTE)datum);
		 else                                       /* indexed registers */
		 {
		  _poke(iw.igidxr,index);                   /* select register */

		  if (offset==0x04)
				_pokew(iw.i16dp,datum);
		  else
				_poke(iw.i8dp,(BYTE)datum);
		 }
	  }

///###################################################
// Logic to write to registers in PCODAR block
//###################################################

	 if (reg_id>=0x005C && reg_id<=0x0081)          /* CIDXR to CLRCTI */
    {   
		 if (reg_id<=0x0061)
			  _poke(iw.pcodar+offset,(BYTE)datum);

       else /* one of the indexed registers */
		 {
			 BYTE cidxr;

			 cidxr = _peek(iw.pcodar);
			 cidxr = (cidxr&0xE0)+index;
			 _poke(iw.pcodar,cidxr);                  /* select register */
	       _poke(iw.cdatap,(BYTE)datum);
		 }
	 }
 //######################################################
// Logic to write to the PnP registers
//######################################################
	 if (reg_id>=0x0082 && reg_id<=0x00B7)
    {
		 if (reg_id==0x0085)
			  {
				_poke(iw.pnprdp,(BYTE)datum);
			   return;
			  }
 
		 if (reg_id<0x0085)
			  _poke((WORD)reg_mnem,(BYTE)datum);

       else /* one of the indexed registers */
		 {
		  if (reg_id>=0x008E&&reg_id<=0x00B7)
			  {
				 _poke(0x0279,0x07);                /* select PLDNI */
				 _poke(0xA79,(BYTE)offset);         /* select logical dev */
			  }
		  _poke(0x0279,index);                    /* select the register */
		  _poke(0xA79,(BYTE)datum);
       }
   }
}
//########################################################################
//
//  FUNCTION: IwaveGetAddr
//
//  PROFILE: This function retrives the value of the local memory address
//           counter (LMALI and LMAHI).
//
//########################################################################
ADDRESS IwaveGetAddr(void)
{
  ADDRESS addr=0L;

  _poke(iw.igidxr,_LMALI);
  addr=(DWORD)_peekw(iw.i16dp);
  _poke(iw.igidxr,_LMAHI);
  addr=addr|(((DWORD)_peek(iw.i8dp))<<16);
  return(addr);
}
//########################################################################
//
// FUNCTION: IwavePrintDma
//
// PROFILE: This function is only for code debug purposes. It prints
//          the settings of the DMA controller registers.
//########################################################################
void IwavePrintDma(DMA *dma)
{
   BYTE low, high;
   WORD reg;

   printf("DMA channel: %d\n",dma->channel);
   printf("dma->cur_page %x    dma->cur_addr = %x\n",dma->cur_page,dma->cur_addr);
   printf("dma->cur_size = %x\n",dma->cur_size);

   _poke(dma->clear_ff,0);                    /* select low byte */
   low=_peek(dma->count);
   high=_peek(dma->count);
   reg= (((WORD)high)<<8) + (WORD)low;
   printf("Count Reg : %x\n",reg);

   _poke(dma->clear_ff,0);                    /* select low byte */
   low=_peek(dma->addr);
   high=_peek(dma->addr);
   reg= (((WORD)high)<<8) + (WORD)low;
   printf("Page Reg: %x     Base Addr Reg : %x\n",_peek(dma->page),reg);
  
   printf("Current Mode = %x\n",dma->cur_mode);
}
//########################################################################
//
//  FUNCTION : WriteEnable
//
//  PROFILE: This function will issue a write-enable instruction to the
//           serial EEPROM so that it can be modified via PSECI. The
//           write-enable instruction consists of the stream 10011xxxxxx.
//           In executing this instruction, keep in mind that SECS must
//           remain low for at least 250ns between instructions.
//
//########################################################################
void WriteEnable(void)
{
  BYTE i;
	for (i=0;i<4;i++){
		  PokePSECI(0x00);  /* SESK low,  SECS=0, SEDI=0 */
		  PokePSECI(0x04);} /* SESK high */

	PokePSECI(0x0A);       /* SESK low, SEDI=1, SECS=1 */
	PokePSECI(0x0E);       /* SESK high */
	PokePSECI(0x08);       /* SESK low, SEDI=0 */
	PokePSECI(0x0C);       /* SESK high */
	PokePSECI(0x08);       /* SESK low, SEDI=0 */
	PokePSECI(0x0C);       /* SESK high */
	PokePSECI(0x0A);       /* SESK low, SEDI=1 */
	PokePSECI(0x0E);       /* SESK high */
	PokePSECI(0x0A);       /* SESK low, SEDI=1 */
	PokePSECI(0x0E);       /* SESK high */

	for (i=0; i<6; i++){
		  PokePSECI(0x08);  /* SESK low, SEDI=0 */
		  PokePSECI(0x0C);} /* SESK high */

	for (i=0; i<4; i++){
		  PokePSECI(0x00);  /* SESK low, SEDI=0, SECS=0 */
		  PokePSECI(0x04);} /* SESK high */

	PokePSECI(0x00);       /* SESK=0 */
}
//########################################################################
//
//  FUNCTION : PokePSECI
//
//  PROFILE: This function is a utility that allows writing to the
//           PSECI register to control the InterWave IC. After each write
//           there is a small delay that must be at least 1us as required
//           by the KM93C66 (Max Freq is 1MHz). This function is used
//           in conjunction by IwavePokeEEPROM() to program the serial
//           EEPROM.
//########################################################################
void PokePSECI(BYTE data)
{
	BYTE i;

	_poke(_PIDXR,0xF1);
	_poke(0xA79,data);
	for (i=0;i<4;i++)           /* create a small delay */
		  (void)_peek (iw.pnprdp);
}
//########################################################################
//
//  FUNCTION : WriteOPCode
//
//  PROFILE: This function is a utility that writes the write instruction
//           code to the serial EEPROM. This is needed right before the
//           address of every word that you write to the EEPROM. The
//           sequence is 101 (KM93C66).
//########################################################################
void WriteOPCode(void)
{
  	 PokePSECI(0x0A);      /* SECS=1, SEDI=1 */
	 PokePSECI(0x0E);      /* SESK=1 */
	 PokePSECI(0x08);      /* SECS=1, SEDI=0 */
	 PokePSECI(0x0C);      /* SESK=1 */
	 PokePSECI(0x0A);      /* SECS=1, SEDI=1 */
	 PokePSECI(0x0E);      /* SESK=1 */
}
//########################################################################
//
//  FUNCTION : ReadOPCode
//
//  PROFILE: This function is a utility that writes the read instruction
//           code to the serial EEPROM. This is needed right before the
//           address of every word that you read from in the EEPROM. The
//           sequence is 110 (KM93C66).
//########################################################################
void ReadOPCode(void)
{
  	 PokePSECI(0x0A);      /* SECS=1, SEDI=1 */
	 PokePSECI(0x0E);      /* SESK=1 */
	 PokePSECI(0x0A);      /* SECS=1, SEDI=0 */
	 PokePSECI(0x0E);      /* SESK=1 */
	 PokePSECI(0x08);      /* SECS=1, SEDI=0 */
	 PokePSECI(0x0C);      /* SESK=1 */
}
//########################################################################
//
// FUNCTION: IwavePokeEEPROM
//
// PROFILE: This function allows the caller to program the contents of
//          the serial EEPROM via PSECI[3:0]. The function places the
//          serial EEPROM in direct-control mode first (PSEENI[0]=1).
//          The serial EEPROM is written starting at address 0 with the
//          data in the buffer pointed to by "data".
//
//          Note that this program is written to program serial EEPROM
//          unit KM93C66 which is a 4K-bit unit organized as 256x16.
//          Compatible units can also be programmed.
//
//          The function assumes the InterWave IC is in configuration
//          mode (this means a CSN has been assigned to the board).
//
//########################################################################
void IwavePokeEEPROM(BYTE *data)
{
	int i;
   BYTE datum;
	WORD j=0x00, se_data;

	WriteEnable();        /* enable all writes */

	while (j<256)
	{
	 se_data=0x00;
	 se_data=*data++;
	 se_data|=(((WORD)(*data++))<<8);
	 WriteOPCode();        /* send write instruction OP code (101)*/
	 for (i=7;i>=0;i--){   /* set the address to write to */
			datum=(((j>>i)&0x01)<<1)|0x08;
			PokePSECI(datum);                     /* SESK=0 */
			PokePSECI((BYTE)(datum|0x04));}       /* SESK=1 */
	 for (i=0;i<16;i++){ /* write two bytes of data (D0-D15) */
			datum=(((se_data>>i)&0x01)<<1)|0x08;
			PokePSECI(datum);                     /* SESK=0 */
			PokePSECI((BYTE)(datum|0x04));}       /* SESK=1 */
								  /* Initiate self-timed write cycle */
	 PokePSECI(0x00);      /* SESK=0, SECS=0 (must) */
	 PokePSECI(0x04);      /* SESK=1 */
	 PokePSECI(0x00);      /* SESK=0, SECS=0 (must) */
	 PokePSECI(0x04);      /* SESK=1 */
	 PokePSECI(0x08);      /* SESK=0 and SECS=1 */
	 while(!(_peek(iw.pnprdp)&0x01))  /* wait for write-cycle to complete */
    {
	   PokePSECI(0x0C);       /* SESK=1 */
	   PokePSECI(0x08);       /* SESK=0 */
    }
	 PokePSECI(0x00);       /* SECS=0 */
	 PokePSECI(0x00);       /* SESK=0 */
	 PokePSECI(0x04);       /* SESK=1 */
	 j++;
	}
}
//########################################################################
//
// FUNCTION: IwavePeekEEPROM
//
// PROFILE: This function allows the caller to read the contents of
//          the serial EEPROM via PSECI[3:0]. The function places the
//          serial EEPROM in direct-control mode first (PSEENI[0]=1).
//          The serial EEPROM is read starting at address 0 and its
//          contents are returned into the buffer pointed to by "data".
//
//          Note that this program is written to program serial EEPROM
//          unit KM93C66 which is a 4K-bit unit organized as 256x16.
//          Compatible units can also be programmed.
//
//          The function assumes the InterWave IC is in configuration
//          mode (this means a CSN has been assigned to the board).
//
//########################################################################
void IwavePeekEEPROM(BYTE *data)
{
	int i;
   BYTE datum;
	WORD j=0x00, se_data;

	while (j<256)
	{
	 se_data=0x00;
	 ReadOPCode();        /* send read instruction OP code (110)*/
	 for (i=7;i>=0;i--){  /* set the address to write to */
			datum=(((j>>i)&0x01)<<1)|0x08;
			PokePSECI(datum);                     /* SESK=0 */
			PokePSECI((BYTE)(datum|0x04));}       /* SESK=1 */
	 PokePSECI(0x08);    /* SESK=0 */
	 PokePSECI(0x0C);    /* SESK=1 */
	 for (i=0;i<16;i++){ /* read two bytes of data (D0-D15) */
	 		se_data|=((_peek(iw.pnprdp)&0x01)<<i);
	    	PokePSECI(0x08);                      /* SESK=0 */
		 	PokePSECI(0x0C);}                     /* SESK=1 */
	 PokePSECI(0x00);      /*SESK=0, SECS=0, SEDI=0 */
	 *data++=(BYTE)se_data;
	 *data++=(BYTE)(se_data>>8);
	 j++;
	}
}
//########################################################################
//
//	FUNCTION: IwaveDelay
//
//	PROFILE:  This function can be called to cause a delay of up to
//            52 milliseconds during program execution. The argument
//            specifies the number of milliseconds to block and should
//            be a value in the range 1 to 52.
//
//########################################################################
void IwaveDelay(WORD count)
{
  WORD cur_cnt=0, last_cnt;
  BYTE reg, portb;
      
      count=1193*count;					  /* convert number of ms to counter */
	   last_cnt=count;
      portb=_peek(0x61)&0xFC;
      _poke(0x61,portb);              /* disable counter */
      _poke(0x43,0xB0);					  /* load LSB first then MSB */
      _poke(0x42,(BYTE)count);
      _poke(0x42,(BYTE)(count>>8));
      _poke(0x61,(BYTE)(portb|0x01)); /* enable counter */
      while(cur_cnt<=count)
      {
        _poke(0x43,0x80);             /* latch counter */
        reg=_peek(0x42);				  /* read latched value */
        cur_cnt=(((WORD)_peek(0x42))<<8)|reg;
        if (cur_cnt>last_cnt)
            break;
        last_cnt=cur_cnt;
      }					 
      _poke(0x61,portb);              /* disable counter */      
 }
#if defined(__BC32__) || defined(__WATCOMC__) || defined(__HIGHC__)
//
// the following functions are provided by the compiler or
// by external code.
//
#else
//########################################################################
//
//  FUNCTION : _peek
//
//  PROFILE: This function will read a hardware port and return its
//           value. An 8-bit port is assumed.
//
//########################################################################
BYTE _peek(PORT portid)
{
/*
#ifdef __WATCOMC__
	return((BYTE)inp(portid));
#else
#ifdef __HIGHC__
	return((BYTE)inp(portid));
#else
*/
	asm
	{
		 mov DX,[portid]            ;
		 in  AL,DX                  ;
	}
//#endif
//#endif
}
//########################################################################
//
//  FUNCTION : _peekw
//
//  PROFILE: This function will read a hardware port and return its
//           value. A 16-bit port is assumed.
//
//########################################################################
WORD  _peekw(PORT portid)
{
/*
#ifdef __WATCOMC__
	return(inpw(portid));
#else
#ifdef __HIGHC__
	return(inpw(portid));
#else
*/
	asm
	{
	    mov DX,[portid]            ;
	    in  AX,DX                  ; /* Read word of data */
	}
//#endif
//#endif
}
//########################################################################
//
//  FUNCTION : _poke
//
//  PROFILE: This function will write a byte to a hardware port
//           An 8-bit port is assumed.
//
//########################################################################
void _poke(PORT portid,BYTE datum)
{
/*
#ifdef __WATCOMC__
	outp(portid,datum);
#else
#ifdef __HIGHC__
	outp(portid,datum);
#else
*/
 asm
	{
		 mov AL,[datum]             ;
		 mov DX,[portid]            ;
		 out DX,AL                  ; /* Write byte of data */
	}
//#endif
//#endif
}
//########################################################################
//
//  FUNCTION : _pokew
//
//  PROFILE: This function will write to a hardware port. A 16-bit
//           port is assumed.
//
//########################################################################
void _pokew(PORT portid, WORD datum)
{
/*
#ifdef __WATCOMC__
	outpw(portid,datum);
#else
#ifdef __HIGHC__
	outpw(portid,datum);
#else
*/
	asm
	{
		 mov AX,[datum]             ;
		 mov DX,[portid]            ;
		 out DX,AX                  ; /* Write word of data */
	}
//#endif
//#endif
}
#endif // !(__BC32__ ||__WATCOMC__||__HIGHC__)
//########################################################################
//
//  FUNCTION : ReadWaveHeader
//
//  PROFILE: This function will read in the header of a .WAV sound file.
//           The information extracted from the file will be placed inside
//           the elements of a structure pointed to by "wav". This function
//           can be used to help create a wave file player.
//
//########################################################################
BOOL ReadWaveHeader(char *fname,WAV *wav)
{
struct riff_header 
  {
	char RIFF[4];
	unsigned long file_size;
	char WAVE[4];
	char fmt[4];
	unsigned long format_size;
  } riff_header;

struct format_header 
  {
	WORD wFormatTag;
	WORD nChannels;
	DWORD nSamplesPerSec;
	DWORD nAvgBytesPerSec;
	WORD nBlockAlign;
	WORD nBitsPerSample;
  } format_header;

struct data_header 
 {
   char DATA[4];
	unsigned long size;
 } data_header;

	FILE *fp;

	if ((fp = fopen((const char *)fname, "rb"))==NULL)
		  return(FALSE);
	fread(&riff_header, sizeof(riff_header), 1, fp);
	if (strncmp(riff_header.RIFF, "RIFF", 4) == 0 &&
		 strncmp(riff_header.WAVE, "WAVE", 4) == 0 &&
		 strncmp(riff_header.fmt, "fmt ", 4) == 0)
	 {
		if (fread(&format_header, riff_header.format_size, 1,fp) != 1)
		  {
			fclose(fp);
			return(FALSE);
		  }
		if (format_header.wFormatTag != 1)
        {
			fclose(fp);
			return(FALSE);
        }
		if (format_header.nChannels == 1)
			  wav->stereo = FALSE;
		else
			  wav->stereo = STEREO;
		if (format_header.nBitsPerSample == 16)
			  wav->sixteen = TRUE;
		else
			  wav->sixteen = FALSE;

		wav->freq = format_header.nSamplesPerSec;

		while(fread(&data_header, sizeof(data_header), 1, fp))
		  {
			if (strncmp(data_header.DATA, "data", 4) == 0)
				 break;
			fseek(fp, data_header.size, 1);
		  }
		 if (strncmp(data_header.DATA, "data", 4))
        {
			  fclose(fp);
			  return(FALSE);
        }
		wav->begin_fpos = ftell(fp);
		wav->end_fpos   = data_header.size - (wav->begin_fpos);
      wav->data_size  = data_header.size;
		fclose(fp);
		return(TRUE);
	}
	else
   {
		fclose(fp);
		return(FALSE);
   }
}
//########################################################################
//
//  FUNCTION : GetSamplePosition
//
//  PROFILE: This function will read the DMA count register and determine
//           how many bytes have been sent or received to/from the codec
//           FIFOs.
//
//########################################################################
WORD GetSamplePosition(DMA *dma)
{
  WORD total_size=0;

  total_size=dma->cur_size-IwaveGetDmaPos(dma)+dma->amnt_sent - 1;
  if (dma->channel>=4)
		 total_size<<=1;
  return(total_size);
}
//########################################################################
//
//  FUNCTION : IwaveCheckSum
//
//  PROFILE: This function looks at the first 8 bytes of PnP resource data
//           and will return the checksum over the those 8 bytes.
//
//########################################################################
BYTE IwaveCheckSum(BYTE *serial)
{
	 unsigned char i,j,bit;
	 unsigned char checksum=0x6A;
//
	 for (i=0;i<8;i++)
	 {
	  for (j=0;j<8;j++)
		{
		 bit=(serial[i]>>j)&0x01;
		 checksum=((((checksum^(checksum>>1))&0x01)^bit)<<7)|(checksum>>1);
		}
	 }
  return(checksum);
}
//########################################################################
//
//  FUNCTION : IwaveJoyPosition
//
//  PROFILE: This function returns the X/Y position of the joystick. It
//           also returns the value stored in GGCR so a caller can
//           determine the state of the buttons.
//
//########################################################################
BYTE IwaveJoyPosition(short *xjoy, short *yjoy)
{
  BYTE val;
//
  *xjoy=0;
  *yjoy=0;
  _poke(iw.p201ar,0x00);   // Start tracking X/Y Position
  while(TRUE)
  { 
    val=_peek(iw.p201ar);
    if (val&0x01)
	(*xjoy)++;
    if (val&0x02)
	(*yjoy)++;
    if (!(val&0x03)) break;
    if ((*xjoy) > 10000 || (*yjoy) > 10000)
	break;
  }
  return(_peek(iw.p201ar));
}