]> 4ch.mooo.com Git - 16.git/blob - src/lib/dl/emm.c
wwww
[16.git] / src / lib / dl / emm.c
1 /* emm.c
2  *
3  * Expanded Memory Manager library.
4  * (C) 2009-2012 Jonathan Campbell.
5  * Hackipedia DOS library.
6  *
7  * This code is licensed under the LGPL.
8  * <insert LGPL legal text here>
9  */
10
11 /* api library for DOS programs that want to use Expanded Memory (usually, EMM386.EXE)
12  *
13  * NOTES:
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
17  *    function.
18  *
19  * Testing:
20  *
21  *    YES* = Yes, if DOS underneath provides it (or if DOS, when configured to load EMM386.EXE). Otherwise, No
22  *
23  * System/configuration                 Works?   Limit?
24  * DOSBox 0.74                          YES      NO
25  * DOSBox 0.74 +
26  *   Microsoft Windows 3.0
27  *     Real mode                        YES*     NO
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
36  * QEMU/VirtualBox
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
39  *     Safe mode                        YES*     64MB
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.
62  *
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:
66  *
67  *     DEVICE=C:\WINDOWS\EMM386.EXE I=E000-EFFF.
68  *
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:
71  *
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]
74  *
75  *             EMMInclude=E000-EFFF
76  *             ReservePageFrame=yes
77  *
78  *        3. Reboot, and enjoy
79  */
80 #if !defined(TARGET_OS2) && !defined(TARGET_WINDOWS)
81
82 #include "src/lib/doslib/emm.h"
83
84 byte emm_status = 0xFF; /* initialize to 0xFF as a way of indicating that nobody checked yet */
85 byte emm_present = 0;
86 byte emm_version = 0;
87 byte emm_phys_pages = 0;
88 word emm_total_pages = 0;
89 unsigned int emm_page_frame_segment = 0;
90 word emm_unallocated_pages = 0;
91 struct emm_phys_page_map *emm_phys_map = NULL;  /* maps physical page number -> segment address */
92 static const char *devname = "EMMXXXX0";
93 static const char *devname2 = "EMMQXXX0"; /* Microsoft publishes EMM standard then breaks it subtly in non-backwards compatible way... news at 11 */
94 #if TARGET_MSDOS == 32 && !defined(TARGET_OS2)
95 static uint16_t emm_phys_map_sel = 0;
96
97 static void emm_realmode_67_call(struct dpmi_realmode_call *rc) {
98         __asm {
99                 mov     ax,0x0300
100                 mov     bx,0x0067
101                 xor     cx,cx
102                 mov     edi,rc          ; we trust Watcom has left ES == DS
103                 int     0x31            ; call DPMI
104         }
105 }
106 #endif
107
108 void emm_phys_pages_sort() {
109         /* TODO */
110 }
111
112 #if TARGET_MSDOS == 16 && !defined(TARGET_OS2)
113 void emm_update_page_count() {
114         emm_unallocated_pages = 0;
115         emm_total_pages = 0;
116
117         if (!emm_present) return;
118
119         __asm {
120                 mov     ah,0x42
121                 push    es
122                 int     0x67
123                 pop     es
124                 mov     emm_unallocated_pages,bx
125                 mov     emm_total_pages,dx
126         }
127 }
128
129 int probe_emm() {
130         void far *emmptr;
131
132         emm_present = 0;
133         emmptr = (void far*)_dos_getvect(0x67);
134         if (emmptr == (void far*)0)
135                 return 0;
136
137         /* apparently 10 bytes into the segment there is the magic string */
138         if (    _fmemcmp((char far*)MK_FP(FP_SEG(emmptr),0x000A),(char far*)devname,8) != 0 &&
139                 _fmemcmp((char far*)MK_FP(FP_SEG(emmptr),0x000A),(char far*)devname2,8) != 0)
140                 return 0;
141
142         emm_present = 1;
143         emm_phys_pages = 1;
144         emm_page_frame_segment = 0;
145
146         __asm {
147                 mov     ah,0x40
148                 push    es
149                 int     0x67
150                 pop     es
151                 mov     emm_status,ah
152
153                 mov     ah,0x41
154                 push    es
155                 int     0x67
156                 pop     es
157                 or      ah,ah
158                 jnz     pfn_end
159                 mov     emm_page_frame_segment,bx
160
161                 mov     ah,0x46
162                 push    es
163                 int     0x67
164                 pop     es
165                 mov     emm_version,al
166 pfn_end:
167         }
168
169         if (emm_phys_map != NULL) {
170                 free(emm_phys_map);
171                 emm_phys_map = NULL;
172         }
173
174         if (emm_phys_map == NULL) {
175                 /* see if the EMM provides a mapping table describing the real-mode segments
176                  * corresponding to each physical page. if not, then assume only one page
177                  * available. the table could be up to 256 entries. the API really doesn't
178                  * have a way to tell us ahead of time, so assume the worst. */
179                 assert(sizeof(struct emm_phys_page_map) == (size_t)4);
180                 emm_phys_map = malloc(sizeof(struct emm_phys_page_map) * 256);
181                 if (emm_phys_map != NULL) {
182                         const unsigned int s = FP_SEG(emm_phys_map);
183                         const unsigned int o = FP_OFF(emm_phys_map);
184                         unsigned int c = 0;
185                         __asm {
186                                 push    es
187                                 mov     ax,0x5800
188                                 mov     di,s
189                                 mov     es,di
190                                 mov     di,o
191                                 int     0x67
192                                 or      ah,ah
193                                 jnz     fail
194                                 mov     c,cx
195 fail:                           pop     es
196                         }
197
198                         if (c == 0) {
199                                 free(emm_phys_map);
200                                 emm_phys_map = NULL;
201                         }
202                         else {
203                                 emm_phys_pages = c;
204                                 if (c < 256) {
205                                         void *x = realloc(emm_phys_map,sizeof(struct emm_phys_page_map) * c);
206                                         if (x != NULL) { /* NTS: if we cannot realloc, well, too bad */
207                                                 emm_phys_map = x;
208                                         }
209                                 }
210
211                                 /* WARNING: we are assuming several things about the table.
212                                  *  - That the table is sorted by real-mode segment (as described in the standard)
213                                  *  - There are no duplicate page numbers
214                                  *  - The table has as many entries as physical pages */
215
216                                 /* do ourself a favor and sort by page number the table */
217                                 emm_phys_pages_sort();
218                         }
219                 }
220         }
221
222         return 1;
223 }
224
225 int emm_alloc_pages(unsigned int pages) {
226         int handle = -1;
227
228         if (emm_present) {
229                 __asm {
230                         mov     ah,0x43
231                         mov     bx,pages
232                         push    es
233                         int     0x67
234                         pop     es
235                         or      ah,ah
236                         jnz     fail
237                         mov     handle,dx
238 fail:
239                 }
240         }
241
242         return handle;
243 }
244
245 int emm_free_pages(unsigned int handle) {
246         int retv = 0;
247
248         if (emm_present) {
249                 __asm {
250                         mov     ah,0x45
251                         mov     dx,handle
252                         push    es
253                         int     0x67
254                         pop     es
255                         or      ah,ah
256                         jnz     fail
257                         mov     retv,1
258 fail:
259                 }
260         }
261
262         return retv;
263 }
264
265 int emm_map_page(unsigned int handle,unsigned int phys_page,unsigned int log_page) {
266         int retv = 0;
267
268         if (phys_page >= (unsigned int)emm_phys_pages)
269                 return 0;
270
271         if (emm_present) {
272                 __asm {
273                         mov     ah,0x44
274                         mov     al,byte ptr phys_page
275                         mov     bx,log_page
276                         mov     dx,handle
277                         push    es
278                         int     0x67
279                         pop     es
280                         or      ah,ah
281                         jnz     fail
282                         mov     retv,1
283 fail:
284                 }
285         }
286
287         return retv;
288 }
289
290 /* given physical page number, return real-mode segment value */
291 word emm_last_phys_page_segment(unsigned int phys_page) {
292         unsigned int i;
293
294         if (phys_page >= (unsigned int)emm_phys_pages)
295                 return 0;
296
297         /* if we don't have a copy of the EMM's mapping table, then assume that there is
298          * only physical page 0 at the page frame address */
299         if (phys_page == 0 && emm_phys_map == NULL)
300                 return emm_page_frame_segment;
301
302         for (i=0;i < emm_phys_pages && emm_phys_map != NULL;i++) {
303                 struct emm_phys_page_map *me = emm_phys_map + i;
304                 if (phys_page == me->number)
305                         return me->segment;
306         }
307
308         return 0;
309 }
310 #else
311 void emm_update_page_count() {
312         emm_unallocated_pages = 0;
313         emm_total_pages = 0;
314
315         if (!emm_present) return;
316
317         __asm {
318                 mov     ah,0x42
319                 push    es
320                 int     0x67
321                 pop     es
322                 mov     emm_unallocated_pages,bx
323                 mov     emm_total_pages,dx
324         }
325 }
326
327 /*int probe_emm() {//32-bit
328         unsigned int emm_seg;
329
330         sanity();
331         emm_present = 0;
332         // Tricky. The DOS extender would likely translate the vector, when what we
333            really want is the segment value of int 67h
334         emm_seg = *((uint16_t*)((0x67 << 2) + 2));
335         sanity();
336
337         // apparently 10 bytes into the segment there is the magic string
338         if (    memcmp((void*)(((unsigned long)emm_seg << 4UL) + 0x000A),devname,8) != 0 &&
339                 memcmp((void*)(((unsigned long)emm_seg << 4UL) + 0x000A),devname2,8) != 0)
340                 return 0;
341
342         sanity();
343         emm_present = 1;
344         emm_phys_pages = 1;
345         emm_page_frame_segment = 0;
346
347         __asm {
348                 mov     ah,0x40
349                 push    es
350                 int     0x67
351                 pop     es
352                 mov     emm_status,ah
353
354                 mov     ah,0x41
355                 push    es
356                 int     0x67
357                 pop     es
358                 or      ah,ah
359                 jnz     pfn_end
360                 mov     word ptr emm_page_frame_segment,bx
361
362                 mov     ah,0x46
363                 push    es
364                 int     0x67
365                 pop     es
366                 mov     emm_version,al
367 pfn_end:
368         }
369         sanity();
370
371         if (emm_phys_map != NULL) {
372                 dpmi_free_dos(emm_phys_map_sel);
373                 emm_phys_map_sel = 0;
374                 emm_phys_map = NULL;
375         }
376
377         if (emm_phys_map == NULL) {
378                 // see if the EMM provides a mapping table describing the real-mode segments
379                 // * corresponding to each physical page. if not, then assume only one page
380                 // * available. the table could be up to 256 entries. the API really doesn't
381                 // * have a way to tell us ahead of time, so assume the worst.
382                 assert(sizeof(struct emm_phys_page_map) == (size_t)4);
383                 emm_phys_map = dpmi_alloc_dos(sizeof(struct emm_phys_page_map) * 256,&emm_phys_map_sel);
384                 if (emm_phys_map != NULL) {
385                         const unsigned int s = ((uint32_t)emm_phys_map) >> 4;
386                         const unsigned int o = ((uint32_t)emm_phys_map) & 0xF;
387                         struct dpmi_realmode_call rc={0};
388                         unsigned int c = 0;
389
390                         rc.eax = 0x5800;
391                         rc.edi = o;
392                         rc.es = s;
393                         rc.ds = s;
394                         emm_realmode_67_call(&rc);
395                         if ((rc.eax&0xFF) == 0) c = rc.ecx & 0xFFFF;
396
397                         if (c == 0) {
398                                 dpmi_free_dos(emm_phys_map_sel);
399                                 emm_phys_map_sel = 0;
400                                 emm_phys_map = NULL;
401                         }
402                         else {
403                                 emm_phys_pages = c;
404
405                                 // * WARNING: we are assuming several things about the table.
406                                 // *  - That the table is sorted by real-mode segment (as described in the standard)
407                                 // *  - There are no duplicate page numbers
408                                 // *  - The table has as many entries as physical pages
409
410                                 // do ourself a favor and sort by page number the table
411                                 emm_phys_pages_sort();
412                         }
413                 }
414         }
415
416         return 1;
417 }*/
418
419 int emm_alloc_pages(unsigned int pages) {
420         int handle = -1;
421
422         if (emm_present) {
423                 __asm {
424                         mov     ah,0x43
425                         mov     bx,pages
426                         push    es
427                         int     0x67
428                         pop     es
429                         or      ah,ah
430                         jnz     fail
431                         and     dx,0xFFFF
432                         mov     handle,dx
433 fail:
434                 }
435         }
436
437         return handle;
438 }
439
440 int emm_free_pages(unsigned int handle) {
441         int retv = 0;
442
443         if (emm_present) {
444                 __asm {
445                         mov     ah,0x45
446                         mov     dx,handle
447                         push    es
448                         int     0x67
449                         pop     es
450                         or      ah,ah
451                         jnz     fail
452                         mov     retv,1
453 fail:
454                 }
455         }
456
457         return retv;
458 }
459
460 int emm_map_page(unsigned int handle,unsigned int phys_page,unsigned int log_page) {
461         int retv = 0;
462
463         if (phys_page >= (unsigned int)emm_phys_pages)
464                 return 0;
465
466         if (emm_present) {
467                 __asm {
468                         mov     ah,0x44
469                         mov     al,byte ptr phys_page
470                         mov     bx,log_page
471                         mov     dx,handle
472                         push    es
473                         int     0x67
474                         pop     es
475                         or      ah,ah
476                         jnz     fail
477                         mov     retv,1
478 fail:
479                 }
480         }
481
482         return retv;
483 }
484
485 word emm_last_phys_page_segment(unsigned int phys_page) {
486         unsigned int i;
487
488         if (phys_page >= (unsigned int)emm_phys_pages)
489                 return 0;
490
491         /* if we don't have a copy of the EMM's mapping table, then assume that there is
492          * only physical page 0 at the page frame address */
493         if (phys_page == 0 && emm_phys_map == NULL)
494                 return emm_page_frame_segment;
495
496         for (i=0;i < emm_phys_pages && emm_phys_map != NULL;i++) {
497                 struct emm_phys_page_map *me = emm_phys_map + i;
498                 if (phys_page == me->number)
499                         return me->segment;
500         }
501
502         return 0;
503 }
504 #endif
505
506 #endif /* !defined(TARGET_OS2) && !defined(TARGET_WINDOWS) */
507