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