Sync timing, limit the amount of queued transactions.

This commit is contained in:
Christian Speckner 2024-07-29 09:30:51 +02:00
parent 4bcc8056b9
commit ba75f72826
9 changed files with 94 additions and 39 deletions

View File

@ -25,6 +25,7 @@
#include "ElfParser.hxx" #include "ElfParser.hxx"
#include "ElfLinker.hxx" #include "ElfLinker.hxx"
#include "ElfEnvironment.hxx" #include "ElfEnvironment.hxx"
#include "Logger.hxx"
#include "exception/FatalEmulationError.hxx" #include "exception/FatalEmulationError.hxx"
#include "CartELF.hxx" #include "CartELF.hxx"
@ -203,7 +204,7 @@ void CartridgeELF::reset()
std::fill_n(myLastPeekResult.get(), 0x1000, 0); std::fill_n(myLastPeekResult.get(), 0x1000, 0);
myIsBusDriven = false; myIsBusDriven = false;
myDriveBusValue = 0; myDriveBusValue = 0;
myArmCycles = 0; myArmCyclesOffset = 0;
std::memset(mySectionStack.get(), 0, STACK_SIZE); std::memset(mySectionStack.get(), 0, STACK_SIZE);
std::memset(mySectionText.get(), 0, TEXT_SIZE); std::memset(mySectionText.get(), 0, TEXT_SIZE);
@ -223,10 +224,12 @@ void CartridgeELF::reset()
myTransactionQueue myTransactionQueue
.reset() .reset()
.injectROM(0x00, 0x1ffc) .injectROMAt(0x00, 0x1ffc)
.injectROM(0x10) .injectROM(0x10)
.setNextInjectAddress(0x1000); .setNextInjectAddress(0x1000);
myVcsLib.reset();
myVcsLib.vcsCopyOverblankToRiotRam(); myVcsLib.vcsCopyOverblankToRiotRam();
myVcsLib.vcsStartOverblank(); myVcsLib.vcsStartOverblank();
myVcsLib.vcsEndOverblank(); myVcsLib.vcsEndOverblank();
@ -295,8 +298,6 @@ const ByteBuffer& CartridgeELF::getImage(size_t& size) const
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 CartridgeELF::overdrivePeek(uInt16 address, uInt8 value) uInt8 CartridgeELF::overdrivePeek(uInt16 address, uInt8 value)
{ {
runArm();
value = driveBus(address, value); value = driveBus(address, value);
if (address & 0x1000) { if (address & 0x1000) {
@ -310,22 +311,41 @@ uInt8 CartridgeELF::overdrivePeek(uInt16 address, uInt8 value)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 CartridgeELF::overdrivePoke(uInt16 address, uInt8 value) uInt8 CartridgeELF::overdrivePoke(uInt16 address, uInt8 value)
{ {
runArm();
return driveBus(address, value); return driveBus(address, value);
} }
inline uInt64 CartridgeELF::getArmCycles() const
{
return myCortexEmu.getCycles() + myArmCyclesOffset;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
inline uInt8 CartridgeELF::driveBus(uInt16 address, uInt8 value) inline uInt8 CartridgeELF::driveBus(uInt16 address, uInt8 value)
{ {
auto* nextTransaction = myTransactionQueue.getNextTransaction(address); auto* nextTransaction = myTransactionQueue.getNextTransaction(address);
if (nextTransaction) nextTransaction->setBusState(myIsBusDriven, myDriveBusValue); if (nextTransaction) {
nextTransaction->setBusState(myIsBusDriven, myDriveBusValue);
syncClock(*nextTransaction);
}
if (myIsBusDriven) value |= myDriveBusValue; if (myIsBusDriven) value |= myDriveBusValue;
runArm();
return value; 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() void CartridgeELF::parseAndLinkElf()
{ {
@ -461,16 +481,20 @@ void CartridgeELF::jumpToMain()
void CartridgeELF::runArm() 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 = const uInt32 cyclesGoal =
(mySystem->cycles() + ARM_RUNAHED_MAX) * myArmCyclesPer6502Cycle - myArmCycles; (mySystem->cycles() + ARM_RUNAHED_MAX) * myArmCyclesPer6502Cycle - getArmCycles();
uInt32 cycles; uInt32 cycles;
const CortexM0::err_t err = myCortexEmu.run(cyclesGoal, 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));
} }

View File

@ -66,7 +66,10 @@ class CartridgeELF: public Cartridge {
bool doesBusStuffing() override { return true; } bool doesBusStuffing() override { return true; }
private: private:
uInt64 getArmCycles() const;
uInt8 driveBus(uInt16 address, uInt8 value); uInt8 driveBus(uInt16 address, uInt8 value);
void syncClock(const BusTransactionQueue::Transaction& transaction);
void parseAndLinkElf(); void parseAndLinkElf();
void setupMemoryMap(); void setupMemoryMap();
@ -105,7 +108,7 @@ class CartridgeELF: public Cartridge {
ConsoleTiming myConsoleTiming{ConsoleTiming::ntsc}; ConsoleTiming myConsoleTiming{ConsoleTiming::ntsc};
uInt32 myArmCyclesPer6502Cycle{80}; uInt32 myArmCyclesPer6502Cycle{80};
uInt64 myArmCycles{0}; Int64 myArmCyclesOffset{0};
}; };
#endif // CARTRIDGE_ELF #endif // CARTRIDGE_ELF

View File

@ -578,6 +578,7 @@ CortexM0& CortexM0::reset()
{ {
reg_norm.fill(0); reg_norm.fill(0);
znFlags = cFlag = vFlag = 0; znFlags = cFlag = vFlag = 0;
myCycleCounter = 0;
recompileCodeRegions(); recompileCodeRegions();
@ -608,7 +609,7 @@ uInt8 CortexM0::decodeInstructionWord(uInt16 instructionWord)
CortexM0::err_t CortexM0::run(uInt32 maxCycles, uInt32& cycles) 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); const uInt32 pc = read_register(15);
uInt16 inst; uInt16 inst;

View File

@ -120,6 +120,10 @@ class CortexM0
err_t write16(uInt32 address, uInt16 value); err_t write16(uInt32 address, uInt16 value);
err_t write8(uInt32 address, uInt8 value); err_t write8(uInt32 address, uInt8 value);
inline uInt64 getCycles() const {
return myCycleCounter;
}
private: private:
enum class MemoryRegionType : uInt8 { enum class MemoryRegionType : uInt8 {
@ -188,6 +192,8 @@ class CortexM0
uInt8 myNextRegionIndex{0}; uInt8 myNextRegionIndex{0};
BusTransactionDelegate* myDefaultDelegate{nullptr}; BusTransactionDelegate* myDefaultDelegate{nullptr};
uInt64 myCycleCounter{0};
static constexpr uInt32 static constexpr uInt32
CPSR_N = 1u << 31, CPSR_N = 1u << 31,
CPSR_Z = 1u << 30, CPSR_Z = 1u << 30,

View File

@ -20,17 +20,19 @@
#include "exception/FatalEmulationError.hxx" #include "exception/FatalEmulationError.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
BusTransactionQueue::Transaction BusTransactionQueue::Transaction::transactionYield(uInt16 address) BusTransactionQueue::Transaction BusTransactionQueue::Transaction::transactionYield(
{ uInt16 address, uInt64 timestamp
) {
address &= 0x1fff; 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; 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; myQueueNext = myQueueSize = 0;
myNextInjectAddress = 0; myNextInjectAddress = 0;
myTimestamp = 0;
return *this; return *this;
} }
@ -75,18 +78,25 @@ uInt16 BusTransactionQueue::getNextInjectAddress() const
return myNextInjectAddress; return myNextInjectAddress;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - BusTransactionQueue& BusTransactionQueue::setTimestamp(uInt64 timestamp)
BusTransactionQueue& BusTransactionQueue::injectROM(uInt8 value)
{ {
injectROM(value, myNextInjectAddress); myTimestamp = timestamp;
return *this; 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; myNextInjectAddress = address + 1;
return *this; return *this;
@ -94,7 +104,7 @@ BusTransactionQueue& BusTransactionQueue::injectROM(uInt8 value, uInt16 address)
BusTransactionQueue& BusTransactionQueue::stuffByte(uInt8 value, uInt16 address) BusTransactionQueue& BusTransactionQueue::stuffByte(uInt8 value, uInt16 address)
{ {
push(Transaction::transactionDrive(address, value)); push(Transaction::transactionDrive(address, value, myTimestamp));
return *this; return *this;
} }
@ -102,7 +112,7 @@ BusTransactionQueue& BusTransactionQueue::stuffByte(uInt8 value, uInt16 address)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
BusTransactionQueue& BusTransactionQueue::yield(uInt16 address) BusTransactionQueue& BusTransactionQueue::yield(uInt16 address)
{ {
push(Transaction::transactionYield(address)); push(Transaction::transactionYield(address, myTimestamp));
return *this; return *this;
} }

View File

@ -23,13 +23,14 @@
class BusTransactionQueue { class BusTransactionQueue {
public: public:
struct Transaction { struct Transaction {
static Transaction transactionYield(uInt16 address); static Transaction transactionYield(uInt16 address, uInt64 timestamp);
static Transaction transactionDrive(uInt16 address, uInt8 value); static Transaction transactionDrive(uInt16 address, uInt8 value, uInt64 timestamp);
void setBusState(bool& drive, uInt8& value) const; void setBusState(bool& drive, uInt8& value) const;
uInt16 address; uInt16 address;
uInt8 value; uInt8 value;
uInt64 timestamp;
bool yield; bool yield;
}; };
@ -41,8 +42,9 @@ class BusTransactionQueue {
BusTransactionQueue& setNextInjectAddress(uInt16 address); BusTransactionQueue& setNextInjectAddress(uInt16 address);
uInt16 getNextInjectAddress() const; uInt16 getNextInjectAddress() const;
BusTransactionQueue& setTimestamp(uInt64 timestamp);
BusTransactionQueue& injectROM(uInt8 value); BusTransactionQueue& injectROM(uInt8 value);
BusTransactionQueue& injectROM(uInt8 value, uInt16 address); BusTransactionQueue& injectROMAt(uInt8 value, uInt16 address);
BusTransactionQueue& stuffByte(uInt8 value, uInt16 address); BusTransactionQueue& stuffByte(uInt8 value, uInt16 address);
BusTransactionQueue& yield(uInt16 address); BusTransactionQueue& yield(uInt16 address);
@ -65,6 +67,7 @@ class BusTransactionQueue {
size_t myQueueSize{0}; size_t myQueueSize{0};
uInt16 myNextInjectAddress{0}; uInt16 myNextInjectAddress{0};
uInt64 myTimestamp{0};
private: private:
BusTransactionQueue(const BusTransactionQueue&) = delete; BusTransactionQueue(const BusTransactionQueue&) = delete;

View File

@ -112,6 +112,9 @@ namespace elfEnvironment {
constexpr uInt32 RETURN_ADDR_MAIN = 0xffffdead; constexpr uInt32 RETURN_ADDR_MAIN = 0xffffdead;
constexpr uInt32 ERR_QUEUE_FULL = 1;
constexpr uInt32 QUEUE_SIZE_LIMIT = 10;
enum class Palette: uInt8 {pal, ntsc}; enum class Palette: uInt8 {pal, ntsc};
vector<ElfLinker::ExternalSymbol> externalSymbols(Palette palette); vector<ElfLinker::ExternalSymbol> externalSymbols(Palette palette);

View File

@ -20,6 +20,7 @@
#include "BusTransactionQueue.hxx" #include "BusTransactionQueue.hxx"
#include "ElfEnvironment.hxx" #include "ElfEnvironment.hxx"
#include "exception/FatalEmulationError.hxx" #include "exception/FatalEmulationError.hxx"
#include "ElfEnvironment.hxx"
using namespace elfEnvironment; using namespace elfEnvironment;
@ -58,7 +59,7 @@ VcsLib::VcsLib(BusTransactionQueue& transactionQueue) : myTransactionQueue(trans
void VcsLib::reset() void VcsLib::reset()
{ {
myStuffMaskA = myStuffMaskX = myStuffMaskY = 0xff; myStuffMaskA = myStuffMaskX = myStuffMaskY = 0x00;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -93,7 +94,7 @@ void VcsLib::vcsStartOverblank()
void VcsLib::vcsEndOverblank() void VcsLib::vcsEndOverblank()
{ {
myTransactionQueue myTransactionQueue
.injectROM(0x00, 0x1fff) .injectROMAt(0x00, 0x1fff)
.yield(0x00ac) .yield(0x00ac)
.setNextInjectAddress(0x1000); .setNextInjectAddress(0x1000);
} }
@ -103,10 +104,9 @@ void VcsLib::vcsNop2n(uInt16 n)
{ {
if (n == 0) return; if (n == 0) return;
myTransactionQueue.injectROM(0xea); myTransactionQueue
myTransactionQueue.setNextInjectAddress( .injectROM(0xea)
myTransactionQueue.getNextInjectAddress() + (n - 1) .setNextInjectAddress(myTransactionQueue.getNextInjectAddress() + n - 1);
);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -123,6 +123,11 @@ CortexM0::err_t VcsLib::fetch16(uInt32 address, uInt16& value, uInt8& op, Cortex
uInt32 arg; uInt32 arg;
CortexM0::err_t err; CortexM0::err_t err;
if (myTransactionQueue.size() >= elfEnvironment::QUEUE_SIZE_LIMIT)
return CortexM0::errCustom(ERR_QUEUE_FULL);
myTransactionQueue.setTimestamp(cortex.getCycles());
switch (address) { switch (address) {
case ADDR_MEMSET: case ADDR_MEMSET:
err = memset(cortex.getRegister(0), cortex.getRegister(1), cortex.getRegister(3), cortex); 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 myTransactionQueue
.injectROM(0x85) .injectROM(0x85)
.injectROM(arg) .injectROM(arg)
.stuffByte(arg, cortex.getRegister(1)); .stuffByte(cortex.getRegister(1), arg);
return returnFromStub(value, op); return returnFromStub(value, op);

View File

@ -44,9 +44,9 @@ class VcsLib: public CortexM0::BusTransactionDelegate {
private: private:
BusTransactionQueue& myTransactionQueue; BusTransactionQueue& myTransactionQueue;
uInt8 myStuffMaskA{0xff}; uInt8 myStuffMaskA{0x00};
uInt8 myStuffMaskX{0xff}; uInt8 myStuffMaskX{0x00};
uInt8 myStuffMaskY{0xff}; uInt8 myStuffMaskY{0x00};
private: private:
VcsLib(const VcsLib&) = delete; VcsLib(const VcsLib&) = delete;