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