DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/ints/ems.cpp
00001 /*
00002  *  Copyright (C) 2002-2020  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 along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 #include <assert.h>
00020 #include <string.h>
00021 #include <stdlib.h>
00022 #include "dosbox.h"
00023 #include "callback.h"
00024 #include "mem.h"
00025 #include "paging.h"
00026 #include "bios.h"
00027 #include "keyboard.h"
00028 #include "regs.h"
00029 #include "inout.h"
00030 #include "dos_inc.h"
00031 #include "setup.h"
00032 #include "support.h"
00033 #include "cpu.h"
00034 #include "dma.h"
00035 #include "control.h"
00036 
00037 /* TODO: Make EMS page frame address (and size) user configurable.
00038  *       With auto setting to fit in automatically with BIOS and UMBs.
00039  *       With limit variable to emulate EMM386.EXE limits (32MB limit in MS-DOS/Win9x).
00040  *       With command .COM program in the Z:\ directly to allow enabling/disabling EMS
00041  *           and turning on/off the virtual 8086 mode, just like what EMM386.EXE offers in DOS.
00042  *       With code to create and maintain vm86 page tables when emulating EMM386.EXE,
00043  *           while retaining the 'hardware' based mapping it does now if "mixed" or "board".
00044  *       With configuration parameter to control the number of EMS handles.
00045  *       With configuration parameter to specify what version EMS to emulate.
00046  *       With code to change DOS device EMMXXXX0 <-> EMMQXXX0 depending on when you enable/disable EMS at runtime.
00047  *       With runtime enable/disable of VCPI (if nobody is using VCPI).
00048  *       With DOSBox option to force EMM OS handle to be allocated from an within an even megabyte if the user
00049  *           would rather be able to manage the A20 line without crashing, and another option to specify if
00050  *           A20 should be turned on or left off in this case.
00051  */
00052 
00053 Bitu XMS_EnableA20(bool enable);
00054 
00055 unsigned short EMM_PAGEFRAME =      0xE000;
00056 unsigned short EMM_PAGEFRAME4K =    ((EMM_PAGEFRAME*16)/4096);
00057 
00058 Bitu GetEMSPageFrameSegment(void) {
00059     return EMM_PAGEFRAME;
00060 }
00061 
00062 #define EMM_MAX_HANDLES 200U                    /* 255 Max */
00063 #define EMM_PAGE_SIZE   (16U*1024U)
00064 #define EMM_MAX_PHYS    4U                              /* 4 16kb pages in pageframe */
00065 
00066 Bitu GetEMSPageFrameSize(void) {
00067     return EMM_MAX_PHYS * EMM_PAGE_SIZE;
00068 }
00069 
00070 #define EMM_VERSION                     0x40
00071 #define EMM_MINOR_VERSION               0x00
00072 //#define EMM_MINOR_VERSION             0x30    // emm386 4.48
00073 #define GEMMIS_VERSION                  0x0001  // Version 1.0
00074 
00075 #define EMM_SYSTEM_HANDLE               0x0000
00076 #define NULL_HANDLE                     0xffff
00077 #define NULL_PAGE                       0xffff
00078 
00079 bool ENABLE_VCPI=true;
00080 bool ENABLE_V86_STARTUP=false;
00081 bool zero_int67_if_no_ems=true;
00082 bool ems_syshandle_on_even_mb=false;
00083 
00084 #define EMM_VOLATILE 0
00085 #define EMM_NONVOLATILE 1
00086 
00087 /* EMM errors */
00088 #define EMM_NO_ERROR                    0x00
00089 #define EMM_SOFT_MAL                    0x80
00090 #define EMM_HARD_MAL                    0x81
00091 #define EMM_INVALID_HANDLE              0x83
00092 #define EMM_FUNC_NOSUP                  0x84
00093 #define EMM_OUT_OF_HANDLES              0x85
00094 #define EMM_SAVEMAP_ERROR               0x86
00095 #define EMM_OUT_OF_PHYS                 0x87
00096 #define EMM_OUT_OF_LOG                  0x88
00097 #define EMM_ZERO_PAGES                  0x89
00098 #define EMM_LOG_OUT_RANGE               0x8a
00099 #define EMM_ILL_PHYS                    0x8b
00100 #define EMM_PAGE_MAP_SAVED              0x8d
00101 #define EMM_NO_SAVED_PAGE_MAP   0x8e
00102 #define EMM_INVALID_SUB                 0x8f
00103 #define EMM_ATTR_UNDEF                  0x90
00104 #define EMM_FEAT_NOSUP                  0x91
00105 #define EMM_MOVE_OVLAP                  0x92
00106 #define EMM_MOVE_OVLAPI                 0x97
00107 #define EMM_NOT_FOUND                   0xa0
00108 
00109 enum {
00110         EMS_NONE=0,                     /* no emulation */
00111         EMS_MIXED,                      /* "mixed mode", default if "true", attempts to please everybody */
00112         EMS_BOARD,                      /* act like pre-386 expanded memory, provided by an expansion card */
00113         EMS_EMM386                      /* act like 386+ expanded memory, faked using virtual 8086 mode (EMM386.EXE style) */
00114 };
00115 
00116 struct EMM_Mapping {
00117         Bit16u handle;
00118         Bit16u page;
00119 };
00120 
00121 struct EMM_Handle {
00122         Bit16u pages;
00123         MemHandle mem;
00124         char name[8];
00125         bool saved_page_map;
00126         EMM_Mapping page_map[EMM_MAX_PHYS];
00127 };
00128 
00129 static Bitu ems_type = EMS_NONE;
00130 
00131 const char *EMS_Type_String(void) {
00132     switch (ems_type) {
00133         case EMS_NONE:  return "None";
00134         case EMS_MIXED: return "Mixed";
00135         case EMS_BOARD: return "Board";
00136         case EMS_EMM386:return "EMM386";
00137         default:        break;
00138     }
00139 
00140     return NULL;
00141 }
00142 
00143 static EMM_Handle emm_handles[EMM_MAX_HANDLES];
00144 static EMM_Mapping emm_mappings[EMM_MAX_PHYS];
00145 static EMM_Mapping emm_segmentmappings[0x40];
00146 
00147 bool EMS_GetMapping(Bitu &handle,Bit16u &log_page,Bitu ems_page) {
00148     if (ems_page < EMM_MAX_PHYS) {
00149         auto &x = emm_mappings[ems_page];
00150 
00151         if (x.handle != NULL_HANDLE && x.page != NULL_PAGE) {
00152             handle = x.handle;
00153             log_page = x.page;
00154             return true;
00155         }
00156     }
00157 
00158     return false;
00159 }
00160 
00161 bool EMS_GetHandle(Bitu &size,PhysPt &addr,std::string &name,Bitu handle) {
00162     if (handle < EMM_MAX_HANDLES) {
00163         auto &x = emm_handles[handle];
00164 
00165         if (x.pages != NULL_HANDLE) {
00166             {
00167                 unsigned int i=0;
00168 
00169                 while (i < sizeof(x.name) && x.name[i] != 0) i++;
00170                 name = std::string(x.name,i);
00171             }
00172             size = (Bitu)x.pages << 14UL; // 16KB pages
00173             addr = (PhysPt)x.mem << 12UL;
00174             return true;
00175         }
00176     }
00177 
00178     return false;
00179 }
00180 
00181 Bitu EMS_Max_Handles(void) {
00182     return EMM_MAX_HANDLES;
00183 }
00184 
00185 bool EMS_Active(void) {
00186     return ems_type != EMS_NONE;
00187 }
00188 
00189 static Bit16u GEMMIS_seg; 
00190 
00191 class device_EMM : public DOS_Device {
00192 public:
00193     device_EMM(bool is_emm386_avail) : is_emm386(is_emm386_avail) {
00194                 SetName("EMMXXXX0");
00195                 GEMMIS_seg=0;
00196         }
00197         bool Read(Bit8u * /*data*/,Bit16u * /*size*/) { return false;}
00198         bool Write(const Bit8u * /*data*/,Bit16u * /*size*/){ 
00199                 LOG(LOG_IOCTL,LOG_NORMAL)("EMS:Write to device");       
00200                 return false;
00201         }
00202         bool Seek(Bit32u * /*pos*/,Bit32u /*type*/){return false;}
00203         bool Close(){return false;}
00204         Bit16u GetInformation(void){return 0xc0c0;}
00205         bool ReadFromControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode);
00206         bool WriteToControlChannel(PhysPt /*bufptr*/,Bit16u /*size*/,Bit16u * /*retcode*/){return true;}
00207 private:
00208 //      Bit8u cache;
00209         bool is_emm386;
00210 };
00211 
00212 bool device_EMM::ReadFromControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode) { 
00213         Bitu subfct=mem_readb(bufptr);
00214         switch (subfct) {
00215                 case 0x00:
00216                         if (size!=6) return false;
00217                         mem_writew(bufptr+0x00,0x0023);         // ID
00218                         mem_writed(bufptr+0x02,0);                      // private API entry point
00219                         *retcode=6;
00220                         return true;
00221                 case 0x01: {
00222                         if (!is_emm386) return false;
00223                         if (size!=6) return false;
00224                         if (GEMMIS_seg==0) GEMMIS_seg=DOS_GetMemory(0x20,"GEMMIS_seg");
00225                         PhysPt GEMMIS_addr=PhysMake(GEMMIS_seg,0);
00226 
00227                         mem_writew(GEMMIS_addr+0x00,0x0004);                    // flags
00228                         mem_writew(GEMMIS_addr+0x02,0x019d);                    // size of this structure
00229                         mem_writew(GEMMIS_addr+0x04,GEMMIS_VERSION);    // version 1.0 (provide ems information only)
00230                         mem_writed(GEMMIS_addr+0x06,0);                                 // reserved
00231 
00232                         /* build non-EMS frames (0-0xe000) */
00233                         for (PhysPt frct=0; frct<(unsigned int)EMM_PAGEFRAME4K/4U; frct++) {
00234                                 mem_writeb(GEMMIS_addr+0x0a+frct*6,0x00);       // frame type: NONE
00235                                 mem_writeb(GEMMIS_addr+0x0b+frct*6,0xff);       // owner: NONE
00236                                 mem_writew(GEMMIS_addr+0x0c+frct*6,0xffff);     // non-EMS frame
00237                                 mem_writeb(GEMMIS_addr+0x0e + frct*6,0xff);     // EMS page number (NONE)
00238                                 mem_writeb(GEMMIS_addr+0x0f+frct*6,0xaa);       // flags: direct mapping
00239                         }
00240                         /* build EMS page frame (0xe000-0xf000) */
00241                         for (PhysPt frct=0; frct<0x10U/4U; frct++) {
00242                                 PhysPt frnr=(frct+EMM_PAGEFRAME4K/4u)*6u;
00243                                 mem_writeb(GEMMIS_addr+0x0a+frnr,0x03);         // frame type: EMS frame in 64k page
00244                                 mem_writeb(GEMMIS_addr+0x0b+frnr,0xff);         // owner: NONE
00245                                 mem_writew(GEMMIS_addr+0x0c+frnr,0x7fff);       // no logical page number
00246                                 mem_writeb(GEMMIS_addr+0x0e + frnr,(Bit8u)(frct&0xff));         // physical EMS page number
00247                                 mem_writeb(GEMMIS_addr+0x0f+frnr,0x00);         // EMS frame
00248                         }
00249                         /* build non-EMS ROM frames (0xf000-0x10000) */
00250                         for (PhysPt frct=(EMM_PAGEFRAME4K+0x10u)/4u; frct<0xf0u/4u; frct++) {
00251                                 mem_writeb(GEMMIS_addr+0x0a+frct*6u,0x00);      // frame type: NONE
00252                                 mem_writeb(GEMMIS_addr+0x0b+frct*6u,0xff);      // owner: NONE
00253                                 mem_writew(GEMMIS_addr+0x0c+frct*6u,0xffff);    // non-EMS frame
00254                                 mem_writeb(GEMMIS_addr+0x0e + frct*6u,0xff);    // EMS page number (NONE)
00255                                 mem_writeb(GEMMIS_addr+0x0f+frct*6u,0xaa);      // flags: direct mapping
00256                         }
00257 
00258                         mem_writeb(GEMMIS_addr+0x18a,0x74);                     // ???
00259                         mem_writeb(GEMMIS_addr+0x18b,0x00);                     // no UMB descriptors following
00260                         mem_writeb(GEMMIS_addr+0x18c,0x01);                     // 1 EMS handle info recort
00261                         mem_writew(GEMMIS_addr+0x18d,0x0000);           // system handle
00262                         mem_writed(GEMMIS_addr+0x18f,0);                        // handle name
00263                         mem_writed(GEMMIS_addr+0x193,0);                        // handle name
00264                         if (emm_handles[EMM_SYSTEM_HANDLE].pages != NULL_HANDLE) {
00265                                 mem_writew(GEMMIS_addr+0x197,(emm_handles[EMM_SYSTEM_HANDLE].pages+3u)/4u);
00266                                 mem_writed(GEMMIS_addr+0x199,(unsigned int)emm_handles[EMM_SYSTEM_HANDLE].mem<<12u);    // physical address
00267                         } else {
00268                                 mem_writew(GEMMIS_addr+0x197,0x0001);           // system handle
00269                                 mem_writed(GEMMIS_addr+0x199,0x00110000);       // physical address
00270                         }
00271 
00272                         /* fill buffer with import structure */
00273                         mem_writed(bufptr+0x00,(unsigned int)GEMMIS_seg<<4u);
00274                         mem_writew(bufptr+0x04,GEMMIS_VERSION);
00275                         *retcode=6;
00276                         return true;
00277                         }
00278                 case 0x02:
00279                         if (!is_emm386) return false;
00280                         if (size!=2) return false;
00281                         mem_writeb(bufptr+0x00,(unsigned int)EMM_VERSION>>4u);          // version 4
00282                         mem_writeb(bufptr+0x01,EMM_MINOR_VERSION);
00283                         *retcode=2;
00284                         return true;
00285                 case 0x03:
00286                         if (!is_emm386) return false;
00287                         if (EMM_MINOR_VERSION < 0x2d) return false;
00288                         if (size!=4) return false;
00289                         mem_writew(bufptr+0x00,(Bit16u)(MEM_TotalPages()*4ul)); // max size (kb)
00290                         mem_writew(bufptr+0x02,0x80);                                                   // min size (kb)
00291                         *retcode=2;
00292                         return true;
00293         }
00294         return false;
00295 }
00296 
00297 static struct {
00298         bool enabled;
00299         Bit16u ems_handle;
00300         Bitu pm_interface;
00301         MemHandle private_area;
00302         Bit8u pic1_remapping,pic2_remapping;
00303 } vcpi ;
00304 
00305 struct MoveRegion {
00306         Bit32u bytes;
00307         Bit8u src_type;
00308         Bit16u src_handle;
00309         Bit16u src_offset;
00310         Bit16u src_page_seg;
00311         Bit8u dest_type;
00312         Bit16u dest_handle;
00313         Bit16u dest_offset;
00314         Bit16u dest_page_seg;
00315 };
00316 
00317 static Bit16u EMM_GetTotalPages(void) {
00318         Bitu count=MEM_FreeLargest()/4;
00319         if (count>0x7fff) count=0x7fff;
00320         return (Bit16u)count;
00321 }
00322 
00323 static Bit16u EMM_GetFreePages(void) {
00324         Bitu count=MEM_FreeTotal()/4;
00325         if (count>0x7fff) count=0x7fff;
00326         return (Bit16u)count;
00327 }
00328 
00329 static bool INLINE ValidHandle(Bit16u handle) {
00330         if (handle>=EMM_MAX_HANDLES) return false;
00331         if (emm_handles[handle].pages==NULL_HANDLE) return false;
00332         return true;
00333 }
00334 
00335 void EMS_ZeroAllocation(MemHandle mem,unsigned int pages) {
00336         PhysPt address;
00337 
00338         if (pages == 0) return;
00339         address = (PhysPt)mem * 4096ul;
00340         pages *= 4096u;
00341 
00342         if ((address+pages) > 0xC0000000ul) E_Exit("EMS_ZeroAllocation out of range");
00343         while (pages != 0) {
00344                 mem_writeb(address++,0);
00345                 pages--;
00346         }
00347 }
00348 
00349 extern bool dbg_zero_on_ems_allocmem;
00350 
00351 /* NTS: "page" in EMS refers to 16KB regions, not the 4KB memory pages we normally work with */
00352 static Bit8u EMM_AllocateMemory(Bit16u pages,Bit16u & dhandle,bool can_allocate_zpages) {
00353         /* Check for 0 page allocation */
00354         if (!pages) {
00355                 if (!can_allocate_zpages) return EMM_ZERO_PAGES;
00356         }
00357         /* Check for enough free pages */
00358         if ((MEM_FreeTotal()/ 4) < pages) { return EMM_OUT_OF_LOG;}
00359         Bit16u handle = 1;
00360         /* Check for a free handle */
00361         while (emm_handles[handle].pages != NULL_HANDLE) {
00362                 if (++handle >= EMM_MAX_HANDLES) {return EMM_OUT_OF_HANDLES;}
00363         }
00364         MemHandle mem = 0;
00365         if (pages) {
00366                 mem = MEM_AllocatePages(pages*4u,false);
00367                 if (!mem) E_Exit("EMS:Memory allocation failure");
00368                 else if (dbg_zero_on_ems_allocmem) EMS_ZeroAllocation(mem,pages*4u);
00369         }
00370         emm_handles[handle].pages = pages;
00371         emm_handles[handle].mem = mem;
00372         /* Change handle only if there is no error. */
00373         dhandle = handle;
00374         return EMM_NO_ERROR;
00375 }
00376 
00377 static Bit8u EMM_AllocateSystemHandle(Bit16u pages/*NTS: EMS pages are 16KB, this does not refer to 4KB CPU pages*/) {
00378         MemHandle mem = 0;
00379 
00380         /* Check for enough free pages */
00381         if ((MEM_FreeTotal()/ 4) < pages) { return EMM_OUT_OF_LOG;}
00382         Bit16u handle = EMM_SYSTEM_HANDLE;      // emm system handle (reserved for OS usage)
00383         /* Release memory if already allocated */
00384         if (emm_handles[handle].pages != NULL_HANDLE) {
00385                 MEM_ReleasePages(emm_handles[handle].mem);
00386         }
00387 
00388         /* NTS: DOSBox 0.74 and older versions of DOSBox-X allocated EMS memory with the "sequential" param == false */
00389         /* We now offer the option, if specified in the configuration, to allocate our system handle on an even megabyte.
00390          * Why? Well, if the system handle exists on an even megabyte, then it no longer matters what state the A20
00391          * gate is in. If the DOS game relies on EMM386.EXE but fiddles with the A20 gate while running in virtual 8086
00392          * mode, setting this option can help avoid crashes, where normally clearing the A20 gate prevents EMM386.EXE
00393          * from using it's protected mode structures (GDT, page tables) and the sudden aliasing turns them into junk
00394          * and the system crashes (random contents in DOS conventional memory interpreted as protected mode structures
00395          * doesn't work very well). */
00396         mem = 0;
00397         if (ems_syshandle_on_even_mb) {
00398                 mem = MEM_AllocatePages_A20_friendly(pages*4u,/*sequential=*/true);
00399                 if (!mem) LOG(LOG_MISC,LOG_WARN)("EMS: Despite configuration setting, I was unable to allocate EMS system handle on even megabyte");
00400         }
00401         if (!mem) mem = MEM_AllocatePages(pages*4u,/*sequential=*/true);
00402         if (!mem) E_Exit("EMS:System handle memory allocation failure");
00403         emm_handles[handle].pages = pages;
00404         emm_handles[handle].mem = mem;
00405         LOG(LOG_MISC,LOG_DEBUG)("EMS: OS handle allocated %u 16KB pages 0x%08lx-0x%08lx",
00406                 (unsigned int)pages,
00407                 (unsigned long)mem * 4096UL,
00408                 ((unsigned long)mem * 4096UL) + (pages * 16384UL) - 1ul);
00409         return EMM_NO_ERROR;
00410 }
00411 
00412 static Bit8u EMM_ReallocatePages(Bit16u handle, const Bit16u& pages) {
00413         /* Check for valid handle */
00414         if (!ValidHandle(handle)) return EMM_INVALID_HANDLE;
00415         if (emm_handles[handle].pages != 0) {
00416                 /* Check for enough pages */
00417                 if (!MEM_ReAllocatePages(emm_handles[handle].mem,pages*4u,false)) return EMM_OUT_OF_LOG;
00418         } else {
00419                 MemHandle mem = MEM_AllocatePages(pages*4u,false);
00420                 if (!mem) E_Exit("EMS:Memory allocation failure during reallocation");
00421                 emm_handles[handle].mem = mem;
00422         }
00423         /* Update size */
00424         emm_handles[handle].pages=pages;
00425         return EMM_NO_ERROR;
00426 }
00427 
00428 Bitu XMS_EnableA20(bool enable);
00429 Bitu XMS_GetEnabledA20(void);
00430 
00431 static Bit8u EMM_MapPage(Bitu phys_page,Bit16u handle,Bit16u log_page) {
00432 //      LOG_MSG("EMS MapPage handle %d phys %d log %d",handle,phys_page,log_page);
00433         /* Check for too high physical page */
00434         if (phys_page>=EMM_MAX_PHYS) return EMM_ILL_PHYS;
00435 
00436     /* Make sure the A20 gate is on, to avoid crashes.
00437      * This code maps pages into the page frame like EMM386.EXE
00438      * does, from extended memory. */
00439     /* TODO: We should NOT do this when emulating an EMM board
00440      *       because those cards have their own memory and do not
00441      *       use the motherboard's extended memory. */
00442     if (!XMS_GetEnabledA20()) XMS_EnableA20(true);
00443 
00444         /* unmapping doesn't need valid handle (as handle isn't used) */
00445         if (log_page==NULL_PAGE) {
00446                 /* Unmapping */
00447                 emm_mappings[phys_page].handle=NULL_HANDLE;
00448                 emm_mappings[phys_page].page=NULL_PAGE;
00449                 for (Bitu i=0;i<4;i++) 
00450                         PAGING_MapPage(EMM_PAGEFRAME4K+phys_page*4u+i,EMM_PAGEFRAME4K+phys_page*4u+i);
00451                 PAGING_ClearTLB();
00452                 return EMM_NO_ERROR;
00453         }
00454         /* Check for valid handle */
00455         if (!ValidHandle(handle)) return EMM_INVALID_HANDLE;
00456         
00457         if (log_page<emm_handles[handle].pages) {
00458                 /* Mapping it is */
00459                 emm_mappings[phys_page].handle=handle;
00460                 emm_mappings[phys_page].page=log_page;
00461                 
00462                 MemHandle memh=MEM_NextHandleAt(emm_handles[handle].mem,log_page*4u);
00463                 for (Bitu i=0;i<4;i++) {
00464                         PAGING_MapPage(EMM_PAGEFRAME4K+(unsigned int)phys_page*4u+i,(Bitu)memh);
00465                         memh=MEM_NextHandle(memh);
00466                 }
00467                 PAGING_ClearTLB();
00468                 return EMM_NO_ERROR;
00469         } else  {
00470                 /* Illegal logical page it is */
00471                 return EMM_LOG_OUT_RANGE;
00472         }
00473 }
00474 
00475 static Bit8u EMM_MapSegment(Bitu segment,Bit16u handle,Bit16u log_page) {
00476 //      LOG_MSG("EMS MapSegment handle %d segment %d log %d",handle,segment,log_page);
00477 
00478         bool valid_segment=false;
00479 
00480         if ((ems_type == EMS_MIXED) || (ems_type == EMS_EMM386)) {
00481                 if (segment<0xf000U+0x1000U) valid_segment=true;
00482         } else {
00483                 if ((segment>=0xa000U) && (segment<0xb000U)) {
00484                         valid_segment=true;             // allow mapping of graphics memory
00485                 }
00486                 if ((segment>=EMM_PAGEFRAME) && (segment<EMM_PAGEFRAME+0x1000U)) {
00487                         valid_segment=true;             // allow mapping of EMS page frame
00488                 }
00489 /*              if ((segment>=EMM_PAGEFRAME-0x1000) && (segment<EMM_PAGEFRAME)) {
00490                         valid_segment=true;
00491                 } */
00492         }
00493 
00494         if (valid_segment) {
00495                 Bit32s tphysPage = ((Bit32s)segment-(Bit32s)EMM_PAGEFRAME)/(Bit32s)(0x1000/EMM_MAX_PHYS);
00496 
00497                 /* unmapping doesn't need valid handle (as handle isn't used) */
00498                 if (log_page==NULL_PAGE) {
00499                         /* Unmapping */
00500                         if ((tphysPage>=0) && ((Bit32u)tphysPage<EMM_MAX_PHYS)) {
00501                                 emm_mappings[tphysPage].handle=NULL_HANDLE;
00502                                 emm_mappings[tphysPage].page=NULL_PAGE;
00503                         } else {
00504                                 emm_segmentmappings[segment>>10].handle=NULL_HANDLE;
00505                                 emm_segmentmappings[segment>>10].page=NULL_PAGE;
00506                         }
00507                         for (Bitu i=0;i<4;i++) 
00508                                 PAGING_MapPage(segment*16u/4096u+i,segment*16u/4096u+i);
00509                         PAGING_ClearTLB();
00510                         return EMM_NO_ERROR;
00511                 }
00512                 /* Check for valid handle */
00513                 if (!ValidHandle(handle)) return EMM_INVALID_HANDLE;
00514                 
00515                 if (log_page<emm_handles[handle].pages) {
00516                         /* Mapping it is */
00517                         if ((tphysPage>=0) && ((Bit32u)tphysPage<EMM_MAX_PHYS)) {
00518                                 emm_mappings[tphysPage].handle=handle;
00519                                 emm_mappings[tphysPage].page=log_page;
00520                         } else {
00521                                 emm_segmentmappings[segment>>10u].handle=handle;
00522                                 emm_segmentmappings[segment>>10u].page=log_page;
00523                         }
00524                         
00525                         MemHandle memh=MEM_NextHandleAt(emm_handles[handle].mem,log_page*4u);
00526                         for (Bitu i=0;i<4;i++) {
00527                                 PAGING_MapPage(segment*16u/4096u+i,(Bitu)memh);
00528                                 memh=MEM_NextHandle(memh);
00529                         }
00530                         PAGING_ClearTLB();
00531                         return EMM_NO_ERROR;
00532                 } else  {
00533                         /* Illegal logical page it is */
00534                         return EMM_LOG_OUT_RANGE;
00535                 }
00536         }
00537 
00538         return EMM_ILL_PHYS;
00539 }
00540 
00541 static Bit8u EMM_ReleaseMemory(Bit16u handle) {
00542         /* Check for valid handle */
00543         if (!ValidHandle(handle)) return EMM_INVALID_HANDLE;
00544 
00545         // should check for saved_page_map flag here, returning an error if it's true
00546         // as apps are required to restore the pagemap beforehand; to be checked
00547 //      if (emm_handles[handle].saved_page_map) return EMM_SAVEMAP_ERROR;
00548 
00549         if (emm_handles[handle].pages != 0) {
00550                 MEM_ReleasePages(emm_handles[handle].mem);
00551         }
00552         /* Reset handle */
00553         emm_handles[handle].mem=0;
00554         if (handle==0) {
00555                 emm_handles[handle].pages=0;    // OS handle is NEVER deallocated
00556         } else {
00557                 emm_handles[handle].pages=NULL_HANDLE;
00558         }
00559         emm_handles[handle].saved_page_map=false;
00560         memset(&emm_handles[handle].name,0,8);
00561         return EMM_NO_ERROR;
00562 }
00563 
00564 static Bit8u EMM_SavePageMap(Bit16u handle) {
00565         /* Check for valid handle */
00566         if (handle>=EMM_MAX_HANDLES || (handle != 0 && emm_handles[handle].pages==NULL_HANDLE)) {
00567                 return EMM_INVALID_HANDLE;
00568         }
00569         /* Check for previous save */
00570         if (emm_handles[handle].saved_page_map) return EMM_PAGE_MAP_SAVED;
00571         /* Copy the mappings over */
00572         for (Bitu i=0;i<EMM_MAX_PHYS;i++) {
00573                 emm_handles[handle].page_map[i].page=emm_mappings[i].page;
00574                 emm_handles[handle].page_map[i].handle=emm_mappings[i].handle;
00575         }
00576         emm_handles[handle].saved_page_map=true;
00577         return EMM_NO_ERROR;
00578 }
00579 
00580 static Bit8u EMM_RestoreMappingTable(void) {
00581         /* Move through the mappings table and setup mapping accordingly */
00582         for (Bitu i=0;i<0x40;i++) {
00583                 /* Skip the pageframe */
00584                 if ((i>=(unsigned int)EMM_PAGEFRAME/0x400U) && (i<((unsigned int)EMM_PAGEFRAME/0x400U)+(unsigned int)EMM_MAX_PHYS)) continue;
00585                 EMM_MapSegment(i<<10,emm_segmentmappings[i].handle,emm_segmentmappings[i].page);
00586         }
00587         for (Bitu i=0;i<EMM_MAX_PHYS;i++) {
00588                 EMM_MapPage(i,emm_mappings[i].handle,emm_mappings[i].page);
00589         }
00590 
00591         return EMM_NO_ERROR;
00592 }
00593 static Bit8u EMM_RestorePageMap(Bit16u handle) {
00594         /* Check for valid handle */
00595         if (handle>=EMM_MAX_HANDLES || (handle != 0 && emm_handles[handle].pages==NULL_HANDLE)) {
00596                 return EMM_INVALID_HANDLE;
00597         }
00598         /* Check for previous save */
00599         if (!emm_handles[handle].saved_page_map) return EMM_NO_SAVED_PAGE_MAP;
00600         /* Restore the mappings */
00601         emm_handles[handle].saved_page_map=false;
00602         for (Bitu i=0;i<EMM_MAX_PHYS;i++) {
00603                 emm_mappings[i].page=emm_handles[handle].page_map[i].page;
00604                 emm_mappings[i].handle=emm_handles[handle].page_map[i].handle;
00605         }
00606         return EMM_RestoreMappingTable();
00607 }
00608 
00609 static Bit8u EMM_GetPagesForAllHandles(PhysPt table,Bit16u & handles) {
00610         handles=0;
00611         for (Bit16u i=0;i<EMM_MAX_HANDLES;i++) {
00612                 if (emm_handles[i].pages!=NULL_HANDLE) {
00613                         handles++;
00614                         mem_writew(table,i);
00615                         mem_writew(table+2,emm_handles[i].pages);
00616                         table+=4;
00617                 }
00618         }
00619         return EMM_NO_ERROR;
00620 }
00621 
00622 static Bit8u EMM_PartialPageMapping(void) {
00623         PhysPt list,data;Bit16u count;
00624         switch (reg_al) {
00625         case 0x00:      /* Save Partial Page Map */
00626                 list = SegPhys(ds)+reg_si;
00627                 data = SegPhys(es)+reg_di;
00628                 count=mem_readw(list);list+=2;
00629                 mem_writew(data,count);data+=2;
00630                 for (;count>0;count--) {
00631                         Bit16u segment=mem_readw(list);list+=2;
00632                         if ((segment>=EMM_PAGEFRAME) && (segment<EMM_PAGEFRAME+0x1000u)) {
00633                                 Bit16u page = (unsigned int)(segment-EMM_PAGEFRAME) / (unsigned int)(EMM_PAGE_SIZE>>4u);
00634                                 mem_writew(data,segment);data+=2;
00635                                 MEM_BlockWrite(data,&emm_mappings[page],sizeof(EMM_Mapping));
00636                                 data+=sizeof(EMM_Mapping);
00637                         } else if ((ems_type == EMS_MIXED) || (ems_type == EMS_EMM386) || ((segment>=EMM_PAGEFRAME-0x1000) && (segment<EMM_PAGEFRAME)) || ((segment>=0xa000) && (segment<0xb000))) {
00638                                 mem_writew(data,segment);data+=2;
00639                                 MEM_BlockWrite(data,&emm_segmentmappings[segment>>10u],sizeof(EMM_Mapping));
00640                                 data+=sizeof(EMM_Mapping);
00641                         } else {
00642                                 return EMM_ILL_PHYS;
00643                         }
00644                 }
00645                 break;
00646         case 0x01:      /* Restore Partial Page Map */
00647                 data = SegPhys(ds)+reg_si;
00648                 count= mem_readw(data);data+=2;
00649                 for (;count>0;count--) {
00650                         Bit16u segment=mem_readw(data);data+=2;
00651                         if ((segment>=EMM_PAGEFRAME) && (segment<EMM_PAGEFRAME+0x1000)) {
00652                                 Bit16u page = (unsigned int)(segment-EMM_PAGEFRAME) / (unsigned int)(EMM_PAGE_SIZE>>4);
00653                                 MEM_BlockRead(data,&emm_mappings[page],sizeof(EMM_Mapping));
00654                         } else if ((ems_type == EMS_MIXED) || (ems_type == EMS_EMM386) || ((segment>=EMM_PAGEFRAME-0x1000) && (segment<EMM_PAGEFRAME)) || ((segment>=0xa000) && (segment<0xb000))) {
00655                                 MEM_BlockRead(data,&emm_segmentmappings[segment>>10u],sizeof(EMM_Mapping));
00656                         } else {
00657                                 return EMM_ILL_PHYS;
00658                         }
00659                         data+=sizeof(EMM_Mapping);
00660                 }
00661                 return EMM_RestoreMappingTable();
00662         case 0x02:      /* Get Partial Page Map Array Size */
00663                 reg_al=(Bit8u)(2u+reg_bx*(2u+sizeof(EMM_Mapping)));
00664                 break;
00665         default:
00666                 LOG(LOG_MISC,LOG_ERROR)("EMS:Call %2X Subfunction %2X not supported",reg_ah,reg_al);
00667                 return EMM_FUNC_NOSUP;
00668         }
00669         return EMM_NO_ERROR;
00670 }
00671 
00672 static Bit8u HandleNameSearch(void) {
00673         char name[9];
00674         Bit16u handle=0;PhysPt data;
00675         switch (reg_al) {
00676         case 0x00:      /* Get all handle names */
00677                 reg_al=0;data=SegPhys(es)+reg_di;
00678                 for (handle=0;handle<EMM_MAX_HANDLES;handle++) {
00679                         if (emm_handles[handle].pages!=NULL_HANDLE) {
00680                                 reg_al++;
00681                                 mem_writew(data,handle);
00682                                 MEM_BlockWrite(data+2,emm_handles[handle].name,8);
00683                                 data+=10;
00684                         }
00685                 }
00686                 break;
00687         case 0x01: /* Search for a handle name */
00688                 MEM_StrCopy(SegPhys(ds)+reg_si,name,8);name[8]=0;
00689                 for (handle=0;handle<EMM_MAX_HANDLES;handle++) {
00690                         if (emm_handles[handle].pages!=NULL_HANDLE) {
00691                                 if (!strncmp(name,emm_handles[handle].name,8)) {
00692                                         reg_dx=handle;
00693                                         return EMM_NO_ERROR;
00694                                 }
00695                         }
00696                 }
00697                 return EMM_NOT_FOUND;
00698                 break;
00699         case 0x02: /* Get Total number of handles */
00700                 reg_bx=EMM_MAX_HANDLES;
00701                 break;
00702         default:
00703                 LOG(LOG_MISC,LOG_ERROR)("EMS:Call %2X Subfunction %2X not supported",reg_ah,reg_al);
00704                 return EMM_INVALID_SUB;
00705         }
00706         return EMM_NO_ERROR;
00707 }
00708 
00709 static Bit8u GetSetHandleName(void) {
00710         Bit16u handle=reg_dx;
00711         switch (reg_al) {
00712         case 0x00:      /* Get Handle Name */
00713                 if (handle>=EMM_MAX_HANDLES || emm_handles[handle].pages==NULL_HANDLE) return EMM_INVALID_HANDLE;
00714                 MEM_BlockWrite(SegPhys(es)+reg_di,emm_handles[handle].name,8);
00715                 break;
00716         case 0x01:      /* Set Handle Name */
00717                 if (handle>=EMM_MAX_HANDLES || emm_handles[handle].pages==NULL_HANDLE) return EMM_INVALID_HANDLE;
00718                 MEM_BlockRead(SegPhys(es)+reg_di,emm_handles[handle].name,8);
00719                 break;
00720         default:
00721                 LOG(LOG_MISC,LOG_ERROR)("EMS:Call %2X Subfunction %2X not supported",reg_ah,reg_al);
00722                 return EMM_INVALID_SUB;
00723         }
00724         return EMM_NO_ERROR;
00725 
00726 }
00727 
00728 static Bit8u GetSetHandleAttributes(void) {
00729         switch (reg_al) {
00730         case 0x00:      // Get handle attribubtes
00731                 if (!ValidHandle(reg_dx)) return EMM_INVALID_HANDLE;
00732                 reg_al = EMM_VOLATILE;  // We only support volatile
00733                 break;
00734         case 0x01:      // Set handle attributes
00735                 if (!ValidHandle(reg_dx)) return EMM_INVALID_HANDLE;
00736                 switch (reg_bl) {
00737                 case EMM_VOLATILE: break;
00738                 case EMM_NONVOLATILE: return EMM_FEAT_NOSUP;
00739                 default: return EMM_ATTR_UNDEF;
00740                 }
00741         case 0x02:      // Get attribute capability
00742                 reg_al = EMM_VOLATILE;  // We only support volatile
00743                 break;
00744         default:
00745                 LOG(LOG_MISC,LOG_ERROR)("EMS:Call %2X Subfunction %2X not supported",reg_ah,reg_al);
00746                 return EMM_INVALID_SUB;                 
00747         }
00748         return EMM_NO_ERROR;
00749 }
00750 
00751 
00752 static void LoadMoveRegion(PhysPt data,MoveRegion & region) {
00753         region.bytes=mem_readd(data+0x0);
00754 
00755         region.src_type=mem_readb(data+0x4);
00756         region.src_handle=mem_readw(data+0x5);
00757         region.src_offset=mem_readw(data+0x7);
00758         region.src_page_seg=mem_readw(data+0x9);
00759 
00760         region.dest_type=mem_readb(data+0xb);
00761         region.dest_handle=mem_readw(data+0xc);
00762         region.dest_offset=mem_readw(data+0xe);
00763         region.dest_page_seg=mem_readw(data+0x10);
00764 }
00765 
00766 static Bit8u MemoryRegion(void) {
00767         MoveRegion region;
00768         Bit8u buf_src[MEM_PAGE_SIZE];
00769         Bit8u buf_dest[MEM_PAGE_SIZE];
00770         if (reg_al>1) {
00771                 LOG(LOG_MISC,LOG_ERROR)("EMS:Call %2X Subfunction %2X not supported",reg_ah,reg_al);
00772                 return EMM_FUNC_NOSUP;
00773         }
00774         LoadMoveRegion(SegPhys(ds)+reg_si,region);
00775         /* Parse the region for information */
00776         PhysPt src_mem = 0,dest_mem = 0;
00777         MemHandle src_handle = 0,dest_handle = 0;
00778         PhysPt src_off = 0,dest_off = 0;
00779     PhysPt src_remain = 0,dest_remain = 0;
00780         if (!region.src_type) {
00781                 src_mem=region.src_page_seg*16u+region.src_offset;
00782         } else {
00783                 if (!ValidHandle(region.src_handle)) return EMM_INVALID_HANDLE;
00784                 if ((emm_handles[region.src_handle].pages*EMM_PAGE_SIZE) < ((region.src_page_seg*EMM_PAGE_SIZE)+region.src_offset+region.bytes)) return EMM_LOG_OUT_RANGE;
00785                 src_handle=emm_handles[region.src_handle].mem;
00786         PhysPt pages=region.src_page_seg*4u+(region.src_offset/MEM_PAGE_SIZE);
00787                 for (;pages>0;pages--) src_handle=MEM_NextHandle(src_handle);
00788                 src_off=region.src_offset&(MEM_PAGE_SIZE-1);
00789                 src_remain=MEM_PAGE_SIZE-src_off;
00790         }
00791         if (!region.dest_type) {
00792                 dest_mem=region.dest_page_seg*16u+region.dest_offset;
00793         } else {
00794                 if (!ValidHandle(region.dest_handle)) return EMM_INVALID_HANDLE;
00795                 if (emm_handles[region.dest_handle].pages*EMM_PAGE_SIZE < (region.dest_page_seg*EMM_PAGE_SIZE)+region.dest_offset+region.bytes) return EMM_LOG_OUT_RANGE;
00796                 dest_handle=emm_handles[region.dest_handle].mem;
00797         PhysPt pages=region.dest_page_seg*4u+(region.dest_offset/MEM_PAGE_SIZE);
00798                 for (;pages>0;pages--) dest_handle=MEM_NextHandle(dest_handle);
00799                 dest_off=region.dest_offset&(MEM_PAGE_SIZE-1);
00800                 dest_remain=MEM_PAGE_SIZE-dest_off;
00801         }
00802     PhysPt toread;
00803     bool a20_was_enabled = XMS_GetEnabledA20();
00804 
00805     XMS_EnableA20(true);
00806         while (region.bytes>0) {
00807                 if (region.bytes>MEM_PAGE_SIZE) toread=MEM_PAGE_SIZE;
00808                 else toread=region.bytes;
00809                 /* Read from the source */
00810                 if (!region.src_type) {
00811                         MEM_BlockRead(src_mem,buf_src,toread);
00812                 } else {
00813                         if (toread<src_remain) {
00814                                 MEM_BlockRead(((unsigned long)src_handle*(unsigned long)MEM_PAGE_SIZE)+src_off,buf_src,toread);
00815                         } else {
00816                                 MEM_BlockRead(((unsigned long)src_handle*(unsigned long)MEM_PAGE_SIZE)+src_off,buf_src,src_remain);
00817                                 MEM_BlockRead(((unsigned long)MEM_NextHandle(src_handle)*(unsigned long)MEM_PAGE_SIZE),&buf_src[src_remain],toread-src_remain);
00818                         }
00819                 }
00820                 /* Check for a move */
00821                 if (reg_al==1) {
00822                         /* Read from the destination */
00823                         if (!region.dest_type) {
00824                                 MEM_BlockRead(dest_mem,buf_dest,toread);
00825                         } else {
00826                                 if (toread<dest_remain) {
00827                                         MEM_BlockRead(((unsigned long)dest_handle*(unsigned long)MEM_PAGE_SIZE)+dest_off,buf_dest,toread);
00828                                 } else {
00829                                         MEM_BlockRead(((unsigned long)dest_handle*(unsigned long)MEM_PAGE_SIZE)+dest_off,buf_dest,dest_remain);
00830                                         MEM_BlockRead(((unsigned long)MEM_NextHandle(dest_handle)*(unsigned long)MEM_PAGE_SIZE),&buf_dest[dest_remain],toread-dest_remain);
00831                                 }
00832                         }
00833                         /* Write to the source */
00834                         if (!region.src_type) {
00835                                 MEM_BlockWrite(src_mem,buf_dest,toread);
00836                         } else {
00837                                 if (toread<src_remain) {
00838                                         MEM_BlockWrite(((unsigned long)src_handle*(unsigned long)MEM_PAGE_SIZE)+src_off,buf_dest,toread);
00839                                 } else {
00840                                         MEM_BlockWrite(((unsigned long)src_handle*(unsigned long)MEM_PAGE_SIZE)+src_off,buf_dest,src_remain);
00841                                         MEM_BlockWrite(((unsigned long)MEM_NextHandle(src_handle)*(unsigned long)MEM_PAGE_SIZE),&buf_dest[src_remain],toread-src_remain);
00842                                 }
00843                         }
00844                 }
00845                 /* Write to the destination */
00846                 if (!region.dest_type) {
00847                         MEM_BlockWrite(dest_mem,buf_src,toread);
00848                 } else {
00849                         if (toread<dest_remain) {
00850                                 MEM_BlockWrite(((unsigned long)dest_handle*(unsigned long)MEM_PAGE_SIZE)+dest_off,buf_src,toread);
00851                         } else {
00852                                 MEM_BlockWrite(((unsigned long)dest_handle*(unsigned long)MEM_PAGE_SIZE)+dest_off,buf_src,dest_remain);
00853                                 MEM_BlockWrite(((unsigned long)MEM_NextHandle(dest_handle)*(unsigned long)MEM_PAGE_SIZE),&buf_src[dest_remain],toread-dest_remain);
00854                         }
00855                 }
00856                 /* Advance the pointers */
00857                 if (!region.src_type) src_mem+=(PhysPt)toread;
00858                 else src_handle=MEM_NextHandle(src_handle);
00859                 if (!region.dest_type) dest_mem+=(PhysPt)toread;
00860                 else dest_handle=MEM_NextHandle(dest_handle);
00861                 region.bytes-=(Bit32u)toread;
00862         }
00863 
00864     if (!a20_was_enabled) XMS_EnableA20(false);
00865     return EMM_NO_ERROR;
00866 }
00867 
00868 
00869 static Bitu INT67_Handler(void) {
00870         switch (reg_ah) {
00871         case 0x40:              /* Get Status */
00872                 reg_ah=EMM_NO_ERROR;    
00873                 break;
00874         case 0x41:              /* Get PageFrame Segment */
00875                 reg_bx=EMM_PAGEFRAME;
00876                 reg_ah=EMM_NO_ERROR;
00877                 break;
00878         case 0x42:              /* Get number of pages */
00879                 reg_dx=EMM_GetTotalPages();
00880                 reg_bx=EMM_GetFreePages();
00881                 reg_ah=EMM_NO_ERROR;
00882                 break;
00883         case 0x43:              /* Get Handle and Allocate Pages */
00884                 reg_ah=EMM_AllocateMemory(reg_bx,reg_dx,false);
00885                 break;
00886         case 0x44:              /* Map Expanded Memory Page */
00887                 reg_ah=EMM_MapPage(reg_al,reg_dx,reg_bx);
00888                 break;
00889         case 0x45:              /* Release handle and free pages */
00890                 reg_ah=EMM_ReleaseMemory(reg_dx);
00891                 break;
00892         case 0x46:              /* Get EMM Version */
00893                 reg_ah=EMM_NO_ERROR;
00894                 reg_al=EMM_VERSION;
00895                 break;
00896         case 0x47:              /* Save Page Map */
00897                 reg_ah=EMM_SavePageMap(reg_dx);
00898                 break;
00899         case 0x48:              /* Restore Page Map */
00900                 reg_ah=EMM_RestorePageMap(reg_dx);
00901                 break;
00902         case 0x4b:              /* Get Handle Count */
00903                 reg_bx=0;
00904                 for (unsigned int i=0;i<EMM_MAX_HANDLES;i++) if (emm_handles[i].pages!=NULL_HANDLE) reg_bx++;
00905                 reg_ah=EMM_NO_ERROR;
00906                 break;
00907         case 0x4c:              /* Get Pages for one Handle */
00908                 if (!ValidHandle(reg_dx)) {reg_ah=EMM_INVALID_HANDLE;break;}
00909                 reg_bx=emm_handles[reg_dx].pages;
00910                 reg_ah=EMM_NO_ERROR;
00911                 break;
00912         case 0x4d:              /* Get Pages for all Handles */
00913                 reg_ah=EMM_GetPagesForAllHandles(SegPhys(es)+reg_di,reg_bx);
00914                 break;
00915         case 0x4e:              /*Save/Restore Page Map */
00916                 switch (reg_al) {
00917                 case 0x00:      /* Save Page Map */
00918                         MEM_BlockWrite(SegPhys(es)+reg_di,emm_mappings,sizeof(emm_mappings));
00919                         reg_ah=EMM_NO_ERROR;
00920                         break;
00921                 case 0x01:      /* Restore Page Map */
00922                         MEM_BlockRead(SegPhys(ds)+reg_si,emm_mappings,sizeof(emm_mappings));
00923                         reg_ah=EMM_RestoreMappingTable();
00924                         break;
00925                 case 0x02:      /* Save and Restore Page Map */
00926                         MEM_BlockWrite(SegPhys(es)+reg_di,emm_mappings,sizeof(emm_mappings));
00927                         MEM_BlockRead(SegPhys(ds)+reg_si,emm_mappings,sizeof(emm_mappings));
00928                         reg_ah=EMM_RestoreMappingTable();
00929                         break;  
00930                 case 0x03:      /* Get Page Map Array Size */
00931                         reg_al=sizeof(emm_mappings);
00932                         reg_ah=EMM_NO_ERROR;
00933                         break;
00934                 default:
00935                         LOG(LOG_MISC,LOG_ERROR)("EMS:Call %2X Subfunction %2X not supported",reg_ah,reg_al);
00936                         reg_ah=EMM_INVALID_SUB;
00937                         break;
00938                 }
00939                 break;
00940         case 0x4f:      /* Save/Restore Partial Page Map */
00941                 reg_ah=EMM_PartialPageMapping();
00942                 break;
00943         case 0x50:      /* Map/Unmap multiple handle pages */
00944                 reg_ah = EMM_NO_ERROR;
00945                 switch (reg_al) {
00946                         case 0x00: // use physical page numbers
00947                                 {       PhysPt data = SegPhys(ds)+reg_si;
00948                                         for (int i=0; i<reg_cx; i++) {
00949                                                 Bit16u logPage  = mem_readw(data); data+=2;
00950                                                 Bit16u physPage = mem_readw(data); data+=2;
00951                                                 reg_ah = EMM_MapPage(physPage,reg_dx,logPage);
00952                                                 if (reg_ah!=EMM_NO_ERROR) break;
00953                                         }
00954                                 } break;
00955                         case 0x01: // use segment address 
00956                                 {       PhysPt data = SegPhys(ds)+reg_si;
00957                                         for (int i=0; i<reg_cx; i++) {
00958                                                 Bit16u logPage  = mem_readw(data); data+=2;
00959                                                 reg_ah = EMM_MapSegment(mem_readw(data),reg_dx,logPage); data+=2;
00960                                                 if (reg_ah!=EMM_NO_ERROR) break;
00961                                         }
00962                                 }
00963                                 break;
00964                         default:
00965                                 LOG(LOG_MISC,LOG_ERROR)("EMS:Call %2X Subfunction %2X not supported",reg_ah,reg_al);
00966                                 reg_ah=EMM_INVALID_SUB;
00967                                 break;
00968                 }
00969                 break;
00970         case 0x51:      /* Reallocate Pages */
00971                 reg_ah=EMM_ReallocatePages(reg_dx,reg_bx);
00972                 break;
00973         case 0x52: // Set/Get Handle attributes
00974                 reg_ah=GetSetHandleAttributes();
00975                 break;
00976         case 0x53: // Set/Get Handlename
00977                 reg_ah=GetSetHandleName();
00978                 break;
00979         case 0x54:      /* Handle Functions */
00980                 reg_ah=HandleNameSearch();
00981                 break;
00982         case 0x57:      /* Memory region */
00983                 reg_ah=MemoryRegion();
00984                 if (reg_ah) LOG(LOG_MISC,LOG_ERROR)("EMS:Function 57 move failed");
00985                 break;
00986         case 0x58: // Get mappable physical array address array
00987                 if (reg_al==0x00) {
00988                         PhysPt data = SegPhys(es)+reg_di;
00989                         Bit16u step = 0x1000 / EMM_MAX_PHYS;
00990                         for (Bit16u i=0; i<EMM_MAX_PHYS; i++) {
00991                                 mem_writew(data,EMM_PAGEFRAME+step*i);  data+=2;
00992                                 mem_writew(data,i);                                             data+=2;
00993                         }
00994                 }
00995                 // Set number of pages
00996                 reg_cx = EMM_MAX_PHYS;
00997                 reg_ah = EMM_NO_ERROR;
00998                 break;
00999         case 0x59: // Get hardware information
01000                 reg_ah=EMM_NO_ERROR;
01001                 switch (reg_al) {
01002                 case 0x00:      // Get hardware configuration
01003                         {
01004                                 PhysPt data=SegPhys(es)+reg_di;
01005                                 mem_writew(data,0x0400); data+=2;               // 1 page is 1K paragraphs (16KB)
01006                                 mem_writew(data,0x0000); data+=2;               // No alternate register sets
01007                                 mem_writew(data,sizeof(emm_mappings)); data+=2; // Context save area size
01008                                 mem_writew(data,0x0000); data+=2;               // No DMA channels
01009                                 mem_writew(data,0x0000);                        // Always 0 for LIM standard
01010                         }
01011                         break;
01012                 case 0x01:      // get unallocated raw page count
01013                         reg_dx=(Bit16u)(MEM_TotalPages()/4);            //Not entirely correct but okay
01014                         reg_bx=EMM_GetFreePages();
01015                         break;
01016                 default:
01017                         LOG(LOG_MISC,LOG_ERROR)("EMS:Call 59 subfct %2X not supported",reg_al);
01018                         reg_ah=EMM_INVALID_SUB;
01019                         break;
01020                 }
01021                 break;
01022         case 0x5A:              /* Allocate standard/raw Pages */
01023                 if (reg_al<=0x01) {
01024                         reg_ah=EMM_AllocateMemory(reg_bx,reg_dx,true);  // can allocate 0 pages
01025                 } else {
01026                         LOG(LOG_MISC,LOG_ERROR)("EMS:Call 5A subfct %2X not supported",reg_al);
01027                         reg_ah=EMM_INVALID_SUB;
01028                 }
01029                 break;
01030     case 0x70:              /* NEC PC-98 specific function group? */
01031         if (reg_al == 1) {
01032             if (IS_PC98_ARCH) {
01033                 /* NTS: EMM386.EXE for PC-98 will return an error code if BX > 1 */
01034                 /* NTS: VEMM486.EXE sets a flag but doesn't seem to care what BX is */
01035                 /* NTS: Neither one seems to remap segment B0000h as far as I can tell, so implementing this will be tricky and full of guesswork. */
01036                 /* See also [http://www.koizuka.jp/~koizuka/master.lib/MASTER.MAN.txt], section "ems_enablepageframe - NEC EMSページフレームバンクの操作" */
01037                 if (reg_bx <= 1) {
01038                     LOG(LOG_MISC,LOG_DEBUG)("EMS:Call 70 subfct %2X remapping EMS page frame at B000h to %s not yet implemented. Hope your DOS application does not rely on that.",
01039                         reg_al,reg_bx == 1 ? "system memory" : "video memory");
01040                 }
01041                 else {
01042                     LOG(LOG_MISC,LOG_DEBUG)("EMS:Call 70 subfct %2X given invalid value BX=%04x which will likely do nothing (master library EMS_ENABLEPAGEFRAME bug)",
01043                         reg_al,reg_bx);
01044                 }
01045 
01046                 reg_ah=EMM_INVALID_SUB;
01047             }
01048             else {
01049                 LOG(LOG_MISC,LOG_DEBUG)("EMS:Call 70 subfct %2X not supported outside PC-98 mode",reg_al);
01050                 reg_ah=EMM_INVALID_SUB;
01051             }
01052         }
01053         else {
01054             LOG(LOG_MISC,LOG_ERROR)("EMS:Call 70 subfct %2X not supported",reg_al);
01055             reg_ah=EMM_INVALID_SUB;
01056         }
01057         break;
01058         case 0xDE:              /* VCPI Functions */
01059                 if (!vcpi.enabled) {
01060                         LOG(LOG_MISC,LOG_ERROR)("EMS:VCPI Call %2X not supported",reg_al);
01061                         reg_ah=EMM_FUNC_NOSUP;
01062                 } else {
01063                         switch (reg_al) {
01064                         case 0x00:              /* VCPI Installation Check */
01065                                 if (((reg_cx==0) && (reg_di==0x0012)) || (cpu.pmode && (reg_flags & FLAG_VM))) {
01066                                         /* JEMM detected or already in v86 mode */
01067                                         reg_ah=EMM_NO_ERROR;
01068                                         reg_bx=0x100;
01069                                 } else {
01070                                         reg_ah=EMM_FUNC_NOSUP;
01071                                 }
01072                                 break;
01073                         case 0x01: {    /* VCPI Get Protected Mode Interface */
01074                                 Bit16u ct;
01075                                 /* Set up page table buffer */
01076                                 for (ct=0; ct<0xff; ct++) {
01077                                         real_writeb(SegValue(es),reg_di+ct*4+0x00,0x67);                // access bits
01078                                         real_writew(SegValue(es),reg_di+ct*4+0x01,ct*0x10);             // mapping
01079                                         real_writeb(SegValue(es),reg_di+ct*4+0x03,0x00);
01080                                 }
01081                                 for (ct=0xff; ct<0x100; ct++) {
01082                                         real_writeb(SegValue(es),reg_di+ct*4+0x00,0x67);                // access bits
01083                                         real_writew(SegValue(es),reg_di+ct*4+0x01,(ct-0xff)*0x10+0x1100);       // mapping
01084                                         real_writeb(SegValue(es),reg_di+ct*4+0x03,0x00);
01085                                 }
01086                                 /* adjust paging entries for page frame (if mapped) */
01087                                 for (ct=0; ct<4; ct++) { 
01088                                         Bit16u handle=emm_mappings[ct].handle;
01089                                         if (handle!=0xffff) {
01090                                                 Bit16u memh=(Bit16u)MEM_NextHandleAt(emm_handles[handle].mem,(unsigned int)emm_mappings[ct].page*4u);
01091                                                 Bit16u entry_addr=(unsigned int)reg_di+(unsigned int)(EMM_PAGEFRAME>>6u)+(unsigned int)(ct*0x10u);
01092                                                 real_writew(SegValue(es),entry_addr+0x00u+0x01u,(memh+0u)*0x10u);               // mapping of 1/4 of page
01093                                                 real_writew(SegValue(es),entry_addr+0x04u+0x01u,(memh+1u)*0x10u);               // mapping of 2/4 of page
01094                                                 real_writew(SegValue(es),entry_addr+0x08u+0x01u,(memh+2u)*0x10u);               // mapping of 3/4 of page
01095                                                 real_writew(SegValue(es),entry_addr+0x0cu+0x01u,(memh+3u)*0x10u);               // mapping of 4/4 of page
01096                                         }
01097                                 }
01098                                 reg_di+=0x400;          // advance pointer by 0x100*4
01099                                 
01100                                 /* Set up three descriptor table entries */
01101                                 Bit32u cbseg_low=(CALLBACK_GetBase()&0xffff)<<16;
01102                                 Bit32u cbseg_high=(CALLBACK_GetBase()&0x1f0000)>>16;
01103                                 /* Descriptor 1 (code segment, callback segment) */
01104                                 real_writed(SegValue(ds),reg_si+0x00,0x0000ffff|cbseg_low);
01105                                 real_writed(SegValue(ds),reg_si+0x04,0x00009a00|cbseg_high);
01106                                 /* Descriptor 2 (data segment, full access) */
01107                                 real_writed(SegValue(ds),reg_si+0x08,0x0000ffff);
01108                                 real_writed(SegValue(ds),reg_si+0x0c,0x00009200);
01109                                 /* Descriptor 3 (full access) */
01110                                 real_writed(SegValue(ds),reg_si+0x10,0x0000ffff);
01111                                 real_writed(SegValue(ds),reg_si+0x14,0x00009200);
01112 
01113                                 reg_ebx=(vcpi.pm_interface&0xffff);
01114                                 reg_ah=EMM_NO_ERROR;
01115                                 break;
01116                                 }
01117                         case 0x02:              /* VCPI Maximum Physical Address */
01118                                 reg_edx=((MEM_TotalPages()*MEM_PAGESIZE)-1)&0xfffff000;
01119                                 reg_ah=EMM_NO_ERROR;
01120                                 break;
01121                         case 0x03:              /* VCPI Get Number of Free Pages */
01122                                 reg_edx=(Bit32u)MEM_FreeTotal();
01123                                 reg_ah=EMM_NO_ERROR;
01124                                 break;
01125                         case 0x04: {    /* VCPI Allocate one Page */
01126                                 MemHandle mem = MEM_AllocatePages(1,false);
01127                                 if (mem) {
01128                                         reg_edx=(unsigned int)mem<<12u;
01129                                         reg_ah=EMM_NO_ERROR;
01130                                 } else {
01131                                         reg_ah=EMM_OUT_OF_LOG;
01132                                 }
01133                                 break;
01134                                 }
01135                         case 0x05:              /* VCPI Free Page */
01136                                 MEM_ReleasePages((MemHandle)((unsigned int)reg_edx>>12u));
01137                                 reg_ah=EMM_NO_ERROR;
01138                                 break;
01139                         case 0x06: {    /* VCPI Get Physical Address of Page in 1st MB */
01140                                 if (((unsigned int)(reg_cx<<8u)>=(unsigned int)EMM_PAGEFRAME) && ((unsigned int)(reg_cx<<8u)<(unsigned int)EMM_PAGEFRAME+0x1000u)) {
01141                                         /* Page is in Pageframe, so check what EMS-page it is
01142                                            and return the physical address */
01143                                         Bit8u phys_page;
01144                                         Bit16u mem_seg=(unsigned int)reg_cx<<8u;
01145                                         if (mem_seg<EMM_PAGEFRAME+0x400) phys_page=0;
01146                                         else if (mem_seg<EMM_PAGEFRAME+0x800) phys_page=1;
01147                                         else if (mem_seg<EMM_PAGEFRAME+0xc00) phys_page=2;
01148                                         else phys_page=3;
01149                                         Bit16u handle=emm_mappings[phys_page].handle;
01150                                         if (handle==0xffff) {
01151                                                 reg_ah=EMM_ILL_PHYS;
01152                                                 break;
01153                                         } else {
01154                                                 MemHandle memh=MEM_NextHandleAt(
01155                                                         emm_handles[handle].mem,
01156                                                         emm_mappings[phys_page].page*4u);
01157                                                 reg_edx=((unsigned int)memh+((unsigned int)reg_cx&3u))<<12u;
01158                                         }
01159                                 } else {
01160                                         /* Page not in Pageframe, so just translate into physical address */
01161                                         reg_edx=(unsigned int)reg_cx<<12u;
01162                                 }
01163 
01164                                 reg_ah=EMM_NO_ERROR;
01165                                 }
01166                                 break;
01167                         case 0x07:              /* VCPI Read CR0 */
01168                                 reg_ah=EMM_NO_ERROR;
01169                                 reg_ebx=(Bit32u)CPU_GET_CRX(0);
01170                                 break;
01171                         case 0x0a:              /* VCPI Get PIC Vector Mappings */
01172                                 reg_bx=vcpi.pic1_remapping;             // master PIC
01173                                 reg_cx=vcpi.pic2_remapping;             // slave PIC
01174                                 reg_ah=EMM_NO_ERROR;
01175                                 break;
01176                         case 0x0b:              /* VCPI Set PIC Vector Mappings */
01177                                 reg_flags&=(~FLAG_IF);
01178                                 vcpi.pic1_remapping=reg_bx&0xff;
01179                                 vcpi.pic2_remapping=reg_cx&0xff;
01180                                 reg_ah=EMM_NO_ERROR;
01181                                 break;
01182                         case 0x0c: {    /* VCPI Switch from V86 to Protected Mode */
01183                                 reg_flags&=(~FLAG_IF);
01184                                 CPU_SetCPL(0);
01185 
01186                                 /* Read data from ESI (linear address) */
01187                                 Bit32u new_cr3=mem_readd(reg_esi);
01188                                 Bit32u new_gdt_addr=mem_readd(reg_esi+4);
01189                                 Bit32u new_idt_addr=mem_readd(reg_esi+8);
01190                                 Bit16u new_ldt=mem_readw(reg_esi+0x0c);
01191                                 Bit16u new_tr=mem_readw(reg_esi+0x0e);
01192                                 Bit32u new_eip=mem_readd(reg_esi+0x10);
01193                                 Bit16u new_cs=mem_readw(reg_esi+0x14);
01194 
01195                                 /* Get GDT and IDT entries */
01196                                 Bit16u new_gdt_limit=mem_readw(new_gdt_addr);
01197                                 Bit32u new_gdt_base=mem_readd(new_gdt_addr+2);
01198                                 Bit16u new_idt_limit=mem_readw(new_idt_addr);
01199                                 Bit32u new_idt_base=mem_readd(new_idt_addr+2);
01200 
01201                                 /* Switch to protected mode, paging enabled if necessary */
01202                                 Bit32u new_cr0=(Bit32u)(CPU_GET_CRX(0)|1u);
01203                                 if (new_cr3!=0) new_cr0|=0x80000000;
01204                                 CPU_SET_CRX(0, new_cr0);
01205                                 CPU_SET_CRX(3, new_cr3);
01206 
01207                                 /* Load tables and initialize segment registers */
01208                                 CPU_LGDT(new_gdt_limit, new_gdt_base);
01209                                 CPU_LIDT(new_idt_limit, new_idt_base);
01210                                 if (CPU_LLDT(new_ldt)) LOG_MSG("VCPI:Could not load LDT with %x",new_ldt);
01211                                 if (CPU_LTR(new_tr)) LOG_MSG("VCPI:Could not load TR with %x",new_tr);
01212 
01213                                 CPU_SetSegGeneral(ds,0);
01214                                 CPU_SetSegGeneral(es,0);
01215                                 CPU_SetSegGeneral(fs,0);
01216                                 CPU_SetSegGeneral(gs,0);
01217 
01218 //                              MEM_A20_Enable(true);
01219 
01220                                 /* Switch to protected mode */
01221                                 reg_flags&=(~(FLAG_VM|FLAG_NT));
01222                                 reg_flags|=0x3000;
01223                                 CPU_JMP(true, new_cs, new_eip, 0);
01224                                 }
01225                                 break;
01226                         default:
01227                                 LOG(LOG_MISC,LOG_ERROR)("EMS:VCPI Call %x not supported",reg_ax);
01228                                 reg_ah=EMM_FUNC_NOSUP;
01229                                 break;
01230                         }
01231                 }
01232                 break;
01233         default:
01234                 LOG(LOG_MISC,LOG_ERROR)("EMS:Call %2X not supported",reg_ah);
01235                 reg_ah=EMM_FUNC_NOSUP;
01236                 break;
01237         }
01238         return CBRET_NONE;
01239 }
01240 
01241 static Bitu VCPI_PM_Handler() {
01242 //      LOG_MSG("VCPI PMODE handler, function %x",reg_ax);
01243         switch (reg_ax) {
01244         case 0xDE03:            /* VCPI Get Number of Free Pages */
01245                 reg_edx=(Bit32u)MEM_FreeTotal();
01246                 reg_ah=EMM_NO_ERROR;
01247                 break;
01248         case 0xDE04: {          /* VCPI Allocate one Page */
01249                 MemHandle mem = MEM_AllocatePages(1,false);
01250                 if (mem) {
01251                         reg_edx=(unsigned int)((unsigned int)mem<<12);
01252                         reg_ah=EMM_NO_ERROR;
01253                 } else {
01254                         reg_ah=EMM_OUT_OF_LOG;
01255                 }
01256                 break;
01257                 }
01258         case 0xDE05:            /* VCPI Free Page */
01259                 MEM_ReleasePages((MemHandle)(reg_edx>>12u));
01260                 reg_ah=EMM_NO_ERROR;
01261                 break;
01262         case 0xDE0C: {          /* VCPI Switch from Protected Mode to V86 */
01263                 reg_flags&=(~FLAG_IF);
01264 
01265                 /* Flags need to be filled in, VM=true, IOPL=3 */
01266                 mem_writed(SegPhys(ss) + (reg_esp & cpu.stack.mask)+0x10, 0x23002);
01267 
01268                 /* Disable Paging */
01269                 CPU_SET_CRX(0, CPU_GET_CRX(0)&0x7ffffff7);
01270                 CPU_SET_CRX(3, 0);
01271 
01272                 PhysPt tbaddr=(PhysPt)vcpi.private_area+0x0000u+(0x10u&0xfff8u)+5u;
01273                 Bit8u tb=mem_readb(tbaddr);
01274                 mem_writeb(tbaddr, tb&0xfd);
01275 
01276                 /* Load descriptor table registers */
01277                 CPU_LGDT(0xff, (unsigned int)vcpi.private_area+0x0000u);
01278                 CPU_LIDT(0x7ff, (unsigned int)vcpi.private_area+0x2000u);
01279                 if (CPU_LLDT(0x08)) LOG_MSG("VCPI:Could not load LDT");
01280                 if (CPU_LTR(0x10)) LOG_MSG("VCPI:Could not load TR");
01281 
01282                 reg_flags&=(~FLAG_NT);
01283                 reg_esp+=8;             // skip interrupt return information
01284 //              MEM_A20_Enable(false);
01285 
01286                 /* Switch to v86-task */
01287                 CPU_IRET(true,0);
01288                 }
01289                 break;
01290         default:
01291                 LOG(LOG_MISC,LOG_WARN)("Unhandled VCPI-function %x in protected mode",reg_al);
01292                 break;
01293         }
01294         return CBRET_NONE;
01295 }
01296 
01297 bool vcpi_virtual_a20 = true;
01298 
01299 /* if we handle the read, we're expected to write over AL/AX */
01300 bool VCPI_trapio_r(uint16_t port,unsigned int sz) {
01301     (void)sz;//UNUSED
01302         switch (port) {
01303                 case 0x92:
01304                         reg_al = vcpi_virtual_a20?0x02:0x00;
01305                         return true;
01306         }
01307 
01308         return false;
01309 }
01310 
01311 bool VCPI_trapio_w(uint16_t port,uint32_t data,unsigned int sz) {
01312     (void)sz;//UNUSED
01313         switch (port) {
01314                 case 0x92:
01315                         vcpi_virtual_a20 = (data & 2) ? true : false;
01316                         return true;
01317         }
01318 
01319         return false;
01320 }
01321 
01322 static Bitu V86_Monitor() {
01323         /* Calculate which interrupt did occur */
01324         Bitu int_num=((unsigned int)mem_readw(SegPhys(ss)+((unsigned int)reg_esp & (unsigned int)cpu.stack.mask)) - 0x2803u);
01325 
01326         /* See if Exception 0x0d and not Interrupt 0x0d */
01327         if ((int_num==(0x0d*4)) && ((reg_sp&0xffff)!=0x1fda)) {
01328                 /* Protection violation during V86-execution,
01329                    needs intervention by monitor (depends on faulting opcode) */
01330 
01331                 reg_esp+=6;             // skip ip of CALL and error code of EXCEPTION 0x0d
01332 
01333                 /* Get address of faulting instruction */
01334                 Bit16u v86_cs=mem_readw(SegPhys(ss)+((reg_esp+4) & cpu.stack.mask));
01335                 Bit16u v86_ip=mem_readw(SegPhys(ss)+((reg_esp+0) & cpu.stack.mask));
01336                 Bit8u v86_opcode=mem_readb(((unsigned int)v86_cs<<4u)+(unsigned int)v86_ip);
01337 //              LOG_MSG("v86 monitor caught protection violation at %x:%x, opcode=%x",v86_cs,v86_ip,v86_opcode);
01338                 switch (v86_opcode) {
01339                         case 0x0f:              // double byte opcode
01340                                 v86_opcode=mem_readb((unsigned int)(v86_cs<<4u)+(unsigned int)v86_ip+1u);
01341                                 switch (v86_opcode) {
01342                                         case 0x20: {    // mov reg,CRx
01343                                                 Bitu rm_val=mem_readb((unsigned int)(v86_cs<<4u)+(unsigned int)v86_ip+2u);
01344                                                 Bitu which=(unsigned int)(rm_val >> 3u) & 7u;
01345                                                 if ((rm_val<0xc0) || (rm_val>=0xe8))
01346                                                         E_Exit("Invalid opcode 0x0f 0x20 %x caused a protection fault!",static_cast<unsigned int>(rm_val));
01347                                                 Bit32u crx=(Bit32u)CPU_GET_CRX(which);
01348                                                 switch (rm_val&7) {
01349                                                         case 0: reg_eax=crx;    break;
01350                                                         case 1: reg_ecx=crx;    break;
01351                                                         case 2: reg_edx=crx;    break;
01352                                                         case 3: reg_ebx=crx;    break;
01353                                                         case 4: reg_esp=crx;    break;
01354                                                         case 5: reg_ebp=crx;    break;
01355                                                         case 6: reg_esi=crx;    break;
01356                                                         case 7: reg_edi=crx;    break;
01357                                                 }
01358                                                 mem_writew(SegPhys(ss)+((reg_esp+0u) & cpu.stack.mask),(unsigned int)v86_ip+3u);
01359                                                 }
01360                                                 break;
01361                                         case 0x22: {    // mov CRx,reg
01362                                                 Bitu rm_val=mem_readb((unsigned int)(v86_cs<<4u)+(unsigned int)v86_ip+2u);
01363                                                 Bitu which=(rm_val >> 3u) & 7u;
01364                                                 if ((rm_val<0xc0) || (rm_val>=0xe8))
01365                                                         E_Exit("Invalid opcode 0x0f 0x22 %x caused a protection fault!",static_cast<unsigned int>(rm_val));
01366                                                 Bit32u crx=0;
01367                                                 switch (rm_val&7) {
01368                                                         case 0: crx=reg_eax;    break;
01369                                                         case 1: crx=reg_ecx;    break;
01370                                                         case 2: crx=reg_edx;    break;
01371                                                         case 3: crx=reg_ebx;    break;
01372                                                         case 4: crx=reg_esp;    break;
01373                                                         case 5: crx=reg_ebp;    break;
01374                                                         case 6: crx=reg_esi;    break;
01375                                                         case 7: crx=reg_edi;    break;
01376                                                 }
01377                                                 if (which==0) crx|=1;   // protection bit always on
01378                                                 CPU_SET_CRX(which,crx);
01379                                                 mem_writew(SegPhys(ss)+((reg_esp+0) & cpu.stack.mask),v86_ip+3);
01380                                                 }
01381                                                 break;
01382                                         default:
01383                                                 E_Exit("Unhandled opcode 0x0f %x caused a protection fault!",v86_opcode);
01384                                 }
01385                                 break;
01386                         case 0xe4:              // IN AL,Ib
01387                                 if (!VCPI_trapio_r(mem_readb((unsigned int)(v86_cs<<4u)+(unsigned int)v86_ip+1u),1))
01388                                         reg_al=IO_ReadB(mem_readb((unsigned int)(v86_cs<<4)+(unsigned int)v86_ip+1))&0xff;
01389                                 mem_writew(SegPhys(ss)+((reg_esp+0) & cpu.stack.mask),v86_ip+2u);
01390                                 break;
01391                         case 0xe5:              // IN AX,Ib
01392                                 if (!VCPI_trapio_r(mem_readb((unsigned int)(v86_cs<<4u)+(unsigned int)v86_ip+1u),2))
01393                                         reg_ax=IO_ReadW(mem_readb((unsigned int)(v86_cs<<4)+(unsigned int)v86_ip+1))&0xffff;
01394                                 mem_writew(SegPhys(ss)+((reg_esp+0) & cpu.stack.mask),v86_ip+2u);
01395                                 break;
01396                         case 0xe6:              // OUT Ib,AL
01397                                 if (!VCPI_trapio_w(mem_readb((unsigned int)(v86_cs<<4u)+(unsigned int)v86_ip+1u),reg_al,1))
01398                                         IO_WriteB(mem_readb((unsigned int)(v86_cs<<4)+(unsigned int)v86_ip+1),reg_al);
01399                                 mem_writew(SegPhys(ss)+((reg_esp+0) & cpu.stack.mask),v86_ip+2u);
01400                                 break;
01401                         case 0xe7:              // OUT Ib,AX
01402                                 if (!VCPI_trapio_w(mem_readb((unsigned int)(v86_cs<<4u)+(unsigned int)v86_ip+1u),reg_ax,2u))
01403                                         IO_WriteW(mem_readb((unsigned int)(v86_cs<<4)+(unsigned int)v86_ip+1),reg_ax);
01404                                 mem_writew(SegPhys(ss)+((reg_esp+0) & cpu.stack.mask),v86_ip+2u);
01405                                 break;
01406                         case 0xec:              // IN AL,DX
01407                                 if (!VCPI_trapio_r(reg_dx,1))
01408                                         reg_al=IO_ReadB(reg_dx)&0xff;
01409                                 mem_writew(SegPhys(ss)+((reg_esp+0) & (unsigned int)cpu.stack.mask),(unsigned int)v86_ip+1u);
01410                                 break;
01411                         case 0xed:              // IN AX,DX
01412                                 if (!VCPI_trapio_r(reg_dx,2))
01413                                         reg_ax=IO_ReadW(reg_dx)&0xffff;
01414                                 mem_writew(SegPhys(ss)+((reg_esp+0) & (unsigned int)cpu.stack.mask),(unsigned int)v86_ip+1u);
01415                                 break;
01416                         case 0xee:              // OUT DX,AL
01417                                 if (!VCPI_trapio_w(reg_dx,reg_al,1))
01418                                         IO_WriteB(reg_dx,reg_al);
01419                                 mem_writew(SegPhys(ss)+((reg_esp+0) & (unsigned int)cpu.stack.mask),(unsigned int)v86_ip+1u);
01420                                 break;
01421                         case 0xef:              // OUT DX,AX
01422                                 if (!VCPI_trapio_w(reg_dx,reg_ax,2))
01423                                         IO_WriteW(reg_dx,reg_ax);
01424                                 mem_writew(SegPhys(ss)+((reg_esp+0) & (unsigned int)cpu.stack.mask),(unsigned int)v86_ip+1u);
01425                                 break;
01426                         case 0xf0:              // LOCK prefix
01427                                 mem_writew(SegPhys(ss)+((reg_esp+0) & (unsigned int)cpu.stack.mask),(unsigned int)v86_ip+1u);
01428                                 break;
01429                         case 0xf4:              // HLT
01430                                 reg_flags|=FLAG_IF;
01431                                 CPU_HLT(reg_eip);
01432                                 mem_writew(SegPhys(ss)+((reg_esp+0) & (unsigned int)cpu.stack.mask),(unsigned int)v86_ip+1u);
01433                                 break;
01434                         default:
01435                                 E_Exit("Unhandled opcode %x caused a protection fault!",v86_opcode);
01436                 }
01437                 return CBRET_NONE;
01438         }
01439 
01440         /* Get address to interrupt handler */
01441         Bit16u vint_vector_seg=mem_readw((PhysPt)(SegValue(ds)+int_num+2u));
01442         Bit16u vint_vector_ofs=mem_readw((PhysPt)int_num);
01443         if (reg_sp!=0x1fdau) reg_esp+=(2u+3u*4u);       // Interrupt from within protected mode
01444         else reg_esp+=2;
01445 
01446         /* Read entries that were pushed onto the stack by the interrupt */
01447         Bit16u return_ip=mem_readw(SegPhys(ss)+(reg_esp & cpu.stack.mask));
01448         Bit16u return_cs=mem_readw(SegPhys(ss)+((reg_esp+4u) & cpu.stack.mask));
01449         Bit32u return_eflags=mem_readd(SegPhys(ss)+((reg_esp+8u) & cpu.stack.mask));
01450 
01451         /* Modify stack to call v86-interrupt handler */
01452         mem_writed(SegPhys(ss)+(reg_esp & cpu.stack.mask),vint_vector_ofs);
01453         mem_writed(SegPhys(ss)+((reg_esp+4u) & cpu.stack.mask),vint_vector_seg);
01454         mem_writed(SegPhys(ss)+((reg_esp+8u) & cpu.stack.mask),return_eflags&(~(FLAG_IF|FLAG_TF)));
01455 
01456         /* Adjust SP of v86-stack */
01457         Bit16u v86_ss=mem_readw(SegPhys(ss)+((reg_esp+0x10u) & cpu.stack.mask));
01458         Bit16u v86_sp=mem_readw(SegPhys(ss)+((reg_esp+0x0cu) & cpu.stack.mask))-6u;
01459         mem_writew(SegPhys(ss)+((reg_esp+0x0cu) & cpu.stack.mask),v86_sp);
01460 
01461         /* Return to original code after v86-interrupt handler */
01462         mem_writew((unsigned int)(v86_ss<<4u)+(unsigned int)v86_sp+0u,return_ip);
01463         mem_writew((unsigned int)(v86_ss<<4u)+(unsigned int)v86_sp+2u,return_cs);
01464         mem_writew((unsigned int)(v86_ss<<4u)+(unsigned int)v86_sp+4u,(Bit16u)(return_eflags&0xffffu));
01465         return CBRET_NONE;
01466 }
01467 
01468 inline void VCPI_iopermw(uint16_t port,bool set) {
01469         unsigned char b;
01470 
01471         b = mem_readb((unsigned int)vcpi.private_area+0x3000u+0x68u+((unsigned int)port>>3u));
01472         if (set) b |= 1u<<(port&7u);
01473         else b &= ~(1u<<(port&7u));
01474         mem_writeb((unsigned int)vcpi.private_area+0x3000u+0x68u+((unsigned int)port>>3u),b);
01475 }
01476 
01477 static void SetupVCPI() {
01478     Bitu old_a20 = XMS_GetEnabledA20();
01479 
01480         /* The EMM OS handle is often located just above the 1MB boundary.
01481          * And we're about to write that area directly. So for obvious
01482          * reasons we should enable the A20 gate now. This fixes random
01483          * crashes in v86 mode when a20=mask as opposed to a20=fast. */
01484         if (((unsigned int)emm_handles[vcpi.ems_handle].mem<<12u) & (1u<<20u)) {
01485                 LOG(LOG_MISC,LOG_DEBUG)("EMS:EMM OS handle is associated with memory on an odd megabyte. Enabling A20 gate to avoid corrupting DOS state, will restore A20 state after this setup phase.");
01486                 XMS_EnableA20(true);
01487         }
01488 
01489         vcpi.ems_handle=0;      // use EMM system handle for VCPI data
01490 
01491         vcpi.enabled=true;
01492 
01493         vcpi.pic1_remapping=0x08;       // master PIC base
01494         vcpi.pic2_remapping=0x70;       // slave PIC base
01495 
01496         vcpi.private_area=(MemHandle)((unsigned int)emm_handles[vcpi.ems_handle].mem<<12u);
01497 
01498         /* GDT */
01499         mem_writed((unsigned int)vcpi.private_area+0x0000,0x00000000);  // descriptor 0
01500         mem_writed((unsigned int)vcpi.private_area+0x0004,0x00000000);  // descriptor 0
01501 
01502         Bit32u ldt_address=((unsigned int)vcpi.private_area+0x1000);
01503         Bit16u ldt_limit=0xff;
01504         Bit32u ldt_desc_part=(((unsigned int)ldt_address&0xffff)<<16u)|(unsigned int)ldt_limit;
01505         mem_writed((unsigned int)vcpi.private_area+0x0008,(unsigned int)ldt_desc_part); // descriptor 1 (LDT)
01506         ldt_desc_part=(((unsigned int)ldt_address&0xff0000)>>16)|((unsigned int)ldt_address&0xff000000)|0x8200;
01507         mem_writed((unsigned int)vcpi.private_area+0x000c,(unsigned int)ldt_desc_part); // descriptor 1
01508 
01509         Bit32u tss_address=((unsigned int)vcpi.private_area+0x3000);
01510         Bit32u tss_desc_part=(((unsigned int)tss_address&0xffff)<<16u)|(0x0068+0x200);
01511         mem_writed((unsigned int)vcpi.private_area+0x0010,(unsigned int)tss_desc_part); // descriptor 2 (TSS)
01512         tss_desc_part=(((unsigned int)tss_address&0xff0000)>>16)|((unsigned int)tss_address&0xff000000)|0x8900;
01513         mem_writed((unsigned int)vcpi.private_area+0x0014,(unsigned int)tss_desc_part); // descriptor 2
01514 
01515         /* LDT */
01516         mem_writed((unsigned int)vcpi.private_area+0x1000,0x00000000);  // descriptor 0
01517         mem_writed((unsigned int)vcpi.private_area+0x1004,0x00000000);  // descriptor 0
01518         Bit32u cs_desc_part=(((unsigned int)vcpi.private_area&0xffff)<<16u)|0xffff;
01519         mem_writed((unsigned int)vcpi.private_area+0x1008,(unsigned int)cs_desc_part);  // descriptor 1 (code)
01520         cs_desc_part=(((unsigned int)vcpi.private_area&0xff0000)>>16u)|((unsigned int)vcpi.private_area&0xff000000)|0x9a00;
01521         mem_writed((unsigned int)vcpi.private_area+0x100c,(unsigned int)cs_desc_part);  // descriptor 1
01522         Bit32u ds_desc_part=(((unsigned int)vcpi.private_area&0xffff)<<16u)|0xffff;
01523         mem_writed((unsigned int)vcpi.private_area+0x1010,(unsigned int)ds_desc_part);  // descriptor 2 (data)
01524         ds_desc_part=(((unsigned int)vcpi.private_area&0xff0000)>>16u)|((unsigned int)vcpi.private_area&0xff000000)|0x9200;
01525         mem_writed((unsigned int)vcpi.private_area+0x1014,(unsigned int)ds_desc_part);  // descriptor 2
01526 
01527         /* IDT setup */
01528         for (Bit16u int_ct=0; int_ct<0x100; int_ct++) {
01529                 /* build a CALL NEAR V86MON, the value of IP pushed by the
01530                         CALL is used to identify the interrupt number */
01531                 mem_writeb((unsigned int)vcpi.private_area+0x2800+(unsigned int)int_ct*4u+0,0xe8);      // call
01532                 mem_writew((unsigned int)vcpi.private_area+0x2800+(unsigned int)int_ct*4u+1,0x05fd-((unsigned int)int_ct*4u));
01533                 mem_writeb((unsigned int)vcpi.private_area+0x2800+(unsigned int)int_ct*4u+3,0xcf);      // iret (dummy)
01534 
01535                 /* put a Gate-Descriptor into the IDT */
01536                 mem_writed((unsigned int)vcpi.private_area+0x2000+(unsigned int)int_ct*8u+0,0x000c0000|(0x2800+(unsigned int)int_ct*4u));
01537                 mem_writed((unsigned int)vcpi.private_area+0x2000+(unsigned int)int_ct*8u+4,0x0000ee00);
01538         }
01539 
01540         /* TSS */
01541         for (Bitu tse_ct=0; tse_ct<0x68+0x2000/*all 65536 I/O ports*/; tse_ct++) {
01542                 /* clear the TSS as most entries are not used here */
01543                 mem_writeb((unsigned int)vcpi.private_area+0x3000+(unsigned int)tse_ct,0);
01544         }
01545 
01546         /* trap some ports */
01547         VCPI_iopermw(0x92,true);
01548 
01549         /* Set up the ring0-stack */
01550         mem_writed((unsigned int)vcpi.private_area+0x3004,0x00002000);  // esp
01551         mem_writed((unsigned int)vcpi.private_area+0x3008,0x00000014);  // ss
01552 
01553         mem_writed((unsigned int)vcpi.private_area+0x3066,0x0068);              // io-map base (map follows, all zero)
01554 
01555     XMS_EnableA20(old_a20 != 0);
01556 }
01557 
01558 static Bitu INT4B_Handler() {
01559         switch (reg_ah) {
01560         case 0x81:
01561                 CALLBACK_SCF(true);
01562                 reg_ax=0x1;
01563                 break;
01564         default:
01565                 LOG(LOG_MISC,LOG_WARN)("Unhandled interrupt 4B function %x",reg_ah);
01566                 break;
01567         }
01568         return CBRET_NONE;
01569 }
01570 
01571 Bitu GetEMSType(const Section_prop* section) {
01572         std::string emstypestr = section->Get_string("ems");
01573         Bitu rtype = 0;
01574 
01575         if (emstypestr == "true")
01576                 rtype = EMS_MIXED;
01577         else if (emstypestr == "emsboard")
01578                 rtype = EMS_BOARD;
01579         else if (emstypestr == "emm386")
01580                 rtype = EMS_EMM386;
01581         else
01582                 rtype = EMS_NONE;
01583 
01584         return rtype;
01585 }
01586 
01587 void setup_EMS_none() {
01588         if (zero_int67_if_no_ems) {
01589                 /* zero INT 67h */
01590                 phys_writed(0x67*4,0);
01591         }
01592 }
01593 
01594 Bitu call_int67 = 0;
01595 
01596 class EMS: public Module_base {
01597 private:
01598     Bit16u ems_baseseg = 0;
01599     DOS_Device* emm_device = NULL;
01600     unsigned int oshandle_memsize_16kb = 0;
01601     RealPt /*old4b_pointer,*/old67_pointer = 0/*NULL*/;
01602         CALLBACK_HandlerObject call_vdma,call_vcpi,call_v86mon;
01603 
01604 public:
01605         EMS(Section* configuration):Module_base(configuration) {
01606 
01607                 /* Virtual DMA interrupt callback */
01608                 call_vdma.Install(&INT4B_Handler,CB_IRET,"Int 4b vdma");
01609                 call_vdma.Set_RealVec(0x4b);
01610 
01611                 vcpi.enabled=false;
01612                 GEMMIS_seg=0;
01613 
01614                 Section_prop * section=static_cast<Section_prop *>(configuration);
01615                 ems_syshandle_on_even_mb = section->Get_bool("ems system handle on even megabyte");
01616                 zero_int67_if_no_ems = section->Get_bool("zero int 67h if no ems");
01617                 ems_type = GetEMSType(section);
01618                 if (ems_type == EMS_NONE) {
01619                         setup_EMS_none();
01620                         return;
01621                 }
01622 
01623                 if (machine == MCH_PCJR) {
01624                         setup_EMS_none();
01625                         ems_type = EMS_NONE;
01626                         LOG_MSG("EMS disabled for PCJr machine");
01627                         return;
01628                 }
01629 
01630         LOG_MSG("EMS page frame at 0x%04x-0x%04x",EMM_PAGEFRAME,EMM_PAGEFRAME+0xFFF);
01631 
01632                 ENABLE_VCPI = section->Get_bool("vcpi");
01633                 ENABLE_V86_STARTUP = section->Get_bool("emm386 startup active");
01634 
01635                 /* if 286 or lower, EMM386 emulation is impossible */
01636                 if (CPU_ArchitectureType < CPU_ARCHTYPE_386 && ems_type != EMS_BOARD) {
01637                         LOG_MSG("CPU is 286 or lower, setting EMS emulation to ems=emsboard and disabling VCPI and v86 startup");
01638                         ENABLE_V86_STARTUP = false;
01639                         ems_type = EMS_BOARD;
01640                         ENABLE_VCPI = false;
01641                 }
01642 
01643         bool XMS_Active(void);
01644 
01645         /* if XMS is not enabled, EMM386 emulation is impossible.
01646          * Real MS-DOS EMM386.EXE will flat out refuse to load if HIMEM.SYS is not loaded.
01647          * that also prevents VCPI from working. */
01648         if (!XMS_Active() && ems_type != EMS_BOARD) {
01649             if (ems_type == EMS_MIXED) {
01650                 LOG_MSG("EMS changed to board mode and VCPI disabled, because XMS is not enabled.");
01651                 ems_type = EMS_BOARD;
01652             }
01653             else if (ems_type == EMS_EMM386) {
01654                 /* do as MS-DOS does */
01655                 setup_EMS_none();
01656                 ems_type = EMS_NONE;
01657                 LOG_MSG("EMS disabled, EMM386 emulation is impossible when XMS is not enabled");
01658                 return;
01659             }
01660 
01661                         ENABLE_V86_STARTUP = false;
01662             ENABLE_VCPI = false;
01663         }
01664 
01665         if (ems_type != EMS_BOARD)
01666             BIOS_ZeroExtendedSize(true);
01667 
01668                 dbg_zero_on_ems_allocmem = section->Get_bool("zero memory on ems memory allocation");
01669                 if (dbg_zero_on_ems_allocmem) {
01670                         LOG(LOG_MISC,LOG_DEBUG)("Debug option enabled: EMS memory allocation will always clear memory block before returning\n");
01671                 }
01672 
01673                 /* FIXME: VM86 monitor is not stable! */
01674                 if (ENABLE_V86_STARTUP) {
01675                         LOG(LOG_MISC,LOG_WARN)("EMM386 virtual 8086 monitor is not stable! Use with caution!");
01676                 }
01677 
01678                 if (ems_type == EMS_BOARD && ENABLE_VCPI) {
01679                         LOG_MSG("VCPI emulation is incompatible with ems=board. Turning off VCPI emulation");
01680                         ENABLE_VCPI=false;
01681                 }
01682                 if (ems_type != EMS_EMM386 && ENABLE_V86_STARTUP) {
01683                         /* starting up in virtual 8086 mode makes no sense unless emulating EMM386.EXE */
01684                         if (ems_type != EMS_MIXED) {
01685                                 /* v86 startup is default. to avoid yelling at the user do not print anything unless
01686                                  * the user set it to any value other than the default ems=true */
01687                                 LOG_MSG("EMS EMM386.EXE v86 mode is incompatible with ems= setting. Starting up in real mode.");
01688                         }
01689                         ENABLE_V86_STARTUP=false;
01690                 }
01691                 if (ENABLE_V86_STARTUP && !ENABLE_VCPI) {
01692                         LOG_MSG("EMS: DOSBox does not support enabling virtual 8086 mode without VCPI.");
01693                         ENABLE_V86_STARTUP=false;
01694                 }
01695 
01696                 oshandle_memsize_16kb = (unsigned int)section->Get_int("ems system handle memory size");
01697                 /* convert KB to 16KB pages */
01698                 oshandle_memsize_16kb = (oshandle_memsize_16kb+15u)/16u;
01699                 if (oshandle_memsize_16kb == 0) oshandle_memsize_16kb = 1;
01700 
01701                 ems_baseseg=DOS_GetMemory(2,"ems_baseseg");     //We have 32 bytes
01702 
01703                 /* Add a little hack so it appears that there is an actual ems device installed */
01704                 char const* emsname="EMMXXXX0";
01705                 MEM_BlockWrite(PhysMake(ems_baseseg,0xa),emsname,(Bitu)(strlen(emsname)+1));
01706 
01707                 call_int67=CALLBACK_Allocate();
01708                 CALLBACK_Setup(call_int67,&INT67_Handler,CB_IRET,PhysMake(ems_baseseg,4),"Int 67 ems");
01709                 RealSetVec(0x67,RealMake(ems_baseseg,4),old67_pointer);
01710 
01711                 /* Register the ems device */
01712                 //TODO MAYBE put it in the class.
01713                 emm_device = new device_EMM(ems_type != EMS_BOARD);
01714                 DOS_AddDevice(emm_device);
01715 
01716                 /* Clear handle and page tables */
01717                 Bitu i;
01718                 for (i=0;i<EMM_MAX_HANDLES;i++) {
01719                         emm_handles[i].mem=0;
01720                         emm_handles[i].pages=NULL_HANDLE;
01721                         memset(&emm_handles[i].name,0,8);
01722                 }
01723                 for (i=0;i<EMM_MAX_PHYS;i++) {
01724                         emm_mappings[i].page=NULL_PAGE;
01725                         emm_mappings[i].handle=NULL_HANDLE;
01726                 }
01727                 for (i=0;i<0x40;i++) {
01728                         emm_segmentmappings[i].page=NULL_PAGE;
01729                         emm_segmentmappings[i].handle=NULL_HANDLE;
01730                 }
01731 
01732                 if (EMM_AllocateSystemHandle(oshandle_memsize_16kb) != EMM_NO_ERROR) { // allocate OS-dedicated handle (ems handle zero, 384kb)
01733                         LOG_MSG("EMS:Unable to allocate EMS system handle. disabling VCPI");
01734                         ENABLE_VCPI = false;
01735                 }
01736 
01737                 if (ems_type == EMS_EMM386) {
01738                         DMA_SetWrapping(0xffffffff);    // emm386-bug that disables dma wrapping
01739                 }
01740 
01741                 /* the VCPI emulation requires a large enough OS handle memory region. */
01742                 if (ENABLE_VCPI && oshandle_memsize_16kb < (0x4000/16384)) { /* at least 16KB */
01743                         LOG_MSG("EMS:System handle memory size too small (<16KB), disabling VCPI");
01744                         ENABLE_VCPI = false;
01745                 }
01746 
01747                 if (ENABLE_VCPI) {
01748                         assert(ems_type != EMS_BOARD);
01749                         LOG(LOG_MISC,LOG_DEBUG)("Enabling VCPI emulation");
01750 
01751                         /* Install a callback that handles VCPI-requests in protected mode requests */
01752                         call_vcpi.Install(&VCPI_PM_Handler,CB_IRETD,"VCPI PM");
01753                         vcpi.pm_interface=(call_vcpi.Get_callback())*CB_SIZE;
01754 
01755                         /* Initialize private data area and set up descriptor tables */
01756                         SetupVCPI();
01757 
01758                         if (!vcpi.enabled) return;
01759 
01760                         /* Install v86-callback that handles interrupts occuring
01761                            in v86 mode, including protection fault exceptions */
01762                         call_v86mon.Install(&V86_Monitor,CB_IRET,"V86 Monitor");
01763 
01764             {
01765                 Bitu old_a20 = XMS_GetEnabledA20();
01766 
01767                 XMS_EnableA20(true);
01768 
01769                 mem_writeb((unsigned int)vcpi.private_area+0x2e00,(Bit8u)0xFE);       //GRP 4
01770                 mem_writeb((unsigned int)vcpi.private_area+0x2e01,(Bit8u)0x38);       //Extra Callback instruction
01771                 mem_writew((unsigned int)vcpi.private_area+0x2e02,call_v86mon.Get_callback());          //The immediate word
01772                 mem_writeb((unsigned int)vcpi.private_area+0x2e04,(Bit8u)0x66);
01773                 mem_writeb((unsigned int)vcpi.private_area+0x2e05,(Bit8u)0xCF);       //A IRETD Instruction
01774 
01775                 XMS_EnableA20(old_a20 != 0);
01776             }
01777 
01778                         /* DOSBox's default EMS emulation provides the EMS memory mapping but without the virtual 8086
01779                          * mode. But there are DOS games and demos that assume EMM386.EXE == virtual 8086 mode and will
01780                          * fail to detect EMS if the v86 bit in EFLAGS is not set.
01781                          *
01782                          * Examples:
01783                          *   ftp://ftp.scene.org/pub/parties/1994/3s94/demo/friends.zip
01784                          *     - Refuses to run unless it detects Expanded memory
01785                          *     - Does not detect Expanded memory because it doesn't detect virtual 8086 mode
01786                          *     - Therefore, without this option, it is impossible to run the demo. */
01787                         if (ENABLE_V86_STARTUP) {
01788                                 LOG(LOG_MISC,LOG_DEBUG)("EMS: Now setting up the DOS environment to run in EMM386.EXE virtual 8086 mode");
01789 
01790                                 /* our V86 state may require A20 enabled to run. the EMM OS handle
01791                                  * often resides on an odd megabyte */
01792                                 if (((unsigned int)emm_handles[vcpi.ems_handle].mem<<12u) & (1u<<20u)) {
01793                                         LOG(LOG_MISC,LOG_DEBUG)("EMS:EMM OS handle is associated with memory on an odd megabyte. Enabling A20 gate to safely enter V86 mode.");
01794                                         XMS_EnableA20(true);
01795                                 }
01796                                 vcpi_virtual_a20 = true;
01797 
01798                                 /* Prepare V86-task */
01799                                 CPU_SET_CRX(0, 1);
01800                                 CPU_LGDT(0xff, (unsigned int)vcpi.private_area+0x0000);
01801                                 CPU_LIDT(0x7ff, (unsigned int)vcpi.private_area+0x2000);
01802                                 if (CPU_LLDT(0x08)) LOG_MSG("VCPI:Could not load LDT");
01803                                 if (CPU_LTR(0x10)) LOG_MSG("VCPI:Could not load TR");
01804 
01805                                 /* TODO: Page tables are usually involved as well. That is the "magic"
01806                                  * behind EMM386.EXE page frames. */
01807 
01808                                 /* TODO: Also setup (here or in SetupVCPI) the I/O permission bitmap
01809                                  * so that the V86 monitor can virtualize certain I/O ports. EMM386.EXE
01810                                  * emulation would demand that we at least virtualize the DMA controller
01811                                  * I/O ports as MS-DOS does. */
01812 
01813                                 /* register setup */
01814                                 CPU_Push32(SegValue(gs));
01815                                 CPU_Push32(SegValue(fs));
01816                                 CPU_Push32(SegValue(ds));
01817                                 CPU_Push32(SegValue(es));
01818                                 CPU_Push32(SegValue(ss));
01819                                 CPU_Push32(0x23002); /* FIXME: Confirm: this is EFLAGS? */
01820                                 CPU_Push32(SegValue(cs));
01821                                 CPU_Push32(reg_eip&0xffff);
01822                                 /* Switch to V86-mode */
01823                                 CPU_SetCPL(0);
01824                                 CPU_IRET(true,0);
01825                         }
01826                 }
01827         }
01828         
01829         ~EMS() {
01830                 if (ems_type==0) return;
01831 
01832                 /* Undo Biosclearing */
01833                 BIOS_ZeroExtendedSize(false);
01834  
01835                 /* Remove ems device */
01836                 if (emm_device!=NULL) {
01837 // FIXME: This is being called now after DOS shutdown. Causes a crash!
01838 //                      DOS_DelDevice(emm_device);
01839                         emm_device=NULL;
01840                 }
01841                 GEMMIS_seg=0;
01842 
01843                 /* Remove the emsname and callback hack */
01844                 if (ems_baseseg != 0) {
01845                         char buf[32]= { 0 };
01846                         MEM_BlockWrite(PhysMake(ems_baseseg,0),buf,32);
01847                 }
01848                 RealSetVec(0x67,zero_int67_if_no_ems ? 0 : old67_pointer);
01849 
01850 #if 0 // FIXME
01851                 /* Release memory allocated to system handle */
01852                 if (emm_handles[EMM_SYSTEM_HANDLE].pages != NULL_HANDLE) {
01853                         MEM_ReleasePages(emm_handles[EMM_SYSTEM_HANDLE].mem);
01854                 }
01855 #endif
01856 
01857                 /* Clear handle and page tables */
01858                 //TODO
01859 
01860                 if (ENABLE_VCPI && vcpi.enabled) {
01861                         if (cpu.pmode && GETFLAG(VM)) {
01862                                 /* Switch back to real mode if in v86-mode */
01863                                 CPU_SET_CRX(0, 0);
01864                                 CPU_SET_CRX(3, 0);
01865                                 reg_flags&=(~(FLAG_IOPL|FLAG_VM));
01866                                 CPU_LIDT(0x3ff, 0);
01867                                 CPU_SetCPL(0);
01868                         }
01869                 }
01870         }
01871 };
01872                 
01873 static EMS* test = NULL;
01874 
01875 void CALLBACK_DeAllocate(Bitu in);
01876 
01877 void EMS_DoShutDown() {
01878         if (test != NULL) {
01879                 delete test;
01880                 test = NULL;
01881         }
01882     if (call_int67 != 0) {
01883         CALLBACK_DeAllocate(call_int67);
01884         call_int67 = 0;
01885     }
01886 }
01887 
01888 void EMS_PickPageFrame(void) {
01889     /* the EMS page frame needs to move depending on IBM PC or PC-98 emulation.
01890      * IBM PC emulation can put the page frame at 0xE000 (as DOSBox has always done).
01891      * PC-98 emulation needs to move the page frame down because 0xE000 is taken by the 4th EGC bitplane. */
01892     EMM_PAGEFRAME =      IS_PC98_ARCH ? 0xD000 : 0xE000;
01893     EMM_PAGEFRAME4K =    ((EMM_PAGEFRAME*16)/4096);
01894 }
01895 
01896 void EMS_DOSBoot(Section* /*sec*/) {
01897     EMS_PickPageFrame();
01898 }
01899 
01900 void EMS_ShutDown(Section* /*sec*/) {
01901         EMS_DoShutDown();
01902 }
01903 
01904 void EMS_Startup(Section* sec) {
01905     (void)sec;//UNUSED
01906         if (test == NULL) {
01907                 LOG(LOG_MISC,LOG_DEBUG)("Allocating EMS emulation");
01908                 test = new EMS(control->GetSection("dos"));
01909         }
01910 }
01911 
01912 void EMS_Init() {
01913         LOG(LOG_MISC,LOG_DEBUG)("Initializing EMS expanded memory services");
01914 
01915         AddExitFunction(AddExitFunctionFuncPair(EMS_ShutDown),true);
01916         AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(EMS_ShutDown));
01917     AddVMEventFunction(VM_EVENT_DOS_BOOT,AddVMEventFunctionFuncPair(EMS_DOSBoot));
01918         AddVMEventFunction(VM_EVENT_DOS_EXIT_BEGIN,AddVMEventFunctionFuncPair(EMS_ShutDown));
01919 }
01920 
01921 //save state support
01922 namespace
01923 {
01924 class SerializeEMS : public SerializeGlobalPOD
01925 {
01926 public:
01927     SerializeEMS() : SerializeGlobalPOD("EMS")
01928     {
01929         registerPOD(emm_handles);
01930         registerPOD(emm_mappings);
01931         registerPOD(emm_segmentmappings);
01932         registerPOD(GEMMIS_seg);
01933                 registerPOD(vcpi.enabled);
01934         registerPOD(vcpi.ems_handle);
01935         registerPOD(vcpi.pm_interface);
01936         registerPOD(vcpi.private_area);
01937         registerPOD(vcpi.pic1_remapping);
01938         registerPOD(vcpi.pic2_remapping);
01939     }
01940 } dummy;
01941 }