1 /****************************************************************************
3 * SciTech OS Portability Manager Library
5 * ========================================================================
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
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.
17 * The Original Code is Copyright (C) 1991-1998 SciTech Software, Inc.
19 * The Initial Developer of the Original Code is SciTech Software, Inc.
20 * All Rights Reserved.
22 * ========================================================================
25 * Environment: 16/32 bit DOS
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.
33 ****************************************************************************/
36 #include "drvlib/os/os.h"
47 #include <sys/nearptr.h>
56 /*--------------------------- Global variables ----------------------------*/
63 #define MAX_RM_BLOCKS 10
68 } rmBlocks[MAX_RM_BLOCKS];
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;
77 static ulong PDB = 0,*pPDB = NULL;
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;
90 /* Memory mapping cache */
92 #define MAX_MEMORY_MAPPINGS 100
98 static mmapping maps[MAX_MEMORY_MAPPINGS] = {0};
99 static int numMaps = 0;
101 /* Page sized block cache */
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;
113 static pageblock *pageBlocks = NULL;
116 /* Start of all page tables in CauseWay */
118 #define CW_PAGE_TABLE_START (1024UL*4096UL*1023UL)
120 /*----------------------------- Implementation ----------------------------*/
122 /* External assembler functions */
124 ulong _ASMAPI _PM_getPDB(void);
125 int _ASMAPI _PM_pagingEnabled(void);
126 void _ASMAPI _PM_VxDCall(VXD_regs *regs,uint off,uint sel);
129 /****************************************************************************
131 Exit function to unload the dynamically loaded VxD
132 ****************************************************************************/
133 static void UnloadVxD(void)
140 r.edx = (uint)VXD_module;
143 r.ds = ((ulong)VXD_module) >> 16;
148 _PM_VxDCall(&r,VXD_loadOff,VXD_loadSel);
151 /****************************************************************************
153 External function to call the PMHELP helper VxD.
154 ****************************************************************************/
155 void PMAPI PM_VxDCall(
158 if (_PM_VXD_sel != 0 || _PM_VXD_off != 0)
159 _PM_VxDCall(regs,_PM_VXD_off,_PM_VXD_sel);
162 /****************************************************************************
164 BCD coded version number of the VxD, or 0 if not loaded (ie: 0x202 - 2.2)
167 This function gets the version number for the VxD that we have connected to.
168 ****************************************************************************/
169 uint PMAPI PMHELP_getVersion(void)
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;
180 return VXD_version = 0;
183 /****************************************************************************
185 Connects to the helper VxD and returns the version number
188 True if the VxD was found and loaded, false otherwise.
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)
200 /* Bail early if we have alread connected */
201 if (VXD_version != -1)
202 return VXD_version != 0;
204 /* Get the static SDDHELP.VXD entry point if available */
207 regs.x.bx = SDDHELP_DeviceID;
210 PM_int386x(0x2F,®s,®s,&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)
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.
224 regs.x.bx = VXDLDR_DeviceID;
227 PM_int386x(0x2F,®s,®s,&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;
234 r.edx = (uint)VXD_name;
238 _PM_VxDCall(&r,VXD_loadOff,VXD_loadSel);
240 return VXD_version = 0;
242 /* Get the dynamic VxD entry point so we can call it */
247 regs.e.edi = (uint)VXD_DDBName;
248 PM_int386x(0x2F,®s,®s,&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)
255 return VXD_version = 0;
259 /****************************************************************************
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)
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.
275 _PM_haveCauseWay = false;
277 PM_int386(0x31,®s,®s);
278 if (regs.x.cflag || !(regs.e.edi & 8)) {
279 /* We are not under DPMI, so now check if CauseWay is active */
281 PM_int386(0x31,®s,®s);
282 if (!regs.x.cflag && regs.e.ecx == 0x43415553 && regs.e.edx == 0x45574159)
283 _PM_haveCauseWay = true;
286 /* Now connect to PMHELP.VXD and initialise MTRR module */
287 if (!PMHELP_connect())
293 /****************************************************************************
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
300 Error code describing the result.
303 Function to enable write combining for the specified region of memory.
304 ****************************************************************************/
305 int PMAPI PM_enableWriteCombine(
313 if (PMHELP_connect()) {
314 memset(®s,0,sizeof(regs));
315 regs.eax = API_NUM(PMHELP_ENABLELFBCOMB);
319 _PM_VxDCall(®s,_PM_VXD_off,_PM_VXD_sel);
322 return MTRR_enableWriteCombine(base,size,type);
324 return PM_MTRR_NOT_SUPPORTED;
328 ibool PMAPI PM_haveBIOSAccess(void)
331 long PMAPI PM_getOSType(void)
334 int PMAPI PM_getModeType(void)
336 #if defined(REALMODE)
345 void PMAPI PM_backslash(char *s)
347 uint pos = strlen(s);
348 if (s[pos-1] != '\\') {
354 void PMAPI PM_setFatalErrorCleanup(
355 void (PMAPIP cleanup)(void))
357 fatalErrorCleanup = cleanup;
360 void PMAPI PM_fatalError(const char *msg)
362 if (fatalErrorCleanup)
364 fprintf(stderr,"%s\n", msg);
368 static void ExitVBEBuf(void)
371 PM_freeRealSeg(VESABuf_ptr);
375 void * PMAPI PM_getVESABuf(uint *len,uint *rseg,uint *roff)
378 /* Allocate a global buffer for communicating with the VESA VBE */
379 if ((VESABuf_ptr = PM_allocRealSeg(VESABuf_len, &VESABuf_rseg, &VESABuf_roff)) == NULL)
384 *rseg = VESABuf_rseg;
385 *roff = VESABuf_roff;
389 int PMAPI PM_int386(int intno, PMREGS *in, PMREGS *out)
393 return PM_int386x(intno,in,out,&sregs);
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.
402 void PMAPI _PM_getRMvect(int intno, long *realisr)
410 PM_int86x(0x21, ®s, ®s, &sregs);
411 *realisr = ((long)sregs.es << 16) | regs.x.bx;
414 void PMAPI _PM_setRMvect(int intno, long realisr)
422 sregs.ds = (int)(realisr >> 16);
423 regs.x.dx = (int)(realisr & 0xFFFF);
424 PM_int86x(0x21, ®s, ®s, &sregs);
427 void PMAPI _PM_addRealModeBlock(void *mem,uint tag)
431 for (i = 0; i < MAX_RM_BLOCKS; i++) {
432 if (rmBlocks[i].p == NULL) {
434 rmBlocks[i].tag = tag;
438 PM_fatalError("To many real mode memory block allocations!");
441 uint PMAPI _PM_findRealModeBlock(void *mem)
445 for (i = 0; i < MAX_RM_BLOCKS; i++) {
446 if (rmBlocks[i].p == mem)
447 return rmBlocks[i].tag;
449 PM_fatalError("Could not find prior real mode memory block allocation!");
453 char * PMAPI PM_getCurrentPath(
457 return getcwd(path,maxLen);
460 char PMAPI PM_getBootDrive(void)
463 const char * PMAPI PM_getVBEAFPath(void)
466 const char * PMAPI PM_getNucleusPath(void)
468 static char path[256];
471 if ((env = getenv("NUCLEUS_PATH")) != NULL)
473 if ((env = getenv("WINBOOTDIR")) != NULL) {
474 /* Running in a Windows 9x DOS box or DOS mode */
476 strcat(path,"\\system\\nucleus");
479 if ((env = getenv("SystemRoot")) != NULL) {
480 /* Running in an NT/2K DOS box */
482 strcat(path,"\\system32\\nucleus");
485 return "c:\\nucleus";
488 const char * PMAPI PM_getNucleusConfigPath(void)
490 static char path[256];
491 strcpy(path,PM_getNucleusPath());
493 strcat(path,"config");
497 const char * PMAPI PM_getUniqueID(void)
500 const char * PMAPI PM_getMachineName(void)
503 int PMAPI PM_kbhit(void)
508 int PMAPI PM_getch(void)
513 PM_HWND PMAPI PM_openConsole(PM_HWND hwndUser,int device,int xRes,int yRes,int bpp,ibool fullScreen)
515 /* Not used for DOS */
525 int PMAPI PM_getConsoleStateSize(void)
527 return sizeof(DOS_stateBuf);
530 void PMAPI PM_saveConsoleState(void *stateBuf,PM_HWND hwndConsole)
533 DOS_stateBuf *sb = stateBuf;
535 /* Save the old video mode state */
537 PM_int86(0x10,®s,®s);
538 sb->oldMode = regs.h.al & 0x7F;
539 sb->old50Lines = false;
540 if (sb->oldMode == 0x3) {
544 PM_int86(0x10,®s,®s);
545 sb->old50Lines = (regs.h.dl == 42 || regs.h.dl == 49);
550 void PMAPI PM_setSuspendAppCallback(int (_ASMAPIP saveState)(int flags))
552 /* Not used for DOS */
556 void PMAPI PM_restoreConsoleState(const void *stateBuf,PM_HWND hwndConsole)
559 const DOS_stateBuf *sb = stateBuf;
561 /* Retore 50 line mode if set */
562 if (sb->old50Lines) {
565 PM_int86(0x10,®s,®s);
570 void PMAPI PM_closeConsole(PM_HWND hwndConsole)
572 /* Not used for DOS */
576 void PMAPI PM_setOSCursorLocation(int x,int y)
578 uchar *_biosPtr = PM_getBIOSPointer();
579 PM_setByte(_biosPtr+0x50,x);
580 PM_setByte(_biosPtr+0x51,y);
583 void PMAPI PM_setOSScreenWidth(int width,int height)
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);
590 PM_setWord(_biosPtr+0x60,0x0607);
591 PM_setByte(_biosPtr+0x85,0x08);
594 PM_setWord(_biosPtr+0x60,0x0D0E);
595 PM_setByte(_biosPtr+0x85,0x016);
599 void * PMAPI PM_mallocShared(long size)
601 return PM_malloc(size);
604 void PMAPI PM_freeShared(void *ptr)
609 #define GetRMVect(intno,isr) *(isr) = ((ulong*)rmZeroPtr)[intno]
610 #define SetRMVect(intno,isr) ((ulong*)rmZeroPtr)[intno] = (isr)
612 ibool PMAPI PM_doBIOSPOST(
618 static int firstTime = true;
619 static uchar *rmZeroPtr;
620 long Current10,Current6D,Current42;
624 /* Create a zero memory mapping for us to use */
626 rmZeroPtr = PM_mapPhysicalAddr(0,0x7FFF,true);
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.
636 * For OS'es that do virtual memory, and remapping of 0xC0000
637 * physical (perhaps a copy on write mapping) should be all that
643 /* Save current handlers of int 10h and 6Dh */
644 GetRMVect(0x10,&Current10);
645 GetRMVect(0x6D,&Current6D);
647 /* POST the secondary BIOS */
648 GetRMVect(0x42,&Current42);
649 SetRMVect(0x10,Current42); /* Restore int 10h to STD-BIOS */
651 PM_callRealMode(0xC000,0x0003,®s,&sregs);
653 /* Restore current handlers */
654 SetRMVect(0x10,Current10);
655 SetRMVect(0x6D,Current6D);
657 /* Second the primary BIOS mappin 1:1 for 0xC0000 physical */
658 if (BIOSPhysAddr != 0xC0000L) {
659 /* DOS does not support this */
665 void PMAPI PM_sleep(ulong milliseconds)
667 ulong microseconds = milliseconds * 1000L;
671 while (LZTimerLapExt(&tm) < microseconds)
676 int PMAPI PM_getCOMPort(int port)
679 case 0: return 0x3F8;
680 case 1: return 0x2F8;
685 int PMAPI PM_getLPTPort(int port)
688 case 0: return 0x3BC;
689 case 1: return 0x378;
690 case 2: return 0x278;
695 PM_MODULE PMAPI PM_loadLibrary(
696 const char *szDLLName)
702 void * PMAPI PM_getProcAddress(
704 const char *szProcName)
711 void PMAPI PM_freeLibrary(
717 int PMAPI PM_setIOPL(
723 /****************************************************************************
725 Internal function to convert the find data to the generic interface.
726 ****************************************************************************/
727 static void convertFindData(
728 PM_findData *findData,
731 ulong dwSize = findData->dwSize;
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;
750 #define FIND_MASK (_A_RDONLY | _A_ARCH | _A_SUBDIR | _A_HIDDEN | _A_SYSTEM)
752 /****************************************************************************
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)
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);
768 return PM_FILE_INVALID;
771 /****************************************************************************
773 Function to find the next file matching a search criteria in a directory.
774 ****************************************************************************/
775 ibool PMAPI PM_findNextFile(
777 PM_findData *findData)
779 struct find_t *blk = handle;
781 if (_dos_findnext(blk) == 0) {
782 convertFindData(findData,blk);
788 /****************************************************************************
790 Function to close the find process
791 ****************************************************************************/
792 void PMAPI PM_findClose(
798 /****************************************************************************
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
810 ****************************************************************************/
811 ibool PMAPI PM_driveValid(
815 regs.h.dl = (uchar)(drive - 'A' + 1);
816 regs.h.ah = 0x36; // Get disk information service
817 PM_int86(0x21,®s,®s);
818 return regs.x.ax != 0xFFFF; // AX = 0xFFFF if disk is invalid
821 /****************************************************************************
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(
832 uint oldDrive,maxDrives;
833 _dos_getdrive(&oldDrive);
834 _dos_setdrive(drive,&maxDrives);
836 _dos_setdrive(oldDrive,&maxDrives);
839 /****************************************************************************
841 Function to change the file attributes for a specific file.
842 ****************************************************************************/
843 void PMAPI PM_setFileAttr(
844 const char *filename,
847 #if defined(TNT) && defined(_MSC_VER)
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);
862 if (attrib & PM_FILE_READONLY)
864 if (attrib & PM_FILE_ARCHIVE)
866 if (attrib & PM_FILE_HIDDEN)
868 if (attrib & PM_FILE_SYSTEM)
870 _dos_setfileattr(filename,attr);
874 /****************************************************************************
876 Function to create a directory.
877 ****************************************************************************/
878 ibool PMAPI PM_mkdir(
879 const char *filename)
882 return mkdir(filename,S_IRUSR) == 0;
884 return mkdir(filename) == 0;
888 /****************************************************************************
890 Function to remove a directory.
891 ****************************************************************************/
892 ibool PMAPI PM_rmdir(
893 const char *filename)
895 return rmdir(filename) == 0;
898 /*-------------------------------------------------------------------------*/
899 /* Generic DPMI routines common to 16/32 bit code */
900 /*-------------------------------------------------------------------------*/
903 ulong PMAPI DPMI_mapPhysicalToLinear(ulong physAddr,ulong limit)
907 ulong baseAddr,baseOfs,roundedLimit;
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.
912 if (physAddr < 0x100000L)
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.
921 for (i = 0; i < numMaps; i++) {
922 if (maps[i].physical == physAddr && maps[i].limit == limit)
923 return maps[i].linear;
926 /* Find a free slot in our physical memory mapping table */
927 for (i = 0; i < numMaps; i++) {
928 if (maps[i].limit == 0)
933 if (i == MAX_MEMORY_MAPPINGS)
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
943 baseOfs = physAddr & 4095;
944 baseAddr = physAddr & ~4095;
945 roundedLimit = ((limit+baseOfs+1+4095) & ~4095)-1;
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);
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;
960 int PMAPI DPMI_setSelectorBase(ushort sel,ulong linAddr)
964 r.x.ax = 7; /* DPMI set selector base address */
966 r.x.cx = linAddr >> 16;
967 r.x.dx = linAddr & 0xFFFF;
968 PM_int386(0x31, &r, &r);
974 ulong PMAPI DPMI_getSelectorBase(ushort sel)
978 r.x.ax = 6; /* DPMI get selector base address */
980 PM_int386(0x31, &r, &r);
981 return ((ulong)r.x.cx << 16) + r.x.dx;
984 int PMAPI DPMI_setSelectorLimit(ushort sel,ulong limit)
988 r.x.ax = 8; /* DPMI set selector limit */
990 r.x.cx = limit >> 16;
991 r.x.dx = limit & 0xFFFF;
992 PM_int386(0x31, &r, &r);
998 uint PMAPI DPMI_createSelector(ulong base,ulong limit)
1003 /* Allocate 1 descriptor */
1006 PM_int386(0x31, &r, &r);
1007 if (r.x.cflag) return 0;
1010 /* Set the descriptor access rights (for a 32 bit page granular
1013 if (limit >= 0x10000L) {
1017 PM_int386(0x31, &r, &r);
1020 /* Map physical memory and create selector */
1021 if ((base = DPMI_mapPhysicalToLinear(base,limit)) == 0xFFFFFFFFUL)
1023 if (!DPMI_setSelectorBase(sel,base))
1025 if (!DPMI_setSelectorLimit(sel,limit))
1030 void PMAPI DPMI_freeSelector(uint sel)
1036 PM_int386(0x31, &r, &r);
1039 int PMAPI DPMI_lockLinearPages(ulong linear,ulong len)
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);
1052 int PMAPI DPMI_unlockLinearPages(ulong linear,ulong len)
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);
1065 /****************************************************************************
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(
1080 int startPDB,endPDB,iPDB,startPage,endPage,start,end,iPage;
1081 ulong andMask,orMask,pageTable,*pPageTable;
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)
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;
1101 pPDB = (ulong*)DPMI_mapPhysicalToLinear(PDB,0xFFF);
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;
1122 void * PMAPI DPMI_mapPhysicalAddr(ulong base,ulong limit,ibool isCached)
1128 /* Get the base address for the default DS selector */
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).
1138 /* Map the memory to a linear address using DPMI function 0x800 */
1139 if ((linAddr = DPMI_mapPhysicalToLinear(base,limit)) == 0xFFFFFFFF) {
1140 if (base >= 0x100000)
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.
1151 /* Now expand the default DS selector to 4Gb so we can access it */
1152 if (!DPMI_setSelectorLimit(sregs.ds,0xFFFFFFFFUL))
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.
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.
1164 if (DSBaseAddr == 0)
1165 PM_adjustPageTables(linAddr,limit,isCached);
1167 /* Now return the base address of the memory into the default DS */
1168 return (void*)(linAddr - DSBaseAddr);
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.
1179 static uchar int6AHandler[] = {
1180 0x00,0x00,0x00,0x00, /* __PMODE_callReal variable */
1182 0x2E,0xFF,0x1E,0x00,0x00, /* call [cs:__PMODE_callReal] */
1185 static uchar *crPtr = NULL; /* Pointer to of int 6A handler */
1186 static uint crRSeg,crROff; /* Real mode seg:offset of handler */
1188 void PMAPI PM_callRealMode(uint seg,uint off, RMREGS *in,
1195 /* Allocate and copy the memory block only once */
1196 crPtr = PM_allocRealSeg(sizeof(int6AHandler), &crRSeg, &crROff);
1197 memcpy(crPtr,int6AHandler,sizeof(int6AHandler));
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);
1213 #endif /* !REALMODE */
1215 /****************************************************************************
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(
1228 uint roundedSize = (size + 4 + 0xFFF) & ~0xFFF;
1229 PM_lockHandle lh; /* Unused in DOS */
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.
1237 memset(®s,0,sizeof(regs));
1238 regs.eax = API_NUM(PMHELP_ALLOCLOCKED);
1240 regs.ecx = (ulong)physAddr;
1241 regs.edx = contiguous | (below16Meg << 8);
1242 _PM_VxDCall(®s,_PM_VXD_off,_PM_VXD_sel);
1243 return (void*)regs.eax;
1246 /* If the memory is not contiguous, we simply need to allocate it
1247 * using regular memory allocation services, and lock it down
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.
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.
1260 p = PM_malloc(roundedSize);
1263 p = PM_allocRealSeg(roundedSize,&r_seg,&r_off);
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);
1274 /* Disable caching for the memory since it is probably a DMA buffer */
1276 PM_adjustPageTables((ulong)roundedP,size-1,false);
1281 /****************************************************************************
1283 Free a block of locked memory.
1284 ****************************************************************************/
1285 void PMAPI PM_freeLockedMem(void *p,uint size,ibool contiguous)
1289 PM_lockHandle lh; /* Unused in DOS */
1294 memset(®s,0,sizeof(regs));
1295 regs.eax = API_NUM(PMHELP_FREELOCKED);
1296 regs.ebx = (ulong)p;
1298 regs.edx = contiguous;
1299 _PM_VxDCall(®s,_PM_VXD_off,_PM_VXD_sel);
1302 PM_unlockDataPages(p,size,&lh);
1304 free(*((void**)((uchar*)p + size)));
1307 PM_freeRealSeg(*((void**)((char*)p + size)));
1311 /****************************************************************************
1313 Allocates a new block of pages for the page block manager.
1314 ****************************************************************************/
1315 static pageblock *PM_addNewPageBlock(void)
1318 pageblock *newBlock;
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)
1325 newBlock->prev = NULL;
1326 newBlock->next = pageBlocks;
1328 pageBlocks->prev = newBlock;
1329 pageBlocks = newBlock;
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;
1343 /****************************************************************************
1345 Allocates a page aligned and page sized block of memory
1346 ****************************************************************************/
1347 void * PMAPI PM_allocPage(
1354 PM_lockHandle lh; /* Unused in DOS */
1356 /* Call the helper VxD for this service if we are running in a DOS box */
1358 memset(®s,0,sizeof(regs));
1359 regs.eax = API_NUM(PMHELP_ALLOCPAGE);
1361 _PM_VxDCall(®s,_PM_VXD_off,_PM_VXD_sel);
1362 return (void*)regs.eax;
1365 /* Scan the block list looking for any free blocks. Allocate a new
1366 * page block if no free blocks are found.
1368 for (block = pageBlocks; block != NULL; block = block->next) {
1369 if (block->freeCount)
1372 if (block == NULL && (block = PM_addNewPageBlock()) == NULL)
1375 p = block->freeList;
1376 block->freeList = FREELIST_NEXT(p);
1378 PM_lockDataPages(p,PM_PAGE_SIZE,&lh);
1385 /****************************************************************************
1387 Free a page aligned and page sized block of memory
1388 ****************************************************************************/
1389 void PMAPI PM_freePage(
1396 /* Call the helper VxD for this service if we are running in a DOS box */
1398 memset(®s,0,sizeof(regs));
1399 regs.eax = API_NUM(PMHELP_FREEPAGE);
1400 regs.ebx = (ulong)p;
1401 _PM_VxDCall(®s,_PM_VXD_off,_PM_VXD_sel);
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)
1410 CHECK(block != NULL);
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.
1419 if (block == pageBlocks) {
1420 /* Delete from head */
1421 pageBlocks = block->next;
1423 block->next->prev = NULL;
1426 /* Delete from middle of list */
1427 CHECK(block->prev != NULL);
1428 block->prev->next = block->next;
1430 block->next->prev = block->prev;
1439 /*-------------------------------------------------------------------------*/
1440 /* DOS Real Mode support. */
1441 /*-------------------------------------------------------------------------*/
1446 #define MK_FP(s,o) ( (void far *)( ((ulong)(s) << 16) + \
1450 void * PMAPI PM_mapRealPointer(uint r_seg,uint r_off)
1451 { return MK_FP(r_seg,r_off); }
1453 void * PMAPI PM_getBIOSPointer(void)
1455 return MK_FP(0x40,0);
1458 void * PMAPI PM_getA0000Pointer(void)
1460 return MK_FP(0xA000,0);
1463 void * PMAPI PM_mapPhysicalAddr(ulong base,ulong limit,ibool isCached)
1465 uint sel = base >> 4;
1466 uint off = base & 0xF;
1468 return MK_FP(sel,off);
1471 void PMAPI PM_freePhysicalAddr(void *ptr,ulong limit)
1474 ulong PMAPI PM_getPhysicalAddr(void *p)
1476 return ((((ulong)p >> 16) << 4) + (ushort)p);
1479 ibool PMAPI PM_getPhysicalAddrRange(void *p,ulong length,ulong *physAddress)
1482 void * PMAPI PM_mapToProcess(void *base,ulong limit)
1483 { return (void*)base; }
1485 void * PMAPI PM_allocRealSeg(uint size,uint *r_seg,uint *r_off)
1487 /* Call malloc() to allocate the memory for us */
1488 void *p = PM_malloc(size);
1494 void PMAPI PM_freeRealSeg(void *mem)
1496 if (mem) PM_free(mem);
1499 int PMAPI PM_int86(int intno, RMREGS *in, RMREGS *out)
1501 return PM_int386(intno,in,out);
1504 int PMAPI PM_int86x(int intno, RMREGS *in, RMREGS *out,
1507 return PM_int386x(intno,in,out,sregs);
1510 void PMAPI PM_availableMemory(ulong *physical,ulong *total)
1516 PM_int86(0x21,®s,®s);
1517 *physical = *total = regs.x.bx * 16UL;
1522 /*-------------------------------------------------------------------------*/
1523 /* Phar Lap TNT DOS Extender support. */
1524 /*-------------------------------------------------------------------------*/
1528 #include <pldos32.h>
1529 #include <pharlap.h>
1532 static uchar *zeroPtr = NULL;
1534 void * PMAPI PM_getBIOSPointer(void)
1537 zeroPtr = PM_mapPhysicalAddr(0,0xFFFFF,true);
1538 return (void*)(zeroPtr + 0x400);
1541 void * PMAPI PM_getA0000Pointer(void)
1543 static void *bankPtr;
1545 bankPtr = PM_mapPhysicalAddr(0xA0000,0xFFFF,true);
1549 void * PMAPI PM_mapPhysicalAddr(ulong base,ulong limit,ibool isCached)
1554 ulong baseAddr,baseOfs,newLimit;
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).
1563 if (VXD_version == -1)
1566 memset(®s,0,sizeof(regs));
1567 regs.eax = API_NUM(PMHELP_MAPPHYS);
1570 regs.edx = isCached;
1571 _PM_VxDCall(®s,_PM_VXD_off,_PM_VXD_sel);
1572 return (void*)regs.eax;
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
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);
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.
1591 return DPMI_mapPhysicalAddr(base,limit,isCached);
1594 return (void*)(offset + baseOfs);
1598 void PMAPI PM_freePhysicalAddr(void *ptr,ulong limit)
1602 ulong PMAPI PM_getPhysicalAddr(void *p)
1603 { return 0xFFFFFFFFUL; }
1605 ibool PMAPI PM_getPhysicalAddrRange(void *p,ulong length,ulong *physAddress)
1608 void * PMAPI PM_mapToProcess(void *base,ulong limit)
1609 { return (void*)base; }
1611 void * PMAPI PM_mapRealPointer(uint r_seg,uint r_off)
1614 zeroPtr = PM_mapPhysicalAddr(0,0xFFFFF);
1615 return (void*)(zeroPtr + MK_PHYS(r_seg,r_off));
1618 void * PMAPI PM_allocRealSeg(uint size,uint *r_seg,uint *r_off)
1623 if (_dx_real_alloc((size + 0xF) >> 4,&addr,&t) != 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);
1632 void PMAPI PM_freeRealSeg(void *mem)
1634 if (mem) _dx_real_free(_PM_findRealModeBlock(mem));
1637 #define INDPMI(reg) rmregs.reg = regs->reg
1638 #define OUTDPMI(reg) regs->reg = rmregs.reg
1640 void PMAPI DPMI_int86(int intno, DPMI_regs *regs)
1644 memset(&rmregs, 0, sizeof(rmregs));
1645 INDPMI(eax); INDPMI(ebx); INDPMI(ecx); INDPMI(edx); INDPMI(esi); INDPMI(edi);
1647 _dx_real_int(intno,&rmregs);
1649 OUTDPMI(eax); OUTDPMI(ebx); OUTDPMI(ecx); OUTDPMI(edx); OUTDPMI(esi); OUTDPMI(edi);
1650 regs->flags = rmregs.flags;
1653 #define IN(reg) rmregs.reg = in->e.reg
1654 #define OUT(reg) out->e.reg = rmregs.reg
1656 int PMAPI PM_int86(int intno, RMREGS *in, RMREGS *out)
1660 memset(&rmregs, 0, sizeof(rmregs));
1661 IN(eax); IN(ebx); IN(ecx); IN(edx); IN(esi); IN(edi);
1663 _dx_real_int(intno,&rmregs);
1665 OUT(eax); OUT(ebx); OUT(ecx); OUT(edx); OUT(esi); OUT(edi);
1666 out->x.cflag = rmregs.flags & 0x1;
1670 int PMAPI PM_int86x(int intno, RMREGS *in, RMREGS *out,
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;
1680 _dx_real_int(intno,&rmregs);
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;
1691 void PMAPI PM_availableMemory(ulong *physical,ulong *total)
1696 r.x.ax = 0x2520; /* Get free memory info */
1698 r.e.edx = (uint)data;
1699 PM_int386(0x21, &r, &r);
1700 *physical = data[21] * 4096;
1701 *total = data[23] * 4096;
1706 /*-------------------------------------------------------------------------*/
1707 /* Symantec C++ DOSX and FlashTek X-32/X-32VM support */
1708 /*-------------------------------------------------------------------------*/
1710 #if defined(DOSX) || defined(X32VM)
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
1719 extern void *_x386_zero_base_ptr;
1722 void * PMAPI PM_mapRealPointer(uint r_seg,uint r_off)
1724 return (void*)((ulong)_x386_zero_base_ptr + MK_PHYS(r_seg,r_off));
1727 void * PMAPI PM_allocRealSeg(uint size,uint *r_seg,uint *r_off)
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 */
1735 return 0; /* Could not allocate the memory */
1738 return PM_mapRealPointer(*r_seg,*r_off);
1741 void PMAPI PM_freeRealSeg(void *mem)
1743 /* Cannot de-allocate this memory */
1761 #define IN(reg) regs.e.reg = in->e.reg
1762 #define OUT(reg) out->e.reg = regs.e.reg
1764 int PMAPI PM_int86(int intno, RMREGS *in, RMREGS *out)
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);
1775 regs.e.edx = (uint)(&rmregs);
1776 PM_segread(&pmsregs);
1777 PM_int386x(0x21,®s,®s,&pmsregs);
1779 OUT(eax); OUT(ebx); OUT(ecx); OUT(esi); OUT(edi);
1780 out->x.dx = rmregs.edx;
1781 out->x.cflag = regs.x.cflag;
1785 int PMAPI PM_int86x(int intno, RMREGS *in, RMREGS *out, RMSREGS *sregs)
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);
1798 regs.e.edx = (uint)(&rmregs);
1799 PM_segread(&pmsregs);
1800 PM_int386x(0x21,®s,®s,&pmsregs);
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;
1810 void * PMAPI PM_getBIOSPointer(void)
1812 return (void*)((ulong)_x386_zero_base_ptr + 0x400);
1815 void * PMAPI PM_getA0000Pointer(void)
1817 return (void*)((ulong)_x386_zero_base_ptr + 0xA0000);
1820 void * PMAPI PM_mapPhysicalAddr(ulong base,ulong limit,ibool isCached)
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).
1830 if (VXD_version == -1)
1833 memset(®s,0,sizeof(regs));
1834 regs.eax = API_NUM(PMHELP_MAPPHYS);
1837 regs.edx = isCached;
1838 _PM_VxDCall(®s,_PM_VXD_off,_PM_VXD_sel);
1839 return (void*)regs.eax;
1842 if (base > 0x100000)
1843 return _x386_map_physical_address((void*)base,limit);
1844 return (void*)((ulong)_x386_zero_base_ptr + base);
1847 void PMAPI PM_freePhysicalAddr(void *ptr,ulong limit)
1849 /* Mapping cannot be freed */
1852 ulong PMAPI PM_getPhysicalAddr(void *p)
1853 { return 0xFFFFFFFFUL; }
1855 ibool PMAPI PM_getPhysicalAddrRange(void *p,ulong length,ulong *physAddress)
1858 void * PMAPI PM_mapToProcess(void *base,ulong limit)
1859 { return (void*)base; }
1861 ulong _cdecl _X32_getPhysMem(void);
1863 void PMAPI PM_availableMemory(ulong *physical,ulong *total)
1867 /* Get total memory available, including virtual memory */
1869 PM_int386(0x21,®s,®s);
1870 *total = regs.e.eax;
1872 /* Get physical memory available */
1873 *physical = _X32_getPhysMem();
1874 if (*physical > *total)
1880 /*-------------------------------------------------------------------------*/
1881 /* Borland's DPMI32, Watcom DOS4GW and DJGPP DPMI support routines */
1882 /*-------------------------------------------------------------------------*/
1884 #if defined(DPMI32) || defined(DOS4GW) || defined(DJGPP)
1886 void * PMAPI PM_getBIOSPointer(void)
1888 return PM_mapPhysicalAddr(0x400,0xFFFF,true);
1891 void * PMAPI PM_getA0000Pointer(void)
1893 return PM_mapPhysicalAddr(0xA0000,0xFFFF,true);
1896 void * PMAPI PM_mapPhysicalAddr(ulong base,ulong limit,ibool isCached)
1901 /* Enable near pointers for DJGPP V2 */
1902 __djgpp_nearptr_enable();
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).
1910 if (VXD_version == -1)
1913 memset(®s,0,sizeof(regs));
1914 regs.eax = API_NUM(PMHELP_MAPPHYS);
1917 regs.edx = isCached;
1918 _PM_VxDCall(®s,_PM_VXD_off,_PM_VXD_sel);
1919 return (void*)regs.eax;
1921 return DPMI_mapPhysicalAddr(base,limit,isCached);
1924 void PMAPI PM_freePhysicalAddr(void *ptr,ulong limit)
1926 /* Mapping cannot be freed */
1931 ulong PMAPI PM_getPhysicalAddr(void *p)
1934 if (!PM_getPhysicalAddrRange(p,1,&physAddr))
1936 return physAddr | ((ulong)p & 0xFFF);
1939 ibool PMAPI PM_getPhysicalAddrRange(
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.
1953 memset(®s,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(®s,_PM_VXD_off,_PM_VXD_sel);
1962 /* Find base address for default DS selector */
1964 DSBaseAddr = DPMI_getSelectorBase(sregs.ds);
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.
1972 if (_PM_pagingEnabled() == 0) {
1974 ulong linAddr = (ulong)p;
1976 /* When paging is disabled physical=linear */
1977 for (count = (length+0xFFF) >> 12; count > 0; count--) {
1978 *physAddress++ = linAddr;
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;
1988 pPDB = (ulong*)DPMI_mapPhysicalToLinear(PDB,0xFFF);
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);
2009 void * PMAPI PM_mapToProcess(void *base,ulong limit)
2015 void * PMAPI PM_mapRealPointer(uint r_seg,uint r_off)
2017 static uchar *zeroPtr = NULL;
2020 zeroPtr = PM_mapPhysicalAddr(0,0xFFFFF,true);
2021 return (void*)(zeroPtr + MK_PHYS(r_seg,r_off));
2024 void * PMAPI PM_allocRealSeg(uint size,uint *r_seg,uint *r_off)
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);
2033 return NULL; /* DPMI call failed */
2034 *r_seg = r.x.ax; /* Real mode segment */
2036 p = PM_mapRealPointer(*r_seg,*r_off);
2037 _PM_addRealModeBlock(p,r.x.dx);
2041 void PMAPI PM_freeRealSeg(void *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);
2052 static DPMI_handler_t DPMI_int10 = NULL;
2054 void PMAPI DPMI_setInt10Handler(DPMI_handler_t handler)
2056 DPMI_int10 = handler;
2059 void PMAPI DPMI_int86(int intno, DPMI_regs *regs)
2064 if (intno == 0x10 && DPMI_int10) {
2065 if (DPMI_int10(regs))
2069 r.x.ax = 0x300; /* DPMI issue real interrupt */
2074 r.e.edi = (uint)regs;
2075 PM_int386x(0x31, &r, &r, &sr); /* Issue the interrupt */
2078 #define IN(reg) rmregs.reg = in->e.reg
2079 #define OUT(reg) out->e.reg = rmregs.reg
2081 int PMAPI PM_int86(int intno, RMREGS *in, RMREGS *out)
2085 memset(&rmregs, 0, sizeof(rmregs));
2086 IN(eax); IN(ebx); IN(ecx); IN(edx); IN(esi); IN(edi);
2088 DPMI_int86(intno,&rmregs); /* DPMI issue real interrupt */
2090 OUT(eax); OUT(ebx); OUT(ecx); OUT(edx); OUT(esi); OUT(edi);
2091 out->x.cflag = rmregs.flags & 0x1;
2095 int PMAPI PM_int86x(int intno, RMREGS *in, RMREGS *out,
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;
2105 DPMI_int86(intno,&rmregs); /* DPMI issue real interrupt */
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;
2119 uint LargestBlockAvail;
2120 uint MaxUnlockedPage;
2121 uint LargestLockablePage;
2123 uint NumFreePagesAvail;
2124 uint NumPhysicalPagesFree;
2125 uint TotalPhysicalPages;
2126 uint FreeLinAddrSpace;
2127 uint SizeOfPageFile;
2133 void PMAPI PM_availableMemory(ulong *physical,ulong *total)
2140 r.x.ax = 0x500; /* DPMI get free memory info */
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)
2154 /****************************************************************************
2156 Call the VBE/Core software interrupt to change display banks.
2157 ****************************************************************************/
2158 void PMAPI PM_setBankA(
2162 memset(®s, 0, sizeof(regs));
2166 DPMI_int86(0x10,®s);
2169 /****************************************************************************
2171 Call the VBE/Core software interrupt to change display banks.
2172 ****************************************************************************/
2173 void PMAPI PM_setBankAB(
2177 memset(®s, 0, sizeof(regs));
2181 DPMI_int86(0x10,®s);
2185 DPMI_int86(0x10,®s);
2188 /****************************************************************************
2190 Call the VBE/Core software interrupt to change display start address.
2191 ****************************************************************************/
2192 void PMAPI PM_setCRTStart(
2198 memset(®s, 0, sizeof(regs));
2203 DPMI_int86(0x10,®s);
2208 /****************************************************************************
2210 Function to get the file attributes for a specific file.
2211 ****************************************************************************/
2212 uint PMAPI PM_getFileAttr(
2213 const char *filename)
2215 // TODO: Implement this!
2219 /****************************************************************************
2221 Function to get the file time and date for a specific file.
2222 ****************************************************************************/
2223 ibool PMAPI PM_getFileTime(
2224 const char *filename,
2228 // TODO: Implement this!
2232 /****************************************************************************
2234 Function to set the file time and date for a specific file.
2235 ****************************************************************************/
2236 ibool PMAPI PM_setFileTime(
2237 const char *filename,
2241 // TODO: Implement this!