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