Metropoli BBS
VIEWER: mdma.c MODE: TEXT (ASCII)
#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <malloc.h>
#include <conio.h>
#include "mtypes.h"
#include "mdma.h"


/* DMA Controler #1 (8-bit controller) */
#define DMA1_STAT       0x08            /* read status register */
#define DMA1_WCMD       0x08            /* write command register */
#define DMA1_WREQ       0x09            /* write request register */
#define DMA1_SNGL       0x0A            /* write single bit register */
#define DMA1_MODE       0x0B            /* write mode register */
#define DMA1_CLRFF      0x0C            /* clear byte ptr flip/flop */
#define DMA1_MCLR       0x0D            /* master clear register */
#define DMA1_CLRM       0x0E            /* clear mask register */
#define DMA1_WRTALL     0x0F            /* write all mask register */

/* DMA Controler #2 (16-bit controller) */
#define DMA2_STAT       0xD0            /* read status register */
#define DMA2_WCMD       0xD0            /* write command register */
#define DMA2_WREQ       0xD2            /* write request register */
#define DMA2_SNGL       0xD4            /* write single bit register */
#define DMA2_MODE       0xD6            /* write mode register */
#define DMA2_CLRFF      0xD8            /* clear byte ptr flip/flop */
#define DMA2_MCLR       0xDA            /* master clear register */
#define DMA2_CLRM       0xDC            /* clear mask register */
#define DMA2_WRTALL     0xDE            /* write all mask register */

#define DMA0_ADDR       0x00            /* chan 0 base adddress */
#define DMA0_CNT        0x01            /* chan 0 base count */
#define DMA1_ADDR       0x02            /* chan 1 base adddress */
#define DMA1_CNT        0x03            /* chan 1 base count */
#define DMA2_ADDR       0x04            /* chan 2 base adddress */
#define DMA2_CNT        0x05            /* chan 2 base count */
#define DMA3_ADDR       0x06            /* chan 3 base adddress */
#define DMA3_CNT        0x07            /* chan 3 base count */
#define DMA4_ADDR       0xC0            /* chan 4 base adddress */
#define DMA4_CNT        0xC2            /* chan 4 base count */
#define DMA5_ADDR       0xC4            /* chan 5 base adddress */
#define DMA5_CNT        0xC6            /* chan 5 base count */
#define DMA6_ADDR       0xC8            /* chan 6 base adddress */
#define DMA6_CNT        0xCA            /* chan 6 base count */
#define DMA7_ADDR       0xCC            /* chan 7 base adddress */
#define DMA7_CNT        0xCE            /* chan 7 base count */

#define DMA0_PAGE       0x87            /* chan 0 page register (refresh)*/
#define DMA1_PAGE       0x83            /* chan 1 page register */
#define DMA2_PAGE       0x81            /* chan 2 page register */
#define DMA3_PAGE       0x82            /* chan 3 page register */
#define DMA4_PAGE       0x8F            /* chan 4 page register (unuseable)*/
#define DMA5_PAGE       0x8B            /* chan 5 page register */
#define DMA6_PAGE       0x89            /* chan 6 page register */
#define DMA7_PAGE       0x8A            /* chan 7 page register */

#define MAX_DMA         8

#define DMA_DECREMENT   0x20    /* mask to make DMA hardware go backwards */

typedef struct {
	UBYTE dma_disable;      /* bits to disable dma channel */
	UBYTE dma_enable;       /* bits to enable dma channel */
	UWORD page;                     /* page port location */
	UWORD addr;                     /* addr port location */
	UWORD count;            /* count port location */
	UWORD single;           /* single mode port location */
	UWORD mode;                     /* mode port location */
	UWORD clear_ff;         /* clear flip-flop port location */
	UBYTE write;            /* bits for write transfer */
	UBYTE read;                     /* bits for read transfer */
} DMA_ENTRY;

#ifdef __WATCOMC__

