refactored Cart3E+

This commit is contained in:
thrust26 2020-04-15 20:59:06 +02:00
parent 00e67f1a51
commit a823fad32c
7 changed files with 212 additions and 515 deletions

View File

@ -30,8 +30,8 @@ Cartridge3EPlusWidget::Cartridge3EPlusWidget(
size_t size = cart.mySize;
ostringstream info;
info << "3EPlus cartridge - (64K ROM + RAM)\n"
<< " 4-64K ROM (1K banks), 32K RAM (512b banks)\n"
info << "3EPlus cartridge - (4..64K ROM + RAM)\n"
<< " 4..64K ROM (1K banks), ..32K RAM (512b banks)\n"
<< "Each 1K ROM selected by writing to $3F\n"
"Each 512b RAM selected by writing to $3E\n"
" Lower 512b of bank x (R)\n"
@ -39,83 +39,78 @@ Cartridge3EPlusWidget::Cartridge3EPlusWidget(
<< "Startup bank = 0/-1/-1/0 (ROM)\n";
// Eventually, we should query this from the debugger/disassembler
//uInt16 start = (cart.myImage[size-3] << 8) | cart.myImage[size-4];
// Currently the cart starts at bank 0. If we change that, we have to change this too.
uInt16 start = (cart.myImage[0x400-3] << 8) | cart.myImage[0x400 - 4];
start -= start % 0x1000;
info << "Bank RORG" << " = $" << Common::Base::HEX4 << start << "\n";
start &= 0xF000;
info << "Bank RORG = $" << Common::Base::HEX4 << start << "\n";
int xpos = 2,
ypos = addBaseInformation(size, "T. Jentzsch", info.str()) +
myLineHeight;
ypos = addBaseInformation(size, "Thomas Jentzsch", info.str()) + 8;
VariantList bankno;
for(uInt32 i = 0; i < myCart.ROM_BANK_COUNT; ++i)
for(uInt32 i = 0; i < myCart.romBankCount(); ++i)
VarList::push_back(bankno, i, i);
VariantList banktype;
VarList::push_back(banktype, "ROM", "ROM");
VarList::push_back(banktype, "RAM", "RAM");
for(uInt32 i = 0; i < 4; ++i)
for(uInt32 seg = 0; seg < myCart.myBankSegs; ++seg)
{
int xpos_s, ypos_s = ypos;
int xpos_s, ypos_s = ypos + 1;
ostringstream label;
label << "Set segment " << i << " as ";
label << "Set segment " << seg << " as ";
new StaticTextWidget(boss, _font, xpos, ypos, _font.getStringWidth(label.str()),
myFontHeight, label.str(), TextAlign::Left);
new StaticTextWidget(boss, _font, xpos, ypos, label.str());
ypos += myLineHeight + 8;
xpos += 20;
myBankNumber[i] =
new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("Slot "),
myLineHeight, bankno, "Slot ",
6*_font.getMaxCharWidth());
addFocusWidget(myBankNumber[i]);
xpos += _font.getMaxCharWidth() * 2;
myBankNumber[seg] =
new PopUpWidget(boss, _font, xpos, ypos-2, 2 *_font.getMaxCharWidth(),
myLineHeight, bankno, "Bank ");
addFocusWidget(myBankNumber[seg]);
xpos += myBankNumber[i]->getWidth();
myBankType[i] =
new PopUpWidget(boss, _font, xpos, ypos-2, 5*_font.getMaxCharWidth(),
myLineHeight, banktype, " of ", _font.getStringWidth(" of "));
addFocusWidget(myBankType[i]);
xpos += myBankNumber[seg]->getWidth();
myBankType[seg] =
new PopUpWidget(boss, _font, xpos, ypos-2, 3 *_font.getMaxCharWidth(),
myLineHeight, banktype, " of ");
addFocusWidget(myBankType[seg]);
xpos += myBankType[i]->getWidth() + 10;
xpos = myBankType[seg]->getRight() + _font.getMaxCharWidth();
myBankCommit[i] = new ButtonWidget(boss, _font, xpos, ypos-4,
// add "Commit" button (why required?)
myBankCommit[seg] = new ButtonWidget(boss, _font, xpos, ypos-4,
_font.getStringWidth(" Commit "), myButtonHeight,
"Commit", bankEnum[i]);
myBankCommit[i]->setTarget(this);
addFocusWidget(myBankCommit[i]);
"Commit", bankEnum[seg]);
myBankCommit[seg]->setTarget(this);
addFocusWidget(myBankCommit[seg]);
xpos_s = xpos + myBankCommit[i]->getWidth() + 20;
xpos_s = myBankCommit[seg]->getRight() + _font.getMaxCharWidth() * 2;
StaticTextWidget* t;
int addr1 = start + (i*0x400), addr2 = addr1 + 0x1FF;
int addr1 = start + (seg * 0x400), addr2 = addr1 + 0x200;
label.str("");
label << Common::Base::HEX4 << addr1 << "-" << Common::Base::HEX4 << addr2;
t = new StaticTextWidget(boss, _font, xpos_s, ypos_s+2,
_font.getStringWidth(label.str()), myFontHeight, label.str(), TextAlign::Left);
label << "$" << Common::Base::HEX4 << addr1 << "-$" << Common::Base::HEX4 << (addr1 + 0x1FF);
t = new StaticTextWidget(boss, _font, xpos_s, ypos_s+2, label.str());
int xoffset = xpos_s+t->getWidth() + 10;
myBankState[2*i] = new EditTextWidget(boss, _font, xoffset, ypos_s,
int xoffset = t->getRight() + _font.getMaxCharWidth();
myBankState[2*seg] = new EditTextWidget(boss, _font, xoffset, ypos_s,
w - xoffset - 10, myLineHeight, "");
myBankState[2*i]->setEditable(false, true);
myBankState[2*seg]->setEditable(false, true);
ypos_s += myLineHeight + 4;
label.str("");
label << Common::Base::HEX4 << (addr2 + 1) << "-" << Common::Base::HEX4 << (addr2 + 1 + 0x1FF);
new StaticTextWidget(boss, _font, xpos_s, ypos_s+2,
_font.getStringWidth(label.str()), myFontHeight, label.str(), TextAlign::Left);
label << "$" << Common::Base::HEX4 << addr2 << "-$" << Common::Base::HEX4 << (addr2 + 0x1FF);
new StaticTextWidget(boss, _font, xpos_s, ypos_s+2, label.str());
myBankState[2*i+1] = new EditTextWidget(boss, _font, xoffset, ypos_s,
myBankState[2*seg+1] = new EditTextWidget(boss, _font, xoffset, ypos_s,
w - xoffset - 10, myLineHeight, "");
myBankState[2*i+1]->setEditable(false, true);
myBankState[2*seg+1]->setEditable(false, true);
xpos = 10;
ypos+= 2 * myLineHeight;
xpos = 2;
ypos += 2 * myLineHeight;
}
}
@ -163,15 +158,14 @@ void Cartridge3EPlusWidget::handleCommand(CommandSender* sender,
myBankType[segment]->getSelected() < 0)
return;
uInt8 bank = (segment << myCart.BANK_BITS) |
(myBankNumber[segment]->getSelected() & myCart.BIT_BANK_MASK);
uInt8 bank = myBankNumber[segment]->getSelected();
myCart.unlockBank();
if(myBankType[segment]->getSelectedTag() == "ROM")
myCart.bankROM(bank);
myCart.bank(bank, segment);
else
myCart.bankRAM(bank);
myCart.bank(bank + myCart.romBankCount(), segment);
myCart.lockBank();
invalidate();
@ -183,26 +177,15 @@ string Cartridge3EPlusWidget::bankState()
{
ostringstream& buf = buffer();
// In this scheme, consecutive 512b segments are either both ROM or both RAM;
// we only need to look at the lower segment to determine what the 1K bank is
for(int i = 0; i < 4; ++i)
for(int seg = 0; seg < myCart.myBankSegs; ++seg)
{
uInt16 bank = myCart.bankInUse[i*2];
int bank = myCart.getSegmentBank(seg);
if(bank == myCart.BANK_UNDEFINED) // never accessed
{
buf << " U!";
}
if(bank >= myCart.romBankCount()) // was RAM mapped here?
buf << " RAM " << bank - myCart.romBankCount();
else
{
int bankno = bank & myCart.BIT_BANK_MASK;
if(bank & myCart.BITMASK_ROMRAM) // was RAM mapped here?
buf << " RAM " << bankno;
else
buf << " ROM " << bankno;
}
if(i < 3)
buf << " ROM " << bank;
if(seg < myCart.myBankSegs - 1)
buf << " /";
}
@ -212,68 +195,42 @@ string Cartridge3EPlusWidget::bankState()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Cartridge3EPlusWidget::updateUIState()
{
// Set description for each 512b bank state (@ each index)
// Set description for each 1K bank state (@ each index)
// Set contents for actual banks number and type (@ each even index)
for(int i = 0; i < 8; ++i)
for(int seg = 0; seg < myCart.myBankSegs; ++seg)
{
uInt16 bank = myCart.bankInUse[i];
uInt16 bank = myCart.getSegmentBank(seg);
ostringstream buf;
if(bank == myCart.BANK_UNDEFINED) // never accessed
if(bank >= myCart.romBankCount()) // was RAM mapped here?
{
myBankState[i]->setText("Undefined");
if(i % 2 == 0)
{
myBankNumber[i/2]->clearSelection();
myBankType[i/2]->clearSelection();
}
uInt16 ramBank = bank - myCart.romBankCount();
buf << "RAM " << std::dec << ramBank << " @ $" << Common::Base::HEX4
<< (ramBank << myCart.myBankShift) << "(R)";
myBankState[seg * 2]->setText(buf.str());
buf.str("");
buf << "RAM " << std::dec << ramBank << " @ $" << Common::Base::HEX4
<< ((ramBank << myCart.myBankShift) + myCart.myBankSize) << "(W)";
myBankState[seg * 2 + 1]->setText(buf.str());
myBankNumber[seg]->setSelected(ramBank);
myBankType[seg]->setSelected("RAM");
}
else
{
ostringstream buf;
int bankno = bank & myCart.BIT_BANK_MASK;
buf << "ROM " << std::dec << bank << " @ $" << Common::Base::HEX4
<< ((bank << myCart.myBankShift));
myBankState[seg * 2]->setText(buf.str());
if(bank & myCart.BITMASK_ROMRAM) // was RAM mapped here?
{
if(bank & myCart.BITMASK_LOWERUPPER) // upper is write port
{
buf << "RAM " << bankno << " @ $" << Common::Base::HEX4
<< (bankno << myCart.RAM_BANK_TO_POWER) << " (W)";
myBankState[i]->setText(buf.str());
}
else
{
buf << "RAM " << bankno << " @ $" << Common::Base::HEX4
<< (bankno << myCart.RAM_BANK_TO_POWER) << " (R)";
myBankState[i]->setText(buf.str());
}
buf.str("");
buf << "ROM " << std::dec << bank << " @ $" << Common::Base::HEX4
<< ((bank << myCart.myBankShift) + myCart.myBankSize);
myBankState[seg * 2 + 1]->setText(buf.str());
if(i % 2 == 0)
{
myBankNumber[i/2]->setSelected(bankno);
myBankType[i/2]->setSelected("RAM");
}
}
else
{
if(bank & myCart.BITMASK_LOWERUPPER) // upper is high 512b
{
buf << "ROM " << bankno << " @ $" << Common::Base::HEX4
<< ((bankno << myCart.RAM_BANK_TO_POWER) + myCart.RAM_BANK_SIZE);
myBankState[i]->setText(buf.str());
}
else
{
buf << "ROM " << bankno << " @ $" << Common::Base::HEX4
<< (bankno << myCart.RAM_BANK_TO_POWER);
myBankState[i]->setText(buf.str());
}
if(i % 2 == 0)
{
myBankNumber[i/2]->setSelected(bankno);
myBankType[i/2]->setSelected("ROM");
}
}
myBankNumber[seg]->setSelected(bank);
myBankType[seg]->setSelected("ROM");
}
}
}
@ -294,9 +251,10 @@ uInt32 Cartridge3EPlusWidget::internalRamRPort(int start)
string Cartridge3EPlusWidget::internalRamDescription()
{
ostringstream desc;
desc << "Accessible 512b at a time via:\n"
<< " $f000/$f400/$f800/$fc00 for Read Access\n"
<< " $f200/$f600/$fa00/$fe00 for Write Access (+$200)";
<< " $f200/$f600/$fa00/$fe00 for Write Access";
return desc.str();
}

View File

@ -42,7 +42,7 @@ int CartDebugWidget::addBaseInformation(size_t bytes, const string& manufacturer
const string& desc, const uInt16 maxlines)
{
const int lwidth = _font.getStringWidth("Manufacturer "),
fwidth = _w - lwidth - 20;
fwidth = _w - lwidth - 12;
EditTextWidget* w = nullptr;
ostringstream buf;

View File

@ -105,7 +105,7 @@ Bankswitch::BSList = {{
{ "128IN1" , "128IN1 Multicart (256/512K)" },
{ "2K" , "2K (32-2048 bytes Atari)" },
{ "3E" , "3E (32K Tigervision)" },
{ "3E+" , "3E+ (TJ modified DASH)" },
{ "3E+" , "3E+ (TJ modified 3E)" },
{ "3F" , "3F (512K Tigervision)" },
{ "4A50" , "4A50 (64K 4A50 + RAM)" },
{ "4K" , "4K (4K Atari)" },

View File

@ -35,7 +35,7 @@ void Cartridge3E::install(System& system)
{
CartridgeEnhanced::install(system);
System::PageAccess access(this, System::PageAccessType::READWRITE);
System::PageAccess access(this, System::PageAccessType::WRITE);
// The hotspots ($3E and $3F) are in TIA address space, so we claim it here
for(uInt16 addr = 0x00; addr < 0x40; addr += System::PAGE_SIZE)

View File

@ -22,339 +22,104 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cartridge3EPlus::Cartridge3EPlus(const ByteBuffer& image, size_t size,
const string& md5, const Settings& settings)
: Cartridge(settings, md5),
mySize(size)
{
// Allocate array for the ROM image
myImage = make_unique<uInt8[]>(mySize);
: CartridgeEnhanced(image, size, md5, settings)
// Copy the ROM image into my buffer
std::copy_n(image.get(), mySize, myImage.get());
createRomAccessArrays(mySize + myRAM.size());
{
myBankShift = BANK_SHIFT;
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()
{
initializeRAM(myRAM.data(), myRAM.size());
CartridgeEnhanced::reset();
// 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);
bank(mySystem->randGenerator().next() % romBankCount(), 1);
bank(mySystem->randGenerator().next() % romBankCount(), 2);
bank(startBank(), 3); // Stella reads the PC vector always from here (TODO?)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Cartridge3EPlus::install(System& system)
{
mySystem = &system;
CartridgeEnhanced::install(system);
System::PageAccess access(this, System::PageAccessType::READWRITE);
System::PageAccess access(this, System::PageAccessType::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);
// 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();
// Setup the last segment (of 4, each 1K) to point to the first ROM slice
// Actually we DO NOT want "always". It's just on bootup, and can be out switched later
bankROM((0 << BANK_BITS) | 0);
bankROM((3 << BANK_BITS) | 0);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt16 Cartridge3EPlus::getBank(uInt16 address) const
bool Cartridge3EPlus::checkSwitchBank(uInt16 address, uInt8 value)
{
return bankInUse[(address & 0xFFF) >> 10]; // 1K slices
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt16 Cartridge3EPlus::romBankCount() const
{
return uInt16(mySize >> 10); // 1K slices
// Switch banks if necessary
if(address == 0x003F) {
// Switch ROM bank into segment 0
bank(value & 0b111111, value >> 6);
return true;
}
else if(address == 0x003E)
{
// Switch RAM bank into segment 0
bank((value & 0b111111) + romBankCount(), value >> 6);
return true;
}
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 Cartridge3EPlus::peek(uInt16 address)
{
uInt16 peekAddress = address;
address &= 0x0FFF; // restrict to 4K address range
address &= 0x0FFF;
uInt8 value = 0;
uInt32 bank = (address >> (ROM_BANK_TO_POWER - 1)) & 7; // convert to 512 byte bank index (0-7)
uInt16 imageBank = bankInUse[bank]; // the ROM/RAM bank that's here
if(address < 0x0040) // TIA peek
return mySystem->tia().peek(address);
if(imageBank == BANK_UNDEFINED) // an uninitialised bank?
{
// accessing invalid bank, so return should be... random?
value = mySystem->randGenerator().next();
}
else if(imageBank & BITMASK_ROMRAM) // a RAM bank
{
Int32 ramBank = imageBank & BIT_BANK_MASK; // discard irrelevant bits
Int32 offset = ramBank << RAM_BANK_TO_POWER; // base bank address in RAM
offset += (address & BITMASK_RAM_BANK); // + byte offset in RAM bank
return peekRAM(myRAM[offset], peekAddress);
}
return value;
return CartridgeEnhanced::peek(peekAddress);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Cartridge3EPlus::poke(uInt16 address, uInt8 value)
{
bool changed = false;
if(CartridgeEnhanced::poke(address, value))
return true;
// Check for write to the bank switch address. RAM/ROM and bank # are encoded in 'value'
// There are NO mirrored hotspots.
if(address == BANK_SWITCH_HOTSPOT_RAM)
changed = bankRAM(value);
else if(address == BANK_SWITCH_HOTSPOT_ROM)
changed = bankROM(value);
if(!(address & 0x1000))
{
if(address < 0x0040) // TIA poke
// Handle TIA space that we claimed above
changed = changed || mySystem->tia().poke(address, value);
}
else
{
uInt32 bankNumber = (address >> RAM_BANK_TO_POWER) & 7; // now 512 byte bank # (ie: 0-7)
Int16 whichBankIsThere = bankInUse[bankNumber]; // ROM or RAM bank reference
return mySystem->tia().poke(address, value);
if(whichBankIsThere & BITMASK_ROMRAM)
{
if(address & RAM_BANK_SIZE)
{
uInt32 byteOffset = address & BITMASK_RAM_BANK;
uInt32 baseAddress = ((whichBankIsThere & BIT_BANK_MASK) << RAM_BANK_TO_POWER) + byteOffset;
pokeRAM(myRAM[baseAddress], address, value);
changed = 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;
changed = false;
}
}
}
return changed;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Cartridge3EPlus::bankRAM(uInt8 bank)
{
if(bankLocked()) // debugger can lock RAM
return false;
//cerr << "bankRAM " << int(bank) << endl;
// Each RAM bank uses two slots, separated by 0x200 in memory -- one read, one write.
bankRAMSlot(bank | BITMASK_ROMRAM | 0);
bankRAMSlot(bank | BITMASK_ROMRAM | BITMASK_LOWERUPPER);
return myBankChanged = true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Cartridge3EPlus::bankRAMSlot(uInt16 bank)
{
uInt16 bankNumber = (bank >> BANK_BITS) & 3; // which bank # we are switching TO (BITS D6,D7) to 512 byte block
uInt16 currentBank = bank & BIT_BANK_MASK; // Wrap around/restrict to valid range
bool upper = bank & BITMASK_LOWERUPPER; // is this the read or write port
uInt32 startCurrentBank = currentBank << RAM_BANK_TO_POWER; // Effectively * 512 bytes
//cerr << "raw bank=" << std::dec << currentBank << endl
// << "startCurrentBank=$" << std::hex << startCurrentBank << endl;
// Setup the page access methods for the current bank
System::PageAccess access(this, System::PageAccessType::READ);
if(upper) // We're mapping the write port
{
bankInUse[bankNumber * 2 + 1] = Int16(bank);
access.type = System::PageAccessType::WRITE;
}
else // We're mapping the read port
{
bankInUse[bankNumber * 2] = Int16(bank);
access.type = System::PageAccessType::READ;
}
uInt16 start = 0x1000 + (bankNumber << (RAM_BANK_TO_POWER+1)) + (upper ? RAM_WRITE_OFFSET : 0);
uInt16 end = start + RAM_BANK_SIZE - 1;
//cerr << "bank RAM: " << bankNumber << " -> " << (bankNumber * 2 + (upper ? 1 : 0)) << (upper ? " (W)" : " (R)") << endl
// << "start=" << std::hex << start << ", end=" << end << endl << endl;
for(uInt16 addr = start; addr <= end; addr += System::PAGE_SIZE)
{
if(!upper)
access.directPeekBase = &myRAM[startCurrentBank + (addr & (RAM_BANK_SIZE - 1))];
access.romAccessBase = &myRomAccessBase[mySize + startCurrentBank + (addr & (RAM_BANK_SIZE - 1))];
mySystem->setPageAccess(addr, access);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Cartridge3EPlus::bankROM(uInt8 bank)
{
if(bankLocked()) // debugger can lock ROM
return false;
// Map ROM bank image into the system into the correct slot
// Memory map is 1K slots at 0x1000, 0x1400, 0x1800, 0x1C00
// Each ROM uses 2 consecutive 512 byte slots
bankROMSlot(bank | 0);
bankROMSlot(bank | BITMASK_LOWERUPPER);
return myBankChanged = true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Cartridge3EPlus::bankROMSlot(uInt16 bank)
{
uInt16 bankNumber = (bank >> BANK_BITS) & 3; // which bank # we are switching TO (BITS D6,D7)
uInt16 currentBank = bank & BIT_BANK_MASK; // Wrap around/restrict to valid range
bool upper = bank & BITMASK_LOWERUPPER; // is this the lower or upper 512b
bankInUse[bankNumber * 2 + (upper ? 1 : 0)] = Int16(bank); // Record which bank switched in (as ROM)
uInt32 startCurrentBank = currentBank << ROM_BANK_TO_POWER; // Effectively *1K
// Setup the page access methods for the current bank
System::PageAccess access(this, System::PageAccessType::READ);
uInt16 start = 0x1000 + (bankNumber << ROM_BANK_TO_POWER) + (upper ? ROM_BANK_SIZE / 2 : 0);
uInt16 end = start + ROM_BANK_SIZE / 2 - 1;
for(uInt16 addr = start; addr <= end; addr += System::PAGE_SIZE)
{
access.directPeekBase = &myImage[startCurrentBank + (addr & (ROM_BANK_SIZE - 1))];
access.romAccessBase = &myRomAccessBase[startCurrentBank + (addr & (ROM_BANK_SIZE - 1))];
mySystem->setPageAccess(addr, access);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Cartridge3EPlus::initializeBankState()
{
// Switch in each 512b slot
for(uInt32 b = 0; b < 8; ++b)
{
if(bankInUse[b] == BANK_UNDEFINED)
{
// All accesses point to peek/poke above
System::PageAccess access(this, System::PageAccessType::READ);
uInt16 start = 0x1000 + (b << RAM_BANK_TO_POWER);
uInt16 end = start + RAM_BANK_SIZE - 1;
for(uInt16 addr = start; addr <= end; addr += System::PAGE_SIZE)
mySystem->setPageAccess(addr, access);
}
else if (bankInUse[b] & BITMASK_ROMRAM)
bankRAMSlot(bankInUse[b]);
else
bankROMSlot(bankInUse[b]);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Cartridge3EPlus::patch(uInt16 address, uInt8 value)
{
#if 0
// Patch the cartridge ROM (for debugger)
myBankChanged = true;
uInt32 bankNumber = (address >> RAM_BANK_TO_POWER) & 7; // now 512 byte bank # (ie: 0-7)
uInt16 whichBankIsThere = bankInUse[bankNumber]; // ROM or RAM bank reference
if (whichBankIsThere == BANK_UNDEFINED) {
// We're trying to access undefined memory (no bank here yet). Fail!
myBankChanged = false;
} else if (whichBankIsThere & BITMASK_ROMRAM) { // patching RAM (512 byte banks)
uInt32 byteOffset = address & BITMASK_RAM_BANK;
uInt32 baseAddress = ((whichBankIsThere & BIT_BANK_MASK) << RAM_BANK_TO_POWER) + byteOffset;
myRAM[baseAddress] = value; // write to RAM
// TODO: Stephen -- should we set 'myBankChanged' true when there's a RAM write?
} else { // patching ROM (1K banks)
uInt32 byteOffset = address & BITMASK_ROM_BANK;
uInt32 baseAddress = (whichBankIsThere << ROM_BANK_TO_POWER) + byteOffset;
myImage[baseAddress] = value; // write to the image
}
return myBankChanged;
#else
return false;
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const uInt8* Cartridge3EPlus::getImage(size_t& size) const
{
size = mySize;
return myImage.get();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Cartridge3EPlus::save(Serializer& out) const
{
try
{
out.putShortArray(bankInUse.data(), bankInUse.size());
out.putByteArray(myRAM.data(), myRAM.size());
}
catch (...)
{
cerr << "ERROR: Cartridge3EPlus::save" << endl;
return false;
}
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Cartridge3EPlus::load(Serializer& in)
{
try
{
in.getShortArray(bankInUse.data(), bankInUse.size());
in.getByteArray(myRAM.data(), myRAM.size());
}
catch (...)
{
cerr << "ERROR: Cartridge3EPlus::load" << endl;
return false;
}
initializeBankState();
return true;
}

View File

@ -21,7 +21,7 @@
class System;
#include "bspf.hxx"
#include "Cart.hxx"
#include "CartEnhanced.hxx"
#ifdef DEBUGGER_SUPPORT
class Cartridge3EPlusWidget;
@ -29,19 +29,67 @@ class Cartridge3EPlusWidget;
#endif
/**
Cartridge class from Thomas Jentzsch, mostly based on the 'DASH' scheme
with the following changes:
Cartridge class for new tiling engine "Boulder Dash" format games with RAM.
Kind of a combination of 3F and 3E, with better switchability.
B.Watson's Cart3E was used as a template for building this implementation.
RAM areas:
- read $x000, write $x200
- read $x400, write $x600
- read $x800, write $xa00
- read $xc00, write $xe00
The destination bank (0-3) is held in the top bits of the value written to
$3E (for RAM switching) or $3F (for ROM switching). The low 6 bits give
the actual bank number (0-63) corresponding to 512 byte blocks for RAM and
1024 byte blocks for ROM. The maximum size is therefore 32K RAM and 64K ROM.
@author Thomas Jentzsch and Stephen Anthony
D7D6 indicate the bank number (0-3)
D5D4D3D2D1D0 indicate the actual # (0-63) from the image/ram
ROM:
Note: in descriptions $F000 is equivalent to $1000 -- that is, we only deal
with the low 13 bits of addressing. Stella code uses $1000, I'm used to $F000
So, mask with top bits clear :) when reading this document.
In this scheme, the 4K address space is broken into four 1K ROM/512b RAM segments
living at 0x1000, 0x1400, 0x1800, 0x1C00 (or, same thing, 0xF000... etc.),
The last 1K ROM ($FC00-$FFFF) segment in the 6502 address space (ie: $1C00-$1FFF)
is initialised to point to the FIRST 1K of the ROM image, so the reset vectors
must be placed at the end of the first 1K in the ROM image. Note, this is
DIFFERENT to 3E which switches in the UPPER bank and this bank is fixed. This
allows variable sized ROM without having to detect size. First bank (0) in ROM is
the default fixed bank mapped to $FC00.
The system requires the reset vectors to be valid on a reset, so either the
hardware first switches in the first bank, or the programmer must ensure
that the reset vector is present in ALL ROM banks which might be switched
into the last bank area. Currently the latter (programmer onus) is required,
but it would be nice for the cartridge hardware to auto-switch on reset.
ROM switching (write of block+bank number to $3F) D7D6 upper 2 bits of bank #
indicates the destination segment (0-3, corresponding to $F000, $F400, $F800,
$FC00), and lower 6 bits indicate the 1K bank to switch in. Can handle 64
x 1K ROM banks (64K total).
D7 D6 D5D4D3D2D1D0
0 0 x x x x x x switch a 1K ROM bank xxxxxx to $F000
0 1 switch a 1K ROM bank xxxxxx to $F400
1 0 switch a 1K ROM bank xxxxxx to $F800
1 1 switch a 1K ROM bank xxxxxx to $FC00
RAM switching (write of segment+bank number to $3E) with D7D6 upper 2 bits of
bank # indicates the destination RAM segment (0-3, corresponding to $F000,
$F400, $F800, $FC00).
Can handle 64 x 512 byte RAM banks (32K total)
D7 D6 D5D4D3D2D1D0
0 0 x x x x x x switch a 512 byte RAM bank xxxxxx to $F000 with write @ $F200
0 1 switch a 512 byte RAM bank xxxxxx to $F400 with write @ $F600
1 0 switch a 512 byte RAM bank xxxxxx to $F800 with write @ $FA00
1 1 switch a 512 byte RAM bank xxxxxx to $FC00 with write @ $FE00
@author Thomas Jentzsch and Stephen Anthony
*/
class Cartridge3EPlus: public Cartridge
class Cartridge3EPlus: public CartridgeEnhanced
{
friend class Cartridge3EPlusWidget;
@ -70,51 +118,6 @@ class Cartridge3EPlus: public Cartridge
*/
void install(System& system) override;
/**
Get the current bank.
@param address The address to use when querying the bank
*/
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.
@param out The Serializer object to use
@return False on any errors, else true
*/
bool save(Serializer& out) const override;
/**
Load the current state of this cart from the given Serializer.
@param in The Serializer object to use
@return False on any errors, else true
*/
bool load(Serializer& in) override;
/**
Get a descriptor for the device name (used in error checking).
@ -152,47 +155,20 @@ class Cartridge3EPlus: public Cartridge
bool poke(uInt16 address, uInt8 value) override;
private:
bool bankRAM(uInt8 bank); // switch a RAM bank
bool bankROM(uInt8 bank); // switch a ROM bank
bool checkSwitchBank(uInt16 address, uInt8 value) override;
void bankRAMSlot(uInt16 bank); // switch in a 512b RAM slot (lower or upper 1/2 bank)
void bankROMSlot(uInt16 bank); // switch in a 512b RAM slot (read or write port)
private:
// log(ROM bank segment size) / log(2)
static constexpr uInt16 BANK_SHIFT = 10; // = 1K = 0x0400
void initializeBankState(); // set all banks according to current bankInUse state
// The size of extra RAM in ROM address space
static constexpr uInt16 RAM_BANKS = 64;
// We have an array that indicates for each of the 8 512 byte areas of the address space, which ROM/RAM
// bank is used in that area. ROM switches 1K so occupies 2 successive entries for each switch. RAM occupies
// two as well, one 512 byte for read and one for write. The RAM locations are +0x800 apart, and the ROM
// are consecutive. This allows us to determine on a read/write exactly where the data is.
// RAM size
static constexpr uInt16 RAM_SIZE = RAM_BANKS << (BANK_SHIFT - 1); // = 32K = 0x4000;
static constexpr uInt16 BANK_UNDEFINED = 0x8000; // bank is undefined and inaccessible
std::array<uInt16, 8> bankInUse; // bank being used for ROM/RAM (eight 512 byte areas)
static constexpr uInt16 BANK_SWITCH_HOTSPOT_RAM = 0x3E; // writes to this address cause bankswitching
static constexpr uInt16 BANK_SWITCH_HOTSPOT_ROM = 0x3F; // writes to this address cause bankswitching
static constexpr uInt8 BANK_BITS = 6; // # bits for bank
static constexpr uInt8 BIT_BANK_MASK = (1 << BANK_BITS) - 1; // mask for those bits
static constexpr uInt16 BITMASK_LOWERUPPER = 0x100; // flags lower or upper section of bank (1==upper)
static constexpr uInt16 BITMASK_ROMRAM = 0x200; // flags ROM or RAM bank switching (1==RAM)
static constexpr uInt16 MAXIMUM_BANK_COUNT = (1 << BANK_BITS);
static constexpr uInt16 RAM_BANK_TO_POWER = 9; // 2^n = 512
static constexpr uInt16 RAM_BANK_SIZE = (1 << RAM_BANK_TO_POWER);
static constexpr uInt16 BITMASK_RAM_BANK = (RAM_BANK_SIZE - 1);
static constexpr uInt32 RAM_TOTAL_SIZE = MAXIMUM_BANK_COUNT * RAM_BANK_SIZE;
static constexpr uInt16 ROM_BANK_TO_POWER = 10; // 2^n = 1024
static constexpr uInt16 ROM_BANK_SIZE = (1 << ROM_BANK_TO_POWER);
static constexpr uInt16 BITMASK_ROM_BANK = (ROM_BANK_SIZE - 1);
static constexpr uInt16 ROM_BANK_COUNT = 64;
static constexpr uInt16 RAM_WRITE_OFFSET = 0x200;
ByteBuffer myImage; // Pointer to a dynamically allocated ROM image of the cartridge
size_t mySize{0}; // Size of the ROM image
std::array<uInt8, RAM_TOTAL_SIZE> myRAM;
// Write port for extra RAM is at high address
static constexpr bool RAM_HIGH_WP = true;
private:
// Following constructors and assignment operators not supported

View File

@ -20,7 +20,7 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CartridgeEnhanced::CartridgeEnhanced(const ByteBuffer& image, size_t size,
const string& md5, const Settings& settings)
const string& md5, const Settings& settings)
: Cartridge(settings, md5),
mySize(size)
{
@ -270,9 +270,7 @@ bool CartridgeEnhanced::bank(uInt16 bank, uInt16 segment)
access.romPeekCounter = &myRomAccessCounter[offset];
access.romPokeCounter = &myRomAccessCounter[offset + myAccessSize];
mySystem->setPageAccess(addr, access);
}
}
}
return myBankChanged = true;
}