mirror of https://github.com/stella-emu/stella.git
Sync timing, limit the amount of queued transactions.
This commit is contained in:
parent
4bcc8056b9
commit
ba75f72826
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue