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
54 #include "src/lib/doslib/himemsys.h"
56 #if !defined(TARGET_WINDOWS) && !defined(TARGET_OS2)
57 /*===================================== MS-DOS only ===================================*/
59 unsigned long himem_sys_largest_free = 0;
60 unsigned long himem_sys_total_free = 0;
61 unsigned char himem_sys_present = 0;
62 unsigned int himem_sys_version = 0;
63 unsigned long himem_sys_entry = 0;
64 unsigned char himem_sys_flags = 0;
66 #if TARGET_MSDOS == 32
67 static void himem_sys_realmode_2F_call(struct dpmi_realmode_call *rc) {
72 mov edi,rc ; we trust Watcom has left ES == DS
77 /* WARNING: If this code is run under DOS4/GW it will silently fail.
78 If the HIMEM.SYS test program spouts nonsense about a
79 HIMEM.SYS that is v0.00 and has some random amount of memory
80 open, that's why. Make sure you link with dos32a. If that's
81 not possible, then run your program with dos32 like this:
84 static void himem_sys_realmode_entry_call(struct dpmi_realmode_call *rc) {
85 rc->ip = himem_sys_entry & 0xFFFF;
86 rc->cs = (himem_sys_entry >> 16UL);
88 if (dpmi_no_0301h > 0) {
89 /* Fuck you DOS4/GW! */
90 dpmi_alternate_rm_call(rc);
97 mov edi,rc ; we trust Watcom has left ES == DS
103 int probe_himem_sys() {
104 struct dpmi_realmode_call rc={0};
107 himem_sys_present = 0;
109 #if TARGET_MSDOS == 32
110 /* WAIT!!! We might be running under DOS4/GW. Make sure we can
111 call real-mode subroutines with the DPMI server. */
112 if (dpmi_no_0301h < 0) probe_dpmi();
116 int386(0x2F,®s,®s);
117 if (regs.h.al != 0x80) return 0;
118 himem_sys_present = 1;
120 /* use the realmode DPMI call to ensure the DPMI server does not screw up (translate) the segment register */
122 himem_sys_realmode_2F_call(&rc);
124 ((unsigned long)rc.es << 16UL) |
125 ((unsigned long)rc.ebx & 0xFFFFUL);
127 /* get version info, and autodetect whether it supports extended functions */
129 himem_sys_realmode_entry_call(&rc);
130 himem_sys_version = rc.eax & 0xFFFF;
131 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? */
135 himem_sys_realmode_entry_call(&rc);
136 himem_sys_flags = (rc.ebx & 0xFF == 0x80) ? 0 : HIMEM_F_4GB;
141 int himem_sys_global_a20(int enable) {
142 struct dpmi_realmode_call rc={0};
143 if (!himem_sys_present) return 0;
144 rc.eax = ((enable > 0) ? 3 : 4) << 8;
145 himem_sys_realmode_entry_call(&rc);
149 int himem_sys_local_a20(int enable) {
150 struct dpmi_realmode_call rc={0};
151 if (!himem_sys_present) return 0;
152 rc.eax = ((enable > 0) ? 5 : 6) << 8;
153 himem_sys_realmode_entry_call(&rc);
157 int himem_sys_query_a20() {
158 struct dpmi_realmode_call rc={0};
159 if (!himem_sys_present) return 0;
161 himem_sys_realmode_entry_call(&rc);
165 /* NTS: This function will likely set largest & free variables to zero,
166 * because most 32-bit DOS extenders take up all extended memory to do their work */
167 void himem_sys_update_free_memory_status() {
168 struct dpmi_realmode_call rc={0};
169 if (!himem_sys_present) return;
171 if (himem_sys_flags & HIMEM_F_4GB) {
173 himem_sys_realmode_entry_call(&rc);
174 himem_sys_largest_free = rc.eax;
175 himem_sys_total_free = rc.edx;
179 himem_sys_realmode_entry_call(&rc);
180 himem_sys_largest_free = rc.eax & 0xFFFF;
181 himem_sys_total_free = rc.edx & 0xFFFF;
185 int __cdecl himem_sys_alloc(unsigned long size/* in KB---not bytes*/) {
186 struct dpmi_realmode_call rc={0};
189 if (himem_sys_present) {
190 if (himem_sys_flags & HIMEM_F_4GB) {
193 himem_sys_realmode_entry_call(&rc);
194 if ((rc.eax & 0xFFFF) == 1) handle = rc.edx & 0xFFFF;
197 if (size >= 65535UL) return -1;
199 rc.edx = size & 0xFFFF;
200 himem_sys_realmode_entry_call(&rc);
201 if ((rc.eax & 0xFFFF) == 1) handle = rc.edx & 0xFFFF;
208 int himem_sys_free(int handle) {
209 struct dpmi_realmode_call rc={0};
211 rc.edx = handle & 0xFFFF;
212 himem_sys_realmode_entry_call(&rc);
213 return (int)(rc.eax & 0xFFFF);
216 int himem_sys_move(unsigned int dst_handle,uint32_t dst_offset,unsigned int src_handle,uint32_t src_offset,uint32_t length) {
217 struct dpmi_realmode_call rc={0};
222 if ((tmp = (unsigned char*)dpmi_alloc_dos(16,&tmpsel)) == NULL)
225 if (himem_sys_present) {
226 /* for src or dest references with handle == 0 the HIMEM.SYS driver actually
227 * takes SEG:OFFSET but we allow the caller to give us a physical memory addr. */
229 src_offset = ((src_offset << 12) & 0xFFFF0000UL) | (src_offset & 0xFUL);
231 dst_offset = ((dst_offset << 12) & 0xFFFF0000UL) | (dst_offset & 0xFUL);
233 *((uint32_t*)(tmp+0x0)) = length;
234 *((uint16_t*)(tmp+0x4)) = src_handle;
235 *((uint32_t*)(tmp+0x6)) = src_offset;
236 *((uint16_t*)(tmp+0xA)) = dst_handle;
237 *((uint32_t*)(tmp+0xC)) = dst_offset;
239 const uint16_t ofsv = (uint16_t)tmp & 0xFUL;
240 const uint16_t segv = (uint16_t)((size_t)tmp >> 4UL);
245 himem_sys_realmode_entry_call(&rc);
246 retv = rc.eax & 0xFFFF;
250 dpmi_free_dos(tmpsel);
254 uint32_t himem_sys_lock(unsigned int handle) {
255 struct dpmi_realmode_call rc={0};
258 if (himem_sys_present) {
260 rc.edx = handle & 0xFFFF;
261 himem_sys_realmode_entry_call(&rc);
262 if (rc.eax & 1) o = ((rc.edx & 0xFFFF) << 16) | (rc.ebx & 0xFFFF);
268 int himem_sys_unlock(unsigned int handle) {
269 struct dpmi_realmode_call rc={0};
272 if (himem_sys_present) {
274 rc.edx = handle & 0xFFFF;
275 himem_sys_realmode_entry_call(&rc);
276 retv = rc.eax & 0xFFFF;
282 int himem_sys_realloc(unsigned int handle,unsigned long size/* in KB---not bytes*/) {
283 struct dpmi_realmode_call rc={0};
286 if (himem_sys_present) {
287 if (himem_sys_flags & HIMEM_F_4GB) {
290 rc.edx = handle & 0xFFFF;
291 himem_sys_realmode_entry_call(&rc);
292 retv = rc.eax & 0xFFFF;
295 if (size >= 65535UL) return 0;
298 rc.edx = handle & 0xFFFF;
299 himem_sys_realmode_entry_call(&rc);
300 retv = rc.eax & 0xFFFF;
307 int himem_sys_get_handle_info(unsigned int handle,struct himem_block_info *b) {
308 struct dpmi_realmode_call rc={0};
311 if (himem_sys_present) {
312 if (himem_sys_flags & HIMEM_F_4GB) {
314 rc.edx = handle & 0xFFFF;
315 himem_sys_realmode_entry_call(&rc);
316 b->block_length_kb = rc.edx;
317 b->lock_count = (rc.ebx >> 8) & 0xFF;
318 b->free_handles = rc.ebx & 0xFF;
319 retv = rc.eax & 0xFFFF;
323 rc.edx = handle & 0xFFFF;
324 himem_sys_realmode_entry_call(&rc);
325 b->block_length_kb = rc.edx & 0xFFFF;
326 b->lock_count = (rc.ebx >> 8) & 0xFF;
327 b->free_handles = rc.ebx & 0xFF;
328 retv = rc.eax & 0xFFFF;
334 #else /* 16-bit real mode */
335 int probe_himem_sys() {
339 himem_sys_present = 0;
340 /* NTS: If this is an 8086, then there is no extended memory, and therefore no reason to call HIMEM.SYS */
341 if (cpu_basic_level < 0) cpu_probe();
342 if (cpu_basic_level < 2) return 0;
345 int86(0x2F,®s,®s);
346 if (regs.h.al != 0x80) return 0;
347 himem_sys_present = 1;
350 int86x(0x2F,®s,®s,&sregs);
352 ((unsigned long)sregs.es << 16UL) |
353 ((unsigned long)regs.w.bx);
356 xor ah,ah ; function 0x00
357 call [himem_sys_entry]
358 mov himem_sys_version,ax
359 and dl,1 ; DX=1 if HMA present, else 0 if not. Your HIMEM.SYS is noncompliant if any other values were put here
360 mov himem_sys_flags,dl ; this maps to HIMEM_F_HMA
363 /* does this HIMEM.SYS support the extended functions to address more than 64MB of memory? */
365 mov ah,0x88 ; function 0x88: query any free memory
367 call [himem_sys_entry]
368 cmp bl,0x80 ; BL=0x80 if error (unsupported)
370 or himem_sys_flags,2 ; <- HIMEM_F_4GB
374 /* Unfortunately, there are HIMEM.SYS implementations that will respond to the extended commands, but fail
375 to read or make use of the upper 16 bits of the registers. These broken implementations are easy to check
376 for: just allocate a block that is 64MB in size (DX == 0 but EDX == 0x00010000) and if the allocation
377 succeeds, use the Get Block Info command to verify that it is in fact 64MB in size. The broken implementation
378 will create a zero-length block (which is legal in the HIMEM.SYS standard) and will say so when we ask.
380 Known HIMEM.SYS broken emulation:
382 - Responds to extended commands as if newer HIMEM.SYS but ignores upper 16 bits. You might as well
383 just call the original API functions you'll get just as far. DOSBox doesn't emulate more than 64MB
385 if (himem_sys_flags & HIMEM_F_4GB) {
386 int h = himem_sys_alloc(0x10000UL);
388 struct himem_block_info binf;
389 if (himem_sys_get_handle_info(h,&binf)) {
390 if (binf.block_length_kb == 0 || binf.block_length_kb == 1) {
391 /* Nope. Our 64MB allocation was mis-interpreted as a zero-length allocation request */
392 himem_sys_flags &= ~HIMEM_F_4GB;
402 int himem_sys_global_a20(int enable) {
405 if (!himem_sys_present) return 0;
406 enable = (enable > 0) ? 3 : 4;
409 mov ah,byte ptr enable
410 call [himem_sys_entry]
417 int himem_sys_local_a20(int enable) {
420 if (!himem_sys_present) return 0;
421 enable = (enable > 0) ? 5 : 6;
424 mov ah,byte ptr enable
425 call [himem_sys_entry]
432 int himem_sys_query_a20() {
435 if (!himem_sys_present) return 0;
439 call [himem_sys_entry]
446 void himem_sys_update_free_memory_status() {
447 if (!himem_sys_present) return;
449 if (himem_sys_flags & HIMEM_F_4GB) {
452 call [himem_sys_entry]
454 mov word ptr himem_sys_largest_free,ax
455 db 0x66,0xC1,0xE8,0x10 ; shr eax,16
456 mov word ptr himem_sys_largest_free+2,ax
458 mov word ptr himem_sys_total_free,dx
459 db 0x66,0xC1,0xEA,0x10 ; shr edx,16
460 mov word ptr himem_sys_total_free+2,dx
466 call [himem_sys_entry]
468 mov word ptr himem_sys_largest_free,ax
469 mov word ptr himem_sys_largest_free+2,0
471 mov word ptr himem_sys_total_free,dx
472 mov word ptr himem_sys_total_free+2,0
477 /* WARNING: do not remove the __cdecl declaration, the hack below relies on it.
478 * Watcom's native register protocol will copy the long value to a 16-bit
479 * word on stack and then we won't get the full value. */
480 int __cdecl himem_sys_alloc(unsigned long size/* in KB---not bytes*/) {
483 if (himem_sys_present) {
484 if (himem_sys_flags & HIMEM_F_4GB) {
488 mov dx,word ptr size ; the 0x66 makes it 'mov edx,size'
489 call [himem_sys_entry]
494 alloc_ok: mov handle,dx
498 if (size >= 65535UL) return -1;
503 call [himem_sys_entry]
508 alloc_ok: mov handle,dx
516 int himem_sys_free(int handle) {
519 if (himem_sys_present) {
523 call [himem_sys_entry]
531 int himem_sys_move(unsigned int dst_handle,uint32_t dst_offset,unsigned int src_handle,uint32_t src_offset,uint32_t length) {
532 unsigned char tmp[16]; /* struct */
535 if (himem_sys_present) {
536 /* for src or dest references with handle == 0 the HIMEM.SYS driver actually
537 * takes SEG:OFFSET but we allow the caller to give us a physical memory addr. */
539 src_offset = ((src_offset << 12) & 0xFFFF0000UL) | (src_offset & 0xFUL);
541 dst_offset = ((dst_offset << 12) & 0xFFFF0000UL) | (dst_offset & 0xFUL);
543 *((uint32_t*)(tmp+0x0)) = length;
544 *((uint16_t*)(tmp+0x4)) = src_handle;
545 *((uint32_t*)(tmp+0x6)) = src_offset;
546 *((uint16_t*)(tmp+0xA)) = dst_handle;
547 *((uint32_t*)(tmp+0xC)) = dst_offset;
549 const void far *x = (void far*)tmp;
550 const uint16_t ofsv = FP_OFF(x);
551 const uint16_t segv = FP_SEG(x);
552 const uint16_t dsseg = 0;
557 assert(segv == dsseg);
561 call [himem_sys_entry]
570 uint32_t himem_sys_lock(unsigned int handle) {
573 if (himem_sys_present) {
577 call [himem_sys_entry]
582 lockend: mov word ptr o,bx
590 int himem_sys_unlock(unsigned int handle) {
593 if (himem_sys_present) {
597 call [himem_sys_entry]
605 int __cdecl himem_sys_realloc(unsigned int handle,unsigned long size/* in KB---not bytes*/) {
608 if (himem_sys_present) {
609 if (himem_sys_flags & HIMEM_F_4GB) {
613 mov bx,word ptr size ; the 0x66 makes it 'mov ebx,size'
615 call [himem_sys_entry]
620 if (size >= 65535UL) return 0;
626 call [himem_sys_entry]
635 int himem_sys_get_handle_info(unsigned int handle,struct himem_block_info *b) {
638 if (himem_sys_present) {
639 if (himem_sys_flags & HIMEM_F_4GB) {
643 call [himem_sys_entry]
646 mov word ptr [si],dx ; becomes dword ptr [esi]
647 mov byte ptr [si+4],bh ; lock count
648 mov byte ptr [si+5],bl ; free handles
656 call [himem_sys_entry]
659 mov word ptr [si+2],0
660 mov byte ptr [si+4],bh ; lock count
661 mov byte ptr [si+5],bl ; free handles
671 #endif /* !defined(TARGET_WINDOWS) && !defined(TARGET_OS2) */