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 "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));
}

View File

@ -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

View File

@ -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;

View File

@ -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,

View File

@ -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;
}

View File

@ -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;

View File

@ -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<ElfLinker::ExternalSymbol> externalSymbols(Palette palette);

View File

@ -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);

View File

@ -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;