#define ENTER_CRITICAL IRQ_PUSH_OFF()
extern void IRQ_PUSH_OFF (void);
#pragma aux IRQ_PUSH_OFF =      \
	"pushfd",                   \
	"cli";

#define ENTER_CRITICAL_ON IRQ_PUSH_ON()
extern void IRQ_PUSH_ON (void);
#pragma aux IRQ_PUSH_ON =       \
	"pushfd",                   \
	"sti";

#define LEAVE_CRITICAL IRQ_POP()
extern void IRQ_POP (void);
#pragma aux IRQ_POP =   \
	"popfd";
#define LEAVE_CRITICAL_ON LEAVE_CRITICAL

#else

#define ENTER_CRITICAL asm{ pushf; cli }
#define LEAVE_CRITICAL asm{ popf }

#endif

/* Variables needed ... */

static DMA_ENTRY mydma[MAX_DMA] = {

/* DMA channel 0 */
			{0x04,0x00,DMA0_PAGE,DMA0_ADDR,DMA0_CNT,
			 DMA1_SNGL,DMA1_MODE,DMA1_CLRFF,0x48,0x44},

/* DMA channel 1 */
			{0x05,0x01,DMA1_PAGE,DMA1_ADDR,DMA1_CNT,
			 DMA1_SNGL,DMA1_MODE,DMA1_CLRFF,0x49,0x45},

/* DMA channel 2 */
			{0x06,0x02,DMA2_PAGE,DMA2_ADDR,DMA2_CNT,
			 DMA1_SNGL,DMA1_MODE,DMA1_CLRFF,0x4A,0x46},

/* DMA channel 3 */
			{0x07,0x03,DMA3_PAGE,DMA3_ADDR,DMA3_CNT,
			 DMA1_SNGL,DMA1_MODE,DMA1_CLRFF,0x4B,0x47},

/* DMA channel 4 */
			{0x04,0x00,DMA4_PAGE,DMA4_ADDR,DMA4_CNT,
			 DMA2_SNGL,DMA2_MODE,DMA2_CLRFF,0x48,0x44},

/* DMA channel 5 */
			{0x05,0x01,DMA5_PAGE,DMA5_ADDR,DMA5_CNT,
			 DMA2_SNGL,DMA2_MODE,DMA2_CLRFF,0x49,0x45},

/* DMA channel 6 */
			{0x06,0x02,DMA6_PAGE,DMA6_ADDR,DMA6_CNT,
			 DMA2_SNGL,DMA2_MODE,DMA2_CLRFF,0x4A,0x46},

/* DMA channel 7 */
			{0x07,0x03,DMA7_PAGE,DMA7_ADDR,DMA7_CNT,
			 DMA2_SNGL,DMA2_MODE,DMA2_CLRFF,0x4B,0x47},
};


#define LPTR(ptr) (((ULONG)FP_SEG(ptr)<<4)+FP_OFF(ptr))


#ifdef __WATCOMC__


static UWORD dma_selector;

void *Dma_AllocMem(UWORD size)
/*
	Allocates a dma buffer of 'size' bytes. 
	this watcom dma memory allocation doesn't check if it's 
	page-continuous, and can only be used to allocate exactly 1 block

	returns NULL if failed.
*/
{
	union REGS r;

	r.x.eax = 0x0100;           /* DPMI allocate DOS memory */
	r.x.ebx = (size + 15) >> 4; /* Number of paragraphs requested */

	int386 (0x31, &r, &r);

	if( r.x.cflag )  /* Failed */
		return ((ULONG) 0);

	dma_selector=r.x.edx;

	return (void *) ((r.x.eax & 0xFFFF) << 4);
}


void Dma_FreeMem(void *p)
{
	union REGS r;
	r.x.eax = 0x0101;           /* DPMI free DOS memory */
	r.x.edx = dma_selector;     /* base selector */
	int386 (0x31, &r, &r);
}


#else


