added an old version of mikmod for dos
[dosdemo] / libs / oldmik / src / mdma.c
diff --git a/libs/oldmik/src/mdma.c b/libs/oldmik/src/mdma.c
new file mode 100644 (file)
index 0000000..aac6cc9
--- /dev/null
@@ -0,0 +1,515 @@
+/*\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