This commit was generated by cvs2svn to compensate for changes in r2,
[btb/d2x.git] / arch / dos / dpmi.c
1 /*
2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
11 COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
12 */
13 /*
14  * $Source: /cvs/cvsroot/d2x/arch/dos/dpmi.c,v $
15  * $Revision: 1.1.1.1 $
16  * $Author: bradleyb $
17  * $Date: 2001-01-19 03:30:15 $
18  * 
19  * Routines that access DPMI services...
20  * 
21  * $Log: not supported by cvs2svn $
22  * Revision 1.1.1.1  1999/06/14 21:58:16  donut
23  * Import of d1x 1.37 source.
24  *
25  * Revision 1.19  1995/02/23  09:02:57  john
26  * Fixed bug with dos_selector.
27  * 
28  * Revision 1.18  1995/02/02  11:10:22  john
29  * Made real mode calls have a 2K stack.
30  * 
31  * Revision 1.17  1995/01/14  19:20:28  john
32  * Added function to set a selector's base address.
33  * 
34  * Revision 1.16  1994/12/14  16:11:40  john
35  * Locked down the memory referenced by GETDS.
36  * 
37  * Revision 1.15  1994/12/06  16:08:06  john
38  * MAde memory checking return better results.
39  * 
40  * Revision 1.14  1994/12/05  23:34:54  john
41  * Made dpmi_init lock down GETDS and chain_intr.
42  * 
43  * Revision 1.13  1994/11/28  21:19:02  john
44  * Made memory checking a bit better.
45  * 
46  * Revision 1.12  1994/11/28  20:22:18  john
47  * Added some variables that return the amount of available 
48  * memory.
49  * 
50  * Revision 1.11  1994/11/15  18:27:21  john
51  * *** empty log message ***
52  * 
53  * Revision 1.10  1994/11/15  18:26:45  john
54  * Added verbose flag.
55  * 
56  * Revision 1.9  1994/10/27  19:54:37  john
57  * Added unlock region function,.
58  * 
59  * Revision 1.8  1994/10/05  16:17:31  john
60  * Took out locked down message.
61  * 
62  * Revision 1.7  1994/10/03  17:21:20  john
63  * Added the code that allocates a 1K DOS buffer.
64  * 
65  * Revision 1.6  1994/09/29  18:29:40  john
66  * Shorted mem info printout
67  * 
68  * Revision 1.5  1994/09/27  11:54:35  john
69  * Added DPMI init function.
70  * 
71  * Revision 1.4  1994/09/19  14:50:43  john
72  * Took out mono debug.
73  * 
74  * Revision 1.3  1994/09/19  14:41:23  john
75  * Fixed some bugs with allocating selectors.
76  * 
77  * Revision 1.2  1994/08/24  18:53:51  john
78  * Made Cyberman read like normal mouse; added dpmi module; moved
79  * mouse from assembly to c. Made mouse buttons return time_down.
80  * 
81  * Revision 1.1  1994/08/24  10:22:34  john
82  * Initial revision
83  * 
84  * 
85  */
86
87
88 #ifdef RCS
89 static char rcsid[] = "$Id: dpmi.c,v 1.1.1.1 2001-01-19 03:30:15 bradleyb Exp $";
90 #endif
91
92 #ifdef __DJGPP__
93 #define _BORLAND_DOS_REGS 1
94 #define far
95 #include <sys/segments.h>
96 #include <sys/nearptr.h>
97 #include <crt0.h>
98 #define FP_SEG(p) _my_ds()
99 #define FP_OFF(p) (int)p
100  int _crt0_startup_flags=_CRT0_FLAG_NONMOVE_SBRK+_CRT0_FLAG_FILL_SBRK_MEMORY+_CRT0_FLAG_FILL_DEADBEEF+_CRT0_FLAG_NEARPTR+_CRT0_FLAG_NO_LFN;
101 #endif
102
103 #include <i86.h>
104 #include <dos.h>
105 #include <stdio.h>
106 #include <string.h>
107 #include <malloc.h>
108 #include <stdlib.h>
109 #include <conio.h>
110
111 #include "types.h"
112 #include "mono.h"
113 #include "error.h"
114 #include "u_dpmi.h"
115
116 int dpmi_find_dos_memory()
117 {
118         union REGS r;
119
120         memset(&r,0,sizeof(r));
121         r.x.eax = 0x0100;                               // DPMI allocate DOS memory 
122         r.x.ebx = 0xffff;       // Number of paragraphs requested
123         int386 (0x31, &r, &r);
124         //if ( (r.x.eax & 0xffff) == 0x08 )
125         //if ( (r.x.eax & 0xffff) == 0x08 )
126         if ( r.x.cflag )
127                 return ((r.x.ebx & 0xffff)*16);
128         else
129                 return 640*1024;
130 }
131
132 void *dpmi_real_malloc( int size, ushort *selector )
133 {
134         union REGS r;
135
136         memset(&r,0,sizeof(r));
137         r.x.eax = 0x0100;                               // DPMI allocate DOS memory 
138         r.x.ebx = (size + 15) >> 4;     // Number of paragraphs requested
139         int386 (0x31, &r, &r);
140
141         if (r.x.cflag)  // Failed
142                 return ((uint) 0);
143
144         if(selector!=NULL)
145                 *selector = r.x.edx & 0xFFFF;
146
147 #ifdef __DJGPP__
148         return (void *) ((r.x.eax & 0xFFFF) << 4)+__djgpp_conventional_base;
149 #else
150         return (void *) ((r.x.eax & 0xFFFF) << 4);
151 #endif
152 }
153
154 void dpmi_real_free( ushort selector )
155 {
156         union REGS r;
157
158         memset(&r,0,sizeof(r));
159         r.x.eax = 0x0101;                               // DPMI free DOS memory 
160         r.x.ebx = selector;                     // Selector to free
161         int386 (0x31, &r, &r);
162 }
163
164 int dos_stack_initialized = 0;
165 ubyte * dos_stack = NULL;
166 ubyte * dos_stack_top = NULL;
167 #define DOS_STACK_SIZE (4*1024)                 // A big ol' 4K stack!!!
168
169 static void dpmi_setup_stack(dpmi_real_regs *rregs)  {
170         ushort temp_selector;
171
172         if ( !dos_stack_initialized )   {
173                 dos_stack_initialized = 1;
174                 dos_stack = dpmi_real_malloc( DOS_STACK_SIZE, &temp_selector );
175                 if ( dos_stack == NULL )        {
176                         printf( "Error allocating real mode stack!\n" );
177                         dos_stack_top = NULL;
178                 } else {
179                         dos_stack_top = &dos_stack[DOS_STACK_SIZE];
180                 }
181         }
182         
183         // Give this puppy a stack!!!
184         if ( dos_stack_top )    {
185                 rregs->ss = DPMI_real_segment(dos_stack_top);
186                 rregs->sp = DPMI_real_offset(dos_stack_top);
187         }
188 }
189
190
191 void dpmi_real_int386x( ubyte intno, dpmi_real_regs * rregs )
192 {
193         union REGS regs;
194         struct SREGS sregs;
195
196     /* Use DMPI call 300h to issue the DOS interrupt */
197
198    dpmi_setup_stack(rregs);
199         memset(&regs,0,sizeof(regs));
200         memset(&sregs,0,sizeof(sregs));
201    regs.w.ax = 0x0300;
202    regs.h.bl = intno;
203    regs.h.bh = 0;
204    regs.w.cx = 0;
205    sregs.es = FP_SEG(rregs);
206    regs.x.edi = FP_OFF(rregs);
207    int386x( 0x31, &regs, &regs, &sregs );
208 }
209
210 void dpmi_real_call(dpmi_real_regs * rregs)
211 {
212         union REGS regs;
213         struct SREGS sregs;
214
215    dpmi_setup_stack(rregs);
216
217     /* Use DMPI call 301h to call real mode procedure */
218         memset(&regs,0,sizeof(regs));
219         memset(&sregs,0,sizeof(sregs));
220    regs.w.ax = 0x0301;
221    regs.h.bh = 0;
222    regs.w.cx = 0;
223    sregs.es = FP_SEG(rregs);
224    regs.x.edi = FP_OFF(rregs);
225    int386x( 0x31, &regs, &regs, &sregs );
226         if ( regs.x.cflag )
227                 exit(regs.w.ax);
228 }
229
230 int total_bytes = 0;
231
232 int dpmi_unlock_region(void *address, unsigned length)
233 {
234         union REGS regs;
235         unsigned int linear;
236
237         linear = (unsigned int) address;
238 #ifdef __DJGPP__
239         linear += __djgpp_base_address;
240 #endif
241
242         total_bytes -= length;
243         //mprintf( 1, "DPMI unlocked %d bytes\n", total_bytes );
244
245         memset(&regs,0,sizeof(regs));
246         regs.w.ax = 0x601;                                      // DPMI Unlock Linear Region
247         regs.w.bx = (linear >> 16);             // Linear address in BX:CX
248         regs.w.cx = (linear & 0xFFFF);
249
250         regs.w.si = (length >> 16);             // Length in SI:DI
251         regs.w.di = (length & 0xFFFF);
252         int386 (0x31, &regs, &regs);
253         return (! regs.w.cflag);                        // Return 0 if can't lock
254 }
255
256 int dpmi_lock_region(void *address, unsigned length)
257 {
258         union REGS regs;
259         unsigned int linear;
260
261         linear = (unsigned int) address;
262 #ifdef __DJGPP__
263         linear += __djgpp_base_address;
264 #endif
265
266         total_bytes += length;
267         //mprintf( 1, "DPMI Locked down %d bytes\n", total_bytes );
268
269         memset(&regs,0,sizeof(regs));
270         regs.w.ax = 0x600;                                      // DPMI Lock Linear Region
271         regs.w.bx = (linear >> 16);             // Linear address in BX:CX
272         regs.w.cx = (linear & 0xFFFF);
273
274         regs.w.si = (length >> 16);             // Length in SI:DI
275         regs.w.di = (length & 0xFFFF);
276         int386 (0x31, &regs, &regs);
277         return (! regs.w.cflag);                        // Return 0 if can't lock
278 }
279
280
281 int dpmi_modify_selector_base( ushort selector, void * address )
282 {
283         union REGS regs;
284         unsigned int linear;
285
286         linear = (unsigned int)address;
287 #ifdef __DJGPP__
288         linear += __djgpp_base_address;
289 #endif
290
291         memset(&regs,0,sizeof(regs));
292         regs.w.ax = 0x0007;                                     // DPMI Change Selector Base Addres
293         regs.w.bx = selector;                           // Selector to change
294         regs.w.cx = (linear >> 16);             // Base address
295         regs.w.dx = (linear & 0xFFFF);
296         int386 (0x31, &regs, &regs);            // call dpmi
297         if (regs.w.cflag)
298                 return 0;                                                       // Return 0 if error
299
300         return 1;
301 }
302
303
304 int dpmi_modify_selector_limit( ushort selector, int size  )
305 {
306         union REGS regs;
307         unsigned int segment_limit;
308
309         segment_limit = (unsigned int) size;
310
311         memset(&regs,0,sizeof(regs));
312         regs.w.ax = 0x0008;                                     // DPMI Change Selector Limit
313         regs.w.bx = selector;                           // Selector to change
314         regs.w.cx = (segment_limit >> 16);              // Size of selector
315         regs.w.dx = (segment_limit & 0xFFFF);
316         int386 (0x31, &regs, &regs);            // call dpmi
317         if (regs.w.cflag)
318                 return 0;                                                       // Return 0 if error
319
320         return 1;
321 }
322
323
324 int dpmi_allocate_selector( void * address, int size, ushort * selector )
325 {
326         union REGS regs;
327
328
329         memset(&regs,0,sizeof(regs));
330         regs.w.ax = 0;                                                  // DPMI Allocate Selector
331         regs.w.cx = 1;                                                  // Allocate 1 selector
332         int386 (0x31, &regs, &regs);            // call dpmi
333         if (regs.w.cflag)
334                 return 0;                                                       // Return 0 if error
335         *selector = regs.w.ax;
336
337         if ( !dpmi_modify_selector_base( *selector, address ) )
338                 return 0;
339
340         if ( !dpmi_modify_selector_limit( *selector, size ) )
341                 return 0;
342
343 //      mprintf( 0, "Selector 0x%4x has base of 0x%8x, size %d bytes\n", *selector, linear,segment_limit);
344
345         return 1;
346 }
347
348 static void * dpmi_dos_buffer = NULL;
349 static ushort dpmi_dos_selector = 0;
350
351 void dpmi_close()
352 {
353         if (dpmi_dos_selector!=0)       {
354                 dpmi_dos_buffer = NULL;
355                 dpmi_dos_selector = 0;
356         }
357 }
358
359 typedef struct mem_data {
360         int     largest_block_bytes;
361         int     max_unlocked_page_allocation;
362         int     largest_lockable_pages;
363         int     total_pages;
364         int     unlocked_pages;
365         int     unused_physical_pages;
366         int     total_physical_pages;
367         int     free_linear_pages;
368         int     paging_size_pages;
369         int     reserved[3];
370 } mem_data;
371
372 unsigned int dpmi_virtual_memory=0;
373 unsigned int dpmi_available_memory=0;
374 unsigned int dpmi_physical_memory=0;
375 unsigned int dpmi_dos_memory = 0;
376
377 #ifdef __WATCOMC__
378 extern void cdecl _GETDS();
379 extern void cdecl cstart_();
380 #endif
381
382 int dpmi_init(int verbose)
383 {
384         union REGS regs;
385         struct SREGS sregs;
386         mem_data mi;
387
388         dpmi_dos_memory = dpmi_find_dos_memory();
389         
390         dpmi_dos_buffer = dpmi_real_malloc( 1024, &dpmi_dos_selector);
391         if (!dpmi_dos_buffer) {
392                 dpmi_dos_selector = 0;
393                 printf( "Error allocating 1K of DOS memory\n" );
394                 exit(1);
395         }
396         atexit(dpmi_close);
397
398         // Check dpmi
399         memset(&regs,0,sizeof(regs));
400         regs.x.eax = 0x400;                                                     // DPMI Get Memory Info
401    int386( 0x31, &regs, &regs );
402         if (!regs.w.cflag)      {
403                 if (verbose) printf( "V%d.%d, CPU:%d, VMM:", regs.h.ah, regs.h.al, regs.h.cl );
404                 if (regs.w.bx & 4)      {
405                         if (verbose) printf( "1" );
406                         dpmi_virtual_memory = 1;
407                 } else {
408                         if (verbose) printf( "0" );
409                 }
410         }
411
412         //--------- Find available memory
413         memset(&regs,0,sizeof(regs));
414         memset(&sregs,0,sizeof(sregs));
415         regs.x.eax = 0x500;                                                     // DPMI Get Memory Info
416    sregs.es = FP_SEG(&mi);
417    regs.x.edi = FP_OFF(&mi);
418    int386x( 0x31, &regs, &regs, &sregs );
419         if (!regs.w.cflag)      {
420                 if (verbose) printf( ", P:%dK", mi.largest_lockable_pages*4 );
421                 if (dpmi_virtual_memory)
422                         if (verbose) printf( ", A:%dK", mi.largest_block_bytes/1024 );
423                 //dpmi_physical_memory = mi.largest_lockable_pages*4096;
424                 //dpmi_available_memory = mi.largest_block_bytes;
425                 dpmi_physical_memory = mi.total_physical_pages*4096;
426                 dpmi_available_memory = mi.total_pages * 4096;
427         } else {
428                 if (verbose) printf( "MemInfo failed!" );
429                 dpmi_physical_memory = 16*1024*1024;            // Assume 16 MB
430                 dpmi_available_memory = 16*1024*1024;           // Assume 16 MB
431         }
432         
433 #ifdef __WATCOMC__
434         if (!dpmi_lock_region( _GETDS, 4096 ))  {
435                 printf( "Error locking _GETDS" );
436                 exit(1);
437         }
438         if (!dpmi_lock_region( cstart_, 4096 )) {
439                 printf( "Error locking cstart" );
440                 exit(1);
441         }
442         if (!dpmi_lock_region( _chain_intr, 4096 ))     {
443                 printf( "Error locking _chain_intr" );
444                 exit(1);
445         }
446 #endif
447         return 1;
448 }
449
450 void *dpmi_get_temp_low_buffer( int size )
451 {
452         if ( dpmi_dos_buffer == NULL ) return NULL;
453         if ( size > 1024 ) return NULL;
454
455         return dpmi_dos_buffer;
456 }
457
458 int dpmi_set_pm_handler(unsigned intnum, void far * isr )
459 {
460         union REGS regs;
461
462     /* Use DMPI call 204h to get pm interrrupt */
463         memset(&regs,0,sizeof(regs));
464    regs.w.ax = 0x0205;
465    regs.h.bl = intnum;
466         regs.w.cx = FP_SEG(isr);
467         regs.x.edx = FP_OFF(isr);
468         int386( 0x31, &regs, &regs );
469         if (!regs.w.cflag)      
470                 return 0;
471         return 1;
472 }
473