]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - board/MAI/bios_emulator/scitech/src/pm/dos/pm.c
* Patch by Thomas Frieden, 13 Nov 2002:
[karo-tx-uboot.git] / board / MAI / bios_emulator / scitech / src / pm / dos / pm.c
1 /****************************************************************************
2 *
3 *                   SciTech OS Portability Manager Library
4 *
5 *  ========================================================================
6 *
7 *    The contents of this file are subject to the SciTech MGL Public
8 *    License Version 1.0 (the "License"); you may not use this file
9 *    except in compliance with the License. You may obtain a copy of
10 *    the License at http://www.scitechsoft.com/mgl-license.txt
11 *
12 *    Software distributed under the License is distributed on an
13 *    "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
14 *    implied. See the License for the specific language governing
15 *    rights and limitations under the License.
16 *
17 *    The Original Code is Copyright (C) 1991-1998 SciTech Software, Inc.
18 *
19 *    The Initial Developer of the Original Code is SciTech Software, Inc.
20 *    All Rights Reserved.
21 *
22 *  ========================================================================
23 *
24 * Language:     ANSI C
25 * Environment:  16/32 bit DOS
26 *
27 * Description:  Implementation for the OS Portability Manager Library, which
28 *               contains functions to implement OS specific services in a
29 *               generic, cross platform API. Porting the OS Portability
30 *               Manager library is the first step to porting any SciTech
31 *               products to a new platform.
32 *
33 ****************************************************************************/
34
35 #include "pmapi.h"
36 #include "drvlib/os/os.h"
37 #include "ztimerc.h"
38 #include "mtrr.h"
39 #include "pm_help.h"
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <dos.h>
44 #include <conio.h>
45 #ifdef  __GNUC__
46 #include <unistd.h>
47 #include <sys/nearptr.h>
48 #include <sys/stat.h>
49 #else
50 #include <direct.h>
51 #endif
52 #ifdef  __BORLANDC__
53 #pragma warn -par
54 #endif
55
56 /*--------------------------- Global variables ----------------------------*/
57
58 typedef struct {
59     int     oldMode;
60     int     old50Lines;
61     } DOS_stateBuf;
62
63 #define MAX_RM_BLOCKS   10
64
65 static struct {
66     void    *p;
67     uint    tag;
68     } rmBlocks[MAX_RM_BLOCKS];
69
70 static uint     VESABuf_len = 1024;     /* Length of the VESABuf buffer     */
71 static void     *VESABuf_ptr = NULL;    /* Near pointer to VESABuf          */
72 static uint     VESABuf_rseg;           /* Real mode segment of VESABuf     */
73 static uint     VESABuf_roff;           /* Real mode offset of VESABuf      */
74 static void     (PMAPIP fatalErrorCleanup)(void) = NULL;
75 ushort _VARAPI  _PM_savedDS = 0;
76 #ifdef  DOS4GW
77 static ulong    PDB = 0,*pPDB = NULL;
78 #endif
79 #ifndef REALMODE
80 static char     VXD_name[] = PMHELP_NAME;
81 static char     VXD_module[] = PMHELP_MODULE;
82 static char     VXD_DDBName[] = PMHELP_DDBNAME;
83 static uint     VXD_version = -1;
84 static uint     VXD_loadOff = 0;
85 static uint     VXD_loadSel = 0;
86 uint _VARAPI    _PM_VXD_off = 0;
87 uint _VARAPI    _PM_VXD_sel = 0;
88 int _VARAPI     _PM_haveCauseWay = -1;
89
90 /* Memory mapping cache */
91
92 #define MAX_MEMORY_MAPPINGS 100
93 typedef struct {
94     ulong   physical;
95     ulong   linear;
96     ulong   limit;
97     } mmapping;
98 static mmapping     maps[MAX_MEMORY_MAPPINGS] = {0};
99 static int          numMaps = 0;
100
101 /* Page sized block cache */
102
103 #define PAGES_PER_BLOCK     100
104 #define FREELIST_NEXT(p)    (*(void**)(p))
105 typedef struct pageblock {
106     struct pageblock    *next;
107     struct pageblock    *prev;
108     void                *freeListStart;
109     void                *freeList;
110     void                *freeListEnd;
111     int                 freeCount;
112     } pageblock;
113 static pageblock    *pageBlocks = NULL;
114 #endif
115
116 /* Start of all page tables in CauseWay */
117
118 #define CW_PAGE_TABLE_START (1024UL*4096UL*1023UL)
119
120 /*----------------------------- Implementation ----------------------------*/
121
122 /* External assembler functions */
123
124 ulong   _ASMAPI _PM_getPDB(void);
125 int     _ASMAPI _PM_pagingEnabled(void);
126 void    _ASMAPI _PM_VxDCall(VXD_regs *regs,uint off,uint sel);
127
128 #ifndef REALMODE
129 /****************************************************************************
130 REMARKS:
131 Exit function to unload the dynamically loaded VxD
132 ****************************************************************************/
133 static void UnloadVxD(void)
134 {
135     PMSREGS     sregs;
136     VXD_regs    r;
137
138     r.eax = 2;
139     r.ebx = 0;
140     r.edx = (uint)VXD_module;
141     PM_segread(&sregs);
142 #ifdef  __16BIT__
143     r.ds = ((ulong)VXD_module) >> 16;
144 #else
145     r.ds = sregs.ds;
146 #endif
147     r.es = sregs.es;
148     _PM_VxDCall(&r,VXD_loadOff,VXD_loadSel);
149 }
150
151 /****************************************************************************
152 REMARKS:
153 External function to call the PMHELP helper VxD.
154 ****************************************************************************/
155 void PMAPI PM_VxDCall(
156     VXD_regs *regs)
157 {
158     if (_PM_VXD_sel != 0 || _PM_VXD_off != 0)
159         _PM_VxDCall(regs,_PM_VXD_off,_PM_VXD_sel);
160 }
161
162 /****************************************************************************
163 RETURNS:
164 BCD coded version number of the VxD, or 0 if not loaded (ie: 0x202 - 2.2)
165
166 REMARKS:
167 This function gets the version number for the VxD that we have connected to.
168 ****************************************************************************/
169 uint PMAPI PMHELP_getVersion(void)
170 {
171     VXD_regs    r;
172
173     /* Call the helper VxD to determine the version number */
174     if (_PM_VXD_sel != 0 || _PM_VXD_off != 0) {
175         memset(&r,0,sizeof(r));
176         r.eax = API_NUM(PMHELP_GETVER);
177         _PM_VxDCall(&r,_PM_VXD_off,_PM_VXD_sel);
178         return VXD_version = (uint)r.eax;
179         }
180     return VXD_version = 0;
181 }
182
183 /****************************************************************************
184 DESCRIPTION:
185 Connects to the helper VxD and returns the version number
186
187 RETURNS:
188 True if the VxD was found and loaded, false otherwise.
189
190 REMARKS:
191 This function connects to the VxD (loading it if it is dynamically loadable)
192 and returns the version number of the VxD.
193 ****************************************************************************/
194 static ibool PMHELP_connect(void)
195 {
196     PMREGS      regs;
197     PMSREGS     sregs;
198     VXD_regs    r;
199
200     /* Bail early if we have alread connected */
201     if (VXD_version != -1)
202         return VXD_version != 0;
203
204     /* Get the static SDDHELP.VXD entry point if available */
205     PM_segread(&sregs);
206     regs.x.ax = 0x1684;
207     regs.x.bx = SDDHELP_DeviceID;
208     regs.x.di = 0;
209     sregs.es = 0;
210     PM_int386x(0x2F,&regs,&regs,&sregs);
211     _PM_VXD_sel = sregs.es;
212     _PM_VXD_off = regs.x.di;
213     if (_PM_VXD_sel != 0 || _PM_VXD_off != 0) {
214         if (PMHELP_getVersion() >= PMHELP_VERSION)
215             return true;
216         }
217
218     /* If we get here, then either SDDHELP.VXD is not loaded, or it is an
219      * earlier version. In this case try to dynamically load the PMHELP.VXD
220      * helper VxD instead.
221      */
222     PM_segread(&sregs);
223     regs.x.ax = 0x1684;
224     regs.x.bx = VXDLDR_DeviceID;
225     regs.x.di = 0;
226     sregs.es = 0;
227     PM_int386x(0x2F,&regs,&regs,&sregs);
228     VXD_loadSel = sregs.es;
229     VXD_loadOff = regs.x.di;
230     if (VXD_loadSel == 0 && VXD_loadOff == 0)
231         return VXD_version = 0;
232     r.eax = 1;
233     r.ebx = 0;
234     r.edx = (uint)VXD_name;
235     PM_segread(&sregs);
236     r.ds = sregs.ds;
237     r.es = sregs.es;
238     _PM_VxDCall(&r,VXD_loadOff,VXD_loadSel);
239     if (r.eax != 0)
240         return VXD_version = 0;
241
242     /* Get the dynamic VxD entry point so we can call it */
243     atexit(UnloadVxD);
244     PM_segread(&sregs);
245     regs.x.ax = 0x1684;
246     regs.x.bx = 0;
247     regs.e.edi = (uint)VXD_DDBName;
248     PM_int386x(0x2F,&regs,&regs,&sregs);
249     _PM_VXD_sel = sregs.es;
250     _PM_VXD_off = regs.x.di;
251     if (_PM_VXD_sel == 0 && _PM_VXD_off == 0)
252         return VXD_version = 0;
253     if (PMHELP_getVersion() >= PMHELP_VERSION)
254         return true;
255     return VXD_version = 0;
256 }
257 #endif
258
259 /****************************************************************************
260 REMARKS:
261 Initialise the PM library. First we try to connect to a static SDDHELP.VXD
262 helper VxD, and check that it is a version we can use. If not we try to
263 dynamically load the PMHELP.VXD helper VxD
264 ****************************************************************************/
265 void PMAPI PM_init(void)
266 {
267 #ifndef REALMODE
268     PMREGS  regs;
269
270     /* Check if we are running under CauseWay under real DOS */
271     if (_PM_haveCauseWay == -1) {
272         /* Check if we are running under DPMI in which case we will not be
273          * able to use our special ring 0 CauseWay functions.
274          */
275         _PM_haveCauseWay = false;
276         regs.x.ax = 0xFF00;
277         PM_int386(0x31,&regs,&regs);
278         if (regs.x.cflag || !(regs.e.edi & 8)) {
279             /* We are not under DPMI, so now check if CauseWay is active */
280             regs.x.ax = 0xFFF9;
281             PM_int386(0x31,&regs,&regs);
282             if (!regs.x.cflag && regs.e.ecx == 0x43415553 && regs.e.edx == 0x45574159)
283                 _PM_haveCauseWay = true;
284             }
285
286         /* Now connect to PMHELP.VXD and initialise MTRR module */
287         if (!PMHELP_connect())
288             MTRR_init();
289         }
290 #endif
291 }
292
293 /****************************************************************************
294 PARAMETERS:
295 base    - The starting physical base address of the region
296 size    - The size in bytes of the region
297 type    - Type to place into the MTRR register
298
299 RETURNS:
300 Error code describing the result.
301
302 REMARKS:
303 Function to enable write combining for the specified region of memory.
304 ****************************************************************************/
305 int PMAPI PM_enableWriteCombine(
306     ulong base,
307     ulong size,
308     uint type)
309 {
310 #ifndef REALMODE
311     VXD_regs    regs;
312
313     if (PMHELP_connect()) {
314         memset(&regs,0,sizeof(regs));
315         regs.eax = API_NUM(PMHELP_ENABLELFBCOMB);
316         regs.ebx = base;
317         regs.ecx = size;
318         regs.edx = type;
319         _PM_VxDCall(&regs,_PM_VXD_off,_PM_VXD_sel);
320         return regs.eax;
321         }
322     return MTRR_enableWriteCombine(base,size,type);
323 #else
324     return PM_MTRR_NOT_SUPPORTED;
325 #endif
326 }
327
328 ibool PMAPI PM_haveBIOSAccess(void)
329 { return true; }
330
331 long PMAPI PM_getOSType(void)
332 { return _OS_DOS; }
333
334 int PMAPI PM_getModeType(void)
335 {
336 #if defined(REALMODE)
337     return PM_realMode;
338 #elif defined(PM286)
339     return PM_286;
340 #elif defined(PM386)
341     return PM_386;
342 #endif
343 }
344
345 void PMAPI PM_backslash(char *s)
346 {
347     uint pos = strlen(s);
348     if (s[pos-1] != '\\') {
349         s[pos] = '\\';
350         s[pos+1] = '\0';
351         }
352 }
353
354 void PMAPI PM_setFatalErrorCleanup(
355     void (PMAPIP cleanup)(void))
356 {
357     fatalErrorCleanup = cleanup;
358 }
359
360 void PMAPI PM_fatalError(const char *msg)
361 {
362     if (fatalErrorCleanup)
363         fatalErrorCleanup();
364     fprintf(stderr,"%s\n", msg);
365     exit(1);
366 }
367
368 static void ExitVBEBuf(void)
369 {
370     if (VESABuf_ptr)
371         PM_freeRealSeg(VESABuf_ptr);
372     VESABuf_ptr = 0;
373 }
374
375 void * PMAPI PM_getVESABuf(uint *len,uint *rseg,uint *roff)
376 {
377     if (!VESABuf_ptr) {
378         /* Allocate a global buffer for communicating with the VESA VBE */
379         if ((VESABuf_ptr = PM_allocRealSeg(VESABuf_len, &VESABuf_rseg, &VESABuf_roff)) == NULL)
380             return NULL;
381         atexit(ExitVBEBuf);
382         }
383     *len = VESABuf_len;
384     *rseg = VESABuf_rseg;
385     *roff = VESABuf_roff;
386     return VESABuf_ptr;
387 }
388
389 int PMAPI PM_int386(int intno, PMREGS *in, PMREGS *out)
390 {
391     PMSREGS sregs;
392     PM_segread(&sregs);
393     return PM_int386x(intno,in,out,&sregs);
394 }
395
396 /* Routines to set and get the real mode interrupt vectors, by making
397  * direct real mode calls to DOS and bypassing the DOS extenders API.
398  * This is the safest way to handle this, as some servers try to be
399  * smart about changing real mode vectors.
400  */
401
402 void PMAPI _PM_getRMvect(int intno, long *realisr)
403 {
404     RMREGS  regs;
405     RMSREGS sregs;
406
407     PM_saveDS();
408     regs.h.ah = 0x35;
409     regs.h.al = intno;
410     PM_int86x(0x21, &regs, &regs, &sregs);
411     *realisr = ((long)sregs.es << 16) | regs.x.bx;
412 }
413
414 void PMAPI _PM_setRMvect(int intno, long realisr)
415 {
416     RMREGS  regs;
417     RMSREGS sregs;
418
419     PM_saveDS();
420     regs.h.ah = 0x25;
421     regs.h.al = intno;
422     sregs.ds = (int)(realisr >> 16);
423     regs.x.dx = (int)(realisr & 0xFFFF);
424     PM_int86x(0x21, &regs, &regs, &sregs);
425 }
426
427 void PMAPI _PM_addRealModeBlock(void *mem,uint tag)
428 {
429     int i;
430
431     for (i = 0; i < MAX_RM_BLOCKS; i++) {
432         if (rmBlocks[i].p == NULL) {
433             rmBlocks[i].p = mem;
434             rmBlocks[i].tag = tag;
435             return;
436             }
437         }
438     PM_fatalError("To many real mode memory block allocations!");
439 }
440
441 uint PMAPI _PM_findRealModeBlock(void *mem)
442 {
443     int i;
444
445     for (i = 0; i < MAX_RM_BLOCKS; i++) {
446         if (rmBlocks[i].p == mem)
447             return rmBlocks[i].tag;
448         }
449     PM_fatalError("Could not find prior real mode memory block allocation!");
450     return 0;
451 }
452
453 char * PMAPI PM_getCurrentPath(
454     char *path,
455     int maxLen)
456 {
457     return getcwd(path,maxLen);
458 }
459
460 char PMAPI PM_getBootDrive(void)
461 { return 'C'; }
462
463 const char * PMAPI PM_getVBEAFPath(void)
464 { return "c:\\"; }
465
466 const char * PMAPI PM_getNucleusPath(void)
467 {
468     static char path[256];
469     char        *env;
470
471     if ((env = getenv("NUCLEUS_PATH")) != NULL)
472         return env;
473     if ((env = getenv("WINBOOTDIR")) != NULL) {
474         /* Running in a Windows 9x DOS box or DOS mode */
475         strcpy(path,env);
476         strcat(path,"\\system\\nucleus");
477         return path;
478         }
479     if ((env = getenv("SystemRoot")) != NULL) {
480         /* Running in an NT/2K DOS box */
481         strcpy(path,env);
482         strcat(path,"\\system32\\nucleus");
483         return path;
484         }
485     return "c:\\nucleus";
486 }
487
488 const char * PMAPI PM_getNucleusConfigPath(void)
489 {
490     static char path[256];
491     strcpy(path,PM_getNucleusPath());
492     PM_backslash(path);
493     strcat(path,"config");
494     return path;
495 }
496
497 const char * PMAPI PM_getUniqueID(void)
498 { return "DOS"; }
499
500 const char * PMAPI PM_getMachineName(void)
501 { return "DOS"; }
502
503 int PMAPI PM_kbhit(void)
504 {
505     return kbhit();
506 }
507
508 int PMAPI PM_getch(void)
509 {
510     return getch();
511 }
512
513 PM_HWND PMAPI PM_openConsole(PM_HWND hwndUser,int device,int xRes,int yRes,int bpp,ibool fullScreen)
514 {
515     /* Not used for DOS */
516     (void)hwndUser;
517     (void)device;
518     (void)xRes;
519     (void)yRes;
520     (void)bpp;
521     (void)fullScreen;
522     return 0;
523 }
524
525 int PMAPI PM_getConsoleStateSize(void)
526 {
527     return sizeof(DOS_stateBuf);
528 }
529
530 void PMAPI PM_saveConsoleState(void *stateBuf,PM_HWND hwndConsole)
531 {
532     RMREGS          regs;
533     DOS_stateBuf    *sb = stateBuf;
534
535     /* Save the old video mode state */
536     regs.h.ah = 0x0F;
537     PM_int86(0x10,&regs,&regs);
538     sb->oldMode = regs.h.al & 0x7F;
539     sb->old50Lines = false;
540     if (sb->oldMode == 0x3) {
541         regs.x.ax = 0x1130;
542         regs.x.bx = 0;
543         regs.x.dx = 0;
544         PM_int86(0x10,&regs,&regs);
545         sb->old50Lines = (regs.h.dl == 42 || regs.h.dl == 49);
546         }
547     (void)hwndConsole;
548 }
549
550 void PMAPI PM_setSuspendAppCallback(int (_ASMAPIP saveState)(int flags))
551 {
552     /* Not used for DOS */
553     (void)saveState;
554 }
555
556 void PMAPI PM_restoreConsoleState(const void *stateBuf,PM_HWND hwndConsole)
557 {
558     RMREGS              regs;
559     const DOS_stateBuf  *sb = stateBuf;
560
561     /* Retore 50 line mode if set */
562     if (sb->old50Lines) {
563         regs.x.ax = 0x1112;
564         regs.x.bx = 0;
565         PM_int86(0x10,&regs,&regs);
566         }
567     (void)hwndConsole;
568 }
569
570 void PMAPI PM_closeConsole(PM_HWND hwndConsole)
571 {
572     /* Not used for DOS */
573     (void)hwndConsole;
574 }
575
576 void PMAPI PM_setOSCursorLocation(int x,int y)
577 {
578     uchar *_biosPtr = PM_getBIOSPointer();
579     PM_setByte(_biosPtr+0x50,x);
580     PM_setByte(_biosPtr+0x51,y);
581 }
582
583 void PMAPI PM_setOSScreenWidth(int width,int height)
584 {
585     uchar *_biosPtr = PM_getBIOSPointer();
586     PM_setWord(_biosPtr+0x4A,width);
587     PM_setWord(_biosPtr+0x4C,width*2);
588     PM_setByte(_biosPtr+0x84,height-1);
589     if (height > 25) {
590         PM_setWord(_biosPtr+0x60,0x0607);
591         PM_setByte(_biosPtr+0x85,0x08);
592         }
593     else {
594         PM_setWord(_biosPtr+0x60,0x0D0E);
595         PM_setByte(_biosPtr+0x85,0x016);
596         }
597 }
598
599 void * PMAPI PM_mallocShared(long size)
600 {
601     return PM_malloc(size);
602 }
603
604 void PMAPI PM_freeShared(void *ptr)
605 {
606     PM_free(ptr);
607 }
608
609 #define GetRMVect(intno,isr)    *(isr) = ((ulong*)rmZeroPtr)[intno]
610 #define SetRMVect(intno,isr)    ((ulong*)rmZeroPtr)[intno] = (isr)
611
612 ibool PMAPI PM_doBIOSPOST(
613     ushort axVal,
614     ulong BIOSPhysAddr,
615     void *mappedBIOS,
616     ulong BIOSLen)
617 {
618     static int      firstTime = true;
619     static uchar    *rmZeroPtr;
620     long            Current10,Current6D,Current42;
621     RMREGS          regs;
622     RMSREGS         sregs;
623
624     /* Create a zero memory mapping for us to use */
625     if (firstTime) {
626         rmZeroPtr = PM_mapPhysicalAddr(0,0x7FFF,true);
627         firstTime = false;
628         }
629
630     /* Remap the secondary BIOS to 0xC0000 physical */
631     if (BIOSPhysAddr != 0xC0000L || BIOSLen > 32768) {
632         /* DOS cannot virtually remap the BIOS, so we can only work if all
633          * the secondary controllers are identical, and we then use the
634          * BIOS on the first controller for all the remaining controllers.
635          *
636          * For OS'es that do virtual memory, and remapping of 0xC0000
637          * physical (perhaps a copy on write mapping) should be all that
638          * is needed.
639          */
640         return false;
641         }
642
643     /* Save current handlers of int 10h and 6Dh */
644     GetRMVect(0x10,&Current10);
645     GetRMVect(0x6D,&Current6D);
646
647     /* POST the secondary BIOS */
648     GetRMVect(0x42,&Current42);
649     SetRMVect(0x10,Current42);  /* Restore int 10h to STD-BIOS */
650     regs.x.ax = axVal;
651     PM_callRealMode(0xC000,0x0003,&regs,&sregs);
652
653     /* Restore current handlers */
654     SetRMVect(0x10,Current10);
655     SetRMVect(0x6D,Current6D);
656
657     /* Second the primary BIOS mappin 1:1 for 0xC0000 physical */
658     if (BIOSPhysAddr != 0xC0000L) {
659         /* DOS does not support this */
660         (void)mappedBIOS;
661         }
662     return true;
663 }
664
665 void PMAPI PM_sleep(ulong milliseconds)
666 {
667     ulong           microseconds = milliseconds * 1000L;
668     LZTimerObject   tm;
669
670     LZTimerOnExt(&tm);
671     while (LZTimerLapExt(&tm) < microseconds)
672         ;
673     LZTimerOffExt(&tm);
674 }
675
676 int PMAPI PM_getCOMPort(int port)
677 {
678     switch (port) {
679         case 0: return 0x3F8;
680         case 1: return 0x2F8;
681         }
682     return 0;
683 }
684
685 int PMAPI PM_getLPTPort(int port)
686 {
687     switch (port) {
688         case 0: return 0x3BC;
689         case 1: return 0x378;
690         case 2: return 0x278;
691         }
692     return 0;
693 }
694
695 PM_MODULE PMAPI PM_loadLibrary(
696     const char *szDLLName)
697 {
698     (void)szDLLName;
699     return NULL;
700 }
701
702 void * PMAPI PM_getProcAddress(
703     PM_MODULE hModule,
704     const char *szProcName)
705 {
706     (void)hModule;
707     (void)szProcName;
708     return NULL;
709 }
710
711 void PMAPI PM_freeLibrary(
712     PM_MODULE hModule)
713 {
714     (void)hModule;
715 }
716
717 int PMAPI PM_setIOPL(
718     int level)
719 {
720     return level;
721 }
722
723 /****************************************************************************
724 REMARKS:
725 Internal function to convert the find data to the generic interface.
726 ****************************************************************************/
727 static void convertFindData(
728     PM_findData *findData,
729     struct find_t *blk)
730 {
731     ulong   dwSize = findData->dwSize;
732
733     memset(findData,0,findData->dwSize);
734     findData->dwSize = dwSize;
735     if (blk->attrib & _A_RDONLY)
736         findData->attrib |= PM_FILE_READONLY;
737     if (blk->attrib & _A_SUBDIR)
738         findData->attrib |= PM_FILE_DIRECTORY;
739     if (blk->attrib & _A_ARCH)
740         findData->attrib |= PM_FILE_ARCHIVE;
741     if (blk->attrib & _A_HIDDEN)
742         findData->attrib |= PM_FILE_HIDDEN;
743     if (blk->attrib & _A_SYSTEM)
744         findData->attrib |= PM_FILE_SYSTEM;
745     findData->sizeLo = blk->size;
746     strncpy(findData->name,blk->name,PM_MAX_PATH);
747     findData->name[PM_MAX_PATH-1] = 0;
748 }
749
750 #define FIND_MASK   (_A_RDONLY | _A_ARCH | _A_SUBDIR | _A_HIDDEN | _A_SYSTEM)
751
752 /****************************************************************************
753 REMARKS:
754 Function to find the first file matching a search criteria in a directory.
755 ****************************************************************************/
756 void * PMAPI PM_findFirstFile(
757     const char *filename,
758     PM_findData *findData)
759 {
760     struct find_t *blk;
761
762     if ((blk = PM_malloc(sizeof(*blk))) == NULL)
763         return PM_FILE_INVALID;
764     if (_dos_findfirst((char*)filename,FIND_MASK,blk) == 0) {
765         convertFindData(findData,blk);
766         return blk;
767         }
768     return PM_FILE_INVALID;
769 }
770
771 /****************************************************************************
772 REMARKS:
773 Function to find the next file matching a search criteria in a directory.
774 ****************************************************************************/
775 ibool PMAPI PM_findNextFile(
776     void *handle,
777     PM_findData *findData)
778 {
779     struct find_t *blk = handle;
780
781     if (_dos_findnext(blk) == 0) {
782         convertFindData(findData,blk);
783         return true;
784         }
785     return false;
786 }
787
788 /****************************************************************************
789 REMARKS:
790 Function to close the find process
791 ****************************************************************************/
792 void PMAPI PM_findClose(
793     void *handle)
794 {
795     PM_free(handle);
796 }
797
798 /****************************************************************************
799 REMARKS:
800 Function to determine if a drive is a valid drive or not. Under Unix this
801 function will return false for anything except a value of 3 (considered
802 the root drive, and equivalent to C: for non-Unix systems). The drive
803 numbering is:
804
805     1   - Drive A:
806     2   - Drive B:
807     3   - Drive C:
808     etc
809
810 ****************************************************************************/
811 ibool PMAPI PM_driveValid(
812     char drive)
813 {
814     RMREGS  regs;
815     regs.h.dl = (uchar)(drive - 'A' + 1);
816     regs.h.ah = 0x36;               // Get disk information service
817     PM_int86(0x21,&regs,&regs);
818     return regs.x.ax != 0xFFFF;     // AX = 0xFFFF if disk is invalid
819 }
820
821 /****************************************************************************
822 REMARKS:
823 Function to get the current working directory for the specififed drive.
824 Under Unix this will always return the current working directory regardless
825 of what the value of 'drive' is.
826 ****************************************************************************/
827 void PMAPI PM_getdcwd(
828     int drive,
829     char *dir,
830     int len)
831 {
832     uint oldDrive,maxDrives;
833     _dos_getdrive(&oldDrive);
834     _dos_setdrive(drive,&maxDrives);
835     getcwd(dir,len);
836     _dos_setdrive(oldDrive,&maxDrives);
837 }
838
839 /****************************************************************************
840 REMARKS:
841 Function to change the file attributes for a specific file.
842 ****************************************************************************/
843 void PMAPI PM_setFileAttr(
844     const char *filename,
845     uint attrib)
846 {
847 #if defined(TNT) && defined(_MSC_VER)
848     DWORD attr = 0;
849
850     if (attrib & PM_FILE_READONLY)
851         attr |= FILE_ATTRIBUTE_READONLY;
852     if (attrib & PM_FILE_ARCHIVE)
853         attr |= FILE_ATTRIBUTE_ARCHIVE;
854     if (attrib & PM_FILE_HIDDEN)
855         attr |= FILE_ATTRIBUTE_HIDDEN;
856     if (attrib & PM_FILE_SYSTEM)
857         attr |= FILE_ATTRIBUTE_SYSTEM;
858     SetFileAttributes((LPSTR)filename, attr);
859 #else
860     uint attr = 0;
861
862     if (attrib & PM_FILE_READONLY)
863         attr |= _A_RDONLY;
864     if (attrib & PM_FILE_ARCHIVE)
865         attr |= _A_ARCH;
866     if (attrib & PM_FILE_HIDDEN)
867         attr |= _A_HIDDEN;
868     if (attrib & PM_FILE_SYSTEM)
869         attr |= _A_SYSTEM;
870     _dos_setfileattr(filename,attr);
871 #endif
872 }
873
874 /****************************************************************************
875 REMARKS:
876 Function to create a directory.
877 ****************************************************************************/
878 ibool PMAPI PM_mkdir(
879     const char *filename)
880 {
881 #ifdef  __GNUC__
882     return mkdir(filename,S_IRUSR) == 0;
883 #else
884     return mkdir(filename) == 0;
885 #endif
886 }
887
888 /****************************************************************************
889 REMARKS:
890 Function to remove a directory.
891 ****************************************************************************/
892 ibool PMAPI PM_rmdir(
893     const char *filename)
894 {
895     return rmdir(filename) == 0;
896 }
897
898 /*-------------------------------------------------------------------------*/
899 /* Generic DPMI routines common to 16/32 bit code                          */
900 /*-------------------------------------------------------------------------*/
901
902 #ifndef REALMODE
903 ulong PMAPI DPMI_mapPhysicalToLinear(ulong physAddr,ulong limit)
904 {
905     PMREGS  r;
906     int     i;
907     ulong   baseAddr,baseOfs,roundedLimit;
908
909     /* We can't map memory below 1Mb, but the linear address are already
910      * mapped 1:1 for this memory anyway so we just return the base address.
911      */
912     if (physAddr < 0x100000L)
913         return physAddr;
914
915     /* Search table of existing mappings to see if we have already mapped
916      * a region of memory that will serve this purpose. We do this because
917      * DPMI 0.9 does not allow us to free physical memory mappings, and if
918      * the mappings get re-used in the program we want to avoid allocating
919      * more mappings than necessary.
920      */
921     for (i = 0; i < numMaps; i++) {
922         if (maps[i].physical == physAddr && maps[i].limit == limit)
923             return maps[i].linear;
924         }
925
926     /* Find a free slot in our physical memory mapping table */
927     for (i = 0; i < numMaps; i++) {
928         if (maps[i].limit == 0)
929             break;
930         }
931     if (i == numMaps) {
932         i = numMaps++;
933         if (i == MAX_MEMORY_MAPPINGS)
934             return NULL;
935         }
936
937     /* Round the physical address to a 4Kb boundary and the limit to a
938      * 4Kb-1 boundary before passing the values to DPMI as some extenders
939      * will fail the calls unless this is the case. If we round the
940      * physical address, then we also add an extra offset into the address
941      * that we return.
942      */
943     baseOfs = physAddr & 4095;
944     baseAddr = physAddr & ~4095;
945     roundedLimit = ((limit+baseOfs+1+4095) & ~4095)-1;
946     r.x.ax = 0x800;
947     r.x.bx = baseAddr >> 16;
948     r.x.cx = baseAddr & 0xFFFF;
949     r.x.si = roundedLimit >> 16;
950     r.x.di = roundedLimit & 0xFFFF;
951     PM_int386(0x31, &r, &r);
952     if (r.x.cflag)
953         return 0xFFFFFFFFUL;
954     maps[i].physical = physAddr;
955     maps[i].limit = limit;
956     maps[i].linear = ((ulong)r.x.bx << 16) + r.x.cx + baseOfs;
957     return maps[i].linear;
958 }
959
960 int PMAPI DPMI_setSelectorBase(ushort sel,ulong linAddr)
961 {
962     PMREGS  r;
963
964     r.x.ax = 7;                     /* DPMI set selector base address   */
965     r.x.bx = sel;
966     r.x.cx = linAddr >> 16;
967     r.x.dx = linAddr & 0xFFFF;
968     PM_int386(0x31, &r, &r);
969     if (r.x.cflag)
970         return 0;
971     return 1;
972 }
973
974 ulong PMAPI DPMI_getSelectorBase(ushort sel)
975 {
976     PMREGS  r;
977
978     r.x.ax = 6;                     /* DPMI get selector base address   */
979     r.x.bx = sel;
980     PM_int386(0x31, &r, &r);
981     return ((ulong)r.x.cx << 16) + r.x.dx;
982 }
983
984 int PMAPI DPMI_setSelectorLimit(ushort sel,ulong limit)
985 {
986     PMREGS  r;
987
988     r.x.ax = 8;                     /* DPMI set selector limit          */
989     r.x.bx = sel;
990     r.x.cx = limit >> 16;
991     r.x.dx = limit & 0xFFFF;
992     PM_int386(0x31, &r, &r);
993     if (r.x.cflag)
994         return 0;
995     return 1;
996 }
997
998 uint PMAPI DPMI_createSelector(ulong base,ulong limit)
999 {
1000     uint    sel;
1001     PMREGS  r;
1002
1003     /* Allocate 1 descriptor */
1004     r.x.ax = 0;
1005     r.x.cx = 1;
1006     PM_int386(0x31, &r, &r);
1007     if (r.x.cflag) return 0;
1008     sel = r.x.ax;
1009
1010     /* Set the descriptor access rights (for a 32 bit page granular
1011      * segment).
1012      */
1013     if (limit >= 0x10000L) {
1014         r.x.ax = 9;
1015         r.x.bx = sel;
1016         r.x.cx = 0x40F3;
1017         PM_int386(0x31, &r, &r);
1018         }
1019
1020     /* Map physical memory and create selector */
1021     if ((base = DPMI_mapPhysicalToLinear(base,limit)) == 0xFFFFFFFFUL)
1022         return 0;
1023     if (!DPMI_setSelectorBase(sel,base))
1024         return 0;
1025     if (!DPMI_setSelectorLimit(sel,limit))
1026         return 0;
1027     return sel;
1028 }
1029
1030 void PMAPI DPMI_freeSelector(uint sel)
1031 {
1032     PMREGS  r;
1033
1034     r.x.ax = 1;
1035     r.x.bx = sel;
1036     PM_int386(0x31, &r, &r);
1037 }
1038
1039 int PMAPI DPMI_lockLinearPages(ulong linear,ulong len)
1040 {
1041     PMREGS  r;
1042
1043     r.x.ax = 0x600;                     /* DPMI Lock Linear Region      */
1044     r.x.bx = (linear >> 16);            /* Linear address in BX:CX      */
1045     r.x.cx = (linear & 0xFFFF);
1046     r.x.si = (len >> 16);               /* Length in SI:DI              */
1047     r.x.di = (len & 0xFFFF);
1048     PM_int386(0x31, &r, &r);
1049     return (!r.x.cflag);
1050 }
1051
1052 int PMAPI DPMI_unlockLinearPages(ulong linear,ulong len)
1053 {
1054     PMREGS  r;
1055
1056     r.x.ax = 0x601;                     /* DPMI Unlock Linear Region    */
1057     r.x.bx = (linear >> 16);            /* Linear address in BX:CX      */
1058     r.x.cx = (linear & 0xFFFF);
1059     r.x.si = (len >> 16);               /* Length in SI:DI              */
1060     r.x.di = (len & 0xFFFF);
1061     PM_int386(0x31, &r, &r);
1062     return (!r.x.cflag);
1063 }
1064
1065 /****************************************************************************
1066 REMARKS:
1067 Adjust the page table caching bits directly. Requires ring 0 access and
1068 only works with DOS4GW and compatible extenders (CauseWay also works since
1069 it has direct support for the ring 0 instructions we need from ring 3). Will
1070 not work in a DOS box, but we call into the ring 0 helper VxD so we should
1071 never get here in a DOS box anyway (assuming the VxD is present). If we
1072 do get here and we are in windows, this code will be skipped.
1073 ****************************************************************************/
1074 static void PM_adjustPageTables(
1075     ulong linear,
1076     ulong limit,
1077     ibool isCached)
1078 {
1079 #ifdef  DOS4GW
1080     int     startPDB,endPDB,iPDB,startPage,endPage,start,end,iPage;
1081     ulong   andMask,orMask,pageTable,*pPageTable;
1082
1083     andMask = ~0x18;
1084     orMask = (isCached) ? 0x00 : 0x18;
1085     if (_PM_pagingEnabled() == 1 && (PDB = _PM_getPDB()) != 0) {
1086         if (_PM_haveCauseWay) {
1087             /* CauseWay is a little different in the page table handling.
1088              * The code that we use for DOS4G/W does not appear to work
1089              * with CauseWay correctly as it does not appear to allow us
1090              * to map the page tables directly. Instead we can directly
1091              * access the page table entries in extended memory where
1092              * CauseWay always locates them (starting at 1024*4096*1023)
1093              */
1094             startPage = (linear >> 12);
1095             endPage = ((linear+limit) >> 12);
1096             pPageTable = (ulong*)CW_PAGE_TABLE_START;
1097             for (iPage = startPage; iPage <= endPage; iPage++)
1098                 pPageTable[iPage] = (pPageTable[iPage] & andMask) | orMask;
1099             }
1100         else {
1101             pPDB = (ulong*)DPMI_mapPhysicalToLinear(PDB,0xFFF);
1102             if (pPDB) {
1103                 startPDB = (linear >> 22) & 0x3FF;
1104                 startPage = (linear >> 12) & 0x3FF;
1105                 endPDB = ((linear+limit) >> 22) & 0x3FF;
1106                 endPage = ((linear+limit) >> 12) & 0x3FF;
1107                 for (iPDB = startPDB; iPDB <= endPDB; iPDB++) {
1108                     pageTable = pPDB[iPDB] & ~0xFFF;
1109                     pPageTable = (ulong*)DPMI_mapPhysicalToLinear(pageTable,0xFFF);
1110                     start = (iPDB == startPDB) ? startPage : 0;
1111                     end = (iPDB == endPDB) ? endPage : 0x3FF;
1112                     for (iPage = start; iPage <= end; iPage++)
1113                         pPageTable[iPage] = (pPageTable[iPage] & andMask) | orMask;
1114                     }
1115                 }
1116             }
1117         PM_flushTLB();
1118         }
1119 #endif
1120 }
1121
1122 void * PMAPI DPMI_mapPhysicalAddr(ulong base,ulong limit,ibool isCached)
1123 {
1124     PMSREGS     sregs;
1125     ulong       linAddr;
1126     ulong       DSBaseAddr;
1127
1128     /* Get the base address for the default DS selector */
1129     PM_segread(&sregs);
1130     DSBaseAddr = DPMI_getSelectorBase(sregs.ds);
1131     if ((base < 0x100000) && (DSBaseAddr == 0)) {
1132         /* DS is zero based, so we can directly access the first 1Mb of
1133          * system memory (like under DOS4GW).
1134          */
1135         return (void*)base;
1136         }
1137
1138     /* Map the memory to a linear address using DPMI function 0x800 */
1139     if ((linAddr = DPMI_mapPhysicalToLinear(base,limit)) == 0xFFFFFFFF) {
1140         if (base >= 0x100000)
1141             return NULL;
1142         /* If the linear address mapping fails but we are trying to
1143          * map an area in the first 1Mb of system memory, then we must
1144          * be running under a Windows or OS/2 DOS box. Under these
1145          * environments we can use the segment wrap around as a fallback
1146          * measure, as this does work properly.
1147          */
1148         linAddr = base;
1149         }
1150
1151     /* Now expand the default DS selector to 4Gb so we can access it */
1152     if (!DPMI_setSelectorLimit(sregs.ds,0xFFFFFFFFUL))
1153         return NULL;
1154
1155     /* Finally enable caching for the page tables that we just mapped in,
1156      * since DOS4GW and PMODE/W create the page table entries without
1157      * caching enabled which hurts the performance of the linear framebuffer
1158      * as it disables write combining on Pentium Pro and above processors.
1159      *
1160      * For those processors cache disabling is better handled through the
1161      * MTRR registers anyway (we can write combine a region but disable
1162      * caching) so that MMIO register regions do not screw up.
1163      */
1164     if (DSBaseAddr == 0)
1165         PM_adjustPageTables(linAddr,limit,isCached);
1166
1167     /* Now return the base address of the memory into the default DS */
1168     return (void*)(linAddr - DSBaseAddr);
1169 }
1170
1171 #if defined(PM386)
1172
1173 /* Some DOS extender implementations do not directly support calling a
1174  * real mode procedure from protected mode. However we can simulate what
1175  * we need temporarily hooking the INT 6Ah vector with a small real mode
1176  * stub that will call our real mode code for us.
1177  */
1178
1179 static uchar int6AHandler[] = {
1180     0x00,0x00,0x00,0x00,        /*  __PMODE_callReal variable           */
1181     0xFB,                       /*  sti                                 */
1182     0x2E,0xFF,0x1E,0x00,0x00,   /*  call    [cs:__PMODE_callReal]       */
1183     0xCF,                       /*  iretf                               */
1184     };
1185 static uchar *crPtr = NULL; /* Pointer to of int 6A handler         */
1186 static uint crRSeg,crROff;  /* Real mode seg:offset of handler      */
1187
1188 void PMAPI PM_callRealMode(uint seg,uint off, RMREGS *in,
1189     RMSREGS *sregs)
1190 {
1191     uchar   *p;
1192     uint    oldSeg,oldOff;
1193
1194     if (!crPtr) {
1195         /* Allocate and copy the memory block only once */
1196         crPtr = PM_allocRealSeg(sizeof(int6AHandler), &crRSeg, &crROff);
1197         memcpy(crPtr,int6AHandler,sizeof(int6AHandler));
1198         }
1199     PM_setWord(crPtr,off);              /* Plug in address to call  */
1200     PM_setWord(crPtr+2,seg);
1201     p = PM_mapRealPointer(0,0x6A * 4);
1202     oldOff = PM_getWord(p);             /* Save old handler address */
1203     oldSeg = PM_getWord(p+2);
1204     PM_setWord(p,crROff+4);             /* Hook 6A handler          */
1205     PM_setWord(p+2,crRSeg);
1206     PM_int86x(0x6A, in, in, sregs);     /* Call real mode code      */
1207     PM_setWord(p,oldOff);               /* Restore old handler      */
1208     PM_setWord(p+2,oldSeg);
1209 }
1210
1211 #endif  /* PM386 */
1212
1213 #endif  /* !REALMODE */
1214
1215 /****************************************************************************
1216 REMARKS:
1217 Allocates a block of locked, physically contiguous memory. The memory
1218 may be required to be below the 16Meg boundary.
1219 ****************************************************************************/
1220 void * PMAPI PM_allocLockedMem(
1221     uint size,
1222     ulong *physAddr,
1223     ibool contiguous,
1224     ibool below16Meg)
1225 {
1226     uchar           *p,*roundedP;
1227     uint            r_seg,r_off;
1228     uint            roundedSize = (size + 4 + 0xFFF) & ~0xFFF;
1229     PM_lockHandle   lh; /* Unused in DOS */
1230 #ifndef REALMODE
1231     VXD_regs        regs;
1232
1233     /* If we have connected to our helper VxD in a Windows DOS box, use the
1234      * helper VxD services to allocate the memory that we need.
1235      */
1236     if (VXD_version) {
1237         memset(&regs,0,sizeof(regs));
1238         regs.eax = API_NUM(PMHELP_ALLOCLOCKED);
1239         regs.ebx = size;
1240         regs.ecx = (ulong)physAddr;
1241         regs.edx = contiguous | (below16Meg << 8);
1242         _PM_VxDCall(&regs,_PM_VXD_off,_PM_VXD_sel);
1243         return (void*)regs.eax;
1244         }
1245
1246     /* If the memory is not contiguous, we simply need to allocate it
1247      * using regular memory allocation services, and lock it down
1248      * in memory.
1249      *
1250      * For contiguous memory blocks, the only way to guarantee contiguous physical
1251      * memory addresses under DOS is to allocate the memory below the
1252      * 1Meg boundary as real mode memory.
1253      *
1254      * Note that we must page align the memory block, and we also must
1255      * keep track of the non-aligned pointer so we can properly free
1256      * it later. Hence we actually allocate 4 bytes more than the
1257      * size rounded up to the next 4K boundary.
1258      */
1259     if (!contiguous)
1260         p = PM_malloc(roundedSize);
1261     else
1262 #endif
1263         p = PM_allocRealSeg(roundedSize,&r_seg,&r_off);
1264     if (p == NULL)
1265         return NULL;
1266     roundedP = (void*)(((ulong)p + 0xFFF) & ~0xFFF);
1267     *((ulong*)(roundedP + size)) = (ulong)p;
1268     PM_lockDataPages(roundedP,size,&lh);
1269     if ((*physAddr = PM_getPhysicalAddr(roundedP)) == 0xFFFFFFFF) {
1270         PM_freeLockedMem(roundedP,size,contiguous);
1271         return NULL;
1272         }
1273
1274     /* Disable caching for the memory since it is probably a DMA buffer */
1275 #ifndef REALMODE
1276     PM_adjustPageTables((ulong)roundedP,size-1,false);
1277 #endif
1278     return roundedP;
1279 }
1280
1281 /****************************************************************************
1282 REMARKS:
1283 Free a block of locked memory.
1284 ****************************************************************************/
1285 void PMAPI PM_freeLockedMem(void *p,uint size,ibool contiguous)
1286 {
1287 #ifndef REALMODE
1288     VXD_regs        regs;
1289     PM_lockHandle   lh; /* Unused in DOS */
1290
1291     if (!p)
1292         return;
1293     if (VXD_version) {
1294         memset(&regs,0,sizeof(regs));
1295         regs.eax = API_NUM(PMHELP_FREELOCKED);
1296         regs.ebx = (ulong)p;
1297         regs.ecx = size;
1298         regs.edx = contiguous;
1299         _PM_VxDCall(&regs,_PM_VXD_off,_PM_VXD_sel);
1300         return;
1301         }
1302     PM_unlockDataPages(p,size,&lh);
1303     if (!contiguous)
1304         free(*((void**)((uchar*)p + size)));
1305     else
1306 #endif
1307         PM_freeRealSeg(*((void**)((char*)p + size)));
1308 }
1309
1310 #ifndef REALMODE
1311 /****************************************************************************
1312 REMARKS:
1313 Allocates a new block of pages for the page block manager.
1314 ****************************************************************************/
1315 static pageblock *PM_addNewPageBlock(void)
1316 {
1317     int         i,size;
1318     pageblock   *newBlock;
1319     char        *p,*next;
1320
1321     /* Allocate memory for the new page block, and add to head of list */
1322     size = PAGES_PER_BLOCK * PM_PAGE_SIZE + (PM_PAGE_SIZE-1) + sizeof(pageblock);
1323     if ((newBlock = PM_malloc(size)) == NULL)
1324         return NULL;
1325     newBlock->prev = NULL;
1326     newBlock->next = pageBlocks;
1327     if (pageBlocks)
1328         pageBlocks->prev = newBlock;
1329     pageBlocks = newBlock;
1330
1331     /* Initialise the page aligned free list for the page block */
1332     newBlock->freeCount = PAGES_PER_BLOCK;
1333     newBlock->freeList = p = (char*)(((ulong)(newBlock + 1) + (PM_PAGE_SIZE-1)) & ~(PM_PAGE_SIZE-1));
1334     newBlock->freeListStart = newBlock->freeList;
1335     newBlock->freeListEnd = p + (PAGES_PER_BLOCK-1) * PM_PAGE_SIZE;
1336     for (i = 0; i < PAGES_PER_BLOCK; i++,p = next)
1337         FREELIST_NEXT(p) = next = p + PM_PAGE_SIZE;
1338     FREELIST_NEXT(p - PM_PAGE_SIZE) = NULL;
1339     return newBlock;
1340 }
1341 #endif
1342
1343 /****************************************************************************
1344 REMARKS:
1345 Allocates a page aligned and page sized block of memory
1346 ****************************************************************************/
1347 void * PMAPI PM_allocPage(
1348     ibool locked)
1349 {
1350 #ifndef REALMODE
1351     VXD_regs        regs;
1352     pageblock       *block;
1353     void            *p;
1354     PM_lockHandle   lh; /* Unused in DOS */
1355
1356     /* Call the helper VxD for this service if we are running in a DOS box */
1357     if (VXD_version) {
1358         memset(&regs,0,sizeof(regs));
1359         regs.eax = API_NUM(PMHELP_ALLOCPAGE);
1360         regs.ebx = locked;
1361         _PM_VxDCall(&regs,_PM_VXD_off,_PM_VXD_sel);
1362         return (void*)regs.eax;
1363         }
1364
1365     /* Scan the block list looking for any free blocks. Allocate a new
1366      * page block if no free blocks are found.
1367      */
1368     for (block = pageBlocks; block != NULL; block = block->next) {
1369         if (block->freeCount)
1370             break;
1371         }
1372     if (block == NULL && (block = PM_addNewPageBlock()) == NULL)
1373         return NULL;
1374     block->freeCount--;
1375     p = block->freeList;
1376     block->freeList = FREELIST_NEXT(p);
1377     if (locked)
1378         PM_lockDataPages(p,PM_PAGE_SIZE,&lh);
1379     return p;
1380 #else
1381     return NULL;
1382 #endif
1383 }
1384
1385 /****************************************************************************
1386 REMARKS:
1387 Free a page aligned and page sized block of memory
1388 ****************************************************************************/
1389 void PMAPI PM_freePage(
1390     void *p)
1391 {
1392 #ifndef REALMODE
1393     VXD_regs    regs;
1394     pageblock   *block;
1395
1396     /* Call the helper VxD for this service if we are running in a DOS box */
1397     if (VXD_version) {
1398         memset(&regs,0,sizeof(regs));
1399         regs.eax = API_NUM(PMHELP_FREEPAGE);
1400         regs.ebx = (ulong)p;
1401         _PM_VxDCall(&regs,_PM_VXD_off,_PM_VXD_sel);
1402         return;
1403         }
1404
1405     /* First find the page block that this page belongs to */
1406     for (block = pageBlocks; block != NULL; block = block->next) {
1407         if (p >= block->freeListStart && p <= block->freeListEnd)
1408             break;
1409         }
1410     CHECK(block != NULL);
1411
1412     /* Now free the block by adding it to the free list */
1413     FREELIST_NEXT(p) = block->freeList;
1414     block->freeList = p;
1415     if (++block->freeCount == PAGES_PER_BLOCK) {
1416         /* If all pages in the page block are now free, free the entire
1417          * page block itself.
1418          */
1419         if (block == pageBlocks) {
1420             /* Delete from head */
1421             pageBlocks = block->next;
1422             if (block->next)
1423                 block->next->prev = NULL;
1424             }
1425         else {
1426             /* Delete from middle of list */
1427             CHECK(block->prev != NULL);
1428             block->prev->next = block->next;
1429             if (block->next)
1430                 block->next->prev = block->prev;
1431             }
1432         PM_free(block);
1433         }
1434 #else
1435     (void)p;
1436 #endif
1437 }
1438
1439 /*-------------------------------------------------------------------------*/
1440 /* DOS Real Mode support.                                                  */
1441 /*-------------------------------------------------------------------------*/
1442
1443 #ifdef REALMODE
1444
1445 #ifndef MK_FP
1446 #define MK_FP(s,o)  ( (void far *)( ((ulong)(s) << 16) + \
1447                     (ulong)(o) ))
1448 #endif
1449
1450 void * PMAPI PM_mapRealPointer(uint r_seg,uint r_off)
1451 { return MK_FP(r_seg,r_off); }
1452
1453 void * PMAPI PM_getBIOSPointer(void)
1454 {
1455     return MK_FP(0x40,0);
1456 }
1457
1458 void * PMAPI PM_getA0000Pointer(void)
1459 {
1460     return MK_FP(0xA000,0);
1461 }
1462
1463 void * PMAPI PM_mapPhysicalAddr(ulong base,ulong limit,ibool isCached)
1464 {
1465     uint sel = base >> 4;
1466     uint off = base & 0xF;
1467     limit = limit;
1468     return MK_FP(sel,off);
1469 }
1470
1471 void PMAPI PM_freePhysicalAddr(void *ptr,ulong limit)
1472 { ptr = ptr; }
1473
1474 ulong PMAPI PM_getPhysicalAddr(void *p)
1475 {
1476     return ((((ulong)p >> 16) << 4) + (ushort)p);
1477 }
1478
1479 ibool PMAPI PM_getPhysicalAddrRange(void *p,ulong length,ulong *physAddress)
1480 { return false; }
1481
1482 void * PMAPI PM_mapToProcess(void *base,ulong limit)
1483 { return (void*)base; }
1484
1485 void * PMAPI PM_allocRealSeg(uint size,uint *r_seg,uint *r_off)
1486 {
1487     /* Call malloc() to allocate the memory for us */
1488     void *p = PM_malloc(size);
1489     *r_seg = FP_SEG(p);
1490     *r_off = FP_OFF(p);
1491     return p;
1492 }
1493
1494 void PMAPI PM_freeRealSeg(void *mem)
1495 {
1496     if (mem) PM_free(mem);
1497 }
1498
1499 int PMAPI PM_int86(int intno, RMREGS *in, RMREGS *out)
1500 {
1501     return PM_int386(intno,in,out);
1502 }
1503
1504 int PMAPI PM_int86x(int intno, RMREGS *in, RMREGS *out,
1505     RMSREGS *sregs)
1506 {
1507     return PM_int386x(intno,in,out,sregs);
1508 }
1509
1510 void PMAPI PM_availableMemory(ulong *physical,ulong *total)
1511 {
1512     PMREGS regs;
1513
1514     regs.h.ah = 0x48;
1515     regs.x.bx = 0xFFFF;
1516     PM_int86(0x21,&regs,&regs);
1517     *physical = *total = regs.x.bx * 16UL;
1518 }
1519
1520 #endif
1521
1522 /*-------------------------------------------------------------------------*/
1523 /* Phar Lap TNT DOS Extender support.                                      */
1524 /*-------------------------------------------------------------------------*/
1525
1526 #ifdef TNT
1527
1528 #include <pldos32.h>
1529 #include <pharlap.h>
1530 #include <hw386.h>
1531
1532 static uchar *zeroPtr = NULL;
1533
1534 void * PMAPI PM_getBIOSPointer(void)
1535 {
1536     if (!zeroPtr)
1537         zeroPtr = PM_mapPhysicalAddr(0,0xFFFFF,true);
1538     return (void*)(zeroPtr + 0x400);
1539 }
1540
1541 void * PMAPI PM_getA0000Pointer(void)
1542 {
1543     static void *bankPtr;
1544     if (!bankPtr)
1545         bankPtr = PM_mapPhysicalAddr(0xA0000,0xFFFF,true);
1546     return bankPtr;
1547 }
1548
1549 void * PMAPI PM_mapPhysicalAddr(ulong base,ulong limit,ibool isCached)
1550 {
1551     CONFIG_INF  config;
1552     ULONG       offset;
1553     int         err;
1554     ulong       baseAddr,baseOfs,newLimit;
1555     VXD_regs    regs;
1556
1557     /* If we have connected to our helper VxD in a Windows DOS box, use
1558      * the helper VxD services to map memory instead of the DPMI services.
1559      * We do this because the helper VxD can properly disable caching
1560      * where necessary, which we can only do directly here if we are
1561      * running at ring 0 (ie: under real DOS).
1562      */
1563     if (VXD_version == -1)
1564         PM_init();
1565     if (VXD_version) {
1566         memset(&regs,0,sizeof(regs));
1567         regs.eax = API_NUM(PMHELP_MAPPHYS);
1568         regs.ebx = base;
1569         regs.ecx = limit;
1570         regs.edx = isCached;
1571         _PM_VxDCall(&regs,_PM_VXD_off,_PM_VXD_sel);
1572         return (void*)regs.eax;
1573         }
1574
1575     /* Round the physical address to a 4Kb boundary and the limit to a
1576      * 4Kb-1 boundary before passing the values to TNT. If we round the
1577      * physical address, then we also add an extra offset into the address
1578      * that we return.
1579      */
1580     baseOfs = base & 4095;
1581     baseAddr = base & ~4095;
1582     newLimit = ((limit+baseOfs+1+4095) & ~4095)-1;
1583     _dx_config_inf(&config, (UCHAR*)&config);
1584     err = _dx_map_phys(config.c_ds_sel,baseAddr,(newLimit + 4095) / 4096,&offset);
1585     if (err == 130) {
1586         /* If the TNT function failed, we are running in a DPMI environment
1587          * and this function does not work. However we know how to handle
1588          * DPMI properly, so we use our generic DPMI functions to do
1589          * what the TNT runtime libraries can't.
1590          */
1591         return DPMI_mapPhysicalAddr(base,limit,isCached);
1592         }
1593     if (err == 0)
1594         return (void*)(offset + baseOfs);
1595     return NULL;
1596 }
1597
1598 void PMAPI PM_freePhysicalAddr(void *ptr,ulong limit)
1599 {
1600 }
1601
1602 ulong PMAPI PM_getPhysicalAddr(void *p)
1603 { return 0xFFFFFFFFUL; }
1604
1605 ibool PMAPI PM_getPhysicalAddrRange(void *p,ulong length,ulong *physAddress)
1606 { return false; }
1607
1608 void * PMAPI PM_mapToProcess(void *base,ulong limit)
1609 { return (void*)base; }
1610
1611 void * PMAPI PM_mapRealPointer(uint r_seg,uint r_off)
1612 {
1613     if (!zeroPtr)
1614         zeroPtr = PM_mapPhysicalAddr(0,0xFFFFF);
1615     return (void*)(zeroPtr + MK_PHYS(r_seg,r_off));
1616 }
1617
1618 void * PMAPI PM_allocRealSeg(uint size,uint *r_seg,uint *r_off)
1619 {
1620     USHORT  addr,t;
1621     void    *p;
1622
1623     if (_dx_real_alloc((size + 0xF) >> 4,&addr,&t) != 0)
1624         return 0;
1625     *r_seg = addr;                  /* Real mode segment address    */
1626     *r_off = 0;                     /* Real mode segment offset     */
1627     p = PM_mapRealPointer(*r_seg,*r_off);
1628     _PM_addRealModeBlock(p,addr);
1629     return p;
1630 }
1631
1632 void PMAPI PM_freeRealSeg(void *mem)
1633 {
1634     if (mem) _dx_real_free(_PM_findRealModeBlock(mem));
1635 }
1636
1637 #define INDPMI(reg)     rmregs.reg = regs->reg
1638 #define OUTDPMI(reg)    regs->reg = rmregs.reg
1639
1640 void PMAPI DPMI_int86(int intno, DPMI_regs *regs)
1641 {
1642     SWI_REGS    rmregs;
1643
1644     memset(&rmregs, 0, sizeof(rmregs));
1645     INDPMI(eax); INDPMI(ebx); INDPMI(ecx); INDPMI(edx); INDPMI(esi); INDPMI(edi);
1646
1647     _dx_real_int(intno,&rmregs);
1648
1649     OUTDPMI(eax); OUTDPMI(ebx); OUTDPMI(ecx); OUTDPMI(edx); OUTDPMI(esi); OUTDPMI(edi);
1650     regs->flags = rmregs.flags;
1651 }
1652
1653 #define IN(reg)     rmregs.reg = in->e.reg
1654 #define OUT(reg)    out->e.reg = rmregs.reg
1655
1656 int PMAPI PM_int86(int intno, RMREGS *in, RMREGS *out)
1657 {
1658     SWI_REGS    rmregs;
1659
1660     memset(&rmregs, 0, sizeof(rmregs));
1661     IN(eax); IN(ebx); IN(ecx); IN(edx); IN(esi); IN(edi);
1662
1663     _dx_real_int(intno,&rmregs);
1664
1665     OUT(eax); OUT(ebx); OUT(ecx); OUT(edx); OUT(esi); OUT(edi);
1666     out->x.cflag = rmregs.flags & 0x1;
1667     return out->x.ax;
1668 }
1669
1670 int PMAPI PM_int86x(int intno, RMREGS *in, RMREGS *out,
1671     RMSREGS *sregs)
1672 {
1673     SWI_REGS    rmregs;
1674
1675     memset(&rmregs, 0, sizeof(rmregs));
1676     IN(eax); IN(ebx); IN(ecx); IN(edx); IN(esi); IN(edi);
1677     rmregs.es = sregs->es;
1678     rmregs.ds = sregs->ds;
1679
1680     _dx_real_int(intno,&rmregs);
1681
1682     OUT(eax); OUT(ebx); OUT(ecx); OUT(edx); OUT(esi); OUT(edi);
1683     sregs->es = rmregs.es;
1684     sregs->cs = rmregs.cs;
1685     sregs->ss = rmregs.ss;
1686     sregs->ds = rmregs.ds;
1687     out->x.cflag = rmregs.flags & 0x1;
1688     return out->x.ax;
1689 }
1690
1691 void PMAPI PM_availableMemory(ulong *physical,ulong *total)
1692 {
1693     PMREGS  r;
1694     uint    data[25];
1695
1696     r.x.ax = 0x2520;                /* Get free memory info */
1697     r.x.bx = 0;
1698     r.e.edx = (uint)data;
1699     PM_int386(0x21, &r, &r);
1700     *physical = data[21] * 4096;
1701     *total = data[23] * 4096;
1702 }
1703
1704 #endif
1705
1706 /*-------------------------------------------------------------------------*/
1707 /* Symantec C++ DOSX and FlashTek X-32/X-32VM support                      */
1708 /*-------------------------------------------------------------------------*/
1709
1710 #if defined(DOSX) || defined(X32VM)
1711
1712 #ifdef  X32VM
1713 #include <x32.h>
1714
1715 #define _x386_mk_protected_ptr(p)   _x32_mk_protected_ptr((void*)p)
1716 #define _x386_free_protected_ptr(p) _x32_free_protected_ptr(p)
1717 #define _x386_zero_base_ptr         _x32_zero_base_ptr
1718 #else
1719 extern void *_x386_zero_base_ptr;
1720 #endif
1721
1722 void * PMAPI PM_mapRealPointer(uint r_seg,uint r_off)
1723 {
1724     return (void*)((ulong)_x386_zero_base_ptr + MK_PHYS(r_seg,r_off));
1725 }
1726
1727 void * PMAPI PM_allocRealSeg(uint size,uint *r_seg,uint *r_off)
1728 {
1729     PMREGS  r;
1730
1731     r.h.ah = 0x48;                  /* DOS function 48h - allocate mem  */
1732     r.x.bx = (size + 0xF) >> 4;     /* Number of paragraphs to allocate */
1733     PM_int386(0x21, &r, &r);        /* Call DOS extender                */
1734     if (r.x.cflag)
1735         return 0;                   /* Could not allocate the memory    */
1736     *r_seg = r.e.eax;
1737     *r_off = 0;
1738     return PM_mapRealPointer(*r_seg,*r_off);
1739 }
1740
1741 void PMAPI PM_freeRealSeg(void *mem)
1742 {
1743     /* Cannot de-allocate this memory */
1744     mem = mem;
1745 }
1746
1747 #pragma pack(1)
1748
1749 typedef struct {
1750     ushort  intno;
1751     ushort  ds;
1752     ushort  es;
1753     ushort  fs;
1754     ushort  gs;
1755     ulong   eax;
1756     ulong   edx;
1757     } _RMREGS;
1758
1759 #pragma pack()
1760
1761 #define IN(reg)     regs.e.reg = in->e.reg
1762 #define OUT(reg)    out->e.reg = regs.e.reg
1763
1764 int PMAPI PM_int86(int intno, RMREGS *in, RMREGS *out)
1765 {
1766     _RMREGS rmregs;
1767     PMREGS  regs;
1768     PMSREGS pmsregs;
1769
1770     rmregs.intno = intno;
1771     rmregs.eax = in->e.eax;
1772     rmregs.edx = in->e.edx;
1773     IN(ebx); IN(ecx); IN(esi); IN(edi);
1774     regs.x.ax = 0x2511;
1775     regs.e.edx = (uint)(&rmregs);
1776     PM_segread(&pmsregs);
1777     PM_int386x(0x21,&regs,&regs,&pmsregs);
1778
1779     OUT(eax); OUT(ebx); OUT(ecx); OUT(esi); OUT(edi);
1780     out->x.dx = rmregs.edx;
1781     out->x.cflag = regs.x.cflag;
1782     return out->x.ax;
1783 }
1784
1785 int PMAPI PM_int86x(int intno, RMREGS *in, RMREGS *out, RMSREGS *sregs)
1786 {
1787     _RMREGS rmregs;
1788     PMREGS  regs;
1789     PMSREGS pmsregs;
1790
1791     rmregs.intno = intno;
1792     rmregs.eax = in->e.eax;
1793     rmregs.edx = in->e.edx;
1794     rmregs.es = sregs->es;
1795     rmregs.ds = sregs->ds;
1796     IN(ebx); IN(ecx); IN(esi); IN(edi);
1797     regs.x.ax = 0x2511;
1798     regs.e.edx = (uint)(&rmregs);
1799     PM_segread(&pmsregs);
1800     PM_int386x(0x21,&regs,&regs,&pmsregs);
1801
1802     OUT(eax); OUT(ebx); OUT(ecx); OUT(esi); OUT(edi);
1803     sregs->es = rmregs.es;
1804     sregs->ds = rmregs.ds;
1805     out->x.dx = rmregs.edx;
1806     out->x.cflag = regs.x.cflag;
1807     return out->x.ax;
1808 }
1809
1810 void * PMAPI PM_getBIOSPointer(void)
1811 {
1812     return (void*)((ulong)_x386_zero_base_ptr + 0x400);
1813 }
1814
1815 void * PMAPI PM_getA0000Pointer(void)
1816 {
1817     return (void*)((ulong)_x386_zero_base_ptr + 0xA0000);
1818 }
1819
1820 void * PMAPI PM_mapPhysicalAddr(ulong base,ulong limit,ibool isCached)
1821 {
1822     VXD_regs    regs;
1823
1824     /* If we have connected to our helper VxD in a Windows DOS box, use
1825      * the helper VxD services to map memory instead of the DPMI services.
1826      * We do this because the helper VxD can properly disable caching
1827      * where necessary, which we can only do directly here if we are
1828      * running at ring 0 (ie: under real DOS).
1829      */
1830     if (VXD_version == -1)
1831         PM_init();
1832     if (VXD_version) {
1833         memset(&regs,0,sizeof(regs));
1834         regs.eax = API_NUM(PMHELP_MAPPHYS);
1835         regs.ebx = base;
1836         regs.ecx = limit;
1837         regs.edx = isCached;
1838         _PM_VxDCall(&regs,_PM_VXD_off,_PM_VXD_sel);
1839         return (void*)regs.eax;
1840         }
1841
1842     if (base > 0x100000)
1843         return _x386_map_physical_address((void*)base,limit);
1844     return (void*)((ulong)_x386_zero_base_ptr + base);
1845 }
1846
1847 void PMAPI PM_freePhysicalAddr(void *ptr,ulong limit)
1848 {
1849     /* Mapping cannot be freed */
1850 }
1851
1852 ulong PMAPI PM_getPhysicalAddr(void *p)
1853 { return 0xFFFFFFFFUL; }
1854
1855 ibool PMAPI PM_getPhysicalAddrRange(void *p,ulong length,ulong *physAddress)
1856 { return false; }
1857
1858 void * PMAPI PM_mapToProcess(void *base,ulong limit)
1859 { return (void*)base; }
1860
1861 ulong _cdecl _X32_getPhysMem(void);
1862
1863 void PMAPI PM_availableMemory(ulong *physical,ulong *total)
1864 {
1865     PMREGS  regs;
1866
1867     /* Get total memory available, including virtual memory */
1868     regs.x.ax = 0x350B;
1869     PM_int386(0x21,&regs,&regs);
1870     *total = regs.e.eax;
1871
1872     /* Get physical memory available */
1873     *physical = _X32_getPhysMem();
1874     if (*physical > *total)
1875         *physical = *total;
1876 }
1877
1878 #endif
1879
1880 /*-------------------------------------------------------------------------*/
1881 /* Borland's DPMI32, Watcom DOS4GW and DJGPP DPMI support routines         */
1882 /*-------------------------------------------------------------------------*/
1883
1884 #if defined(DPMI32) || defined(DOS4GW) || defined(DJGPP)
1885
1886 void * PMAPI PM_getBIOSPointer(void)
1887 {
1888     return PM_mapPhysicalAddr(0x400,0xFFFF,true);
1889 }
1890
1891 void * PMAPI PM_getA0000Pointer(void)
1892 {
1893     return PM_mapPhysicalAddr(0xA0000,0xFFFF,true);
1894 }
1895
1896 void * PMAPI PM_mapPhysicalAddr(ulong base,ulong limit,ibool isCached)
1897 {
1898     VXD_regs    regs;
1899
1900 #ifdef  DJGPP
1901     /* Enable near pointers for DJGPP V2 */
1902     __djgpp_nearptr_enable();
1903 #endif
1904     /* If we have connected to our helper VxD in a Windows DOS box, use
1905      * the helper VxD services to map memory instead of the DPMI services.
1906      * We do this because the helper VxD can properly disable caching
1907      * where necessary, which we can only do directly here if we are
1908      * running at ring 0 (ie: under real DOS).
1909      */
1910     if (VXD_version == -1)
1911         PM_init();
1912     if (VXD_version) {
1913         memset(&regs,0,sizeof(regs));
1914         regs.eax = API_NUM(PMHELP_MAPPHYS);
1915         regs.ebx = base;
1916         regs.ecx = limit;
1917         regs.edx = isCached;
1918         _PM_VxDCall(&regs,_PM_VXD_off,_PM_VXD_sel);
1919         return (void*)regs.eax;
1920         }
1921     return DPMI_mapPhysicalAddr(base,limit,isCached);
1922 }
1923
1924 void PMAPI PM_freePhysicalAddr(void *ptr,ulong limit)
1925 {
1926     /* Mapping cannot be freed */
1927     (void)ptr;
1928     (void)limit;
1929 }
1930
1931 ulong PMAPI PM_getPhysicalAddr(void *p)
1932 {
1933     ulong   physAddr;
1934     if (!PM_getPhysicalAddrRange(p,1,&physAddr))
1935         return 0xFFFFFFFF;
1936     return physAddr | ((ulong)p & 0xFFF);
1937 }
1938
1939 ibool PMAPI PM_getPhysicalAddrRange(
1940     void *p,
1941     ulong length,
1942     ulong *physAddress)
1943 {
1944     VXD_regs    regs;
1945     ulong       pte;
1946     PMSREGS     sregs;
1947     ulong       DSBaseAddr;
1948
1949     /* If we have connected to our helper VxD in a Windows DOS box, use the
1950      * helper VxD services to find the physical address of an address.
1951      */
1952     if (VXD_version) {
1953         memset(&regs,0,sizeof(regs));
1954         regs.eax = API_NUM(PMHELP_GETPHYSICALADDRRANGE);
1955         regs.ebx = (ulong)p;
1956         regs.ecx = (ulong)length;
1957         regs.edx = (ulong)physAddress;
1958         _PM_VxDCall(&regs,_PM_VXD_off,_PM_VXD_sel);
1959         return regs.eax;
1960         }
1961
1962     /* Find base address for default DS selector */
1963     PM_segread(&sregs);
1964     DSBaseAddr = DPMI_getSelectorBase(sregs.ds);
1965
1966     /* Otherwise directly access the page tables to determine the
1967      * physical memory address. Note that we touch the memory before
1968      * calling, otherwise the memory may not be paged in correctly.
1969      */
1970     pte = *((ulong*)p);
1971 #ifdef  DOS4GW
1972     if (_PM_pagingEnabled() == 0) {
1973         int     count;
1974         ulong   linAddr = (ulong)p;
1975
1976         /* When paging is disabled physical=linear */
1977         for (count = (length+0xFFF) >> 12; count > 0; count--) {
1978             *physAddress++ = linAddr;
1979             linAddr += 4096;
1980             }
1981         return true;
1982         }
1983     else if ((PDB = _PM_getPDB()) != 0 && DSBaseAddr == 0) {
1984         int     startPDB,endPDB,iPDB,startPage,endPage,start,end,iPage;
1985         ulong   pageTable,*pPageTable,linAddr = (ulong)p;
1986         ulong   limit = length-1;
1987
1988         pPDB = (ulong*)DPMI_mapPhysicalToLinear(PDB,0xFFF);
1989         if (pPDB) {
1990             startPDB = (linAddr >> 22) & 0x3FFL;
1991             startPage = (linAddr >> 12) & 0x3FFL;
1992             endPDB = ((linAddr+limit) >> 22) & 0x3FFL;
1993             endPage = ((linAddr+limit) >> 12) & 0x3FFL;
1994             for (iPDB = startPDB; iPDB <= endPDB; iPDB++) {
1995                 pageTable = pPDB[iPDB] & ~0xFFFL;
1996                 pPageTable = (ulong*)DPMI_mapPhysicalToLinear(pageTable,0xFFF);
1997                 start = (iPDB == startPDB) ? startPage : 0;
1998                 end = (iPDB == endPDB) ? endPage : 0x3FFL;
1999                 for (iPage = start; iPage <= end; iPage++)
2000                     *physAddress++ = (pPageTable[iPage] & ~0xFFF);
2001                 }
2002             return true;
2003             }
2004         }
2005 #endif
2006     return false;
2007 }
2008
2009 void * PMAPI PM_mapToProcess(void *base,ulong limit)
2010 {
2011     (void)limit;
2012     return (void*)base;
2013 }
2014
2015 void * PMAPI PM_mapRealPointer(uint r_seg,uint r_off)
2016 {
2017     static uchar *zeroPtr = NULL;
2018
2019     if (!zeroPtr)
2020         zeroPtr = PM_mapPhysicalAddr(0,0xFFFFF,true);
2021     return (void*)(zeroPtr + MK_PHYS(r_seg,r_off));
2022 }
2023
2024 void * PMAPI PM_allocRealSeg(uint size,uint *r_seg,uint *r_off)
2025 {
2026     PMREGS      r;
2027     void        *p;
2028
2029     r.x.ax = 0x100;                 /* DPMI allocate DOS memory         */
2030     r.x.bx = (size + 0xF) >> 4;     /* number of paragraphs             */
2031     PM_int386(0x31, &r, &r);
2032     if (r.x.cflag)
2033         return NULL;                /* DPMI call failed                 */
2034     *r_seg = r.x.ax;                /* Real mode segment                */
2035     *r_off = 0;
2036     p = PM_mapRealPointer(*r_seg,*r_off);
2037     _PM_addRealModeBlock(p,r.x.dx);
2038     return p;
2039 }
2040
2041 void PMAPI PM_freeRealSeg(void *mem)
2042 {
2043     PMREGS  r;
2044
2045     if (mem) {
2046         r.x.ax = 0x101;                     /* DPMI free DOS memory         */
2047         r.x.dx = _PM_findRealModeBlock(mem);/* DX := selector from 0x100    */
2048         PM_int386(0x31, &r, &r);
2049         }
2050 }
2051
2052 static DPMI_handler_t   DPMI_int10 = NULL;
2053
2054 void PMAPI DPMI_setInt10Handler(DPMI_handler_t handler)
2055 {
2056     DPMI_int10 = handler;
2057 }
2058
2059 void PMAPI DPMI_int86(int intno, DPMI_regs *regs)
2060 {
2061     PMREGS      r;
2062     PMSREGS     sr;
2063
2064     if (intno == 0x10 && DPMI_int10) {
2065         if (DPMI_int10(regs))
2066             return;
2067         }
2068     PM_segread(&sr);
2069     r.x.ax = 0x300;                 /* DPMI issue real interrupt    */
2070     r.h.bl = intno;
2071     r.h.bh = 0;
2072     r.x.cx = 0;
2073     sr.es = sr.ds;
2074     r.e.edi = (uint)regs;
2075     PM_int386x(0x31, &r, &r, &sr);  /* Issue the interrupt          */
2076 }
2077
2078 #define IN(reg)     rmregs.reg = in->e.reg
2079 #define OUT(reg)    out->e.reg = rmregs.reg
2080
2081 int PMAPI PM_int86(int intno, RMREGS *in, RMREGS *out)
2082 {
2083     DPMI_regs   rmregs;
2084
2085     memset(&rmregs, 0, sizeof(rmregs));
2086     IN(eax); IN(ebx); IN(ecx); IN(edx); IN(esi); IN(edi);
2087
2088     DPMI_int86(intno,&rmregs);      /* DPMI issue real interrupt    */
2089
2090     OUT(eax); OUT(ebx); OUT(ecx); OUT(edx); OUT(esi); OUT(edi);
2091     out->x.cflag = rmregs.flags & 0x1;
2092     return out->x.ax;
2093 }
2094
2095 int PMAPI PM_int86x(int intno, RMREGS *in, RMREGS *out,
2096     RMSREGS *sregs)
2097 {
2098     DPMI_regs   rmregs;
2099
2100     memset(&rmregs, 0, sizeof(rmregs));
2101     IN(eax); IN(ebx); IN(ecx); IN(edx); IN(esi); IN(edi);
2102     rmregs.es = sregs->es;
2103     rmregs.ds = sregs->ds;
2104
2105     DPMI_int86(intno,&rmregs);      /* DPMI issue real interrupt    */
2106
2107     OUT(eax); OUT(ebx); OUT(ecx); OUT(edx); OUT(esi); OUT(edi);
2108     sregs->es = rmregs.es;
2109     sregs->cs = rmregs.cs;
2110     sregs->ss = rmregs.ss;
2111     sregs->ds = rmregs.ds;
2112     out->x.cflag = rmregs.flags & 0x1;
2113     return out->x.ax;
2114 }
2115
2116 #pragma pack(1)
2117
2118 typedef struct {
2119         uint    LargestBlockAvail;
2120         uint    MaxUnlockedPage;
2121         uint    LargestLockablePage;
2122         uint    LinAddrSpace;
2123         uint    NumFreePagesAvail;
2124         uint    NumPhysicalPagesFree;
2125         uint    TotalPhysicalPages;
2126         uint    FreeLinAddrSpace;
2127         uint    SizeOfPageFile;
2128         uint    res[3];
2129         } MemInfo;
2130
2131 #pragma pack()
2132
2133 void PMAPI PM_availableMemory(ulong *physical,ulong *total)
2134 {
2135     PMREGS  r;
2136     PMSREGS sr;
2137     MemInfo memInfo;
2138
2139     PM_segread(&sr);
2140     r.x.ax = 0x500;                 /* DPMI get free memory info */
2141     sr.es = sr.ds;
2142     r.e.edi = (uint)&memInfo;
2143     PM_int386x(0x31, &r, &r, &sr);  /* Issue the interrupt */
2144     *physical = memInfo.NumPhysicalPagesFree * 4096;
2145     *total = memInfo.LargestBlockAvail;
2146     if (*total < *physical)
2147         *physical = *total;
2148 }
2149
2150 #endif
2151
2152 #ifndef __16BIT__
2153
2154 /****************************************************************************
2155 REMARKS:
2156 Call the VBE/Core software interrupt to change display banks.
2157 ****************************************************************************/
2158 void PMAPI PM_setBankA(
2159     int bank)
2160 {
2161     DPMI_regs   regs;
2162     memset(&regs, 0, sizeof(regs));
2163     regs.eax = 0x4F05;
2164     regs.ebx = 0x0000;
2165     regs.edx = bank;
2166     DPMI_int86(0x10,&regs);
2167 }
2168
2169 /****************************************************************************
2170 REMARKS:
2171 Call the VBE/Core software interrupt to change display banks.
2172 ****************************************************************************/
2173 void PMAPI PM_setBankAB(
2174     int bank)
2175 {
2176     DPMI_regs   regs;
2177     memset(&regs, 0, sizeof(regs));
2178     regs.eax = 0x4F05;
2179     regs.ebx = 0x0000;
2180     regs.edx = bank;
2181     DPMI_int86(0x10,&regs);
2182     regs.eax = 0x4F05;
2183     regs.ebx = 0x0001;
2184     regs.edx = bank;
2185     DPMI_int86(0x10,&regs);
2186 }
2187
2188 /****************************************************************************
2189 REMARKS:
2190 Call the VBE/Core software interrupt to change display start address.
2191 ****************************************************************************/
2192 void PMAPI PM_setCRTStart(
2193     int x,
2194     int y,
2195     int waitVRT)
2196 {
2197     DPMI_regs   regs;
2198     memset(&regs, 0, sizeof(regs));
2199     regs.eax = 0x4F07;
2200     regs.ebx = waitVRT;
2201     regs.ecx = x;
2202     regs.edx = y;
2203     DPMI_int86(0x10,&regs);
2204 }
2205
2206 #endif
2207
2208 /****************************************************************************
2209 REMARKS:
2210 Function to get the file attributes for a specific file.
2211 ****************************************************************************/
2212 uint PMAPI PM_getFileAttr(
2213     const char *filename)
2214 {
2215     // TODO: Implement this!
2216     return 0;
2217 }
2218
2219 /****************************************************************************
2220 REMARKS:
2221 Function to get the file time and date for a specific file.
2222 ****************************************************************************/
2223 ibool PMAPI PM_getFileTime(
2224     const char *filename,
2225     ibool gmTime,
2226     PM_time *time)
2227 {
2228     // TODO: Implement this!
2229     return false;
2230 }
2231
2232 /****************************************************************************
2233 REMARKS:
2234 Function to set the file time and date for a specific file.
2235 ****************************************************************************/
2236 ibool PMAPI PM_setFileTime(
2237     const char *filename,
2238     ibool gmTime,
2239     PM_time *time)
2240 {
2241     // TODO: Implement this!
2242     return false;
2243 }