3 * Support calls to use HIMEM.SYS
4 * (C) 2009-2012 Jonathan Campbell.
5 * Hackipedia DOS library.
7 * This code is licensed under the LGPL.
8 * <insert LGPL legal text here>
11 /* HIMEM.SYS api for DOS programs library
18 * System/configuration Works? Supports >= 64MB?
19 * DOSBox 0.74 YES NO, BUGS
21 * Microsoft Windows 3.0
23 * Standard mode NO -- Reports 0KB free memory (why?)
24 * 386 Enhanced mode YES --
25 * Microsoft Windows 3.1
26 * Standard mode NO -- Reports 0KB free memory (why?)
27 * 386 Enhanced mode YES --
28 * Microsoft Windows 3.11
29 * Standard mode NO -- Reports 0KB free memory (why?)
30 * 386 Enhanced mode YES --
32 * Microsoft Windows 95 (4.00.950)
33 * Normal mode YES YES Allows my program to request more memory than available, then triggers the "needs MS-DOS mode" warning (PIF: XMS memory setting on "auto")
34 * Normal mode (PIF: XMS=2MB) YES YES This program's attempts to alloc > 1MB fail (correctly). It still triggers the "needs MS-DOS mode" dialog
35 * Safe mode YES YES Allows my program to request more memory than available, then triggers the "needs MS-DOS mode" warning (PIF: XMS memory setting on "auto")
36 * MS-DOS mode (official) YES YES
37 * MS-DOS mode (gui=0) YES YES
38 * * NOTE: I also noticed that within the DOS box the Windows kernel denies all requests to lock a handle
39 * Microsoft Windows 98 (4.10.1998)
40 * Normal mode YES YES Same problem as Windows 95
41 * MS-DOS mode (gui=0) YES YES
42 * Microsoft Windows ME (4.90.3000)
43 * Normal mode YES YES Same problem as Windows 95, triggers "needs MS-DOS mode" warning----Hey wait, Windows ME doesn't have a "DOS mode". A hilarious oversight by Microsoft.
44 * Microsoft Windows 2000 Professional
45 * Normal mode YES NO NTVDM is very conservative about HIMEM.SYS allocation, listing the largest block size as 919KB. So apparently the default is that MS-DOS
46 * applications are allowed up to 1MB of extended memory? The usual MS-DOS configuration options are there, suggesting that in reality the
47 * program should have NO extended memory (?). Apparently when you say "none" what it really means is "1MB". Locking the handle is permitted though.
48 * The highest value you can enter in the PIF through the GUI is 65534. Setting to 65535 somehow triggers internally the "auto" setting, and is
49 * the highest value the editor will let you type in.
50 * Microsoft Windows XP Professional
51 * Normal mode YES NO Same problems as Windows 2000
62 #include <hw/cpu/cpu.h>
63 #include <hw/dos/dos.h>
64 #include <hw/dos/himemsys.h>
66 #if !defined(TARGET_WINDOWS) && !defined(TARGET_OS2)
67 /*===================================== MS-DOS only ===================================*/
69 unsigned long himem_sys_largest_free = 0;
70 unsigned long himem_sys_total_free = 0;
71 unsigned char himem_sys_present = 0;
72 unsigned int himem_sys_version = 0;
73 unsigned long himem_sys_entry = 0;
74 unsigned char himem_sys_flags = 0;
76 #if TARGET_MSDOS == 32
77 static void himem_sys_realmode_2F_call(struct dpmi_realmode_call *rc) {
82 mov edi,rc ; we trust Watcom has left ES == DS
87 /* WARNING: If this code is run under DOS4/GW it will silently fail.
88 If the HIMEM.SYS test program spouts nonsense about a
89 HIMEM.SYS that is v0.00 and has some random amount of memory
90 open, that's why. Make sure you link with dos32a. If that's
91 not possible, then run your program with dos32 like this:
94 static void himem_sys_realmode_entry_call(struct dpmi_realmode_call *rc) {
95 rc->ip = himem_sys_entry & 0xFFFF;
96 rc->cs = (himem_sys_entry >> 16UL);
98 if (dpmi_no_0301h > 0) {
99 /* Fuck you DOS4/GW! */
100 dpmi_alternate_rm_call(rc);
107 mov edi,rc ; we trust Watcom has left ES == DS
113 int probe_himem_sys() {
114 struct dpmi_realmode_call rc={0};
117 himem_sys_present = 0;
119 #if TARGET_MSDOS == 32
120 /* WAIT!!! We might be running under DOS4/GW. Make sure we can
121 call real-mode subroutines with the DPMI server. */
122 if (dpmi_no_0301h < 0) probe_dpmi();
126 int386(0x2F,®s,®s);
127 if (regs.h.al != 0x80) return 0;
128 himem_sys_present = 1;
130 /* use the realmode DPMI call to ensure the DPMI server does not screw up (translate) the segment register */
132 himem_sys_realmode_2F_call(&rc);
134 ((unsigned long)rc.es << 16UL) |
135 ((unsigned long)rc.ebx & 0xFFFFUL);
137 /* get version info, and autodetect whether it supports extended functions */
139 himem_sys_realmode_entry_call(&rc);
140 himem_sys_version = rc.eax & 0xFFFF;
141 himem_sys_flags = (rc.edx & 1) ? HIMEM_F_HMA : 0; /* FIXME: Am I crazy, or does HIMEM.SYS suddenly stop mentioning HMA when we call from protected mode? */
145 himem_sys_realmode_entry_call(&rc);
146 himem_sys_flags = (rc.ebx & 0xFF == 0x80) ? 0 : HIMEM_F_4GB;
151 int himem_sys_global_a20(int enable) {
152 struct dpmi_realmode_call rc={0};
153 if (!himem_sys_present) return 0;
154 rc.eax = ((enable > 0) ? 3 : 4) << 8;
155 himem_sys_realmode_entry_call(&rc);
159 int himem_sys_local_a20(int enable) {
160 struct dpmi_realmode_call rc={0};
161 if (!himem_sys_present) return 0;
162 rc.eax = ((enable > 0) ? 5 : 6) << 8;
163 himem_sys_realmode_entry_call(&rc);
167 int himem_sys_query_a20() {
168 struct dpmi_realmode_call rc={0};
169 if (!himem_sys_present) return 0;
171 himem_sys_realmode_entry_call(&rc);
175 /* NTS: This function will likely set largest & free variables to zero,
176 * because most 32-bit DOS extenders take up all extended memory to do their work */
177 void himem_sys_update_free_memory_status() {
178 struct dpmi_realmode_call rc={0};
179 if (!himem_sys_present) return;
181 if (himem_sys_flags & HIMEM_F_4GB) {
183 himem_sys_realmode_entry_call(&rc);
184 himem_sys_largest_free = rc.eax;
185 himem_sys_total_free = rc.edx;
189 himem_sys_realmode_entry_call(&rc);
190 himem_sys_largest_free = rc.eax & 0xFFFF;
191 himem_sys_total_free = rc.edx & 0xFFFF;
195 int __cdecl himem_sys_alloc(unsigned long size/* in KB---not bytes*/) {
196 struct dpmi_realmode_call rc={0};
199 if (himem_sys_present) {
200 if (himem_sys_flags & HIMEM_F_4GB) {
203 himem_sys_realmode_entry_call(&rc);
204 if ((rc.eax & 0xFFFF) == 1) handle = rc.edx & 0xFFFF;
207 if (size >= 65535UL) return -1;
209 rc.edx = size & 0xFFFF;
210 himem_sys_realmode_entry_call(&rc);
211 if ((rc.eax & 0xFFFF) == 1) handle = rc.edx & 0xFFFF;
218 int himem_sys_free(int handle) {
219 struct dpmi_realmode_call rc={0};
221 rc.edx = handle & 0xFFFF;
222 himem_sys_realmode_entry_call(&rc);
223 return (int)(rc.eax & 0xFFFF);
226 int himem_sys_move(unsigned int dst_handle,uint32_t dst_offset,unsigned int src_handle,uint32_t src_offset,uint32_t length) {
227 struct dpmi_realmode_call rc={0};
232 if ((tmp = (unsigned char*)dpmi_alloc_dos(16,&tmpsel)) == NULL)
235 if (himem_sys_present) {
236 /* for src or dest references with handle == 0 the HIMEM.SYS driver actually
237 * takes SEG:OFFSET but we allow the caller to give us a physical memory addr. */
239 src_offset = ((src_offset << 12) & 0xFFFF0000UL) | (src_offset & 0xFUL);
241 dst_offset = ((dst_offset << 12) & 0xFFFF0000UL) | (dst_offset & 0xFUL);
243 *((uint32_t*)(tmp+0x0)) = length;
244 *((uint16_t*)(tmp+0x4)) = src_handle;
245 *((uint32_t*)(tmp+0x6)) = src_offset;
246 *((uint16_t*)(tmp+0xA)) = dst_handle;
247 *((uint32_t*)(tmp+0xC)) = dst_offset;
249 const uint16_t ofsv = (uint16_t)tmp & 0xFUL;
250 const uint16_t segv = (uint16_t)((size_t)tmp >> 4UL);
255 himem_sys_realmode_entry_call(&rc);
256 retv = rc.eax & 0xFFFF;
260 dpmi_free_dos(tmpsel);
264 uint32_t himem_sys_lock(unsigned int handle) {
265 struct dpmi_realmode_call rc={0};
268 if (himem_sys_present) {
270 rc.edx = handle & 0xFFFF;
271 himem_sys_realmode_entry_call(&rc);
272 if (rc.eax & 1) o = ((rc.edx & 0xFFFF) << 16) | (rc.ebx & 0xFFFF);
278 int himem_sys_unlock(unsigned int handle) {
279 struct dpmi_realmode_call rc={0};
282 if (himem_sys_present) {
284 rc.edx = handle & 0xFFFF;
285 himem_sys_realmode_entry_call(&rc);
286 retv = rc.eax & 0xFFFF;
292 int himem_sys_realloc(unsigned int handle,unsigned long size/* in KB---not bytes*/) {
293 struct dpmi_realmode_call rc={0};
296 if (himem_sys_present) {
297 if (himem_sys_flags & HIMEM_F_4GB) {
300 rc.edx = handle & 0xFFFF;
301 himem_sys_realmode_entry_call(&rc);
302 retv = rc.eax & 0xFFFF;
305 if (size >= 65535UL) return 0;
308 rc.edx = handle & 0xFFFF;
309 himem_sys_realmode_entry_call(&rc);
310 retv = rc.eax & 0xFFFF;
317 int himem_sys_get_handle_info(unsigned int handle,struct himem_block_info *b) {
318 struct dpmi_realmode_call rc={0};
321 if (himem_sys_present) {
322 if (himem_sys_flags & HIMEM_F_4GB) {
324 rc.edx = handle & 0xFFFF;
325 himem_sys_realmode_entry_call(&rc);
326 b->block_length_kb = rc.edx;
327 b->lock_count = (rc.ebx >> 8) & 0xFF;
328 b->free_handles = rc.ebx & 0xFF;
329 retv = rc.eax & 0xFFFF;
333 rc.edx = handle & 0xFFFF;
334 himem_sys_realmode_entry_call(&rc);
335 b->block_length_kb = rc.edx & 0xFFFF;
336 b->lock_count = (rc.ebx >> 8) & 0xFF;
337 b->free_handles = rc.ebx & 0xFF;
338 retv = rc.eax & 0xFFFF;
344 #else /* 16-bit real mode */
345 int probe_himem_sys() {
349 himem_sys_present = 0;
350 /* NTS: If this is an 8086, then there is no extended memory, and therefore no reason to call HIMEM.SYS */
351 if (cpu_basic_level < 0) cpu_probe();
352 if (cpu_basic_level < 2) return 0;
355 int86(0x2F,®s,®s);
356 if (regs.h.al != 0x80) return 0;
357 himem_sys_present = 1;
360 int86x(0x2F,®s,®s,&sregs);
362 ((unsigned long)sregs.es << 16UL) |
363 ((unsigned long)regs.w.bx);
366 xor ah,ah ; function 0x00
367 call [himem_sys_entry]
368 mov himem_sys_version,ax
369 and dl,1 ; DX=1 if HMA present, else 0 if not. Your HIMEM.SYS is noncompliant if any other values were put here
370 mov himem_sys_flags,dl ; this maps to HIMEM_F_HMA
373 /* does this HIMEM.SYS support the extended functions to address more than 64MB of memory? */
375 mov ah,0x88 ; function 0x88: query any free memory
377 call [himem_sys_entry]
378 cmp bl,0x80 ; BL=0x80 if error (unsupported)
380 or himem_sys_flags,2 ; <- HIMEM_F_4GB
384 /* Unfortunately, there are HIMEM.SYS implementations that will respond to the extended commands, but fail
385 to read or make use of the upper 16 bits of the registers. These broken implementations are easy to check
386 for: just allocate a block that is 64MB in size (DX == 0 but EDX == 0x00010000) and if the allocation
387 succeeds, use the Get Block Info command to verify that it is in fact 64MB in size. The broken implementation
388 will create a zero-length block (which is legal in the HIMEM.SYS standard) and will say so when we ask.
390 Known HIMEM.SYS broken emulation:
392 - Responds to extended commands as if newer HIMEM.SYS but ignores upper 16 bits. You might as well
393 just call the original API functions you'll get just as far. DOSBox doesn't emulate more than 64MB
395 if (himem_sys_flags & HIMEM_F_4GB) {
396 int h = himem_sys_alloc(0x10000UL);
398 struct himem_block_info binf;
399 if (himem_sys_get_handle_info(h,&binf)) {
400 if (binf.block_length_kb == 0 || binf.block_length_kb == 1) {
401 /* Nope. Our 64MB allocation was mis-interpreted as a zero-length allocation request */
402 himem_sys_flags &= ~HIMEM_F_4GB;
412 int himem_sys_global_a20(int enable) {
415 if (!himem_sys_present) return 0;
416 enable = (enable > 0) ? 3 : 4;
419 mov ah,byte ptr enable
420 call [himem_sys_entry]
427 int himem_sys_local_a20(int enable) {
430 if (!himem_sys_present) return 0;
431 enable = (enable > 0) ? 5 : 6;
434 mov ah,byte ptr enable
435 call [himem_sys_entry]
442 int himem_sys_query_a20() {
445 if (!himem_sys_present) return 0;
449 call [himem_sys_entry]
456 void himem_sys_update_free_memory_status() {
457 if (!himem_sys_present) return;
459 if (himem_sys_flags & HIMEM_F_4GB) {
462 call [himem_sys_entry]
464 mov word ptr himem_sys_largest_free,ax
465 db 0x66,0xC1,0xE8,0x10 ; shr eax,16
466 mov word ptr himem_sys_largest_free+2,ax
468 mov word ptr himem_sys_total_free,dx
469 db 0x66,0xC1,0xEA,0x10 ; shr edx,16
470 mov word ptr himem_sys_total_free+2,dx
476 call [himem_sys_entry]
478 mov word ptr himem_sys_largest_free,ax
479 mov word ptr himem_sys_largest_free+2,0
481 mov word ptr himem_sys_total_free,dx
482 mov word ptr himem_sys_total_free+2,0
487 /* WARNING: do not remove the __cdecl declaration, the hack below relies on it.
488 * Watcom's native register protocol will copy the long value to a 16-bit
489 * word on stack and then we won't get the full value. */
490 int __cdecl himem_sys_alloc(unsigned long size/* in KB---not bytes*/) {
493 if (himem_sys_present) {
494 if (himem_sys_flags & HIMEM_F_4GB) {
498 mov dx,word ptr size ; the 0x66 makes it 'mov edx,size'
499 call [himem_sys_entry]
504 alloc_ok: mov handle,dx
508 if (size >= 65535UL) return -1;
513 call [himem_sys_entry]
518 alloc_ok: mov handle,dx
526 int himem_sys_free(int handle) {
529 if (himem_sys_present) {
533 call [himem_sys_entry]
541 int himem_sys_move(unsigned int dst_handle,uint32_t dst_offset,unsigned int src_handle,uint32_t src_offset,uint32_t length) {
542 unsigned char tmp[16]; /* struct */
545 if (himem_sys_present) {
546 /* for src or dest references with handle == 0 the HIMEM.SYS driver actually
547 * takes SEG:OFFSET but we allow the caller to give us a physical memory addr. */
549 src_offset = ((src_offset << 12) & 0xFFFF0000UL) | (src_offset & 0xFUL);
551 dst_offset = ((dst_offset << 12) & 0xFFFF0000UL) | (dst_offset & 0xFUL);
553 *((uint32_t*)(tmp+0x0)) = length;
554 *((uint16_t*)(tmp+0x4)) = src_handle;
555 *((uint32_t*)(tmp+0x6)) = src_offset;
556 *((uint16_t*)(tmp+0xA)) = dst_handle;
557 *((uint32_t*)(tmp+0xC)) = dst_offset;
559 const void far *x = (void far*)tmp;
560 const uint16_t ofsv = FP_OFF(x);
561 const uint16_t segv = FP_SEG(x);
562 const uint16_t dsseg = 0;
567 assert(segv == dsseg);
571 call [himem_sys_entry]
580 uint32_t himem_sys_lock(unsigned int handle) {
583 if (himem_sys_present) {
587 call [himem_sys_entry]
592 lockend: mov word ptr o,bx
600 int himem_sys_unlock(unsigned int handle) {
603 if (himem_sys_present) {
607 call [himem_sys_entry]
615 int __cdecl himem_sys_realloc(unsigned int handle,unsigned long size/* in KB---not bytes*/) {
618 if (himem_sys_present) {
619 if (himem_sys_flags & HIMEM_F_4GB) {
623 mov bx,word ptr size ; the 0x66 makes it 'mov ebx,size'
625 call [himem_sys_entry]
630 if (size >= 65535UL) return 0;
636 call [himem_sys_entry]
645 int himem_sys_get_handle_info(unsigned int handle,struct himem_block_info *b) {
648 if (himem_sys_present) {
649 if (himem_sys_flags & HIMEM_F_4GB) {
653 call [himem_sys_entry]
656 mov word ptr [si],dx ; becomes dword ptr [esi]
657 mov byte ptr [si+4],bh ; lock count
658 mov byte ptr [si+5],bl ; free handles
666 call [himem_sys_entry]
669 mov word ptr [si+2],0
670 mov byte ptr [si+4],bh ; lock count
671 mov byte ptr [si+5],bl ; free handles
681 #endif /* !defined(TARGET_WINDOWS) && !defined(TARGET_OS2) */