2 Implementation of DMA routines on DOS
3 Copyright (C) 1999 by Andrew Zabolotny, <bit@eltech.ru>
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with this library; if not, write to the Free
17 Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include <go32.h> /* includes sys/version.h (djgpp >= 2.02) */
25 #include <sys/nearptr.h>
27 #include "mikmod.h" /* for MikMod_malloc() & co */
29 /* BUG WARNING: there is an error in DJGPP libraries <= 2.01:
30 * src/libc/dpmi/api/d0102.s loads the selector and allocsize
31 * arguments in the wrong order. DJGPP >= 2.02 have it fixed. */
32 #if (!defined(__DJGPP_MINOR__) || (__DJGPP_MINOR__+0) < 2)
33 #warning __dpmi_resize_dos_memory() from DJGPP <= 2.01 is broken!
38 {DMA_ADDR_0, DMA_PAGE_0, DMA_SIZE_0,
39 DMA1_MASK_REG, DMA1_CLEAR_FF_REG, DMA1_MODE_REG},
40 {DMA_ADDR_1, DMA_PAGE_1, DMA_SIZE_1,
41 DMA1_MASK_REG, DMA1_CLEAR_FF_REG, DMA1_MODE_REG},
43 {DMA_ADDR_2, DMA_PAGE_2, DMA_SIZE_2,
44 DMA1_MASK_REG, DMA1_CLEAR_FF_REG, DMA1_MODE_REG},
45 {DMA_ADDR_3, DMA_PAGE_3, DMA_SIZE_3,
46 DMA1_MASK_REG, DMA1_CLEAR_FF_REG, DMA1_MODE_REG},
48 {DMA_ADDR_4, 0, DMA_SIZE_4,
49 DMA2_MASK_REG, DMA2_CLEAR_FF_REG, DMA2_MODE_REG},
50 {DMA_ADDR_5, DMA_PAGE_5, DMA_SIZE_5,
51 DMA2_MASK_REG, DMA2_CLEAR_FF_REG, DMA2_MODE_REG},
53 {DMA_ADDR_6, DMA_PAGE_6, DMA_SIZE_6,
54 DMA2_MASK_REG, DMA2_CLEAR_FF_REG, DMA2_MODE_REG},
55 {DMA_ADDR_7, DMA_PAGE_7, DMA_SIZE_7,
56 DMA2_MASK_REG, DMA2_CLEAR_FF_REG, DMA2_MODE_REG}
60 static int __initialized = 0;
61 static int __buffer_count = 0;
62 static __dpmi_meminfo __locked_data;
66 if (!__djgpp_nearptr_enable())
69 /* Trick: Avoid re-setting DS selector limit on each memory allocation
71 __djgpp_selector_limit = 0xffffffff;
73 __locked_data.address = __djgpp_base_address + (unsigned long)&dma;
74 __locked_data.size = sizeof(dma);
75 if (__dpmi_lock_linear_region(&__locked_data))
78 return (__initialized = 1);
85 __dpmi_unlock_linear_region(&__locked_data);
86 __djgpp_nearptr_disable();
89 dma_buffer *dma_allocate(unsigned int channel, unsigned int size)
91 int parsize = (size + 15) >> 4; /* size in paragraphs */
92 int par = 0; /* Real-mode paragraph */
93 int selector = 0; /* Protected-mode selector */
94 int mask = channel <= 3 ? 0xfff : 0x1fff; /* Alignment mask in para. */
95 int allocsize = parsize; /* Allocated size in paragraphs */
96 int count; /* Try count */
97 int bound = 0; /* Nearest bound address */
98 int maxsize; /* Maximal possible block size */
99 dma_buffer *buffer = NULL;
100 __dpmi_meminfo buff_info, struct_info;
102 if (!dma_initialize())
105 /* Loop until we'll get a properly aligned memory block */
106 for (count = 8; count; count--) {
107 int resize = (selector != 0);
109 /* Try first to resize (possibly previously) allocated block */
111 maxsize = (bound + parsize) - par;
112 if (maxsize > parsize * 2)
113 maxsize = parsize * 2;
114 if (maxsize == allocsize)
118 if (__dpmi_resize_dos_memory(selector, allocsize, &maxsize) !=
125 __dpmi_free_dos_memory(selector), selector = 0;
126 par = __dpmi_allocate_dos_memory(allocsize, &selector);
129 if ((par == 0) || (par == -1))
132 /* If memory block contains a properly aligned portion, quit loop */
133 bound = (par + mask + 1) & ~mask;
134 if (par + parsize <= bound)
136 if (bound + parsize <= par + allocsize) {
142 __dpmi_free_dos_memory(selector);
146 buffer = (dma_buffer *) MikMod_malloc(sizeof(dma_buffer));
147 buffer->linear = (unsigned char *)(__djgpp_conventional_base + bound * 16);
148 buffer->physical = bound * 16;
149 buffer->size = parsize * 16;
150 buffer->selector = selector;
151 buffer->channel = channel;
153 buff_info.address = buffer->physical;
154 buff_info.size = buffer->size;
156 Don't pay attention to return code since under plain DOS it often
157 returns error (at least under HIMEM/CWSDPMI and EMM386/DPMI)
159 __dpmi_lock_linear_region(&buff_info);
161 /* Lock the DMA buffer control structure as well */
162 struct_info.address = __djgpp_base_address + (unsigned long)buffer;
163 struct_info.size = sizeof(dma_buffer);
164 if (__dpmi_lock_linear_region(&struct_info)) {
165 __dpmi_unlock_linear_region(&buff_info);
166 __dpmi_free_dos_memory(selector);
175 else if (--__buffer_count == 0)
180 void dma_free(dma_buffer * buffer)
182 __dpmi_meminfo buff_info;
187 buff_info.address = buffer->physical;
188 buff_info.size = buffer->size;
189 __dpmi_unlock_linear_region(&buff_info);
191 __dpmi_free_dos_memory(buffer->selector);
194 if (--__buffer_count == 0)
198 void dma_start(dma_buffer * buffer, unsigned long count, unsigned char mode)
200 /* Disable interrupts */
201 int old_ints = disable();
202 dma_disable(buffer->channel);
203 dma_set_mode(buffer->channel, mode);
204 dma_clear_ff(buffer->channel);
205 dma_set_addr(buffer->channel, buffer->physical);
206 dma_clear_ff(buffer->channel);
207 dma_set_count(buffer->channel, count);
208 dma_enable(buffer->channel);
209 /* Re-enable interrupts */