/* -*- 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 "EmBankROM.h" #include "Byteswapping.h" // ByteswapWords #include "EmCPU68K.h" // gCPU68K #include "EmErrCodes.h" // kError_UnsupportedROM #include "EmHAL.h" // EmHAL #include "EmMemory.h" // Memory::InitializeBanks, EmMem_memset #include "EmPalmStructs.h" // EmProxyCardHeaderType #include "EmSession.h" // GetDevice, ScheduleDeferredError #include "ErrorHandling.h" // Errors::Throw #include "Miscellaneous.h" // StWordSwapper, NextPowerOf2 #include "Profiling.h" // WAITSTATES_ROM #include "SessionFile.h" // WriteROMFileReference #include "Strings.r.h" // kStr_BadChecksum // Private function declarations class Card { public: static Bool CheckCardHeader (const EmProxyCardHeaderType& cardHdr); static Bool CheckChecksum (const void* p, uint32 romLength); static Bool Supports328 (const EmAliasCardHeaderType& cardHdr); static Bool SupportsEZ (const EmAliasCardHeaderType& cardHdr); static Bool SupportsVZ (const EmAliasCardHeaderType& cardHdr); static Bool SupportsSZ (const EmAliasCardHeaderType& cardHdr); }; // static member initialization emuptr EmBankROM::gROMMemoryStart = kDefaultROMMemoryStart; // =========================================================================== // ¥ ROM Bank Accessors // =========================================================================== // These functions provide fetch and store access to the emulator's read only // memory. static EmAddressBank gROMAddressBank = { EmBankROM::GetLong, EmBankROM::GetWord, EmBankROM::GetByte, EmBankROM::SetLong, EmBankROM::SetWord, EmBankROM::SetByte, EmBankROM::GetRealAddress, EmBankROM::ValidAddress, EmBankROM::GetMetaAddress, EmBankROM::AddOpcodeCycles }; static uint32 gROMBank_Size; static uint32 gManagedROMSize; static uint32 gROMImage_Size; static uint32 gROMBank_Mask; static uint8* gROM_Memory; static uint8* gROM_MetaMemory; /*********************************************************************** * * FUNCTION: EmBankROM::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 EmBankROM::Initialize (EmStream& romStream) { // When creating a new session, Initialize is called followed by // Reset. When loading a session, Initialize is called by Load. // So it makes sense to load the ROM image here. However, when // reloading a session as part of a Gremlin Horde, only Load is // called, so it is critical that all parts of a horde use the // same ROM image. EmBankROM::LoadROM (romStream); } /*********************************************************************** * * FUNCTION: EmBankROM::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 EmBankROM::Reset (Bool /*hardwareReset*/) { memset (gROM_MetaMemory, 0, gROMImage_Size); } /*********************************************************************** * * FUNCTION: EmBankROM::Save * * DESCRIPTION: Standard save function. Saves any sub-system state to * the given session file. * * PARAMETERS: None. * * RETURNED: Nothing. * ***********************************************************************/ void EmBankROM::Save (SessionFile& f) { EmAssert (gSession); Configuration cfg = gSession->GetConfiguration (); f.WriteROMFileReference (cfg.fROMFile); StWordSwapper swapper1 (gROM_MetaMemory, gROMImage_Size); f.WriteMetaROMImage (gROM_MetaMemory, gROMImage_Size); } /*********************************************************************** * * FUNCTION: EmBankROM::Load * * DESCRIPTION: Standard load function. Loads any sub-system state * from the given session file. * * PARAMETERS: None. * * RETURNED: Nothing. * ***********************************************************************/ void EmBankROM::Load (SessionFile& f) { // ROM reference was read when the basic configuration was read // and passed to EmSession::Initialize, which then called // EmBankROM::Initialize, which then called EmBankROM::LoadROM. EmAssert (gROM_MetaMemory != NULL); if (f.ReadMetaROMImage (gROM_MetaMemory)) { ByteswapWords (gROM_MetaMemory, gROMImage_Size); } else { f.SetCanReload (false); } } /*********************************************************************** * * FUNCTION: EmBankROM::Dispose * * DESCRIPTION: Standard dispose function. Completely release any * resources acquired or allocated in Initialize and/or * Load. * * PARAMETERS: None. * * RETURNED: Nothing. * ***********************************************************************/ void EmBankROM::Dispose (void) { Platform::DisposeMemory (gROM_Memory); Platform::DisposeMemory (gROM_MetaMemory); } /*********************************************************************** * * FUNCTION: EmBankROM::SetBankHandlers * * DESCRIPTION: Set the bank handlers UAE uses to dispatch memory * access operations. * * PARAMETERS: None * * RETURNED: Nothing * ***********************************************************************/ void EmBankROM::SetBankHandlers (void) { if (EmHAL::ChipSelectsConfigured ()) { gROMMemoryStart = EmHAL::GetROMBaseAddress (); } gManagedROMSize = EmHAL::GetROMSize (); // Memory banks gROMMemoryStart to are managed by the functions // in EmBankROM. is based on the amount of ROM being emulated. uint32 first_bank = EmMemBankIndex (EmBankROM::GetMemoryStart ()); uint32 last_bank = EmMemBankIndex (EmBankROM::GetMemoryStart () + gManagedROMSize - 1); Memory::InitializeBanks ( gROMAddressBank, first_bank, last_bank - first_bank + 1); } // --------------------------------------------------------------------------- // ¥ EmBankROM::GetLong // --------------------------------------------------------------------------- uint32 EmBankROM::GetLong (emuptr address) { #if PROFILE_MEMORY gMemoryAccess[kROMLongRead]++; if (address & 2) gMemoryAccess[kROMLongRead2]++; #endif #if (CHECK_FOR_ADDRESS_ERROR) if ((address & 1) != 0) { AddressError (address, sizeof (uint32), true); } #endif #if (VALIDATE_ROM_GET) if (gMemAccessFlags.fValidate_ROMGet && !ValidAddress (address, sizeof (uint32))) { InvalidAccess (address, sizeof (uint32), true); } #endif #if (PREVENT_USER_ROM_GET || PREVENT_SYSTEM_ROM_GET) if (EmMemory::IsPCInRAM ()) { if (PREVENT_USER_ROM_GET && gMemAccessFlags.fProtect_ROMGet) { InvalidAccess (address, sizeof (uint32), true); } } else { if (PREVENT_SYSTEM_ROM_GET && gMemAccessFlags.fProtect_SysROMGet) { InvalidAccess (address, sizeof (uint32), true); } } #endif #if HAS_PROFILING CYCLE_GETLONG (WAITSTATES_ROM); #endif address &= gROMBank_Mask; return EmMemDoGet32 (gROM_Memory + address); } // --------------------------------------------------------------------------- // ¥ EmBankROM::GetWord // --------------------------------------------------------------------------- uint32 EmBankROM::GetWord (emuptr address) { #if PROFILE_MEMORY gMemoryAccess[kROMWordRead]++; #endif #if (CHECK_FOR_ADDRESS_ERROR) if ((address & 1) != 0) { AddressError (address, sizeof (uint16), true); } #endif #if (VALIDATE_ROM_GET) if (gMemAccessFlags.fValidate_ROMGet && !ValidAddress (address, sizeof (uint16))) { InvalidAccess (address, sizeof (uint16), true); } #endif #if (PREVENT_USER_ROM_GET || PREVENT_SYSTEM_ROM_GET) if (EmMemory::IsPCInRAM ()) { if (PREVENT_USER_ROM_GET && gMemAccessFlags.fProtect_ROMGet) { InvalidAccess (address, sizeof (uint16), true); } } else { if (PREVENT_SYSTEM_ROM_GET && gMemAccessFlags.fProtect_SysROMGet) { InvalidAccess (address, sizeof (uint16), true); } } #endif #if HAS_PROFILING CYCLE_GETWORD (WAITSTATES_ROM); #endif address &= gROMBank_Mask; return EmMemDoGet16 (gROM_Memory + address); } // --------------------------------------------------------------------------- // ¥ EmBankROM::GetByte // --------------------------------------------------------------------------- uint32 EmBankROM::GetByte (emuptr address) { #if PROFILE_MEMORY gMemoryAccess[kROMByteRead]++; #endif #if (VALIDATE_ROM_GET) if (gMemAccessFlags.fValidate_ROMGet && !ValidAddress (address, sizeof (uint8))) { InvalidAccess (address, sizeof (uint8), true); } #endif #if (PREVENT_USER_ROM_GET || PREVENT_SYSTEM_ROM_GET) if (EmMemory::IsPCInRAM ()) { if (PREVENT_USER_ROM_GET && gMemAccessFlags.fProtect_ROMGet) { InvalidAccess (address, sizeof (uint8), true); } } else { if (PREVENT_SYSTEM_ROM_GET && gMemAccessFlags.fProtect_SysROMGet) { InvalidAccess (address, sizeof (uint8), true); } } #endif #if HAS_PROFILING CYCLE_GETBYTE (WAITSTATES_ROM); #endif address &= gROMBank_Mask; return EmMemDoGet8 (gROM_Memory + address); } // --------------------------------------------------------------------------- // ¥ EmBankROM::SetLong // --------------------------------------------------------------------------- void EmBankROM::SetLong (emuptr address, uint32 value) { #if PROFILE_MEMORY gMemoryAccess[kROMLongWrite]++; if (address & 2) gMemoryAccess[kROMLongWrite2]++; #endif #if (CHECK_FOR_ADDRESS_ERROR) if ((address & 1) != 0) { AddressError (address, sizeof (uint32), false); } #endif EmAssert (ValidAddress (address, sizeof (uint32))); #if (PREVENT_USER_ROM_SET || PREVENT_SYSTEM_ROM_SET) if (EmMemory::IsPCInRAM ()) { if (PREVENT_USER_ROM_SET && gMemAccessFlags.fProtect_ROMSet) { InvalidAccess (address, sizeof (uint32), false); return; } } else { if (PREVENT_SYSTEM_ROM_SET && gMemAccessFlags.fProtect_SysROMSet) { InvalidAccess (address, sizeof (uint32), false); return; } } #endif #if HAS_PROFILING // writing to ROM?? don't know what to make of this CYCLE_PUTLONG (WAITSTATES_ROM); #endif address &= gROMBank_Mask; EmMemDoPut32 (gROM_Memory + address, value); } // --------------------------------------------------------------------------- // ¥ EmBankROM::SetWord // --------------------------------------------------------------------------- void EmBankROM::SetWord (emuptr address, uint32 value) { #if PROFILE_MEMORY gMemoryAccess[kROMWordWrite]++; #endif #if (CHECK_FOR_ADDRESS_ERROR) if ((address & 1) != 0) { AddressError (address, sizeof (uint16), false); } #endif EmAssert (ValidAddress (address, sizeof (uint16))); #if (PREVENT_USER_ROM_SET || PREVENT_SYSTEM_ROM_SET) if (EmMemory::IsPCInRAM ()) { if (PREVENT_USER_ROM_SET && gMemAccessFlags.fProtect_ROMSet) { InvalidAccess (address, sizeof (uint16), false); return; } } else { if (PREVENT_SYSTEM_ROM_SET && gMemAccessFlags.fProtect_SysROMSet) { InvalidAccess (address, sizeof (uint16), false); return; } } #endif #if HAS_PROFILING // writing to ROM?? don't know what to make of this CYCLE_PUTWORD (WAITSTATES_ROM); #endif address &= gROMBank_Mask; EmMemDoPut16 (gROM_Memory + address, value); } // --------------------------------------------------------------------------- // ¥ EmBankROM::SetByte // --------------------------------------------------------------------------- void EmBankROM::SetByte (emuptr address, uint32 value) { #if PROFILE_MEMORY gMemoryAccess[kROMByteWrite]++; #endif EmAssert (ValidAddress (address, sizeof (uint8))); #if (PREVENT_USER_ROM_SET || PREVENT_SYSTEM_ROM_SET) if (EmMemory::IsPCInRAM ()) { if (PREVENT_USER_ROM_SET && gMemAccessFlags.fProtect_ROMSet) { InvalidAccess (address, sizeof (uint8), false); return; } } else { if (PREVENT_SYSTEM_ROM_SET && gMemAccessFlags.fProtect_SysROMSet) { InvalidAccess (address, sizeof (uint8), false); return; } } #endif #if HAS_PROFILING // writing to ROM?? don't know what to make of this CYCLE_PUTBYTE (WAITSTATES_ROM); #endif address &= gROMBank_Mask; EmMemDoPut8 (gROM_Memory + address, value); } // --------------------------------------------------------------------------- // ¥ EmBankROM::ValidAddress // --------------------------------------------------------------------------- int EmBankROM::ValidAddress (emuptr address, uint32 size) { address -= gROMMemoryStart; int result = (address + size) <= (gROMMemoryStart + gROMBank_Size); return result; } // --------------------------------------------------------------------------- // ¥ EmBankROM::GetRealAddress // --------------------------------------------------------------------------- uint8* EmBankROM::GetRealAddress (emuptr address) { // Strip the uppermost bit of the address. address &= gROMBank_Mask; return (uint8*) &gROM_Memory[address]; } // --------------------------------------------------------------------------- // ¥ EmBankROM::GetMetaAddress // --------------------------------------------------------------------------- uint8* EmBankROM::GetMetaAddress (emuptr address) { // Strip the uppermost bit of the address. address &= gROMBank_Mask; return (uint8*) &(gROM_MetaMemory[address]); } // --------------------------------------------------------------------------- // ¥ EmBankROM::AddOpcodeCycles // --------------------------------------------------------------------------- void EmBankROM::AddOpcodeCycles (void) { #if (HAS_PROFILING) CYCLE_GETWORD (WAITSTATES_ROM); #endif } // --------------------------------------------------------------------------- // ¥ EmBankROM::AddressError // --------------------------------------------------------------------------- void EmBankROM::AddressError (emuptr address, long size, Bool forRead) { EmAssert (gCPU68K); gCPU68K->AddressError (address, size, forRead); } // --------------------------------------------------------------------------- // ¥ EmBankROM::InvalidAccess // --------------------------------------------------------------------------- void EmBankROM::InvalidAccess (emuptr address, long size, Bool forRead) { EmAssert (gSession); gSession->ScheduleDeferredError (new EmDeferredErrROM (address, size, forRead)); } /*********************************************************************** * * FUNCTION: EmBankROM::LoadROM * * DESCRIPTION: Does the work of actually loading (and validating a ROM * from a stream. * * PARAMETERS: EmStream hROM * * RETURNED: Nothing * ***********************************************************************/ void EmBankROM::LoadROM (EmStream& hROM) { // Make sure the file is big enough to have a card header. if (hROM.GetLength() < (long) EmProxyCardHeaderType::GetSize ()) Errors::Throw (kError_BadROM); // Read the card header. EmProxyCardHeaderType cardHeader; hROM.GetBytes (cardHeader.GetPtr (), cardHeader.GetSize ()); // Validate the card header. if (!Card::CheckCardHeader (cardHeader)) Errors::Throw (kError_BadROM); // The ROM is made up of two parts: a Big ROM and a Small ROM. ROM image // files can contain both parts or just the Big ROM part. If it contains // both parts, then everything is just fine, and we can use it no problem. // If the Small ROM part is missing, we have to detect that, dummy up a // small ROM for correct operation, and relocate the Big ROM higher in memory. // Both the Big ROM and Small ROM start off with a CardHeaderType struct. // We just read in one of them. Let's look at where the Big ROM _should_ // be to see if there's another CardHeader there. If so, we have both parts. // If not, we have just the Big ROM. int32 bigROMOffset; if (cardHeader.hdrVersion == 1) { bigROMOffset = 0x3000; } else { // Version 2 CardHeaders contain a "bigROMOffset" field that contains the // offset from the beginning of the card to where the ROM should // appear in memory. Normally the card appears at 0x10000000 (which // would make bigROMOffset something like 0x00C08000), but that // isn't always the case. The problem is, we don't always know // where the card beginning will be. For now, we'll assume that // the ROM wants to live at 0x10C00000, and that the Big ROM will // want to begin somewhere soon after that. This means we can get by // with using only the lower bits of bigROMOffset. bigROMOffset = cardHeader.bigROMOffset; bigROMOffset &= 0x000FFFFF; // Allows for 1 Meg offset. } // Make sure the file is big enough to have a Big ROM. if (hROM.GetLength() < bigROMOffset) Errors::Throw (kError_BadROM); // We'll use bigROMOffset for later to get to some Big ROM data. // But we'll also need a value when we read the ROM image into // memory. This value will be used as an offset into a buffer // where we'll be reading the ROM image. If we have a Small ROM, // the offset will be zero. If we have only a Big ROM, the offset // will be the same value as bigROMOffset. uint32 bufferOffset = bigROMOffset; // See if there's a card header there, too. EmProxyCardHeaderType cardHeader2; hROM.SetMarker (bigROMOffset, kStreamFromStart); hROM.GetBytes (cardHeader2.GetPtr (), cardHeader2.GetSize ()); if (Card::CheckCardHeader (cardHeader2)) { // Looks like, so we don't have to relocate the ROM image. bufferOffset = 0; } // The ROM size is now the size of the file plus any offset for // any small ROM that we have to add and dummy up. gROMImage_Size = hROM.GetLength() + bufferOffset; gROMBank_Size = ::NextPowerOf2 (gROMImage_Size); // Read in the ROM image. StMemory romImage (gROMImage_Size); StMemory romMetaImage (gROMImage_Size); hROM.SetMarker (0, kStreamFromStart); hROM.GetBytes (romImage.Get () + bufferOffset, hROM.GetLength()); // See if the big ROM checksum looks OK. Card::CheckChecksum (romImage.Get () + bigROMOffset, gROMImage_Size - bigROMOffset); // If we only had a Big ROM, dummy up the Small ROM. All we really // need to do here is copy the Big ROM's card header to the Small // ROM area. // // Also, clear out this area to 0xFF to look like new Flash RAM. if (bufferOffset) { memset (romImage, 0xFF, bigROMOffset); memcpy (romImage.Get (), romImage.Get () + bigROMOffset, EmProxyCardHeaderType::GetSize ()); } else { // See if the small ROM checksum looks OK. // Note that checksumBytes is invalid for v1 card headers, // but in those cases, it's not really used anyway. EmAliasCardHeaderType cardHdr (romImage.Get ()); uint32 smallROMSize = cardHdr.checksumBytes; Card::CheckChecksum (romImage.Get (), smallROMSize); } // Check that the ROM we just loaded can be run on this device. EmAliasCardHeaderType cardHdr (romImage.Get ()); EmAssert (gSession); if (Card::SupportsEZ (cardHdr)) { if (!gSession->GetDevice ().Supports68EZ328 ()) { /* Make a hack for the Prism, Platinum and Edge below since Handspring seems to report the EZ bit in their ROMs. */ if (!gSession->GetDevice ().PrismPlatinumEdgeHack ()) { Errors::Throw (kError_WrongROMForType); } } } else if (Card::SupportsVZ (cardHdr)) { if (!gSession->GetDevice ().Supports68VZ328 ()) { Errors::Throw (kError_WrongROMForType); } } else if (Card::SupportsSZ (cardHdr)) { if (!gSession->GetDevice ().Supports68SZ328 ()) { Errors::Throw (kError_WrongROMForType); } } else { if (!gSession->GetDevice ().Supports68328 ()) { Errors::Throw (kError_WrongROMForType); } } // Byteswap all the words in the ROM (if necessary). Most accesses // are 16-bit accesses, so we optimize for that case. ByteswapWords (romImage.Get (), gROMImage_Size); // Everything seems to be OK. Save the ROM data in some global // variables for the CPU emulator to access. Make sure that // gROMBank_Size is a power-of-2. The EmBankROM memory routines // require this. EmAssert (gROM_Memory == NULL); EmAssert (gROM_MetaMemory == NULL); gROM_Memory = (uint8*) romImage.Release (); gROM_MetaMemory = (uint8*) romMetaImage.Release (); gROMBank_Mask = gROMBank_Size - 1; // Guess the default ROM base address. // This will be used until the chip selects are set up. // // (I tried just setting this to zero, but the Palm OS 3.0 // startup code probes at 0x10c00008 and 0x10d00008 to see if // it can find signatures there. If we map the ROM to zero, // then we'll get bus errors when those accesses are made.) gROMMemoryStart = cardHeader.resetVector & 0xFFF00000; } #pragma mark - // =========================================================================== // ¥ Flash Bank Accessors // =========================================================================== // These functions provide fetch and store access to the emulator's read only // memory. /* There are five types of flash that our flash manager routines identify: Mitsubishi, Hitachi, Intel, AMD, and Fujitsu. Of these, only the last two are really supported right now. Unfortunately, they are the hardest to emulate. :-( To identify the kind of flash being used, our ROM routines: - Read a word from FLASHBASE - Write 0x00FF to FLASHBASE - Read a word from FLASHBASE - Write 0x0090 to FLASHBASE - Read a manufacturer ID (word) from FLASHBASE - Read a device ID (word) from FLASHBASE + 2 - Write 0x00FF to FLASHBASE Mitsubishi: manufacturer == 0x001C, device == 0x005E Hitachi: manufacturer == 0x0007, device == 0x0086 Intel: manufacturer == 0x0089, device == 0x0091 If the flash is not one of those, our ROM routines: - Read a word from FLASHBASE - Write 0x00F0 to FLASHBASE - Write 0x00AA to FLASHBASE + 0xAAAA - Write 0x0055 to FLASHBASE + 0x5554 - Write 0x0090 to FLASHBASE + 0xAAAA - Read a manufacturer ID (word) from FLASHBASE - Read a device ID (word) from FLASHBASE + 2 - Write 0x00F0 to FLASHBASE AMD: manufacturer == 0x0001, device == 0x0049 Fujitsu: manufacturer == 0x0004, device == 0x0049 To erase a word of flash, our ROM routines: AMD, Fujitsu: - Read a word from FLASHBASE - Write 0x00F0 to FLASHBASE - Write 0x00AA to FLASHBASE + 0xAAAA - Write 0x0055 to FLASHBASE + 0x5554 - Write 0x0080 to FLASHBASE + 0xAAAA - Write 0x00AA to FLASHBASE + 0xAAAA - Write 0x0055 to FLASHBASE + 0x5554 - Write 0x0030 to location to be erased - Check erase location for 0x0080 - Read from FLASHBASE - Write 0x00F0 to FLASHBASE Mitsubishi, Hitachi: - Read a word from FLASHBASE - Write 0x00FF to FLASHBASE - Write 0x0020 to FLASHBASE - Write 0x00D0 to location to be erased - Check erase location for 0x0080 -- If 0x0020 is also set, an error occurred - Read from FLASHBASE - Write 0x00FF to FLASHBASE Intel - Not supported To program a block of flash: AMD, Fujitsu: - Read a word from FLASHBASE - Write 0x00F0 to FLASHBASE - For each word to write - If the word is already there, continue - Write 0x00AA to FLASHBASE + 0xAAAA - Write 0x0055 to FLASHBASE + 0x5554 - Write 0x00A0 to FLASHBASE + 0xAAAA - Write the word to the dest location - Check write location for 0x0080 - Read from FLASHBASE - Write 0x00F0 to FLASHBASE Mitsubishi, Hitachi, Intel: - Not supported */ enum { kAMDState_Normal, // Read-only mode. Acts like a normal ROM. // Almost any write of 0x00F0 to any address will get us here. // Also enter here automatically after Erase or Program operation. // A write of 0x00AA to 0xAAAA will put us in kAMDState_Unlocked1. kAMDState_Unlocked1, // After first unlock cycle (0x00AA written to 0xAAAA) // Looking for the second unlock cycle (0x0055 written to 0x5554); // If we find one, go to kAMDState_Unlocked2. // ??? What happens on other operations? kAMDState_Unlocked2, // After second unlock cycle (0x0055 written to 0x5554) // Now looking for a command to be written to 0xAAAA // If we find 0x0090, go to kAMDState_Autoselect. // If we find 0x0080, set gEraseIsSetup and go to kAMDState_Normal. ??? When should gEraseIsSetup get cleared? // If we find 0x0030, if gEraseIsSetup erase the sector and go to kAMDState_EraseDone. // If we fine 0x00A0, go to kAMDState_Program. // ??? What happens on other operations? kAMDState_Autoselect, // After a write of 0x0090 to 0x5555. // A read of 0x0000 returns 0x0001 (manufacturer ID). // A read of 0x0002 returns 0x0049 (device ID). // A write of 0x00F0 to any address returns us to kAMDState_Normal. // ??? What happens on other operations? kAMDState_Program, // After a program sequence was entered. // Accept any write operation and go to kAMDState_ProgramDone. // ??? What happens on other operations? kAMDState_EraseDone, // After a Erase operation. // On the next read, return successful operation and return to kAMDState_Normal // ??? What happens on other operations? kAMDState_ProgramDone, // After a Program operation. // On the next read, return successful operation and return to kAMDState_Normal // ??? What happens on other operations? kAMDState_Dummy }; static EmAddressBank gFlashAddressBank = { EmBankFlash::GetLong, EmBankFlash::GetWord, EmBankFlash::GetByte, EmBankFlash::SetLong, EmBankFlash::SetWord, EmBankFlash::SetByte, EmBankFlash::GetRealAddress, EmBankFlash::ValidAddress, EmBankFlash::GetMetaAddress, EmBankFlash::AddOpcodeCycles }; #define FLASHBASE (EmBankROM::GetMemoryStart ()) static int gState = kAMDState_Normal; static Bool gEraseIsSetup; /*********************************************************************** * * FUNCTION: EmBankFlash::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 EmBankFlash::Initialize (void) { } /*********************************************************************** * * FUNCTION: EmBankFlash::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 EmBankFlash::Reset (Bool hardwareReset) { if (hardwareReset) { gState = kAMDState_Normal; } } /*********************************************************************** * * FUNCTION: EmBankFlash::Save * * DESCRIPTION: Standard save function. Saves any sub-system state to * the given session file. * * PARAMETERS: None. * * RETURNED: Nothing. * ***********************************************************************/ void EmBankFlash::Save (SessionFile&) { // !!! Save gState && gEraseIsSetup? } /*********************************************************************** * * FUNCTION: EmBankFlash::Load * * DESCRIPTION: Standard load function. Loads any sub-system state * from the given session file. * * PARAMETERS: None. * * RETURNED: Nothing. * ***********************************************************************/ void EmBankFlash::Load (SessionFile&) { // !!! Load gState && gEraseIsSetup? } /*********************************************************************** * * FUNCTION: EmBankFlash::Dispose * * DESCRIPTION: Standard dispose function. Completely release any * resources acquired or allocated in Initialize and/or * Load. * * PARAMETERS: None. * * RETURNED: Nothing. * ***********************************************************************/ void EmBankFlash::Dispose (void) { } /*********************************************************************** * * FUNCTION: EmBankFlash::SetBankHandlers * * DESCRIPTION: Set the bank handlers UAE uses to dispatch memory * access operations. * * PARAMETERS: None * * RETURNED: Nothing * ***********************************************************************/ void EmBankFlash::SetBankHandlers (void) { gManagedROMSize = EmHAL::GetROMSize (); // Memory banks gROMMemoryStart to are managed by the functions // in EmBankROM. is based on the amount of ROM being emulated. uint32 first_bank = EmMemBankIndex (EmBankROM::GetMemoryStart ()); uint32 last_bank = EmMemBankIndex (EmBankROM::GetMemoryStart () + gManagedROMSize - 1); Memory::InitializeBanks ( gFlashAddressBank, first_bank, last_bank - first_bank + 1); } // --------------------------------------------------------------------------- // ¥ EmBankFlash::GetLong // --------------------------------------------------------------------------- uint32 EmBankFlash::GetLong (emuptr address) { return EmBankROM::GetLong (address); } // --------------------------------------------------------------------------- // ¥ EmBankFlash::GetWord // --------------------------------------------------------------------------- uint32 EmBankFlash::GetWord (emuptr address) { switch (gState) { case kAMDState_Normal: // Read-only mode. Acts like a normal ROM. // Almost any write of 0x00F0 to any address will get us here. // Also enter here automatically after Erase or Program operation. // A write of 0x00AA to 0xAAAA will put us in kAMDState_Unlocked1. break; case kAMDState_Unlocked1: // After first unlock cycle (0x00AA written to 0xAAAA) // Looking for the second unlock cycle (0x0055 written to 0x5554); // If we find one, go to kAMDState_Unlocked2. // ??? What happens on other operations? break; case kAMDState_Unlocked2: // After second unlock cycle (0x0055 written to 0x5554) // Now looking for a command to be written to 0xAAAA // If we find 0x0090, go to kAMDState_Autoselect. // If we find 0x0080, set gEraseIsSetup and go to kAMDState_Normal. ??? When should gEraseIsSetup get cleared? // If we find 0x0030, if gEraseIsSetup erase the sector and go to kAMDState_ProgramDone. // If we fine 0x00A0, go to kAMDState_Program. // ??? What happens on other operations? break; case kAMDState_Autoselect: // After a write of 0x0090 to 0x5555. // A read of 0x0000 returns 0x0001 (manufacturer ID). // A read of 0x0002 returns 0x0049 (device ID). // A write of 0x00F0 to any address returns us to kAMDState_Normal. // ??? What happens on other operations? if (address == FLASHBASE) { return 0x0001; } else if (address == FLASHBASE + 2) { return 0x0049; } break; case kAMDState_Program: // After a program sequence was entered. // Accept any write operation and go to kAMDState_ProgramDone. // ??? What happens on other operations? break; case kAMDState_EraseDone: // After a Program or Erase operation. // On the next read, return successful operation and return to kAMDState_Normal // ??? What happens on other operations? // !!! We should really check that address is the same as the // one specified in the Erase command. gState = kAMDState_Normal; return 0x0080; case kAMDState_ProgramDone: // After a Program or Erase operation. // On the next read, return successful operation and return to kAMDState_Normal // ??? What happens on other operations? // !!! We should really check that address is the same as the // one specified in the Program command. gState = kAMDState_Normal; return 0x0080 & EmBankROM::GetWord (address); } return EmBankROM::GetWord (address); } // --------------------------------------------------------------------------- // ¥ EmBankFlash::GetByte // --------------------------------------------------------------------------- uint32 EmBankFlash::GetByte (emuptr address) { return EmBankROM::GetByte (address); } // --------------------------------------------------------------------------- // ¥ EmBankFlash::SetLong // --------------------------------------------------------------------------- void EmBankFlash::SetLong (emuptr address, uint32 value) { EmBankROM::SetLong (address, value); } // --------------------------------------------------------------------------- // ¥ EmBankFlash::SetWord // --------------------------------------------------------------------------- void EmBankFlash::SetWord (emuptr address, uint32 value) { switch (gState) { case kAMDState_Normal: // Read-only mode. Acts like a normal ROM. // Almost any write of 0x00F0 to any address will get us here. // Also enter here automatically after Erase or Program operation. // A write of 0x00AA to 0xAAAA will put us in kAMDState_Unlocked1. if (address == FLASHBASE + 0xAAAA && value == 0x00AA) { gState = kAMDState_Unlocked1; return; } // Allow these. PrvIdentifyFlashCode probes with these values // to determine what Flash part we've got. if (address == FLASHBASE && (value == 0x00FF || value == 0x00F0 || value == 0x0090)) { return; } break; case kAMDState_Unlocked1: // After first unlock cycle (0x00AA written to 0xAAAA) // Looking for the second unlock cycle (0x0055 written to 0x5554); // If we find one, go to kAMDState_Unlocked2. // ??? What happens on other operations? if (value == 0x00F0) { gState = kAMDState_Normal; return; } else if (address == FLASHBASE + 0x5554 && value == 0x0055) { gState = kAMDState_Unlocked2; return; } break; case kAMDState_Unlocked2: // After second unlock cycle (0x0055 written to 0x5554) // Now looking for a command to be written to 0xAAAA // If we find 0x0090, go to kAMDState_Autoselect. // If we find 0x0080, set gEraseIsSetup and go to kAMDState_Normal. ??? When should gEraseIsSetup get cleared? // If we find 0x0030, if gEraseIsSetup erase the sector and go to kAMDState_EraseDone. // If we fine 0x00A0, go to kAMDState_Program. // ??? What happens on other operations? if (value == 0x00F0) { gState = kAMDState_Normal; return; } else if (value == 0x0030 && gEraseIsSetup) { const int kEraseValue = 0xFF; #if 0 DWord readWriteParmsOffset = EmMemGet32 (0x10C00000 + offsetof (CardHeaderType, readWriteParmsOffset)); DWord readWriteParmsSize = EmMemGet32 (0x10C00000 + offsetof (CardHeaderType, readWriteParmsSize)); DWord readOnlyParmsOffset = EmMemGet32 (0x10C00000 + offsetof (CardHeaderType, readOnlyParmsOffset)); DWord readOnlyParmsSize = 0x00002000; DWord readWriteWorkingOffset = EmMemGet32 (0x10C00000 + offsetof (CardHeaderType, readWriteWorkingOffset)); DWord readWriteWorkingSize = EmMemGet32 (0x10C00000 + offsetof (CardHeaderType, readWriteWorkingSize)); if (address >= 0x10000000 + readWriteParmsOffset && address < 0x10000000 + readWriteParmsOffset + readWriteParmsSize) { EmMem_memset (0x10000000 + readWriteParmsOffset, kEraseValue, readWriteParmsSize); } else if (address >= 0x10000000 + readOnlyParmsOffset && address < 0x10000000 + readOnlyParmsOffset + readOnlyParmsSize) { EmMem_memset (0x10000000 + readOnlyParmsOffset, kEraseValue, readOnlyParmsSize); } else if (address >= 0x10000000 + readWriteWorkingOffset && address < 0x10000000 + readWriteWorkingOffset + readWriteWorkingSize) { EmMem_memset (0x10000000 + readWriteWorkingOffset, kEraseValue, readWriteWorkingSize); } #endif const unsigned long kSector1Start = 0x10C00000; const unsigned long kSector1Size = 0x00004000; const unsigned long kSector2Start = 0x10C04000; const unsigned long kSector2Size = 0x00002000; const unsigned long kSector3Start = 0x10C06000; const unsigned long kSector3Size = 0x00002000; const unsigned long kSector4Start = 0x10C08000; const unsigned long kSector4Size = 0x00008000; CEnableFullAccess munge; if (address == kSector1Start) { EmMem_memset (kSector1Start, kEraseValue, kSector1Size); } else if (address == kSector2Start) { EmMem_memset (kSector2Start, kEraseValue, kSector2Size); } else if (address == kSector3Start) { EmMem_memset (kSector3Start, kEraseValue, kSector3Size); } else if (address == kSector4Start) { EmMem_memset (kSector4Start, kEraseValue, kSector4Size); } gState = kAMDState_EraseDone; return; } else if (address == FLASHBASE + 0xAAAA) { if (value == 0x0090) { gState = kAMDState_Autoselect; return; } else if (value == 0x0080) { gEraseIsSetup = true; gState = kAMDState_Normal; return; } else if (value == 0x00A0) { gState = kAMDState_Program; return; } } break; case kAMDState_Autoselect: // After a write of 0x0090 to 0x5555. // A read of 0x0000 returns 0x0001 (manufacturer ID). // A read of 0x0002 returns 0x0049 (device ID). // A write of 0x00F0 to any address returns us to kAMDState_Normal. // ??? What happens on other operations? if (value == 0x00F0) { gState = kAMDState_Normal; return; } break; case kAMDState_Program: // After a program sequence was entered. // Accept any write operation and go to kAMDState_ProgramDone. // ??? What happens on other operations? address &= gROMBank_Mask; EmMemDoPut16 (gROM_Memory + address, value); gState = kAMDState_ProgramDone; return; case kAMDState_EraseDone: // After a Program or Erase operation. // On the next read, return successful operation and return to kAMDState_Normal // ??? What happens on other operations? return; case kAMDState_ProgramDone: // After a Program or Erase operation. // On the next read, return successful operation and return to kAMDState_Normal // ??? What happens on other operations? return; } EmBankROM::SetWord (address, value); } // --------------------------------------------------------------------------- // ¥ EmBankFlash::SetByte // --------------------------------------------------------------------------- void EmBankFlash::SetByte (emuptr address, uint32 value) { EmBankROM::SetByte (address, value); } // --------------------------------------------------------------------------- // ¥ EmBankFlash::ValidAddress // --------------------------------------------------------------------------- int EmBankFlash::ValidAddress (emuptr address, uint32 size) { return EmBankROM::ValidAddress (address, size); } // --------------------------------------------------------------------------- // ¥ EmBankFlash::GetRealAddress // --------------------------------------------------------------------------- uint8* EmBankFlash::GetRealAddress (emuptr address) { // Strip the uppermost bit of the address. address &= gROMBank_Mask; return (uint8*) &gROM_Memory[address]; } // --------------------------------------------------------------------------- // ¥ EmBankFlash::GetMetaAddress // --------------------------------------------------------------------------- uint8* EmBankFlash::GetMetaAddress (emuptr address) { // Strip the uppermost bit of the address. address &= gROMBank_Mask; return (uint8*) &(gROM_MetaMemory[address]); } // --------------------------------------------------------------------------- // ¥ EmBankFlash::AddOpcodeCycles // --------------------------------------------------------------------------- void EmBankFlash::AddOpcodeCycles (void) { #if (HAS_PROFILING) CYCLE_GETWORD (WAITSTATES_ROM); #endif } #pragma mark - // =========================================================================== // ¥ Card // =========================================================================== // --------------------------------------------------------------------------- // ¥ Card::CheckCardHeader // --------------------------------------------------------------------------- // Determines if the memory pointed to looks like a card header. Bool Card::CheckCardHeader (const EmProxyCardHeaderType& cardHdr) { // Make sure the stack size isn't something ludicrous. if (cardHdr.initStack == 0 || cardHdr.initStack > 0x000FFFFF) // 1 Meg return false; // Make sure the signature is right. if (cardHdr.signature != sysCardSignature) return false; // Make sure the header version isn't ludicrous. if (cardHdr.hdrVersion == 0 || cardHdr.hdrVersion > 255) // arbitrary value return false; // Make sure this isn't a "RAM only" card. if (cardHdr.flags & memCardHeaderFlagRAMOnly) return false; return true; } // --------------------------------------------------------------------------- // ¥ Card::CheckChecksum // --------------------------------------------------------------------------- // Determines if the memory pointed to looks like a card header. Bool Card::CheckChecksum (const void* p, uint32 fileLength) { EmAliasCardHeaderType cardHdr ((void*) p); if (cardHdr.hdrVersion == 1) { static const UInt16 kDebug20 = 0x9BED; UInt16 checksumValue = Crc16CalcBigBlock ((/*non-const*/ void*) p, fileLength, 0); // Special hack for 2.0 Debug ROMs. The version with this checksum was // bogus, so let's make sure we never load it. if (checksumValue == kDebug20) { // Throw a special error for this one. Errors::Throw (kError_UnsupportedROM); } return true; } else { // The checksum is the cumulative checksum of the ROM image before // the stored checksum value and the ROM image following the checksum // value. First, calculate the first part. UInt32 chunkSize = cardHdr.offsetof_checksumValue (); UInt16 checksumValue = Crc16CalcBigBlock ( (/*non-const*/ void*) p, chunkSize, 0); // Now calculate the second part. UInt32 skipSize = chunkSize + cardHdr.checksumValue.GetSize (); checksumValue = Crc16CalcBigBlock ( ((char*) p) + skipSize, cardHdr.checksumBytes - skipSize, checksumValue); if (checksumValue == cardHdr.checksumValue) { return true; } } Errors::DoDialog (kStr_BadChecksum, kDlgFlags_OK); return false; } // --------------------------------------------------------------------------- // ¥ Card::Supports328 // --------------------------------------------------------------------------- Bool Card::Supports328 (const EmAliasCardHeaderType& cardHdr) { Bool dbMode = false; if (cardHdr.hdrVersion < 3 || (cardHdr.flags & memCardHeaderFlag328)) { dbMode = true; } return dbMode; } // --------------------------------------------------------------------------- // ¥ Card::SupportsEZ // --------------------------------------------------------------------------- Bool Card::SupportsEZ (const EmAliasCardHeaderType& cardHdr) { Bool ezMode = false; if (cardHdr.hdrVersion >= 3 && (cardHdr.flags & memCardHeaderFlagEZ)) { ezMode = true; } return ezMode; } // --------------------------------------------------------------------------- // ¥ Card::SupportsVZ // --------------------------------------------------------------------------- #define memCardHeaderFlagVZ 0x0040 // ROM Supports 68VZ328 processor Bool Card::SupportsVZ (const EmAliasCardHeaderType& cardHdr) { Bool vzMode = false; if (cardHdr.hdrVersion >= 3 && (cardHdr.flags & memCardHeaderFlagVZ)) { vzMode = true; } return vzMode; } // --------------------------------------------------------------------------- // ¥ Card::SupportsSZ // --------------------------------------------------------------------------- #define memCardHeaderFlagSZ 0x0080 // ROM Supports 68SZ328 processor Bool Card::SupportsSZ (const EmAliasCardHeaderType& cardHdr) { Bool szMode = false; if (cardHdr.hdrVersion >= 3 && (cardHdr.flags & memCardHeaderFlagSZ)) { szMode = true; } return szMode; }