mirror of https://github.com/stella-emu/stella.git
Refactoring.
This commit is contained in:
parent
239c307f91
commit
54e65a9c03
|
@ -176,7 +176,8 @@ namespace {
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
CartridgeELF::CartridgeELF(const ByteBuffer& image, size_t size, string_view md5,
|
||||
const Settings& settings)
|
||||
: Cartridge(settings, md5), myImageSize(size), myVcslibDelegate(*this)
|
||||
: Cartridge(settings, md5), myImageSize(size), myTransactionQueue(TRANSACTION_QUEUE_CAPACITY),
|
||||
myVcsLib(myTransactionQueue)
|
||||
{
|
||||
myImage = make_unique<uInt8[]>(size);
|
||||
std::memcpy(myImage.get(), image.get(), size);
|
||||
|
@ -203,13 +204,14 @@ void CartridgeELF::reset()
|
|||
myIsBusDriven = false;
|
||||
myDriveBusValue = 0;
|
||||
|
||||
myTransactionQueue.reset();
|
||||
myTransactionQueue.injectROM(0x00, 0x1ffc);
|
||||
myTransactionQueue.injectROM(0x10);
|
||||
myTransactionQueue.setNextInjectAddress(0x1000);
|
||||
myTransactionQueue
|
||||
.reset()
|
||||
.injectROM(0x00, 0x1ffc)
|
||||
.injectROM(0x10)
|
||||
.setNextInjectAddress(0x1000);
|
||||
|
||||
vcsCopyOverblankToRiotRam();
|
||||
vcsStartOverblank();
|
||||
myVcsLib.vcsCopyOverblankToRiotRam();
|
||||
myVcsLib.vcsStartOverblank();
|
||||
|
||||
std::memset(mySectionStack.get(), 0, STACK_SIZE);
|
||||
std::memset(mySectionText.get(), 0, TEXT_SIZE);
|
||||
|
@ -299,7 +301,7 @@ uInt8 CartridgeELF::overdrivePoke(uInt16 address, uInt8 value)
|
|||
|
||||
inline uInt8 CartridgeELF::driveBus(uInt16 address, uInt8 value)
|
||||
{
|
||||
BusTransaction* nextTransaction = myTransactionQueue.getNextTransaction(address);
|
||||
auto* nextTransaction = myTransactionQueue.getNextTransaction(address);
|
||||
if (nextTransaction) nextTransaction->setBusState(myIsBusDriven, myDriveBusValue);
|
||||
|
||||
if (myIsBusDriven) value |= myDriveBusValue;
|
||||
|
@ -307,266 +309,6 @@ inline uInt8 CartridgeELF::driveBus(uInt16 address, uInt8 value)
|
|||
return value;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void CartridgeELF::vcsWrite5(uInt8 zpAddress, uInt8 value)
|
||||
{
|
||||
myTransactionQueue.injectROM(0xa9);
|
||||
myTransactionQueue.injectROM(value);
|
||||
myTransactionQueue.injectROM(0x85);
|
||||
myTransactionQueue.injectROM(zpAddress);
|
||||
myTransactionQueue.yield(zpAddress);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void CartridgeELF::vcsCopyOverblankToRiotRam()
|
||||
{
|
||||
for (uInt8 i = 0; i < OVERBLANK_PROGRAM_SIZE; i++)
|
||||
vcsWrite5(0x80 + i, OVERBLANK_PROGRAM[i]);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void CartridgeELF::vcsStartOverblank()
|
||||
{
|
||||
myTransactionQueue.injectROM(0x4c);
|
||||
myTransactionQueue.injectROM(0x80);
|
||||
myTransactionQueue.injectROM(0x00);
|
||||
myTransactionQueue.yield(0x0080);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
CartridgeELF::BusTransaction CartridgeELF::BusTransaction::transactionYield(uInt16 address)
|
||||
{
|
||||
address &= 0x1fff;
|
||||
return {.address = address, .value = 0, .yield = true};
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
CartridgeELF::BusTransaction CartridgeELF::BusTransaction::transactionDrive(uInt16 address, uInt8 value)
|
||||
{
|
||||
address &= 0x1fff;
|
||||
return {.address = address, .value = value, .yield = false};
|
||||
}
|
||||
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void CartridgeELF::BusTransaction::setBusState(bool& bs_drive, uInt8& bs_value) const
|
||||
{
|
||||
if (yield) {
|
||||
bs_drive = false;
|
||||
} else {
|
||||
bs_drive = true;
|
||||
bs_value = this->value;
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
CartridgeELF::BusTransactionQueue::BusTransactionQueue()
|
||||
{
|
||||
myQueue = make_unique<BusTransaction[]>(TRANSACTION_QUEUE_CAPACITY);
|
||||
reset();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void CartridgeELF::BusTransactionQueue::reset()
|
||||
{
|
||||
myQueueNext = myQueueSize = 0;
|
||||
myNextInjectAddress = 0;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void CartridgeELF::BusTransactionQueue::setNextInjectAddress(uInt16 address)
|
||||
{
|
||||
myNextInjectAddress = address;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void CartridgeELF::BusTransactionQueue::injectROM(uInt8 value)
|
||||
{
|
||||
injectROM(value, myNextInjectAddress);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void CartridgeELF::BusTransactionQueue::injectROM(uInt8 value, uInt16 address)
|
||||
{
|
||||
push(BusTransaction::transactionDrive(address, value));
|
||||
myNextInjectAddress = address + 1;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void CartridgeELF::BusTransactionQueue::yield(uInt16 address)
|
||||
{
|
||||
push(BusTransaction::transactionYield(address));
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool CartridgeELF::BusTransactionQueue::hasPendingTransaction() const
|
||||
{
|
||||
return myQueueSize > 0;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
CartridgeELF::BusTransaction* CartridgeELF::BusTransactionQueue::getNextTransaction(uInt16 address)
|
||||
{
|
||||
if (myQueueSize == 0) return nullptr;
|
||||
|
||||
BusTransaction* nextTransaction = &myQueue[myQueueNext];
|
||||
if (nextTransaction->address != (address & 0x1fff)) return nullptr;
|
||||
|
||||
myQueueNext = (myQueueNext + 1) % TRANSACTION_QUEUE_CAPACITY;
|
||||
myQueueSize--;
|
||||
|
||||
return nextTransaction;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void CartridgeELF::BusTransactionQueue::push(const BusTransaction& transaction)
|
||||
{
|
||||
if (myQueueSize > 0) {
|
||||
BusTransaction& lastTransaction = myQueue[(myQueueNext + myQueueSize - 1) % TRANSACTION_QUEUE_CAPACITY];
|
||||
|
||||
if (lastTransaction.address == transaction.address) {
|
||||
lastTransaction = transaction;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (myQueueSize == TRANSACTION_QUEUE_CAPACITY)
|
||||
throw FatalEmulationError("read stream overflow");
|
||||
|
||||
myQueue[(myQueueNext + myQueueSize++) % TRANSACTION_QUEUE_CAPACITY] = transaction;
|
||||
}
|
||||
|
||||
CortexM0::err_t CartridgeELF::VcslibDelegate::fetch16(uInt32 address, uInt16& value, uInt8& op, CortexM0& cortex)
|
||||
{
|
||||
switch (address) {
|
||||
case ADDR_MEMSET:
|
||||
FatalEmulationError::raise("unimplemented: memset");
|
||||
|
||||
case ADDR_MEMCPY:
|
||||
FatalEmulationError::raise("unimplemented: memcpy");
|
||||
|
||||
case ADDR_VCS_LDA_FOR_BUS_STUFF2:
|
||||
FatalEmulationError::raise("unimplemented: vcsLdaForBusStuff2");
|
||||
|
||||
case ADDR_VCS_LDX_FOR_BUS_STUFF2:
|
||||
FatalEmulationError::raise("unimplemented: vcsLdxForBusStuff2");
|
||||
|
||||
case ADDR_VCS_LDY_FOR_BUS_STUFF2:
|
||||
FatalEmulationError::raise("unimplemented: vcsLdyForBusStuff2");
|
||||
|
||||
case ADDR_VCS_WRITE3:
|
||||
FatalEmulationError::raise("unimplemented: vcsWrite3");
|
||||
|
||||
case ADDR_VCS_JMP3:
|
||||
FatalEmulationError::raise("unimplemented: vcsJump3");
|
||||
|
||||
case ADDR_VCS_NOP2:
|
||||
FatalEmulationError::raise("unimplemented: vcsNop2");
|
||||
|
||||
case ADDR_VCS_NOP2N:
|
||||
FatalEmulationError::raise("unimplemented: vcsNop2n");
|
||||
|
||||
case ADDR_VCS_WRITE5:
|
||||
FatalEmulationError::raise("unimplemented: vcsWrite5");
|
||||
|
||||
case ADDR_VCS_WRITE6:
|
||||
FatalEmulationError::raise("unimplemented: vcsWrite6");
|
||||
|
||||
case ADDR_VCS_LDA2:
|
||||
FatalEmulationError::raise("unimplemented: vcsLda2");
|
||||
|
||||
case ADDR_VCS_LDX2:
|
||||
FatalEmulationError::raise("unimplemented: vcsLdx2");
|
||||
|
||||
case ADDR_VCS_LDY2:
|
||||
FatalEmulationError::raise("unimplemented: vcsLdy2");
|
||||
|
||||
case ADDR_VCS_SAX3:
|
||||
FatalEmulationError::raise("unimplemented: vcsSax3");
|
||||
|
||||
case ADDR_VCS_STA3:
|
||||
FatalEmulationError::raise("unimplemented: vcsSta3");
|
||||
|
||||
case ADDR_VCS_STX3:
|
||||
FatalEmulationError::raise("unimplemented: vcsStx3");
|
||||
|
||||
case ADDR_VCS_STY3:
|
||||
FatalEmulationError::raise("unimplemented: vcsSty3");
|
||||
|
||||
case ADDR_VCS_STA4:
|
||||
FatalEmulationError::raise("unimplemented: vcsSta4");
|
||||
|
||||
case ADDR_VCS_STX4:
|
||||
FatalEmulationError::raise("unimplemented: vcsStx4");
|
||||
|
||||
case ADDR_VCS_STY4:
|
||||
FatalEmulationError::raise("unimplemented: vcsSty4");
|
||||
|
||||
case ADDR_VCS_COPY_OVERBLANK_TO_RIOT_RAM:
|
||||
myCart.vcsCopyOverblankToRiotRam();
|
||||
return returnFromStub(value, op);
|
||||
|
||||
case ADDR_VCS_START_OVERBLANK:
|
||||
myCart.vcsStartOverblank();
|
||||
return returnFromStub(value, op);
|
||||
|
||||
case ADDR_VCS_END_OVERBLANK:
|
||||
FatalEmulationError::raise("unimplemented: vcsEndOverblank");
|
||||
|
||||
case ADDR_VCS_READ4:
|
||||
FatalEmulationError::raise("unimplemented: vcsRead4");
|
||||
|
||||
case ADDR_RANDINT:
|
||||
FatalEmulationError::raise("unimplemented: randint ");
|
||||
|
||||
case ADDR_VCS_TXS2:
|
||||
FatalEmulationError::raise("unimplemented: vcsTx2");
|
||||
|
||||
case ADDR_VCS_JSR6:
|
||||
FatalEmulationError::raise("unimplemented: vcsJsr6");
|
||||
|
||||
case ADDR_VCS_PHA3:
|
||||
FatalEmulationError::raise("unimplemented: vcsPha3");
|
||||
|
||||
case ADDR_VCS_PHP3:
|
||||
FatalEmulationError::raise("unimplemented: vcsPph3");
|
||||
|
||||
case ADDR_VCS_PLA4:
|
||||
FatalEmulationError::raise("unimplemented: vcsPla4");
|
||||
|
||||
case ADDR_VCS_PLP4:
|
||||
FatalEmulationError::raise("unimplemented: vcsPlp4");
|
||||
|
||||
case ADDR_VCS_PLA4_EX:
|
||||
FatalEmulationError::raise("unimplemented: vcsPla4Ex");
|
||||
|
||||
case ADDR_VCS_PLP4_EX:
|
||||
FatalEmulationError::raise("unimplemented: vcsPlp4Ex");
|
||||
|
||||
case ADDR_VCS_JMP_TO_RAM3:
|
||||
FatalEmulationError::raise("unimplemented: vcsJmpToRam3");
|
||||
|
||||
case ADDR_VCS_WAIT_FOR_ADDRESS:
|
||||
FatalEmulationError::raise("unimplemented: vcsWaitForAddress");
|
||||
|
||||
case ADDR_INJECT_DMA_DATA:
|
||||
FatalEmulationError::raise("unimplemented: vcsInjectDmaData");
|
||||
|
||||
default:
|
||||
return CortexM0::errIntrinsic(CortexM0::ERR_UNMAPPED_FETCH16, address);
|
||||
}
|
||||
}
|
||||
|
||||
CortexM0::err_t CartridgeELF::VcslibDelegate::returnFromStub(uInt16& value, uInt8& op)
|
||||
{
|
||||
constexpr uInt16 BX_LR = 0x7047;
|
||||
|
||||
value = BX_LR;
|
||||
op = CortexM0::decodeInstructionWord(BX_LR);
|
||||
|
||||
return CortexM0::ERR_NONE;
|
||||
}
|
||||
|
||||
void CartridgeELF::parseAndLinkElf()
|
||||
{
|
||||
ElfParser elfParser;
|
||||
|
@ -635,7 +377,7 @@ void CartridgeELF::setupMemoryMap()
|
|||
.mapRegionData(ADDR_TABLES_BASE / CortexM0::PAGE_SIZE,
|
||||
TABLES_SIZE / CortexM0::PAGE_SIZE, true, mySectionTables.get())
|
||||
.mapRegionDelegate(ADDR_STUB_BASE / CortexM0::PAGE_SIZE,
|
||||
STUB_SIZE / CortexM0::PAGE_SIZE, true, &myVcslibDelegate);
|
||||
STUB_SIZE / CortexM0::PAGE_SIZE, true, &myVcsLib);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#include "bspf.hxx"
|
||||
#include "Cart.hxx"
|
||||
#include "CortexM0.hxx"
|
||||
#include "BusTransactionQueue.hxx"
|
||||
#include "VcsLib.hxx"
|
||||
|
||||
class ElfLinker;
|
||||
|
||||
|
@ -64,63 +66,6 @@ class CartridgeELF: public Cartridge {
|
|||
private:
|
||||
uInt8 driveBus(uInt16 address, uInt8 value);
|
||||
|
||||
void vcsWrite5(uInt8 zpAddress, uInt8 value);
|
||||
void vcsCopyOverblankToRiotRam();
|
||||
void vcsStartOverblank();
|
||||
|
||||
private:
|
||||
struct BusTransaction {
|
||||
static BusTransaction transactionYield(uInt16 address);
|
||||
static BusTransaction transactionDrive(uInt16 address, uInt8 value);
|
||||
|
||||
void setBusState(bool& drive, uInt8& value) const;
|
||||
|
||||
uInt16 address;
|
||||
uInt8 value;
|
||||
bool yield;
|
||||
};
|
||||
|
||||
class BusTransactionQueue {
|
||||
public:
|
||||
BusTransactionQueue();
|
||||
|
||||
void reset();
|
||||
|
||||
void setNextInjectAddress(uInt16 address);
|
||||
void injectROM(uInt8 value);
|
||||
void injectROM(uInt8 value, uInt16 address);
|
||||
|
||||
void yield(uInt16 address);
|
||||
|
||||
bool hasPendingTransaction() const;
|
||||
BusTransaction* getNextTransaction(uInt16 address);
|
||||
|
||||
private:
|
||||
void push(const BusTransaction& transaction);
|
||||
|
||||
private:
|
||||
unique_ptr<BusTransaction[]> myQueue;
|
||||
size_t myQueueNext{0};
|
||||
size_t myQueueSize{0};
|
||||
|
||||
uInt16 myNextInjectAddress{0};
|
||||
};
|
||||
|
||||
class VcslibDelegate: public CortexM0::BusTransactionDelegate {
|
||||
public:
|
||||
VcslibDelegate(CartridgeELF& cart) : myCart(cart) {}
|
||||
|
||||
CortexM0::err_t fetch16(uInt32 address, uInt16& value, uInt8& op, CortexM0& cortex) override;
|
||||
|
||||
private:
|
||||
CortexM0::err_t returnFromStub(uInt16& value, uInt8& op);
|
||||
|
||||
private:
|
||||
CartridgeELF& myCart;
|
||||
};
|
||||
|
||||
friend VcslibDelegate;
|
||||
|
||||
private:
|
||||
void parseAndLinkElf();
|
||||
void setupMemoryMap();
|
||||
|
@ -148,7 +93,7 @@ class CartridgeELF: public Cartridge {
|
|||
unique_ptr<uInt8[]> mySectionRodata;
|
||||
unique_ptr<uInt8[]> mySectionTables;
|
||||
|
||||
VcslibDelegate myVcslibDelegate;
|
||||
VcsLib myVcsLib;
|
||||
};
|
||||
|
||||
#endif // CARTRIDGE_ELF
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
//============================================================================
|
||||
//
|
||||
// SSSS tt lll lll
|
||||
// SS SS tt ll ll
|
||||
// SS tttttt eeee ll ll aaaa
|
||||
// SSSS tt ee ee ll ll aa
|
||||
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
||||
// SS SS tt ee ll ll aa aa
|
||||
// SSSS ttt eeeee llll llll aaaaa
|
||||
//
|
||||
// Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
|
||||
// and the Stella Team
|
||||
//
|
||||
// See the file "License.txt" for information on usage and redistribution of
|
||||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
//============================================================================
|
||||
|
||||
#include "BusTransactionQueue.hxx"
|
||||
|
||||
#include "exception/FatalEmulationError.hxx"
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
BusTransactionQueue::Transaction BusTransactionQueue::Transaction::transactionYield(uInt16 address)
|
||||
{
|
||||
address &= 0x1fff;
|
||||
return {.address = address, .value = 0, .yield = true};
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
BusTransactionQueue::Transaction BusTransactionQueue::Transaction::transactionDrive(uInt16 address, uInt8 value)
|
||||
{
|
||||
address &= 0x1fff;
|
||||
return {.address = address, .value = value, .yield = false};
|
||||
}
|
||||
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void BusTransactionQueue::Transaction::setBusState(bool& bs_drive, uInt8& bs_value) const
|
||||
{
|
||||
if (yield) {
|
||||
bs_drive = false;
|
||||
} else {
|
||||
bs_drive = true;
|
||||
bs_value = this->value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
BusTransactionQueue::BusTransactionQueue(size_t capacity) : myQueueCapacity(capacity)
|
||||
{
|
||||
myQueue = make_unique<Transaction[]>(myQueueCapacity);
|
||||
reset();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
BusTransactionQueue& BusTransactionQueue::reset()
|
||||
{
|
||||
myQueueNext = myQueueSize = 0;
|
||||
myNextInjectAddress = 0;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
BusTransactionQueue& BusTransactionQueue::setNextInjectAddress(uInt16 address)
|
||||
{
|
||||
myNextInjectAddress = address;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
BusTransactionQueue& BusTransactionQueue::injectROM(uInt8 value)
|
||||
{
|
||||
injectROM(value, myNextInjectAddress);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
BusTransactionQueue& BusTransactionQueue::injectROM(uInt8 value, uInt16 address)
|
||||
{
|
||||
push(Transaction::transactionDrive(address, value));
|
||||
myNextInjectAddress = address + 1;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
BusTransactionQueue& BusTransactionQueue::yield(uInt16 address)
|
||||
{
|
||||
push(Transaction::transactionYield(address));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool BusTransactionQueue::hasPendingTransaction() const
|
||||
{
|
||||
return myQueueSize > 0;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
BusTransactionQueue::Transaction* BusTransactionQueue::getNextTransaction(uInt16 address)
|
||||
{
|
||||
if (myQueueSize == 0) return nullptr;
|
||||
|
||||
Transaction* nextTransaction = &myQueue[myQueueNext];
|
||||
if (nextTransaction->address != (address & 0x1fff)) return nullptr;
|
||||
|
||||
myQueueNext = (myQueueNext + 1) % myQueueCapacity;
|
||||
myQueueSize--;
|
||||
|
||||
return nextTransaction;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void BusTransactionQueue::push(const Transaction& transaction)
|
||||
{
|
||||
if (myQueueSize > 0) {
|
||||
Transaction& lastTransaction = myQueue[(myQueueNext + myQueueSize - 1) % myQueueCapacity];
|
||||
|
||||
if (lastTransaction.address == transaction.address) {
|
||||
lastTransaction = transaction;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (myQueueSize == myQueueCapacity)
|
||||
throw FatalEmulationError("read stream overflow");
|
||||
|
||||
myQueue[(myQueueNext + myQueueSize++) % myQueueCapacity] = transaction;
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
//============================================================================
|
||||
//
|
||||
// SSSS tt lll lll
|
||||
// SS SS tt ll ll
|
||||
// SS tttttt eeee ll ll aaaa
|
||||
// SSSS tt ee ee ll ll aa
|
||||
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
||||
// SS SS tt ee ll ll aa aa
|
||||
// SSSS ttt eeeee llll llll aaaaa
|
||||
//
|
||||
// Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
|
||||
// and the Stella Team
|
||||
//
|
||||
// See the file "License.txt" for information on usage and redistribution of
|
||||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
//============================================================================
|
||||
|
||||
#ifndef BUS_TRANSACTION_QUEUE
|
||||
#define BUS_TRANSACTION_QUEUE
|
||||
|
||||
#include "bspf.hxx"
|
||||
|
||||
class BusTransactionQueue {
|
||||
public:
|
||||
struct Transaction {
|
||||
static Transaction transactionYield(uInt16 address);
|
||||
static Transaction transactionDrive(uInt16 address, uInt8 value);
|
||||
|
||||
void setBusState(bool& drive, uInt8& value) const;
|
||||
|
||||
uInt16 address;
|
||||
uInt8 value;
|
||||
bool yield;
|
||||
};
|
||||
|
||||
public:
|
||||
explicit BusTransactionQueue(size_t capacity);
|
||||
|
||||
BusTransactionQueue& reset();
|
||||
|
||||
BusTransactionQueue& setNextInjectAddress(uInt16 address);
|
||||
BusTransactionQueue& injectROM(uInt8 value);
|
||||
BusTransactionQueue& injectROM(uInt8 value, uInt16 address);
|
||||
|
||||
BusTransactionQueue& yield(uInt16 address);
|
||||
|
||||
bool hasPendingTransaction() const;
|
||||
Transaction* getNextTransaction(uInt16 address);
|
||||
|
||||
private:
|
||||
void push(const Transaction& transaction);
|
||||
|
||||
private:
|
||||
const size_t myQueueCapacity;
|
||||
|
||||
unique_ptr<Transaction[]> myQueue;
|
||||
size_t myQueueNext{0};
|
||||
size_t myQueueSize{0};
|
||||
|
||||
uInt16 myNextInjectAddress{0};
|
||||
|
||||
private:
|
||||
BusTransactionQueue(const BusTransactionQueue&) = delete;
|
||||
BusTransactionQueue(BusTransactionQueue&&) = delete;
|
||||
BusTransactionQueue& operator=(const BusTransactionQueue&) = delete;
|
||||
BusTransactionQueue& operator=(BusTransactionQueue&&) = delete;
|
||||
};
|
||||
|
||||
#endif // BUS_TRANSACTION_QUEUE
|
|
@ -0,0 +1,189 @@
|
|||
//============================================================================
|
||||
//
|
||||
// SSSS tt lll lll
|
||||
// SS SS tt ll ll
|
||||
// SS tttttt eeee ll ll aaaa
|
||||
// SSSS tt ee ee ll ll aa
|
||||
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
||||
// SS SS tt ee ll ll aa aa
|
||||
// SSSS ttt eeeee llll llll aaaaa
|
||||
//
|
||||
// Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
|
||||
// and the Stella Team
|
||||
//
|
||||
// See the file "License.txt" for information on usage and redistribution of
|
||||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
//============================================================================
|
||||
|
||||
#include "VcsLib.hxx"
|
||||
|
||||
#include "BusTransactionQueue.hxx"
|
||||
#include "ElfEnvironment.hxx"
|
||||
#include "exception/FatalEmulationError.hxx"
|
||||
|
||||
using namespace elfEnvironment;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
VcsLib::VcsLib(BusTransactionQueue& transactionQueue) : myTransactionQueue(transactionQueue)
|
||||
{}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void VcsLib::vcsWrite5(uInt8 zpAddress, uInt8 value)
|
||||
{
|
||||
myTransactionQueue
|
||||
.injectROM(0xa9)
|
||||
.injectROM(value)
|
||||
.injectROM(0x85)
|
||||
.injectROM(zpAddress)
|
||||
.yield(zpAddress);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void VcsLib::vcsCopyOverblankToRiotRam()
|
||||
{
|
||||
for (uInt8 i = 0; i < OVERBLANK_PROGRAM_SIZE; i++)
|
||||
vcsWrite5(0x80 + i, OVERBLANK_PROGRAM[i]);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void VcsLib::vcsStartOverblank()
|
||||
{
|
||||
myTransactionQueue
|
||||
.injectROM(0x4c)
|
||||
.injectROM(0x80)
|
||||
.injectROM(0x00)
|
||||
.yield(0x0080);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
CortexM0::err_t VcsLib::fetch16(uInt32 address, uInt16& value, uInt8& op, CortexM0& cortex)
|
||||
{
|
||||
switch (address) {
|
||||
case ADDR_MEMSET:
|
||||
FatalEmulationError::raise("unimplemented: memset");
|
||||
|
||||
case ADDR_MEMCPY:
|
||||
FatalEmulationError::raise("unimplemented: memcpy");
|
||||
|
||||
case ADDR_VCS_LDA_FOR_BUS_STUFF2:
|
||||
FatalEmulationError::raise("unimplemented: vcsLdaForBusStuff2");
|
||||
|
||||
case ADDR_VCS_LDX_FOR_BUS_STUFF2:
|
||||
FatalEmulationError::raise("unimplemented: vcsLdxForBusStuff2");
|
||||
|
||||
case ADDR_VCS_LDY_FOR_BUS_STUFF2:
|
||||
FatalEmulationError::raise("unimplemented: vcsLdyForBusStuff2");
|
||||
|
||||
case ADDR_VCS_WRITE3:
|
||||
FatalEmulationError::raise("unimplemented: vcsWrite3");
|
||||
|
||||
case ADDR_VCS_JMP3:
|
||||
FatalEmulationError::raise("unimplemented: vcsJump3");
|
||||
|
||||
case ADDR_VCS_NOP2:
|
||||
FatalEmulationError::raise("unimplemented: vcsNop2");
|
||||
|
||||
case ADDR_VCS_NOP2N:
|
||||
FatalEmulationError::raise("unimplemented: vcsNop2n");
|
||||
|
||||
case ADDR_VCS_WRITE5:
|
||||
FatalEmulationError::raise("unimplemented: vcsWrite5");
|
||||
|
||||
case ADDR_VCS_WRITE6:
|
||||
FatalEmulationError::raise("unimplemented: vcsWrite6");
|
||||
|
||||
case ADDR_VCS_LDA2:
|
||||
FatalEmulationError::raise("unimplemented: vcsLda2");
|
||||
|
||||
case ADDR_VCS_LDX2:
|
||||
FatalEmulationError::raise("unimplemented: vcsLdx2");
|
||||
|
||||
case ADDR_VCS_LDY2:
|
||||
FatalEmulationError::raise("unimplemented: vcsLdy2");
|
||||
|
||||
case ADDR_VCS_SAX3:
|
||||
FatalEmulationError::raise("unimplemented: vcsSax3");
|
||||
|
||||
case ADDR_VCS_STA3:
|
||||
FatalEmulationError::raise("unimplemented: vcsSta3");
|
||||
|
||||
case ADDR_VCS_STX3:
|
||||
FatalEmulationError::raise("unimplemented: vcsStx3");
|
||||
|
||||
case ADDR_VCS_STY3:
|
||||
FatalEmulationError::raise("unimplemented: vcsSty3");
|
||||
|
||||
case ADDR_VCS_STA4:
|
||||
FatalEmulationError::raise("unimplemented: vcsSta4");
|
||||
|
||||
case ADDR_VCS_STX4:
|
||||
FatalEmulationError::raise("unimplemented: vcsStx4");
|
||||
|
||||
case ADDR_VCS_STY4:
|
||||
FatalEmulationError::raise("unimplemented: vcsSty4");
|
||||
|
||||
case ADDR_VCS_COPY_OVERBLANK_TO_RIOT_RAM:
|
||||
vcsCopyOverblankToRiotRam();
|
||||
return returnFromStub(value, op);
|
||||
|
||||
case ADDR_VCS_START_OVERBLANK:
|
||||
vcsStartOverblank();
|
||||
return returnFromStub(value, op);
|
||||
|
||||
case ADDR_VCS_END_OVERBLANK:
|
||||
FatalEmulationError::raise("unimplemented: vcsEndOverblank");
|
||||
|
||||
case ADDR_VCS_READ4:
|
||||
FatalEmulationError::raise("unimplemented: vcsRead4");
|
||||
|
||||
case ADDR_RANDINT:
|
||||
FatalEmulationError::raise("unimplemented: randint ");
|
||||
|
||||
case ADDR_VCS_TXS2:
|
||||
FatalEmulationError::raise("unimplemented: vcsTx2");
|
||||
|
||||
case ADDR_VCS_JSR6:
|
||||
FatalEmulationError::raise("unimplemented: vcsJsr6");
|
||||
|
||||
case ADDR_VCS_PHA3:
|
||||
FatalEmulationError::raise("unimplemented: vcsPha3");
|
||||
|
||||
case ADDR_VCS_PHP3:
|
||||
FatalEmulationError::raise("unimplemented: vcsPph3");
|
||||
|
||||
case ADDR_VCS_PLA4:
|
||||
FatalEmulationError::raise("unimplemented: vcsPla4");
|
||||
|
||||
case ADDR_VCS_PLP4:
|
||||
FatalEmulationError::raise("unimplemented: vcsPlp4");
|
||||
|
||||
case ADDR_VCS_PLA4_EX:
|
||||
FatalEmulationError::raise("unimplemented: vcsPla4Ex");
|
||||
|
||||
case ADDR_VCS_PLP4_EX:
|
||||
FatalEmulationError::raise("unimplemented: vcsPlp4Ex");
|
||||
|
||||
case ADDR_VCS_JMP_TO_RAM3:
|
||||
FatalEmulationError::raise("unimplemented: vcsJmpToRam3");
|
||||
|
||||
case ADDR_VCS_WAIT_FOR_ADDRESS:
|
||||
FatalEmulationError::raise("unimplemented: vcsWaitForAddress");
|
||||
|
||||
case ADDR_INJECT_DMA_DATA:
|
||||
FatalEmulationError::raise("unimplemented: vcsInjectDmaData");
|
||||
|
||||
default:
|
||||
return CortexM0::errIntrinsic(CortexM0::ERR_UNMAPPED_FETCH16, address);
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
CortexM0::err_t VcsLib::returnFromStub(uInt16& value, uInt8& op)
|
||||
{
|
||||
constexpr uInt16 BX_LR = 0x7047;
|
||||
|
||||
value = BX_LR;
|
||||
op = CortexM0::decodeInstructionWord(BX_LR);
|
||||
|
||||
return CortexM0::ERR_NONE;
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
//============================================================================
|
||||
//
|
||||
// SSSS tt lll lll
|
||||
// SS SS tt ll ll
|
||||
// SS tttttt eeee ll ll aaaa
|
||||
// SSSS tt ee ee ll ll aa
|
||||
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
||||
// SS SS tt ee ll ll aa aa
|
||||
// SSSS ttt eeeee llll llll aaaaa
|
||||
//
|
||||
// Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
|
||||
// and the Stella Team
|
||||
//
|
||||
// See the file "License.txt" for information on usage and redistribution of
|
||||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
//============================================================================
|
||||
|
||||
#ifndef VCSLIB_H
|
||||
#define VCSLIB_H
|
||||
|
||||
#include "bspf.hxx"
|
||||
#include "CortexM0.hxx"
|
||||
|
||||
class BusTransactionQueue;
|
||||
|
||||
class VcsLib: public CortexM0::BusTransactionDelegate {
|
||||
public:
|
||||
explicit VcsLib(BusTransactionQueue& transactionQueue);
|
||||
|
||||
CortexM0::err_t fetch16(uInt32 address, uInt16& value, uInt8& op, CortexM0& cortex) override;
|
||||
|
||||
void vcsWrite5(uInt8 zpAddress, uInt8 value);
|
||||
void vcsCopyOverblankToRiotRam();
|
||||
void vcsStartOverblank();
|
||||
|
||||
private:
|
||||
CortexM0::err_t returnFromStub(uInt16& value, uInt8& op);
|
||||
|
||||
private:
|
||||
BusTransactionQueue& myTransactionQueue;
|
||||
|
||||
private:
|
||||
VcsLib(const VcsLib&) = delete;
|
||||
VcsLib(VcsLib&&) = delete;
|
||||
const VcsLib& operator=(const VcsLib&) = delete;
|
||||
const VcsLib& operator=(VcsLib&&) = delete;
|
||||
};
|
||||
|
||||
#endif // VCSLIB_H
|
|
@ -4,7 +4,9 @@ MODULE_OBJS = \
|
|||
src/emucore/elf/ElfParser.o \
|
||||
src/emucore/elf/ElfLinker.o \
|
||||
src/emucore/elf/ElfUtil.o \
|
||||
src/emucore/elf/ElfEnvironment.o
|
||||
src/emucore/elf/ElfEnvironment.o \
|
||||
src/emucore/elf/BusTransactionQueue.o \
|
||||
src/emucore/elf/VcsLib.o
|
||||
|
||||
MODULE_TEST_OBJS = \
|
||||
src/emucore/elf/ElfUtil.o \
|
||||
|
|
|
@ -695,6 +695,8 @@
|
|||
<ClCompile Include="..\..\emucore\elf\ElfLinker.cxx" />
|
||||
<ClCompile Include="..\..\emucore\elf\ElfUtil.cxx" />
|
||||
<ClCompile Include="..\..\emucore\elf\ElfEnvironment.cxx" />
|
||||
<ClCompile Include="..\..\emucore\elf\BusTransactionQueue.cxx" />
|
||||
<ClCompile Include="..\..\emucore\elf\VcsLib.cxx" />
|
||||
<ClCompile Include="..\..\emucore\CortexM0.cxx" />
|
||||
<ClCompile Include="..\..\emucore\EmulationTiming.cxx" />
|
||||
<ClCompile Include="..\..\emucore\EmulationWorker.cxx" />
|
||||
|
@ -1671,6 +1673,8 @@
|
|||
<ClInclude Include="..\..\emucore\elf\ElfLinker.hxx" />
|
||||
<ClInclude Include="..\..\emucore\elf\ElfUtil.hxx" />
|
||||
<ClInclude Include="..\..\emucore\elf\ElfEnvironment.hxx" />
|
||||
<ClCompile Include="..\..\emucore\elf\BusTransactionQueue.hxx" />
|
||||
<ClCompile Include="..\..\emucore\elf\VcsLib.hxx" />
|
||||
<ClInclude Include="..\..\emucore\CortexM0.hxx" />
|
||||
<ClInclude Include="..\..\emucore\EmulationTiming.hxx" />
|
||||
<ClInclude Include="..\..\emucore\EmulationWorker.hxx" />
|
||||
|
|
|
@ -1260,6 +1260,12 @@
|
|||
<ClCompile Include="..\..\emucore\elf\ElfEnvironment.cxx">
|
||||
<Filter>Source Files\emucore\elf</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\emucore\elf\BusTransactionCode.cxx">
|
||||
<Filter>Source Files\emucore\elf</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\emucore\elf\VcsLib.cxx">
|
||||
<Filter>Source Files\emucore\elf</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\emucore\AtariVox.hxx">
|
||||
|
@ -2552,6 +2558,12 @@
|
|||
<ClInclude Include="..\..\emucore\elf\ElfEnvironment.hxx">
|
||||
<Filter>Header Files\emucore\elf</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\emucore\elf\BusTransactionCode.hxx">
|
||||
<Filter>Source Files\emucore\elf</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\emucore\elf\VcsLib.hxx">
|
||||
<Filter>Source Files\emucore\elf</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="stella.ico">
|
||||
|
|
Loading…
Reference in New Issue