From 443a14a604590901d38af2a246d85c30c3609eff Mon Sep 17 00:00:00 2001 From: Christian Speckner Date: Mon, 29 Jul 2024 09:30:51 +0200 Subject: [PATCH] Sync timing, limit the amount of queued transactions. --- src/emucore/CartELF.cxx | 46 +++++++++++++++++++------ src/emucore/CartELF.hxx | 5 ++- src/emucore/CortexM0.cxx | 3 +- src/emucore/CortexM0.hxx | 6 ++++ src/emucore/elf/BusTransactionQueue.cxx | 36 ++++++++++++------- src/emucore/elf/BusTransactionQueue.hxx | 9 +++-- src/emucore/elf/ElfEnvironment.hxx | 3 ++ src/emucore/elf/VcsLib.cxx | 19 ++++++---- src/emucore/elf/VcsLib.hxx | 6 ++-- 9 files changed, 94 insertions(+), 39 deletions(-) diff --git a/src/emucore/CartELF.cxx b/src/emucore/CartELF.cxx index be2311756..e36ffc551 100644 --- a/src/emucore/CartELF.cxx +++ b/src/emucore/CartELF.cxx @@ -25,6 +25,7 @@ #include "ElfParser.hxx" #include "ElfLinker.hxx" #include "ElfEnvironment.hxx" +#include "Logger.hxx" #include "exception/FatalEmulationError.hxx" #include "CartELF.hxx" @@ -203,7 +204,7 @@ void CartridgeELF::reset() std::fill_n(myLastPeekResult.get(), 0x1000, 0); myIsBusDriven = false; myDriveBusValue = 0; - myArmCycles = 0; + myArmCyclesOffset = 0; std::memset(mySectionStack.get(), 0, STACK_SIZE); std::memset(mySectionText.get(), 0, TEXT_SIZE); @@ -223,10 +224,12 @@ void CartridgeELF::reset() myTransactionQueue .reset() - .injectROM(0x00, 0x1ffc) + .injectROMAt(0x00, 0x1ffc) .injectROM(0x10) .setNextInjectAddress(0x1000); + myVcsLib.reset(); + myVcsLib.vcsCopyOverblankToRiotRam(); myVcsLib.vcsStartOverblank(); myVcsLib.vcsEndOverblank(); @@ -295,8 +298,6 @@ const ByteBuffer& CartridgeELF::getImage(size_t& size) const // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uInt8 CartridgeELF::overdrivePeek(uInt16 address, uInt8 value) { - runArm(); - value = driveBus(address, value); if (address & 0x1000) { @@ -310,22 +311,41 @@ uInt8 CartridgeELF::overdrivePeek(uInt16 address, uInt8 value) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uInt8 CartridgeELF::overdrivePoke(uInt16 address, uInt8 value) { - runArm(); - return driveBus(address, value); } +inline uInt64 CartridgeELF::getArmCycles() const +{ + return myCortexEmu.getCycles() + myArmCyclesOffset; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - inline uInt8 CartridgeELF::driveBus(uInt16 address, uInt8 value) { auto* nextTransaction = myTransactionQueue.getNextTransaction(address); - if (nextTransaction) nextTransaction->setBusState(myIsBusDriven, myDriveBusValue); + if (nextTransaction) { + nextTransaction->setBusState(myIsBusDriven, myDriveBusValue); + syncClock(*nextTransaction); + } if (myIsBusDriven) value |= myDriveBusValue; + runArm(); + return value; } +inline void CartridgeELF::syncClock(const BusTransactionQueue::Transaction& transaction) +{ + const Int64 currentSystemArmCycles = mySystem->cycles() * myArmCyclesPer6502Cycle; + const Int64 transactionArmCycles = transaction.timestamp + myArmCyclesOffset; + + myArmCyclesOffset += currentSystemArmCycles - transactionArmCycles; + + if (transactionArmCycles > currentSystemArmCycles) + Logger::error("ARM took too many cycles between bus transitions"); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void CartridgeELF::parseAndLinkElf() { @@ -461,16 +481,20 @@ void CartridgeELF::jumpToMain() void CartridgeELF::runArm() { - if (myArmCycles >= (mySystem->cycles() + ARM_RUNAHED_MIN) * myArmCyclesPer6502Cycle) return; + if ( + (getArmCycles() >= (mySystem->cycles() + ARM_RUNAHED_MIN) * myArmCyclesPer6502Cycle) || + myTransactionQueue.size() >= QUEUE_SIZE_LIMIT + ) + return; const uInt32 cyclesGoal = - (mySystem->cycles() + ARM_RUNAHED_MAX) * myArmCyclesPer6502Cycle - myArmCycles; + (mySystem->cycles() + ARM_RUNAHED_MAX) * myArmCyclesPer6502Cycle - getArmCycles(); uInt32 cycles; const CortexM0::err_t err = myCortexEmu.run(cyclesGoal, cycles); - myArmCycles += cycles; - if (err != 0) FatalEmulationError::raise("error executing ARM code: " + CortexM0::describeError(err)); + if (err && (CortexM0::getErrCustom(err) != ERR_QUEUE_FULL)) + FatalEmulationError::raise("error executing ARM code: " + CortexM0::describeError(err)); } diff --git a/src/emucore/CartELF.hxx b/src/emucore/CartELF.hxx index 8fcfe67dc..e4ad96311 100644 --- a/src/emucore/CartELF.hxx +++ b/src/emucore/CartELF.hxx @@ -66,7 +66,10 @@ class CartridgeELF: public Cartridge { bool doesBusStuffing() override { return true; } private: + uInt64 getArmCycles() const; + uInt8 driveBus(uInt16 address, uInt8 value); + void syncClock(const BusTransactionQueue::Transaction& transaction); void parseAndLinkElf(); void setupMemoryMap(); @@ -105,7 +108,7 @@ class CartridgeELF: public Cartridge { ConsoleTiming myConsoleTiming{ConsoleTiming::ntsc}; uInt32 myArmCyclesPer6502Cycle{80}; - uInt64 myArmCycles{0}; + Int64 myArmCyclesOffset{0}; }; #endif // CARTRIDGE_ELF diff --git a/src/emucore/CortexM0.cxx b/src/emucore/CortexM0.cxx index 7ef9829e2..86fbc39c3 100644 --- a/src/emucore/CortexM0.cxx +++ b/src/emucore/CortexM0.cxx @@ -578,6 +578,7 @@ CortexM0& CortexM0::reset() { reg_norm.fill(0); znFlags = cFlag = vFlag = 0; + myCycleCounter = 0; recompileCodeRegions(); @@ -608,7 +609,7 @@ uInt8 CortexM0::decodeInstructionWord(uInt16 instructionWord) CortexM0::err_t CortexM0::run(uInt32 maxCycles, uInt32& cycles) { - for (cycles = 0; cycles < maxCycles; cycles++) { + for (cycles = 0; cycles < maxCycles; cycles++, myCycleCounter++) { const uInt32 pc = read_register(15); uInt16 inst; diff --git a/src/emucore/CortexM0.hxx b/src/emucore/CortexM0.hxx index 81224b70c..f2f547a9d 100644 --- a/src/emucore/CortexM0.hxx +++ b/src/emucore/CortexM0.hxx @@ -120,6 +120,10 @@ class CortexM0 err_t write16(uInt32 address, uInt16 value); err_t write8(uInt32 address, uInt8 value); + inline uInt64 getCycles() const { + return myCycleCounter; + } + private: enum class MemoryRegionType : uInt8 { @@ -188,6 +192,8 @@ class CortexM0 uInt8 myNextRegionIndex{0}; BusTransactionDelegate* myDefaultDelegate{nullptr}; + uInt64 myCycleCounter{0}; + static constexpr uInt32 CPSR_N = 1u << 31, CPSR_Z = 1u << 30, diff --git a/src/emucore/elf/BusTransactionQueue.cxx b/src/emucore/elf/BusTransactionQueue.cxx index d9a2cb294..915243635 100644 --- a/src/emucore/elf/BusTransactionQueue.cxx +++ b/src/emucore/elf/BusTransactionQueue.cxx @@ -20,17 +20,19 @@ #include "exception/FatalEmulationError.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -BusTransactionQueue::Transaction BusTransactionQueue::Transaction::transactionYield(uInt16 address) -{ +BusTransactionQueue::Transaction BusTransactionQueue::Transaction::transactionYield( + uInt16 address, uInt64 timestamp +) { address &= 0x1fff; - return {.address = address, .value = 0, .yield = true}; + return {.address = address, .value = 0, .timestamp = timestamp, .yield = true}; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -BusTransactionQueue::Transaction BusTransactionQueue::Transaction::transactionDrive(uInt16 address, uInt8 value) -{ +BusTransactionQueue::Transaction BusTransactionQueue::Transaction::transactionDrive( + uInt16 address, uInt8 value, uInt64 timestamp +) { address &= 0x1fff; - return {.address = address, .value = value, .yield = false}; + return {.address = address, .value = value, .timestamp = timestamp, .yield = false}; } @@ -58,6 +60,7 @@ BusTransactionQueue& BusTransactionQueue::reset() { myQueueNext = myQueueSize = 0; myNextInjectAddress = 0; + myTimestamp = 0; return *this; } @@ -75,18 +78,25 @@ uInt16 BusTransactionQueue::getNextInjectAddress() const return myNextInjectAddress; } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -BusTransactionQueue& BusTransactionQueue::injectROM(uInt8 value) +BusTransactionQueue& BusTransactionQueue::setTimestamp(uInt64 timestamp) { - injectROM(value, myNextInjectAddress); + myTimestamp = timestamp; return *this; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -BusTransactionQueue& BusTransactionQueue::injectROM(uInt8 value, uInt16 address) +BusTransactionQueue& BusTransactionQueue::injectROM(uInt8 value) { - push(Transaction::transactionDrive(address, value)); + injectROMAt(value, myNextInjectAddress); + + return *this; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +BusTransactionQueue& BusTransactionQueue::injectROMAt(uInt8 value, uInt16 address) +{ + push(Transaction::transactionDrive(address, value, myTimestamp)); myNextInjectAddress = address + 1; return *this; @@ -94,7 +104,7 @@ BusTransactionQueue& BusTransactionQueue::injectROM(uInt8 value, uInt16 address) BusTransactionQueue& BusTransactionQueue::stuffByte(uInt8 value, uInt16 address) { - push(Transaction::transactionDrive(address, value)); + push(Transaction::transactionDrive(address, value, myTimestamp)); return *this; } @@ -102,7 +112,7 @@ BusTransactionQueue& BusTransactionQueue::stuffByte(uInt8 value, uInt16 address) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - BusTransactionQueue& BusTransactionQueue::yield(uInt16 address) { - push(Transaction::transactionYield(address)); + push(Transaction::transactionYield(address, myTimestamp)); return *this; } diff --git a/src/emucore/elf/BusTransactionQueue.hxx b/src/emucore/elf/BusTransactionQueue.hxx index c6449c4be..35d9fc41b 100644 --- a/src/emucore/elf/BusTransactionQueue.hxx +++ b/src/emucore/elf/BusTransactionQueue.hxx @@ -23,13 +23,14 @@ class BusTransactionQueue { public: struct Transaction { - static Transaction transactionYield(uInt16 address); - static Transaction transactionDrive(uInt16 address, uInt8 value); + static Transaction transactionYield(uInt16 address, uInt64 timestamp); + static Transaction transactionDrive(uInt16 address, uInt8 value, uInt64 timestamp); void setBusState(bool& drive, uInt8& value) const; uInt16 address; uInt8 value; + uInt64 timestamp; bool yield; }; @@ -41,8 +42,9 @@ class BusTransactionQueue { BusTransactionQueue& setNextInjectAddress(uInt16 address); uInt16 getNextInjectAddress() const; + BusTransactionQueue& setTimestamp(uInt64 timestamp); BusTransactionQueue& injectROM(uInt8 value); - BusTransactionQueue& injectROM(uInt8 value, uInt16 address); + BusTransactionQueue& injectROMAt(uInt8 value, uInt16 address); BusTransactionQueue& stuffByte(uInt8 value, uInt16 address); BusTransactionQueue& yield(uInt16 address); @@ -65,6 +67,7 @@ class BusTransactionQueue { size_t myQueueSize{0}; uInt16 myNextInjectAddress{0}; + uInt64 myTimestamp{0}; private: BusTransactionQueue(const BusTransactionQueue&) = delete; diff --git a/src/emucore/elf/ElfEnvironment.hxx b/src/emucore/elf/ElfEnvironment.hxx index e90c51daa..e69927644 100644 --- a/src/emucore/elf/ElfEnvironment.hxx +++ b/src/emucore/elf/ElfEnvironment.hxx @@ -112,6 +112,9 @@ namespace elfEnvironment { constexpr uInt32 RETURN_ADDR_MAIN = 0xffffdead; + constexpr uInt32 ERR_QUEUE_FULL = 1; + constexpr uInt32 QUEUE_SIZE_LIMIT = 10; + enum class Palette: uInt8 {pal, ntsc}; vector externalSymbols(Palette palette); diff --git a/src/emucore/elf/VcsLib.cxx b/src/emucore/elf/VcsLib.cxx index f4213215c..03b07f075 100644 --- a/src/emucore/elf/VcsLib.cxx +++ b/src/emucore/elf/VcsLib.cxx @@ -20,6 +20,7 @@ #include "BusTransactionQueue.hxx" #include "ElfEnvironment.hxx" #include "exception/FatalEmulationError.hxx" +#include "ElfEnvironment.hxx" using namespace elfEnvironment; @@ -58,7 +59,7 @@ VcsLib::VcsLib(BusTransactionQueue& transactionQueue) : myTransactionQueue(trans void VcsLib::reset() { - myStuffMaskA = myStuffMaskX = myStuffMaskY = 0xff; + myStuffMaskA = myStuffMaskX = myStuffMaskY = 0x00; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -93,7 +94,7 @@ void VcsLib::vcsStartOverblank() void VcsLib::vcsEndOverblank() { myTransactionQueue - .injectROM(0x00, 0x1fff) + .injectROMAt(0x00, 0x1fff) .yield(0x00ac) .setNextInjectAddress(0x1000); } @@ -103,10 +104,9 @@ void VcsLib::vcsNop2n(uInt16 n) { if (n == 0) return; - myTransactionQueue.injectROM(0xea); - myTransactionQueue.setNextInjectAddress( - myTransactionQueue.getNextInjectAddress() + (n - 1) - ); + myTransactionQueue + .injectROM(0xea) + .setNextInjectAddress(myTransactionQueue.getNextInjectAddress() + n - 1); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -123,6 +123,11 @@ CortexM0::err_t VcsLib::fetch16(uInt32 address, uInt16& value, uInt8& op, Cortex uInt32 arg; CortexM0::err_t err; + if (myTransactionQueue.size() >= elfEnvironment::QUEUE_SIZE_LIMIT) + return CortexM0::errCustom(ERR_QUEUE_FULL); + + myTransactionQueue.setTimestamp(cortex.getCycles()); + switch (address) { case ADDR_MEMSET: err = memset(cortex.getRegister(0), cortex.getRegister(1), cortex.getRegister(3), cortex); @@ -151,7 +156,7 @@ CortexM0::err_t VcsLib::fetch16(uInt32 address, uInt16& value, uInt8& op, Cortex myTransactionQueue .injectROM(0x85) .injectROM(arg) - .stuffByte(arg, cortex.getRegister(1)); + .stuffByte(cortex.getRegister(1), arg); return returnFromStub(value, op); diff --git a/src/emucore/elf/VcsLib.hxx b/src/emucore/elf/VcsLib.hxx index 0e4f68365..e40c3f27f 100644 --- a/src/emucore/elf/VcsLib.hxx +++ b/src/emucore/elf/VcsLib.hxx @@ -44,9 +44,9 @@ class VcsLib: public CortexM0::BusTransactionDelegate { private: BusTransactionQueue& myTransactionQueue; - uInt8 myStuffMaskA{0xff}; - uInt8 myStuffMaskX{0xff}; - uInt8 myStuffMaskY{0xff}; + uInt8 myStuffMaskA{0x00}; + uInt8 myStuffMaskX{0x00}; + uInt8 myStuffMaskY{0x00}; private: VcsLib(const VcsLib&) = delete;