]> 4ch.mooo.com Git - 16.git/blob - src/lib/doslib/dos/emm.c
added a bunch of things~ and midi stuff~
[16.git] / src / lib / doslib / dos / 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 <stdlib.h>
83 #include <string.h>
84 #include <stdint.h>
85 #include <assert.h>
86 #include <stdio.h>
87 #include <conio.h>
88 #include <dos.h>
89
90 #include <hw/cpu/cpu.h>
91 #include <hw/dos/dos.h>
92 #include <hw/dos/emm.h>
93
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;
106
107 static void emm_realmode_67_call(struct dpmi_realmode_call *rc) {
108         __asm {
109                 mov     ax,0x0300
110                 mov     bx,0x0067
111                 xor     cx,cx
112                 mov     edi,rc          ; we trust Watcom has left ES == DS
113                 int     0x31            ; call DPMI
114         }
115 }
116 #endif
117
118 void emm_phys_pages_sort() {
119         /* TODO */
120 }
121
122 #if TARGET_MSDOS == 16 && !defined(TARGET_OS2)
123 void emm_update_page_count() {
124         emm_unallocated_pages = 0;
125         emm_total_pages = 0;
126
127         if (!emm_present) return;
128
129         __asm {
130                 mov     ah,0x42
131                 push    es
132                 int     0x67
133                 pop     es
134                 mov     emm_unallocated_pages,bx
135                 mov     emm_total_pages,dx
136         }
137 }
138
139 int probe_emm() {
140         void far *emmptr;
141
142         emm_present = 0;
143         emmptr = (void far*)_dos_getvect(0x67);
144         if (emmptr == (void far*)0)
145                 return 0;
146
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)
150                 return 0;
151
152         emm_present = 1;
153         emm_phys_pages = 1;
154         emm_page_frame_segment = 0;
155
156         __asm {
157                 mov     ah,0x40
158                 push    es
159                 int     0x67
160                 pop     es
161                 mov     emm_status,ah
162
163                 mov     ah,0x41
164                 push    es
165                 int     0x67
166                 pop     es
167                 or      ah,ah
168                 jnz     pfn_end
169                 mov     emm_page_frame_segment,bx
170
171                 mov     ah,0x46
172                 push    es
173                 int     0x67
174                 pop     es
175                 mov     emm_version,al
176 pfn_end:
177         }
178
179         if (emm_phys_map != NULL) {
180                 free(emm_phys_map);
181                 emm_phys_map = NULL;
182         }
183
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);
194                         unsigned int c = 0;
195                         __asm {
196                                 push    es
197                                 mov     ax,0x5800
198                                 mov     di,s
199                                 mov     es,di
200                                 mov     di,o
201                                 int     0x67
202                                 or      ah,ah
203                                 jnz     fail
204                                 mov     c,cx
205 fail:                           pop     es
206                         }
207
208                         if (c == 0) {
209                                 free(emm_phys_map);
210                                 emm_phys_map = NULL;
211                         }
212                         else {
213                                 emm_phys_pages = c;
214                                 if (c < 256) {
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 */
217                                                 emm_phys_map = x;
218                                         }
219                                 }
220
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 */
225
226                                 /* do ourself a favor and sort by page number the table */
227                                 emm_phys_pages_sort();
228                         }
229                 }
230         }
231
232         return 1;
233 }
234
235 int emm_alloc_pages(unsigned int pages) {
236         int handle = -1;
237
238         if (emm_present) {
239                 __asm {
240                         mov     ah,0x43
241                         mov     bx,pages
242                         push    es
243                         int     0x67
244                         pop     es
245                         or      ah,ah
246                         jnz     fail
247                         mov     handle,dx
248 fail:
249                 }
250         }
251
252         return handle;
253 }
254
255 int emm_free_pages(unsigned int handle) {
256         int retv = 0;
257
258         if (emm_present) {
259                 __asm {
260                         mov     ah,0x45
261                         mov     dx,handle
262                         push    es
263                         int     0x67
264                         pop     es
265                         or      ah,ah
266                         jnz     fail
267                         mov     retv,1
268 fail:
269                 }
270         }
271
272         return retv;
273 }
274
275 int emm_map_page(unsigned int handle,unsigned int phys_page,unsigned int log_page) {
276         int retv = 0;
277
278         if (phys_page >= (unsigned int)emm_phys_pages)
279                 return 0;
280
281         if (emm_present) {
282                 __asm {
283                         mov     ah,0x44
284                         mov     al,byte ptr phys_page
285                         mov     bx,log_page
286                         mov     dx,handle
287                         push    es
288                         int     0x67
289                         pop     es
290                         or      ah,ah
291                         jnz     fail
292                         mov     retv,1
293 fail:
294                 }
295         }
296
297         return retv;
298 }
299
300 /* given physical page number, return real-mode segment value */
301 unsigned short emm_last_phys_page_segment(unsigned int phys_page) {
302         unsigned int i;
303
304         if (phys_page >= (unsigned int)emm_phys_pages)
305                 return 0;
306
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;
311
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)
315                         return me->segment;
316         }
317
318         return 0;
319 }
320 #else
321 void emm_update_page_count() {
322         emm_unallocated_pages = 0;
323         emm_total_pages = 0;
324
325         if (!emm_present) return;
326
327         __asm {
328                 mov     ah,0x42
329                 push    es
330                 int     0x67
331                 pop     es
332                 mov     emm_unallocated_pages,bx
333                 mov     emm_total_pages,dx
334         }
335 }
336
337 int probe_emm() {/*32-bit*/
338         unsigned int emm_seg;
339
340         sanity();
341         emm_present = 0;
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));
345         sanity();
346
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)
350                 return 0;
351
352         sanity();
353         emm_present = 1;
354         emm_phys_pages = 1;
355         emm_page_frame_segment = 0;
356
357         __asm {
358                 mov     ah,0x40
359                 push    es
360                 int     0x67
361                 pop     es
362                 mov     emm_status,ah
363
364                 mov     ah,0x41
365                 push    es
366                 int     0x67
367                 pop     es
368                 or      ah,ah
369                 jnz     pfn_end
370                 mov     word ptr emm_page_frame_segment,bx
371
372                 mov     ah,0x46
373                 push    es
374                 int     0x67
375                 pop     es
376                 mov     emm_version,al
377 pfn_end:
378         }
379         sanity();
380
381         if (emm_phys_map != NULL) {
382                 dpmi_free_dos(emm_phys_map_sel);
383                 emm_phys_map_sel = 0;
384                 emm_phys_map = NULL;
385         }
386
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};
398                         unsigned int c = 0;
399
400                         rc.eax = 0x5800;
401                         rc.edi = o;
402                         rc.es = s;
403                         rc.ds = s;
404                         emm_realmode_67_call(&rc);
405                         if ((rc.eax&0xFF) == 0) c = rc.ecx & 0xFFFF;
406
407                         if (c == 0) {
408                                 dpmi_free_dos(emm_phys_map_sel);
409                                 emm_phys_map_sel = 0;
410                                 emm_phys_map = NULL;
411                         }
412                         else {
413                                 emm_phys_pages = c;
414
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 */
419
420                                 /* do ourself a favor and sort by page number the table */
421                                 emm_phys_pages_sort();
422                         }
423                 }
424         }
425
426         return 1;
427 }
428
429 int emm_alloc_pages(unsigned int pages) {
430         int handle = -1;
431
432         if (emm_present) {
433                 __asm {
434                         mov     ah,0x43
435                         mov     ebx,pages
436                         push    es
437                         int     0x67
438                         pop     es
439                         or      ah,ah
440                         jnz     fail
441                         and     edx,0xFFFF
442                         mov     handle,edx
443 fail:
444                 }
445         }
446
447         return handle;
448 }
449
450 int emm_free_pages(unsigned int handle) {
451         int retv = 0;
452
453         if (emm_present) {
454                 __asm {
455                         mov     ah,0x45
456                         mov     edx,handle
457                         push    es
458                         int     0x67
459                         pop     es
460                         or      ah,ah
461                         jnz     fail
462                         mov     retv,1
463 fail:
464                 }
465         }
466
467         return retv;
468 }
469
470 int emm_map_page(unsigned int handle,unsigned int phys_page,unsigned int log_page) {
471         int retv = 0;
472
473         if (phys_page >= (unsigned int)emm_phys_pages)
474                 return 0;
475
476         if (emm_present) {
477                 __asm {
478                         mov     ah,0x44
479                         mov     al,byte ptr phys_page
480                         mov     ebx,log_page
481                         mov     edx,handle
482                         push    es
483                         int     0x67
484                         pop     es
485                         or      ah,ah
486                         jnz     fail
487                         mov     retv,1
488 fail:
489                 }
490         }
491
492         return retv;
493 }
494
495 unsigned short emm_last_phys_page_segment(unsigned int phys_page) {
496         unsigned int i;
497
498         if (phys_page >= (unsigned int)emm_phys_pages)
499                 return 0;
500
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;
505
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)
509                         return me->segment;
510         }
511
512         return 0;
513 }
514 #endif
515
516 #endif /* !defined(TARGET_OS2) && !defined(TARGET_WINDOWS) */
517