1 /* Implementation of DMA routines on DOS - from libMikMod.
2    Copyright (C) 1999 by Andrew Zabolotny, <bit@eltech.ru>
3 
4    This file is part of WildMIDI.
5 
6    WildMIDI is free software: you can redistribute and/or modify the player
7    under the terms of the GNU General Public License and you can redistribute
8    and/or modify the library under the terms of the GNU Lesser General Public
9    License as published by the Free Software Foundation, either version 3 of
10    the licenses, or(at your option) any later version.
11 
12    WildMIDI is distributed in the hope that it will be useful, but WITHOUT
13    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14    FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and
15    the GNU Lesser General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License and the
18    GNU Lesser General Public License along with WildMIDI.  If not,  see
19    <http://www.gnu.org/licenses/>.
20 */
21 
22 #include "dosdma.h"
23 
24 #include <go32.h> /* includes sys/version.h (djgpp >= 2.02) */
25 #include <dos.h>
26 #include <dpmi.h>
27 #include <sys/nearptr.h>
28 #include <malloc.h>
29 
30 /* BUG WARNING:  there is an error in DJGPP libraries <= 2.01:
31  * src/libc/dpmi/api/d0102.s loads the selector and allocsize
32  * arguments in the wrong order.  DJGPP >= 2.02 have it fixed. */
33 #if !defined(__DJGPP_MINOR__) || (__DJGPP_MINOR__-0 < 2)
34 #warning __dpmi_resize_dos_memory() from DJGPP <= 2.01 is broken!
35 #endif
36 
37 __dma_regs dma[8] = {
38 /* *INDENT-OFF* */
39 	{DMA_ADDR_0, DMA_PAGE_0, DMA_SIZE_0,
40 	 DMA1_MASK_REG, DMA1_CLEAR_FF_REG, DMA1_MODE_REG},
41 	{DMA_ADDR_1, DMA_PAGE_1, DMA_SIZE_1,
42 	 DMA1_MASK_REG, DMA1_CLEAR_FF_REG, DMA1_MODE_REG},
43 
44 	{DMA_ADDR_2, DMA_PAGE_2, DMA_SIZE_2,
45 	 DMA1_MASK_REG, DMA1_CLEAR_FF_REG, DMA1_MODE_REG},
46 	{DMA_ADDR_3, DMA_PAGE_3, DMA_SIZE_3,
47 	 DMA1_MASK_REG, DMA1_CLEAR_FF_REG, DMA1_MODE_REG},
48 
49 	{DMA_ADDR_4,          0, DMA_SIZE_4,
50 	 DMA2_MASK_REG, DMA2_CLEAR_FF_REG, DMA2_MODE_REG},
51 	{DMA_ADDR_5, DMA_PAGE_5, DMA_SIZE_5,
52 	 DMA2_MASK_REG, DMA2_CLEAR_FF_REG, DMA2_MODE_REG},
53 
54 	{DMA_ADDR_6, DMA_PAGE_6, DMA_SIZE_6,
55 	 DMA2_MASK_REG, DMA2_CLEAR_FF_REG, DMA2_MODE_REG},
56 	{DMA_ADDR_7, DMA_PAGE_7, DMA_SIZE_7,
57 	 DMA2_MASK_REG, DMA2_CLEAR_FF_REG, DMA2_MODE_REG}
58 /* *INDENT-ON* */
59 };
60 
61 static int __initialized = 0;
62 static int __buffer_count = 0;
63 static __dpmi_meminfo __locked_data;
64 
dma_initialize()65 int dma_initialize()
66 {
67 	if (!__djgpp_nearptr_enable())
68 		return 0;
69 
70 	/* Trick: Avoid re-setting DS selector limit on each memory allocation
71 	   call */
72 	__djgpp_selector_limit = 0xffffffff;
73 
74 	__locked_data.address = __djgpp_base_address + (unsigned long)&dma;
75 	__locked_data.size = sizeof(dma);
76 	if (__dpmi_lock_linear_region(&__locked_data))
77 		return 0;
78 
79 	return (__initialized = 1);
80 }
81 
dma_finalize()82 void dma_finalize()
83 {
84 	if (!__initialized)
85 		return;
86 	__dpmi_unlock_linear_region(&__locked_data);
87 	__djgpp_nearptr_disable();
88 }
89 
dma_allocate(unsigned int channel,unsigned int size)90 dma_buffer *dma_allocate(unsigned int channel, unsigned int size)
91 {
92 	int parsize = (size + 15) >> 4;	/* size in paragraphs */
93 	int par = 0;				/* Real-mode paragraph */
94 	int selector = 0;			/* Protected-mode selector */
95 	int mask = channel <= 3 ? 0xfff : 0x1fff;	/* Alignment mask in para. */
96 	int allocsize = parsize;	/* Allocated size in paragraphs */
97 	int count;					/* Try count */
98 	int bound = 0;				/* Nearest bound address */
99 	int maxsize;				/* Maximal possible block size */
100 	dma_buffer *buffer = NULL;
101 	__dpmi_meminfo buff_info, struct_info;
102 
103 	if (!dma_initialize())
104 		return NULL;
105 
106 	/* Loop until we'll get a properly aligned memory block */
107 	for (count = 8; count; count--) {
108 		int resize = (selector != 0);
109 
110 		/* Try first to resize (possibly previously) allocated block */
111 		if (resize) {
112 			maxsize = (bound + parsize) - par;
113 			if (maxsize > parsize * 2)
114 				maxsize = parsize * 2;
115 			if (maxsize == allocsize)
116 				resize = 0;
117 			else {
118 				allocsize = maxsize;
119 				if (__dpmi_resize_dos_memory(selector, allocsize, &maxsize) !=
120 					0) resize = 0;
121 			}
122 		}
123 
124 		if (!resize) {
125 			if (selector)
126 				__dpmi_free_dos_memory(selector), selector = 0;
127 			par = __dpmi_allocate_dos_memory(allocsize, &selector);
128 		}
129 
130 		if ((par == 0) || (par == -1))
131 			goto exit;
132 
133 		/* If memory block contains a properly aligned portion, quit loop */
134 		bound = (par + mask + 1) & ~mask;
135 		if (par + parsize <= bound)
136 			break;
137 		if (bound + parsize <= par + allocsize) {
138 			par = bound;
139 			break;
140 		}
141 	}
142 	if (!count) {
143 		__dpmi_free_dos_memory(selector);
144 		goto exit;
145 	}
146 
147 	buffer = (dma_buffer *) malloc(sizeof(dma_buffer));
148 	buffer->linear = (unsigned char *)(__djgpp_conventional_base + bound * 16);
149 	buffer->physical = bound * 16;
150 	buffer->size = parsize * 16;
151 	buffer->selector = selector;
152 	buffer->channel = channel;
153 
154 	buff_info.address = buffer->physical;
155 	buff_info.size = buffer->size;
156 	/*
157 	   Don't pay attention to return code since under plain DOS it often
158 	   returns error (at least under HIMEM/CWSDPMI and EMM386/DPMI)
159 	 */
160 	__dpmi_lock_linear_region(&buff_info);
161 
162 	/* Lock the DMA buffer control structure as well */
163 	struct_info.address = __djgpp_base_address + (unsigned long)buffer;
164 	struct_info.size = sizeof(dma_buffer);
165 	if (__dpmi_lock_linear_region(&struct_info)) {
166 		__dpmi_unlock_linear_region(&buff_info);
167 		__dpmi_free_dos_memory(selector);
168 		free(buffer);
169 		buffer = NULL;
170 		goto exit;
171 	}
172 
173   exit:
174 	if (buffer)
175 		__buffer_count++;
176 	else if (--__buffer_count == 0)
177 		dma_finalize();
178 	return buffer;
179 }
180 
dma_free(dma_buffer * buffer)181 void dma_free(dma_buffer * buffer)
182 {
183 	__dpmi_meminfo buff_info;
184 
185 	if (!buffer)
186 		return;
187 
188 	buff_info.address = buffer->physical;
189 	buff_info.size = buffer->size;
190 	__dpmi_unlock_linear_region(&buff_info);
191 
192 	__dpmi_free_dos_memory(buffer->selector);
193 	free(buffer);
194 
195 	if (--__buffer_count == 0)
196 		dma_finalize();
197 }
198 
dma_start(dma_buffer * buffer,unsigned long count,unsigned char mode)199 void dma_start(dma_buffer * buffer, unsigned long count, unsigned char mode)
200 {
201 	/* Disable interrupts */
202 	int old_ints = disable();
203 	dma_disable(buffer->channel);
204 	dma_set_mode(buffer->channel, mode);
205 	dma_clear_ff(buffer->channel);
206 	dma_set_addr(buffer->channel, buffer->physical);
207 	dma_clear_ff(buffer->channel);
208 	dma_set_count(buffer->channel, count);
209 	dma_enable(buffer->channel);
210 	/* Re-enable interrupts */
211 	if (old_ints)
212 		enable();
213 }
214