added an old version of mikmod for dos
[dosdemo] / libs / oldmik / src / mdma.c
1 /*\r
2 \r
3 Name:\r
4 MDMA.C\r
5 \r
6 Description:\r
7 DMA routines\r
8 \r
9 Portability:\r
10 \r
11 MSDOS:  BC(y)   Watcom(y)       DJGPP(y)\r
12 Win95:  n\r
13 Os2:    n\r
14 Linux:  n\r
15 \r
16 (y) - yes\r
17 (n) - no (not possible or not useful)\r
18 (?) - may be possible, but not tested\r
19 \r
20 */\r
21 #include <dos.h>\r
22 #include <malloc.h>\r
23 #include <conio.h>\r
24 #include "mdma.h"\r
25 \r
26 \r
27 /* DMA Controler #1 (8-bit controller) */\r
28 #define DMA1_STAT       0x08            /* read status register */\r
29 #define DMA1_WCMD       0x08            /* write command register */\r
30 #define DMA1_WREQ       0x09            /* write request register */\r
31 #define DMA1_SNGL       0x0A            /* write single bit register */\r
32 #define DMA1_MODE       0x0B            /* write mode register */\r
33 #define DMA1_CLRFF      0x0C            /* clear byte ptr flip/flop */\r
34 #define DMA1_MCLR       0x0D            /* master clear register */\r
35 #define DMA1_CLRM       0x0E            /* clear mask register */\r
36 #define DMA1_WRTALL     0x0F            /* write all mask register */\r
37 \r
38 /* DMA Controler #2 (16-bit controller) */\r
39 #define DMA2_STAT       0xD0            /* read status register */\r
40 #define DMA2_WCMD       0xD0            /* write command register */\r
41 #define DMA2_WREQ       0xD2            /* write request register */\r
42 #define DMA2_SNGL       0xD4            /* write single bit register */\r
43 #define DMA2_MODE       0xD6            /* write mode register */\r
44 #define DMA2_CLRFF      0xD8            /* clear byte ptr flip/flop */\r
45 #define DMA2_MCLR       0xDA            /* master clear register */\r
46 #define DMA2_CLRM       0xDC            /* clear mask register */\r
47 #define DMA2_WRTALL     0xDE            /* write all mask register */\r
48 \r
49 #define DMA0_ADDR       0x00            /* chan 0 base adddress */\r
50 #define DMA0_CNT        0x01            /* chan 0 base count */\r
51 #define DMA1_ADDR       0x02            /* chan 1 base adddress */\r
52 #define DMA1_CNT        0x03            /* chan 1 base count */\r
53 #define DMA2_ADDR       0x04            /* chan 2 base adddress */\r
54 #define DMA2_CNT        0x05            /* chan 2 base count */\r
55 #define DMA3_ADDR       0x06            /* chan 3 base adddress */\r
56 #define DMA3_CNT        0x07            /* chan 3 base count */\r
57 #define DMA4_ADDR       0xC0            /* chan 4 base adddress */\r
58 #define DMA4_CNT        0xC2            /* chan 4 base count */\r
59 #define DMA5_ADDR       0xC4            /* chan 5 base adddress */\r
60 #define DMA5_CNT        0xC6            /* chan 5 base count */\r
61 #define DMA6_ADDR       0xC8            /* chan 6 base adddress */\r
62 #define DMA6_CNT        0xCA            /* chan 6 base count */\r
63 #define DMA7_ADDR       0xCC            /* chan 7 base adddress */\r
64 #define DMA7_CNT        0xCE            /* chan 7 base count */\r
65 \r
66 #define DMA0_PAGE       0x87            /* chan 0 page register (refresh)*/\r
67 #define DMA1_PAGE       0x83            /* chan 1 page register */\r
68 #define DMA2_PAGE       0x81            /* chan 2 page register */\r
69 #define DMA3_PAGE       0x82            /* chan 3 page register */\r
70 #define DMA4_PAGE       0x8F            /* chan 4 page register (unuseable)*/\r
71 #define DMA5_PAGE       0x8B            /* chan 5 page register */\r
72 #define DMA6_PAGE       0x89            /* chan 6 page register */\r
73 #define DMA7_PAGE       0x8A            /* chan 7 page register */\r
74 \r
75 #define MAX_DMA         8\r
76 \r
77 #define DMA_DECREMENT   0x20    /* mask to make DMA hardware go backwards */\r
78 \r
79 typedef struct {\r
80         UBYTE dma_disable;      /* bits to disable dma channel */\r
81         UBYTE dma_enable;       /* bits to enable dma channel */\r
82         UWORD page;                     /* page port location */\r
83         UWORD addr;                     /* addr port location */\r
84         UWORD count;            /* count port location */\r
85         UWORD single;           /* single mode port location */\r
86         UWORD mode;                     /* mode port location */\r
87         UWORD clear_ff;         /* clear flip-flop port location */\r
88         UBYTE write;            /* bits for write transfer */\r
89         UBYTE read;                     /* bits for read transfer */\r
90 } DMA_ENTRY;\r
91 \r
92 /* Variables needed ... */\r
93 \r
94 static DMA_ENTRY mydma[MAX_DMA] = {\r
95 \r
96 /* DMA channel 0 */\r
97                         {0x04,0x00,DMA0_PAGE,DMA0_ADDR,DMA0_CNT,\r
98                          DMA1_SNGL,DMA1_MODE,DMA1_CLRFF,0x48,0x44},\r
99 \r
100 /* DMA channel 1 */\r
101                         {0x05,0x01,DMA1_PAGE,DMA1_ADDR,DMA1_CNT,\r
102                          DMA1_SNGL,DMA1_MODE,DMA1_CLRFF,0x49,0x45},\r
103 \r
104 /* DMA channel 2 */\r
105                         {0x06,0x02,DMA2_PAGE,DMA2_ADDR,DMA2_CNT,\r
106                          DMA1_SNGL,DMA1_MODE,DMA1_CLRFF,0x4A,0x46},\r
107 \r
108 /* DMA channel 3 */\r
109                         {0x07,0x03,DMA3_PAGE,DMA3_ADDR,DMA3_CNT,\r
110                          DMA1_SNGL,DMA1_MODE,DMA1_CLRFF,0x4B,0x47},\r
111 \r
112 /* DMA channel 4 */\r
113                         {0x04,0x00,DMA4_PAGE,DMA4_ADDR,DMA4_CNT,\r
114                          DMA2_SNGL,DMA2_MODE,DMA2_CLRFF,0x48,0x44},\r
115 \r
116 /* DMA channel 5 */\r
117                         {0x05,0x01,DMA5_PAGE,DMA5_ADDR,DMA5_CNT,\r
118                          DMA2_SNGL,DMA2_MODE,DMA2_CLRFF,0x49,0x45},\r
119 \r
120 /* DMA channel 6 */\r
121                         {0x06,0x02,DMA6_PAGE,DMA6_ADDR,DMA6_CNT,\r
122                          DMA2_SNGL,DMA2_MODE,DMA2_CLRFF,0x4A,0x46},\r
123 \r
124 /* DMA channel 7 */\r
125                         {0x07,0x03,DMA7_PAGE,DMA7_ADDR,DMA7_CNT,\r
126                          DMA2_SNGL,DMA2_MODE,DMA2_CLRFF,0x4B,0x47},\r
127 };\r
128 \r
129 \r
130 /*\r
131 \r
132 Each specialised DMA code part should provide the following things:\r
133 \r
134 In MDMA.H:\r
135 \r
136   -     a DMAMEM typedef, which should contain all the data that the\r
137         routines need for maintaining/allocating/freeing dma memory.\r
138 \r
139 \r
140 In MDMA.C:\r
141 \r
142   -     2 macros ENTER_CRITICAL and LEAVE_CRITICAL\r
143 \r
144   - A function 'static BOOL MDma_AllocMem0(DMAMEM *dm,UWORD size)'\r
145         which should perform the actual dma-memory allocation. It should\r
146         use DMAMEM *dm to store all it's information.\r
147 \r
148   - A function 'static void MDma_FreeMem0(DMAMEM *dm)' to free the memory\r
149 \r
150   - A function 'static ULONG MDma_GetLinearPtr(DMAMEM *dm)' which should\r
151         return the linear 20 bits pointer to the actual dmabuffer.. this\r
152         function is     used by MDma_Start\r
153 \r
154   - A function 'void *MDma_GetPtr(DMAMEM *dm)' which should return a pointer\r
155         to the dmabuffer. If the dma memory can't be accessed directly it should\r
156         return a pointer to a FAKE dma buffer (DJGPP!!)\r
157 \r
158   - A function 'void MDma_Commit(DMAMEM *dm,UWORD index,UWORD count)'. This\r
159         function will be called each time a routine wrote something to the\r
160         dmabuffer (returned by MDma_GetPtr()). In the case of a FAKE dmabuffer\r
161         this routine should take care of copying the data from the fake buffer to\r
162         the real dma memory ('count' bytes from byteoffset 'index').\r
163 \r
164 */\r
165 \r
166 \r
167 \r
168 #ifdef __WATCOMC__\r
169 \r
170 /****************************************************************************\r
171 ********************* Watcom C specialised DMA code: ************************\r
172 ****************************************************************************/\r
173 \r
174 #define ENTER_CRITICAL IRQ_PUSH_OFF()\r
175 extern void IRQ_PUSH_OFF (void);\r
176 #pragma aux IRQ_PUSH_OFF =      \\r
177         "pushfd",                   \\r
178                 "cli"           \\r
179                 modify [esp];\r
180 \r
181 #define LEAVE_CRITICAL IRQ_POP()\r
182 extern void IRQ_POP (void);\r
183 #pragma aux IRQ_POP =   \\r
184                 "popfd"         \\r
185                 modify [esp];\r
186 \r
187 \r
188 static BOOL MDma_AllocMem0(DMAMEM *dm,UWORD size)\r
189 /*\r
190         Allocates a dma buffer of 'size' bytes.\r
191         returns FALSE if failed.\r
192 */\r
193 {\r
194         static union REGS r;\r
195         ULONG p;\r
196 \r
197         /* allocate TWICE the size of the requested dma buffer..\r
198         this fixes the 'page-crossing' bug of previous versions */\r
199 \r
200         r.x.eax = 0x0100;           /* DPMI allocate DOS memory */\r
201         r.x.ebx = ((size*2) + 15) >> 4; /* Number of paragraphs requested */\r
202 \r
203         int386 (0x31, &r, &r);\r
204 \r
205         if( r.x.cflag ) return 0;       /* failed */\r
206 \r
207         dm->raw_selector=r.x.edx;\r
208 \r
209         /* convert the segment into a linear address */\r
210 \r
211         p=(r.x.eax&0xffff)<<4;\r
212 \r
213         /* if the first half of the allocated memory crosses a page\r
214         boundary, return the second half which is then guaranteed to\r
215         be page-continuous */\r
216 \r
217         if( (p>>16) != ((p+size-1)>>16) ) p+=size;\r
218 \r
219                 dm->continuous=(void *)p;\r
220 \r
221         return 1;\r
222 }\r
223 \r
224 \r
225 static void MDma_FreeMem0(DMAMEM *dm)\r
226 {\r
227         static union REGS r;\r
228         r.x.eax = 0x0101;               /* DPMI free DOS memory */\r
229         r.x.edx = dm->raw_selector;     /* base selector */\r
230         int386 (0x31, &r, &r);\r
231 }\r
232 \r
233 \r
234 static ULONG MDma_GetLinearPtr(DMAMEM *dm)\r
235 {\r
236         return (ULONG)dm->continuous;\r
237 }\r
238 \r
239 \r
240 void *MDma_GetPtr(DMAMEM *dm)\r
241 {\r
242         return(dm->continuous);\r
243 }\r
244 \r
245 \r
246 void MDma_Commit(DMAMEM *dm,UWORD index,UWORD count)\r
247 {\r
248         /* This function doesnt do anything here (WATCOM C\r
249            can access dma memory directly) */\r
250 }\r
251 \r
252 \r
253 #elif defined(__DJGPP__)\r
254 /****************************************************************************\r
255 *********************** DJGPP specialised DMA code: *************************\r
256 ****************************************************************************/\r
257 #define ENTER_CRITICAL __asm__( "pushf \n\t cli" )\r
258 #define LEAVE_CRITICAL __asm__( "popf \n\t" )\r
259 #include <sys/farptr.h>\r
260 \r
261 static BOOL MDma_AllocMem0(DMAMEM *dm,UWORD size)\r
262 /*\r
263         Allocates a dma buffer of 'size' bytes - one in the code segment and\r
264                 one in the lower 1 Mb physical mem.\r
265         It doesn't check if the dma mem is page-continuous, and can only be\r
266                 used to allocate exactly 1 block.\r
267 */\r
268 {\r
269         dm->raw.size = (size + 15) >> 4;\r
270         if (_go32_dpmi_allocate_dos_memory(&(dm->raw)))\r
271           return 0;\r
272         dm->continuous = (void *) malloc(size);\r
273                 return 1;\r
274 }\r
275 \r
276 \r
277 \r
278 static void MDma_FreeMem0(DMAMEM *dm)\r
279 {\r
280         _go32_dpmi_free_dos_memory(&(dm->raw));\r
281         free(dm->continuous);\r
282 }\r
283 \r
284 static ULONG MDma_GetLinearPtr(DMAMEM *dm)\r
285 {\r
286         return (ULONG) dm->raw.rm_segment << 4;\r
287 }\r
288 \r
289 \r
290 void *MDma_GetPtr(DMAMEM *dm)\r
291 {\r
292         return(dm->continuous);\r
293 }\r
294 \r
295 void MDma_Commit(DMAMEM *dm,UWORD index,UWORD count)\r
296 {\r
297    char *src = &(((UBYTE*)dm->continuous)[index]);\r
298    ULONG dest = 16 * dm->raw.rm_segment + (ULONG) index;\r
299    _farsetsel(_go32_conventional_mem_selector());\r
300    while(count--) {\r
301           _farnspokeb(dest++, *(src++));\r
302    }\r
303 }\r
304 \r
305 #else\r
306 \r
307 /****************************************************************************\r
308 ********************* Borland C specialised DMA code: ***********************\r
309 ****************************************************************************/\r
310 \r
311 #define ENTER_CRITICAL asm{ pushf; cli }\r
312 #define LEAVE_CRITICAL asm{ popf }\r
313 \r
314 #define LPTR(ptr) (((ULONG)FP_SEG(ptr)<<4)+FP_OFF(ptr))\r
315 #define NPTR(ptr) MK_FP(FP_SEG(p)+(FP_OFF(p)>>4),FP_OFF(p)&15)\r
316 \r
317 \r
318 static BOOL MDma_AllocMem0(DMAMEM *dm,UWORD size)\r
319 /*\r
320         Allocates a dma buffer of 'size' bytes.\r
321         returns FALSE if failed.\r
322 */\r
323 {\r
324         char huge *p;\r
325         ULONG s;\r
326 \r
327         /* allocate TWICE the size of the requested dma buffer..\r
328         so we can always get a page-contiguous dma buffer */\r
329 \r
330         if((dm->raw=malloc((ULONG)size*2))==NULL) return 0;\r
331 \r
332         p=(char huge *)dm->raw;\r
333         s=LPTR(p);\r
334 \r
335         /* if the first half of the allocated memory crosses a page\r
336         boundary, return the second half which is then guaranteed to\r
337         be page-continuous */\r
338 \r
339         if( (s>>16) != ((s+size-1)>>16) ) p+=size;\r
340 \r
341         /* put the page-continuous pointer into DMAMEM */\r
342 \r
343         dm->continuous=NPTR(p);\r
344 \r
345         return 1;\r
346 }\r
347 \r
348 \r
349 static void MDma_FreeMem0(DMAMEM *dm)\r
350 {\r
351         free(dm->raw);\r
352 }\r
353 \r
354 \r
355 static ULONG MDma_GetLinearPtr(DMAMEM *dm)\r
356 {\r
357         return LPTR(dm->continuous);\r
358 }\r
359 \r
360 \r
361 void *MDma_GetPtr(DMAMEM *dm)\r
362 {\r
363         return(dm->continuous);\r
364 }\r
365 \r
366 #pragma argsused\r
367 \r
368 void MDma_Commit(DMAMEM *dm,UWORD index,UWORD count)\r
369 {\r
370         /* This function doesnt do anything here (BORLAND C\r
371            can access dma memory directly) */\r
372 }\r
373 \r
374 #endif\r
375 \r
376 \r
377 /****************************************************************************\r
378 ************************* General DMA code: *********************************\r
379 ****************************************************************************/\r
380 \r
381 \r
382 DMAMEM *MDma_AllocMem(UWORD size)\r
383 {\r
384         DMAMEM *p;\r
385 \r
386         /* allocate dma memory structure */\r
387 \r
388         if(!(p=(DMAMEM *)malloc(sizeof(DMAMEM)))) return NULL;\r
389 \r
390         /* allocate dma memory */\r
391 \r
392         if(!MDma_AllocMem0(p,size)){\r
393 \r
394                 /* didn't succeed? -> free everything & return NULL */\r
395 \r
396                 free(p);\r
397                 return NULL;\r
398         }\r
399 \r
400         return p;\r
401 }\r
402 \r
403 \r
404 void MDma_FreeMem(DMAMEM *p)\r
405 {\r
406         MDma_FreeMem0(p);\r
407         free(p);\r
408 }\r
409 \r
410 \r
411 int MDma_Start(int channel,DMAMEM *dm,UWORD size,int type)\r
412 {\r
413         DMA_ENTRY *tdma;\r
414         ULONG s_20bit,e_20bit;\r
415         UWORD spage,saddr,tcount;\r
416         UWORD epage,eaddr;\r
417         UBYTE cur_mode;\r
418 \r
419         tdma=&mydma[channel];           /* point to this dma data */\r
420 \r
421         /* Convert the pc address to a 20 bit physical\r
422            address that the DMA controller needs */\r
423 \r
424         s_20bit = MDma_GetLinearPtr(dm);\r
425 \r
426         e_20bit = s_20bit + size - 1;\r
427         spage = s_20bit>>16;\r
428         epage = e_20bit>>16;\r
429 \r
430         if(spage != epage) return 0;\r
431 \r
432         if(channel>=4){\r
433                 /* if 16-bit xfer, then addr,count & size are divided by 2 */\r
434                 s_20bit = s_20bit >> 1;\r
435                 e_20bit = e_20bit >> 1;\r
436                 size = size >> 1;\r
437         }\r
438 \r
439         saddr=s_20bit&0xffff;\r
440 \r
441         tcount = size-1;\r
442 \r
443         switch (type){\r
444 \r
445                 case READ_DMA:\r
446                         cur_mode = tdma->read;\r
447                         break;\r
448 \r
449                 case WRITE_DMA:\r
450                         cur_mode = tdma->write;\r
451                         break;\r
452 \r
453                 case INDEF_READ:\r
454                         cur_mode = tdma->read | 0x10;   /* turn on auto init */\r
455                         break;\r
456 \r
457                 case INDEF_WRITE:\r
458                         cur_mode = tdma->write | 0x10;  /* turn on auto init */\r
459                         break;\r
460         }\r
461 \r
462         ENTER_CRITICAL;\r
463         outportb(tdma->single,tdma->dma_disable);               /* disable channel */\r
464         outportb(tdma->mode,cur_mode);                                  /* set mode */\r
465         outportb(tdma->clear_ff,0);                                             /* clear f/f */\r
466         outportb(tdma->addr,saddr&0xff);                                /* LSB */\r
467         outportb(tdma->addr,saddr>>8);                                  /* MSB */\r
468         outportb(tdma->page,spage);                                             /* page # */\r
469         outportb(tdma->clear_ff,0);                                             /* clear f/f */\r
470         outportb(tdma->count,tcount&0x0ff);                             /* LSB count */\r
471         outportb(tdma->count,tcount>>8);                                /* MSB count */\r
472         outportb(tdma->single,tdma->dma_enable);                /* enable */\r
473         LEAVE_CRITICAL;\r
474 \r
475         return 1;\r
476 }\r
477 \r
478 \r
479 void MDma_Stop(int channel)\r
480 {\r
481         DMA_ENTRY *tdma;\r
482         tdma=&mydma[channel];                                           /* point to this dma data */\r
483         outportb(tdma->single,tdma->dma_disable);   /* disable chan */\r
484 }\r
485 \r
486 \r
487 UWORD MDma_Todo(int channel)\r
488 {\r
489         UWORD creg;\r
490         UWORD val1,val2;\r
491 \r
492         DMA_ENTRY *tdma=&mydma[channel];\r
493 \r
494         creg=tdma->count;\r
495 \r
496         ENTER_CRITICAL;\r
497 \r
498         outportb(tdma->clear_ff,0xff);\r
499 \r
500         redo:\r
501         val1=inportb(creg);\r
502         val1|=inportb(creg)<<8;\r
503         val2=inportb(creg);\r
504         val2|=inportb(creg)<<8;\r
505 \r
506         val1-=val2;\r
507         if((SWORD)val1>64) goto redo;\r
508         if((SWORD)val1<-64) goto redo;\r
509 \r
510         LEAVE_CRITICAL;\r
511 \r
512         if(channel>3) val2<<=1;\r
513 \r
514         return val2;\r
515 }\r