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