DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/ints/xms.cpp
00001 /*
00002  *  Copyright (C) 2002-2015  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017  */
00018 
00019 
00020 #include <stdlib.h>
00021 #include <string.h>
00022 #include <stddef.h>
00023 #include "dosbox.h"
00024 #include "callback.h"
00025 #include "mem.h"
00026 #include "regs.h"
00027 #include "dos_inc.h"
00028 #include "setup.h"
00029 #include "inout.h"
00030 #include "xms.h"
00031 #include "bios.h"
00032 #include "cpu.h"
00033 #include "control.h"
00034 
00035 #include <algorithm>
00036 
00037 #define XMS_HANDLES                                                     50u             /* 50 XMS Memory Blocks */ 
00038 #define XMS_VERSION                                             0x0300u /* version 3.00 */
00039 #define XMS_DRIVER_VERSION                                      0x0301u /* my driver version 3.01 */
00040 
00041 #define XMS_GET_VERSION                                         0x00u
00042 #define XMS_ALLOCATE_HIGH_MEMORY                        0x01u
00043 #define XMS_FREE_HIGH_MEMORY                            0x02u
00044 #define XMS_GLOBAL_ENABLE_A20                           0x03u
00045 #define XMS_GLOBAL_DISABLE_A20                          0x04u
00046 #define XMS_LOCAL_ENABLE_A20                            0x05u
00047 #define XMS_LOCAL_DISABLE_A20                           0x06u
00048 #define XMS_QUERY_A20                                           0x07u
00049 #define XMS_QUERY_FREE_EXTENDED_MEMORY          0x08u
00050 #define XMS_ALLOCATE_EXTENDED_MEMORY            0x09u
00051 #define XMS_FREE_EXTENDED_MEMORY                        0x0au
00052 #define XMS_MOVE_EXTENDED_MEMORY_BLOCK          0x0bu
00053 #define XMS_LOCK_EXTENDED_MEMORY_BLOCK          0x0cu
00054 #define XMS_UNLOCK_EXTENDED_MEMORY_BLOCK        0x0du
00055 #define XMS_GET_EMB_HANDLE_INFORMATION          0x0eu
00056 #define XMS_RESIZE_EXTENDED_MEMORY_BLOCK        0x0fu
00057 #define XMS_ALLOCATE_UMB                                        0x10u
00058 #define XMS_DEALLOCATE_UMB                                      0x11u
00059 #define XMS_QUERY_ANY_FREE_MEMORY                       0x88u
00060 #define XMS_ALLOCATE_ANY_MEMORY                         0x89u
00061 #define XMS_GET_EMB_HANDLE_INFORMATION_EXT      0x8eu
00062 #define XMS_RESIZE_ANY_EXTENDED_MEMORY_BLOCK 0x8fu
00063 
00064 #define XMS_FUNCTION_NOT_IMPLEMENTED            0x80u
00065 #define HIGH_MEMORY_NOT_EXIST                           0x90u
00066 #define HIGH_MEMORY_IN_USE                                      0x91u
00067 #define HIGH_MEMORY_NOT_BIG_ENOUGH                      0x92u
00068 #define HIGH_MEMORY_NOT_ALLOCATED                       0x93u
00069 #define XMS_OUT_OF_SPACE                                        0xa0u
00070 #define XMS_OUT_OF_HANDLES                                      0xa1u
00071 #define XMS_INVALID_HANDLE                                      0xa2u
00072 #define XMS_INVALID_SOURCE_HANDLE                       0xa3u
00073 #define XMS_INVALID_SOURCE_OFFSET                       0xa4u
00074 #define XMS_INVALID_DEST_HANDLE                         0xa5u
00075 #define XMS_INVALID_DEST_OFFSET                         0xa6u
00076 #define XMS_INVALID_LENGTH                                      0xa7u
00077 #define XMS_BLOCK_NOT_LOCKED                            0xaau
00078 #define XMS_BLOCK_LOCKED                                        0xabu
00079 #define UMB_ONLY_SMALLER_BLOCK                          0xb0u
00080 #define UMB_NO_BLOCKS_AVAILABLE                         0xb1u
00081 
00082 bool DOS_IS_IN_HMA();
00083 
00084 extern Bitu rombios_minimum_location;
00085 
00086 Bitu xms_hma_minimum_alloc = 0;
00087 bool xms_hma_exists = true;
00088 bool xms_hma_application_has_control = false;
00089 bool xms_hma_alloc_non_dos_kernel_control = true;
00090 
00091 struct XMS_Block {
00092         Bitu    size;
00093         MemHandle mem;
00094         Bit8u   locked;
00095         bool    free;
00096 };
00097 
00098 #ifdef _MSC_VER
00099 #pragma pack (1)
00100 #endif
00101 struct XMS_MemMove{
00102         Bit32u length;
00103         Bit16u src_handle;
00104         union {
00105                 RealPt realpt;
00106                 Bit32u offset;
00107         } src;
00108         Bit16u dest_handle;
00109         union {
00110                 RealPt realpt;
00111                 Bit32u offset;
00112         } dest;
00113 
00114 } GCC_ATTRIBUTE(packed);
00115 #ifdef _MSC_VER
00116 #pragma pack ()
00117 #endif
00118 
00119 static bool xms_global_enable = false;
00120 static int xms_local_enable_count = 0;
00121 
00122 void DOS_Write_HMA_CPM_jmp(void);
00123 
00124 Bitu XMS_EnableA20(bool enable) {
00125         Bit8u val;
00126 
00127     if (IS_PC98_ARCH) {
00128         // NEC PC-98: Unmask (enable) A20 by writing to port 0xF2.
00129         //            Mask (disable) A20 by writing to port 0xF6.
00130         IO_Write(0xF6,enable ? 0x02 : 0x03); /* 0000 001x  x = mask A20 */
00131     }
00132     else {
00133         // IBM PC/AT: Port 0x92, bit 1, set if A20 enabled
00134         val = IO_Read(0x92);
00135         if (enable) IO_Write(0x92,val | 2);
00136         else            IO_Write(0x92,val & ~2);
00137     }
00138 
00139         return 0;
00140 }
00141 
00142 Bitu XMS_GetEnabledA20(void) {
00143     if (IS_PC98_ARCH) // NEC PC-98: Port 0xF2, bit 0, cleared if A20 enabled (set if A20 masked)
00144             return (IO_Read(0xF2)&1) == 0;
00145 
00146     // IBM PC/AT: Port 0x92, bit 1, set if A20 enabled
00147         return (IO_Read(0x92)&2)>0;
00148 }
00149 
00150 static RealPt xms_callback;
00151 static bool umb_available = false;
00152 static bool umb_init = false;
00153 
00154 static XMS_Block xms_handles[XMS_HANDLES];
00155 
00156 Bitu XMS_GetTotalHandles(void) {
00157     return XMS_HANDLES;
00158 }
00159 
00160 bool XMS_GetHandleInfo(Bitu &phys_location,Bitu &size,Bitu &lockcount,bool &free,Bitu handle) {
00161     if (handle != 0 && handle < XMS_HANDLES) {
00162         phys_location = 0;
00163         lockcount = 0;
00164         free = true;
00165         size = 0;
00166 
00167         auto &x = xms_handles[handle];
00168 
00169         if (!x.free) {
00170             free = false;
00171             size = x.size;
00172             lockcount = x.locked;
00173             phys_location = (unsigned long)x.mem << 12UL;
00174         }
00175 
00176         return true;
00177     }
00178 
00179     return false;
00180 }
00181 
00182 static INLINE bool InvalidHandle(Bitu handle) {
00183         return (!handle || (handle>=XMS_HANDLES) || xms_handles[handle].free);
00184 }
00185 
00186 Bitu XMS_QueryFreeMemory(Bit32u& largestFree, Bit32u& totalFree) {
00187         /* Scan the tree for free memory and find largest free block */
00188         totalFree=(Bit32u)(MEM_FreeTotal()*4);
00189         largestFree=(Bit32u)(MEM_FreeLargest()*4);
00190         if (!totalFree) return XMS_OUT_OF_SPACE;
00191         return 0;
00192 }
00193 
00194 void XMS_ZeroAllocation(MemHandle mem,unsigned int pages) {
00195         PhysPt address;
00196 
00197         if (pages == 0) return;
00198         address = (PhysPt)mem * (PhysPt)4096UL;
00199         pages *= 4096UL;
00200 
00201         if ((address+pages) > 0xC0000000UL) E_Exit("XMS_ZeroAllocation out of range");
00202         while (pages != 0) {
00203                 mem_writeb(address++,0);
00204                 pages--;
00205         }
00206 }
00207 
00208 extern bool enable_a20_on_windows_init;
00209 extern bool dbg_zero_on_xms_allocmem;
00210 
00211 Bitu XMS_AllocateMemory(Bitu size, Bit16u& handle) {    // size = kb
00212         /* Find free handle */
00213         Bit16u index=1;
00214         while (!xms_handles[index].free) {
00215                 if (++index>=XMS_HANDLES) return XMS_OUT_OF_HANDLES;
00216         }
00217         MemHandle mem;
00218         if (size!=0) {
00219                 Bitu pages=(size/4) + ((size & 3) ? 1 : 0);
00220                 mem=MEM_AllocatePages(pages,true);
00221                 if (!mem) return XMS_OUT_OF_SPACE;
00222                 if (dbg_zero_on_xms_allocmem) XMS_ZeroAllocation(mem,pages);
00223         } else {
00224                 mem=MEM_GetNextFreePage();
00225                 if (mem==0) LOG(LOG_MISC,LOG_DEBUG)("XMS:Allocate zero pages with no memory left"); // Windows 3.1 triggers this surprisingly often!
00226                 if (mem != 0 && dbg_zero_on_xms_allocmem) XMS_ZeroAllocation(mem,1);
00227         }
00228         xms_handles[index].free=false;
00229         xms_handles[index].mem=mem;
00230         xms_handles[index].locked=0;
00231         xms_handles[index].size=size;
00232         handle=index;
00233         return 0;
00234 }
00235 
00236 Bitu XMS_FreeMemory(Bitu handle) {
00237         if (InvalidHandle(handle)) return XMS_INVALID_HANDLE;
00238         MEM_ReleasePages(xms_handles[handle].mem);
00239         xms_handles[handle].mem=-1;
00240         xms_handles[handle].size=0;
00241         xms_handles[handle].free=true;
00242         return 0;
00243 }
00244 
00245 Bitu XMS_MoveMemory(PhysPt bpt) {
00246         /* Read the block with mem_read's */
00247         Bitu length=mem_readd(bpt+offsetof(XMS_MemMove,length));
00248         Bitu src_handle=mem_readw(bpt+offsetof(XMS_MemMove,src_handle));
00249         union {
00250                 RealPt realpt;
00251                 Bit32u offset;
00252         } src,dest;
00253         src.offset=mem_readd(bpt+offsetof(XMS_MemMove,src.offset));
00254         Bitu dest_handle=mem_readw(bpt+offsetof(XMS_MemMove,dest_handle));
00255         dest.offset=mem_readd(bpt+offsetof(XMS_MemMove,dest.offset));
00256         PhysPt srcpt,destpt;
00257         if (src_handle) {
00258                 if (InvalidHandle(src_handle)) {
00259                         return XMS_INVALID_SOURCE_HANDLE;
00260                 }
00261                 if (src.offset>=(xms_handles[src_handle].size*1024U)) {
00262                         return XMS_INVALID_SOURCE_OFFSET;
00263                 }
00264                 if (length>xms_handles[src_handle].size*1024U-src.offset) {
00265                         return XMS_INVALID_LENGTH;
00266                 }
00267                 srcpt=((unsigned int)xms_handles[src_handle].mem*4096U)+src.offset;
00268         } else {
00269                 srcpt=Real2Phys(src.realpt);
00270         }
00271         if (dest_handle) {
00272                 if (InvalidHandle(dest_handle)) {
00273                         return XMS_INVALID_DEST_HANDLE;
00274                 }
00275                 if (dest.offset>=(xms_handles[dest_handle].size*1024U)) {
00276                         return XMS_INVALID_DEST_OFFSET;
00277                 }
00278                 if (length>xms_handles[dest_handle].size*1024U-dest.offset) {
00279                         return XMS_INVALID_LENGTH;
00280                 }
00281                 destpt=((unsigned int)xms_handles[dest_handle].mem*4096U)+dest.offset;
00282         } else {
00283                 destpt=Real2Phys(dest.realpt);
00284         }
00285 //      LOG_MSG("XMS move src %X dest %X length %X",srcpt,destpt,length);
00286 
00287     /* we must enable the A20 gate during this copy.
00288      * DOSBox-X masks the A20 line and this will only cause corruption otherwise. */
00289 
00290     if (length != 0) {
00291         bool a20_was_enabled = XMS_GetEnabledA20();
00292 
00293         xms_local_enable_count++;
00294         XMS_EnableA20(true);
00295 
00296         mem_memcpy(destpt,srcpt,length);
00297 
00298         xms_local_enable_count--;
00299         if (!a20_was_enabled) XMS_EnableA20(false);
00300     }
00301 
00302     return 0;
00303 }
00304 
00305 Bitu XMS_LockMemory(Bitu handle, Bit32u& address) {
00306         if (InvalidHandle(handle)) return XMS_INVALID_HANDLE;
00307         if (xms_handles[handle].locked<255) xms_handles[handle].locked++;
00308         address = (unsigned long)xms_handles[handle].mem * 4096UL;
00309         return 0;
00310 }
00311 
00312 Bitu XMS_UnlockMemory(Bitu handle) {
00313         if (InvalidHandle(handle)) return XMS_INVALID_HANDLE;
00314         if (xms_handles[handle].locked) {
00315                 xms_handles[handle].locked--;
00316                 return 0;
00317         }
00318         return XMS_BLOCK_NOT_LOCKED;
00319 }
00320 
00321 Bitu XMS_GetHandleInformation(Bitu handle, Bit8u& lockCount, Bit8u& numFree, Bit32u& size) {
00322         if (InvalidHandle(handle)) return XMS_INVALID_HANDLE;
00323         lockCount = xms_handles[handle].locked;
00324         /* Find available blocks */
00325         numFree=0;
00326         for (Bitu i=1;i<XMS_HANDLES;i++) {
00327                 if (xms_handles[i].free) numFree++;
00328         }
00329         size=(Bit32u)(xms_handles[handle].size);
00330         return 0;
00331 }
00332 
00333 Bitu XMS_ResizeMemory(Bitu handle, Bitu newSize) {
00334         if (InvalidHandle(handle)) return XMS_INVALID_HANDLE;   
00335         // Block has to be unlocked
00336         if (xms_handles[handle].locked>0) return XMS_BLOCK_LOCKED;
00337         Bitu pages=newSize/4 + ((newSize & 3) ? 1 : 0);
00338         if (MEM_ReAllocatePages(xms_handles[handle].mem,pages,true)) {
00339                 xms_handles[handle].size = newSize;
00340                 return 0;
00341         } else return XMS_OUT_OF_SPACE;
00342 }
00343 
00344 static bool multiplex_xms(void) {
00345         switch (reg_ax) {
00346         case 0x4300:                                    /* XMS installed check */
00347                         reg_al=0x80;
00348                         return true;
00349         case 0x4310:                                    /* XMS handler seg:offset */
00350                         SegSet16(es,RealSeg(xms_callback));
00351                         reg_bx=RealOff(xms_callback);
00352                         return true;                    
00353         }
00354         return false;
00355 
00356 }
00357 
00358 INLINE void SET_RESULT(Bitu res,bool touch_bl_on_succes=true) {
00359         if(touch_bl_on_succes || res) reg_bl = (Bit8u)res;
00360         reg_ax = (res==0)?1:0;
00361 }
00362 
00363 Bitu XMS_LocalEnableA20(void) {
00364     /* This appears to be how Microsoft HIMEM.SYS implements this */
00365     if ((xms_local_enable_count++) == 0)
00366         XMS_EnableA20(true);
00367 
00368     return 0;
00369 }
00370 
00371 Bitu XMS_LocalDisableA20(void) {
00372     /* This appears to be how Microsoft HIMEM.SYS implements this */
00373     if (xms_local_enable_count > 0) {
00374         if (--xms_local_enable_count == 0)
00375             XMS_EnableA20(false);
00376     }
00377     else {
00378         return 0x82; // A20 error
00379     }
00380 
00381     return 0;
00382 }
00383 
00384 Bitu XMS_Handler(void) {
00385     Bitu r;
00386 
00387 //      LOG(LOG_MISC,LOG_ERROR)("XMS: CALL %02X",reg_ah);
00388         switch (reg_ah) {
00389         case XMS_GET_VERSION:                                                                           /* 00 */
00390                 reg_ax=XMS_VERSION;
00391                 reg_bx=XMS_DRIVER_VERSION;
00392                 reg_dx=xms_hma_exists?1:0;
00393                 break;
00394         case XMS_ALLOCATE_HIGH_MEMORY:                                                          /* 01 */
00395                 if (xms_hma_exists) {
00396                         if (xms_hma_application_has_control || DOS_IS_IN_HMA()) {
00397                                 /* hma already controlled by application or DOS kernel */
00398                                 reg_ax=0;
00399                                 reg_bl=HIGH_MEMORY_IN_USE;
00400                         }
00401                         else if (reg_dx < xms_hma_minimum_alloc) {
00402                                 /* not big enough */
00403                                 reg_ax=0;
00404                                 reg_bl=HIGH_MEMORY_NOT_BIG_ENOUGH;
00405                         }
00406                         else { /* program allocation */
00407                                 LOG(LOG_MISC,LOG_DEBUG)("XMS: HMA allocated by application/TSR");
00408                                 xms_hma_application_has_control = true;
00409                                 reg_ax=1;
00410                         }
00411                 }
00412                 else {
00413                         reg_ax=0;
00414                         reg_bl=HIGH_MEMORY_NOT_EXIST;
00415                 }
00416                 break;
00417         case XMS_FREE_HIGH_MEMORY:                                                                      /* 02 */
00418                 if (xms_hma_exists) {
00419                         if (DOS_IS_IN_HMA()) LOG(LOG_MISC,LOG_WARN)("DOS application attempted to free HMA while DOS kernel occupies it!");
00420 
00421                         if (xms_hma_application_has_control) {
00422                                 LOG(LOG_MISC,LOG_DEBUG)("XMS: HMA freed by application/TSR");
00423                                 xms_hma_application_has_control = false;
00424                                 reg_ax=1;
00425                         }
00426                         else {
00427                                 reg_ax=0;
00428                                 reg_bl=HIGH_MEMORY_NOT_ALLOCATED;
00429                         }
00430                 }
00431                 else {
00432                         reg_ax=0;
00433                         reg_bl=HIGH_MEMORY_NOT_EXIST;
00434                 }
00435                 break;
00436         case XMS_GLOBAL_ENABLE_A20:                                                                     /* 03 */
00437         /* This appears to be how Microsoft HIMEM.SYS implements this */
00438         if (!xms_global_enable) {
00439             if ((r=XMS_LocalEnableA20()) == 0)
00440                 xms_global_enable = true;
00441         }
00442         else {
00443             r = 0;
00444         }
00445         SET_RESULT(r);
00446                 break;
00447         case XMS_GLOBAL_DISABLE_A20:                                                            /* 04 */
00448         /* This appears to be how Microsoft HIMEM.SYS implements this */
00449         if (xms_global_enable) {
00450             if ((r=XMS_LocalDisableA20()) == 0)
00451                 xms_global_enable = false;
00452         }
00453         else {
00454             r = 0;
00455         }
00456         SET_RESULT(r);
00457         break;
00458     case XMS_LOCAL_ENABLE_A20:                                                                  /* 05 */
00459         SET_RESULT(XMS_LocalEnableA20());
00460         break;
00461         case XMS_LOCAL_DISABLE_A20:                                                                     /* 06 */
00462         SET_RESULT(XMS_LocalDisableA20());
00463                 break;
00464         case XMS_QUERY_A20:                                                                                     /* 07 */
00465                 reg_ax = XMS_GetEnabledA20();
00466                 reg_bl = 0;
00467                 break;
00468         case XMS_QUERY_FREE_EXTENDED_MEMORY:                                            /* 08 */
00469                 reg_bl = XMS_QueryFreeMemory(reg_eax,reg_edx);
00470                 if (reg_eax > 65535) reg_eax = 65535; /* cap sizes for older DOS programs. newer ones use function 0x88 */
00471                 if (reg_edx > 65535) reg_edx = 65535;
00472                 break;
00473         case XMS_ALLOCATE_ANY_MEMORY:                                                           /* 89 */
00474                 { /* Chopping off bits 16-31 to fall through to ALLOCATE_EXTENDED_MEMORY is inaccurate.
00475                      The Extended Memory Specification states you use all of EDX, so programs can request
00476                      64MB or more. Even if DOSBox does not (yet) support >= 64MB of RAM. */
00477                 Bit16u handle = 0;
00478                 SET_RESULT(XMS_AllocateMemory(reg_edx,handle));
00479                 reg_dx = handle;
00480                 }; break;
00481         case XMS_ALLOCATE_EXTENDED_MEMORY:                                                      /* 09 */
00482                 {
00483                 Bit16u handle = 0;
00484                 SET_RESULT(XMS_AllocateMemory(reg_dx,handle));
00485                 reg_dx = handle;
00486                 }; break;
00487         case XMS_FREE_EXTENDED_MEMORY:                                                          /* 0a */
00488                 SET_RESULT(XMS_FreeMemory(reg_dx));
00489                 break;
00490         case XMS_MOVE_EXTENDED_MEMORY_BLOCK:                                            /* 0b */
00491                 SET_RESULT(XMS_MoveMemory(SegPhys(ds)+reg_si),false);
00492                 break;
00493         case XMS_LOCK_EXTENDED_MEMORY_BLOCK: {                                          /* 0c */
00494                 Bit32u address;
00495                 Bitu res = XMS_LockMemory(reg_dx, address);
00496                 if(res) reg_bl = (Bit8u)res;
00497                 reg_ax = (res==0);
00498                 if (res==0) { // success
00499                         reg_bx=(Bit16u)(address & 0xFFFF);
00500                         reg_dx=(Bit16u)(address >> 16);
00501                 };
00502                 }; break;
00503         case XMS_UNLOCK_EXTENDED_MEMORY_BLOCK:                                          /* 0d */
00504                 SET_RESULT(XMS_UnlockMemory(reg_dx));
00505                 break;
00506         case XMS_GET_EMB_HANDLE_INFORMATION:                                            /* 0e */
00507                 SET_RESULT(XMS_GetHandleInformation(reg_dx,reg_bh,reg_bl,reg_edx),false);
00508                 reg_edx &= 0xFFFF;
00509                 break;
00510         case XMS_RESIZE_ANY_EXTENDED_MEMORY_BLOCK:                                      /* 0x8f */
00511                 SET_RESULT(XMS_ResizeMemory(reg_dx, reg_ebx));
00512                 break;
00513         case XMS_RESIZE_EXTENDED_MEMORY_BLOCK:                                          /* 0f */
00514                 SET_RESULT(XMS_ResizeMemory(reg_dx, reg_bx));
00515                 break;
00516         case XMS_ALLOCATE_UMB: {                                                                        /* 10 */
00517                 if (!umb_available) {
00518                         reg_ax=0;
00519                         reg_bl=XMS_FUNCTION_NOT_IMPLEMENTED;
00520                         break;
00521                 }
00522                 Bit16u umb_start=dos_infoblock.GetStartOfUMBChain();
00523                 if (umb_start==0xffff) {
00524                         reg_ax=0;
00525                         reg_bl=UMB_NO_BLOCKS_AVAILABLE;
00526                         reg_dx=0;       // no upper memory available
00527                         break;
00528                 }
00529                 /* Save status and linkage of upper UMB chain and link upper
00530                    memory to the regular MCB chain */
00531                 Bit8u umb_flag=dos_infoblock.GetUMBChainState();
00532                 if ((umb_flag&1)==0) DOS_LinkUMBsToMemChain(1);
00533                 Bit8u old_memstrat=DOS_GetMemAllocStrategy()&0xff;
00534                 DOS_SetMemAllocStrategy(0x40);  // search in UMBs only
00535 
00536                 Bit16u size=reg_dx;Bit16u seg;
00537                 if (DOS_AllocateMemory(&seg,&size)) {
00538                         reg_ax=1;
00539                         reg_bx=seg;
00540                 } else {
00541                         reg_ax=0;
00542                         if (size==0) reg_bl=UMB_NO_BLOCKS_AVAILABLE;
00543                         else reg_bl=UMB_ONLY_SMALLER_BLOCK;
00544                         reg_dx=size;    // size of largest available UMB
00545                 }
00546 
00547                 /* Restore status and linkage of upper UMB chain */
00548                 Bit8u current_umb_flag=dos_infoblock.GetUMBChainState();
00549                 if ((current_umb_flag&1)!=(umb_flag&1)) DOS_LinkUMBsToMemChain(umb_flag);
00550                 DOS_SetMemAllocStrategy(old_memstrat);
00551                 }
00552                 break;
00553         case XMS_DEALLOCATE_UMB:                                                                        /* 11 */
00554                 if (!umb_available) {
00555                         reg_ax=0;
00556                         reg_bl=XMS_FUNCTION_NOT_IMPLEMENTED;
00557                         break;
00558                 }
00559                 if (dos_infoblock.GetStartOfUMBChain()!=0xffff) {
00560                         if (DOS_FreeMemory(reg_dx)) {
00561                                 reg_ax=0x0001;
00562                                 break;
00563                         }
00564                 }
00565                 reg_ax=0x0000;
00566                 reg_bl=UMB_NO_BLOCKS_AVAILABLE;
00567                 break;
00568         case XMS_QUERY_ANY_FREE_MEMORY:                                                         /* 88 */
00569                 reg_bl = XMS_QueryFreeMemory(reg_eax,reg_edx);
00570                 reg_ecx = (MEM_TotalPages()*MEM_PAGESIZE)-1;                    // highest known physical memory address
00571                 break;
00572         case XMS_GET_EMB_HANDLE_INFORMATION_EXT: {                                      /* 8e */
00573                 Bit8u free_handles;
00574                 Bitu result = XMS_GetHandleInformation(reg_dx,reg_bh,free_handles,reg_edx);
00575                 if (result != 0) reg_bl = result;
00576                 else reg_cx = free_handles;
00577                 reg_ax = (result==0);
00578                 } break;
00579         default:
00580                 LOG(LOG_MISC,LOG_ERROR)("XMS: unknown function %02X",reg_ah);
00581                 reg_ax=0;
00582                 reg_bl=XMS_FUNCTION_NOT_IMPLEMENTED;
00583         }
00584 //      LOG(LOG_MISC,LOG_ERROR)("XMS: CALL Result: %02X",reg_bl);
00585         return CBRET_NONE;
00586 }
00587 
00588 bool xms_init = false;
00589 
00590 bool keep_umb_on_boot;
00591 
00592 extern Bitu VGA_BIOS_SEG;
00593 extern Bitu VGA_BIOS_SEG_END;
00594 extern Bitu VGA_BIOS_Size;
00595 
00596 bool XMS_IS_ACTIVE() {
00597         return (xms_callback != 0);
00598 }
00599 
00600 bool XMS_HMA_EXISTS() {
00601         return XMS_IS_ACTIVE() && xms_hma_exists;
00602 }
00603 
00604 Bitu GetEMSType(Section_prop * section);
00605 void DOS_GetMemory_Choose();
00606 
00607 void ROMBIOS_FreeUnusedMinToLoc(Bitu phys);
00608 bool MEM_unmap_physmem(Bitu start,Bitu end);
00609 Bitu ROMBIOS_MinAllocatedLoc();
00610 
00611 void RemoveUMBBlock() {
00612         /* FIXME: Um... why is umb_available == false even when set to true below? */
00613         if (umb_init) {
00614                 LOG_MSG("Removing UMB block 0x%04x-0x%04x\n",first_umb_seg,first_umb_seg+first_umb_size-1);
00615                 MEM_unmap_physmem((unsigned long)first_umb_seg<<4ul,(((unsigned long)first_umb_seg+(unsigned long)first_umb_size)<<4ul)-1ul);
00616                 umb_init = false;
00617         }
00618 }
00619 
00620 class XMS: public Module_base {
00621 private:
00622         CALLBACK_HandlerObject callbackhandler;
00623 public:
00624         XMS(Section* configuration):Module_base(configuration){
00625                 Section_prop * section=static_cast<Section_prop *>(configuration);
00626                 umb_available=false;
00627 
00628                 if (!section->Get_bool("xms")) return;
00629 
00630                 /* NTS: Disable XMS emulation if CPU type is less than a 286, because extended memory did not
00631                  *      exist until the CPU had enough address lines to read past the 1MB mark.
00632                  *
00633                  *      The other reason we do this is that there is plenty of software that assumes 286+ instructions
00634                  *      if they detect XMS services, including but not limited to:
00635                  *
00636                  *      MSD.EXE Microsoft Diagnostics
00637                  *      Microsoft Windows 3.0
00638                  *
00639                  *      Not emulating XMS for 8086/80186 emulation prevents the software from crashing. */
00640 
00641                 /* TODO: Add option to allow users to *force* XMS emulation, overriding this lockout, if they're
00642                  *       crazy enough to see what happens or they want to witness the mis-detection mentioned above. */
00643                 if (CPU_ArchitectureType < CPU_ARCHTYPE_286) {
00644                         LOG_MSG("CPU is 80186 or lower model that lacks the address lines needed for 'extended memory' to exist, disabling XMS");
00645                         return;
00646                 }
00647 
00648                 xms_init = true;
00649 
00650                 xms_hma_exists = section->Get_bool("hma");
00651                 xms_hma_minimum_alloc = (unsigned int)section->Get_int("hma minimum allocation");
00652                 xms_hma_alloc_non_dos_kernel_control = section->Get_bool("hma allow reservation");
00653                 if (xms_hma_minimum_alloc > 0xFFF0U) xms_hma_minimum_alloc = 0xFFF0U;
00654 
00655                 Bitu i;
00656                 BIOS_ZeroExtendedSize(true);
00657                 DOS_AddMultiplexHandler(multiplex_xms);
00658 
00659                 enable_a20_on_windows_init = section->Get_bool("enable a20 on windows init");
00660                 dbg_zero_on_xms_allocmem = section->Get_bool("zero memory on xms memory allocation");
00661 
00662                 if (dbg_zero_on_xms_allocmem) {
00663                         LOG_MSG("Debug option enabled: XMS memory allocation will always clear memory block before returning\n");
00664                 }
00665 
00666                 /* place hookable callback in writable memory area */
00667                 xms_callback=RealMake(DOS_GetMemory(0x1,"xms_callback")-1,0x10);
00668                 callbackhandler.Install(&XMS_Handler,CB_HOOKABLE,Real2Phys(xms_callback),"XMS Handler");
00669                 // pseudocode for CB_HOOKABLE:
00670                 //      jump near skip
00671                 //      nop,nop,nop
00672                 //      label skip:
00673                 //      callback XMS_Handler
00674                 //      retf
00675            
00676                 for (i=0;i<XMS_HANDLES;i++) {
00677                         xms_handles[i].free=true;
00678                         xms_handles[i].mem=-1;
00679                         xms_handles[i].size=0;
00680                         xms_handles[i].locked=0;
00681                 }
00682                 /* Disable the 0 handle */
00683                 xms_handles[0].free     = false;
00684 
00685                 /* Set up UMB chain */
00686                 keep_umb_on_boot=section->Get_bool("keep umb on boot");
00687                 umb_available=section->Get_bool("umb");
00688                 first_umb_seg=section->Get_hex("umb start");
00689                 first_umb_size=section->Get_hex("umb end");
00690 
00691                 DOS_GetMemory_Choose();
00692 
00693                 // Sanity check
00694                 if (rombios_minimum_location == 0) E_Exit("Uninitialized ROM BIOS base");
00695 
00696                 if (first_umb_seg == 0) {
00697                         first_umb_seg = DOS_PRIVATE_SEGMENT_END;
00698                         if (first_umb_seg < VGA_BIOS_SEG_END)
00699                                 first_umb_seg = VGA_BIOS_SEG_END;
00700                 }
00701                 if (first_umb_size == 0) first_umb_size = ROMBIOS_MinAllocatedLoc()>>4;
00702 
00703                 if (first_umb_seg < 0xC000 || first_umb_seg < DOS_PRIVATE_SEGMENT_END) {
00704                         LOG(LOG_MISC,LOG_WARN)("UMB blocks before 0xD000 conflict with VGA (0xA000-0xBFFF), VGA BIOS (0xC000-0xC7FF) and DOSBox private area (0x%04x-0x%04x)",
00705                                 DOS_PRIVATE_SEGMENT,DOS_PRIVATE_SEGMENT_END-1);
00706                         first_umb_seg = 0xC000;
00707                         if (first_umb_seg < (Bitu)DOS_PRIVATE_SEGMENT_END) first_umb_seg = (Bitu)DOS_PRIVATE_SEGMENT_END;
00708                 }
00709                 if (first_umb_seg >= (rombios_minimum_location>>4)) {
00710                         LOG(LOG_MISC,LOG_NORMAL)("UMB starting segment 0x%04x conflict with BIOS at 0x%04x. Disabling UMBs",(int)first_umb_seg,(int)(rombios_minimum_location>>4));
00711                         umb_available = false;
00712                 }
00713                 if (first_umb_size >= (rombios_minimum_location>>4)) {
00714                         /* we can ask the BIOS code to trim back the region, assuming it hasn't allocated anything there yet */
00715                         LOG(LOG_MISC,LOG_DEBUG)("UMB ending segment 0x%04x conflicts with BIOS at 0x%04x, asking BIOS to move aside",(int)first_umb_size,(int)(rombios_minimum_location>>4));
00716                         ROMBIOS_FreeUnusedMinToLoc((unsigned int)first_umb_size<<4U);
00717                 }
00718                 if (first_umb_size >= ((unsigned int)rombios_minimum_location>>4U)) {
00719                         LOG(LOG_MISC,LOG_DEBUG)("UMB ending segment 0x%04x conflicts with BIOS at 0x%04x, truncating region",(int)first_umb_size,(int)(rombios_minimum_location>>4));
00720                         first_umb_size = ((unsigned int)rombios_minimum_location>>4u)-1u;
00721                 }
00722 
00723         Bitu GetEMSPageFrameSegment(void);
00724 
00725         bool ems_available = GetEMSType(section)>0;
00726 
00727         /* 2017/12/24 I just noticed that the EMS page frame will conflict with UMB on standard configuration.
00728          * In IBM PC mode the EMS page frame is at E000:0000.
00729          * In PC-98 mode the EMS page frame is at D000:0000. */
00730         if (ems_available && first_umb_size >= GetEMSPageFrameSegment()) {
00731             assert(GetEMSPageFrameSegment() >= 0xA000);
00732             LOG(LOG_MISC,LOG_DEBUG)("UMB overlaps EMS page frame at 0x%04x, truncating region",(unsigned int)GetEMSPageFrameSegment());
00733             first_umb_size = GetEMSPageFrameSegment() - 1;
00734         }
00735         /* UMB cannot interfere with EGC 4th graphics bitplane on PC-98 */
00736         /* TODO: Allow UMB into E000:xxxx if emulating a PC-98 that lacks 16-color mode. */
00737         if (IS_PC98_ARCH && first_umb_size >= 0xE000) {
00738             LOG(LOG_MISC,LOG_DEBUG)("UMB overlaps PC-98 EGC 4th graphics bitplane, truncating region");
00739             first_umb_size = 0xDFFF;
00740         }
00741                 if (first_umb_size < first_umb_seg) {
00742                         LOG(LOG_MISC,LOG_NORMAL)("UMB end segment below UMB start. I'll just assume you mean to disable UMBs then.");
00743                         first_umb_size = first_umb_seg - 1;
00744                         umb_available = false;
00745                 }
00746                 first_umb_size = (first_umb_size + 1 - first_umb_seg);
00747                 if (umb_available) {
00748                         LOG(LOG_MISC,LOG_NORMAL)("UMB assigned region is 0x%04x-0x%04x",(int)first_umb_seg,(int)(first_umb_seg+first_umb_size-1));
00749                         if (MEM_map_RAM_physmem((unsigned int)first_umb_seg<<4u,(((unsigned int)first_umb_seg+(unsigned int)first_umb_size)<<4u)-1u)) {
00750                                 memset(GetMemBase()+((unsigned int)first_umb_seg<<4u),0x00u,(unsigned int)first_umb_size<<4u);
00751                         }
00752                         else {
00753                                 LOG(LOG_MISC,LOG_WARN)("Unable to claim UMB region (perhaps adapter ROM is in the way). Disabling UMB");
00754                                 umb_available = false;
00755                         }
00756                 }
00757 
00758                 DOS_BuildUMBChain(umb_available,ems_available);
00759                 umb_init = true;
00760 
00761         /* CP/M compat will break unless a copy of the JMP instruction is mirrored in HMA */
00762         DOS_Write_HMA_CPM_jmp();
00763         }
00764 
00765         ~XMS(){
00766                 /* Remove upper memory information */
00767                 dos_infoblock.SetStartOfUMBChain(0xffff);
00768                 if (umb_available) {
00769                         dos_infoblock.SetUMBChainState(0);
00770                         umb_available=false;
00771                 }
00772 
00773                 if (!xms_init) return;
00774 
00775                 /* Undo biosclearing */
00776                 BIOS_ZeroExtendedSize(false);
00777 
00778                 /* Remove Multiplex */
00779                 DOS_DelMultiplexHandler(multiplex_xms);
00780 
00781                 /* Free used memory while skipping the 0 handle */
00782                 for (Bitu i = 1;i<XMS_HANDLES;i++) 
00783                         if(!xms_handles[i].free) XMS_FreeMemory(i);
00784 
00785                 xms_init = false;
00786         }
00787 
00788 };
00789 
00790 static XMS* test = NULL;
00791 
00792 void XMS_DoShutDown() {
00793         if (test != NULL) {
00794                 delete test;    
00795                 test = NULL;
00796         }
00797 }
00798 
00799 void XMS_ShutDown(Section* /*sec*/) {
00800         XMS_DoShutDown();
00801 }
00802 
00803 bool XMS_Active(void) {
00804     return (test != NULL) && xms_init;
00805 }
00806 
00807 void XMS_Startup(Section *sec) {
00808     (void)sec;//UNUSED
00809         if (test == NULL) {
00810                 LOG(LOG_MISC,LOG_DEBUG)("Allocating XMS emulation");
00811                 test = new XMS(control->GetSection("dos"));
00812         }
00813 }
00814 
00815 void XMS_Init() {
00816         LOG(LOG_MISC,LOG_DEBUG)("Initializing XMS extended memory services");
00817 
00818         AddExitFunction(AddExitFunctionFuncPair(XMS_ShutDown),true);
00819         AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(XMS_ShutDown));
00820         AddVMEventFunction(VM_EVENT_DOS_EXIT_BEGIN,AddVMEventFunctionFuncPair(XMS_ShutDown));
00821 }
00822