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 00020 #include <stdint.h> 00021 #include <assert.h> 00022 #include "dosbox.h" 00023 #include "dos_inc.h" 00024 #include "mem.h" 00025 #include "inout.h" 00026 #include "setup.h" 00027 #include "paging.h" 00028 #include "programs.h" 00029 #include "zipfile.h" 00030 #include "regs.h" 00031 #ifndef WIN32 00032 # include <stdlib.h> 00033 # include <unistd.h> 00034 # include <stdio.h> 00035 #endif 00036 00037 #include "voodoo.h" 00038 00039 #include <string.h> 00040 00041 static MEM_Callout_t lfb_mem_cb = MEM_Callout_t_none; 00042 static MEM_Callout_t lfb_mmio_cb = MEM_Callout_t_none; 00043 00044 #define MEM_callouts_max (MEM_TYPE_MAX - MEM_TYPE_MIN) 00045 #define MEM_callouts_index(t) (t - MEM_TYPE_MIN) 00046 00047 class MEM_callout_vector : public std::vector<MEM_CalloutObject> { 00048 public: 00049 MEM_callout_vector() : std::vector<MEM_CalloutObject>(), getcounter(0), alloc_from(0) { }; 00050 public: 00051 unsigned int getcounter; 00052 unsigned int alloc_from; 00053 }; 00054 00055 static MEM_callout_vector MEM_callouts[MEM_callouts_max]; 00056 00057 bool a20_guest_changeable = true; 00058 bool a20_fake_changeable = false; 00059 bool a20_fast_changeable = false; 00060 00061 bool enable_port92 = true; 00062 bool has_Init_RAM = false; 00063 bool has_Init_MemHandles = false; 00064 bool has_Init_MemoryAccessArray = false; 00065 00066 extern Bitu rombios_minimum_location; 00067 extern bool VIDEO_BIOS_always_carry_14_high_font; 00068 extern bool VIDEO_BIOS_always_carry_16_high_font; 00069 00070 static struct MemoryBlock { 00071 MemoryBlock() : pages(0), handler_pages(0), reported_pages(0), phandlers(NULL), mhandles(NULL), mem_alias_pagemask(0), mem_alias_pagemask_active(0), address_bits(0) { } 00072 00073 Bitu pages; 00074 Bitu handler_pages; 00075 Bitu reported_pages; 00076 PageHandler * * phandlers; 00077 MemHandle * mhandles; 00078 struct { 00079 Bitu start_page; 00080 Bitu end_page; 00081 Bitu pages; 00082 PageHandler *handler; 00083 } lfb = {}; 00084 struct { 00085 Bitu start_page; 00086 Bitu end_page; 00087 Bitu pages; 00088 PageHandler *handler; 00089 } lfb_mmio = {}; 00090 struct { 00091 bool enabled; 00092 Bit8u controlport; 00093 } a20 = {}; 00094 Bit32u mem_alias_pagemask; 00095 Bit32u mem_alias_pagemask_active; 00096 Bit32u address_bits; 00097 } memory; 00098 00099 Bit32u MEM_get_address_bits() { 00100 return memory.address_bits; 00101 } 00102 00103 HostPt MemBase = NULL; 00104 00105 class UnmappedPageHandler : public PageHandler { 00106 public: 00107 UnmappedPageHandler() : PageHandler(PFLAG_INIT|PFLAG_NOCODE) {} 00108 Bit8u readb(PhysPt addr) { 00109 (void)addr;//UNUSED 00110 return 0xFF; /* Real hardware returns 0xFF not 0x00 */ 00111 } 00112 void writeb(PhysPt addr,Bit8u val) { 00113 (void)addr;//UNUSED 00114 (void)val;//UNUSED 00115 } 00116 }; 00117 00118 class IllegalPageHandler : public PageHandler { 00119 public: 00120 IllegalPageHandler() : PageHandler(PFLAG_INIT|PFLAG_NOCODE) {} 00121 Bit8u readb(PhysPt addr) { 00122 (void)addr; 00123 #if C_DEBUG 00124 LOG_MSG("Warning: Illegal read from %x, CS:IP %8x:%8x",addr,SegValue(cs),reg_eip); 00125 #else 00126 static Bits lcount=0; 00127 if (lcount<1000) { 00128 lcount++; 00129 //LOG_MSG("Warning: Illegal read from %x, CS:IP %8x:%8x",addr,SegValue(cs),reg_eip); 00130 } 00131 #endif 00132 return 0xFF; /* Real hardware returns 0xFF not 0x00 */ 00133 } 00134 void writeb(PhysPt addr,Bit8u val) { 00135 (void)addr;//UNUSED 00136 (void)val;//UNUSED 00137 #if C_DEBUG 00138 LOG_MSG("Warning: Illegal write to %x, CS:IP %8x:%8x",addr,SegValue(cs),reg_eip); 00139 #else 00140 static Bits lcount=0; 00141 if (lcount<1000) { 00142 lcount++; 00143 //LOG_MSG("Warning: Illegal write to %x, CS:IP %8x:%8x",addr,SegValue(cs),reg_eip); 00144 } 00145 #endif 00146 } 00147 }; 00148 00149 class RAMPageHandler : public PageHandler { 00150 public: 00151 RAMPageHandler() : PageHandler(PFLAG_READABLE|PFLAG_WRITEABLE) {} 00152 RAMPageHandler(Bitu flags) : PageHandler(flags) {} 00153 HostPt GetHostReadPt(Bitu phys_page) { 00154 if (!a20_fast_changeable || (phys_page & (~0xFul/*64KB*/)) == 0x100ul/*@1MB*/) 00155 return MemBase+(phys_page&memory.mem_alias_pagemask_active)*MEM_PAGESIZE; 00156 00157 return MemBase+phys_page*MEM_PAGESIZE; 00158 } 00159 HostPt GetHostWritePt(Bitu phys_page) { 00160 if (!a20_fast_changeable || (phys_page & (~0xFul/*64KB*/)) == 0x100ul/*@1MB*/) 00161 return MemBase+(phys_page&memory.mem_alias_pagemask_active)*MEM_PAGESIZE; 00162 00163 return MemBase+phys_page*MEM_PAGESIZE; 00164 } 00165 }; 00166 00167 class ROMAliasPageHandler : public PageHandler { 00168 public: 00169 ROMAliasPageHandler() { 00170 flags=PFLAG_READABLE|PFLAG_HASROM; 00171 } 00172 HostPt GetHostReadPt(Bitu phys_page) { 00173 return MemBase+((phys_page&0xF)+0xF0)*MEM_PAGESIZE; 00174 } 00175 HostPt GetHostWritePt(Bitu phys_page) { 00176 return MemBase+((phys_page&0xF)+0xF0)*MEM_PAGESIZE; 00177 } 00178 }; 00179 00180 class ROMPageHandler : public RAMPageHandler { 00181 public: 00182 ROMPageHandler() { 00183 flags=PFLAG_READABLE|PFLAG_HASROM; 00184 } 00185 void writeb(PhysPt addr,Bit8u val){ 00186 if (IS_PC98_ARCH && (addr & ~0x7FFF) == 0xE0000u) 00187 { /* Many PC-98 games and programs will zero 0xE0000-0xE7FFF whether or not the 4th bitplane is mapped */ } 00188 else 00189 LOG(LOG_CPU,LOG_ERROR)("Write %x to rom at %x",(int)val,(int)addr); 00190 } 00191 void writew(PhysPt addr,Bit16u val){ 00192 if (IS_PC98_ARCH && (addr & ~0x7FFF) == 0xE0000u) 00193 { /* Many PC-98 games and programs will zero 0xE0000-0xE7FFF whether or not the 4th bitplane is mapped */ } 00194 else 00195 LOG(LOG_CPU,LOG_ERROR)("Write %x to rom at %x",(int)val,(int)addr); 00196 } 00197 void writed(PhysPt addr,Bit32u val){ 00198 if (IS_PC98_ARCH && (addr & ~0x7FFF) == 0xE0000u) 00199 { /* Many PC-98 games and programs will zero 0xE0000-0xE7FFF whether or not the 4th bitplane is mapped */ } 00200 else 00201 LOG(LOG_CPU,LOG_ERROR)("Write %x to rom at %x",(int)val,(int)addr); 00202 } 00203 }; 00204 00205 00206 00207 static UnmappedPageHandler unmapped_page_handler; 00208 static IllegalPageHandler illegal_page_handler; 00209 static RAMPageHandler ram_page_handler; 00210 static ROMPageHandler rom_page_handler; 00211 static ROMAliasPageHandler rom_page_alias_handler; 00212 00213 PageHandler &Get_ROM_page_handler(void) { 00214 return rom_page_handler; 00215 } 00216 00217 extern bool pcibus_enable; 00218 00219 template <enum MEM_Type_t iotype> static unsigned int MEM_Gen_Callout(Bitu &ret,PageHandler* &f,Bitu page) { 00220 int actual = iotype - MEM_TYPE_MIN; 00221 MEM_callout_vector &vec = MEM_callouts[actual]; 00222 unsigned int match = 0; 00223 PageHandler *t_f; 00224 size_t scan = 0; 00225 00226 (void)ret;//UNUSED 00227 00228 while (scan < vec.size()) { 00229 MEM_CalloutObject &obj = vec[scan++]; 00230 if (!obj.isInstalled()) continue; 00231 if (obj.m_handler == NULL) continue; 00232 if (!obj.MatchPage(page)) continue; 00233 00234 t_f = obj.m_handler(obj,page); 00235 if (t_f != NULL) { 00236 if (match == 0) { 00237 f = t_f; 00238 } 00239 else { 00240 /* device conflict! */ 00241 /* TODO: to handle it properly, we would need to know whether this was a memory read or memory write, 00242 * and then have some way for the slow mem page handler to call each page handler one by one 00243 * and either write to each or read from each and combine. */ 00244 /* TODO: as usual, if iotype is PCI, multiple writes are permitted, but break out after finding one match for read. */ 00245 break; 00246 } 00247 match++; 00248 } 00249 } 00250 00251 return match; 00252 } 00253 00254 static unsigned int MEM_Motherboard_Callout(Bitu &ret,PageHandler* &f,Bitu page) { 00255 return MEM_Gen_Callout<MEM_TYPE_MB>(ret,f,page); 00256 } 00257 00258 static unsigned int MEM_PCI_Callout(Bitu &ret,PageHandler* &f,Bitu page) { 00259 return MEM_Gen_Callout<MEM_TYPE_PCI>(ret,f,page); 00260 } 00261 00262 static unsigned int MEM_ISA_Callout(Bitu &ret,PageHandler* &f,Bitu page) { 00263 return MEM_Gen_Callout<MEM_TYPE_ISA>(ret,f,page); 00264 } 00265 00266 static PageHandler *MEM_SlowPath(Bitu page) { 00267 PageHandler *f = &unmapped_page_handler; 00268 unsigned int match = 0; 00269 Bitu ret = ~0ul; 00270 00271 if (page >= memory.handler_pages) 00272 return &illegal_page_handler; 00273 00274 /* TEMPORARY, REMOVE LATER. SHOULD NOT HAPPEN. */ 00275 if (page < memory.reported_pages) { 00276 LOG(LOG_MISC,LOG_WARN)("MEM_SlowPath called within system RAM at page %x",(unsigned int)page); 00277 f = (PageHandler*)(&ram_page_handler); 00278 } 00279 00280 /* check motherboard devices (ROM BIOS, system RAM, etc.) */ 00281 match = MEM_Motherboard_Callout(/*&*/ret,/*&*/f,page); 00282 00283 if (match == 0) { 00284 /* first PCI bus device, then ISA. */ 00285 if (pcibus_enable) { 00286 /* PCI and PCI/ISA bridge emulation */ 00287 match = MEM_PCI_Callout(/*&*/ret,/*&*/f,page); 00288 if (match == 0) { 00289 /* PCI didn't take it, ask ISA bus */ 00290 match = MEM_ISA_Callout(/*&*/ret,/*&*/f,page); 00291 } 00292 } 00293 else { 00294 /* Pure ISA emulation */ 00295 match = MEM_ISA_Callout(/*&*/ret,/*&*/f,page); 00296 } 00297 } 00298 00299 /* if nothing matched, assign default handler to MEM handler slot. 00300 * if one device responded, assign it's handler to the MEM handler slot. 00301 * if more than one responded, then do not update the MEM handler slot. */ 00302 // assert(iolen >= 1 && iolen <= 4); 00303 // porti = (iolen >= 4) ? 2 : (iolen - 1); /* 1 2 x 4 -> 0 1 1 2 */ 00304 LOG(LOG_MISC,LOG_DEBUG)("MEM slow path page=%x: device matches=%u",(unsigned int)page,(unsigned int)match); 00305 if (match <= 1) memory.phandlers[page] = f; 00306 00307 return f; 00308 } 00309 00310 void MEM_RegisterHandler(Bitu phys_page,PageHandler * handler,Bitu page_range) { 00311 assert((phys_page+page_range) <= memory.handler_pages); 00312 while (page_range--) memory.phandlers[phys_page++]=handler; 00313 } 00314 00315 void MEM_InvalidateCachedHandler(Bitu phys_page,Bitu range) { 00316 assert((phys_page+range) <= memory.handler_pages); 00317 while (range--) memory.phandlers[phys_page++]=NULL; 00318 } 00319 00320 void MEM_FreeHandler(Bitu phys_page,Bitu page_range) { 00321 MEM_InvalidateCachedHandler(phys_page,page_range); 00322 } 00323 00324 void MEM_CalloutObject::InvalidateCachedHandlers(void) { 00325 Bitu p; 00326 00327 /* for both the base page, as well as it's aliases, revert the pages back to "slow path" */ 00328 for (p=m_base;p < memory.handler_pages;p += alias_mask+1) 00329 MEM_InvalidateCachedHandler(p,range_mask+1); 00330 } 00331 00332 void MEM_CalloutObject::Install(Bitu page,Bitu pagemask/*MEMMASK_ISA_10BIT, etc.*/,MEM_CalloutHandler *handler) { 00333 if(!installed) { 00334 if (pagemask == 0 || (pagemask & ~0xFFFFFFFU)) { 00335 LOG(LOG_MISC,LOG_ERROR)("MEM_CalloutObject::Install: Page mask %x is invalid",(unsigned int)pagemask); 00336 return; 00337 } 00338 00339 /* we need a mask for the distance between aliases of the port, and the range of I/O ports. */ 00340 /* only the low part of the mask where bits are zero, not the upper. 00341 * This loop is the reason portmask cannot be ~0 else it would become an infinite loop. 00342 * This also serves to check that the mask is a proper combination of ISA masking and 00343 * I/O port range (example: IOMASK_ISA_10BIT & (~0x3) == 0x3FF & 0xFFFFFFFC = 0x3FC for a device with 4 I/O ports). 00344 * A proper mask has (from MSB to LSB): 00345 * - zero or more 0 bits from MSB 00346 * - 1 or more 1 bits in the middle 00347 * - zero or more 0 bits to LSB */ 00348 { 00349 Bitu m = 1; 00350 Bitu test; 00351 00352 /* compute range mask from zero bits at LSB */ 00353 range_mask = 0; 00354 test = pagemask ^ 0xFFFFFFFU; 00355 while ((test & m) == m) { 00356 range_mask = m; 00357 m = (m << 1) + 1; 00358 } 00359 00360 /* DEBUG */ 00361 if ((pagemask & range_mask) != 0 || 00362 ((range_mask + 1) & range_mask) != 0/* should be a mask, therefore AND by itself + 1 should be zero (think (0xF + 1) & 0xF = 0x10 & 0xF = 0x0) */) { 00363 LOG(LOG_MISC,LOG_ERROR)("MEM_CalloutObject::Install: pagemask(%x) & range_mask(%x) != 0 (%x). You found a corner case that broke this code, fix it.", 00364 (unsigned int)pagemask, 00365 (unsigned int)range_mask, 00366 (unsigned int)(pagemask & range_mask)); 00367 return; 00368 } 00369 00370 /* compute alias mask from middle 1 bits */ 00371 alias_mask = range_mask; 00372 test = pagemask + range_mask; /* will break if portmask & range_mask != 0 */ 00373 while ((test & m) == m) { 00374 alias_mask = m; 00375 m = (m << 1) + 1; 00376 } 00377 00378 /* any bits after that should be zero. */ 00379 /* confirm this by XORing portmask by (alias_mask ^ range_mask). */ 00380 /* we already confirmed that portmask & range_mask == 0. */ 00381 /* Example: 00382 * 00383 * Sound Blaster at port 220-22Fh with 10-bit ISA decode would be 0x03F0 therefore: 00384 * portmask = 0x03F0 00385 * range_mask = 0x000F 00386 * alias_mask = 0x03FF 00387 * 00388 * portmask ^ range_mask = 0x3FF 00389 * portmask ^ range_mask ^ alias_mask = 0x0000 00390 * 00391 * Example of invalid portmask 0x13F0: 00392 * portmask = 0x13F0 00393 * range_mask = 0x000F 00394 * alias_mask = 0x03FF 00395 * 00396 * portmask ^ range_mask = 0x13FF 00397 * portmask ^ range_mask ^ alias_mask = 0x1000 */ 00398 if ((pagemask ^ range_mask ^ alias_mask) != 0 || 00399 ((alias_mask + 1) & alias_mask) != 0/* should be a mask, therefore AND by itself + 1 should be zero */) { 00400 LOG(LOG_MISC,LOG_ERROR)("MEM_CalloutObject::Install: pagemask(%x) ^ range_mask(%x) ^ alias_mask(%x) != 0 (%x). Invalid portmask.", 00401 (unsigned int)pagemask, 00402 (unsigned int)range_mask, 00403 (unsigned int)alias_mask, 00404 (unsigned int)(pagemask ^ range_mask ^ alias_mask)); 00405 return; 00406 } 00407 00408 if (page & range_mask) { 00409 LOG(LOG_MISC,LOG_ERROR)("MEM_CalloutObject::Install: page %x and page mask %x not aligned (range_mask %x)", 00410 (unsigned int)page,(unsigned int)pagemask,(unsigned int)range_mask); 00411 return; 00412 } 00413 } 00414 00415 installed=true; 00416 m_base=page; 00417 mem_mask=pagemask; 00418 m_handler=handler; 00419 00420 /* add this object to the callout array. 00421 * don't register any I/O handlers. those will be registered during the "slow path" 00422 * callout process when the CPU goes to access them. to encourage that to happen, 00423 * we invalidate the I/O ranges */ 00424 LOG(LOG_MISC,LOG_DEBUG)("MEM_CalloutObject::Install added device with page=0x%x mem_mask=0x%x rangemask=0x%x aliasmask=0x%x", 00425 (unsigned int)page,(unsigned int)mem_mask,(unsigned int)range_mask,(unsigned int)alias_mask); 00426 00427 InvalidateCachedHandlers(); 00428 } 00429 } 00430 00431 void MEM_CalloutObject::Uninstall() { 00432 if(!installed) return; 00433 InvalidateCachedHandlers(); 00434 installed=false; 00435 } 00436 00437 /* callers maintain a handle to it. 00438 * if they need to touch it, they get a pointer, which they then have to put back. 00439 * The way DOSBox/DOSbox-X code handles MEM callbacks it's common to declare an MEM object, 00440 * call the install, but then never touch it again, so this should work fine. 00441 * 00442 * this allows us to maintain ready-made MEM callout objects to return quickly rather 00443 * than write more complicated code where the caller has to make an MEM_CalloutObject 00444 * and then call install and we have to add it's pointer to a list/vector/whatever. 00445 * It also avoids problems where if we have to resize the vector, the pointers become 00446 * invalid, because callers have only handles and they have to put all the pointers 00447 * back in order for us to resize the vector. */ 00448 MEM_Callout_t MEM_AllocateCallout(MEM_Type_t t) { 00449 if (t < MEM_TYPE_MIN || t >= MEM_TYPE_MAX) 00450 return MEM_Callout_t_none; 00451 00452 MEM_callout_vector &vec = MEM_callouts[t - MEM_TYPE_MIN]; 00453 00454 try_again: 00455 while (vec.alloc_from < vec.size()) { 00456 MEM_CalloutObject &obj = vec[vec.alloc_from]; 00457 00458 if (!obj.alloc) { 00459 obj.alloc = true; 00460 assert(obj.isInstalled() == false); 00461 return MEM_Callout_t_comb(t,vec.alloc_from++); /* make combination, then increment alloc_from */ 00462 } 00463 00464 vec.alloc_from++; 00465 } 00466 00467 /* okay, double the size of the vector within reason. 00468 * if anyone has pointers out to our elements, then we cannot resize. vector::resize() may invalidate them. */ 00469 if (vec.size() < 4096 && vec.getcounter == 0) { 00470 size_t nsz = vec.size() * 2; 00471 00472 LOG(LOG_MISC,LOG_WARN)("MEM_AllocateCallout type %u expanding array to %u",(unsigned int)t,(unsigned int)nsz); 00473 vec.alloc_from = (unsigned int)vec.size(); /* allocate from end of old vector size */ 00474 vec.resize(nsz); 00475 goto try_again; 00476 } 00477 00478 LOG(LOG_MISC,LOG_WARN)("MEM_AllocateCallout type %u no free entries",(unsigned int)t); 00479 return MEM_Callout_t_none; 00480 } 00481 00482 void MEM_FreeCallout(MEM_Callout_t c) { 00483 enum MEM_Type_t t = MEM_Callout_t_type(c); 00484 00485 if (t < MEM_TYPE_MIN || t >= MEM_TYPE_MAX) 00486 return; 00487 00488 MEM_callout_vector &vec = MEM_callouts[t - MEM_TYPE_MIN]; 00489 uint32_t idx = MEM_Callout_t_index(c); 00490 00491 if (idx >= vec.size()) 00492 return; 00493 00494 MEM_CalloutObject &obj = vec[idx]; 00495 if (!obj.alloc) return; 00496 00497 if (obj.isInstalled()) 00498 obj.Uninstall(); 00499 00500 obj.alloc = false; 00501 if (vec.alloc_from > idx) 00502 vec.alloc_from = idx; /* an empty slot just opened up, you can alloc from there */ 00503 } 00504 00505 MEM_CalloutObject *MEM_GetCallout(MEM_Callout_t c) { 00506 enum MEM_Type_t t = MEM_Callout_t_type(c); 00507 00508 if (t < MEM_TYPE_MIN || t >= MEM_TYPE_MAX) 00509 return NULL; 00510 00511 MEM_callout_vector &vec = MEM_callouts[t - MEM_TYPE_MIN]; 00512 uint32_t idx = MEM_Callout_t_index(c); 00513 00514 if (idx >= vec.size()) 00515 return NULL; 00516 00517 MEM_CalloutObject &obj = vec[idx]; 00518 if (!obj.alloc) return NULL; 00519 obj.getcounter++; 00520 00521 return &obj; 00522 } 00523 00524 void MEM_PutCallout(MEM_CalloutObject *obj) { 00525 if (obj == NULL) return; 00526 if (obj->getcounter == 0) return; 00527 obj->getcounter--; 00528 } 00529 00530 void lfb_mem_cb_free(void) { 00531 if (lfb_mem_cb != MEM_Callout_t_none) { 00532 MEM_FreeCallout(lfb_mem_cb); 00533 lfb_mem_cb = MEM_Callout_t_none; 00534 } 00535 if (lfb_mmio_cb != MEM_Callout_t_none) { 00536 MEM_FreeCallout(lfb_mmio_cb); 00537 lfb_mmio_cb = MEM_Callout_t_none; 00538 } 00539 } 00540 00541 PageHandler* lfb_memio_cb(MEM_CalloutObject &co,Bitu phys_page) { 00542 (void)co;//UNUSED 00543 if (memory.lfb.start_page == 0 || memory.lfb.pages == 0) 00544 return NULL; 00545 if (phys_page >= memory.lfb.start_page || phys_page < memory.lfb.end_page) 00546 return memory.lfb.handler; 00547 if (phys_page >= memory.lfb_mmio.start_page || phys_page < memory.lfb_mmio.end_page) 00548 return memory.lfb_mmio.handler; 00549 00550 return NULL; 00551 } 00552 00553 void lfb_mem_cb_init() { 00554 if (lfb_mem_cb == MEM_Callout_t_none) { 00555 lfb_mem_cb = MEM_AllocateCallout(pcibus_enable ? MEM_TYPE_PCI : MEM_TYPE_ISA); 00556 if (lfb_mem_cb == MEM_Callout_t_none) E_Exit("Unable to allocate mem cb for LFB"); 00557 } 00558 if (lfb_mmio_cb == MEM_Callout_t_none) { 00559 lfb_mmio_cb = MEM_AllocateCallout(pcibus_enable ? MEM_TYPE_PCI : MEM_TYPE_ISA); 00560 if (lfb_mmio_cb == MEM_Callout_t_none) E_Exit("Unable to allocate mmio cb for LFB"); 00561 } 00562 00563 { 00564 MEM_CalloutObject *cb = MEM_GetCallout(lfb_mem_cb); 00565 00566 assert(cb != NULL); 00567 00568 cb->Uninstall(); 00569 00570 if (memory.lfb.pages != 0) { 00571 Bitu p2sz = 1; 00572 /* make p2sz the largest power of 2 that covers the LFB */ 00573 while (p2sz < memory.lfb.pages) p2sz <<= (Bitu)1; 00574 cb->Install(memory.lfb.start_page,MEMMASK_Combine(MEMMASK_FULL,MEMMASK_Range(p2sz)),lfb_memio_cb); 00575 } 00576 00577 MEM_PutCallout(cb); 00578 } 00579 00580 { 00581 MEM_CalloutObject *cb = MEM_GetCallout(lfb_mmio_cb); 00582 00583 assert(cb != NULL); 00584 00585 cb->Uninstall(); 00586 if (memory.lfb_mmio.pages != 0) { 00587 Bitu p2sz = 1; 00588 /* make p2sz the largest power of 2 that covers the LFB */ 00589 while (p2sz < memory.lfb_mmio.pages) p2sz <<= (Bitu)1; 00590 cb->Install(memory.lfb_mmio.start_page,MEMMASK_Combine(MEMMASK_FULL,MEMMASK_Range(p2sz)),lfb_memio_cb); 00591 } 00592 00593 MEM_PutCallout(cb); 00594 } 00595 } 00596 00597 /* TODO: At some point, this common code needs to be removed and the S3 emulation (or whatever else) 00598 * needs to provide LFB and/or MMIO mapping. */ 00599 void MEM_SetLFB(Bitu page, Bitu pages, PageHandler *handler, PageHandler *mmiohandler) { 00600 if (page == memory.lfb.start_page && memory.lfb.end_page == (page+pages) && 00601 memory.lfb.pages == pages && memory.lfb.handler == handler && 00602 memory.lfb_mmio.handler == mmiohandler) 00603 return; 00604 00605 memory.lfb.handler=handler; 00606 if (handler != NULL) { 00607 memory.lfb.start_page=page; 00608 memory.lfb.end_page=page+pages; 00609 memory.lfb.pages=pages; 00610 } 00611 else { 00612 memory.lfb.start_page=0; 00613 memory.lfb.end_page=0; 00614 memory.lfb.pages=0; 00615 } 00616 00617 memory.lfb_mmio.handler=mmiohandler; 00618 if (mmiohandler != NULL) { 00619 /* FIXME: Why is this hard-coded? There's more than just S3 emulation in this code's future! */ 00620 memory.lfb_mmio.start_page=page+(0x01000000/4096); 00621 memory.lfb_mmio.end_page=page+(0x01000000/4096)+16; 00622 memory.lfb_mmio.pages=16; 00623 } 00624 else { 00625 memory.lfb_mmio.start_page=0; 00626 memory.lfb_mmio.end_page=0; 00627 memory.lfb_mmio.pages=0; 00628 } 00629 00630 if (pages == 0 || page == 0) { 00631 lfb_mem_cb_free(); 00632 LOG(LOG_MISC,LOG_DEBUG)("MEM: Linear framebuffer disabled"); 00633 } 00634 else { 00635 lfb_mem_cb_init(); 00636 00637 LOG(LOG_MISC,LOG_DEBUG)("MEM: Linear framebuffer is now set to 0x%lx-0x%lx (%uKB)", 00638 (unsigned long)(page*4096), 00639 (unsigned long)(((page+pages)*4096)-1), 00640 (unsigned int)(pages*4)); 00641 // FIXME: Because this code emulates S3 by hardcoding the MMIO address! 00642 LOG(LOG_MISC,LOG_DEBUG)("MEM: Linear framebuffer MMIO is now set to 0x%lx-0x%lx (%uKB)", 00643 (unsigned long)(page*4096)+0x01000000, 00644 (unsigned long)(((page+16)*4096)+0x01000000-1), 00645 (unsigned int)(16*4)); 00646 } 00647 00648 PAGING_ClearTLB(); 00649 } 00650 00651 PageHandler * MEM_GetPageHandler(Bitu phys_page) { 00652 phys_page &= memory.mem_alias_pagemask_active; 00653 if (phys_page<memory.handler_pages) { 00654 if (memory.phandlers[phys_page] != NULL) /*likely*/ 00655 return memory.phandlers[phys_page]; 00656 00657 return MEM_SlowPath(phys_page); /* will also fill in phandlers[] if zero or one matches, so the next access is very fast */ 00658 } 00659 00660 return &illegal_page_handler; 00661 } 00662 00663 void MEM_SetPageHandler(Bitu phys_page,Bitu pages,PageHandler * handler) { 00664 for (;pages>0;pages--) { 00665 memory.phandlers[phys_page]=handler; 00666 phys_page++; 00667 } 00668 } 00669 00670 void MEM_ResetPageHandler_RAM(Bitu phys_page, Bitu pages) { 00671 PageHandler *ram_ptr = (PageHandler*)(&ram_page_handler); 00672 for (;pages>0;pages--) { 00673 memory.phandlers[phys_page]=ram_ptr; 00674 phys_page++; 00675 } 00676 } 00677 00678 void MEM_ResetPageHandler_Unmapped(Bitu phys_page, Bitu pages) { 00679 for (;pages>0;pages--) { 00680 memory.phandlers[phys_page]=&unmapped_page_handler; 00681 phys_page++; 00682 } 00683 } 00684 00685 Bitu mem_strlen(PhysPt pt) { 00686 Bit16u x=0; 00687 while (x<1024) { 00688 if (!mem_readb_inline(pt+x)) return x; 00689 x++; 00690 } 00691 return 0; //Hope this doesn't happen 00692 } 00693 00694 void mem_strcpy(PhysPt dest,PhysPt src) { 00695 Bit8u r; 00696 while ( (r = mem_readb(src++)) ) mem_writeb_inline(dest++,r); 00697 mem_writeb_inline(dest,0); 00698 } 00699 00700 void mem_memcpy(PhysPt dest,PhysPt src,Bitu size) { 00701 while (size--) mem_writeb_inline(dest++,mem_readb_inline(src++)); 00702 } 00703 00704 void MEM_BlockRead(PhysPt pt,void * data,Bitu size) { 00705 Bit8u * write=reinterpret_cast<Bit8u *>(data); 00706 while (size--) { 00707 *write++=mem_readb_inline(pt++); 00708 } 00709 } 00710 00711 void MEM_BlockWrite(PhysPt pt,void const * const data,Bitu size) { 00712 Bit8u const* read = reinterpret_cast<Bit8u const*>(data); 00713 if (size==0) 00714 return; 00715 00716 if ((pt >> 12) == ((pt+size-1)>>12)) { // Always same TLB entry 00717 HostPt tlb_addr=get_tlb_write(pt); 00718 if (!tlb_addr) { 00719 Bit8u val = *read++; 00720 get_tlb_writehandler(pt)->writeb(pt,val); 00721 tlb_addr=get_tlb_write(pt); 00722 pt++; size--; 00723 if (!tlb_addr) { 00724 // Slow path 00725 while (size--) { 00726 mem_writeb_inline(pt++,*read++); 00727 } 00728 return; 00729 } 00730 } 00731 // Fast path 00732 memcpy(tlb_addr+pt, read, size); 00733 } 00734 else { 00735 const Bitu current = (((pt>>12)+1)<<12) - pt; 00736 Bitu remainder = size - current; 00737 MEM_BlockWrite(pt, data, current); 00738 MEM_BlockWrite((PhysPt)(pt + current), reinterpret_cast<Bit8u const*>(data) + current, remainder); 00739 } 00740 } 00741 00742 void MEM_BlockRead32(PhysPt pt,void * data,Bitu size) { 00743 Bit32u * write=(Bit32u *) data; 00744 size>>=2; 00745 while (size--) { 00746 *write++=mem_readd_inline(pt); 00747 pt+=4; 00748 } 00749 } 00750 00751 void MEM_BlockWrite32(PhysPt pt,void * data,Bitu size) { 00752 Bit32u * read=(Bit32u *) data; 00753 size>>=2; 00754 while (size--) { 00755 mem_writed_inline(pt,*read++); 00756 pt+=4; 00757 } 00758 } 00759 00760 void MEM_BlockCopy(PhysPt dest,PhysPt src,Bitu size) { 00761 mem_memcpy(dest,src,size); 00762 } 00763 00764 void MEM_StrCopy(PhysPt pt,char * data,Bitu size) { 00765 while (size--) { 00766 Bit8u r=mem_readb_inline(pt++); 00767 if (!r) break; 00768 *data++=(char)r; 00769 } 00770 *data=0; 00771 } 00772 00773 Bitu MEM_TotalPages(void) { 00774 return memory.reported_pages; 00775 } 00776 00777 Bitu MEM_FreeLargest(void) { 00778 Bitu size=0;Bitu largest=0; 00779 Bitu index=XMS_START; 00780 while (index<memory.reported_pages) { 00781 if (!memory.mhandles[index]) { 00782 size++; 00783 } else { 00784 if (size>largest) largest=size; 00785 size=0; 00786 } 00787 index++; 00788 } 00789 if (size>largest) largest=size; 00790 return largest; 00791 } 00792 00793 Bitu MEM_FreeTotal(void) { 00794 Bitu free=0; 00795 Bitu index=XMS_START; 00796 while (index<memory.reported_pages) { 00797 if (!memory.mhandles[index]) free++; 00798 index++; 00799 } 00800 return free; 00801 } 00802 00803 Bitu MEM_AllocatedPages(MemHandle handle) 00804 { 00805 Bitu pages = 0; 00806 while (handle>0) { 00807 pages++; 00808 handle=memory.mhandles[handle]; 00809 } 00810 return pages; 00811 } 00812 00813 //TODO Maybe some protection for this whole allocation scheme 00814 00815 INLINE Bit32u BestMatch(Bitu size) { 00816 Bit32u index=XMS_START; 00817 Bit32u first=0; 00818 Bit32u best=0xfffffff; 00819 Bit32u best_first=0; 00820 while (index<memory.reported_pages) { 00821 /* Check if we are searching for first free page */ 00822 if (!first) { 00823 /* Check if this is a free page */ 00824 if (!memory.mhandles[index]) { 00825 first=index; 00826 } 00827 } else { 00828 /* Check if this still is used page */ 00829 if (memory.mhandles[index]) { 00830 Bit32u pages=index-first; 00831 if (pages==size) { 00832 return first; 00833 } else if (pages>size) { 00834 if (pages<best) { 00835 best=pages; 00836 best_first=first; 00837 } 00838 } 00839 first=0; //Always reset for new search 00840 } 00841 } 00842 index++; 00843 } 00844 /* Check for the final block if we can */ 00845 if (first && (index-first>=size) && (index-first<best)) { 00846 return first; 00847 } 00848 return best_first; 00849 } 00850 00851 /* alternate copy, that will only allocate memory on addresses 00852 * where the 20th address bit is zero. memory allocated in this 00853 * way will always be accessible no matter the state of the A20 gate */ 00854 INLINE Bit32u BestMatch_A20_friendly(Bitu size) { 00855 Bit32u index=XMS_START; 00856 Bit32u first=0; 00857 Bit32u best=0xfffffff; 00858 Bit32u best_first=0; 00859 00860 /* if the memory to allocate is more than 1MB this function will never work. give up now. */ 00861 if (size > 0x100) 00862 return 0; 00863 00864 /* TODO: For EMS allocation this would put things in the middle of extended memory space, 00865 * which would increase possible memory fragmentation! Could we avoid memory fragmentation 00866 * by instead scanning from the top down i.e. so the EMS system memory takes the top of 00867 * extended memory and the DOS program is free to gobble up a large continuous range from 00868 * below? */ 00869 while (index<memory.reported_pages) { 00870 /* Check if we are searching for first free page */ 00871 if (!first) { 00872 /* if the index is now on an odd megabyte, skip forward and try again */ 00873 if (index & 0x100) { 00874 index = (index|0xFF)+1; /* round up to an even megabyte */ 00875 continue; 00876 } 00877 00878 /* Check if this is a free page */ 00879 if (!memory.mhandles[index]) { 00880 first=index; 00881 } 00882 } else { 00883 /* Check if this still is used page or on odd megabyte */ 00884 if (memory.mhandles[index] || (index & 0x100)) { 00885 Bit32u pages=index-first; 00886 if (pages==size) { 00887 return first; 00888 } else if (pages>size) { 00889 if (pages<best) { 00890 best=pages; 00891 best_first=first; 00892 } 00893 } 00894 first=0; //Always reset for new search 00895 } 00896 } 00897 index++; 00898 } 00899 /* Check for the final block if we can */ 00900 if (first && (index-first>=size) && (index-first<best)) { 00901 return first; 00902 } 00903 return best_first; 00904 } 00905 00906 MemHandle MEM_AllocatePages(Bitu pages,bool sequence) { 00907 MemHandle ret; 00908 if (!pages) return 0; 00909 if (sequence) { 00910 Bit32u index=BestMatch(pages); 00911 if (!index) return 0; 00912 MemHandle * next=&ret; 00913 while (pages) { 00914 *next=(MemHandle)index; 00915 next=&memory.mhandles[index]; 00916 index++;pages--; 00917 } 00918 *next=-1; 00919 } else { 00920 if (MEM_FreeTotal()<pages) return 0; 00921 MemHandle * next=&ret; 00922 while (pages) { 00923 Bit32u index=BestMatch(1); 00924 if (!index) E_Exit("MEM:corruption during allocate"); 00925 while (pages && (!memory.mhandles[index])) { 00926 *next=(MemHandle)index; 00927 next=&memory.mhandles[index]; 00928 index++;pages--; 00929 } 00930 *next=-1; //Invalidate it in case we need another match 00931 } 00932 } 00933 return ret; 00934 } 00935 00936 /* alternate version guaranteed to allocate memory that is fully accessible 00937 * regardless of the state of the A20 gate. the physical memory address will 00938 * always have the 20th bit zero */ 00939 MemHandle MEM_AllocatePages_A20_friendly(Bitu pages,bool sequence) { 00940 MemHandle ret; 00941 if (!pages) return 0; 00942 if (sequence) { 00943 Bit32u index=BestMatch_A20_friendly(pages); 00944 if (!index) return 0; 00945 #if C_DEBUG 00946 if (index & 0x100) E_Exit("MEM_AllocatePages_A20_friendly failed to make sure address has bit 20 == 0"); 00947 if ((index+pages-1) & 0x100) E_Exit("MEM_AllocatePages_A20_friendly failed to make sure last page has bit 20 == 0"); 00948 #endif 00949 MemHandle * next=&ret; 00950 while (pages) { 00951 *next=(MemHandle)index; 00952 next=&memory.mhandles[index]; 00953 index++;pages--; 00954 } 00955 *next=-1; 00956 } else { 00957 if (MEM_FreeTotal()<pages) return 0; 00958 MemHandle * next=&ret; 00959 while (pages) { 00960 Bit32u index=BestMatch_A20_friendly(1); 00961 if (!index) E_Exit("MEM:corruption during allocate"); 00962 #if C_DEBUG 00963 if (index & 0x100) E_Exit("MEM_AllocatePages_A20_friendly failed to make sure address has bit 20 == 0"); 00964 #endif 00965 while (pages && (!memory.mhandles[index])) { 00966 *next=(MemHandle)index; 00967 next=&memory.mhandles[index]; 00968 index++;pages--; 00969 } 00970 *next=-1; //Invalidate it in case we need another match 00971 } 00972 } 00973 return ret; 00974 } 00975 00976 MemHandle MEM_GetNextFreePage(void) { 00977 return (MemHandle)BestMatch(1); 00978 } 00979 00980 void MEM_ReleasePages(MemHandle handle) { 00981 if (memory.mhandles == NULL) { 00982 LOG(LOG_MISC,LOG_WARN)("MEM_ReleasePages() called when mhandles==NULL, nothing to release"); 00983 return; 00984 } 00985 00986 while (handle>0) { 00987 MemHandle next=memory.mhandles[handle]; 00988 memory.mhandles[handle]=0; 00989 handle=next; 00990 } 00991 } 00992 00993 bool MEM_ReAllocatePages(MemHandle & handle,Bitu pages,bool sequence) { 00994 if (handle<=0) { 00995 if (!pages) return true; 00996 handle=MEM_AllocatePages(pages,sequence); 00997 return (handle>0); 00998 } 00999 if (!pages) { 01000 MEM_ReleasePages(handle); 01001 handle=-1; 01002 return true; 01003 } 01004 MemHandle index=handle; 01005 MemHandle last;Bitu old_pages=0; 01006 while (index>0) { 01007 old_pages++; 01008 last=index; 01009 index=memory.mhandles[index]; 01010 } 01011 if (old_pages == pages) return true; 01012 if (old_pages > pages) { 01013 /* Decrease size */ 01014 pages--;index=handle;old_pages--; 01015 while (pages) { 01016 index=memory.mhandles[index]; 01017 pages--;old_pages--; 01018 } 01019 MemHandle next=memory.mhandles[index]; 01020 memory.mhandles[index]=-1; 01021 index=next; 01022 while (old_pages) { 01023 next=memory.mhandles[index]; 01024 memory.mhandles[index]=0; 01025 index=next; 01026 old_pages--; 01027 } 01028 return true; 01029 } else { 01030 /* Increase size, check for enough free space */ 01031 Bitu need=pages-old_pages; 01032 if (sequence) { 01033 index=last+1; 01034 Bitu free=0; 01035 while ((index<(MemHandle)memory.reported_pages) && !memory.mhandles[index]) { 01036 index++;free++; 01037 } 01038 if (free>=need) { 01039 /* Enough space allocate more pages */ 01040 index=last; 01041 while (need) { 01042 memory.mhandles[index]=index+1; 01043 need--;index++; 01044 } 01045 memory.mhandles[index]=-1; 01046 return true; 01047 } else { 01048 /* Not Enough space allocate new block and copy */ 01049 MemHandle newhandle=MEM_AllocatePages(pages,true); 01050 if (!newhandle) return false; 01051 MEM_BlockCopy((unsigned int)newhandle*4096u,(unsigned int)handle*4096u,(unsigned int)old_pages*4096u); 01052 MEM_ReleasePages(handle); 01053 handle=newhandle; 01054 return true; 01055 } 01056 } else { 01057 MemHandle rem=MEM_AllocatePages(need,false); 01058 if (!rem) return false; 01059 memory.mhandles[last]=rem; 01060 return true; 01061 } 01062 } 01063 return 0; 01064 } 01065 01066 MemHandle MEM_NextHandle(MemHandle handle) { 01067 return memory.mhandles[handle]; 01068 } 01069 01070 MemHandle MEM_NextHandleAt(MemHandle handle,Bitu where) { 01071 while (where) { 01072 where--; 01073 handle=memory.mhandles[handle]; 01074 } 01075 return handle; 01076 } 01077 01078 01079 /* 01080 A20 line handling, 01081 Basically maps the 4 pages at the 1mb to 0mb in the default page directory 01082 */ 01083 bool MEM_A20_Enabled(void) { 01084 return memory.a20.enabled; 01085 } 01086 01087 void MEM_A20_Enable(bool enabled) { 01088 if (memory.a20.enabled != enabled) 01089 LOG(LOG_MISC,LOG_DEBUG)("MEM_A20_Enable(%u)",enabled?1:0); 01090 01091 if (a20_guest_changeable || a20_fake_changeable) 01092 memory.a20.enabled = enabled; 01093 01094 if (!a20_fake_changeable && (memory.mem_alias_pagemask & 0x100ul)) { 01095 if (memory.a20.enabled) memory.mem_alias_pagemask_active |= 0x100ul; 01096 else memory.mem_alias_pagemask_active &= ~0x100ul; 01097 PAGING_ClearTLB(); 01098 } 01099 } 01100 01101 01102 /* Memory access functions */ 01103 Bit16u mem_unalignedreadw(PhysPt address) { 01104 Bit16u ret = (Bit16u)mem_readb_inline(address); 01105 ret |= (Bit16u)mem_readb_inline(address+1u) << 8u; 01106 return ret; 01107 } 01108 01109 Bit32u mem_unalignedreadd(PhysPt address) { 01110 Bit32u ret = (Bit32u)mem_readb_inline(address ); 01111 ret |= (Bit32u)mem_readb_inline(address+1u) << 8u; 01112 ret |= (Bit32u)mem_readb_inline(address+2u) << 16u; 01113 ret |= (Bit32u)mem_readb_inline(address+3u) << 24u; 01114 return ret; 01115 } 01116 01117 01118 void mem_unalignedwritew(PhysPt address,Bit16u val) { 01119 mem_writeb_inline(address, (Bit8u)val);val>>=8u; 01120 mem_writeb_inline(address+1u,(Bit8u)val); 01121 } 01122 01123 void mem_unalignedwrited(PhysPt address,Bit32u val) { 01124 mem_writeb_inline(address, (Bit8u)val);val>>=8u; 01125 mem_writeb_inline(address+1u,(Bit8u)val);val>>=8u; 01126 mem_writeb_inline(address+2u,(Bit8u)val);val>>=8u; 01127 mem_writeb_inline(address+3u,(Bit8u)val); 01128 } 01129 01130 01131 bool mem_unalignedreadw_checked(PhysPt address, Bit16u * val) { 01132 Bit8u rval1,rval2; 01133 if (mem_readb_checked(address+0, &rval1)) return true; 01134 if (mem_readb_checked(address+1, &rval2)) return true; 01135 *val=(Bit16u)(((Bit8u)rval1) | (((Bit8u)rval2) << 8)); 01136 return false; 01137 } 01138 01139 bool mem_unalignedreadd_checked(PhysPt address, Bit32u * val) { 01140 Bit8u rval1,rval2,rval3,rval4; 01141 if (mem_readb_checked(address+0, &rval1)) return true; 01142 if (mem_readb_checked(address+1, &rval2)) return true; 01143 if (mem_readb_checked(address+2, &rval3)) return true; 01144 if (mem_readb_checked(address+3, &rval4)) return true; 01145 *val=(Bit32u)(((Bit8u)rval1) | (((Bit8u)rval2) << 8) | (((Bit8u)rval3) << 16) | (((Bit8u)rval4) << 24)); 01146 return false; 01147 } 01148 01149 bool mem_unalignedwritew_checked(PhysPt address,Bit16u val) { 01150 if (mem_writeb_checked(address,(Bit8u)(val & 0xff))) return true; 01151 val>>=8; 01152 if (mem_writeb_checked(address+1,(Bit8u)(val & 0xff))) return true; 01153 return false; 01154 } 01155 01156 bool mem_unalignedwrited_checked(PhysPt address,Bit32u val) { 01157 if (mem_writeb_checked(address,(Bit8u)(val & 0xff))) return true; 01158 val>>=8; 01159 if (mem_writeb_checked(address+1,(Bit8u)(val & 0xff))) return true; 01160 val>>=8; 01161 if (mem_writeb_checked(address+2,(Bit8u)(val & 0xff))) return true; 01162 val>>=8; 01163 if (mem_writeb_checked(address+3,(Bit8u)(val & 0xff))) return true; 01164 return false; 01165 } 01166 01167 Bit8u mem_readb(const PhysPt address) { 01168 return mem_readb_inline(address); 01169 } 01170 01171 Bit16u mem_readw(const PhysPt address) { 01172 return mem_readw_inline(address); 01173 } 01174 01175 Bit32u mem_readd(const PhysPt address) { 01176 return mem_readd_inline(address); 01177 } 01178 01179 #include "cpu.h" 01180 01181 extern bool warn_on_mem_write; 01182 extern CPUBlock cpu; 01183 01184 void mem_writeb(PhysPt address,Bit8u val) { 01185 // if (warn_on_mem_write && cpu.pmode) LOG_MSG("WARNING: post-killswitch memory write to 0x%08x = 0x%02x\n",address,val); 01186 mem_writeb_inline(address,val); 01187 } 01188 01189 void mem_writew(PhysPt address,Bit16u val) { 01190 // if (warn_on_mem_write && cpu.pmode) LOG_MSG("WARNING: post-killswitch memory write to 0x%08x = 0x%04x\n",address,val); 01191 mem_writew_inline(address,val); 01192 } 01193 01194 void mem_writed(PhysPt address,Bit32u val) { 01195 // if (warn_on_mem_write && cpu.pmode) LOG_MSG("WARNING: post-killswitch memory write to 0x%08x = 0x%08x\n",address,val); 01196 mem_writed_inline(address,val); 01197 } 01198 01199 void phys_writes(PhysPt addr, const char* string, Bitu length) { 01200 for(Bitu i = 0; i < length; i++) host_writeb(MemBase+addr+i,(Bit8u)string[i]); 01201 } 01202 01203 #include "control.h" 01204 01205 unsigned char CMOS_GetShutdownByte(); 01206 void CPU_Snap_Back_To_Real_Mode(); 01207 void DEBUG_Enable(bool pressed); 01208 void CPU_Snap_Back_Forget(); 01209 Bit16u CPU_Pop16(void); 01210 01211 static bool cmos_reset_type_9_sarcastic_win31_comments=true; 01212 01213 void On_Software_286_int15_block_move_return(unsigned char code) { 01214 Bit16u vec_seg,vec_off; 01215 01216 /* make CPU core stop immediately */ 01217 CPU_Cycles = 0; 01218 01219 /* force CPU back to real mode */ 01220 CPU_Snap_Back_To_Real_Mode(); 01221 CPU_Snap_Back_Forget(); 01222 01223 /* read the reset vector from the BIOS data area. this time, it's a stack pointer */ 01224 vec_off = phys_readw(0x400 + 0x67); 01225 vec_seg = phys_readw(0x400 + 0x69); 01226 01227 if (cmos_reset_type_9_sarcastic_win31_comments) { 01228 cmos_reset_type_9_sarcastic_win31_comments=false; 01229 LOG_MSG("CMOS Shutdown byte 0x%02x says to do INT 15 block move reset %04x:%04x. Only weirdos like Windows 3.1 use this... NOT WELL TESTED!",code,vec_seg,vec_off); 01230 } 01231 01232 #if C_DYNAMIC_X86 01233 /* FIXME: The way I will be carrying this out is incompatible with the Dynamic core! */ 01234 if (cpudecoder == &CPU_Core_Dyn_X86_Run) E_Exit("Sorry, CMOS shutdown CPU reset method is not compatible with dynamic core"); 01235 #endif 01236 01237 /* set stack pointer. prepare to emulate BIOS returning from INT 15h block move, 286 style */ 01238 CPU_SetSegGeneral(cs,0xF000); 01239 CPU_SetSegGeneral(ss,vec_seg); 01240 reg_esp = vec_off; 01241 01242 /* WARNING! WARNING! This is based on what Windows 3.1 standard mode (cputype=286) expects. 01243 * We need more comprehensive documentation on what actual 286 BIOSes do. 01244 * This code, order-wise, is a guess! No documentation exists on what actually happens! 01245 * But so far, this allows Windows 3.1 to run in full Standard Mode when cputype=286 without crashing. 01246 * Be warned that while it works DOSBox-X will spit a shitload of messages about the triple-fault reset trick it's using. */ 01247 CPU_SetSegGeneral(es,CPU_Pop16()); /* FIXME: ES? or DS? */ 01248 CPU_SetSegGeneral(ds,CPU_Pop16()); /* FIXME: ES? or DS? */ 01249 /* probably the stack frame of POPA */ 01250 reg_di=CPU_Pop16();reg_si=CPU_Pop16();reg_bp=CPU_Pop16();CPU_Pop16();//Don't save SP 01251 reg_bx=CPU_Pop16();reg_dx=CPU_Pop16();reg_cx=CPU_Pop16();reg_ax=CPU_Pop16(); 01252 /* and then finally what looks like an IRET frame */ 01253 CPU_IRET(false,0); 01254 01255 /* force execution change (FIXME: Is there a better way to do this?) */ 01256 throw int(4); 01257 /* does not return */ 01258 } 01259 01260 void On_Software_286_reset_vector(unsigned char code) { 01261 Bit16u vec_seg,vec_off; 01262 01263 /* make CPU core stop immediately */ 01264 CPU_Cycles = 0; 01265 01266 /* force CPU back to real mode */ 01267 CPU_Snap_Back_To_Real_Mode(); 01268 CPU_Snap_Back_Forget(); 01269 01270 /* read the reset vector from the BIOS data area */ 01271 vec_off = phys_readw(0x400 + 0x67); 01272 vec_seg = phys_readw(0x400 + 0x69); 01273 01274 /* TODO: If cputype=386 or cputype=486, and A20 gate disabled, treat it as an intentional trick 01275 * to trigger a reset + invalid opcode exception through which the program can then read the 01276 * CPU stepping ID register */ 01277 01278 LOG_MSG("CMOS Shutdown byte 0x%02x says to jump to reset vector %04x:%04x",code,vec_seg,vec_off); 01279 01280 #if C_DYNAMIC_X86 01281 /* FIXME: The way I will be carrying this out is incompatible with the Dynamic core! */ 01282 if (cpudecoder == &CPU_Core_Dyn_X86_Run) E_Exit("Sorry, CMOS shutdown CPU reset method is not compatible with dynamic core"); 01283 #endif 01284 01285 /* following CPU reset, and coming from the BIOS, CPU registers are trashed */ 01286 reg_eax = 0x2010000; 01287 reg_ebx = 0x2111; 01288 reg_ecx = 0; 01289 reg_edx = 0xABCD; 01290 reg_esi = 0; 01291 reg_edi = 0; 01292 reg_ebp = 0; 01293 reg_esp = 0x4F8; 01294 CPU_SetSegGeneral(ds,0x0040); 01295 CPU_SetSegGeneral(es,0x0000); 01296 CPU_SetSegGeneral(ss,0x0000); 01297 /* redirect CPU instruction pointer */ 01298 CPU_SetSegGeneral(cs,vec_seg); 01299 reg_eip = vec_off; 01300 01301 /* force execution change (FIXME: Is there a better way to do this?) */ 01302 throw int(4); 01303 /* does not return */ 01304 } 01305 01306 void CPU_Exception_Level_Reset(); 01307 01308 extern bool custom_bios; 01309 extern bool PC98_SHUT0,PC98_SHUT1; 01310 01311 void On_Software_CPU_Reset() { 01312 unsigned char c; 01313 01314 CPU_Exception_Level_Reset(); 01315 01316 if (custom_bios) { 01317 /* DO NOTHING */ 01318 LOG_MSG("CPU RESET: Doing nothing, custom BIOS loaded"); 01319 01320 if (IS_PC98_ARCH) 01321 LOG_MSG("CPU RESET: SHUT0=%u SHUT1=%u",PC98_SHUT0,PC98_SHUT1); 01322 else 01323 LOG_MSG("CPU RESET: CMOS BYTE 0x%02x",CMOS_GetShutdownByte()); 01324 } 01325 else if (IS_PC98_ARCH) { 01326 /* From Undocumented 9801, 9821 Volume 2: 01327 * 01328 * SHUT0 | SHUT1 | Meaning 01329 * ----------------------- 01330 * 1 1 System reset (BIOS performs full reinitialization) 01331 * 1 0 Invalid (BIOS will show "SYSTEM SHUTDOWN" and stop) 01332 * 0 x Continue program execution after CPU reset. 01333 * BIOS loads SS:SP from 0000:0404 and then executes RETF */ 01334 if (PC98_SHUT0) { 01335 if (!PC98_SHUT1) 01336 E_Exit("PC-98 invalid reset aka SYSTEM SHUTDOWN (SHUT0=1 SHUT1=0)"); 01337 } 01338 else { // SHUT0=0 SHUT1=x 01339 void CPU_Snap_Back_To_Real_Mode(); 01340 void CPU_Snap_Back_Forget(); 01341 01342 /* fake CPU reset */ 01343 CPU_Snap_Back_To_Real_Mode(); 01344 CPU_Snap_Back_Forget(); 01345 01346 /* following CPU reset, and coming from the BIOS, CPU registers are trashed */ 01347 /* FIXME: VEM486.EXE appears to use this reset vector trick, then when regaining control, 01348 * checks to see if DX is 0x00F0 just as it was when it issued the OUT DX,AL 01349 * instruction. Why? If DX != 0x00F0 it writes whatever DX is to 0000:0486 and 01350 * then proceeds anyway. */ 01351 reg_eax = 0x2010000; 01352 reg_ebx = 0x2111; 01353 reg_ecx = 0; 01354 reg_edx = 0xABCD; 01355 reg_esi = 0; 01356 reg_edi = 0; 01357 reg_ebp = 0; 01358 reg_esp = 0x4F8; 01359 CPU_SetSegGeneral(ds,0x0040); 01360 CPU_SetSegGeneral(es,0x0000); 01361 CPU_SetSegGeneral(ss,0x0000); 01362 01363 /* continue program execution after CPU reset */ 01364 Bit16u reset_sp = mem_readw(0x404); 01365 Bit16u reset_ss = mem_readw(0x406); 01366 01367 LOG_MSG("PC-98 reset and continue: SS:SP = %04x:%04x",reset_ss,reset_sp); 01368 01369 reg_esp = reset_sp; 01370 CPU_SetSegGeneral(ss,reset_ss); 01371 01372 Bit16u new_ip = CPU_Pop16(); 01373 Bit16u new_cs = CPU_Pop16(); 01374 01375 reg_eip = new_ip; 01376 CPU_SetSegGeneral(cs,new_cs); 01377 01378 LOG_MSG("PC-98 reset and continue: RETF to %04x:%04x",SegValue(cs),reg_ip); 01379 01380 // DEBUG 01381 // Bitu DEBUG_EnableDebugger(void); 01382 // DEBUG_EnableDebugger(); 01383 01384 /* force execution change (FIXME: Is there a better way to do this?) */ 01385 throw int(4); 01386 /* does not return */ 01387 } 01388 } 01389 else { 01390 /* IBM reset vector or system reset by CMOS shutdown byte */ 01391 01392 /* software-initiated CPU reset. but the intent may not be to reset the system but merely 01393 * the CPU. check the CMOS shutdown byte */ 01394 switch (c=CMOS_GetShutdownByte()) { 01395 case 0x05: /* JMP double word pointer with EOI */ 01396 case 0x0A: /* JMP double word pointer without EOI */ 01397 On_Software_286_reset_vector(c); 01398 return; 01399 case 0x09: /* INT 15h block move return to real mode (to appease Windows 3.1 KRNL286.EXE and cputype=286, yuck) */ 01400 On_Software_286_int15_block_move_return(c); 01401 return; 01402 } 01403 } 01404 01405 #if C_DYNAMIC_X86 01406 /* this technique is NOT reliable when running the dynamic core! */ 01407 if (cpudecoder == &CPU_Core_Dyn_X86_Run) { 01408 E_Exit("C++ exception method is not compatible with dynamic core when emulating reset, aborting"); 01409 return; 01410 } 01411 #endif 01412 01413 throw int(3); 01414 /* does not return */ 01415 } 01416 01417 bool allow_port_92_reset = true; 01418 01419 static void write_p92(Bitu port,Bitu val,Bitu iolen) { 01420 (void)iolen;//UNUSED 01421 (void)port;//UNUSED 01422 memory.a20.controlport = (Bit8u)(val & ~2u); 01423 MEM_A20_Enable((val & 2u)>0); 01424 01425 // Bit 0 = system reset (switch back to real mode) 01426 if (val & 1) { 01427 if (allow_port_92_reset) { 01428 LOG_MSG("Restart by port 92h requested\n"); 01429 On_Software_CPU_Reset(); 01430 } 01431 else { 01432 LOG_MSG("WARNING: port 92h written with bit 0 set. Is the guest OS or application attempting to reset the system?\n"); 01433 } 01434 } 01435 } 01436 01437 static Bitu read_p92(Bitu port,Bitu iolen) { 01438 (void)iolen;//UNUSED 01439 (void)port;//UNUSED 01440 return (Bitu)memory.a20.controlport | (memory.a20.enabled ? 0x02u : 0); 01441 } 01442 01443 static Bitu read_pc98_a20(Bitu port,Bitu iolen) { 01444 (void)iolen;//UNUSED 01445 if (port == 0xF2) 01446 return (memory.a20.enabled ? 0x00 : 0x01); // bit 0 indicates whether A20 is MASKED, not ENABLED 01447 01448 return ~0ul; 01449 } 01450 01451 static void write_pc98_a20(Bitu port,Bitu val,Bitu iolen) { 01452 (void)iolen;//UNUSED 01453 if (port == 0xF2) { 01454 MEM_A20_Enable(1); // writing port 0xF2 unmasks (enables) A20 regardless of value 01455 } 01456 else if (port == 0xF6) { 01457 if ((val & 0xFE) == 0x02) { // A20 gate control 0000 001x x=mask A20 if set 01458 MEM_A20_Enable(!(val & 1)); 01459 } 01460 else { 01461 LOG_MSG("PC-98 port F6h unknown value 0x%x",(unsigned int)val); 01462 } 01463 } 01464 } 01465 01466 void RemoveEMSPageFrame(void) { 01467 LOG(LOG_MISC,LOG_DEBUG)("Removing EMS page frame"); 01468 01469 /* Setup rom at 0xe0000-0xf0000 */ 01470 for (Bitu ct=0xe0;ct<0xf0;ct++) { 01471 memory.phandlers[ct] = &rom_page_handler; 01472 } 01473 } 01474 01475 void PreparePCJRCartRom(void) { 01476 LOG(LOG_MISC,LOG_DEBUG)("Preparing mapping for PCjr cartridge ROM"); 01477 01478 /* Setup rom at 0xd0000-0xe0000 */ 01479 for (Bitu ct=0xd0;ct<0xe0;ct++) { 01480 memory.phandlers[ct] = &rom_page_handler; 01481 } 01482 } 01483 01484 /* how to use: unmap_physmem(0xA0000,0xBFFFF) to unmap 0xA0000 to 0xBFFFF */ 01485 bool MEM_unmap_physmem(Bitu start,Bitu end) { 01486 Bitu p; 01487 01488 if (start & 0xFFF) 01489 LOG_MSG("WARNING: unmap_physmem() start not page aligned.\n"); 01490 if ((end & 0xFFF) != 0xFFF) 01491 LOG_MSG("WARNING: unmap_physmem() end not page aligned.\n"); 01492 start >>= 12; end >>= 12; 01493 01494 if (start >= memory.handler_pages || end >= memory.handler_pages) 01495 E_Exit("%s: attempt to map pages beyond handler page limit (0x%lx-0x%lx >= 0x%lx)", 01496 __FUNCTION__,(unsigned long)start,(unsigned long)end,(unsigned long)memory.handler_pages); 01497 01498 for (p=start;p <= end;p++) 01499 memory.phandlers[p] = &unmapped_page_handler; 01500 01501 PAGING_ClearTLB(); 01502 return true; 01503 } 01504 01505 bool MEM_map_RAM_physmem(Bitu start,Bitu end) { 01506 Bitu p; 01507 PageHandler *ram_ptr = (PageHandler*)(&ram_page_handler); 01508 01509 if (start & 0xFFF) 01510 LOG_MSG("WARNING: unmap_physmem() start not page aligned.\n"); 01511 if ((end & 0xFFF) != 0xFFF) 01512 LOG_MSG("WARNING: unmap_physmem() end not page aligned.\n"); 01513 start >>= 12; end >>= 12; 01514 01515 if (start >= memory.handler_pages || end >= memory.handler_pages) 01516 E_Exit("%s: attempt to map pages beyond handler page limit (0x%lx-0x%lx >= 0x%lx)", 01517 __FUNCTION__,(unsigned long)start,(unsigned long)end,(unsigned long)memory.handler_pages); 01518 01519 for (p=start;p <= end;p++) { 01520 if (memory.phandlers[p] != NULL && memory.phandlers[p] != &illegal_page_handler && 01521 memory.phandlers[p] != &unmapped_page_handler && memory.phandlers[p] != &ram_page_handler) 01522 return false; 01523 } 01524 01525 for (p=start;p <= end;p++) 01526 memory.phandlers[p] = ram_ptr; 01527 01528 PAGING_ClearTLB(); 01529 return true; 01530 } 01531 01532 bool MEM_map_ROM_physmem(Bitu start,Bitu end) { 01533 Bitu p; 01534 01535 if (start & 0xFFF) 01536 LOG_MSG("WARNING: unmap_physmem() start not page aligned.\n"); 01537 if ((end & 0xFFF) != 0xFFF) 01538 LOG_MSG("WARNING: unmap_physmem() end not page aligned.\n"); 01539 start >>= 12; end >>= 12; 01540 01541 if (start >= memory.handler_pages || end >= memory.handler_pages) 01542 E_Exit("%s: attempt to map pages beyond handler page limit (0x%lx-0x%lx >= 0x%lx)", 01543 __FUNCTION__,(unsigned long)start,(unsigned long)end,(unsigned long)memory.handler_pages); 01544 01545 for (p=start;p <= end;p++) { 01546 if (memory.phandlers[p] != NULL && memory.phandlers[p] != &illegal_page_handler && 01547 memory.phandlers[p] != &unmapped_page_handler && memory.phandlers[p] != &rom_page_handler) 01548 return false; 01549 } 01550 01551 for (p=start;p <= end;p++) 01552 memory.phandlers[p] = &rom_page_handler; 01553 01554 PAGING_ClearTLB(); 01555 return true; 01556 } 01557 01558 bool MEM_map_ROM_alias_physmem(Bitu start,Bitu end) { 01559 Bitu p; 01560 01561 if (start & 0xFFF) 01562 LOG_MSG("WARNING: unmap_physmem() start not page aligned.\n"); 01563 if ((end & 0xFFF) != 0xFFF) 01564 LOG_MSG("WARNING: unmap_physmem() end not page aligned.\n"); 01565 start >>= 12; end >>= 12; 01566 01567 if (start >= memory.handler_pages || end >= memory.handler_pages) 01568 E_Exit("%s: attempt to map pages beyond handler page limit (0x%lx-0x%lx >= 0x%lx)", 01569 __FUNCTION__,(unsigned long)start,(unsigned long)end,(unsigned long)memory.handler_pages); 01570 01571 for (p=start;p <= end;p++) { 01572 if (memory.phandlers[p] != NULL && memory.phandlers[p] != &illegal_page_handler && memory.phandlers[p] != &unmapped_page_handler) 01573 return false; 01574 } 01575 01576 for (p=start;p <= end;p++) 01577 memory.phandlers[p] = &rom_page_alias_handler; 01578 01579 PAGING_ClearTLB(); 01580 return true; 01581 } 01582 01583 HostPt GetMemBase(void) { return MemBase; } 01584 01587 class REDOS : public Program { 01588 public: 01590 void Run(void) { 01591 if (cmd->FindExist("/?", false) || cmd->FindExist("-?", false)) { 01592 WriteOut("Restarts the kernel of DOSBox-X's emulated DOS.\n\nRE-DOS\n"); 01593 return; 01594 } 01595 throw int(6); 01596 } 01597 }; 01598 01599 void REDOS_ProgramStart(Program * * make) { 01600 *make=new REDOS; 01601 } 01602 01607 class A20GATE : public Program { 01608 public: 01610 void Run(void) { 01611 if (cmd->FindString("SET",temp_line,false)) { 01612 char *x = (char*)temp_line.c_str(); 01613 01614 a20_fast_changeable = false; 01615 a20_fake_changeable = false; 01616 a20_guest_changeable = true; 01617 MEM_A20_Enable(true); 01618 01619 if (!strncasecmp(x,"off_fake",8)) { 01620 MEM_A20_Enable(false); 01621 a20_guest_changeable = false; 01622 a20_fake_changeable = true; 01623 WriteOut("A20 gate now off_fake mode\n"); 01624 } 01625 else if (!strncasecmp(x,"off",3)) { 01626 MEM_A20_Enable(false); 01627 a20_guest_changeable = false; 01628 a20_fake_changeable = false; 01629 WriteOut("A20 gate now off mode\n"); 01630 } 01631 else if (!strncasecmp(x,"on_fake",7)) { 01632 MEM_A20_Enable(true); 01633 a20_guest_changeable = false; 01634 a20_fake_changeable = true; 01635 WriteOut("A20 gate now on_fake mode\n"); 01636 } 01637 else if (!strncasecmp(x,"on",2)) { 01638 MEM_A20_Enable(true); 01639 a20_guest_changeable = false; 01640 a20_fake_changeable = false; 01641 WriteOut("A20 gate now on mode\n"); 01642 } 01643 else if (!strncasecmp(x,"mask",4)) { 01644 MEM_A20_Enable(false); 01645 a20_guest_changeable = true; 01646 a20_fake_changeable = false; 01647 memory.a20.enabled = 0; 01648 WriteOut("A20 gate now mask mode\n"); 01649 } 01650 else if (!strncasecmp(x,"fast",4)) { 01651 MEM_A20_Enable(false); 01652 a20_guest_changeable = true; 01653 a20_fake_changeable = false; 01654 a20_fast_changeable = true; 01655 WriteOut("A20 gate now fast mode\n"); 01656 } 01657 else { 01658 WriteOut("Unknown setting\n"); 01659 } 01660 } 01661 else if (cmd->FindExist("ON")) { 01662 MEM_A20_Enable(true); 01663 WriteOut("Enabling A20 gate\n"); 01664 } 01665 else if (cmd->FindExist("OFF")) { 01666 MEM_A20_Enable(false); 01667 WriteOut("Disabling A20 gate\n"); 01668 } 01669 else { 01670 WriteOut("Turns on/off or changes the A20 gate mode.\n\n"); 01671 WriteOut("A20GATE SET [off | off_fake | on | on_fake | mask | fast]\n"); 01672 WriteOut("A20GATE [ON | OFF]\n"); 01673 } 01674 } 01675 }; 01676 01677 void A20GATE_ProgramStart(Program * * make) { 01678 *make=new A20GATE; 01679 } 01680 01681 void Init_AddressLimitAndGateMask() { 01682 Section_prop * section=static_cast<Section_prop *>(control->GetSection("dosbox")); 01683 01684 LOG(LOG_MISC,LOG_DEBUG)("Initializing address limit/gate system"); 01685 01686 // TODO: this option should be handled by the CPU init since it concerns emulation 01687 // of older 386 and 486 boards with fewer than 32 address lines: 01688 // 01689 // 24-bit addressing on the 386SX vs full 32-bit addressing on the 386DX 01690 // 26-bit addressing on the 486SX vs full 32-bit addressing on the 486DX 01691 // 01692 // Also this code should automatically cap itself at 24 for 286 emulation and 01693 // 20 for 8086 emulation. 01694 memory.address_bits=(unsigned int)section->Get_int("memalias"); 01695 01696 if (memory.address_bits == 0) 01697 memory.address_bits = 32; 01698 else if (memory.address_bits < 20) 01699 memory.address_bits = 20; 01700 else if (memory.address_bits > 32) 01701 memory.address_bits = 32; 01702 01703 // TODO: This should be ...? CPU init? Motherboard init? 01704 /* WARNING: Binary arithmetic done with 64-bit integers because under Microsoft C++ 01705 ((1UL << 32UL) - 1UL) == 0, which is WRONG. 01706 But I'll never get back the 4 days I wasted chasing it down, trying to 01707 figure out why DOSBox was getting stuck reopening it's own CON file handle. */ 01708 memory.mem_alias_pagemask = (uint32_t) 01709 (((((uint64_t)1) << (uint64_t)memory.address_bits) - (uint64_t)1) >> (uint64_t)12); 01710 01711 /* memory aliasing cannot go below 1MB or serious problems may result. */ 01712 if ((memory.mem_alias_pagemask & 0xFF) != 0xFF) E_Exit("alias pagemask < 1MB"); 01713 01714 /* update alias pagemask according to A20 gate */ 01715 memory.mem_alias_pagemask_active = memory.mem_alias_pagemask; 01716 if (a20_fake_changeable && !memory.a20.enabled) 01717 memory.mem_alias_pagemask_active &= ~0x100u; 01718 01719 /* log */ 01720 LOG(LOG_MISC,LOG_DEBUG)("Memory: address_bits=%u alias_pagemask=%lx",(unsigned int)memory.address_bits,(unsigned long)memory.mem_alias_pagemask); 01721 } 01722 01723 void ShutDownRAM(Section * sec) { 01724 (void)sec;//UNUSED 01725 if (MemBase != NULL) { 01726 delete [] MemBase; 01727 MemBase = NULL; 01728 } 01729 } 01730 01731 void MEM_InitCallouts(void) { 01732 /* make sure each vector has enough for a typical load */ 01733 MEM_callouts[MEM_callouts_index(MEM_TYPE_ISA)].resize(64); 01734 MEM_callouts[MEM_callouts_index(MEM_TYPE_PCI)].resize(64); 01735 MEM_callouts[MEM_callouts_index(MEM_TYPE_MB)].resize(64); 01736 } 01737 01738 void Init_RAM() { 01739 Section_prop * section=static_cast<Section_prop *>(control->GetSection("dosbox")); 01740 Bitu i; 01741 01742 /* please let me know about shutdown! */ 01743 if (!has_Init_RAM) { 01744 AddExitFunction(AddExitFunctionFuncPair(ShutDownRAM)); 01745 has_Init_RAM = true; 01746 } 01747 01748 /* prepare callouts */ 01749 MEM_InitCallouts(); 01750 01751 // LOG 01752 LOG(LOG_MISC,LOG_DEBUG)("Initializing RAM emulation (system memory)"); 01753 01754 // CHECK: address mask init must have been called! 01755 assert(memory.mem_alias_pagemask >= 0xFF); 01756 01757 /* Setup the Physical Page Links */ 01758 Bitu memsizekb = (Bitu)section->Get_int("memsizekb"); 01759 { 01760 Bitu memsize = (Bitu)section->Get_int("memsize"); 01761 01762 if (memsizekb == 0 && memsize < 1) memsize = 1; 01763 else if (memsizekb != 0 && (Bits)memsize < 0) memsize = 0; 01764 01765 /* round up memsizekb to 4KB multiple */ 01766 memsizekb = (memsizekb + 3ul) & (~3ul); 01767 01768 /* roll memsize into memsizekb, simplify this code */ 01769 memsizekb += memsize * 1024ul; 01770 } 01771 01772 /* we can't have more memory than the memory aliasing allows */ 01773 if ((memory.mem_alias_pagemask+1) != 0/*32-bit integer overflow avoidance*/ && 01774 (memsizekb/4) > (memory.mem_alias_pagemask+1)) { 01775 LOG_MSG("%u-bit memory aliasing limits you to %uKB", 01776 (int)memory.address_bits,(int)((memory.mem_alias_pagemask+1)*4)); 01777 memsizekb = (memory.mem_alias_pagemask+1) * 4; 01778 } 01779 01780 /* cap at 3.5GB */ 01781 { 01782 Bitu maxsz; 01783 01784 if (sizeof(void*) > 4) // 64-bit address space 01785 maxsz = (Bitu)(3584ul * 1024ul); // 3.5GB 01786 else 01787 maxsz = (Bitu)(1024ul * 1024ul); // 1.0GB 01788 01789 LOG_MSG("Max %lu sz %lu\n",(unsigned long)maxsz,(unsigned long)memsizekb); 01790 if (memsizekb > maxsz) { 01791 LOG_MSG("Maximum memory size is %luKB",(unsigned long)maxsz); 01792 memsizekb = maxsz; 01793 } 01794 LOG_MSG("Final %lu\n",(unsigned long)memsizekb); 01795 } 01796 memory.reported_pages = memory.pages = memsizekb/4; 01797 01798 // FIXME: Hopefully our refactoring will remove the need for this hack 01799 /* if the config file asks for less than 1MB of memory 01800 * then say so to the DOS program. but way too much code 01801 * here assumes memsize >= 1MB */ 01802 if (memory.pages < ((1024*1024)/4096)) 01803 memory.pages = ((1024*1024)/4096); 01804 01805 // DEBUG message 01806 LOG(LOG_MISC,LOG_DEBUG)("Memory: %u pages (%uKB) of RAM, %u (%uKB) reported to OS, %u (0x%x) (%uKB) pages of memory handlers", 01807 (unsigned int)memory.pages, 01808 (unsigned int)memory.pages*4, 01809 (unsigned int)memory.reported_pages, 01810 (unsigned int)memory.reported_pages*4, 01811 (unsigned int)memory.handler_pages, 01812 (unsigned int)memory.handler_pages, 01813 (unsigned int)memory.handler_pages*4); 01814 01815 // sanity check! 01816 assert(memory.handler_pages >= memory.pages); 01817 assert(memory.reported_pages <= memory.pages); 01818 assert(memory.handler_pages >= memory.reported_pages); 01819 assert(memory.handler_pages >= 0x100); /* enough for at minimum 1MB of addressable memory */ 01820 01821 /* Allocate the RAM. We alloc as a large unsigned char array. new[] does not initialize the array, 01822 * so we then must zero the buffer. */ 01823 MemBase = new Bit8u[memory.pages*4096]; 01824 if (!MemBase) E_Exit("Can't allocate main memory of %d KB",(int)memsizekb); 01825 /* Clear the memory, as new doesn't always give zeroed memory 01826 * (Visual C debug mode). We want zeroed memory though. */ 01827 memset((void*)MemBase,0,memory.reported_pages*4096); 01828 /* the rest of "ROM" is for unmapped devices so we need to fill it appropriately */ 01829 if (memory.reported_pages < memory.pages) 01830 memset((char*)MemBase+(memory.reported_pages*4096),0xFF, 01831 (memory.pages - memory.reported_pages)*4096); 01832 /* adapter ROM */ 01833 memset((char*)MemBase+0xA0000,0xFF,0x60000); 01834 /* except for 0xF0000-0xFFFFF */ 01835 memset((char*)MemBase+0xF0000,0x00,0x10000); 01836 01837 // sanity check. if this condition is false the loops below will overrun the array! 01838 assert(memory.reported_pages <= memory.handler_pages); 01839 01840 PageHandler *ram_ptr = (PageHandler*)(&ram_page_handler); 01841 01842 for (i=0;i < memory.reported_pages;i++) 01843 memory.phandlers[i] = ram_ptr; 01844 for (;i < memory.handler_pages;i++) 01845 memory.phandlers[i] = NULL;//&illegal_page_handler; 01846 01847 /* FIXME: VGA emulation will selectively respond to 0xA0000-0xBFFFF according to the video mode, 01848 * what we want however is for the VGA emulation to assign illegal_page_handler for 01849 * address ranges it is not responding to when mapping changes. */ 01850 for (i=0xa0;i<0x100;i++) /* we want to make sure adapter ROM is unmapped entirely! */ 01851 memory.phandlers[i] = NULL;//&unmapped_page_handler; 01852 } 01853 01854 /* ROM BIOS emulation will call this to impose an additional cap on RAM 01855 * to make sure the upper alias of the ROM BIOS has room. */ 01856 void MEM_cut_RAM_up_to(Bitu addr) { 01857 Bitu pages = addr >> 12ul; 01858 01859 if (memory.reported_pages > pages) { 01860 LOG(LOG_MISC,LOG_DEBUG)("Memory: Reducing RAM to 0x%lx",(unsigned long)addr); 01861 01862 do { memory.phandlers[--memory.reported_pages] = NULL; 01863 } while (memory.reported_pages > pages); 01864 } 01865 } 01866 01867 static IO_ReadHandleObject PS2_Port_92h_ReadHandler; 01868 static IO_WriteHandleObject PS2_Port_92h_WriteHandler; 01869 static IO_WriteHandleObject PS2_Port_92h_WriteHandler2; 01870 01871 void ShutDownMemoryAccessArray(Section * sec) { 01872 (void)sec;//UNUSED 01873 if (memory.phandlers != NULL) { 01874 delete [] memory.phandlers; 01875 memory.phandlers = NULL; 01876 } 01877 } 01878 01879 void XMS_ShutDown(Section* /*sec*/); 01880 01881 void ShutDownMemHandles(Section * sec) { 01882 /* XMS relies on us, so shut it down first to avoid spurious warnings about freeing when mhandles == NULL */ 01883 XMS_ShutDown(NULL); 01884 01885 (void)sec;//UNUSED 01886 if (memory.mhandles != NULL) { 01887 delete [] memory.mhandles; 01888 memory.mhandles = NULL; 01889 } 01890 } 01891 01892 /* this is called on hardware reset. the BIOS needs the A20 gate ON to boot properly on 386 or higher! 01893 * this temporarily switches on the A20 gate and lets it function as normal despite user settings. 01894 * BIOS will POST and then permit the A20 gate to go back to whatever emulation setting given in dosbox.conf */ 01895 void A20Gate_OnReset(Section *sec) { 01896 (void)sec;//UNUSED 01897 void A20Gate_OverrideOn(Section *sec); 01898 01899 memory.a20.controlport = 0; 01900 A20Gate_OverrideOn(sec); 01901 MEM_A20_Enable(true); 01902 } 01903 01904 void A20Gate_OverrideOn(Section *sec) { 01905 (void)sec;//UNUSED 01906 memory.a20.enabled = 1; 01907 a20_fake_changeable = false; 01908 a20_guest_changeable = true; 01909 } 01910 01911 /* this is called after BIOS boot. the BIOS needs the A20 gate ON to boot properly on 386 or higher! */ 01912 void A20Gate_TakeUserSetting(Section *sec) { 01913 (void)sec;//UNUSED 01914 Section_prop * section=static_cast<Section_prop *>(control->GetSection("dosbox")); 01915 01916 memory.a20.enabled = 0; 01917 a20_fake_changeable = false; 01918 a20_guest_changeable = true; 01919 a20_fast_changeable = false; 01920 01921 // TODO: A20 gate control should also be handled by a motherboard init routine 01922 std::string ss = section->Get_string("a20"); 01923 if (ss == "mask" || ss == "") { 01924 LOG(LOG_MISC,LOG_DEBUG)("A20: masking emulation"); 01925 a20_guest_changeable = true; 01926 } 01927 else if (ss == "on") { 01928 LOG(LOG_MISC,LOG_DEBUG)("A20: locked on"); 01929 a20_guest_changeable = false; 01930 memory.a20.enabled = 1; 01931 } 01932 else if (ss == "on_fake") { 01933 LOG(LOG_MISC,LOG_DEBUG)("A20: locked on (but will fake control bit)"); 01934 a20_guest_changeable = false; 01935 a20_fake_changeable = true; 01936 memory.a20.enabled = 1; 01937 } 01938 else if (ss == "off") { 01939 LOG(LOG_MISC,LOG_DEBUG)("A20: locked off"); 01940 a20_guest_changeable = false; 01941 memory.a20.enabled = 0; 01942 } 01943 else if (ss == "off_fake") { 01944 LOG(LOG_MISC,LOG_DEBUG)("A20: locked off (but will fake control bit)"); 01945 a20_guest_changeable = false; 01946 a20_fake_changeable = true; 01947 memory.a20.enabled = 0; 01948 } 01949 else if (ss == "fast") { 01950 LOG(LOG_MISC,LOG_DEBUG)("A20: fast mode"); 01951 a20_fast_changeable = true; 01952 a20_guest_changeable = true; 01953 } 01954 else { 01955 LOG(LOG_MISC,LOG_DEBUG)("A20: masking emulation"); 01956 a20_guest_changeable = true; 01957 } 01958 } 01959 01960 void Init_A20_Gate() { 01961 LOG(LOG_MISC,LOG_DEBUG)("Initializing A20 gate emulation"); 01962 01963 AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(A20Gate_OnReset)); 01964 } 01965 01966 void PS2Port92_OnReset(Section *sec) { 01967 (void)sec;//UNUSED 01968 Section_prop * section=static_cast<Section_prop *>(control->GetSection("dosbox")); 01969 01970 PS2_Port_92h_WriteHandler2.Uninstall(); 01971 PS2_Port_92h_WriteHandler.Uninstall(); 01972 PS2_Port_92h_ReadHandler.Uninstall(); 01973 01974 if (IS_PC98_ARCH) { 01975 // TODO: add separate dosbox.conf variable for A20 gate control on PC-98 01976 enable_port92 = true; 01977 if (enable_port92) { 01978 PS2_Port_92h_WriteHandler2.Install(0xF6,write_pc98_a20,IO_MB); 01979 PS2_Port_92h_WriteHandler.Install(0xF2,write_pc98_a20,IO_MB); 01980 PS2_Port_92h_ReadHandler.Install(0xF2,read_pc98_a20,IO_MB); 01981 } 01982 } 01983 else { 01984 // TODO: this should be handled in a motherboard init routine 01985 enable_port92 = section->Get_bool("enable port 92"); 01986 if (enable_port92) { 01987 // A20 Line - PS/2 system control port A 01988 // TODO: This should exist in the motherboard emulation code yet to come! The motherboard 01989 // determines A20 gating, not the RAM! 01990 LOG(LOG_MISC,LOG_DEBUG)("Port 92h installed, emulating PS/2 system control port A"); 01991 PS2_Port_92h_WriteHandler.Install(0x92,write_p92,IO_MB); 01992 PS2_Port_92h_ReadHandler.Install(0x92,read_p92,IO_MB); 01993 } 01994 } 01995 } 01996 01997 void Init_PS2_Port_92h() { 01998 LOG(LOG_MISC,LOG_DEBUG)("Initializing PS/2 port 92h emulation"); 01999 02000 AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(PS2Port92_OnReset)); 02001 } 02002 02003 void Init_MemHandles() { 02004 Bitu i; 02005 02006 if (!has_Init_MemHandles) { 02007 AddExitFunction(AddExitFunctionFuncPair(ShutDownMemHandles)); 02008 has_Init_MemHandles = true; 02009 } 02010 02011 // LOG 02012 LOG(LOG_MISC,LOG_DEBUG)("Initializing memory handle array (EMS/XMS handle management). mem_pages=%lx",(unsigned long)memory.pages); 02013 02014 if (memory.mhandles == NULL) 02015 memory.mhandles = new MemHandle[memory.pages]; 02016 02017 for (i = 0;i < memory.pages;i++) 02018 memory.mhandles[i] = 0; //Set to 0 for memory allocation 02019 } 02020 02021 void Init_MemoryAccessArray() { 02022 Bitu i; 02023 02024 /* HACK: need to zero these! */ 02025 memory.lfb.handler=NULL; 02026 memory.lfb.start_page=0; 02027 memory.lfb.end_page=0; 02028 memory.lfb.pages=0; 02029 02030 memory.lfb_mmio.handler=NULL; 02031 memory.lfb_mmio.start_page=0; 02032 memory.lfb_mmio.end_page=0; 02033 memory.lfb_mmio.pages=0; 02034 02035 if (!has_Init_MemoryAccessArray) { 02036 has_Init_MemoryAccessArray = true; 02037 AddExitFunction(AddExitFunctionFuncPair(ShutDownMemoryAccessArray)); 02038 } 02039 02040 // LOG 02041 LOG(LOG_MISC,LOG_DEBUG)("Initializing memory access array (page handler callback system). mem_alias_pagemask=%lx",(unsigned long)memory.mem_alias_pagemask); 02042 02043 // CHECK: address mask init must have been called! 02044 assert(memory.mem_alias_pagemask >= 0xFF); 02045 02046 // we maintain a different page count for page handlers because we want to maintain a 02047 // "cache" of sorts of what device responds to a given memory address. 02048 memory.handler_pages = (1 << (32 - 12)); /* enough for 4GB */ 02049 if ((memory.mem_alias_pagemask+1) != 0/*integer overflow check*/ && 02050 memory.handler_pages > (memory.mem_alias_pagemask+1)) 02051 memory.handler_pages = (memory.mem_alias_pagemask+1); 02052 02053 if (memory.phandlers == NULL) 02054 memory.phandlers = new PageHandler* [memory.handler_pages]; 02055 02056 for (i=0;i < memory.handler_pages;i++) // FIXME: This will eventually init all pages to the "slow path" for device lookup 02057 memory.phandlers[i] = NULL;//&illegal_page_handler; 02058 } 02059 02060 void Init_PCJR_CartridgeROM() { 02061 Bitu i; 02062 02063 // log 02064 LOG(LOG_MISC,LOG_DEBUG)("Mapping ROM handler for PCjr cartridge emulation"); 02065 02066 /* Setup cartridge rom at 0xe0000-0xf0000. 02067 * Don't call this function unless emulating PCjr! */ 02068 for (i=0xe0;i<0xf0;i++) 02069 memory.phandlers[i] = &rom_page_handler; 02070 } 02071 02072 Bitu MEM_PageMask(void) { 02073 return memory.mem_alias_pagemask; 02074 } 02075 02076 Bitu MEM_PageMaskActive(void) { 02077 return memory.mem_alias_pagemask_active; 02078 } 02079 02080 //save state support 02081 extern void* VGA_PageHandler_Func[16]; 02082 02083 void *Memory_PageHandler_table[] = 02084 { 02085 NULL, 02086 &ram_page_handler, 02087 &rom_page_handler, 02088 02089 VGA_PageHandler_Func[0], 02090 VGA_PageHandler_Func[1], 02091 VGA_PageHandler_Func[2], 02092 VGA_PageHandler_Func[3], 02093 VGA_PageHandler_Func[4], 02094 VGA_PageHandler_Func[5], 02095 VGA_PageHandler_Func[6], 02096 VGA_PageHandler_Func[7], 02097 VGA_PageHandler_Func[8], 02098 VGA_PageHandler_Func[9], 02099 VGA_PageHandler_Func[10], 02100 VGA_PageHandler_Func[11], 02101 VGA_PageHandler_Func[12], 02102 VGA_PageHandler_Func[13], 02103 VGA_PageHandler_Func[14], 02104 VGA_PageHandler_Func[15], 02105 }; 02106 02107 extern bool dos_kernel_disabled; 02108 02109 namespace 02110 { 02111 class SerializeMemory : public SerializeGlobalPOD 02112 { 02113 public: 02114 SerializeMemory() : SerializeGlobalPOD("Memory") 02115 {} 02116 02117 private: 02118 virtual void getBytes(std::ostream& stream) 02119 { 02120 Bit8u pagehandler_idx[0x40000]; 02121 unsigned int size_table; 02122 02123 // Assume 1000MB maximum memory size 02124 // FIXME: Memory size can be even larger! Up to 3.5GB on 64-bit builds! 02125 size_table = sizeof(Memory_PageHandler_table) / sizeof(void *); 02126 for( unsigned int lcv=0; lcv<memory.pages; lcv++ ) { 02127 pagehandler_idx[lcv] = 0xff; 02128 02129 for( unsigned int lcv2=0; lcv2<size_table; lcv2++ ) { 02130 if( memory.phandlers[lcv] == Memory_PageHandler_table[lcv2] ) { 02131 pagehandler_idx[lcv] = lcv2; 02132 break; 02133 } 02134 } 02135 } 02136 02137 //******************************************* 02138 //******************************************* 02139 02140 SerializeGlobalPOD::getBytes(stream); 02141 02142 // - near-pure data 02143 WRITE_POD( &memory, memory ); 02144 02145 // - static 'new' ptr 02146 WRITE_POD_SIZE( MemBase, memory.pages*4096 ); 02147 02148 //*********************************************** 02149 //*********************************************** 02150 02151 if (!dos_kernel_disabled) { 02152 WRITE_POD_SIZE( memory.mhandles, sizeof(MemHandle) * memory.pages ); 02153 } 02154 else { 02155 /* gotta fake it! */ 02156 MemHandle m = 0; 02157 for (unsigned int i=0;i < memory.pages;i++) { 02158 WRITE_POD_SIZE( &m, sizeof(MemHandle) ); 02159 } 02160 } 02161 WRITE_POD( &pagehandler_idx, pagehandler_idx ); 02162 } 02163 02164 virtual void setBytes(std::istream& stream) 02165 { 02166 Bit8u pagehandler_idx[0x40000]; 02167 void *old_ptrs[4]; 02168 02169 old_ptrs[0] = (void *) memory.phandlers; 02170 old_ptrs[1] = (void *) memory.mhandles; 02171 old_ptrs[2] = (void *) memory.lfb.handler; 02172 old_ptrs[3] = (void *) memory.lfb_mmio.handler; 02173 02174 //*********************************************** 02175 //*********************************************** 02176 02177 SerializeGlobalPOD::setBytes(stream); 02178 02179 02180 // - near-pure data 02181 READ_POD( &memory, memory ); 02182 02183 // - static 'new' ptr 02184 READ_POD_SIZE( MemBase, memory.pages*4096 ); 02185 02186 //*********************************************** 02187 //*********************************************** 02188 02189 memory.phandlers = (PageHandler **) old_ptrs[0]; 02190 memory.mhandles = (MemHandle *) old_ptrs[1]; 02191 memory.lfb.handler = (PageHandler *) old_ptrs[2]; 02192 memory.lfb_mmio.handler = (PageHandler *) old_ptrs[3]; 02193 02194 02195 if (!dos_kernel_disabled) { 02196 READ_POD_SIZE( memory.mhandles, sizeof(MemHandle) * memory.pages ); 02197 } 02198 else { 02199 /* gotta fake it! */ 02200 MemHandle m = 0; 02201 for (unsigned int i=0;i < memory.pages;i++) { 02202 READ_POD_SIZE( &m, sizeof(MemHandle) ); 02203 } 02204 } 02205 READ_POD( &pagehandler_idx, pagehandler_idx ); 02206 02207 02208 for( unsigned int lcv=0; lcv<memory.pages; lcv++ ) { 02209 if( pagehandler_idx[lcv] != 0xff ) 02210 memory.phandlers[lcv] = (PageHandler *) Memory_PageHandler_table[ pagehandler_idx[lcv] ]; 02211 else if ( lcv >= 0xa0 && lcv <= 0xff) 02212 { /* VGA and BIOS emulation does not handle this right, yet */ } 02213 else 02214 memory.phandlers[lcv] = NULL; /* MEM_SlowPath() will fill it in again */ 02215 } 02216 } 02217 } dummy; 02218 }