From a823fad32c4fd002ff4a7fe662e442310d95f27a Mon Sep 17 00:00:00 2001 From: thrust26 Date: Wed, 15 Apr 2020 20:59:06 +0200 Subject: [PATCH] refactored Cart3E+ --- src/debugger/gui/Cart3EPlusWidget.cxx | 196 ++++++-------- src/debugger/gui/CartDebugWidget.cxx | 2 +- src/emucore/Bankswitch.cxx | 2 +- src/emucore/Cart3E.cxx | 2 +- src/emucore/Cart3EPlus.cxx | 359 +++++--------------------- src/emucore/Cart3EPlus.hxx | 160 +++++------- src/emucore/CartEnhanced.cxx | 6 +- 7 files changed, 212 insertions(+), 515 deletions(-) diff --git a/src/debugger/gui/Cart3EPlusWidget.cxx b/src/debugger/gui/Cart3EPlusWidget.cxx index 74935f22b..a93cb8f98 100644 --- a/src/debugger/gui/Cart3EPlusWidget.cxx +++ b/src/debugger/gui/Cart3EPlusWidget.cxx @@ -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(); } diff --git a/src/debugger/gui/CartDebugWidget.cxx b/src/debugger/gui/CartDebugWidget.cxx index 9e164f4a6..208eec5a7 100644 --- a/src/debugger/gui/CartDebugWidget.cxx +++ b/src/debugger/gui/CartDebugWidget.cxx @@ -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; diff --git a/src/emucore/Bankswitch.cxx b/src/emucore/Bankswitch.cxx index 9726dc6aa..84d0dd037 100644 --- a/src/emucore/Bankswitch.cxx +++ b/src/emucore/Bankswitch.cxx @@ -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)" }, diff --git a/src/emucore/Cart3E.cxx b/src/emucore/Cart3E.cxx index a927ddd2d..3e7454bda 100644 --- a/src/emucore/Cart3E.cxx +++ b/src/emucore/Cart3E.cxx @@ -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) diff --git a/src/emucore/Cart3EPlus.cxx b/src/emucore/Cart3EPlus.cxx index 1fe28a022..f6ac01679 100644 --- a/src/emucore/Cart3EPlus.cxx +++ b/src/emucore/Cart3EPlus.cxx @@ -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(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(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; } diff --git a/src/emucore/Cart3EPlus.hxx b/src/emucore/Cart3EPlus.hxx index 70309d308..1a2440e40 100644 --- a/src/emucore/Cart3EPlus.hxx +++ b/src/emucore/Cart3EPlus.hxx @@ -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 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 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 diff --git a/src/emucore/CartEnhanced.cxx b/src/emucore/CartEnhanced.cxx index b8c1cde13..01cf5cbcb 100644 --- a/src/emucore/CartEnhanced.cxx +++ b/src/emucore/CartEnhanced.cxx @@ -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; }