aboutsummaryrefslogtreecommitdiff
path: root/SrcShared/Hardware/EmMemory.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'SrcShared/Hardware/EmMemory.cpp')
-rw-r--r--SrcShared/Hardware/EmMemory.cpp1342
1 files changed, 1342 insertions, 0 deletions
diff --git a/SrcShared/Hardware/EmMemory.cpp b/SrcShared/Hardware/EmMemory.cpp
new file mode 100644
index 0000000..90051ac
--- /dev/null
+++ b/SrcShared/Hardware/EmMemory.cpp
@@ -0,0 +1,1342 @@
+/* -*- mode: C++; tab-width: 4 -*- */
+/* ===================================================================== *\
+ Copyright (c) 1998-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 "EmMemory.h"
+
+#include "EmBankDRAM.h" // EmBankDRAM::Initialize
+#include "EmBankDummy.h" // EmBankDummy::Initialize
+#include "EmBankMapped.h" // EmBankMapped::Initialize
+#include "EmBankRegs.h" // EmBankRegs::Initialize
+#include "EmBankROM.h" // EmBankROM::Initialize
+#include "EmBankSRAM.h" // EmBankSRAM::Initialize
+#include "EmSession.h" // gSession, GetDevice
+#include "MetaMemory.h" // MetaMemory::Initialize
+
+
+#if HAS_PROFILING
+#include "Profiling.h" // gProfilingCounted
+#endif
+
+
+/*
+ Hitchhiker's Guide To Accessing Memory
+
+ When emulating CPU instructions, all memory access must be emulated as
+ well. Additionally, the memory location that the emulated instruction
+ is accessing is not necessarily the same as the memory location in the
+ emulator's address space. That is, memory location 0x00001234 in the
+ emulated process's address space is not the same as memory location
+ 0x00001234 in the emulator's address space. Instead, the emulated
+ 0x00001234 is actually 0x00001234 bytes into some buffer that we've
+ allocate for the purpose of holding the emulated address space's contents.
+
+ There are several ranges of Palm OS memory that we need to emulate:
+
+ - Low-memory RAM (for the dynamic heap)
+ - High-memory RAM (for the storage heap)
+ - ROM
+ - Dragonball and other memory-mapped registers
+ - Everything else
+
+ Each of these ranges (except for the last one) is managed by a set of
+ functions for reading from and writing to that memory and a chunk of
+ memory that we allocate as a "backing store" for that memory.
+
+ Thus, for each of these ranges, we need to quickly determine what set of
+ functions need to be called. The method for doing this is to divide the
+ entire 4GB address space into 64K banks, each 64K long.
+
+ Each 64K bank is managed by a set of functions. These functions handle
+ reading from and writing to that 64K bank, handling 1-byte, 2-byte, and
+ 4-byte accesses. They also manage mapping from the emulated addres space
+ to the physical space as viewed by the emulator, and validate memory
+ addresses.
+
+ Because there are 64K of these 64K banks, the functions that manage each
+ bank are stored in an array of 64K elements. When we need to invoke a
+ function for a particular bank, the upper word of the prospective memory
+ address is used as a "bank index" into this array. From that, the right
+ set of functions is found, and the desired function from that set of
+ functions is called.
+
+ This 64K array is very sparse. There are one to four entries for managing
+ the dynamic heap (depending on whether the dynamic heap is 64K, 96K, 128K,
+ or 256K), 2 to 128 entries for the storage heap (depending on whether the
+ storage heap is 128K to 8Meg), 8 to 32 entries for the ROM (again,
+ depending on its size), and 1 for the Dragonball registers. That leaves
+ the remaining 64K - 12 entries to be managed by a set of functions that do
+ little but signal an address error if the memory they manage is being
+ accessed.
+
+ Pictorally, it looks like this:
+
+ Emulated 4G space: Memory function array Address Bank functions
+
+ 0x00000000 +--------------+ +--------------------------------+
+ | 64K |<---| Ptr to fns that mng this range |------+
+ 0x00010000 +--------------+ +--------------------------------+ |
+ | 64K |<---| Ptr to fns that mng this range |------+
+ 0x00020000 +--------------+ +--------------------------------+ |
+ | 64K | +-----> EmBankDRAM::GetLong, EmBankDRAM::SetLong
+ \/\/\/\/\/\/\/\/ EmBankDRAM::GetWord, EmBankDRAM::SetWord
+ \/\/\/\/\/\/\/\/ EmBankDRAM::GetByte, EmBankDRAM::SetByte
+ | 64K | etc.
+ 0x10000000 +--------------+ +--------------------------------+
+ | 64K |<---| Ptr to fns that mng this range |------+
+ 0x10010000 +--------------+ +--------------------------------+ |
+ | 64K |<---| Ptr to fns that mng this range |------+
+ 0x10020000 +--------------+ +--------------------------------+ |
+ | 64K | +-----> EmBankSRAM::GetLong, EmBankSRAM::SetLong
+ \/\/\/\/\/\/\/\/ EmBankSRAM::GetWord, EmBankSRAM::SetWord
+ \/\/\/\/\/\/\/\/ EmBankSRAM::GetByte, EmBankSRAM::SetByte
+ | 64K | etc.
+ 0x10C00000 +--------------+ +--------------------------------+
+ | 64K |<---| Ptr to fns that mng this range |------+
+ 0x10C10000 +--------------+ +--------------------------------+ |
+ | 64K |<---| Ptr to fns that mng this range |------+
+ 0x10C20000 +--------------+ +--------------------------------+ |
+ | 64K | +-----> EmBankROM::GetLong, EmBankROM::SetLong
+ \/\/\/\/\/\/\/\/ EmBankROM::GetWord, EmBankROM::SetWord
+ \/\/\/\/\/\/\/\/ EmBankROM::GetByte, EmBankROM::SetByte
+ | 64K | etc.
+ 0xFFFE0000 +--------------+ +--------------------------------+
+ | 64K |<---| Ptr to fns that mng this range |
+ 0xFFFF0000 +--------------+ +--------------------------------+
+ | 64K |<---| Ptr to fns that mng this range |------+
+ 0xFFFFFFFF +--------------+ +--------------------------------+ |
+ +-----> EmRegs::GetLong, EmRegs::SetLong
+ EmRegs::GetWord, EmRegs::SetWord
+ EmRegs::GetByte, EmRegs::SetByte
+ etc.
+
+ All of the remaining memory function array entries are initialized with
+ pointers to the EmBankDummy::GetLong, EmBankDummy::SetLong, etc., set of
+ functions.
+
+ Using EmBankMapped accessors, we also have the facility for mapping memory
+ from the emulator's address space directly into the emulated address
+ space. This memory appears as additional RAM that magically appears out
+ of nowhere. This mechanism makes it easy for us to "inject" data into the
+ emulated address space. It also makes it easier to call emulated ROM
+ functions as subroutines. Some ROM functions take as parameters pointers
+ to memory locations that are to receive values (for example, pointers to
+ local variables). For those functions, we just map the emulator's stack
+ into the emulated address space. After that's done, we can pass pointers
+ to stack-based variables with impunity.
+
+ A 64K bank can be further sub-divided into 1, 2, or 4 byte chunks
+ spanning any range of the 64K chunk. EmBankRegs manages a collection
+ of EmRegs objects. The EmRegs abstract base class is sub-classed into
+ classes that specify the sub-range of the 64K for which they are
+ responsible and dispatch memory accesses to individual functions
+ that handle accesses at more-or-less a byte level.
+
+ Mapping from emulated addresses to "real" addresses (that is, the
+ corresponding memory locations within the buffers we've allocated to
+ "stand in" for the emulated memory) is performed via macros and inline
+ functions, described below.
+
+ Executive summary:
+
+ - use EmMemGetFoo if you want the address checked for even/oddness.
+ - use EmMemDoGetFoo to actually get the value using a physical address.
+
+ EmMemGet32, EmMemGet16, EmMemGet8
+ EmMemPut32, EmMemPut16, EmMemPut8
+ Inline functions that dispatch to the right bank of functions
+ using EmMemCallGetFunc, EmMemGetBank, and EmMemBankIndex.
+
+ EmMemBankIndex
+ Returns a bank index for the given address.
+
+ EmMemGetBank
+ Gets the right EmAddressBank object.
+
+ EmMemCallGetFunc
+ Merely calls the given function through the EmAddressBank fn ptr.
+
+ EmMemDoGet32, EmMemDoGet16, EmMemDoGet8
+ EmMemDoPut32, EmMemDoPut16, EmMemDoPut8
+ Very low-level memory access. They return the value at the
+ given physical location, byte-swapping if needed.
+*/
+
+// Types.
+
+#pragma mark Types
+
+// Globals.
+
+#pragma mark Globals
+
+EmAddressBank* gEmMemBanks[65536]; // (normally defined in memory.c)
+
+Bool gPCInRAM;
+Bool gPCInROM;
+
+MemAccessFlags gMemAccessFlags =
+{
+ MASTER_RUNTIME_VALIDATE_SWITCH,
+ MASTER_RUNTIME_VALIDATE_SWITCH,
+ MASTER_RUNTIME_VALIDATE_SWITCH,
+ MASTER_RUNTIME_VALIDATE_SWITCH,
+ MASTER_RUNTIME_VALIDATE_SWITCH,
+ MASTER_RUNTIME_VALIDATE_SWITCH,
+ MASTER_RUNTIME_VALIDATE_SWITCH,
+ MASTER_RUNTIME_VALIDATE_SWITCH,
+ MASTER_RUNTIME_VALIDATE_SWITCH,
+ MASTER_RUNTIME_VALIDATE_SWITCH,
+
+ // User prevention flags.
+
+// MASTER_RUNTIME_PREVENT_SWITCH,
+// MASTER_RUNTIME_PREVENT_SWITCH,
+// MASTER_RUNTIME_PREVENT_SWITCH,
+// MASTER_RUNTIME_PREVENT_SWITCH,
+// MASTER_RUNTIME_PREVENT_SWITCH,
+// MASTER_RUNTIME_PREVENT_SWITCH,
+ false, // SRAM-get
+ true, // SRAM-set
+ false, // ROM-get
+ true, // ROM-set (HACK: We really want it to be true!)
+ MASTER_RUNTIME_PREVENT_SWITCH,
+ MASTER_RUNTIME_PREVENT_SWITCH,
+
+// false, // fCheck_UserChunkGet
+// false, // fCheck_UserChunkSet
+// false, // fCheck_SysChunkGet
+// false, // fCheck_SysChunkSet
+
+ // System prevention flags.
+
+// MASTER_RUNTIME_PREVENT_SWITCH,
+// MASTER_RUNTIME_PREVENT_SWITCH,
+// MASTER_RUNTIME_PREVENT_SWITCH,
+// MASTER_RUNTIME_PREVENT_SWITCH,
+// MASTER_RUNTIME_PREVENT_SWITCH,
+// MASTER_RUNTIME_PREVENT_SWITCH,
+// false, // SRAM-get
+// true, // SRAM-set
+ false, // ROM-get
+ true, // ROM-set (HACK: We really want it to be true!)
+// MASTER_RUNTIME_PREVENT_SWITCH,
+// MASTER_RUNTIME_PREVENT_SWITCH
+};
+
+MemAccessFlags kZeroMemAccessFlags;
+
+
+#if PROFILE_MEMORY
+/*
+ After 12 Million instructions executed...
+
+ kDRAMLongRead, // 2,797,111
+ kDRAMLongRead2, //
+ kDRAMWordRead, // 1,334,295
+ kDRAMByteRead, // 427,936
+
+ kDRAMLongWrite, // 1,960,933
+ kDRAMLongWrite2, //
+ kDRAMWordWrite, // 800,780
+ kDRAMByteWrite, // 213,029
+
+ kSRAMLongRead, // 4,167
+ kSRAMLongRead2, // 4,167
+ kSRAMWordRead, // 10,768
+ kSRAMByteRead, // 6,295 (jumped WAY up (5-fold) after playing with Address Book / 20 Million instructions)
+
+ kSRAMLongWrite, // 516,246 (stable after boot?)
+ kSRAMLongWrite2, //
+ kSRAMWordWrite, // 5,112
+ kSRAMByteWrite, // 2,183
+
+ kROMLongRead, // 10,156
+ kROMLongRead2, //
+ kROMWordRead, // 557,062
+ kROMByteRead, // 15,516
+
+ kROMLongWrite, // 0
+ kROMLongWrite2, //
+ kROMWordWrite, // 0
+ kROMByteWrite, // 0
+
+ kREGLongRead, // 16,638
+ kREGLongRead2, //
+ kREGWordRead, // 30,477
+ kREGByteRead, // 3,553
+
+ kREGLongWrite, // 9,472
+ kREGLongWrite2, //
+ kREGWordWrite, // 20,709
+ kREGByteWrite, // 12,656
+
+
+ After 32,439,154 instructions:
+
+ kDRAMLongRead, // 6,466,064
+ kDRAMWordRead, // 3,342,826
+ kDRAMByteRead, // 940,345
+
+ kDRAMLongWrite, // 4,136,635
+ kDRAMWordWrite, // 1,462,995
+ kDRAMByteWrite, // 509,189
+
+ kSRAMLongRead, // 24,355
+ kSRAMWordRead, // 32,190
+ kSRAMByteRead, // 75,917
+
+ kSRAMLongWrite, // 2,074,137 (8-Meg ROM)
+ kSRAMWordWrite, // 42,050
+ kSRAMByteWrite, // 11,292
+
+ kROMLongRead, // 25,806
+ kROMWordRead, // 1,302,897
+ kROMByteRead, // 63,590
+
+ kROMLongWrite, // 0
+ kROMWordWrite, // 0
+ kROMByteWrite, // 0
+
+ kREGLongRead, // 32,037
+ kREGWordRead, // 74,299
+ kREGByteRead, // 91,930
+
+ kREGLongWrite, // 20,358
+ kREGWordWrite, // 57,879
+ kREGByteWrite, // 22,592
+*/
+
+long gMemoryAccess[kLastEnum];
+#endif
+
+
+#pragma mark -
+
+
+// ===========================================================================
+// ¥ Memory
+// ===========================================================================
+
+
+// ---------------------------------------------------------------------------
+// ¥ Memory::Initialize
+// ---------------------------------------------------------------------------
+// Initializes the RAM, ROM, and special memory areas of the emulator. Takes
+// a stream handle to the ROM.
+
+void Memory::Initialize ( EmStream& hROM,
+ RAMSizeType ramSize)
+{
+ // Clear everything out.
+
+ memset (gEmMemBanks, 0, sizeof (gEmMemBanks));
+
+ // Initialize the valid memory banks.
+
+ EmBankDummy::Initialize ();
+
+ // Initialize the Hardware registers memory bank. Do this
+ // *before* initializing the other RAM banks so that we
+ // know how memory is laid out from the chip selects.
+
+ EmBankRegs::Initialize ();
+
+ // Initialize EmBankDRAM after initializing the EmBankSRAM. The order is
+ // important for DragonballEZ. On Dragonball devices, DRAM is
+ // at 0x00000000, and SRAM is at 0x10000000. But on EZ devices,
+ // both start at 0x00000000. By calling EmBankDRAM::Initialize
+ // second, we allow it to overwrite the EmAddressBank handlers
+ // for the part of memory where they overlap.
+
+ EmBankSRAM::Initialize (ramSize);
+ EmBankDRAM::Initialize ();
+
+ EmBankROM::Initialize (hROM);
+
+ EmBankMapped::Initialize ();
+
+ EmAssert (gSession);
+ if (gSession->GetDevice ().HasFlash ())
+ EmBankFlash::Initialize ();
+
+ MetaMemory::Initialize ();
+
+// Memory::ResetBankHandlers (); // Can't do this yet. We can't set the
+ // bank handlers until we know how memory
+ // is laid out, and that information isn't
+ // determined until reset.
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: Memory::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 Memory::Reset (Bool hardwareReset)
+{
+ EmBankDummy::Reset (hardwareReset);
+ EmBankRegs::Reset (hardwareReset);
+ EmBankSRAM::Reset (hardwareReset);
+ EmBankDRAM::Reset (hardwareReset);
+ EmBankROM::Reset (hardwareReset);
+ EmBankMapped::Reset (hardwareReset);
+
+ EmAssert (gSession);
+ if (gSession->GetDevice ().HasFlash ())
+ EmBankFlash::Reset (hardwareReset);
+
+ Memory::ResetBankHandlers ();
+
+ MetaMemory::Reset ();
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: Memory::Save
+ *
+ * DESCRIPTION: Standard save function. Saves any sub-system state to
+ * the given session file.
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+void Memory::Save (SessionFile& f)
+{
+ EmBankDummy::Save (f);
+ EmBankRegs::Save (f);
+ EmBankSRAM::Save (f);
+ EmBankDRAM::Save (f);
+ EmBankROM::Save (f);
+ EmBankMapped::Save (f);
+
+ EmAssert (gSession);
+ if (gSession->GetDevice ().HasFlash ())
+ EmBankFlash::Save (f);
+
+ MetaMemory::Save (f);
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: Memory::Load
+ *
+ * DESCRIPTION: Standard load function. Loads any sub-system state
+ * from the given session file.
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+void Memory::Load (SessionFile& f)
+{
+ EmBankDummy::Load (f);
+ EmBankRegs::Load (f);
+ EmBankSRAM::Load (f);
+ EmBankDRAM::Load (f);
+ EmBankROM::Load (f);
+ EmBankMapped::Load (f);
+
+ EmAssert (gSession);
+ if (gSession->GetDevice ().HasFlash ())
+ EmBankFlash::Load (f);
+
+ MetaMemory::Load (f);
+
+ Memory::ResetBankHandlers ();
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: Memory::Dispose
+ *
+ * DESCRIPTION: Standard dispose function. Completely release any
+ * resources acquired or allocated in Initialize and/or
+ * Load.
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+void Memory::Dispose (void)
+{
+ EmBankDummy::Dispose ();
+ EmBankRegs::Dispose ();
+ EmBankSRAM::Dispose ();
+ EmBankDRAM::Dispose ();
+ EmBankROM::Dispose ();
+ EmBankMapped::Dispose ();
+
+ // We can't reliably call GetDevice here. That's because the
+ // session may not have been initialized (we could be disposing
+ // of everything because an error condition occurred), and so
+ // there may be no assigned device yet. So we can't ask that
+ // device if there's flash. However, EmBankFlash::Dispose
+ // doesn't do anything, so we don'thave to worry about it
+ // being called.
+
+// EmAssert (gSession);
+// if (gSession->GetDevice ().HasFlash ())
+// EmBankFlash::Dispose ();
+
+ MetaMemory::Dispose ();
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ Memory::InitializeBanks
+// ---------------------------------------------------------------------------
+// Initializes the specified memory banks with the given data.
+
+void Memory::InitializeBanks ( EmAddressBank& iBankInitializer,
+ int32 iStartingBankIndex,
+ int32 iNumberOfBanks)
+{
+ for (int32 aBankIndex = iStartingBankIndex;
+ aBankIndex < iStartingBankIndex + iNumberOfBanks;
+ aBankIndex++)
+ {
+ gEmMemBanks[aBankIndex] = &iBankInitializer;
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ Memory::ResetBankHandlers
+// ---------------------------------------------------------------------------
+// Reset the memory bank handlers in response to the chip selects changing.
+// Also called on Initialize, Reset, and Load, just to make sure everything's
+// nailed down.
+
+void Memory::ResetBankHandlers (void)
+{
+ EmBankDummy::SetBankHandlers ();
+ EmBankRegs::SetBankHandlers ();
+ EmBankSRAM::SetBankHandlers ();
+ EmBankDRAM::SetBankHandlers ();
+ EmBankROM::SetBankHandlers ();
+ EmBankMapped::SetBankHandlers ();
+
+ if (gSession->GetDevice ().HardwareID () == 0x0a /*halModelIDVisorPrism*/)
+ {
+ // Run this one again. Visor Prism has this thing where the USB
+ // controller is at 0x10800000, but it sets up the ROM chip select
+ // for 0x10000000 to 0x10FFFFFF. In order to not have EmBankROM
+ // take control over the USB chip, have EmBankRegs install *after*
+ // EmBankROM.
+ EmBankRegs::SetBankHandlers ();
+ }
+
+ EmAssert (gSession);
+ if (gSession->GetDevice ().HasFlash ())
+ EmBankFlash::SetBankHandlers ();
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ Memory::MapPhysicalMemory
+// ---------------------------------------------------------------------------
+// Maps a range of physical memory to appear at the same location of the
+// emulated Palm OS's virtual memory.
+
+void Memory::MapPhysicalMemory (const void* addr, uint32 size)
+{
+ EmBankMapped::MapPhysicalMemory (addr, size);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ Memory::UnmapPhysicalMemory
+// ---------------------------------------------------------------------------
+// Unmaps a range of physical memory from appearing at the same location of
+// the emulated Palm OS's virtual memory.
+
+void Memory::UnmapPhysicalMemory (const void* addr)
+{
+ EmBankMapped::UnmapPhysicalMemory (addr);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ Memory::GetMappingInfo
+// ---------------------------------------------------------------------------
+
+void Memory::GetMappingInfo (emuptr addr, void** start, uint32* len)
+{
+ EmBankMapped::GetMappingInfo (addr, start, len);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ Memory::CheckNewPC
+// ---------------------------------------------------------------------------
+
+void Memory::CheckNewPC (emuptr newPC)
+{
+ gPCInRAM = newPC < EmBankROM::GetMemoryStart ();
+ gPCInROM = newPC >= EmBankROM::GetMemoryStart ();
+}
+
+
+#pragma mark -
+
+// ===========================================================================
+// ¥ CEnableFullAccess
+// ===========================================================================
+
+long CEnableFullAccess::fgAccessCount = 0;
+
+
+// ---------------------------------------------------------------------------
+// ¥ CEnableFullAccess::CEnableFullAccess
+// ---------------------------------------------------------------------------
+
+CEnableFullAccess::CEnableFullAccess (void) :
+ fOldMemAccessFlags (gMemAccessFlags)
+#if HAS_PROFILING
+ , fOldProfilingCounted (gProfilingCounted)
+#endif
+{
+ gMemAccessFlags = kZeroMemAccessFlags;
+
+#if HAS_PROFILING
+ gProfilingCounted = false;
+#endif
+
+ ++fgAccessCount;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ CEnableFullAccess::~CEnableFullAccess
+// ---------------------------------------------------------------------------
+
+CEnableFullAccess::~CEnableFullAccess (void)
+{
+ gMemAccessFlags = fOldMemAccessFlags;
+
+#if HAS_PROFILING
+ gProfilingCounted = fOldProfilingCounted;
+#endif
+
+ --fgAccessCount;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ CEnableFullAccess::AccessOK
+// ---------------------------------------------------------------------------
+
+Bool CEnableFullAccess::AccessOK (void)
+{
+ return fgAccessCount > 0;
+}
+
+
+#pragma mark -
+
+// ---------------------------------------------------------------------------
+// These functions with two kinds of "pointers": emuptr's
+// and void*'s. Accessing memory for each kind of pointer is different.
+// Since the functions are template functions, we can't know at the time we
+// write the function what kind of pointer we'll be dealing with, so we can't
+// hard-code the memory-accessing method. Instead, we provide all of these
+// inline accessors and let the compiler sort it out.
+// ---------------------------------------------------------------------------
+
+inline char _get_byte(const void* p)
+{
+ return *(char*) p;
+}
+
+inline char _get_byte(emuptr p)
+{
+ return (char) EmMemGet8(p);
+}
+
+inline void _put_byte(void* p, const uint8 v)
+{
+ *(uint8*) p = v;
+}
+
+inline void _put_byte(emuptr p, const uint8 v)
+{
+ EmMemPut8(p, v);
+}
+
+inline uint16 _get_word(const void* p)
+{
+ return *(uint16*) p;
+}
+
+inline uint16 _get_word(emuptr p)
+{
+ return (uint16) EmMemGet16(p);
+}
+
+inline void _put_word(void* p, const uint16 v)
+{
+ *(uint16*) p = v;
+}
+
+inline void _put_word(emuptr p, const uint16 v)
+{
+ EmMemPut16(p, v);
+}
+
+inline uint32 _get_long(const void* p)
+{
+ return *(uint32*) p;
+}
+
+inline uint32 _get_long(emuptr p)
+{
+ return (uint32) EmMemGet32(p);
+}
+
+inline void _put_long(void* p, const uint32 v)
+{
+ *(uint32*) p = v;
+}
+
+inline void _put_long(emuptr p, const uint32 v)
+{
+ EmMemPut32(p, v);
+}
+
+inline const void* _get_real_address(const void* p)
+{
+ return p;
+}
+
+inline const void* _get_real_address(emuptr p)
+{
+ return EmMemGetRealAddress(p);
+}
+
+template <class T>
+inline void _add_delta (T*& v, long delta)
+{
+ v = (T*) (((char*) v) + delta);
+}
+
+inline void _add_delta (emuptr& v, long delta)
+{
+ v += delta;
+}
+
+template <class T>
+inline void _increment (T& v)
+{
+ _add_delta (v, 1);
+}
+
+template <class T>
+inline void _decrement (T& v)
+{
+ _add_delta (v, -1);
+}
+
+
+#pragma mark -
+
+/***********************************************************************
+ *
+ * FUNCTION: EmMem_memset
+ *
+ * DESCRIPTION: Same as Std C Library memset function.
+ *
+ * PARAMETERS: Same as Std C Library memset function.
+ *
+ * RETURNED: Same as Std C Library memset function.
+ *
+ ***********************************************************************/
+
+emuptr EmMem_memset(emuptr dst, int val, size_t len)
+{
+ emuptr q = dst;
+
+ uint32 longVal = val;
+ longVal |= (longVal << 8);
+ longVal |= (longVal << 16);
+
+ EmMemPutFunc longPutter = EmMemGetBank(dst).lput;
+ EmMemPutFunc bytePutter = EmMemGetBank(dst).bput;
+
+ while ((q & 3) && len > 0) // while there are leading bytes
+ {
+ bytePutter(q, val);
+ q += sizeof (char);
+ len -= sizeof (char);
+ }
+
+ while (len >= sizeof (long)) // while there are middle longs
+ {
+ longPutter(q, longVal);
+ q += sizeof (long);
+ len -= sizeof (long);
+ }
+
+ while (len > 0) // while there are trailing bytes
+ {
+ bytePutter(q, val);
+ q += sizeof (char);
+ len -= sizeof (char);
+ }
+
+ return dst;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmMem_memchr
+ *
+ * DESCRIPTION: Same as Std C Library memchr function.
+ *
+ * PARAMETERS: Same as Std C Library memchr function.
+ *
+ * RETURNED: Same as Std C Library memchr function.
+ *
+ ***********************************************************************/
+
+emuptr EmMem_memchr(emuptr src, int val, size_t len)
+{
+ emuptr p = src;
+
+ ++len;
+
+ while (--len)
+ {
+ if (_get_byte (p) == val)
+ return p;
+
+ _increment (p);
+ }
+
+ return EmMemNULL;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmMem_memcmp
+ *
+ * DESCRIPTION: Same as Std C Library memcmp function.
+ *
+ * PARAMETERS: Same as Std C Library memcmp function.
+ *
+ * RETURNED: Same as Std C Library memcmp function.
+ *
+ ***********************************************************************/
+
+template <class T1, class T2>
+int EmMem_memcmp(T1 src1, T2 src2, size_t len)
+{
+ T1 p1 = src1;
+ T2 p2 = src2;
+
+ ++len;
+
+ while (--len)
+ {
+ unsigned char ch1 = _get_byte (p1);
+ unsigned char ch2 = _get_byte (p2);
+
+ if (ch1 != ch2)
+ return (ch1 < ch2) ? -1 : 1;
+
+ _increment (p1);
+ _increment (p2);
+ }
+
+ return 0;
+}
+
+ // Instantiate EmMem_memcmp's that work with:
+ //
+ // dest source
+ // ------ ------
+ // void* emuptr
+ // const void* emuptr
+ // emuptr void*
+ // emuptr const void*
+ // emuptr emuptr
+
+template int EmMem_memcmp<void*, emuptr> (void* dst, emuptr src, size_t len);
+template int EmMem_memcmp<const void*, emuptr> (const void* dst, emuptr src, size_t len);
+template int EmMem_memcmp<emuptr, void*> (emuptr dst, void* src, size_t len);
+template int EmMem_memcmp<emuptr, const void*> (emuptr dst, const void* src, size_t len);
+template int EmMem_memcmp<emuptr, emuptr> (emuptr dst, emuptr src, size_t len);
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmMem_memcpy
+ *
+ * DESCRIPTION: Same as Std C Library memcpy function.
+ *
+ * PARAMETERS: Same as Std C Library memcpy function.
+ *
+ * RETURNED: Same as Std C Library memcpy function.
+ *
+ ***********************************************************************/
+
+template <class T1, class T2>
+T1 EmMem_memcpy (T1 dst, T2 src, size_t len)
+{
+ T1 q = dst;
+ T2 p = src;
+
+ while (len--)
+ {
+ _put_byte(q, _get_byte(p));
+ _increment (q);
+ _increment (p);
+ }
+
+ return dst;
+}
+
+ // Instantiate EmMem_memcpy's that work with:
+ //
+ // dest source
+ // ------ ------
+ // void* emuptr
+ // emuptr void*
+ // emuptr const void*
+ // emuptr emuptr
+
+template void* EmMem_memcpy<void*, emuptr> (void* dst, emuptr src, size_t len);
+template emuptr EmMem_memcpy<emuptr, void*> (emuptr dst, void* src, size_t len);
+template emuptr EmMem_memcpy<emuptr, const void*> (emuptr dst, const void* src, size_t len);
+template emuptr EmMem_memcpy<emuptr, emuptr> (emuptr dst, emuptr src, size_t len);
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmMem_memmove
+ *
+ * DESCRIPTION: Same as Std C Library memmove function.
+ *
+ * PARAMETERS: Same as Std C Library memmove function.
+ *
+ * RETURNED: Same as Std C Library memmove function.
+ *
+ ***********************************************************************/
+
+template <class T1, class T2>
+T1 EmMem_memmove (T1 dst, T2 src, size_t len)
+{
+ T1 q = dst;
+ T2 p = src;
+
+ Bool backward = _get_real_address(dst) <= _get_real_address(src);
+
+ if (backward)
+ {
+ while (len--)
+ {
+ _put_byte(q, _get_byte(p));
+ _increment (q);
+ _increment (p);
+ }
+ }
+ else
+ {
+ _add_delta (q, len);
+ _add_delta (p, len);
+
+ while (len--)
+ {
+ _decrement (q);
+ _decrement (p);
+ _put_byte(q, _get_byte(p));
+ }
+ }
+
+ return dst;
+}
+
+ // Instantiate EmMem_memmove's that work with:
+ //
+ // dest source
+ // ------ ------
+ // void* emuptr
+ // emuptr void*
+ // emuptr const void*
+ // emuptr emuptr
+
+template void* EmMem_memmove<void*, emuptr> (void* dst, emuptr src, size_t len);
+template emuptr EmMem_memmove<emuptr, void*> (emuptr dst, void* src, size_t len);
+template emuptr EmMem_memmove<emuptr, const void*> (emuptr dst, const void* src, size_t len);
+template emuptr EmMem_memmove<emuptr, emuptr> (emuptr dst, emuptr src, size_t len);
+
+
+#pragma mark -
+
+/***********************************************************************
+ *
+ * FUNCTION: EmMem_strlen
+ *
+ * DESCRIPTION: Same as Std C Library strlen function.
+ *
+ * PARAMETERS: Same as Std C Library strlen function.
+ *
+ * RETURNED: Same as Std C Library strlen function.
+ *
+ ***********************************************************************/
+
+size_t EmMem_strlen(emuptr str)
+{
+ emuptr eos = str;
+
+ while (_get_byte(eos))
+ _increment (eos);
+
+ return ((size_t) (eos - str));
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmMem_strcpy
+ *
+ * DESCRIPTION: Same as Std C Library strcpy function.
+ *
+ * PARAMETERS: Same as Std C Library strcpy function.
+ *
+ * RETURNED: Same as Std C Library strcpy function.
+ *
+ ***********************************************************************/
+
+template <class T1, class T2>
+T1 EmMem_strcpy(T1 dst, T2 src)
+{
+ T1 q = dst;
+ T2 p = src;
+ char ch;
+
+ do {
+ ch = _get_byte (p);
+ _increment (p);
+ _put_byte (q, ch);
+ _increment (q);
+ } while (ch);
+
+ return dst;
+}
+
+ // Instantiate EmMem_strcpy's that work with:
+ //
+ // dest source
+ // ------ ------
+ // char* emuptr
+ // emuptr char*
+ // emuptr const char*
+ // emuptr emuptr
+
+template char* EmMem_strcpy<char*, emuptr> (char* dst, emuptr src);
+template emuptr EmMem_strcpy<emuptr, char*> (emuptr dst, char* src);
+template emuptr EmMem_strcpy<emuptr, const char*> (emuptr dst, const char* src);
+template emuptr EmMem_strcpy<emuptr, emuptr> (emuptr dst, emuptr src);
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmMem_strncpy
+ *
+ * DESCRIPTION: Same as Std C Library strncpy function.
+ *
+ * PARAMETERS: Same as Std C Library strncpy function.
+ *
+ * RETURNED: Same as Std C Library strncpy function.
+ *
+ ***********************************************************************/
+
+template <class T1, class T2>
+T1 EmMem_strncpy(T1 dst, T2 src, size_t len)
+{
+ T1 q = dst;
+ T2 p = src;
+
+ ++len;
+
+ while (--len)
+ {
+ char ch = _get_byte(p);
+ _increment (p);
+
+ _put_byte (q, ch);
+ _increment (q);
+
+ if (!ch)
+ {
+ while (--len)
+ {
+ _put_byte (q, 0);
+ _increment (q);
+ }
+
+ break;
+ }
+ }
+
+ return dst;
+}
+
+ // Instantiate EmMem_strncpy's that work with:
+ //
+ // dest source
+ // ------ ------
+ // char* emuptr
+ // emuptr char*
+ // emuptr const char*
+ // emuptr emuptr
+
+template char* EmMem_strncpy<char*, emuptr> (char* dst, emuptr src, size_t len);
+template emuptr EmMem_strncpy<emuptr, char*> (emuptr dst, char* src, size_t len);
+template emuptr EmMem_strncpy<emuptr, const char*> (emuptr dst, const char* src, size_t len);
+template emuptr EmMem_strncpy<emuptr, emuptr> (emuptr dst, emuptr src, size_t len);
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmMem_strcat
+ *
+ * DESCRIPTION: Same as Std C Library strcat function.
+ *
+ * PARAMETERS: Same as Std C Library strcat function.
+ *
+ * RETURNED: Same as Std C Library strcat function.
+ *
+ ***********************************************************************/
+
+template <class T1, class T2>
+T1 EmMem_strcat(T1 dst, T2 src)
+{
+ T1 q = dst;
+ T2 p = src;
+
+ while (_get_byte (q))
+ _increment(q);
+
+ while (_get_byte (p))
+ {
+ _put_byte (q, _get_byte (p));
+ _increment(q);
+ _increment(p);
+ }
+
+ _put_byte (q, 0);
+
+ return dst;
+}
+
+ // Instantiate EmMem_strcat's that work with:
+ //
+ // dest source
+ // ------ ------
+ // char* emuptr
+ // emuptr char*
+ // emuptr const char*
+ // emuptr emuptr
+
+template char* EmMem_strcat<char*, emuptr> (char* dst, emuptr src);
+template emuptr EmMem_strcat<emuptr, char*> (emuptr dst, char* src);
+template emuptr EmMem_strcat<emuptr, const char*> (emuptr dst, const char* src);
+template emuptr EmMem_strcat<emuptr, emuptr> (emuptr dst, emuptr src);
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmMem_strncat
+ *
+ * DESCRIPTION: Same as Std C Library strncat function.
+ *
+ * PARAMETERS: Same as Std C Library strncat function.
+ *
+ * RETURNED: Same as Std C Library strncat function.
+ *
+ ***********************************************************************/
+
+template <class T1, class T2>
+T1 EmMem_strncat(T1 dst, T2 src, size_t len)
+{
+ T1 q = dst;
+ T2 p = src;
+
+ while (_get_byte (q))
+ _increment (q);
+
+ ++len;
+
+ while (--len)
+ {
+ char ch = _get_byte(p);
+ _increment (p);
+
+ _put_byte (q, ch);
+ _increment (q);
+
+ if (!ch)
+ return dst;
+ }
+
+ _put_byte (q, 0);
+
+ return dst;
+}
+
+ // Instantiate EmMem_strncat's that work with:
+ //
+ // dest source
+ // ------ ------
+ // char* emuptr
+ // emuptr char*
+ // emuptr const char*
+ // emuptr emuptr
+
+template char* EmMem_strncat<char*, emuptr> (char* dst, emuptr src, size_t len);
+template emuptr EmMem_strncat<emuptr, char*> (emuptr dst, char* src, size_t len);
+template emuptr EmMem_strncat<emuptr, const char*> (emuptr dst, const char* src, size_t len);
+template emuptr EmMem_strncat<emuptr, emuptr> (emuptr dst, emuptr src, size_t len);
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmMem_strcmp
+ *
+ * DESCRIPTION: Same as Std C Library strcmp function.
+ *
+ * PARAMETERS: Same as Std C Library strcmp function.
+ *
+ * RETURNED: Same as Std C Library strcmp function.
+ *
+ ***********************************************************************/
+
+template <class T1, class T2>
+int EmMem_strcmp(T1 dst, T2 src)
+{
+ T1 p1 = dst;
+ T2 p2 = src;
+
+ while (1)
+ {
+ unsigned char c1 = _get_byte (p1);
+ unsigned char c2 = _get_byte (p2);
+
+ if (c1 != c2)
+ return (c1 - c2);
+ else if (!c1)
+ break;
+
+ _increment (p1);
+ _increment (p2);
+ }
+
+ return 0;
+}
+
+ // Instantiate EmMem_strcmp's that work with:
+ //
+ // dest source
+ // ------ ------
+ // char* emuptr
+ // const char* emuptr
+ // emuptr char*
+ // emuptr const char*
+ // emuptr emuptr
+
+template int EmMem_strcmp<char*, emuptr> (char* dst, emuptr src);
+template int EmMem_strcmp<const char*, emuptr> (const char* dst, emuptr src);
+template int EmMem_strcmp<emuptr, char*> (emuptr dst, char* src);
+template int EmMem_strcmp<emuptr, const char*> (emuptr dst, const char* src);
+template int EmMem_strcmp<emuptr, emuptr> (emuptr dst, emuptr src);
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmMem_strncmp
+ *
+ * DESCRIPTION: Same as Std C Library strncmp function.
+ *
+ * PARAMETERS: Same as Std C Library strncmp function.
+ *
+ * RETURNED: Same as Std C Library strncmp function.
+ *
+ ***********************************************************************/
+
+template <class T1, class T2>
+int EmMem_strncmp(T1 dst, T2 src, size_t len)
+{
+ T1 p1 = dst;
+ T2 p2 = src;
+
+ ++len;
+
+ while (--len)
+ {
+ unsigned char c1 = _get_byte (p1);
+ unsigned char c2 = _get_byte (p2);
+
+ if (c1 != c2)
+ return (c1 - c2);
+ else if (!c1)
+ break;
+
+ _increment (p1);
+ _increment (p2);
+ }
+
+ return 0;
+}
+
+ // Instantiate EmMem_strncmp's that work with:
+ //
+ // dest source
+ // ------ ------
+ // char* emuptr
+ // const char* emuptr
+ // emuptr char*
+ // emuptr const char*
+ // emuptr emuptr
+
+template int EmMem_strncmp<char*, emuptr> (char* dst, emuptr src, size_t len);
+template int EmMem_strncmp<const char*, emuptr> (const char* dst, emuptr src, size_t len);
+template int EmMem_strncmp<emuptr, char*> (emuptr dst, char* src, size_t len);
+template int EmMem_strncmp<emuptr, const char*> (emuptr dst, const char* src, size_t len);
+template int EmMem_strncmp<emuptr, emuptr> (emuptr dst, emuptr src, size_t len);