/* -*- 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 "EmBankSRAM.h" #include "Byteswapping.h" // ByteswapWords #include "DebugMgr.h" // Debug::CheckStepSpy #include "EmCPU68K.h" // gCPU68K #include "EmMemory.h" // gRAMBank_Size, gRAM_Memory, gMemoryAccess #include "EmScreen.h" // EmScreen::MarkDirty #include "EmSession.h" // GetDevice #include "MetaMemory.h" // MetaMemory:: #include "Miscellaneous.h" // StWordSwapper #include "Profiling.h" // WAITSTATES_SRAM #include "SessionFile.h" // WriteRAMImage // =========================================================================== // ¥ SRAM Bank Accessors // =========================================================================== // These functions provide fetch and store access to the emulator's random // access memory. static EmAddressBank gAddressBank = { EmBankSRAM::GetLong, EmBankSRAM::GetWord, EmBankSRAM::GetByte, EmBankSRAM::SetLong, EmBankSRAM::SetWord, EmBankSRAM::SetByte, EmBankSRAM::GetRealAddress, EmBankSRAM::ValidAddress, EmBankSRAM::GetMetaAddress, EmBankSRAM::AddOpcodeCycles }; // Note: I'd've used hwrCardBase0 here, except that that // changes on different hardware. It's 0x10000000 in some // cases, 0x00000000 in others. static const emuptr kMemoryStart = 0x10000000; static const emuptr kMemoryStartEZ = 0x00000000; static const emuptr kMemoryStartVZ = 0x00000000; emuptr gMemoryStart; uint32 gRAMBank_Size; uint32 gRAMBank_Mask; uint8* gRAM_Memory; uint8* gRAM_MetaMemory; #if defined (_DEBUG) // In debug mode, define a global variable that points to the // Palm ROM's low-memory globals. That makes it easier to find // out what's going wrong when something goes wrong. // // Keep in mind that on Windows, all values are "wordswapped" // (each pair of bytes is byteswapped). LowMemHdrType* gLowMemory; #endif static inline uint8* InlineGetMetaAddress (emuptr address) { return (uint8*) &(gRAM_MetaMemory[address]); } static inline void PrvScreenCheck (uint8* metaAddress, emuptr address, size_t size) { #if defined (macintosh) if (size == 1 && !MetaMemory::IsScreenBuffer8 (metaAddress)) return; if (size == 2 && !MetaMemory::IsScreenBuffer16 (metaAddress)) return; if (size == 4 && !MetaMemory::IsScreenBuffer32 (metaAddress)) return; #else if (MetaMemory::IsScreenBuffer (metaAddress, size)) #endif { EmScreen::MarkDirty (address, size); } } /*********************************************************************** * * FUNCTION: EmBankSRAM::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 EmBankSRAM::Initialize (RAMSizeType ramSize) { EmAssert (gRAM_Memory == NULL); EmAssert (gRAM_MetaMemory == NULL); if (ramSize > 0) { gRAMBank_Size = ramSize * 1024; gRAMBank_Mask = gRAMBank_Size - 1; gRAM_Memory = (uint8*) Platform::AllocateMemoryClear (gRAMBank_Size); gRAM_MetaMemory = (uint8*) Platform::AllocateMemoryClear (gRAMBank_Size); #if defined (_DEBUG) // In debug mode, define a global variable that points to the // Palm ROM's low-memory globals. That makes it easier to find // out what's going wrong when something goes wrong. gLowMemory = (LowMemHdrType*) gRAM_Memory; #endif } EmAssert (gSession); if (gSession->GetDevice ().Supports68EZ328 ()) { gMemoryStart = kMemoryStartEZ; } else if (gSession->GetDevice ().Supports68VZ328 ()) { gMemoryStart = kMemoryStartVZ; } else { gMemoryStart = kMemoryStart; } } /*********************************************************************** * * FUNCTION: EmBankSRAM::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 EmBankSRAM::Reset (Bool /*hardwareReset*/) { memset (gRAM_MetaMemory, 0, gRAMBank_Size); } /*********************************************************************** * * FUNCTION: EmBankSRAM::Save * * DESCRIPTION: Standard save function. Saves any sub-system state to * the given session file. * * PARAMETERS: None. * * RETURNED: Nothing. * ***********************************************************************/ void EmBankSRAM::Save (SessionFile& f) { StWordSwapper swapper1 (gRAM_Memory, gRAMBank_Size); f.WriteRAMImage (gRAM_Memory, gRAMBank_Size); StWordSwapper swapper2 (gRAM_MetaMemory, gRAMBank_Size); f.WriteMetaRAMImage (gRAM_MetaMemory, gRAMBank_Size); } /*********************************************************************** * * FUNCTION: EmBankSRAM::Load * * DESCRIPTION: Standard load function. Loads any sub-system state * from the given session file. * * PARAMETERS: None. * * RETURNED: Nothing. * ***********************************************************************/ void EmBankSRAM::Load (SessionFile& f) { EmAssert (gRAM_Memory); EmAssert (gRAM_MetaMemory); if (f.ReadRAMImage (gRAM_Memory)) { ByteswapWords (gRAM_Memory, gRAMBank_Size); } else { f.SetCanReload (false); } if (f.ReadMetaRAMImage (gRAM_MetaMemory)) { ByteswapWords (gRAM_MetaMemory, gRAMBank_Size); } else { f.SetCanReload (false); } } /*********************************************************************** * * FUNCTION: EmBankSRAM::Dispose * * DESCRIPTION: Standard dispose function. Completely release any * resources acquired or allocated in Initialize and/or * Load. * * PARAMETERS: None. * * RETURNED: Nothing. * ***********************************************************************/ void EmBankSRAM::Dispose (void) { Platform::DisposeMemory (gRAM_Memory); Platform::DisposeMemory (gRAM_MetaMemory); } /*********************************************************************** * * FUNCTION: EmBankSRAM::SetBankHandlers * * DESCRIPTION: Set the bank handlers UAE uses to dispatch memory * access operations. * * PARAMETERS: None * * RETURNED: Nothing * ***********************************************************************/ void EmBankSRAM::SetBankHandlers (void) { // Memory banks 0x1000 to are managed by the functions // in EmBankSRAM. is based on the amount of RAM being emulated. long numBanks = EmMemBankIndex (gMemoryStart + gRAMBank_Size - 1) - EmMemBankIndex (gMemoryStart) + 1; Memory::InitializeBanks (gAddressBank, EmMemBankIndex (gMemoryStart), numBanks); } // --------------------------------------------------------------------------- // ¥ EmBankSRAM::GetLong // --------------------------------------------------------------------------- uint32 EmBankSRAM::GetLong (emuptr address) { #if PROFILE_MEMORY gMemoryAccess[kSRAMLongRead]++; if (address & 2) gMemoryAccess[kSRAMLongRead2]++; #endif #if (CHECK_FOR_ADDRESS_ERROR) if ((address & 1) != 0) { AddressError (address, sizeof (uint32), true); } #endif #if (PREVENT_USER_SRAM_GET) if (gMemAccessFlags.fProtect_SRAMGet) { ProtectedAccess (address, sizeof (uint32), true); } #endif #if (VALIDATE_SRAM_GET) if (gMemAccessFlags.fValidate_SRAMGet && !ValidAddress (address, sizeof (uint32))) { InvalidAccess (address, sizeof (uint32), true); } #endif #if HAS_PROFILING CYCLE_GETLONG (WAITSTATES_SRAM); #endif address &= gRAMBank_Mask; return EmMemDoGet32 (gRAM_Memory + address); } // --------------------------------------------------------------------------- // ¥ EmBankSRAM::GetWord // --------------------------------------------------------------------------- uint32 EmBankSRAM::GetWord (emuptr address) { #if PROFILE_MEMORY gMemoryAccess[kSRAMWordRead]++; #endif #if (CHECK_FOR_ADDRESS_ERROR) if ((address & 1) != 0) { AddressError (address, sizeof (uint16), true); } #endif #if (PREVENT_USER_SRAM_GET) if (gMemAccessFlags.fProtect_SRAMGet) { ProtectedAccess (address, sizeof (uint16), true); } #endif #if (VALIDATE_SRAM_GET) if (gMemAccessFlags.fValidate_SRAMGet && !ValidAddress (address, sizeof (uint16))) { InvalidAccess (address, sizeof (uint16), true); } #endif #if HAS_PROFILING CYCLE_GETWORD (WAITSTATES_SRAM); #endif address &= gRAMBank_Mask; return EmMemDoGet16 (gRAM_Memory + address); } // --------------------------------------------------------------------------- // ¥ EmBankSRAM::GetByte // --------------------------------------------------------------------------- uint32 EmBankSRAM::GetByte (emuptr address) { #if PROFILE_MEMORY gMemoryAccess[kSRAMByteRead]++; #endif #if (PREVENT_USER_SRAM_GET) if (gMemAccessFlags.fProtect_SRAMGet) { ProtectedAccess (address, sizeof (uint8), true); } #endif #if (VALIDATE_SRAM_GET) if (gMemAccessFlags.fValidate_SRAMGet && !ValidAddress (address, sizeof (uint8))) { InvalidAccess (address, sizeof (uint8), true); } #endif #if HAS_PROFILING CYCLE_GETBYTE (WAITSTATES_SRAM); #endif address &= gRAMBank_Mask; return EmMemDoGet8 (gRAM_Memory + address); } // --------------------------------------------------------------------------- // ¥ EmBankSRAM::SetLong // --------------------------------------------------------------------------- void EmBankSRAM::SetLong (emuptr address, uint32 value) { #if PROFILE_MEMORY gMemoryAccess[kSRAMLongWrite]++; if (address & 2) gMemoryAccess[kSRAMLongWrite2]++; #endif #if (CHECK_FOR_ADDRESS_ERROR) if ((address & 1) != 0) { AddressError (address, sizeof (uint32), false); } #endif #if (PREVENT_USER_SRAM_SET) if (gMemAccessFlags.fProtect_SRAMSet) { ProtectedAccess (address, sizeof (uint32), false); } #endif #if (VALIDATE_SRAM_SET) if (gMemAccessFlags.fValidate_SRAMSet && !ValidAddress (address, sizeof (uint32))) { InvalidAccess (address, sizeof (uint32), false); } #endif #if HAS_PROFILING CYCLE_PUTLONG (WAITSTATES_SRAM); #endif emuptr phyAddress = address; phyAddress &= gRAMBank_Mask; register uint8* metaAddress = InlineGetMetaAddress (phyAddress); // META_CHECK (metaAddress, address, SetLong, uint32, false); ::PrvScreenCheck (metaAddress, address, sizeof (uint32)); EmMemDoPut32 (gRAM_Memory + phyAddress, value); // See if any interesting memory locations have changed. If so, // CheckStepSpy will report it. Debug::CheckStepSpy (address, sizeof (uint32)); } // --------------------------------------------------------------------------- // ¥ EmBankSRAM::SetWord // --------------------------------------------------------------------------- void EmBankSRAM::SetWord (emuptr address, uint32 value) { #if PROFILE_MEMORY gMemoryAccess[kSRAMWordWrite]++; #endif #if (CHECK_FOR_ADDRESS_ERROR) if ((address & 1) != 0) { AddressError (address, sizeof (uint16), false); } #endif #if (PREVENT_USER_SRAM_SET) if (gMemAccessFlags.fProtect_SRAMSet) { ProtectedAccess (address, sizeof (uint16), false); } #endif #if (VALIDATE_SRAM_SET) if (gMemAccessFlags.fValidate_SRAMSet && !ValidAddress (address, sizeof (uint16))) { InvalidAccess (address, sizeof (uint16), false); } #endif #if HAS_PROFILING CYCLE_PUTWORD (WAITSTATES_SRAM); #endif emuptr phyAddress = address; phyAddress &= gRAMBank_Mask; register uint8* metaAddress = InlineGetMetaAddress (phyAddress); // META_CHECK (metaAddress, address, SetLong, uint16, false); ::PrvScreenCheck (metaAddress, address, sizeof (uint16)); EmMemDoPut16 (gRAM_Memory + phyAddress, value); // See if any interesting memory locations have changed. If so, // CheckStepSpy will report it. Debug::CheckStepSpy (address, sizeof (uint16)); } // --------------------------------------------------------------------------- // ¥ EmBankSRAM::SetByte // --------------------------------------------------------------------------- void EmBankSRAM::SetByte (emuptr address, uint32 value) { #if PROFILE_MEMORY gMemoryAccess[kSRAMByteWrite]++; #endif #if (PREVENT_USER_SRAM_SET) if (gMemAccessFlags.fProtect_SRAMSet) { ProtectedAccess (address, sizeof (uint8), false); } #endif #if (VALIDATE_SRAM_SET) if (gMemAccessFlags.fValidate_SRAMSet && !ValidAddress (address, sizeof (uint8))) { InvalidAccess (address, sizeof (uint8), false); } #endif #if HAS_PROFILING CYCLE_PUTBYTE (WAITSTATES_SRAM); #endif emuptr phyAddress = address; phyAddress &= gRAMBank_Mask; register uint8* metaAddress = InlineGetMetaAddress (phyAddress); // META_CHECK (metaAddress, address, SetLong, uint8, false); ::PrvScreenCheck (metaAddress, address, sizeof (uint8)); EmMemDoPut8 (gRAM_Memory + phyAddress, value); // See if any interesting memory locations have changed. If so, // CheckStepSpy will report it. Debug::CheckStepSpy (address, sizeof (uint8)); } // --------------------------------------------------------------------------- // ¥ EmBankSRAM::ValidAddress // --------------------------------------------------------------------------- int EmBankSRAM::ValidAddress (emuptr address, uint32 size) { address -= gMemoryStart; int result = (address + size) <= (gMemoryStart + gRAMBank_Size); return result; } // --------------------------------------------------------------------------- // ¥ EmBankSRAM::GetRealAddress // --------------------------------------------------------------------------- uint8* EmBankSRAM::GetRealAddress (emuptr address) { // Strip the uppermost bit of the address. address &= gRAMBank_Mask; return (uint8*) &(gRAM_Memory[address]); } // --------------------------------------------------------------------------- // ¥ EmBankSRAM::GetMetaAddress // --------------------------------------------------------------------------- uint8* EmBankSRAM::GetMetaAddress (emuptr address) { // Strip the uppermost bit of the address. address &= gRAMBank_Mask; return (uint8*) &(gRAM_MetaMemory[address]); } // --------------------------------------------------------------------------- // ¥ EmBankSRAM::AddOpcodeCycles // --------------------------------------------------------------------------- void EmBankSRAM::AddOpcodeCycles (void) { #if (HAS_PROFILING) CYCLE_GETWORD (WAITSTATES_SRAM); #endif } // --------------------------------------------------------------------------- // ¥ EmBankSRAM::AddressError // --------------------------------------------------------------------------- void EmBankSRAM::AddressError (emuptr address, long size, Bool forRead) { EmAssert (gCPU68K); gCPU68K->AddressError (address, size, forRead); } // --------------------------------------------------------------------------- // ¥ EmBankSRAM::InvalidAccess // --------------------------------------------------------------------------- void EmBankSRAM::InvalidAccess (emuptr address, long size, Bool forRead) { EmAssert (gCPU68K); gCPU68K->BusError (address, size, forRead); } // --------------------------------------------------------------------------- // ¥ EmBankSRAM::ProtectedAccess // --------------------------------------------------------------------------- void EmBankSRAM::ProtectedAccess (emuptr address, long size, Bool forRead) { EmAssert (gCPU68K); gCPU68K->BusError (address, size, forRead); }