diff options
Diffstat (limited to 'SrcShared/Hardware/EmBankMapped.cpp')
-rw-r--r-- | SrcShared/Hardware/EmBankMapped.cpp | 723 |
1 files changed, 723 insertions, 0 deletions
diff --git a/SrcShared/Hardware/EmBankMapped.cpp b/SrcShared/Hardware/EmBankMapped.cpp new file mode 100644 index 0000000..8ae284a --- /dev/null +++ b/SrcShared/Hardware/EmBankMapped.cpp @@ -0,0 +1,723 @@ +/* -*- mode: C++; tab-width: 4 -*- */ +/* ===================================================================== *\ + Copyright (c) 2000-2001 Palm, Inc. or its subsidiaries. + All rights reserved. + + This file is part of the Palm OS Emulator. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. +\* ===================================================================== */ + +#include "EmCommon.h" +#include "EmBankMapped.h" + +#include "EmCPU68K.h" // gCPU68K +#include "EmMemory.h" // Memory::InitializeBanks +#include "Profiling.h" // WAITSTATES_DUMMYBANK + +#include <vector> + + +// =========================================================================== +// ¥ Dummy Bank Accessors +// =========================================================================== +// Dummy banks are non-existent blocks of memory. Dummy bank accessors do +// not do anything. + +static EmAddressBank gAddressBank = +{ + EmBankMapped::GetLong, + EmBankMapped::GetWord, + EmBankMapped::GetByte, + EmBankMapped::SetLong, + EmBankMapped::SetWord, + EmBankMapped::SetByte, + EmBankMapped::GetRealAddress, + EmBankMapped::ValidAddress, + EmBankMapped::GetMetaAddress, + EmBankMapped::AddOpcodeCycles +}; + +struct MapRange +{ + Bool Contains (const void* addr) + { + const char* begin = (const char*) this->realAddress; + const char* end = begin + this->size; + + return ((addr >= begin) && (addr < end)); + } + + Bool Contains (emuptr addr) + { + emuptr begin = this->mappedAddress; + emuptr end = begin + this->size; + + return ((addr >= begin) && (addr < end)); + } + + const void* realAddress; // Address in host's space + emuptr mappedAddress; // Address that emulated code sees + uint32 size; +}; +typedef vector<MapRange> MapRangeList; + +static MapRangeList gMappedRanges; +static MapRangeList::iterator gLastIter; + +// Map in blocks starting at this address. I used to have it way out of +// the way at 0x60000000. However, there's a check in SysGetAppInfo to +// make sure that certain addresses are less than 0x20000000. So set +// kMemoryStart lower than that. +// +// OK...Let's try again. I had changed this to manage 0x18000000 to +// 0x1FFFFFFF. However, the SED 1375 is mapped to 0x1F000000. So let's +// make sure we stay out of that range, too. +// +// Urm...TRG is mapping some stuff into 0x18000000. Now we have to stay +// out of its way, too. + +const emuptr kMemoryStart = 0x13000000; +const emuptr kMemoryFinish = 0x18000000; +const int32 kMemorySize = kMemoryFinish - kMemoryStart; // 80MB + +static MapRangeList::iterator PrvGetMappingInfo (const void* addr); +static MapRangeList::iterator PrvGetMappingInfo (emuptr addr); +static emuptr PrvEnsureAligned (emuptr candidate, const void* addr); +static void PrvInvalidateCache (void); +static void PrvCheckRanges (void); + + +/*********************************************************************** + * + * FUNCTION: EmBankMapped::Initialize + * + * DESCRIPTION: Standard initialization function. Responsible for + * initializing this sub-system when a new session is + * created. Will be followed by at least one call to + * Reset or Load. + * + * PARAMETERS: None. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +void EmBankMapped::Initialize (void) +{ + gMappedRanges.clear (); + ::PrvInvalidateCache (); +} + + +/*********************************************************************** + * + * FUNCTION: EmBankMapped::Reset + * + * DESCRIPTION: Standard reset function. Sets the sub-system to a + * default state. This occurs not only on a Reset (as + * from the menu item), but also when the sub-system + * is first initialized (Reset is called after Initialize) + * as well as when the system is re-loaded from an + * insufficient session file. + * + * PARAMETERS: None. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +void EmBankMapped::Reset (Bool /*hardwareReset*/) +{ + // Note: I used to clear out the mapped ranges in this function. + // However, this didn't work out too well. While manually resetting + // the device (as occurs at the end of a debugging session), there + // would be several StMemoryMapper objects instantiated. After the + // reset process, these StMemoryMappers would try to destruct + // themselves, calling EmBankMapped::UnmapPhysicalMemory in the + // process. But since the mapped ranges had been cleared, that + // function would throw an assert. So....I can't think of a reason + // for clearing out the ranges en masse, so let's no do that anymore. +} + + +/*********************************************************************** + * + * FUNCTION: EmBankMapped::Save + * + * DESCRIPTION: Standard save function. Saves any sub-system state to + * the given session file. + * + * PARAMETERS: None. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +void EmBankMapped::Save (SessionFile&) +{ + // The only state we have to save is in gMappedRanges. We shouldn't + // have to actually save this because there shouldn't be anything in + // there at the moment when we save files. We map in memory ranges + // at the following times: + // + // * Calling system functions + // * Loading .prc, etc., files + // * Mapping in environment strings + // + // The first two should not be in "effect" at the time we save a file. + // The last one we'll leave up to the HostControl system to re-establish. +} + + +/*********************************************************************** + * + * FUNCTION: EmBankMapped::Load + * + * DESCRIPTION: Standard load function. Loads any sub-system state + * from the given session file. + * + * PARAMETERS: None. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +void EmBankMapped::Load (SessionFile&) +{ +} + + +/*********************************************************************** + * + * FUNCTION: EmBankMapped::Dispose + * + * DESCRIPTION: Standard dispose function. Completely release any + * resources acquired or allocated in Initialize and/or + * Load. + * + * PARAMETERS: None. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +void EmBankMapped::Dispose (void) +{ + gMappedRanges.clear (); + ::PrvInvalidateCache (); +} + + +/*********************************************************************** + * + * FUNCTION: EmBankMapped::SetBankHandlers + * + * DESCRIPTION: Set the bank handlers UAE uses to dispatch memory + * access operations. + * + * PARAMETERS: None + * + * RETURNED: Nothing + * + ***********************************************************************/ + +void EmBankMapped::SetBankHandlers (void) +{ + Memory::InitializeBanks (gAddressBank, EmMemBankIndex (kMemoryStart), kMemorySize >> 16); +} + + +// --------------------------------------------------------------------------- +// ¥ EmBankMapped::GetLong +// --------------------------------------------------------------------------- + +uint32 EmBankMapped::GetLong (emuptr address) +{ + if (CHECK_FOR_ADDRESS_ERROR && (address & 1) != 0) + { + AddressError (address, sizeof (uint32), true); + } + +#if HAS_PROFILING + CYCLE_GETLONG (WAITSTATES_DUMMYBANK); +#endif + + uint8* p = GetRealAddress (address); + + if (p == NULL) + { + return ~0; + } + + return (((uint32) p[0]) << 24) | (((uint32) p[1]) << 16) | (((uint32) p[2]) << 8) | p[3]; +} + + +// --------------------------------------------------------------------------- +// ¥ EmBankMapped::GetWord +// --------------------------------------------------------------------------- + +uint32 EmBankMapped::GetWord (emuptr address) +{ + if (CHECK_FOR_ADDRESS_ERROR && (address & 1) != 0) + { + AddressError (address, sizeof (uint16), true); + } + +#if HAS_PROFILING + CYCLE_GETWORD (WAITSTATES_DUMMYBANK); +#endif + + uint8* p = GetRealAddress (address); + + if (p == NULL) + { + return ~0; + } + + return (((uint32) p[0]) << 8) | p[1]; +} + + +// --------------------------------------------------------------------------- +// ¥ EmBankMapped::GetByte +// --------------------------------------------------------------------------- + +uint32 EmBankMapped::GetByte (emuptr address) +{ +#if HAS_PROFILING + CYCLE_GETBYTE (WAITSTATES_DUMMYBANK); +#endif + + uint8* p = GetRealAddress (address); + + if (p == NULL) + { + return ~0; + } + + return p[0]; +} + + +// --------------------------------------------------------------------------- +// ¥ EmBankMapped::SetLong +// --------------------------------------------------------------------------- + +void EmBankMapped::SetLong (emuptr address, uint32 value) +{ + if (CHECK_FOR_ADDRESS_ERROR && (address & 1) != 0) + { + AddressError (address, sizeof (uint32), false); + } + +#if HAS_PROFILING + CYCLE_PUTLONG (WAITSTATES_DUMMYBANK); +#endif + + uint8* p = GetRealAddress (address); + + if (p == NULL) + { + return; + } + + p[0] = (uint8) (value >> 24); + p[1] = (uint8) (value >> 16); + p[2] = (uint8) (value >> 8); + p[3] = (uint8) (value >> 0); +} + + +// --------------------------------------------------------------------------- +// ¥ EmBankMapped::SetWord +// --------------------------------------------------------------------------- + +void EmBankMapped::SetWord (emuptr address, uint32 value) +{ + if (CHECK_FOR_ADDRESS_ERROR && (address & 1) != 0) + { + AddressError (address, sizeof (uint16), false); + } + +#if HAS_PROFILING + CYCLE_PUTWORD (WAITSTATES_DUMMYBANK); +#endif + + uint8* p = GetRealAddress (address); + + if (p == NULL) + { + return; + } + + p[0] = (uint8) (value >> 8); + p[1] = (uint8) (value >> 0); +} + + +// --------------------------------------------------------------------------- +// ¥ EmBankMapped::SetByte +// --------------------------------------------------------------------------- + +void EmBankMapped::SetByte (emuptr address, uint32 value) +{ +#if HAS_PROFILING + CYCLE_PUTBYTE (WAITSTATES_DUMMYBANK); +#endif + + uint8* p = GetRealAddress (address); + + if (p == NULL) + { + return; + } + + p[0] = (uint8) (value >> 0); +} + + +// --------------------------------------------------------------------------- +// ¥ EmBankMapped::ValidAddress +// --------------------------------------------------------------------------- + +int EmBankMapped::ValidAddress (emuptr address, uint32) +{ + uint8* realAddress = GetRealAddress (address); + int result = realAddress != NULL; + + return result; +} + + +// --------------------------------------------------------------------------- +// ¥ EmBankMapped::GetRealAddress +// --------------------------------------------------------------------------- + +uint8* EmBankMapped::GetRealAddress (emuptr address) +{ + MapRangeList::iterator iter = ::PrvGetMappingInfo (address); + + if (iter == gMappedRanges.end ()) + return NULL; + + return ((uint8*) iter->realAddress) + (address - iter->mappedAddress); +} + + +// --------------------------------------------------------------------------- +// ¥ EmBankMapped::GetMetaAddress +// --------------------------------------------------------------------------- + +uint8* EmBankMapped::GetMetaAddress (emuptr address) +{ + UNUSED_PARAM(address) + + static uint8 dummyBits[4] = {0, 0, 0, 0}; + + return dummyBits; +} + + +// --------------------------------------------------------------------------- +// ¥ EmBankMapped::GetEmulatedAddress +// --------------------------------------------------------------------------- + +emuptr EmBankMapped::GetEmulatedAddress (const void* address) +{ + MapRangeList::iterator iter = ::PrvGetMappingInfo (address); + + if (iter == gMappedRanges.end ()) + { + EmAssert (false); + return EmMemNULL; + } + + return iter->mappedAddress + ((char*) address - (char*) iter->realAddress); +} + + +// --------------------------------------------------------------------------- +// ¥ EmBankMapped::AddOpcodeCycles +// --------------------------------------------------------------------------- + +void EmBankMapped::AddOpcodeCycles (void) +{ +} + + +// --------------------------------------------------------------------------- +// ¥ EmBankMapped::AddressError +// --------------------------------------------------------------------------- + +void EmBankMapped::AddressError (emuptr address, long size, Bool forRead) +{ + EmAssert (gCPU68K); + gCPU68K->AddressError (address, size, forRead); +} + + +// --------------------------------------------------------------------------- +// ¥ EmBankMapped::InvalidAccess +// --------------------------------------------------------------------------- + +void EmBankMapped::InvalidAccess (emuptr address, long size, Bool forRead) +{ + EmAssert (gCPU68K); + gCPU68K->BusError (address, size, forRead); +} + + +// --------------------------------------------------------------------------- +// ¥ EmBankMapped::MapPhysicalMemory +// --------------------------------------------------------------------------- +// Maps a range of physical memory to appear at the same location of the +// emulated Palm OS's virtual memory. + +void EmBankMapped::MapPhysicalMemory (const void* addr, uint32 size) +{ + if (addr == NULL) + return; + + emuptr candidate = ::PrvEnsureAligned (kMemoryStart, addr); + + // If the list is empty, add the item + + if (gMappedRanges.size () == 0) + { + MapRange range; + + range.realAddress = addr; + range.mappedAddress = candidate; + range.size = size; + + gLastIter = gMappedRanges.insert (gMappedRanges.begin (), range); + ::PrvCheckRanges (); + +// ::PrvInvalidateCache (); + + return; + } + + // Find an available space for the range. + + MapRangeList::iterator iter = gMappedRanges.begin (); + + while (iter != gMappedRanges.end ()) + { + // If there's room in front of the current block... + + if (iter->mappedAddress >= candidate + size) + { + // ...insert a map record in front of the current block. + + MapRange range; + + range.realAddress = addr; + range.mappedAddress = candidate; + range.size = size; + + gLastIter = gMappedRanges.insert (iter, range); + ::PrvCheckRanges (); + +// ::PrvInvalidateCache (); + + return; + } + + // No room at the inn...move onto the next block. + + candidate = ::PrvEnsureAligned (iter->mappedAddress + iter->size, addr); + ++iter; + } + + // Couldn't find room at the beginning or the middle of the list. Add to the end. + + MapRangeList::iterator last = gMappedRanges.end (); + iter = last - 1; + + MapRange range; + + range.realAddress = addr; + range.mappedAddress = ::PrvEnsureAligned (iter->mappedAddress + iter->size, addr); + range.size = size; + + gLastIter = gMappedRanges.insert (last, range); + ::PrvCheckRanges (); + +// ::PrvInvalidateCache (); +} + + +// --------------------------------------------------------------------------- +// ¥ EmBankMapped::UnmapPhysicalMemory +// --------------------------------------------------------------------------- +// Unmaps a range of physical memory from appearing at the same location of +// the emulated Palm OS's virtual memory. + +void EmBankMapped::UnmapPhysicalMemory (const void* addr) +{ + if (addr == NULL) + return; + + MapRangeList::iterator iter = ::PrvGetMappingInfo (addr); + + // Take out this assert. Without it, it's possible to unmap ranges + // of memory that we may not have actually mapped in. Being able to + // do that makes things like Reset and Dispose methods easier to write. +// EmAssert (iter != gMappedRanges.end ()); + + if (iter != gMappedRanges.end ()) + { + gMappedRanges.erase (iter); + ::PrvCheckRanges (); + + ::PrvInvalidateCache (); + } +} + + +// --------------------------------------------------------------------------- +// ¥ EmBankMapped::GetMappingInfo +// --------------------------------------------------------------------------- + +void EmBankMapped::GetMappingInfo (emuptr addr, void** start, uint32* len) +{ + MapRangeList::iterator iter = ::PrvGetMappingInfo (addr); + + if (iter != gMappedRanges.end ()) + { + if (start) + *start = (void*) iter->realAddress; + + if (len) + *len = iter->size; + } + else + { + if (start) + *start = NULL; + + if (len) + *len = 0; + } +} + + +// --------------------------------------------------------------------------- +// ¥ PrvGetMappingInfo +// --------------------------------------------------------------------------- + +MapRangeList::iterator PrvGetMappingInfo (const void* addr) +{ + if (gLastIter != gMappedRanges.end () && gLastIter->Contains (addr)) + return gLastIter; + + MapRangeList::iterator iter = gMappedRanges.begin (); + while (iter != gMappedRanges.end ()) + { + if (iter->Contains (addr)) + break; + + ++iter; + } + + gLastIter = iter; + + return iter; +} + + +MapRangeList::iterator PrvGetMappingInfo (emuptr addr) +{ + if (gLastIter != gMappedRanges.end () && gLastIter->Contains (addr)) + return gLastIter; + + MapRangeList::iterator iter = gMappedRanges.begin (); + while (iter != gMappedRanges.end ()) + { + if (iter->Contains (addr)) + break; + + ++iter; + } + + gLastIter = iter; + + return iter; +} + + +emuptr PrvEnsureAligned (emuptr candidate, const void* addr) +{ + // Make sure that "candidate" -- which is the candidate + // mapped address -- maintains the same alignment as + // the incoming address. + + while ((candidate & 0x03) != ((uint32) addr & 0x03)) + ++candidate; + + return candidate; +} + +void PrvInvalidateCache (void) +{ + gLastIter = gMappedRanges.end (); +} + +void PrvCheckRanges (void) +{ +#if _DEBUG + /* + Check for overlaps. We iterate over the container with + two iterators: outerIter and innerIter. These two iterators + will allow us to compare all possible combinations of + elements in the container. + + When checking for overlaps, there are six cases to consider: + + *-----------* Segment A + + *---* Possible segment B's. + *----------* + *---------------------------* + *---* + *---------------* + *---* + + An overlap occurs if any part of segment B crosses segment A. + From the drawing, we can see that the first and last possible + segments B's are OK; the others overlap with segment A in + some fashion. Thus, Segment B is OK if it's end is less than + Segment A's start, or its start is greater than segment A's end. + */ + + MapRangeList::iterator outerIter = gMappedRanges.begin (); + while (outerIter != gMappedRanges.end ()) + { + MapRangeList::iterator innerIter = outerIter + 1; + while (innerIter != gMappedRanges.end ()) + { + char* outerStart = (char*) outerIter->realAddress; + char* innerStart = (char*) innerIter->realAddress; + + char* outerEnd = outerStart + outerIter->size; + char* innerEnd = innerStart + innerIter->size; + + if (innerEnd > outerStart && innerStart < outerEnd) + { + EmAssert (false); + } + + ++innerIter; + } + + ++outerIter; + } +#endif +} + |