aboutsummaryrefslogtreecommitdiff
path: root/SrcShared/Hardware/EmBankRegs.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'SrcShared/Hardware/EmBankRegs.cpp')
-rw-r--r--SrcShared/Hardware/EmBankRegs.cpp694
1 files changed, 694 insertions, 0 deletions
diff --git a/SrcShared/Hardware/EmBankRegs.cpp b/SrcShared/Hardware/EmBankRegs.cpp
new file mode 100644
index 0000000..c03deb3
--- /dev/null
+++ b/SrcShared/Hardware/EmBankRegs.cpp
@@ -0,0 +1,694 @@
+/* -*- 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 "EmBankRegs.h"
+
+#include "DebugMgr.h" // Debug::CheckStepSpy
+#include "EmCPU.h" // GetPC
+#include "EmCPU68K.h" // gCPU68K
+#include "EmMemory.h" // gMemAccessFlags, EmMemory::IsPCInRAM
+#include "EmSession.h" // GetDevice, ScheduleDeferredError
+#include "ErrorHandling.h" // Errors::ReportErrHardwareRegisters
+#include "MetaMemory.h" // MetaMemory::InRAMOSComponent
+#include "Profiling.h" // WAITSTATES_PLD
+
+
+/*
+ When emulating memory, UAE divides up the 4GB address space into
+ 65,536 banks, each of which is 64K long. For more details, see the
+ comments in EmMemory.cpp.
+
+ For our purposes, we sometimes need to divide up each 64K bank in a
+ similar fashion. For instance, in order to support accesses to each
+ of the Dragonball registers, we'd like to assign a handler to each
+ byte, word, or long of memory in the bank that contains the
+ Dragonball registers.
+
+ This module supports that further subdivision. EmBankRegs is a bank
+ handler -- just like the RAM and ROM bank handlers -- in that it is
+ responsible for memory accesses to one or more 64K banks of memory.
+ It divides up each 64K bank by making use of instances of EmRegs
+ subclasses.
+
+ EmRegs subclasses are the moral equivalent of EmBanks, except on a
+ smaller scale. They are responsible for byte, word, and long memory
+ accesses to a small range of memory completely contained within a
+ 64K bank.
+
+ Many such EmRegs instances can be created and installed into the
+ EmBankRegs object. EmBankRegs stores these EmRegs in a list. When
+ in comes time for the EmBanks to register which 64K banks they are
+ responsible for, EmBankRegs iterates over all the EmRegs objects
+ registered with it, and takes responsibility for each 64K bank that
+ contains the EmRegs object memory range (an EmRegs object can be
+ queried as to the memory start and length it is responsible for).
+
+ Later, when a memory access is made to any of the 64K banks
+ EmBankRegs registered for, it again iterates over the EmRegs objects
+ installed into it, looking for one that takes responsibility for the
+ memory address being accessed. When it finds one, it passes off the
+ request to that object. Otherwise, it signals a bus error.
+*/
+
+
+static EmAddressBank gAddressBank =
+{
+ EmBankRegs::GetLong,
+ EmBankRegs::GetWord,
+ EmBankRegs::GetByte,
+ EmBankRegs::SetLong,
+ EmBankRegs::SetWord,
+ EmBankRegs::SetByte,
+ EmBankRegs::GetRealAddress,
+ EmBankRegs::ValidAddress,
+ NULL,
+ NULL
+};
+
+
+EmRegsList EmBankRegs::fgSubBanks;
+EmRegsList EmBankRegs::fgDisabledSubBanks;
+
+static EmRegs* gLastSubBank;
+static uint64 gLastStart;
+static uint32 gLastRange;
+
+static void PrvSwitchBanks (EmRegsList& fromList, EmRegsList& toList, emuptr address);
+
+#pragma mark -
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmBankRegs::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 EmBankRegs::Initialize (void)
+{
+ EmAssert (gSession);
+ gSession->GetDevice ().CreateRegs ();
+
+ EmRegsList::iterator iter = fgSubBanks.begin ();
+ while (iter != fgSubBanks.end ())
+ {
+ (*iter)->Initialize ();
+ ++iter;
+ }
+
+ // Install the handlers for each affected bank.
+
+ EmBankRegs::SetBankHandlers ();
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmBankRegs::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 EmBankRegs::Reset (Bool hardwareReset)
+{
+ EmRegsList::iterator iter = fgSubBanks.begin ();
+ while (iter != fgSubBanks.end ())
+ {
+ (*iter)->Reset (hardwareReset);
+ ++iter;
+ }
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmBankRegs::Save
+ *
+ * DESCRIPTION: Standard save function. Saves any sub-system state to
+ * the given session file.
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+void EmBankRegs::Save (SessionFile& f)
+{
+ EmRegsList::iterator iter = fgSubBanks.begin ();
+ while (iter != fgSubBanks.end ())
+ {
+ (*iter)->Save (f);
+ ++iter;
+ }
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmBankRegs::Load
+ *
+ * DESCRIPTION: Standard load function. Loads any sub-system state
+ * from the given session file.
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+void EmBankRegs::Load (SessionFile& f)
+{
+ EmRegsList::iterator iter = fgSubBanks.begin ();
+ while (iter != fgSubBanks.end ())
+ {
+ (*iter)->Load (f);
+ ++iter;
+ }
+
+ gLastSubBank = NULL;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmBankRegs::Dispose
+ *
+ * DESCRIPTION: Standard dispose function. Completely release any
+ * resources acquired or allocated in Initialize and/or
+ * Load.
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+void EmBankRegs::Dispose (void)
+{
+ EmRegsList::iterator iter = fgSubBanks.begin ();
+ while (iter != fgSubBanks.end ())
+ {
+ (*iter)->Dispose ();
+ ++iter;
+ }
+
+ while (fgSubBanks.size () > 0)
+ {
+ EmRegs* bank = fgSubBanks[0];
+ fgSubBanks.erase (fgSubBanks.begin ());
+ delete bank;
+ }
+
+ gLastSubBank = NULL;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmBankRegs::SetBankHandlers
+ *
+ * DESCRIPTION: Set the bank handlers UAE uses to dispatch memory
+ * access operations.
+ *
+ * PARAMETERS: None
+ *
+ * RETURNED: Nothing
+ *
+ ***********************************************************************/
+
+void EmBankRegs::SetBankHandlers (void)
+{
+ EmRegsList::iterator iter = fgSubBanks.begin ();
+ while (iter != fgSubBanks.end ())
+ {
+ (*iter)->SetBankHandlers (gAddressBank);
+ ++iter;
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmBankRegs::GetLong
+// ---------------------------------------------------------------------------
+
+uint32 EmBankRegs::GetLong (emuptr address)
+{
+#if (CHECK_FOR_ADDRESS_ERROR)
+ if ((address & 1) != 0)
+ {
+ AddressError (address, sizeof (uint32), true);
+ }
+#endif
+
+#if (PREVENT_USER_REGISTER_GET)
+ if (gMemAccessFlags.fProtect_RegisterGet && EmMemory::IsPCInRAM () && !MetaMemory::InRAMOSComponent (gCPU->GetPC ()))
+ {
+ EmBankRegs::PreventedAccess (address, sizeof (uint32), true);
+ }
+#endif
+
+#if (VALIDATE_REGISTER_GET)
+ if (gMemAccessFlags.fValidate_RegisterGet && !ValidAddress (address, sizeof (uint32)))
+ {
+ EmBankRegs::InvalidAccess (address, sizeof (uint32), true);
+ }
+#endif
+
+#if HAS_PROFILING
+ CYCLE_GETLONG (WAITSTATES_PLD);
+#endif
+
+ EmRegs* bank = EmBankRegs::GetSubBank (address, sizeof (uint32));
+
+ if (bank)
+ {
+ return bank->GetLong (address);
+ }
+
+ EmBankRegs::InvalidAccess (address, sizeof (uint32), true);
+ return ~0;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmBankRegs::GetWord
+// ---------------------------------------------------------------------------
+
+uint32 EmBankRegs::GetWord (emuptr address)
+{
+#if (CHECK_FOR_ADDRESS_ERROR)
+ if ((address & 1) != 0)
+ {
+ AddressError (address, sizeof (uint16), true);
+ }
+#endif
+
+#if (PREVENT_USER_REGISTER_GET)
+ if (gMemAccessFlags.fProtect_RegisterGet && EmMemory::IsPCInRAM () && !MetaMemory::InRAMOSComponent (gCPU->GetPC ()))
+ {
+ EmBankRegs::PreventedAccess (address, sizeof (uint16), true);
+ }
+#endif
+
+#if (VALIDATE_REGISTER_GET)
+ if (gMemAccessFlags.fValidate_RegisterGet && !ValidAddress (address, sizeof (uint16)))
+ {
+ EmBankRegs::InvalidAccess (address, sizeof (uint16), true);
+ }
+#endif
+
+#if HAS_PROFILING
+ CYCLE_GETWORD (WAITSTATES_PLD);
+#endif
+
+ EmRegs* bank = EmBankRegs::GetSubBank (address, sizeof (uint16));
+
+ if (bank)
+ {
+ return bank->GetWord (address);
+ }
+
+ EmBankRegs::InvalidAccess (address, sizeof (uint16), true);
+ return ~0;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmBankRegs::GetByte
+// ---------------------------------------------------------------------------
+
+uint32 EmBankRegs::GetByte (emuptr address)
+{
+#if (PREVENT_USER_REGISTER_GET)
+ if (gMemAccessFlags.fProtect_RegisterGet && EmMemory::IsPCInRAM () && !MetaMemory::InRAMOSComponent (gCPU->GetPC ()))
+ {
+ EmBankRegs::PreventedAccess (address, sizeof (uint8), true);
+ }
+#endif
+
+#if (VALIDATE_REGISTER_GET)
+ if (gMemAccessFlags.fValidate_RegisterGet && !ValidAddress (address, sizeof (uint8)))
+ {
+ EmBankRegs::InvalidAccess (address, sizeof (uint8), true);
+ }
+#endif
+
+#if HAS_PROFILING
+ CYCLE_GETBYTE (WAITSTATES_PLD);
+#endif
+
+ EmRegs* bank = EmBankRegs::GetSubBank (address, sizeof (uint8));
+
+ if (bank)
+ {
+ return bank->GetByte (address);
+ }
+
+ EmBankRegs::InvalidAccess (address, sizeof (uint8), true);
+ return ~0;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmBankRegs::SetLong
+// ---------------------------------------------------------------------------
+
+void EmBankRegs::SetLong (emuptr address, uint32 value)
+{
+#if PROFILE_MEMORY
+ gMemoryAccess[kPLDLongWrite]++;
+ if (address & 2)
+ gMemoryAccess[kPLDLongWrite2]++;
+#endif
+
+#if (CHECK_FOR_ADDRESS_ERROR)
+ if ((address & 1) != 0)
+ {
+ AddressError (address, sizeof (uint32), false);
+ }
+#endif
+
+#if (PREVENT_USER_REGISTER_SET)
+ if (gMemAccessFlags.fProtect_RegisterSet && EmMemory::IsPCInRAM () && !MetaMemory::InRAMOSComponent (gCPU->GetPC ()))
+ {
+ EmBankRegs::PreventedAccess (address, sizeof (uint32), false);
+ }
+#endif
+
+#if (VALIDATE_REGISTER_SET)
+ if (gMemAccessFlags.fValidate_RegisterSet && !ValidAddress (address, sizeof (uint32)))
+ {
+ EmBankRegs::InvalidAccess (address, sizeof (uint32), false);
+ }
+#endif
+
+#if HAS_PROFILING
+ CYCLE_PUTLONG (WAITSTATES_PLD);
+#endif
+
+ EmRegs* bank = EmBankRegs::GetSubBank (address, sizeof (uint32));
+
+ if (bank)
+ {
+ bank->SetLong (address, value);
+
+ // See if any interesting memory locations have changed. If so,
+ // CheckStepSpy will report it.
+
+ Debug::CheckStepSpy (address, sizeof (uint32));
+
+ return;
+ }
+
+ EmBankRegs::InvalidAccess (address, sizeof (uint32), false);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmBankRegs::SetWord
+// ---------------------------------------------------------------------------
+
+void EmBankRegs::SetWord (emuptr address, uint32 value)
+{
+#if (CHECK_FOR_ADDRESS_ERROR)
+ if ((address & 1) != 0)
+ {
+ AddressError (address, sizeof (uint16), false);
+ }
+#endif
+
+#if (PREVENT_USER_REGISTER_SET)
+ if (gMemAccessFlags.fProtect_RegisterSet && EmMemory::IsPCInRAM () && !MetaMemory::InRAMOSComponent (gCPU->GetPC ()))
+ {
+ EmBankRegs::PreventedAccess (address, sizeof (uint16), false);
+ }
+#endif
+
+#if (VALIDATE_REGISTER_SET)
+ if (gMemAccessFlags.fValidate_RegisterSet && !ValidAddress (address, sizeof (uint16)))
+ {
+ EmBankRegs::InvalidAccess (address, sizeof (uint16), false);
+ }
+#endif
+
+#if HAS_PROFILING
+ CYCLE_PUTWORD (WAITSTATES_PLD);
+#endif
+
+ EmRegs* bank = EmBankRegs::GetSubBank (address, sizeof (uint16));
+
+ if (bank)
+ {
+ bank->SetWord (address, value);
+
+ // See if any interesting memory locations have changed. If so,
+ // CheckStepSpy will report it.
+
+ Debug::CheckStepSpy (address, sizeof (uint16));
+
+ return;
+ }
+
+ EmBankRegs::InvalidAccess (address, sizeof (uint16), false);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmBankRegs::SetByte
+// ---------------------------------------------------------------------------
+
+void EmBankRegs::SetByte (emuptr address, uint32 value)
+{
+#if (PREVENT_USER_REGISTER_SET)
+ if (gMemAccessFlags.fProtect_RegisterSet && EmMemory::IsPCInRAM () && !MetaMemory::InRAMOSComponent (gCPU->GetPC ()))
+ {
+ EmBankRegs::PreventedAccess (address, sizeof (uint8), false);
+ }
+#endif
+
+#if (VALIDATE_REGISTER_SET)
+ if (gMemAccessFlags.fValidate_RegisterSet && !ValidAddress (address, sizeof (uint8)))
+ {
+ EmBankRegs::InvalidAccess (address, sizeof (uint8), false);
+ }
+#endif
+
+#if HAS_PROFILING
+ CYCLE_PUTBYTE (WAITSTATES_PLD);
+#endif
+
+ EmRegs* bank = EmBankRegs::GetSubBank (address, sizeof (uint8));
+
+ if (bank)
+ {
+ bank->SetByte (address, value);
+
+ // See if any interesting memory locations have changed. If so,
+ // CheckStepSpy will report it.
+
+ Debug::CheckStepSpy (address, sizeof (uint8));
+
+ return;
+ }
+
+ EmBankRegs::InvalidAccess (address, sizeof (uint8), false);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmBankRegs::ValidAddress
+// ---------------------------------------------------------------------------
+
+int EmBankRegs::ValidAddress (emuptr address, uint32 size)
+{
+ EmRegs* bank = EmBankRegs::GetSubBank (address, size);
+
+ if (bank)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmBankRegs::GetRealAddress
+// ---------------------------------------------------------------------------
+
+uint8* EmBankRegs::GetRealAddress (emuptr address)
+{
+ EmRegs* bank = EmBankRegs::GetSubBank (address, 0);
+
+ if (bank)
+ {
+ return bank->GetRealAddress (address);
+ }
+
+ return NULL;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmBankRegs::AddSubBank
+// ---------------------------------------------------------------------------
+
+void EmBankRegs::AddSubBank (EmRegs* bank)
+{
+ fgSubBanks.push_back (bank);
+ gLastSubBank = NULL;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmBankRegs::EnableSubBank
+// ---------------------------------------------------------------------------
+
+void EmBankRegs::EnableSubBank (emuptr address)
+{
+ PrvSwitchBanks (fgDisabledSubBanks, fgSubBanks, address);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmBankRegs::DisableSubBank
+// ---------------------------------------------------------------------------
+
+void EmBankRegs::DisableSubBank (emuptr address)
+{
+ PrvSwitchBanks (fgSubBanks, fgDisabledSubBanks, address);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmBankRegs::GetSubBank
+// ---------------------------------------------------------------------------
+
+EmRegs* EmBankRegs::GetSubBank (emuptr address, long size)
+{
+ // Cast address to a 64-bit value in case address + size == 0x1 0000 0000.
+
+ uint64 addrStart64 = address;
+ uint64 addrEnd64 = addrStart64 + size;
+
+ // This cache is a big win. When emulating a Palm IIIc (which uses
+ // three EmRegs-based objects: EmRegsEZPalmIIIc, EmRegsSED1375, and
+ // EmRegsFrameBuffer) and doing a Gremlins run, the cache was hit
+ // 2,356,670 times and missed 19456 times. When doing the same run
+ // on a Palm V, the cache was missed only the first time.
+
+ if (gLastSubBank &&
+ (addrStart64 >= gLastStart) &&
+ (addrEnd64 <= gLastStart + gLastRange))
+ {
+ return gLastSubBank;
+ }
+
+ EmRegsList::iterator iter = fgSubBanks.begin ();
+ while (iter != fgSubBanks.end ())
+ {
+ uint64 start = (*iter)->GetAddressStart ();
+ uint32 range = (*iter)->GetAddressRange ();
+
+ if ((addrStart64 >= start) && (addrEnd64 <= start + range))
+ {
+ gLastSubBank = *iter;
+ gLastStart = start;
+ gLastRange = range;
+
+ return *iter;
+ }
+
+ ++iter;
+ }
+
+ return NULL;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmBankRegs::AddressError
+// ---------------------------------------------------------------------------
+
+void EmBankRegs::AddressError (emuptr address, long size, Bool forRead)
+{
+ EmAssert (gCPU68K);
+ gCPU68K->AddressError (address, size, forRead);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmBankRegs::InvalidAccess
+// ---------------------------------------------------------------------------
+
+void EmBankRegs::InvalidAccess (emuptr address, long size, Bool forRead)
+{
+ EmAssert (gCPU68K);
+ gCPU68K->BusError (address, size, forRead);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmBankRegs::PreventedAccess
+// ---------------------------------------------------------------------------
+
+void EmBankRegs::PreventedAccess (emuptr address, long size, Bool forRead)
+{
+ EmAssert (gSession);
+ gSession->ScheduleDeferredError (new EmDeferredErrHardwareRegisters (address, size, forRead));
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ PrvSwitchBanks
+// ---------------------------------------------------------------------------
+
+void PrvSwitchBanks (EmRegsList& fromList, EmRegsList& toList, emuptr address)
+{
+ EmRegsList::iterator iter = fromList.begin ();
+ while (iter != fromList.end ())
+ {
+ uint64 start = (*iter)->GetAddressStart ();
+ uint32 range = (*iter)->GetAddressRange ();
+
+ if ((address >= start) && (address <= start + range))
+ {
+ toList.push_back (*iter);
+ fromList.erase (iter);
+ return;
+ }
+
+ ++iter;
+ }
+}