refactored CartWD

fixed CartEnhanced for multi-segment ROMs with extra RAM
improved CartEnhanced by enabling directPoke for extra RAM
This commit is contained in:
thrust26 2020-04-17 13:43:49 +02:00
parent 0851a445e5
commit 0bf12045d8
5 changed files with 60 additions and 291 deletions

View File

@ -29,44 +29,17 @@ Cartridge3EPlus::Cartridge3EPlus(const ByteBuffer& image, size_t size,
myRamSize = RAM_SIZE;
myRamBankCount = RAM_BANKS;
myRamWpHigh = RAM_HIGH_WP;
//// Allocate array for the ROM image
//myImage = make_unique<uInt8[]>(mySize);
//// Copy the ROM image into my buffer
//std::copy_n(image.get(), mySize, myImage.get());
//createRomAccessArrays(mySize + myRAM.size());
}
//// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//void Cartridge3EPlus::reset()
//{
// initializeRAM(myRAM.data(), myRAM.size());
//
// // Remember startup bank (0 per spec, rather than last per 3E scheme).
// // Set this to go to 3rd 1K Bank.
// initializeStartBank(0);
//
// // Initialise bank values for all ROM/RAM access
// // This is used to reverse-lookup from address to bank location
// for(auto& b: bankInUse)
// b = BANK_UNDEFINED; // bank is undefined and inaccessible!
//
// initializeBankState();
//
// // We'll map the startup banks 0 and 3 from the image into the third 1K bank upon reset
// bankROM((0 << BANK_BITS) | 0);
// bankROM((3 << BANK_BITS) | 0);
//}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Cartridge3EPlus::reset()
{
CartridgeEnhanced::reset();
// 1st segment in mapped to start bank in CartridgeEnhanced
bank(mySystem->randGenerator().next() % romBankCount(), 1);
bank(mySystem->randGenerator().next() % romBankCount(), 2);
bank(startBank(), 3); // Stella reads the PC vector always from here (TODO?)
bank(startBank(), 3);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -68,6 +68,8 @@ void CartridgeEnhanced::install(System& system)
for(uInt16 addr = ROM_OFFSET + myWriteOffset; addr < ROM_OFFSET + myWriteOffset + myRamSize; addr += System::PAGE_SIZE)
{
uInt16 offset = addr & myRamMask;
access.directPokeBase = &myRAM[offset];
access.romAccessBase = &myRomAccessBase[myWriteOffset + offset];
access.romPeekCounter = &myRomAccessCounter[myWriteOffset + offset];
access.romPokeCounter = &myRomAccessCounter[myWriteOffset + offset + myAccessSize];
@ -76,6 +78,7 @@ void CartridgeEnhanced::install(System& system)
// Set the page accessing method for the RAM reading pages
access.type = System::PageAccessType::READ;
access.directPokeBase = nullptr;
for(uInt16 addr = ROM_OFFSET + myReadOffset; addr < ROM_OFFSET + myReadOffset + myRamSize; addr += System::PAGE_SIZE)
{
uInt16 offset = addr & myRamMask;
@ -125,7 +128,7 @@ uInt8 CartridgeEnhanced::peek(uInt16 address)
}
else
{
address &= myBankMask;
address &= ROM_MASK;
// Write port is e.g. at 0xF000 - 0xF07F (128 bytes)
if(address < myReadOffset + myRamSize && address >= myReadOffset)
@ -133,7 +136,8 @@ uInt8 CartridgeEnhanced::peek(uInt16 address)
// Reading from the write port triggers an unwanted write
return peekRAM(myRAM[address], peekAddress);
else
return myImage[myCurrentSegOffset[(peekAddress & ROM_MASK) >> myBankShift] + address];
return myImage[myCurrentSegOffset[(peekAddress & ROM_MASK) >> myBankShift]
+ (peekAddress & myBankMask)];
}
}
@ -206,7 +210,8 @@ bool CartridgeEnhanced::bank(uInt16 bank, uInt16 segment)
uInt32 bankOffset = myCurrentSegOffset[segment] = romBank << myBankShift;
uInt16 hotspot = this->hotspot();
uInt16 hotSpotAddr;
uInt16 fromAddr = (ROM_OFFSET + segmentOffset + myRomOffset) & ~System::PAGE_MASK;
// Skip extra RAM; if existing it is only mapped into first segment
uInt16 fromAddr = (ROM_OFFSET + segmentOffset + (segment == 0 ? myRomOffset : 0)) & ~System::PAGE_MASK;
// for ROMs < 4_KB, the whole address space will be mapped.
uInt16 toAddr = (ROM_OFFSET + segmentOffset + (mySize < 4_KB ? 4_KB : myBankSize)) & ~System::PAGE_MASK;

View File

@ -65,7 +65,7 @@ class CartridgeEnhanced : public Cartridge
@return true, if bank has changed
*/
bool bank(uInt16 bank, uInt16 segment);
virtual bool bank(uInt16 bank, uInt16 segment);
/**
Install pages for the specified bank in the system.

View File

@ -23,70 +23,46 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CartridgeWD::CartridgeWD(const ByteBuffer& image, size_t size,
const string& md5, const Settings& settings)
: Cartridge(settings, md5),
mySize(std::min<size_t>(8_KB + 3, size))
: CartridgeEnhanced(image, size, md5, settings)
{
// Copy the ROM image into my buffer
if (mySize == 8_KB + 3)
{
// swap slices 2 & 3
std::copy_n(image.get(), 1_KB * 2, myImage.begin());
std::copy_n(image.get() + 1_KB * 3, 1_KB * 1, myImage.begin() + 1_KB * 2);
std::copy_n(image.get() + 1_KB * 2, 1_KB * 1, myImage.begin() + 1_KB * 3);
std::copy_n(image.get() + 1_KB * 4, 1_KB * 4, myImage.begin() + 1_KB * 4);
// swap slices 2 & 3 of bad dump and correct size
std::copy_n(image.get() + 1_KB * 3, 1_KB * 1, myImage.get() + 1_KB * 2);
std::copy_n(image.get() + 1_KB * 2, 1_KB * 1, myImage.get() + 1_KB * 3);
mySize = 8_KB;
}
else
std::copy_n(image.get(), mySize, myImage.begin());
createRomAccessArrays(8_KB);
myDirectPeek = false;
myBankShift = BANK_SHIFT;
myRamSize = RAM_SIZE;
myRamWpHigh = RAM_HIGH_WP;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeWD::reset()
{
initializeRAM(myRAM.data(), myRAM.size());
initializeStartBank(0);
CartridgeEnhanced::reset();
myCyclesAtBankswitchInit = 0;
myPendingBank = 0xF0; // one more than the allowable bank #
// Setup segments to some default slices
bank(startBank());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeWD::install(System& system)
{
mySystem = &system;
CartridgeEnhanced::install(system);
// Set the page accessing method for the RAM reading pages
System::PageAccess read(this, System::PageAccessType::READ);
for(uInt16 addr = 0x1000; addr < 0x1040; addr += System::PAGE_SIZE)
{
read.directPeekBase = &myRAM[addr & 0x003F];
read.romAccessBase = &myRomAccessBase[addr & 0x003F];
read.romPeekCounter = &myRomAccessCounter[addr & 0x003F];
read.romPokeCounter = &myRomAccessCounter[(addr & 0x003F) + 8_KB];
mySystem->setPageAccess(addr, read);
}
System::PageAccess access(this, System::PageAccessType::READ);
// Set the page accessing method for the RAM writing pages
// Map access to this class, since we need to inspect all accesses to
// check if RWP happens
System::PageAccess write(this, System::PageAccessType::WRITE);
for(uInt16 addr = 0x1040; addr < 0x1080; addr += System::PAGE_SIZE)
{
write.romAccessBase = &myRomAccessBase[addr & 0x003F];
write.romPeekCounter = &myRomAccessCounter[addr & 0x003F];
write.romPokeCounter = &myRomAccessCounter[(addr & 0x003F) + 8_KB];
mySystem->setPageAccess(addr, write);
}
// The hotspots are in TIA address space, so we claim it here
for(uInt16 addr = 0x00; addr < 0x40; addr += System::PAGE_SIZE)
mySystem->setPageAccess(addr, access);
// Mirror all access in TIA; by doing so we're taking responsibility
// for that address space in peek and poke below.
mySystem->tia().installDelegate(system, *this);
// Setup segments to some default slices
bank(startBank());
//mySystem->tia().installDelegate(system, *this);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -113,134 +89,31 @@ uInt8 CartridgeWD::peek(uInt16 address)
return mySystem->tia().peek(address);
}
else
{
uInt16 peekAddress = address;
address &= 0x0FFF;
if(address < 0x0040) // RAM read port
return myRAM[address];
else if(address < 0x0080) // RAM write port
// Reading from the write port @ $1040 - $107F triggers an unwanted write
return peekRAM(myRAM[address & 0x003F], peekAddress);
else if(address < 0x0400)
return myImage[myOffset[0] + (address & 0x03FF)];
else if(address < 0x0800)
return myImage[myOffset[1] + (address & 0x03FF)];
else if(address < 0x0C00)
return myImage[myOffset[2] + (address & 0x03FF)];
else
return mySegment3[address & 0x03FF];
}
return CartridgeEnhanced::peek(address);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeWD::poke(uInt16 address, uInt8 value)
{
if(!(address & 0x1000)) // TIA addresses
if(address < 0x40)
return mySystem->tia().poke(address, value);
else
{
if(address & 0x040)
{
pokeRAM(myRAM[address & 0x003F], address, value);
return true;
}
else
{
// Writing to the read port should be ignored, but trigger a break if option enabled
uInt8 dummy;
pokeRAM(dummy, address, value);
myRamWriteAccess = address;
return false;
}
}
return CartridgeEnhanced::poke(address, value);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeWD::bank(uInt16 bank)
bool CartridgeWD::bank(uInt16 bank, uInt16)
{
if(bankLocked() || bank > 15) return false;
if(bankLocked()) return false;
myCurrentBank = bank;
myCurrentBank = bank % romBankCount();
segmentZero(ourBankOrg[bank & 0x7].zero);
segmentOne(ourBankOrg[bank & 0x7].one);
segmentTwo(ourBankOrg[bank & 0x7].two);
segmentThree(ourBankOrg[bank & 0x7].three);
CartridgeEnhanced::bank(ourBankOrg[myCurrentBank].zero, 0);
CartridgeEnhanced::bank(ourBankOrg[myCurrentBank].one, 1);
CartridgeEnhanced::bank(ourBankOrg[myCurrentBank].two, 2);
CartridgeEnhanced::bank(ourBankOrg[myCurrentBank].three, 3);
return myBankChanged = true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeWD::segmentZero(uInt8 slice)
{
uInt16 offset = slice << 10;
System::PageAccess access(this, System::PageAccessType::READ);
// Skip first 128 bytes; it is always RAM
for(uInt16 addr = 0x1080; addr < 0x1400; addr += System::PAGE_SIZE)
{
access.romAccessBase = &myRomAccessBase[offset + (addr & 0x03FF)];
access.romPeekCounter = &myRomAccessCounter[offset + (addr & 0x03FF)];
access.romPokeCounter = &myRomAccessCounter[offset + (addr & 0x03FF) + 8_KB];
mySystem->setPageAccess(addr, access);
}
myOffset[0] = offset;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeWD::segmentOne(uInt8 slice)
{
uInt16 offset = slice << 10;
System::PageAccess access(this, System::PageAccessType::READ);
for(uInt16 addr = 0x1400; addr < 0x1800; addr += System::PAGE_SIZE)
{
access.romAccessBase = &myRomAccessBase[offset + (addr & 0x03FF)];
access.romPeekCounter = &myRomAccessCounter[offset + (addr & 0x03FF)];
access.romPokeCounter = &myRomAccessCounter[offset + (addr & 0x03FF) + 8_KB];
mySystem->setPageAccess(addr, access);
}
myOffset[1] = offset;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeWD::segmentTwo(uInt8 slice)
{
uInt16 offset = slice << 10;
System::PageAccess access(this, System::PageAccessType::READ);
for(uInt16 addr = 0x1800; addr < 0x1C00; addr += System::PAGE_SIZE)
{
access.romAccessBase = &myRomAccessBase[offset + (addr & 0x03FF)];
access.romPeekCounter = &myRomAccessCounter[offset + (addr & 0x03FF)];
access.romPokeCounter = &myRomAccessCounter[offset + (addr & 0x03FF) + 8_KB];
mySystem->setPageAccess(addr, access);
}
myOffset[2] = offset;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeWD::segmentThree(uInt8 slice)
{
uInt16 offset = slice << 10;
// Make a copy of the address space pointed to by the slice
// Then overwrite one byte with 0
std::copy_n(myImage.begin()+offset, mySegment3.size(), mySegment3.begin());
mySegment3[0x3FC] = 0;
System::PageAccess access(this, System::PageAccessType::READ);
for(uInt16 addr = 0x1C00; addr < 0x2000; addr += System::PAGE_SIZE)
{
access.romAccessBase = &myRomAccessBase[offset + (addr & 0x03FF)];
access.romPeekCounter = &myRomAccessCounter[offset + (addr & 0x03FF)];
access.romPokeCounter = &myRomAccessCounter[offset + (addr & 0x03FF) + 8_KB];
mySystem->setPageAccess(addr, access);
}
myOffset[3] = offset;
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -249,41 +122,13 @@ uInt16 CartridgeWD::getBank(uInt16) const
return myCurrentBank;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt16 CartridgeWD::romBankCount() const
{
return 16;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeWD::patch(uInt16 address, uInt8 value)
{
address &= 0x0FFF;
uInt16 idx = address >> 10;
myImage[myOffset[idx] + (address & 0x03FF)] = value;
// The upper segment is mirrored, so we need to patch its buffer too
if(idx == 3)
mySegment3[(address & 0x03FF)] = value;
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const uInt8* CartridgeWD::getImage(size_t& size) const
{
size = mySize;
return myImage.data();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeWD::save(Serializer& out) const
{
CartridgeEnhanced::save(out);
try
{
out.putShort(myCurrentBank);
out.putByteArray(myRAM.data(), myRAM.size());
out.putLong(myCyclesAtBankswitchInit);
out.putShort(myPendingBank);
}
@ -299,10 +144,10 @@ bool CartridgeWD::save(Serializer& out) const
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeWD::load(Serializer& in)
{
CartridgeEnhanced::load(in);
try
{
myCurrentBank = in.getShort();
in.getByteArray(myRAM.data(), myRAM.size());
myCyclesAtBankswitchInit = in.getLong();
myPendingBank = in.getShort();
@ -319,9 +164,9 @@ bool CartridgeWD::load(Serializer& in)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const std::array<CartridgeWD::BankOrg, 8> CartridgeWD::ourBankOrg = {{
// 0 1 2 3 4 5 6 7
{ 0, 0, 1, 3 }, // Bank 0, 8 2 1 - 1 - - - -
{ 0, 1, 2, 3 }, // Bank 1, 9 1 1 1 1 - - - -
// 0 1 2 3 4 5 6 7
{ 0, 0, 1, 3 }, // Bank 0, 8 2 1 - 1 - - - -
{ 0, 1, 2, 3 }, // Bank 1, 9 1 1 1 1 - - - -
{ 4, 5, 6, 7 }, // Bank 2, 10 - - - - 1 1 1 1
{ 7, 4, 2, 3 }, // Bank 3, 11 - - 1 1 1 - - 1
{ 0, 0, 6, 7 }, // Bank 4, 12 2 - - - - - 1 1

View File

@ -21,7 +21,7 @@
class System;
#include "bspf.hxx"
#include "Cart.hxx"
#include "CartEnhanced.hxx"
#ifdef DEBUGGER_SUPPORT
#include "CartWDWidget.hxx"
#endif
@ -45,15 +45,15 @@ class System;
$0037, $003F: 6,0,5,1
In the uppermost (third) segment, the byte at $3FC is overwritten by 0.
(Removed: In the uppermost (third) segment, the byte at $3FC is overwritten by 0.)
The 64 bytes of RAM are accessible at $1000 - $103F (read port) and
$1040 - $107F (write port). Because the RAM takes 128 bytes of address
space, the range $1000 - $107F of segment 0 ROM will never be available.
@author Stephen Anthony
@author Stephen Anthony, Thomas Jentzsch
*/
class CartridgeWD : public Cartridge
class CartridgeWD : public CartridgeEnhanced
{
friend class CartridgeWDWidget;
@ -89,7 +89,7 @@ class CartridgeWD : public Cartridge
@param bank The bank that should be installed in the system
*/
bool bank(uInt16 bank) override;
bool bank(uInt16 bank, uInt16 = 0) override;
/**
Get the current bank.
@ -98,28 +98,6 @@ class CartridgeWD : public Cartridge
*/
uInt16 getBank(uInt16 address = 0) const override;
/**
Query the number of banks supported by the cartridge.
*/
uInt16 romBankCount() const override;
/**
Patch the cartridge ROM.
@param address The ROM address to patch
@param value The value to place into the address
@return Success or failure of the patch operation
*/
bool patch(uInt16 address, uInt8 value) override;
/**
Access the internal ROM image for this cartridge.
@param size Set to the size of the internal ROM image data
@return A pointer to the internal ROM image data
*/
const uInt8* getImage(size_t& size) const override;
/**
Save the current state of this cart to the given Serializer.
@ -173,51 +151,9 @@ class CartridgeWD : public Cartridge
bool poke(uInt16 address, uInt8 value) override;
private:
/**
Install the specified slice for segment zero.
@param slice The slice to map into the segment
*/
void segmentZero(uInt8 slice);
/**
Install the specified slice for segment one.
@param slice The slice to map into the segment
*/
void segmentOne(uInt8 slice);
/**
Install the specified slice for segment two.
@param slice The slice to map into the segment
*/
void segmentTwo(uInt8 slice);
/**
Install the specified slice for segment three.
Note that this method also takes care of setting one byte to 0.
@param slice The slice to map into the segment
*/
void segmentThree(uInt8 slice);
bool checkSwitchBank(uInt16, uInt8 = 0) override { return false; };
private:
// The 8K ROM image of the cartridge
std::array<uInt8, 8_KB> myImage;
// Indicates the actual size of the ROM image (either 8K or 8K + 3)
size_t mySize{0};
// The 64 bytes RAM of the cartridge
std::array<uInt8, 64> myRAM;
// The 1K ROM mirror of segment 3 (sometimes contains extra 3 bytes)
std::array<uInt8, 1_KB> mySegment3;
// Indicates the offset for each of the four segments
std::array<uInt16, 4> myOffset;
// Indicates the cycle at which a bankswitch was initiated
uInt64 myCyclesAtBankswitchInit{0};
@ -233,6 +169,16 @@ class CartridgeWD : public Cartridge
};
static const std::array<BankOrg, 8> ourBankOrg;
private:
// log(ROM bank segment size) / log(2)
static constexpr uInt16 BANK_SHIFT = 10; // = 1K = 0x0400
// RAM size
static constexpr uInt16 RAM_SIZE = 0x40;
// Write port for extra RAM is at low address by default
static constexpr bool RAM_HIGH_WP = true;
private:
// Following constructors and assignment operators not supported
CartridgeWD() = delete;