#include <dos.h>
#include <conio.h>
#include "iwdefs.h"
#include "iwprotos.h"
extern IWAVE iw;
//########################################################################
//
// FILE   : iwdma.c
//
// REMARKS: This file contains the definitions of DDK functions in
//          the DMA transfer family.
//
// UPDATE: 4/13/95
//
//########################################################################
//
// FUNTION : IwaveDmaXfer
//
// PROFILE : This function is an upper level driver that programs the DMA
//           controller, the InterWave and triggers a DMA transfer to or
//           from local memory. The function will return DMA_OK if no
//           problems were encountered.
//
//########################################################################
FLAG IwaveDmaXfer(DMA *dma,
                  WORD size)            /* Bytes to DMA */
{
 ADDRESS end_addr;
 ADDRESS nxt_local;
#ifdef FLAT_MODEL
 void *nxt_pc_ram;
#else
 void far *nxt_pc_ram;
#endif
 int flag;
 WORD cur_size;
 WORD nxt_size;
 BYTE two_pages = FALSE;
 
 if (iw.smode==GUS_MODE)
  {
//########################################################################
// Check for GUS-page cross over. This restriction applies
// to 16-bit DMA channels. Split the xfer if there is a LM
// cross over a 256KB page.
//########################################################################

     ADDRESS pageS;
     ADDRESS pageE;

     end_addr = dma->local+(DWORD)size-1L; /* Last byte in LM*/

     pageS = dma->local>>18;
     pageE = end_addr>>18;

    if (pageS != pageE)
     {
       nxt_local = pageE<<18;                  /* start of GUS page */
       nxt_size = (WORD)(end_addr-nxt_local);  /* size of 2nd chunk */
       cur_size = size-nxt_size;               /* size of 1st chunk */
#ifdef FLAT_MODEL
       nxt_pc_ram = (void *)((char *)dma->pc_ram+cur_size);
#else
       nxt_pc_ram = (void far*)((char far*)dma->pc_ram+cur_size);
#endif
       two_pages = TRUE;
     }
     else
       cur_size = size;
   }
 else                                      /* Enhanced Mode */
   cur_size = size;
   
 flag = IwaveDmaPage(dma,cur_size);

 if (flag != DMA_OK)
     return(flag);

 if (two_pages)
  {
    dma->pc_ram = nxt_pc_ram;
    dma->local = nxt_local;

    flag = IwaveDmaPage(dma,nxt_size);

    if (flag!=DMA_OK)
        return(flag);
  }
 return(DMA_OK);
}
//########################################################################
//
// FUNTION : IwaveDmaPage
//
// PROFILE : This function sets up the InterWave and then initiates
//           the transfer of up to one DMA page (64K) to or from the
//           the InterWave hardware.
//
//########################################################################
FLAG IwaveDmaPage(DMA *dma, WORD size)
{
 ADDRESS local_mem;
 BYTE control;

 if (IwaveDmaCtrl(dma,size)!=DMA_OK)
	  return(~DMA_OK);

 if (dma->channel >=4)                       /* 16-bit DMA channel */
		 local_mem=IwaveAddrTrans(dma->local);
 else                                        /* 8-bit DMA channel */
		 local_mem = dma->local;

//##########################################################
//	Set up DMA start address in InterWave's DMA Address
//	registers LDSALI and LDSAHI.
//##########################################################

	ENTER_CRITICAL;

	if (iw.smode==GUS_MODE)                     /* GUS Mode */
	 {
	  _poke(iw.igidxr,_LDSALI);                 /* select LDSALI */
	  _pokew(iw.i16dp,(WORD)(local_mem>>4));    /* set A[19:4] */
	 }
	else                                        /* Enhanced Mode */
	{
	BYTE ldsahi;

	ldsahi =  (BYTE)((local_mem>>16)&0x000000F0L);  /* isolate A[23:20] */
	ldsahi = ldsahi+(BYTE)(local_mem&0x0000000FL);

	_poke(iw.igidxr,_LDSALI);                /* select LDSALI */
	_pokew(iw.i16dp,(WORD)(local_mem>>4));   /* set A[19:4] */
	_poke(iw.igidxr,_LDSAHI);                /* select LDSAHI */
	_poke(iw.i8dp,ldsahi);                   /* set A[23:20,3:0] */
	}
  iw.flags |= DMA_BUSY;                     /* flag impending transfer */
  control = dma->cur_control;

//########################################################################
//  The only settings of the DMA control register that
//  this function does not set are DMA_UPLOAD, and DMA_INV.
//  All others are set here.
//########################################################################

  control &= (DMA_UPLOAD|DMA_DATA_16|DMA_WIDTH_16|DMA_INV|DMA_R3);
  control &= DMA_R0;                         /* fastest */
  control |= (DMA_ENABLE|DMA_IRQ_ENABLE);
  _poke(iw.igidxr,_LDMACI);                  /* select DMA ctrl reg */
  _poke(iw.i8dp,control);                    /* program LDMACI and trigger DMA*/
  LEAVE_CRITICAL;

return(DMA_OK);
}
//########################################################################
//
// FUNTION : IwaveDmaCtrl
//
// PROFILE : This function readies the DMA controller on the PC
//           for an impending DMA transfer and also initializes the DMA
//           structure pointed to by "dma". It then calls "IwaveDmaPgm"
//           to program the DMA controller. It does NOT start the
//           transfer.
//
// INPUTS  : the arguments to this function are
//
//           size   - size in bytes of block of data to transfer
//
//
//           dma    - a pointer to a DMA structure containing
//                    relevant controller port addresses and data.
//
//########################################################################
FLAG IwaveDmaCtrl(DMA *dma, WORD size)
{
DWORD phys_addrS;
DWORD phys_addrE;
WORD  base_addrS;
WORD  base_addrE;
WORD  pageS;
WORD  pageE;
#ifndef FLAT_MODEL
WORD  seg;
WORD  off;
#endif

//##############################################
// Check to see if DMA is in progress
//##############################################
 if (dma->flags & DMA_BUSY)
     return(DMA_BUSY);

 dma->flags|=DMA_BUSY;						  /* mark channel busy in background */
 iw.flags|=DMA_BUSY;	                    /* mark channel busy in foreground */
 
//###################################################
// Generate physical address for DMA controller.
//###################################################

#ifdef FLAT_MODEL
phys_addrS = (DWORD)dma->pc_ram;
#else                                  /* segmented model */
seg = FP_SEG(dma->pc_ram);
off = FP_OFF(dma->pc_ram);
phys_addrS=((((DWORD)seg)<<4) + (DWORD)off);
#endif

 phys_addrE=phys_addrS+(DWORD)size-1L; /* addr. of last byte of data */

//########################################################################
// Isolate the pages where the first and last byte of data reside.
//########################################################################

 pageS = (WORD)(phys_addrS>>16);
 pageE = (WORD)(phys_addrE>>16);

 if (dma->channel >= 4) /* 16-bit DMA channel */
  {
//#####################################################
// For 16-bit DMA channels,  the address
// translation mechanism of the 8237A requires
// a shift to the right.
//#####################################################

	  phys_addrS= phys_addrS>>1;
	  phys_addrE= phys_addrE>>1;
     size = size>>1;              /* two bytes per count */
  }

 base_addrS = (WORD)phys_addrS;   /* DMA base-addr register */
 base_addrE = (WORD)phys_addrE;

//########################################################################
// If the block of data crosses over a DMA page, then the
// transfer will have to be split into two transfers. The second
// transfer will be initiated by the handler.
//########################################################################
  if (pageS != pageE)              /* DMA page cross-over */
   {
    dma->flags|=DMA_SPLIT;         /* flag the need for two xfers */
    dma->nxt_page = pageE;
      if (dma->channel >= 4)
       {
         if (dma->nxt_page & 0x01)
             dma->nxt_addr=0x8000;
         else 
             dma->nxt_addr=0;

         base_addrE&=0x7fff;
       }
      else                         /* 8-bit DMA channel */
         dma->nxt_addr = 0;

     dma->nxt_size = base_addrE;
     size = size-base_addrE-1;     /* size of first xfer */
   }
  else                             /* same page */
   dma->flags&=~DMA_SPLIT;         /* just one transfer */

//########################################################################
// if the controller is to be placed in auto-initialization
// mode the transfer can not be split.
//########################################################################

if ((dma->type==AUTO_READ||dma->type==AUTO_WRITE)&&(dma->flags&DMA_SPLIT))
     return(DMA_BAD_ADDR);

//#############################################
// Initialize DMA structure for transfer
//#############################################
 dma->cur_page = pageS;
 dma->cur_addr = base_addrS;
 dma->amnt_sent = 0;          /* amount xferred so far */
 dma->cur_size = size;
  
 switch(dma->type)
     {
          case DMA_READ:
               dma->cur_mode = dma->read;
               break;
          case DMA_WRITE:
               dma->cur_mode = dma->write;
               break;
          case AUTO_READ:
               dma->cur_mode = dma->read|AUTO_INIT;
               break;
          case AUTO_WRITE:
               dma->cur_mode = dma->write|AUTO_INIT;
               break;
     } 

  IwaveDmaPgm(dma);   // Now program the controller

return(DMA_OK);
}
//########################################################################
//
// FUNCTION: IwaveDmaNext
//
// PROFILE: In cases where the data to be DMA'd crosses over one DMA page
//          in PC ram, this function will be called by the DMA handler
//          to send the data in the second DMA page.
//
//########################################################################
void IwaveDmaNext(DMA *dma)
{
  dma->flags&=~DMA_SPLIT;                           /* turn off cross-over flag */
  dma->amnt_sent=dma->cur_size;                     /* amount sent so far */
  dma->cur_size=dma->nxt_size;
  IwaveDmaPgm(dma);                                 /* program DMA controller */
  ENTER_CRITICAL;
  if (dma->flags&0x8000){	                         /* Interleaved DMA ? */
	_poke(iw.igidxr,_LDICI);                         /* select LDICI */
	_pokew(iw.i16dp,_peek(iw.i16dp)|0x0200);}        /* trigger DMA  */
  else{
  _poke(iw.igidxr,_LDMACI);                         /* select LDMACI */
  _poke(iw.i8dp,(BYTE)(_peek(iw.i8dp)|DMA_ENABLE));}/* trigger DMA */

  LEAVE_CRITICAL;
}
//########################################################################
//
// FUNTION : IwaveDmaPgm
//
// PROFILE : This function does the programming of the DMA controller
//           for an impending DMA transfer. It simply loads the
//           controller's registers with values specified in a DMA
//           structure pointed to by "dma".
//
// INPUT   : the argument to this function is:
//
//           dma   - a pointer to a DMA structure containing the
//                   settings for the DMA controller's registers.
//
//
//########################################################################
void IwaveDmaPgm(DMA *dma)
{
 WORD count;

   count = dma->cur_size-1;

//#############################################################
// Program DMA controller with settings in DMA structure.
//#############################################################

   _poke(dma->single,dma->disable);           /* disable channel */
   _poke(dma->clear_ff,0);                    /* select low byte */
   _poke(dma->addr,(BYTE)dma->cur_addr);      /* low byte of base addr */
   _poke(dma->addr,(BYTE)(dma->cur_addr>>8));	/* high byte of base addr */
   _poke(dma->page,(BYTE)(dma->cur_page));    /* controller page */
   _poke(dma->mode,dma->cur_mode);            /* set mode */
   _poke(dma->clear_ff,0);                    /* select low byte */
   _poke(dma->count,(BYTE)count);             /* low byte of count */
   _poke(dma->count,(BYTE)(count>>8));        /* high byte of count */
   _poke(dma->single,dma->enable);            /* enable channel */
  
//   IwavePrintDma(dma);
}
//########################################################################
//
//  FUNCTION : IwaveDmaWait
//
//  PROFILE: This function will block until a specific DMA transfer is
//           completed. The interrupt handler will clear the third bit
//           in iw.flags.
//
//########################################################################
void IwaveDmaWait(void)
{
  while(iw.flags&DMA_BUSY)    /* wait until flag clears */
  { }
}
//########################################################################
//
// FUNCTION: IwaveGetDmaPos
//
// PROFILE: This function reads the count register of the DMA controller
//          to determine its current position in a transfer.
//
//########################################################################
WORD IwaveGetDmaPos(DMA *dma)
{
  WORD cnt2,low2;
  WORD high1, high2;

  ENTER_CRITICAL;
  while(TRUE)
   {
    _poke(dma->clear_ff,0x00);
    (void)_peek(dma->count);
    high1=(WORD)_peek(dma->count);
    low2=(WORD)_peek(dma->count);
    high2=(WORD)_peek(dma->count);
    if (high1==high2)
     {
       cnt2=(high2<<8)+low2;
       LEAVE_CRITICAL;
		 return(cnt2);
     }
  }
}
//########################################################################
//
// FUNCTION: IwaveDmaIleaved
//
// PROFILE: This function programs the InterWave for an interleaved DMA
//          transfer. It sets up the Interleaved DMA Control register
//          LDICI and the address register LDIBI. Notice that the local
//          memory base address must be aligned to a 256 byte boundary.
//
//          The inputs to this function are as follows:
//
//          dma    - this is a pointer to a structure of type DMA
//                   corresponding to the DMA channel.
//
//          tracks - this is the number of tracks. This can be from 0 to
//                   31.
//
//          size   - this is the size in bytes of each track. This can be
//                   from 512 bytes to 64K bytes.
//
//          ctrl  -  this argument allows to specify the data width as well
//                   as whether to invert the MSB of samples. Its values
//                   could be IDMA_INV or IDMA_WIDTH_16 or
//                   IDMA_INV | IDMA_WIDTH_16 or 0.
//
//########################################################################
void IwaveDmaIleaved(DMA *dma, WORD ctrl, BYTE tracks, WORD size)
{
	BYTE isize=0x00;
	BYTE temp;
	WORD val=0;

	temp=size>>9;
	while (temp!=1)
	{
		temp=temp>>1;
		isize++;
	}
	dma->flags|=0x8000;                     /* flag Interleaved DMA */
   dma->type=DMA_READ;
   val=size;
   temp=tracks;
   while (temp-->1)
          val+=size;
	IwaveDmaCtrl(dma,val);                 /* prime DMA controller */
	val=((--tracks)<<3)|isize|ctrl;
	ENTER_CRITICAL;
	_poke(iw.igidxr,_LDICI);               /* select LDICI */
	_pokew(iw.i16dp,val);       
	_poke(iw.igidxr,_LDIBI);               /* select LDIBI */
	_pokew(iw.i16dp,(WORD)(dma->local>>8));/* set address */
	_poke(iw.igidxr,_LDICI);               /* select LDICI */
	_pokew(iw.i16dp,val|0x0200);           /* trigger DMA */
	LEAVE_CRITICAL;
}

