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