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