3 * Expanded Memory Manager library.
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 /* api library for DOS programs that want to use Expanded Memory (usually, EMM386.EXE)
14 * This code is intended for use with 16-bit real-mode programs. 32-bit programs have whatever the DOS extender
15 * offers and have no need for expanded memory, in fact, the DOS extender will often take up all extended
16 * & expanded memory for it's use and leave us nothing, which is why 32-bit builds of this library do not
21 * YES* = Yes, if DOS underneath provides it (or if DOS, when configured to load EMM386.EXE). Otherwise, No
23 * System/configuration Works? Limit?
26 * Microsoft Windows 3.0
28 * Standard mode NO -- EMM functions present, but will always report 0KB free. If more than 16MB of RAM is present, Windows causes a serious fault and DOSBox aborts
29 * 386 Enhanced mode YES* ?
30 * Microsoft Windows 3.1
31 * Standard mode NO -- EMM functions present, but will always report 0KB free
32 * 386 Enhanced mode YES* NO
33 * Microsoft Windows 3.11
34 * Standard mode NO -- EMM functions present, but will always report 0KB free
35 * 386 Enhanced mode YES* NO
37 * Microsoft Windows 95 (4.00.950)[1]
38 * Normal mode YES* 64MB API usually reports 16MB free. The test VM had 96MB of RAM
40 * MS-DOS mode (official) YES* 32MB
41 * MS-DOS mode (gui=0) YES* 32MB
42 * Microsoft Windows 98 (4.10.1998)[1]
43 * Normal mode YES* 64MB API usually reports 16MB free. The test VM had 96MB of RAM
44 * MS-DOS mode (gui=0) YES* 32MB
45 * Microsoft Windows ME (4.90.3000)[2]
46 * Normal mode YES* 64MB The API will never report more than 16MB free, but you can hack
47 * the PIF editor for the DOS program to allow up to 65534KB of
48 * EMM memory. The test program seems to have no problem allocating
49 * 48MB of expanded memory when said hack is applied. I suppose the
50 * API could handle more, but remember limits are imposed by the
51 * DOS Box VM and those are apparently represented by unsigned
52 * 16-bit integers, thus the 64MB (65534KB) limit.
53 * MS-DOS mode (bootdisk) ? ? I am unable to get Windows ME to make a bootdisk at this time.
54 * So I attemped to use a Windows ME bootdisk from bootdisk.com,
55 * and added DEVICE=EMM386.EXE only to find that at boot time
56 * it locks up the computer! So, I have no way of knowing what
57 * a pure MS-DOS mode EMM386.EXE from Windows ME does. It probably
58 * acts just like the Windows 95/98 versions...
59 * Microsoft Windows 2000 Professional
60 * Normal mode YES 32MB For whatever reason NTVDM defaults to NOT providing EMM memory.
61 * Limits to 32MB even if you type in larger values in the PIF editor.
63 * [1] EMM386.EXE for these systems will not be able to automatically find a page frame in QEMU or VirtualBox, probably because for
64 * unmapped regions the emulator returns 0x00 not 0xFF. To work around that, open CONFIG.SYS in a text editor and edit the
65 * line referring to EMM386.EXE. Add I=E000-EFFF and save. It should look like:
67 * DEVICE=C:\WINDOWS\EMM386.EXE I=E000-EFFF.
69 * [2] You're probably wondering... if Windows ME ignores AUTOEXEC.BAT and CONFIG.SYS then how the hell do you get EMM386.EXE
70 * loaded? Well, it's very obscure and undocumented, but you can get it loaded on boot up as follows:
72 * 1. Go to the start menu, select "run" and type "notepad c:\windows\system.ini"
73 * 2. Locate the [386Enh] section, go to the bottom of the section, and add the following lines of text to the end of [386Enh]
75 * EMMInclude=E000-EFFF
76 * ReservePageFrame=yes
78 * 3. Reboot, and enjoy
80 #if !defined(TARGET_OS2) && !defined(TARGET_WINDOWS)
90 #include <hw/cpu/cpu.h>
91 #include <hw/dos/dos.h>
92 #include <hw/dos/emm.h>
94 unsigned char emm_status = 0xFF; /* initialize to 0xFF as a way of indicating that nobody checked yet */
95 unsigned char emm_present = 0;
96 unsigned char emm_version = 0;
97 unsigned char emm_phys_pages = 0;
98 unsigned short emm_total_pages = 0;
99 unsigned int emm_page_frame_segment = 0;
100 unsigned short emm_unallocated_pages = 0;
101 struct emm_phys_page_map *emm_phys_map = NULL; /* maps physical page number -> segment address */
102 static const char *devname = "EMMXXXX0";
103 static const char *devname2 = "EMMQXXX0"; /* Microsoft publishes EMM standard then breaks it subtly in non-backwards compatible way... news at 11 */
104 #if TARGET_MSDOS == 32 && !defined(TARGET_OS2)
105 static uint16_t emm_phys_map_sel = 0;
107 static void emm_realmode_67_call(struct dpmi_realmode_call *rc) {
112 mov edi,rc ; we trust Watcom has left ES == DS
118 void emm_phys_pages_sort() {
122 #if TARGET_MSDOS == 16 && !defined(TARGET_OS2)
123 void emm_update_page_count() {
124 emm_unallocated_pages = 0;
127 if (!emm_present) return;
134 mov emm_unallocated_pages,bx
135 mov emm_total_pages,dx
143 emmptr = (void far*)_dos_getvect(0x67);
144 if (emmptr == (void far*)0)
147 /* apparently 10 bytes into the segment there is the magic string */
148 if ( _fmemcmp((char far*)MK_FP(FP_SEG(emmptr),0x000A),(char far*)devname,8) != 0 &&
149 _fmemcmp((char far*)MK_FP(FP_SEG(emmptr),0x000A),(char far*)devname2,8) != 0)
154 emm_page_frame_segment = 0;
169 mov emm_page_frame_segment,bx
179 if (emm_phys_map != NULL) {
184 if (emm_phys_map == NULL) {
185 /* see if the EMM provides a mapping table describing the real-mode segments
186 * corresponding to each physical page. if not, then assume only one page
187 * available. the table could be up to 256 entries. the API really doesn't
188 * have a way to tell us ahead of time, so assume the worst. */
189 assert(sizeof(struct emm_phys_page_map) == (size_t)4);
190 emm_phys_map = malloc(sizeof(struct emm_phys_page_map) * 256);
191 if (emm_phys_map != NULL) {
192 const unsigned int s = FP_SEG(emm_phys_map);
193 const unsigned int o = FP_OFF(emm_phys_map);
215 void *x = realloc(emm_phys_map,sizeof(struct emm_phys_page_map) * c);
216 if (x != NULL) { /* NTS: if we cannot realloc, well, too bad */
221 /* WARNING: we are assuming several things about the table.
222 * - That the table is sorted by real-mode segment (as described in the standard)
223 * - There are no duplicate page numbers
224 * - The table has as many entries as physical pages */
226 /* do ourself a favor and sort by page number the table */
227 emm_phys_pages_sort();
235 int emm_alloc_pages(unsigned int pages) {
255 int emm_free_pages(unsigned int handle) {
275 int emm_map_page(unsigned int handle,unsigned int phys_page,unsigned int log_page) {
278 if (phys_page >= (unsigned int)emm_phys_pages)
284 mov al,byte ptr phys_page
300 /* given physical page number, return real-mode segment value */
301 unsigned short emm_last_phys_page_segment(unsigned int phys_page) {
304 if (phys_page >= (unsigned int)emm_phys_pages)
307 /* if we don't have a copy of the EMM's mapping table, then assume that there is
308 * only physical page 0 at the page frame address */
309 if (phys_page == 0 && emm_phys_map == NULL)
310 return emm_page_frame_segment;
312 for (i=0;i < emm_phys_pages && emm_phys_map != NULL;i++) {
313 struct emm_phys_page_map *me = emm_phys_map + i;
314 if (phys_page == me->number)
321 void emm_update_page_count() {
322 emm_unallocated_pages = 0;
325 if (!emm_present) return;
332 mov emm_unallocated_pages,bx
333 mov emm_total_pages,dx
337 int probe_emm() {/*32-bit*/
338 unsigned int emm_seg;
342 /* Tricky. The DOS extender would likely translate the vector, when what we
343 really want is the segment value of int 67h */
344 emm_seg = *((uint16_t*)((0x67 << 2) + 2));
347 /* apparently 10 bytes into the segment there is the magic string */
348 if ( memcmp((void*)(((unsigned long)emm_seg << 4UL) + 0x000A),devname,8) != 0 &&
349 memcmp((void*)(((unsigned long)emm_seg << 4UL) + 0x000A),devname2,8) != 0)
355 emm_page_frame_segment = 0;
370 mov word ptr emm_page_frame_segment,bx
381 if (emm_phys_map != NULL) {
382 dpmi_free_dos(emm_phys_map_sel);
383 emm_phys_map_sel = 0;
387 if (emm_phys_map == NULL) {
388 /* see if the EMM provides a mapping table describing the real-mode segments
389 * corresponding to each physical page. if not, then assume only one page
390 * available. the table could be up to 256 entries. the API really doesn't
391 * have a way to tell us ahead of time, so assume the worst. */
392 assert(sizeof(struct emm_phys_page_map) == (size_t)4);
393 emm_phys_map = dpmi_alloc_dos(sizeof(struct emm_phys_page_map) * 256,&emm_phys_map_sel);
394 if (emm_phys_map != NULL) {
395 const unsigned int s = ((uint32_t)emm_phys_map) >> 4;
396 const unsigned int o = ((uint32_t)emm_phys_map) & 0xF;
397 struct dpmi_realmode_call rc={0};
404 emm_realmode_67_call(&rc);
405 if ((rc.eax&0xFF) == 0) c = rc.ecx & 0xFFFF;
408 dpmi_free_dos(emm_phys_map_sel);
409 emm_phys_map_sel = 0;
415 /* WARNING: we are assuming several things about the table.
416 * - That the table is sorted by real-mode segment (as described in the standard)
417 * - There are no duplicate page numbers
418 * - The table has as many entries as physical pages */
420 /* do ourself a favor and sort by page number the table */
421 emm_phys_pages_sort();
429 int emm_alloc_pages(unsigned int pages) {
450 int emm_free_pages(unsigned int handle) {
470 int emm_map_page(unsigned int handle,unsigned int phys_page,unsigned int log_page) {
473 if (phys_page >= (unsigned int)emm_phys_pages)
479 mov al,byte ptr phys_page
495 unsigned short emm_last_phys_page_segment(unsigned int phys_page) {
498 if (phys_page >= (unsigned int)emm_phys_pages)
501 /* if we don't have a copy of the EMM's mapping table, then assume that there is
502 * only physical page 0 at the page frame address */
503 if (phys_page == 0 && emm_phys_map == NULL)
504 return emm_page_frame_segment;
506 for (i=0;i < emm_phys_pages && emm_phys_map != NULL;i++) {
507 struct emm_phys_page_map *me = emm_phys_map + i;
508 if (phys_page == me->number)
516 #endif /* !defined(TARGET_OS2) && !defined(TARGET_WINDOWS) */