From cbe0ba8eee1944889071332a1fdf48041e2861ab Mon Sep 17 00:00:00 2001 From: thrust26 Date: Sat, 5 Aug 2023 15:20:46 +0200 Subject: [PATCH] added 03E0 bankswitching for Brazilian Parker Bros ROMs (resolves #887) --- Changes.txt | 2 + docs/index.html | 1 + src/debugger/gui/Cart03E0Widget.cxx | 74 ++++++++++ src/debugger/gui/Cart03E0Widget.hxx | 54 +++++++ src/debugger/gui/CartEnhancedWidget.cxx | 2 +- src/debugger/gui/module.mk | 1 + src/emucore/Bankswitch.cxx | 5 + src/emucore/Bankswitch.hxx | 14 +- src/emucore/Cart03E0.cxx | 113 +++++++++++++++ src/emucore/Cart03E0.hxx | 137 ++++++++++++++++++ src/emucore/CartCreator.cxx | 3 + src/emucore/CartDetector.cxx | 18 +++ src/emucore/CartDetector.hxx | 7 +- src/emucore/CartE0.cxx | 1 + src/emucore/module.mk | 1 + src/gui/FileListWidget.cxx | 2 +- src/os/libretro/Makefile.common | 1 + src/os/libretro/Stella.vcxproj | 2 + src/os/windows/Stella.vcxproj | 4 + src/os/windows/Stella.vcxproj.filters | 12 ++ ...'s Revenge (Brazilian Parker Brothers).bin | Bin 0 -> 8192 bytes .../Popeye (Brazilian Parker Brothers).bin | Bin 0 -> 8192 bytes 22 files changed, 444 insertions(+), 10 deletions(-) create mode 100644 src/debugger/gui/Cart03E0Widget.cxx create mode 100644 src/debugger/gui/Cart03E0Widget.hxx create mode 100644 src/emucore/Cart03E0.cxx create mode 100644 src/emucore/Cart03E0.hxx create mode 100644 test/roms/bankswitching/03E0/Montezuma's Revenge (Brazilian Parker Brothers).bin create mode 100644 test/roms/bankswitching/03E0/Popeye (Brazilian Parker Brothers).bin diff --git a/Changes.txt b/Changes.txt index 4603ade98..a6b34ae41 100644 --- a/Changes.txt +++ b/Changes.txt @@ -38,6 +38,8 @@ * Added limited GameLine Master Module bankswitching support. + * Added 03E0 bankswitching for Brazilian Parker Bros ROMs. + * Added BUS bankswitching support for some older demos. * Fixed broken 7800 pause key support. diff --git a/docs/index.html b/docs/index.html index 575f6c525..f7696d7c0 100644 --- a/docs/index.html +++ b/docs/index.html @@ -4888,6 +4888,7 @@ Ms Pac-Man (Stella extended codes): Note: If 'Filter' is checked, only the bankswitching types matching the ROM size are listed. + diff --git a/src/debugger/gui/Cart03E0Widget.cxx b/src/debugger/gui/Cart03E0Widget.cxx new file mode 100644 index 000000000..7d35467d1 --- /dev/null +++ b/src/debugger/gui/Cart03E0Widget.cxx @@ -0,0 +1,74 @@ +//============================================================================ +// +// 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-2023 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 "Cart03E0.hxx" +#include "Cart03E0Widget.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Cartridge03E0Widget::Cartridge03E0Widget( + GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, + int x, int y, int w, int h, Cartridge03E0& cart) + : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart) +{ + initialize(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string Cartridge03E0Widget::description() +{ + ostringstream info; + + info << "03E0 cartridge,\n eight 1K banks mapped into four segments\n" + << CartridgeEnhancedWidget::description(); + + return info.str(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string Cartridge03E0Widget::romDescription() +{ + ostringstream info; + + for(int seg = 0; seg < 4; ++seg) + { + const uInt16 segmentOffset = seg << 10; // myCart.myBankShift; + + info << "Segment #" << seg << " accessible @ $" + << Common::Base::HEX4 << (ADDR_BASE | segmentOffset) + << " - $" << (ADDR_BASE | (segmentOffset + /*myCart.myBankSize - 1*/ 0x3FF)) << ",\n"; + if (seg < 3) + info << " Hotspots " << hotspotStr(0, seg, true) << " - " << hotspotStr(7, seg, true) << "\n"; + else + info << " Always points to last 1K bank of ROM\n"; + } + info << "Startup banks = 4 / 5 / 6 or undetermined"; + + return info.str(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string Cartridge03E0Widget::hotspotStr(int bank, int segment, bool noBrackets) +{ + static constexpr uInt16 hotspots[3] = {0x03E0, 0x03D0, 0x03B0}; + ostringstream info; + + info << (noBrackets ? "" : "(") + << "$" << Common::Base::HEX1 << ( hotspots[segment] + bank) + << (noBrackets ? "" : ")"); + + return info.str(); +} diff --git a/src/debugger/gui/Cart03E0Widget.hxx b/src/debugger/gui/Cart03E0Widget.hxx new file mode 100644 index 000000000..eccb9cdf6 --- /dev/null +++ b/src/debugger/gui/Cart03E0Widget.hxx @@ -0,0 +1,54 @@ +//============================================================================ +// +// 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-2023 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 CARTRIDGE03E0_WIDGET_HXX +#define CARTRIDGE03E0_WIDGET_HXX + +class Cartridge03E0; + +#include "CartEnhancedWidget.hxx" + +class Cartridge03E0Widget : public CartridgeEnhancedWidget +{ + public: + Cartridge03E0Widget(GuiObject* boss, const GUI::Font& lfont, + const GUI::Font& nfont, + int x, int y, int w, int h, + Cartridge03E0& cart); + ~Cartridge03E0Widget() override = default; + + private: + string manufacturer() override { return "Parker Brothers (Brazil Pirate)"; } + + string description() override; + + string romDescription() override; + + string hotspotStr(int bank, int segment, bool noBrackets = false) override; + + uInt16 bankSegs() override { return 3; } + + private: + // Following constructors and assignment operators not supported + Cartridge03E0Widget() = delete; + Cartridge03E0Widget(const Cartridge03E0Widget&) = delete; + Cartridge03E0Widget(Cartridge03E0Widget&&) = delete; + Cartridge03E0Widget& operator=(const Cartridge03E0Widget&) = delete; + Cartridge03E0Widget& operator=(Cartridge03E0Widget&&) = delete; +}; + +#endif diff --git a/src/debugger/gui/CartEnhancedWidget.cxx b/src/debugger/gui/CartEnhancedWidget.cxx index 3cd486d5f..5b82a8a5c 100644 --- a/src/debugger/gui/CartEnhancedWidget.cxx +++ b/src/debugger/gui/CartEnhancedWidget.cxx @@ -270,7 +270,7 @@ string CartridgeEnhancedWidget::bankState() //if(hotspot >= 0x100) if(hotspot != 0 && myHotspotDelta > 0) - buf << " " << hotspotStr(bank, 0, bankSegs() < 3); + buf << " " << hotspotStr(bank, seg, bankSegs() < 3); } } else diff --git a/src/debugger/gui/module.mk b/src/debugger/gui/module.mk index 45ea9b5ae..7742e84f6 100644 --- a/src/debugger/gui/module.mk +++ b/src/debugger/gui/module.mk @@ -6,6 +6,7 @@ MODULE_OBJS := \ src/debugger/gui/AtariVoxWidget.o \ src/debugger/gui/AudioWidget.o \ src/debugger/gui/BoosterWidget.o \ + src/debugger/gui/Cart03E0Widget.o \ src/debugger/gui/Cart0840Widget.o \ src/debugger/gui/Cart0FA0Widget.o \ src/debugger/gui/Cart2KWidget.o \ diff --git a/src/emucore/Bankswitch.cxx b/src/emucore/Bankswitch.cxx index 6c17fca3b..52aec7232 100644 --- a/src/emucore/Bankswitch.cxx +++ b/src/emucore/Bankswitch.cxx @@ -75,6 +75,7 @@ bool Bankswitch::isValidRomName(string_view name, string& ext) constexpr std::array(Bankswitch::Type::NumSchemes)> Bankswitch::BSList = {{ { "AUTO" , "Auto-detect" }, + { "03E0" , "03E0 (8K Braz. Parker Bros)" }, { "0840" , "0840 (8K EconoBanking)" }, { "0FA0" , "0FA0 (8K Fotomania)" }, { "2IN1" , "2in1 Multicart (4-64K)" }, @@ -138,6 +139,7 @@ Bankswitch::BSList = {{ const std::array(Bankswitch::Type::NumSchemes)> Bankswitch::Sizes = {{ { Bankswitch::any_KB, Bankswitch::any_KB }, // _AUTO + { 8_KB, 8_KB }, // _03E0 { 8_KB, 8_KB }, // _0840 { 8_KB, 8_KB }, // _0FA0 { 4_KB, 64_KB }, // _2IN1 @@ -210,6 +212,8 @@ Bankswitch::ExtensionMap Bankswitch::ourExtensions = { { "cu" , Bankswitch::Type::_AUTO }, // All bankswitch types (those that UnoCart and HarmonyCart support have the same name) + { "03E" , Bankswitch::Type::_03E0 }, + { "03E0" , Bankswitch::Type::_03E0 }, { "084" , Bankswitch::Type::_0840 }, { "0840" , Bankswitch::Type::_0840 }, { "0FA" , Bankswitch::Type::_0FA0 }, @@ -288,6 +292,7 @@ Bankswitch::ExtensionMap Bankswitch::ourExtensions = { // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Bankswitch::NameToTypeMap Bankswitch::ourNameToTypes = { { "AUTO" , Bankswitch::Type::_AUTO }, + { "03E0" , Bankswitch::Type::_03E0 }, { "0840" , Bankswitch::Type::_0840 }, { "0FA0" , Bankswitch::Type::_0FA0 }, { "2IN1" , Bankswitch::Type::_2IN1 }, diff --git a/src/emucore/Bankswitch.hxx b/src/emucore/Bankswitch.hxx index d7eebd9a4..b3e3f7750 100644 --- a/src/emucore/Bankswitch.hxx +++ b/src/emucore/Bankswitch.hxx @@ -38,13 +38,13 @@ class Bankswitch public: // Currently supported bankswitch schemes enum class Type { - _AUTO, _0840, _0FA0, _2IN1, _4IN1, _8IN1, _16IN1, _32IN1, - _64IN1, _128IN1, _2K, _3E, _3EX, _3EP, _3F, _4A50, - _4K, _4KSC, _AR, _BF, _BFSC, _BUS, _CDF, _CM, - _CTY, _CV, _DF, _DFSC, _DPC, _DPCP, _E0, _E7, - _EF, _EFSC, _F0, _F4, _F4SC, _F6, _F6SC, _F8, - _F8SC, _FA, _FA2, _FC, _FE, _GL, _MDM, _MVC, - _SB, _TVBOY, _UA, _UASW, _WD, _WDSW, _X07, + _AUTO, _03E0, _0840, _0FA0, _2IN1, _4IN1, _8IN1, _16IN1, + _32IN1, _64IN1, _128IN1, _2K, _3E, _3EX, _3EP, _3F, + _4A50, _4K, _4KSC, _AR, _BF, _BFSC, _BUS, _CDF, + _CM, _CTY, _CV, _DF, _DFSC, _DPC, _DPCP, _E0, + _E7, _EF, _EFSC, _F0, _F4, _F4SC, _F6, _F6SC, + _F8, _F8SC, _FA, _FA2, _FC, _FE, _GL, _MDM, + _MVC, _SB, _TVBOY, _UA, _UASW, _WD, _WDSW, _X07, #ifdef CUSTOM_ARM _CUSTOM, #endif diff --git a/src/emucore/Cart03E0.cxx b/src/emucore/Cart03E0.cxx new file mode 100644 index 000000000..e6cf12577 --- /dev/null +++ b/src/emucore/Cart03E0.cxx @@ -0,0 +1,113 @@ +//============================================================================ +// +// 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-2023 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 "System.hxx" +#include "Cart03E0.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Cartridge03E0::Cartridge03E0(const ByteBuffer& image, size_t size, + string_view md5, const Settings& settings, + size_t bsSize) + : CartridgeEnhanced(image, size, md5, settings, bsSize) +{ + myBankShift = BANK_SHIFT; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Cartridge03E0::install(System& system) +{ + CartridgeEnhanced::install(system); + + // Get the page accessing methods for the hot spots since they overlap + // areas within the TIA we'll need to forward requests to the TIA + myHotSpotPageAccess[0] = mySystem->getPageAccess(0x0380); + myHotSpotPageAccess[1] = mySystem->getPageAccess(0x03c0); + + // Set the page accessing methods for the hot spots + const System::PageAccess access(this, System::PageAccessType::READ); + for(uInt16 addr = 0x0380; addr < 0x03FF; addr += System::PAGE_SIZE) + mySystem->setPageAccess(addr, access); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Cartridge03E0::reset() +{ + // Setup segments to some default banks + if(randomStartBank()) + { + bank(mySystem->randGenerator().next() % 8, 0); + bank(mySystem->randGenerator().next() % 8, 1); + bank(mySystem->randGenerator().next() % 8, 2); + } + else + { + bank(4, 0); + bank(5, 1); + bank(6, 2); + } + myBankChanged = true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool Cartridge03E0::checkSwitchBank(uInt16 address, uInt8) +{ + bool switched = false; + + if((address & 0x10) == 0) + { + bank(address & 0x0007, 0); + switched = true; + } + if((address & 0x20) == 0) + { + bank(address & 0x0007, 1); + switched = true; + } + if((address & 0x40) == 0) + { + bank(address & 0x0007, 2); + switched = true; + } + return switched; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 Cartridge03E0::peek(uInt16 address) +{ + checkSwitchBank(address, 0); + + // Because of the way we've set up accessing above, we can only + // get here when the addresses are from 0x380 - 0x3FF + const int hotspot = ((address & 0x40) >> 6); + return myHotSpotPageAccess[hotspot].device->peek(address); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool Cartridge03E0::poke(uInt16 address, uInt8 value) +{ + // Because of the way accessing is set up, we will may get here by + // doing a write to 0x380 - 0x3FF or cart; we ignore the cart write + if(!(address & 0x1000)) + { + checkSwitchBank(address, 0); + + const int hotspot = ((address & 0x40) >> 6); + myHotSpotPageAccess[hotspot].device->poke(address, value); + } + + return false; +} diff --git a/src/emucore/Cart03E0.hxx b/src/emucore/Cart03E0.hxx new file mode 100644 index 000000000..899c23438 --- /dev/null +++ b/src/emucore/Cart03E0.hxx @@ -0,0 +1,137 @@ +//============================================================================ +// +// 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-2023 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 Cartridge03E0_HXX +#define Cartridge03E0_HXX + +#include "bspf.hxx" +#include "CartEnhanced.hxx" +#include "System.hxx" +#ifdef DEBUGGER_SUPPORT + #include "Cart03e0Widget.hxx" +#endif + +/** + This is the cartridge class for Parker Brothers' 8K games with special + Brazilian bankswitching. In this bankswitching scheme the 2600's 4K + cartridge address space is broken into four 1K segments. + + The desired 1K bank of the ROM is selected as follows: + If A12 == 0, A9 == 1, A8 == 1, A7 == 1 ($0380..$03ff): + A4 == 0 ($03e0) loads the bank number for segment #0 + A5 == 0 ($03d0) loads the bank number for segment #1 + A6 == 0 ($03b0) loads the bank number for segment #2 + Bits A0, A1, A2 determine the bank number (0..7) + + The last 1K segment always points to the last 1K of the ROM image. + + Because of the complexity of this scheme, the cart reports having + only one actual bank, in which pieces of it can be swapped out in + many different ways. + + @author Thomas Jentzsch +*/ +class Cartridge03E0 : public CartridgeEnhanced +{ + friend class Cartridge03E0Widget; + + public: + /** + Create a new cartridge using the specified image + + @param image Pointer to the ROM image + @param size The size of the ROM image + @param md5 The md5sum of the ROM image + @param settings A reference to the various settings (read-only) + @param bsSize The size specified by the bankswitching scheme + */ + Cartridge03E0(const ByteBuffer& image, size_t size, string_view md5, + const Settings& settings, size_t bsSize = 8_KB); + ~Cartridge03E0() override = default; + + public: + /** + Install cartridge in the specified system. Invoked by the system + when the cartridge is attached to it. + + @param system The system the device should install itself in + */ + void install(System& system) override; + + /** + Reset device to its power-on state + */ + void reset() override; + + /** + Get a descriptor for the device name (used in error checking). + + @return The name of the object + */ + string name() const override { return "Cartridge03E0"; } + + #ifdef DEBUGGER_SUPPORT + /** + Get debugger widget responsible for accessing the inner workings + of the cart. + */ + CartDebugWidget* debugWidget(GuiObject* boss, const GUI::Font& lfont, + const GUI::Font& nfont, int x, int y, int w, int h) override + { + return new Cartridge03E0Widget(boss, lfont, nfont, x, y, w, h, *this); + } + #endif + + public: + /** + Get the byte at the specified address. + + @return The byte at the specified address + */ + uInt8 peek(uInt16 address) override; + + /** + Change the byte at the specified address to the given value + + @param address The address where the value should be stored + @param value The value to be stored at the address + @return True if the poke changed the device address space, else false + */ + bool poke(uInt16 address, uInt8 value) override; + + private: + bool checkSwitchBank(uInt16 address, uInt8) override; + + uInt16 hotspot() const override { return 0x0380; } + + private: + // log(ROM bank segment size) / log(2) + static constexpr uInt16 BANK_SHIFT = 10; // = 1K = 0x0400 + + // Previous Device's page access + std::array myHotSpotPageAccess; + + private: + // Following constructors and assignment operators not supported + Cartridge03E0() = delete; + Cartridge03E0(const Cartridge03E0&) = delete; + Cartridge03E0(Cartridge03E0&&) = delete; + Cartridge03E0& operator=(const Cartridge03E0&) = delete; + Cartridge03E0& operator=(Cartridge03E0&&) = delete; +}; + +#endif diff --git a/src/emucore/CartCreator.cxx b/src/emucore/CartCreator.cxx index 111695afe..021cbed2f 100644 --- a/src/emucore/CartCreator.cxx +++ b/src/emucore/CartCreator.cxx @@ -17,6 +17,7 @@ #include "bspf.hxx" #include "Cart.hxx" +#include "Cart03E0.hxx" #include "Cart0840.hxx" #include "Cart0FA0.hxx" #include "Cart2K.hxx" @@ -232,6 +233,8 @@ CartCreator::createFromImage(const ByteBuffer& image, size_t size, // We should know the cart's type by now so let's create it switch(type) { + case Bankswitch::Type::_03E0: + return make_unique(image, size, md5, settings); case Bankswitch::Type::_0840: return make_unique(image, size, md5, settings); case Bankswitch::Type::_0FA0: diff --git a/src/emucore/CartDetector.cxx b/src/emucore/CartDetector.cxx index 1e3b9a4f4..d1f3af5d4 100644 --- a/src/emucore/CartDetector.cxx +++ b/src/emucore/CartDetector.cxx @@ -92,6 +92,8 @@ Bankswitch::Type CartDetector::autodetectType(const ByteBuffer& image, size_t si type = Bankswitch::Type::_WD; else if (isProbablyFC(image, size)) type = Bankswitch::Type::_FC; + else if(isProbably03E0(image, size)) + type = Bankswitch::Type::_03E0; else type = Bankswitch::Type::_F8; } @@ -324,6 +326,22 @@ bool CartDetector::isProbablyARM(const ByteBuffer& image, size_t size) return searchForBytes(image, std::min(size, 1_KB), signature[1], 4); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool CartDetector::isProbably03E0(const ByteBuffer& image, size_t size) +{ + // 03E0 cart bankswitching for Brazilian Parker Bros ROMs, switches segment + // 0 into bank 0 by accessing address 0x3E0 using 'LDA $3E0' or 'ORA $3E0'. + static constexpr uInt8 signature[2][4] = { + { 0x0D, 0xE0, 0x03, 0x0D }, // ORA $3E0, ORA (Popeye) + { 0xAD, 0xE0, 0x03, 0xAD } // LDA $3E0, ORA (Montezuma's Revenge) + }; + for(const auto* const sig: signature) + if(searchForBytes(image, size, sig, 4)) + return true; + + return false; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool CartDetector::isProbably0840(const ByteBuffer& image, size_t size) { diff --git a/src/emucore/CartDetector.hxx b/src/emucore/CartDetector.hxx index e2d346f0a..b9ce7a2bc 100644 --- a/src/emucore/CartDetector.hxx +++ b/src/emucore/CartDetector.hxx @@ -86,13 +86,18 @@ class CartDetector */ static bool isProbablyARM(const ByteBuffer& image, size_t size); + /** + Returns true if the image is probably a 03E0 bankswitching cartridge + */ + static bool isProbably03E0(const ByteBuffer& image, size_t size); + /** Returns true if the image is probably a 0840 bankswitching cartridge */ static bool isProbably0840(const ByteBuffer& image, size_t size); /** - Returns true if the image is probably a BRazilian bankswitching cartridge + Returns true if the image is probably a Brazilian 0FA0 bankswitching cartridge */ static bool isProbably0FA0(const ByteBuffer& image, size_t size); diff --git a/src/emucore/CartE0.cxx b/src/emucore/CartE0.cxx index 4fcf5ad40..24bbcb32d 100644 --- a/src/emucore/CartE0.cxx +++ b/src/emucore/CartE0.cxx @@ -67,5 +67,6 @@ bool CartridgeE0::checkSwitchBank(uInt16 address, uInt8) bank(address & 0x0007, 2); return true; } + return false; } diff --git a/src/emucore/module.mk b/src/emucore/module.mk index be23f42ee..38e53273d 100644 --- a/src/emucore/module.mk +++ b/src/emucore/module.mk @@ -9,6 +9,7 @@ MODULE_OBJS := \ src/emucore/CartCreator.o \ src/emucore/CartDetector.o \ src/emucore/CartEnhanced.o \ + src/emucore/Cart03E0.o \ src/emucore/Cart0840.o \ src/emucore/Cart0FA0.o \ src/emucore/Cart2K.o \ diff --git a/src/gui/FileListWidget.cxx b/src/gui/FileListWidget.cxx index 01f2a53e7..205ffe53a 100644 --- a/src/gui/FileListWidget.cxx +++ b/src/gui/FileListWidget.cxx @@ -353,7 +353,7 @@ bool FileListWidget::handleKeyDown(StellaKey key, StellaMod mod) _lastKey = key; _lastMod = mod; if(_quickSelectTime < TimerManager::getTicks() / 1000) _firstMod = mod; - else if(key == KBDK_SPACE) // allow seaching strings with a space without selecting/starting + else if(key == KBDK_SPACE) // allow searching ROMs with a space without selecting/starting handled = true; return handled; diff --git a/src/os/libretro/Makefile.common b/src/os/libretro/Makefile.common index 453f3cf3f..724fd4adb 100644 --- a/src/os/libretro/Makefile.common +++ b/src/os/libretro/Makefile.common @@ -43,6 +43,7 @@ SOURCES_CXX := \ $(CORE_DIR)/emucore/CartCreator.cxx \ $(CORE_DIR)/emucore/CartDetector.cxx \ $(CORE_DIR)/emucore/CartEnhanced.cxx \ + $(CORE_DIR)/emucore/Cart03E0.cxx \ $(CORE_DIR)/emucore/Cart0840.cxx \ $(CORE_DIR)/emucore/Cart0FA0.cxx \ $(CORE_DIR)/emucore/Cart2K.cxx \ diff --git a/src/os/libretro/Stella.vcxproj b/src/os/libretro/Stella.vcxproj index ee90e9f86..c9ba437b3 100644 --- a/src/os/libretro/Stella.vcxproj +++ b/src/os/libretro/Stella.vcxproj @@ -219,6 +219,7 @@ + @@ -382,6 +383,7 @@ + diff --git a/src/os/windows/Stella.vcxproj b/src/os/windows/Stella.vcxproj index d659a040d..8e25f756f 100755 --- a/src/os/windows/Stella.vcxproj +++ b/src/os/windows/Stella.vcxproj @@ -918,6 +918,7 @@ true + true @@ -978,6 +979,7 @@ + @@ -2108,6 +2110,7 @@ true + true @@ -2303,6 +2306,7 @@ + diff --git a/src/os/windows/Stella.vcxproj.filters b/src/os/windows/Stella.vcxproj.filters index 7d3849df5..d75b91f01 100644 --- a/src/os/windows/Stella.vcxproj.filters +++ b/src/os/windows/Stella.vcxproj.filters @@ -1203,6 +1203,12 @@ Source Files\debugger\gui + + Source Files\emucore + + + Source Files\debugger\gui + @@ -2453,6 +2459,12 @@ Header Files\debugger\gui + + Header Files\emucore + + + Header Files\debugger\gui + diff --git a/test/roms/bankswitching/03E0/Montezuma's Revenge (Brazilian Parker Brothers).bin b/test/roms/bankswitching/03E0/Montezuma's Revenge (Brazilian Parker Brothers).bin new file mode 100644 index 0000000000000000000000000000000000000000..5652d9d674a1010f1476121f2777dd0ba3f58fc4 GIT binary patch literal 8192 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
 Type DescriptionFile Extension
(to force type)
03e0 8K Brazilian Parker Bros.03e, .03e0
0840 8K EconoBanking.084, .0840
0FA0 8K Fotomania.0FA, .0FA0
2IN1 ¹4-64K Multicart (2 games).2N1