From fcc05a84fc8d835143fe0fd96a2d4950072615c8 Mon Sep 17 00:00:00 2001 From: Christian Speckner Date: Tue, 13 Aug 2024 23:14:50 +0200 Subject: [PATCH] State saves. --- src/emucore/CartELF.cxx | 130 +++++++++++++++--------- src/emucore/CartELF.hxx | 9 +- src/emucore/CortexM0.cxx | 98 ++++++++++++++++-- src/emucore/CortexM0.hxx | 11 ++ src/emucore/elf/BusTransactionQueue.cxx | 22 ++++ src/emucore/elf/BusTransactionQueue.hxx | 6 ++ src/emucore/elf/VcsLib.cxx | 15 +++ src/emucore/elf/VcsLib.hxx | 4 + 8 files changed, 238 insertions(+), 57 deletions(-) diff --git a/src/emucore/CartELF.cxx b/src/emucore/CartELF.cxx index e282ee38c..b57476a02 100644 --- a/src/emucore/CartELF.cxx +++ b/src/emucore/CartELF.cxx @@ -237,53 +237,8 @@ CartridgeELF::~CartridgeELF() = default; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void CartridgeELF::reset() { - const bool devMode = mySettings.getBool("dev.settings"); - const bool strictMode = devMode && mySettings.getBool("dev.thumb.trapfatal"); - const uInt32 mips = devMode ? mySettings.getInt("dev.arm.mips") : MIPS_MAX; - - std::fill_n(myLastPeekResult.get(), 0x1000, 0); - myIsBusDriven = false; - myDriveBusValue = 0; - myArmCyclesOffset = 0; - myArmCyclesPer6502Cycle = (mips * 1000000) / get6502SpeedHz(myConsoleTiming); - - mySystemType = determineSystemType(myProperties); - myLinker->relink(externalSymbols(mySystemType)); - - std::memset(mySectionStack.get(), 0, STACK_SIZE); - std::memset(mySectionText.get(), 0, TEXT_SIZE); - std::memset(mySectionData.get(), 0, DATA_SIZE); - std::memset(mySectionRodata.get(), 0, RODATA_SIZE); - std::memset(mySectionTables.get(), 0, TABLES_SIZE); - - std::memcpy(mySectionText.get(), myLinker->getSegmentData(ElfLinker::SegmentType::text), - myLinker->getSegmentSize(ElfLinker::SegmentType::text)); - std::memcpy(mySectionData.get(), myLinker->getSegmentData(ElfLinker::SegmentType::data), - myLinker->getSegmentSize(ElfLinker::SegmentType::data)); - std::memcpy(mySectionRodata.get(), myLinker->getSegmentData(ElfLinker::SegmentType::rodata), - myLinker->getSegmentSize(ElfLinker::SegmentType::rodata)); - std::memcpy(mySectionTables.get(), LOOKUP_TABLES, sizeof(LOOKUP_TABLES)); - - setupMemoryMap(strictMode); - myCortexEmu.reset(); - - myTransactionQueue - .reset() - .injectROMAt(0x00, 0x1ffc) - .injectROM(0x10) - .setNextInjectAddress(0x1000); - - myVcsLib.reset(); - - myVcsLib.vcsCopyOverblankToRiotRam(); - myVcsLib.vcsStartOverblank(); - myVcsLib.vcsEndOverblank(); - myVcsLib.vcsNop2n(1024); - - myExecutionStage = ExecutionStage::boot; - myInitFunctionIndex = 0; - - switchExecutionStage(); + setupConfig(); + resetWithConfig(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -303,7 +258,28 @@ void CartridgeELF::install(System& system) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool CartridgeELF::save(Serializer& out) const { - return false; + try { + out.putBool(myConfigStrictMode); + out.putInt(myConfigMips); + out.putByte(static_cast(myConfigSystemType)); + + out.putBool(myIsBusDriven); + out.putByte(myDriveBusValue); + out.putLong(myArmCyclesOffset); + out.putByte(static_cast(myExecutionStage)); + out.putInt(myInitFunctionIndex); + out.putByte(static_cast(myConsoleTiming)); + + myTransactionQueue.save(out); + myCortexEmu.save(out); + myVcsLib.save(out); + } + catch(...) { + cerr << "ERROR: failed to save ELF\n"; + return false; + } + + return true; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -533,7 +509,7 @@ void CartridgeELF::callMain() err |= myCortexEmu.write32(sp, myArmCyclesPer6502Cycle * get6502SpeedHz(myConsoleTiming)); sp -= 4; - err |= myCortexEmu.write32(sp, getSystemTypeParam(mySystemType)); + err |= myCortexEmu.write32(sp, getSystemTypeParam(myConfigSystemType)); if (err) throw runtime_error("unable to setup main args"); @@ -651,3 +627,59 @@ CortexM0::err_t CartridgeELF::BusFallbackDelegate::handleError( return CortexM0::ERR_NONE; } + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeELF::setupConfig() +{ + const bool devMode = mySettings.getBool("dev.settings"); + + myConfigStrictMode = devMode && mySettings.getBool("dev.thumb.trapfatal"); + myConfigMips = devMode ? mySettings.getInt("dev.arm.mips") : MIPS_MAX; + myConfigSystemType = determineSystemType(myProperties); +} + +void CartridgeELF::resetWithConfig() +{ + std::fill_n(myLastPeekResult.get(), 0x1000, 0); + myIsBusDriven = false; + myDriveBusValue = 0; + myArmCyclesOffset = 0; + myArmCyclesPer6502Cycle = (myConfigMips * 1000000) / get6502SpeedHz(myConsoleTiming); + + myLinker->relink(externalSymbols(myConfigSystemType)); + + std::memset(mySectionStack.get(), 0, STACK_SIZE); + std::memset(mySectionText.get(), 0, TEXT_SIZE); + std::memset(mySectionData.get(), 0, DATA_SIZE); + std::memset(mySectionRodata.get(), 0, RODATA_SIZE); + std::memset(mySectionTables.get(), 0, TABLES_SIZE); + + std::memcpy(mySectionText.get(), myLinker->getSegmentData(ElfLinker::SegmentType::text), + myLinker->getSegmentSize(ElfLinker::SegmentType::text)); + std::memcpy(mySectionData.get(), myLinker->getSegmentData(ElfLinker::SegmentType::data), + myLinker->getSegmentSize(ElfLinker::SegmentType::data)); + std::memcpy(mySectionRodata.get(), myLinker->getSegmentData(ElfLinker::SegmentType::rodata), + myLinker->getSegmentSize(ElfLinker::SegmentType::rodata)); + std::memcpy(mySectionTables.get(), LOOKUP_TABLES, sizeof(LOOKUP_TABLES)); + + setupMemoryMap(myConfigStrictMode); + myCortexEmu.reset(); + + myTransactionQueue + .reset() + .injectROMAt(0x00, 0x1ffc) + .injectROM(0x10) + .setNextInjectAddress(0x1000); + + myVcsLib.reset(); + + myVcsLib.vcsCopyOverblankToRiotRam(); + myVcsLib.vcsStartOverblank(); + myVcsLib.vcsEndOverblank(); + myVcsLib.vcsNop2n(1024); + + myExecutionStage = ExecutionStage::boot; + myInitFunctionIndex = 0; + + switchExecutionStage(); +} diff --git a/src/emucore/CartELF.hxx b/src/emucore/CartELF.hxx index 448c90f4c..97cb93500 100644 --- a/src/emucore/CartELF.hxx +++ b/src/emucore/CartELF.hxx @@ -99,6 +99,9 @@ class CartridgeELF: public Cartridge { }; private: + void setupConfig(); + void resetWithConfig(); + uInt64 getArmCycles() const; uInt8 driveBus(uInt16 address, uInt8 value); @@ -120,6 +123,10 @@ class CartridgeELF: public Cartridge { System* mySystem{nullptr}; + bool myConfigStrictMode{false}; + uInt32 myConfigMips{100}; + elfEnvironment::SystemType myConfigSystemType{elfEnvironment::SystemType::ntsc}; + unique_ptr myLastPeekResult; BusTransactionQueue myTransactionQueue; @@ -149,8 +156,6 @@ class CartridgeELF: public Cartridge { ExecutionStage myExecutionStage{ExecutionStage::boot}; uInt32 myInitFunctionIndex{0}; - elfEnvironment::SystemType mySystemType{elfEnvironment::SystemType::ntsc}; - private: // Following constructors and assignment operators not supported CartridgeELF(const CartridgeELF&) = delete; diff --git a/src/emucore/CortexM0.cxx b/src/emucore/CortexM0.cxx index e78a2fafb..427450a92 100644 --- a/src/emucore/CortexM0.cxx +++ b/src/emucore/CortexM0.cxx @@ -22,6 +22,9 @@ #include "CortexM0.hxx" +#include + +#include "Serializable.hxx" #include "Base.hxx" namespace { @@ -555,13 +558,66 @@ CortexM0::CortexM0() reset(); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CortexM0::MemoryRegion::save(Serializer& serializer) const +{ + if (type != MemoryRegionType::directCode && type != MemoryRegionType::directData) return; + + serializer.putBool(dirty); + if (!dirty) return; + + serializer.putInt(accessWatermarkLow); + serializer.putInt(accessWatermarkHigh); + + switch (type) { + case MemoryRegionType::directCode: + serializer.putByteArray( + std::get<1>(access).backingStore + (accessWatermarkLow - base), + accessWatermarkHigh - accessWatermarkLow + 1 + ); + + break; + + case MemoryRegionType::directData: + serializer.putByteArray( + std::get<0>(access).backingStore + (accessWatermarkLow - base), + accessWatermarkHigh - accessWatermarkLow + 1 + ); + + break; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CortexM0::save(Serializer& serializer) const +{ + for (size_t i = 0; i < 16; i++) + serializer.putInt(reg_norm[i]); + + serializer.putInt(znFlags); + serializer.putInt(cFlag); + serializer.putInt(vFlag); + serializer.putLong(myCycleCounter); + + for (size_t i = 0; i < myNextRegionIndex; i++) + myRegions[i].save(serializer); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CortexM0::MemoryRegion::reset() +{ + type = MemoryRegionType::unmapped; + access.emplace(); + + accessWatermarkHigh = 0; + accessWatermarkLow = ~0; + dirty = false; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CortexM0& CortexM0::resetMappings() { - for (auto& region: myRegions) { - region.type = MemoryRegionType::unmapped; - region.access.emplace(); - } + for (auto& region: myRegions) region.reset(); myNextRegionIndex = 0; std::fill_n(myPageMap.get(), PAGEMAP_SIZE, 0xff); @@ -802,11 +858,25 @@ CortexM0::err_t CortexM0::write32(uInt32 address, uInt32 value) case MemoryRegionType::delegate: return std::get<2>(region.access)->write32(address, value, *this); - case MemoryRegionType::directCode: - WRITE32(std::get<1>(region.access).backingStore, address - region.base, value); + case MemoryRegionType::directCode: { + const uInt32 offset = address - region.base; + + region.dirty = true; + region.accessWatermarkLow = std::min(address, region.accessWatermarkLow); + region.accessWatermarkHigh = std::max(address + 3, region.accessWatermarkHigh); + + WRITE32(std::get<1>(region.access).backingStore, offset, value); + std::get<1>(region.access).ops[offset >> 1] = decodeInstructionWord(value); + std::get<1>(region.access).ops[(offset + 2) >> 1] = decodeInstructionWord(value >> 16); + return ERR_NONE; + } case MemoryRegionType::directData: + region.dirty = true; + region.accessWatermarkLow = std::min(address, region.accessWatermarkLow); + region.accessWatermarkHigh = std::max(address + 3, region.accessWatermarkHigh); + WRITE32(std::get<0>(region.access).backingStore, address - region.base, value); return ERR_NONE; @@ -832,6 +902,10 @@ CortexM0::err_t CortexM0::write16(uInt32 address, uInt16 value) case MemoryRegionType::directCode: { const uInt32 offset = address - region.base; + region.dirty = true; + region.accessWatermarkLow = std::min(address, region.accessWatermarkLow); + region.accessWatermarkHigh = std::max(address + 1, region.accessWatermarkHigh); + WRITE16(std::get<1>(region.access).backingStore, offset, value); std::get<1>(region.access).ops[offset >> 1] = decodeInstructionWord(value); @@ -839,6 +913,10 @@ CortexM0::err_t CortexM0::write16(uInt32 address, uInt16 value) } case MemoryRegionType::directData: + region.dirty = true; + region.accessWatermarkLow = std::min(address, region.accessWatermarkLow); + region.accessWatermarkHigh = std::max(address + 1, region.accessWatermarkHigh); + WRITE16(std::get<0>(region.access).backingStore, address - region.base, value); return ERR_NONE; @@ -862,6 +940,10 @@ CortexM0::err_t CortexM0::write8(uInt32 address, uInt8 value) case MemoryRegionType::directCode: { const uInt32 offset = address - region.base; + region.dirty = true; + region.accessWatermarkLow = std::min(address, region.accessWatermarkLow); + region.accessWatermarkHigh = std::max(address, region.accessWatermarkHigh); + std::get<1>(region.access).backingStore[offset] = value; std::get<1>(region.access).ops[offset >> 1] = decodeInstructionWord(value); @@ -869,6 +951,10 @@ CortexM0::err_t CortexM0::write8(uInt32 address, uInt8 value) } case MemoryRegionType::directData: + region.dirty = true; + region.accessWatermarkLow = std::min(address, region.accessWatermarkLow); + region.accessWatermarkHigh = std::max(address, region.accessWatermarkHigh); + std::get<0>(region.access).backingStore[address - region.base] = value; return ERR_NONE; diff --git a/src/emucore/CortexM0.hxx b/src/emucore/CortexM0.hxx index a952c2b43..682717032 100644 --- a/src/emucore/CortexM0.hxx +++ b/src/emucore/CortexM0.hxx @@ -27,6 +27,8 @@ #include "bspf.hxx" +class Serializer; + class CortexM0 { public: @@ -116,6 +118,8 @@ class CortexM0 CortexM0& mapDefault(BusTransactionDelegate* delegate); + void save(Serializer& serializer) const; + CortexM0& reset(); CortexM0& setPc(uInt32 pc); CortexM0& setRegister(uInt8 regno, uInt32 value); @@ -165,6 +169,10 @@ class CortexM0 uInt32 size{0}; bool readOnly{false}; + bool dirty{false}; + uInt32 accessWatermarkLow{static_cast(~0)}; + uInt32 accessWatermarkHigh{0}; + std::variant< MemoryRegionAccessData, // ::get<0>, directData MemoryRegionAccessCode, // ::get<1>, directCode @@ -172,6 +180,9 @@ class CortexM0 std::monostate > access; + void reset(); + void save(Serializer& serializer) const; + private: MemoryRegion(const MemoryRegion&) = delete; MemoryRegion(MemoryRegion&&) = delete; diff --git a/src/emucore/elf/BusTransactionQueue.cxx b/src/emucore/elf/BusTransactionQueue.cxx index dae5ae1a9..a0695342c 100644 --- a/src/emucore/elf/BusTransactionQueue.cxx +++ b/src/emucore/elf/BusTransactionQueue.cxx @@ -17,6 +17,7 @@ #include "BusTransactionQueue.hxx" +#include "Serializable.hxx" #include "exception/FatalEmulationError.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -64,6 +65,27 @@ BusTransactionQueue& BusTransactionQueue::reset() return *this; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void BusTransactionQueue::save(Serializer& serializer) const +{ + serializer.putInt(myQueueSize); + serializer.putShort(myNextInjectAddress); + serializer.putLong(myTimestamp); + + for (size_t i = 0; i < myQueueSize; i++) + myQueue[(myQueueNext + i) % myQueueCapacity].save(serializer); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void BusTransactionQueue::Transaction::save(Serializer& serializer) const +{ + serializer.putShort(address); + serializer.putShort(mask); + serializer.putByte(value); + serializer.putLong(timestamp); + serializer.putBool(yield); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - BusTransactionQueue& BusTransactionQueue::setNextInjectAddress(uInt16 address) { diff --git a/src/emucore/elf/BusTransactionQueue.hxx b/src/emucore/elf/BusTransactionQueue.hxx index bd4625171..b0a4d6995 100644 --- a/src/emucore/elf/BusTransactionQueue.hxx +++ b/src/emucore/elf/BusTransactionQueue.hxx @@ -20,6 +20,8 @@ #include "bspf.hxx" +class Serializer; + class BusTransactionQueue { public: struct Transaction { @@ -33,6 +35,8 @@ class BusTransactionQueue { uInt8 value{0}; uInt64 timestamp{0}; bool yield{false}; + + void save(Serializer& serializer) const; }; public: @@ -41,6 +45,8 @@ class BusTransactionQueue { BusTransactionQueue& reset(); + void save(Serializer& serializer) const; + BusTransactionQueue& setNextInjectAddress(uInt16 address); uInt16 getNextInjectAddress() const; diff --git a/src/emucore/elf/VcsLib.cxx b/src/emucore/elf/VcsLib.cxx index 8eace4581..9a5af6c3c 100644 --- a/src/emucore/elf/VcsLib.cxx +++ b/src/emucore/elf/VcsLib.cxx @@ -111,6 +111,21 @@ VcsLib::VcsLib(BusTransactionQueue& transactionQueue) { } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void VcsLib::save(Serializer& serializer) const +{ + serializer.putByte(myStuffMaskA); + serializer.putByte(myStuffMaskX); + serializer.putByte(myStuffMaskY); + serializer.putBool(myIsWaitingForRead); + serializer.putShort(myWaitingForReadAddress); + serializer.putShort(myCurrentAddress); + serializer.putBool(myCurrentValue); + + if (!myRand.save(serializer)) + throw runtime_error("failed to save RNG"); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void VcsLib::reset() { diff --git a/src/emucore/elf/VcsLib.hxx b/src/emucore/elf/VcsLib.hxx index 2781c9eef..d091d8868 100644 --- a/src/emucore/elf/VcsLib.hxx +++ b/src/emucore/elf/VcsLib.hxx @@ -23,11 +23,15 @@ #include "CortexM0.hxx" #include "BusTransactionQueue.hxx" +class Serializer; + class VcsLib: public CortexM0::BusTransactionDelegate { public: explicit VcsLib(BusTransactionQueue& transactionQueue); ~VcsLib() override = default; + void save(Serializer& serializer) const; + void reset(); CortexM0::err_t fetch16(uInt32 address, uInt16& value, uInt8& op, CortexM0& cortex) override;