--- /dev/null
+/*\r
+\r
+Name:\r
+MDMA.C\r
+\r
+Description:\r
+DMA routines\r
+\r
+Portability:\r
+\r
+MSDOS: BC(y) Watcom(y) DJGPP(y)\r
+Win95: n\r
+Os2: n\r
+Linux: n\r
+\r
+(y) - yes\r
+(n) - no (not possible or not useful)\r
+(?) - may be possible, but not tested\r
+\r
+*/\r
+#include <dos.h>\r
+#include <malloc.h>\r
+#include <conio.h>\r
+#include "mdma.h"\r
+\r
+\r
+/* DMA Controler #1 (8-bit controller) */\r
+#define DMA1_STAT 0x08 /* read status register */\r
+#define DMA1_WCMD 0x08 /* write command register */\r
+#define DMA1_WREQ 0x09 /* write request register */\r
+#define DMA1_SNGL 0x0A /* write single bit register */\r
+#define DMA1_MODE 0x0B /* write mode register */\r
+#define DMA1_CLRFF 0x0C /* clear byte ptr flip/flop */\r
+#define DMA1_MCLR 0x0D /* master clear register */\r
+#define DMA1_CLRM 0x0E /* clear mask register */\r
+#define DMA1_WRTALL 0x0F /* write all mask register */\r
+\r
+/* DMA Controler #2 (16-bit controller) */\r
+#define DMA2_STAT 0xD0 /* read status register */\r
+#define DMA2_WCMD 0xD0 /* write command register */\r
+#define DMA2_WREQ 0xD2 /* write request register */\r
+#define DMA2_SNGL 0xD4 /* write single bit register */\r
+#define DMA2_MODE 0xD6 /* write mode register */\r
+#define DMA2_CLRFF 0xD8 /* clear byte ptr flip/flop */\r
+#define DMA2_MCLR 0xDA /* master clear register */\r
+#define DMA2_CLRM 0xDC /* clear mask register */\r
+#define DMA2_WRTALL 0xDE /* write all mask register */\r
+\r
+#define DMA0_ADDR 0x00 /* chan 0 base adddress */\r
+#define DMA0_CNT 0x01 /* chan 0 base count */\r
+#define DMA1_ADDR 0x02 /* chan 1 base adddress */\r
+#define DMA1_CNT 0x03 /* chan 1 base count */\r
+#define DMA2_ADDR 0x04 /* chan 2 base adddress */\r
+#define DMA2_CNT 0x05 /* chan 2 base count */\r
+#define DMA3_ADDR 0x06 /* chan 3 base adddress */\r
+#define DMA3_CNT 0x07 /* chan 3 base count */\r
+#define DMA4_ADDR 0xC0 /* chan 4 base adddress */\r
+#define DMA4_CNT 0xC2 /* chan 4 base count */\r
+#define DMA5_ADDR 0xC4 /* chan 5 base adddress */\r
+#define DMA5_CNT 0xC6 /* chan 5 base count */\r
+#define DMA6_ADDR 0xC8 /* chan 6 base adddress */\r
+#define DMA6_CNT 0xCA /* chan 6 base count */\r
+#define DMA7_ADDR 0xCC /* chan 7 base adddress */\r
+#define DMA7_CNT 0xCE /* chan 7 base count */\r
+\r
+#define DMA0_PAGE 0x87 /* chan 0 page register (refresh)*/\r
+#define DMA1_PAGE 0x83 /* chan 1 page register */\r
+#define DMA2_PAGE 0x81 /* chan 2 page register */\r
+#define DMA3_PAGE 0x82 /* chan 3 page register */\r
+#define DMA4_PAGE 0x8F /* chan 4 page register (unuseable)*/\r
+#define DMA5_PAGE 0x8B /* chan 5 page register */\r
+#define DMA6_PAGE 0x89 /* chan 6 page register */\r
+#define DMA7_PAGE 0x8A /* chan 7 page register */\r
+\r
+#define MAX_DMA 8\r
+\r
+#define DMA_DECREMENT 0x20 /* mask to make DMA hardware go backwards */\r
+\r
+typedef struct {\r
+ UBYTE dma_disable; /* bits to disable dma channel */\r
+ UBYTE dma_enable; /* bits to enable dma channel */\r
+ UWORD page; /* page port location */\r
+ UWORD addr; /* addr port location */\r
+ UWORD count; /* count port location */\r
+ UWORD single; /* single mode port location */\r
+ UWORD mode; /* mode port location */\r
+ UWORD clear_ff; /* clear flip-flop port location */\r
+ UBYTE write; /* bits for write transfer */\r
+ UBYTE read; /* bits for read transfer */\r
+} DMA_ENTRY;\r
+\r
+/* Variables needed ... */\r
+\r
+static DMA_ENTRY mydma[MAX_DMA] = {\r
+\r
+/* DMA channel 0 */\r
+ {0x04,0x00,DMA0_PAGE,DMA0_ADDR,DMA0_CNT,\r
+ DMA1_SNGL,DMA1_MODE,DMA1_CLRFF,0x48,0x44},\r
+\r
+/* DMA channel 1 */\r
+ {0x05,0x01,DMA1_PAGE,DMA1_ADDR,DMA1_CNT,\r
+ DMA1_SNGL,DMA1_MODE,DMA1_CLRFF,0x49,0x45},\r
+\r
+/* DMA channel 2 */\r
+ {0x06,0x02,DMA2_PAGE,DMA2_ADDR,DMA2_CNT,\r
+ DMA1_SNGL,DMA1_MODE,DMA1_CLRFF,0x4A,0x46},\r
+\r
+/* DMA channel 3 */\r
+ {0x07,0x03,DMA3_PAGE,DMA3_ADDR,DMA3_CNT,\r
+ DMA1_SNGL,DMA1_MODE,DMA1_CLRFF,0x4B,0x47},\r
+\r
+/* DMA channel 4 */\r
+ {0x04,0x00,DMA4_PAGE,DMA4_ADDR,DMA4_CNT,\r
+ DMA2_SNGL,DMA2_MODE,DMA2_CLRFF,0x48,0x44},\r
+\r
+/* DMA channel 5 */\r
+ {0x05,0x01,DMA5_PAGE,DMA5_ADDR,DMA5_CNT,\r
+ DMA2_SNGL,DMA2_MODE,DMA2_CLRFF,0x49,0x45},\r
+\r
+/* DMA channel 6 */\r
+ {0x06,0x02,DMA6_PAGE,DMA6_ADDR,DMA6_CNT,\r
+ DMA2_SNGL,DMA2_MODE,DMA2_CLRFF,0x4A,0x46},\r
+\r
+/* DMA channel 7 */\r
+ {0x07,0x03,DMA7_PAGE,DMA7_ADDR,DMA7_CNT,\r
+ DMA2_SNGL,DMA2_MODE,DMA2_CLRFF,0x4B,0x47},\r
+};\r
+\r
+\r
+/*\r
+\r
+Each specialised DMA code part should provide the following things:\r
+\r
+In MDMA.H:\r
+\r
+ - a DMAMEM typedef, which should contain all the data that the\r
+ routines need for maintaining/allocating/freeing dma memory.\r
+\r
+\r
+In MDMA.C:\r
+\r
+ - 2 macros ENTER_CRITICAL and LEAVE_CRITICAL\r
+\r
+ - A function 'static BOOL MDma_AllocMem0(DMAMEM *dm,UWORD size)'\r
+ which should perform the actual dma-memory allocation. It should\r
+ use DMAMEM *dm to store all it's information.\r
+\r
+ - A function 'static void MDma_FreeMem0(DMAMEM *dm)' to free the memory\r
+\r
+ - A function 'static ULONG MDma_GetLinearPtr(DMAMEM *dm)' which should\r
+ return the linear 20 bits pointer to the actual dmabuffer.. this\r
+ function is used by MDma_Start\r
+\r
+ - A function 'void *MDma_GetPtr(DMAMEM *dm)' which should return a pointer\r
+ to the dmabuffer. If the dma memory can't be accessed directly it should\r
+ return a pointer to a FAKE dma buffer (DJGPP!!)\r
+\r
+ - A function 'void MDma_Commit(DMAMEM *dm,UWORD index,UWORD count)'. This\r
+ function will be called each time a routine wrote something to the\r
+ dmabuffer (returned by MDma_GetPtr()). In the case of a FAKE dmabuffer\r
+ this routine should take care of copying the data from the fake buffer to\r
+ the real dma memory ('count' bytes from byteoffset 'index').\r
+\r
+*/\r
+\r
+\r
+\r
+#ifdef __WATCOMC__\r
+\r
+/****************************************************************************\r
+********************* Watcom C specialised DMA code: ************************\r
+****************************************************************************/\r
+\r
+#define ENTER_CRITICAL IRQ_PUSH_OFF()\r
+extern void IRQ_PUSH_OFF (void);\r
+#pragma aux IRQ_PUSH_OFF = \\r
+ "pushfd", \\r
+ "cli" \\r
+ modify [esp];\r
+\r
+#define LEAVE_CRITICAL IRQ_POP()\r
+extern void IRQ_POP (void);\r
+#pragma aux IRQ_POP = \\r
+ "popfd" \\r
+ modify [esp];\r
+\r
+\r
+static BOOL MDma_AllocMem0(DMAMEM *dm,UWORD size)\r
+/*\r
+ Allocates a dma buffer of 'size' bytes.\r
+ returns FALSE if failed.\r
+*/\r
+{\r
+ static union REGS r;\r
+ ULONG p;\r
+\r
+ /* allocate TWICE the size of the requested dma buffer..\r
+ this fixes the 'page-crossing' bug of previous versions */\r
+\r
+ r.x.eax = 0x0100; /* DPMI allocate DOS memory */\r
+ r.x.ebx = ((size*2) + 15) >> 4; /* Number of paragraphs requested */\r
+\r
+ int386 (0x31, &r, &r);\r
+\r
+ if( r.x.cflag ) return 0; /* failed */\r
+\r
+ dm->raw_selector=r.x.edx;\r
+\r
+ /* convert the segment into a linear address */\r
+\r
+ p=(r.x.eax&0xffff)<<4;\r
+\r
+ /* if the first half of the allocated memory crosses a page\r
+ boundary, return the second half which is then guaranteed to\r
+ be page-continuous */\r
+\r
+ if( (p>>16) != ((p+size-1)>>16) ) p+=size;\r
+\r
+ dm->continuous=(void *)p;\r
+\r
+ return 1;\r
+}\r
+\r
+\r
+static void MDma_FreeMem0(DMAMEM *dm)\r
+{\r
+ static union REGS r;\r
+ r.x.eax = 0x0101; /* DPMI free DOS memory */\r
+ r.x.edx = dm->raw_selector; /* base selector */\r
+ int386 (0x31, &r, &r);\r
+}\r
+\r
+\r
+static ULONG MDma_GetLinearPtr(DMAMEM *dm)\r
+{\r
+ return (ULONG)dm->continuous;\r
+}\r
+\r
+\r
+void *MDma_GetPtr(DMAMEM *dm)\r
+{\r
+ return(dm->continuous);\r
+}\r
+\r
+\r
+void MDma_Commit(DMAMEM *dm,UWORD index,UWORD count)\r
+{\r
+ /* This function doesnt do anything here (WATCOM C\r
+ can access dma memory directly) */\r
+}\r
+\r
+\r
+#elif defined(__DJGPP__)\r
+/****************************************************************************\r
+*********************** DJGPP specialised DMA code: *************************\r
+****************************************************************************/\r
+#define ENTER_CRITICAL __asm__( "pushf \n\t cli" )\r
+#define LEAVE_CRITICAL __asm__( "popf \n\t" )\r
+#include <sys/farptr.h>\r
+\r
+static BOOL MDma_AllocMem0(DMAMEM *dm,UWORD size)\r
+/*\r
+ Allocates a dma buffer of 'size' bytes - one in the code segment and\r
+ one in the lower 1 Mb physical mem.\r
+ It doesn't check if the dma mem is page-continuous, and can only be\r
+ used to allocate exactly 1 block.\r
+*/\r
+{\r
+ dm->raw.size = (size + 15) >> 4;\r
+ if (_go32_dpmi_allocate_dos_memory(&(dm->raw)))\r
+ return 0;\r
+ dm->continuous = (void *) malloc(size);\r
+ return 1;\r
+}\r
+\r
+\r
+\r
+static void MDma_FreeMem0(DMAMEM *dm)\r
+{\r
+ _go32_dpmi_free_dos_memory(&(dm->raw));\r
+ free(dm->continuous);\r
+}\r
+\r
+static ULONG MDma_GetLinearPtr(DMAMEM *dm)\r
+{\r
+ return (ULONG) dm->raw.rm_segment << 4;\r
+}\r
+\r
+\r
+void *MDma_GetPtr(DMAMEM *dm)\r
+{\r
+ return(dm->continuous);\r
+}\r
+\r
+void MDma_Commit(DMAMEM *dm,UWORD index,UWORD count)\r
+{\r
+ char *src = &(((UBYTE*)dm->continuous)[index]);\r
+ ULONG dest = 16 * dm->raw.rm_segment + (ULONG) index;\r
+ _farsetsel(_go32_conventional_mem_selector());\r
+ while(count--) {\r
+ _farnspokeb(dest++, *(src++));\r
+ }\r
+}\r
+\r
+#else\r
+\r
+/****************************************************************************\r
+********************* Borland C specialised DMA code: ***********************\r
+****************************************************************************/\r
+\r
+#define ENTER_CRITICAL asm{ pushf; cli }\r
+#define LEAVE_CRITICAL asm{ popf }\r
+\r
+#define LPTR(ptr) (((ULONG)FP_SEG(ptr)<<4)+FP_OFF(ptr))\r
+#define NPTR(ptr) MK_FP(FP_SEG(p)+(FP_OFF(p)>>4),FP_OFF(p)&15)\r
+\r
+\r
+static BOOL MDma_AllocMem0(DMAMEM *dm,UWORD size)\r
+/*\r
+ Allocates a dma buffer of 'size' bytes.\r
+ returns FALSE if failed.\r
+*/\r
+{\r
+ char huge *p;\r
+ ULONG s;\r
+\r
+ /* allocate TWICE the size of the requested dma buffer..\r
+ so we can always get a page-contiguous dma buffer */\r
+\r
+ if((dm->raw=malloc((ULONG)size*2))==NULL) return 0;\r
+\r
+ p=(char huge *)dm->raw;\r
+ s=LPTR(p);\r
+\r
+ /* if the first half of the allocated memory crosses a page\r
+ boundary, return the second half which is then guaranteed to\r
+ be page-continuous */\r
+\r
+ if( (s>>16) != ((s+size-1)>>16) ) p+=size;\r
+\r
+ /* put the page-continuous pointer into DMAMEM */\r
+\r
+ dm->continuous=NPTR(p);\r
+\r
+ return 1;\r
+}\r
+\r
+\r
+static void MDma_FreeMem0(DMAMEM *dm)\r
+{\r
+ free(dm->raw);\r
+}\r
+\r
+\r
+static ULONG MDma_GetLinearPtr(DMAMEM *dm)\r
+{\r
+ return LPTR(dm->continuous);\r
+}\r
+\r
+\r
+void *MDma_GetPtr(DMAMEM *dm)\r
+{\r
+ return(dm->continuous);\r
+}\r
+\r
+#pragma argsused\r
+\r
+void MDma_Commit(DMAMEM *dm,UWORD index,UWORD count)\r
+{\r
+ /* This function doesnt do anything here (BORLAND C\r
+ can access dma memory directly) */\r
+}\r
+\r
+#endif\r
+\r
+\r
+/****************************************************************************\r
+************************* General DMA code: *********************************\r
+****************************************************************************/\r
+\r
+\r
+DMAMEM *MDma_AllocMem(UWORD size)\r
+{\r
+ DMAMEM *p;\r
+\r
+ /* allocate dma memory structure */\r
+\r
+ if(!(p=(DMAMEM *)malloc(sizeof(DMAMEM)))) return NULL;\r
+\r
+ /* allocate dma memory */\r
+\r
+ if(!MDma_AllocMem0(p,size)){\r
+\r
+ /* didn't succeed? -> free everything & return NULL */\r
+\r
+ free(p);\r
+ return NULL;\r
+ }\r
+\r
+ return p;\r
+}\r
+\r
+\r
+void MDma_FreeMem(DMAMEM *p)\r
+{\r
+ MDma_FreeMem0(p);\r
+ free(p);\r
+}\r
+\r
+\r
+int MDma_Start(int channel,DMAMEM *dm,UWORD size,int type)\r
+{\r
+ DMA_ENTRY *tdma;\r
+ ULONG s_20bit,e_20bit;\r
+ UWORD spage,saddr,tcount;\r
+ UWORD epage,eaddr;\r
+ UBYTE cur_mode;\r
+\r
+ tdma=&mydma[channel]; /* point to this dma data */\r
+\r
+ /* Convert the pc address to a 20 bit physical\r
+ address that the DMA controller needs */\r
+\r
+ s_20bit = MDma_GetLinearPtr(dm);\r
+\r
+ e_20bit = s_20bit + size - 1;\r
+ spage = s_20bit>>16;\r
+ epage = e_20bit>>16;\r
+\r
+ if(spage != epage) return 0;\r
+\r
+ if(channel>=4){\r
+ /* if 16-bit xfer, then addr,count & size are divided by 2 */\r
+ s_20bit = s_20bit >> 1;\r
+ e_20bit = e_20bit >> 1;\r
+ size = size >> 1;\r
+ }\r
+\r
+ saddr=s_20bit&0xffff;\r
+\r
+ tcount = size-1;\r
+\r
+ switch (type){\r
+\r
+ case READ_DMA:\r
+ cur_mode = tdma->read;\r
+ break;\r
+\r
+ case WRITE_DMA:\r
+ cur_mode = tdma->write;\r
+ break;\r
+\r
+ case INDEF_READ:\r
+ cur_mode = tdma->read | 0x10; /* turn on auto init */\r
+ break;\r
+\r
+ case INDEF_WRITE:\r
+ cur_mode = tdma->write | 0x10; /* turn on auto init */\r
+ break;\r
+ }\r
+\r
+ ENTER_CRITICAL;\r
+ outportb(tdma->single,tdma->dma_disable); /* disable channel */\r
+ outportb(tdma->mode,cur_mode); /* set mode */\r
+ outportb(tdma->clear_ff,0); /* clear f/f */\r
+ outportb(tdma->addr,saddr&0xff); /* LSB */\r
+ outportb(tdma->addr,saddr>>8); /* MSB */\r
+ outportb(tdma->page,spage); /* page # */\r
+ outportb(tdma->clear_ff,0); /* clear f/f */\r
+ outportb(tdma->count,tcount&0x0ff); /* LSB count */\r
+ outportb(tdma->count,tcount>>8); /* MSB count */\r
+ outportb(tdma->single,tdma->dma_enable); /* enable */\r
+ LEAVE_CRITICAL;\r
+\r
+ return 1;\r
+}\r
+\r
+\r
+void MDma_Stop(int channel)\r
+{\r
+ DMA_ENTRY *tdma;\r
+ tdma=&mydma[channel]; /* point to this dma data */\r
+ outportb(tdma->single,tdma->dma_disable); /* disable chan */\r
+}\r
+\r
+\r
+UWORD MDma_Todo(int channel)\r
+{\r
+ UWORD creg;\r
+ UWORD val1,val2;\r
+\r
+ DMA_ENTRY *tdma=&mydma[channel];\r
+\r
+ creg=tdma->count;\r
+\r
+ ENTER_CRITICAL;\r
+\r
+ outportb(tdma->clear_ff,0xff);\r
+\r
+ redo:\r
+ val1=inportb(creg);\r
+ val1|=inportb(creg)<<8;\r
+ val2=inportb(creg);\r
+ val2|=inportb(creg)<<8;\r
+\r
+ val1-=val2;\r
+ if((SWORD)val1>64) goto redo;\r
+ if((SWORD)val1<-64) goto redo;\r
+\r
+ LEAVE_CRITICAL;\r
+\r
+ if(channel>3) val2<<=1;\r
+\r
+ return val2;\r
+}\r