void *Dma_AllocMem(UWORD size)
/*
	Allocates a dma buffer of 'size' bytes.
	
	returns NULL if failed.
*/
{
	char huge *p1;
	char huge *p2;
	ULONG s1;

	// Alloceer size bytes van de far heap

	if((p1=malloc((ULONG)size))==NULL) return NULL;

	s1=LPTR(p1);

	/* Controleer of de gehele buffer
	  zich in hetzelfde 64k gebied bevindt */

	if( (s1>>16) != ( (s1+size-1) >> 16 ) ){

		/* Als dit niet het geval is, p1 kleiner maken
		   zodat het precies tot die 64k grens loopt */

		realloc(p1,0x10000L-(s1&0xffff));

		/* en opnieuw proberen een buffer te alloceren */

		p2=Dma_AllocMem(size);

		/* en de oude vrijgeven */

		free(p1);

		/* return met nieuw gevonden pointer */

		p1=p2;
	}
	return(p1);
}


void Dma_FreeMem(void *p)
{
	free(p);
}
#endif


int Dma_Start(int channel,void *pc_ptr,UWORD size,int type)
{
	DMA_ENTRY *tdma;
	ULONG s_20bit,e_20bit;
	UWORD spage,saddr,tcount;
	UWORD epage,eaddr;
	UBYTE cur_mode;

	tdma=&mydma[channel];           /* point to this dma data */

	/* Convert the pc address to a 20 bit physical
	   address that the DMA controller needs */

#ifdef __WATCOMC__
	s_20bit = pc_ptr;
#else
	s_20bit = LPTR(pc_ptr);
#endif

	e_20bit = s_20bit + size - 1;
	spage = s_20bit>>16;
	epage = e_20bit>>16;

	if(spage != epage) return 0;

	if(channel>=4){
		/* if 16-bit xfer, then addr,count & size are divided by 2 */
		s_20bit = s_20bit >> 1;
		e_20bit = e_20bit >> 1;
		size = size >> 1;
	}

	saddr=s_20bit&0xffff;

	tcount = size-1;

	switch (type){

		case READ_DMA:
			cur_mode = tdma->read;
			break;

		case WRITE_DMA:
			cur_mode = tdma->write;
			break;

		case INDEF_READ:
			cur_mode = tdma->read | 0x10;   /* turn on auto init */
			break;

		case INDEF_WRITE:
			cur_mode = tdma->write | 0x10;  /* turn on auto init */
			break;
	}

//        ENTER_CRITICAL;
	outportb(tdma->single,tdma->dma_disable);               /* disable channel */
	outportb(tdma->mode,cur_mode);                                  /* set mode */
	outportb(tdma->clear_ff,0);                                             /* clear f/f */
	outportb(tdma->addr,saddr&0xff);                                /* LSB */
	outportb(tdma->addr,saddr>>8);                                  /* MSB */
	outportb(tdma->page,spage);                                             /* page # */
	outportb(tdma->clear_ff,0);                                             /* clear f/f */
	outportb(tdma->count,tcount&0x0ff);                             /* LSB count */
	outportb(tdma->count,tcount>>8);                                /* MSB count */
	outportb(tdma->single,tdma->dma_enable);                /* enable */
//        LEAVE_CRITICAL;

	return 1;
}


void Dma_Stop(int channel)
{
	DMA_ENTRY *tdma;
	tdma=&mydma[channel];                                                   /* point to this dma data */
	outportb(tdma->single,tdma->dma_disable);               /* disable chan */
}



UWORD Dma_Todo(int channel)
{
	UWORD creg;
	UWORD val1,val2;

	DMA_ENTRY *tdma=&mydma[channel];

	creg=tdma->count;

	ENTER_CRITICAL;

//        outportb(tdma->clear_ff,0xff);

	redo:
	val1=inportb(creg);
	val1|=inportb(creg)<<8;
	val2=inportb(creg);
	val2|=inportb(creg)<<8;

	val1-=val2;
	if(val1>0x40) goto redo;

	LEAVE_CRITICAL;

//      if(val1<0xffc0) goto redo;

	if(channel>3) val2<<=1;

	return val2;
}

[ RETURN TO DIRECTORY ]