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