diff --git a/Changes.txt b/Changes.txt index c3c0304e1..2fb02da2b 100644 --- a/Changes.txt +++ b/Changes.txt @@ -26,6 +26,8 @@ * Added selectable dialog fonts + * Added separate positioning of launcher, emulator and debugger + * Added option which lets default ROM path follow launcher navigation * Added displaying last write address in the debugger. @@ -34,6 +36,13 @@ * Restored 'cfg' directory for Distella config files. + * Added 3EX bank switching type. + + * Removed unused CV+ and DASH bank switching types. + + * Added debugger pseudo-register '_scanend', which gives the number of + scanlines at the end of the last frame. + -Have fun! diff --git a/docs/debugger.html b/docs/debugger.html index 4f0c1cf6f..91e71bd47 100644 --- a/docs/debugger.html +++ b/docs/debugger.html @@ -755,6 +755,7 @@ that holds 'number of scanlines' on an actual console).

_fcycles Number of cycles since frame started _icycles Number of cycles of last instruction _scan Current scanline count + _scanend Scanline count at end of last frame _scycles Number of cycles in current scanline _vblank Whether vertical blank is enabled (1 or 0) _vsync Whether vertical sync is enabled (1 or 0) diff --git a/docs/index.html b/docs/index.html index c845b487f..028062bb4 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1999,6 +1999,16 @@ Centers game window (if possible). + +
-windowedpos <WxH>
+ Sets the window position in windowed emulator mode. + + + +
-display <number>
+ Sets the display for Stella's emulator. + +
-palette <standard|z26|user>
Set the palette to either normal Stella, the one used in the z26 @@ -2376,6 +2386,16 @@ launcher only happens when started with the launcher). + +
-launcherpos <WxH>
+ Sets the window position in windowed ROM launcher mode. + + + +
-launcherdisplay <number>
+ Sets the display for the ROM launcher. + +
-launcherres <WxH>
Set the size of the ROM launcher. @@ -2522,6 +2542,16 @@ Relocate calls out of address range in the disassembler. + +
-dbg.pos <WxH>
+ Sets the window position in windowed debugger mode. + + + +
-dbg.display <number>
+ Sets the display for the debugger. + +
-dbg.res <WxH>
Set the size of the debugger window. @@ -3778,7 +3808,9 @@ Ms Pac-Man (Stella extended codes):

Each block in a property file consists of a set of properties for a single game. Stella supports the properties described below:

- +

+ +

@@ -3799,9 +3831,10 @@ Ms Pac-Man (Stella extended codes): - - - + + + + @@ -3813,9 +3846,7 @@ Ms Pac-Man (Stella extended codes): - - - + @@ -3837,7 +3868,7 @@ Ms Pac-Man (Stella extended codes): - + @@ -3897,11 +3928,15 @@ Ms Pac-Man (Stella extended codes):
32IN1 ¹64-128K Multicart (32 games) .32N, .32N1
64IN1 ¹64/128K Multicart .64N, .64N1
128IN1 ¹256/512K Multicart .128, .128N1
2K 32-2048 byte Atari .2K
3E 32K Tigervision .3E
3E+ 3E+ (TJ modified DASH) .3EP, .3E+
2K 32-2048 bytes Atari .2K
3E 512K Tigervision + 32K RAM.3E
3EX 512K Tigervision + 256K RAM.3EX
3E+ 3E+ (TJ modified 3E) .3EP, .3E+
3F 512K Tigervision .3F
4A50 ²64K 4A50 + RAM .4A5, .4A50
4K 4K Atari .4K
CDF Chris, Darrell, Fred (includes CDFJ).CDF
CM ¹Spectravideo CompuMate .CM
CTY ²CDW - Chetiry .CTY
CV Commavid extra RAM .CV
CV+ Extended Commavid extra RAM.CVP
DASH Boulder Dash 2 .DAS, .DASH
CV CommaVid extra RAM .CV
DF CPUWIZ 128K .DF
DFSC CPUWIZ 128K + RAM.DFS, .DFSC
DPC Pitfall II .DPC
FC Amiga Power Play Aracde 16/32K .FC
FE 8K Decathlon .FE
MDM Menu Driven Megacart .MDM
SB 128-256k SUPERbanking .SB
SB 128-256K SUPERbanking .SB
UA 8K UA Ltd. .UA
UASW 8K UA Ltd. (swapped banks).UASW
WD Wickstead Design (Pink Panther) .WD
+
- +

+ +

+ @@ -3921,8 +3956,11 @@ Ms Pac-Man (Stella extended codes): right player. The value must be A or B.
Console.TelevisionType:
+
- +

+ +

@@ -3997,8 +4035,11 @@ Ms Pac-Man (Stella extended codes): how to use the X/Y axis (ie, 02 is paddle0/paddle2). -->
Controller.Left:
Controller.Right:
+
- +

+ +

diff --git a/src/cheat/CheatCodeDialog.cxx b/src/cheat/CheatCodeDialog.cxx index 5ce380e0b..09ac1cd3c 100644 --- a/src/cheat/CheatCodeDialog.cxx +++ b/src/cheat/CheatCodeDialog.cxx @@ -42,7 +42,6 @@ CheatCodeDialog::CheatCodeDialog(OSystem& osystem, DialogContainer& parent, buttonHeight = font.getLineHeight() * 1.25; const int VBORDER = fontHeight / 2; const int HBORDER = fontWidth * 1.25; - const int INDENT = fontWidth * 2; const int VGAP = fontHeight / 4; int xpos, ypos; diff --git a/src/common/FrameBufferSDL2.cxx b/src/common/FrameBufferSDL2.cxx index bce5c2e7c..4d013a35d 100644 --- a/src/common/FrameBufferSDL2.cxx +++ b/src/common/FrameBufferSDL2.cxx @@ -48,8 +48,6 @@ FrameBufferSDL2::FrameBufferSDL2(OSystem& osystem) // since the structure may be needed before any FBSurface's have // been created myPixelFormat = SDL_AllocFormat(SDL_PIXELFORMAT_ARGB8888); - - myWindowedPos = myOSystem.settings().getPoint("windowedpos"); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -183,27 +181,34 @@ void FrameBufferSDL2::queryHardware(vector& fullscreenRes, } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Int32 FrameBufferSDL2::getCurrentDisplayIndex() +bool FrameBufferSDL2::isCurrentWindowPositioned() const +{ + ASSERT_MAIN_THREAD; + + return !myCenter + && myWindow && !(SDL_GetWindowFlags(myWindow) & SDL_WINDOW_FULLSCREEN_DESKTOP); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Common::Point FrameBufferSDL2::getCurrentWindowPos() const +{ + ASSERT_MAIN_THREAD; + + Common::Point pos; + + SDL_GetWindowPosition(myWindow, &pos.x, &pos.y); + + return pos; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Int32 FrameBufferSDL2::getCurrentDisplayIndex() const { ASSERT_MAIN_THREAD; return SDL_GetWindowDisplayIndex(myWindow); } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameBufferSDL2::updateWindowedPos() -{ - ASSERT_MAIN_THREAD; - - // only save if the window is not centered and not in full screen mode - if (!myCenter && myWindow && !(SDL_GetWindowFlags(myWindow) & SDL_WINDOW_FULLSCREEN_DESKTOP)) - { - // save current windowed position - SDL_GetWindowPosition(myWindow, &myWindowedPos.x, &myWindowedPos.y); - myOSystem.settings().setValue("windowedpos", myWindowedPos); - } -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool FrameBufferSDL2::setVideoMode(const string& title, const VideoMode& mode) { @@ -213,24 +218,13 @@ bool FrameBufferSDL2::setVideoMode(const string& title, const VideoMode& mode) if(SDL_WasInit(SDL_INIT_VIDEO) == 0) return false; - Int32 displayIndex = mode.fsIndex; - if (displayIndex == -1) - { - // windowed mode - if (myWindow) - { - // Show it on same screen as the previous window - displayIndex = SDL_GetWindowDisplayIndex(myWindow); - } - if (displayIndex < 0) - { - // fallback to the last used screen if still existing - displayIndex = std::min(myNumDisplays, myOSystem.settings().getInt("display")); - } - } + // TODO: On multiple displays, switching from centered mode, does not respect + // current window's display (which many not be centered anymore) - // save and get last windowed window's position - updateWindowedPos(); + // Get windowed window's last display + Int32 displayIndex = std::min(myNumDisplays, myOSystem.settings().getInt(getDisplayKey())); + // Get windowed window's last position + myWindowedPos = myOSystem.settings().getPoint(getPositionKey()); // Always recreate renderer (some systems need this) if(myRenderer) @@ -249,7 +243,7 @@ bool FrameBufferSDL2::setVideoMode(const string& title, const VideoMode& mode) posX = myWindowedPos.x; posY = myWindowedPos.y; - // make sure the window is at least partially visibile + // Make sure the window is at least partially visibile int x0 = 0, y0 = 0, x1 = 0, y1 = 0; for (int display = SDL_GetNumVideoDisplays() - 1; display >= 0; display--) @@ -276,13 +270,15 @@ bool FrameBufferSDL2::setVideoMode(const string& title, const VideoMode& mode) // toggling fullscreen windowed mode // So we have a special case for macOS #ifndef BSPF_MACOS - // Don't re-create the window if its size hasn't changed, as it's not - // necessary, and causes flashing in fullscreen mode + // Don't re-create the window if its display and size hasn't changed, + // as it's not necessary, and causes flashing in fullscreen mode if(myWindow) { + int d = SDL_GetWindowDisplayIndex(myWindow); int w, h; + SDL_GetWindowSize(myWindow, &w, &h); - if(uInt32(w) != mode.screen.w || uInt32(h) != mode.screen.h) + if(d != displayIndex || uInt32(w) != mode.screen.w || uInt32(h) != mode.screen.h) { SDL_DestroyWindow(myWindow); myWindow = nullptr; diff --git a/src/common/FrameBufferSDL2.hxx b/src/common/FrameBufferSDL2.hxx index 857891685..d2825037c 100644 --- a/src/common/FrameBufferSDL2.hxx +++ b/src/common/FrameBufferSDL2.hxx @@ -95,6 +95,21 @@ class FrameBufferSDL2 : public FrameBuffer */ void readPixels(uInt8* buffer, uInt32 pitch, const Common::Rect& rect) const override; + /** + This method is called to query if the current window is not centered + or fullscreen. + + @return True, if the current window is positioned + */ + bool isCurrentWindowPositioned() const override; + + /** + This method is called to query the video hardware for position of + the current window + + @return The position of the currently displayed window + */ + Common::Point getCurrentWindowPos() const override; /** This method is called to query the video hardware for the index of the display the current window is displayed on @@ -102,12 +117,7 @@ class FrameBufferSDL2 : public FrameBuffer @return the current display index or a negative value if no window is displayed */ - Int32 getCurrentDisplayIndex() override; - - /** - This method is called to preserve the last current windowed position. - */ - void updateWindowedPos() override; + Int32 getCurrentDisplayIndex() const override; /** Clear the frame buffer. diff --git a/src/common/bspf.hxx b/src/common/bspf.hxx index 18c60bd2a..1484b21ed 100644 --- a/src/common/bspf.hxx +++ b/src/common/bspf.hxx @@ -84,6 +84,7 @@ using ByteArray = std::vector; using ShortArray = std::vector; using StringList = std::vector; using ByteBuffer = std::unique_ptr; // NOLINT +using DWordBuffer = std::unique_ptr; // NOLINT // We use KB a lot; let's make a literal for it constexpr uInt32 operator "" _KB(unsigned long long size) diff --git a/src/debugger/CartDebug.cxx b/src/debugger/CartDebug.cxx index 9666fc6d2..3198eb9ea 100644 --- a/src/debugger/CartDebug.cxx +++ b/src/debugger/CartDebug.cxx @@ -79,7 +79,10 @@ CartDebug::CartDebug(Debugger& dbg, Console& console, const OSystem& osystem) BankInfo info; info.size = std::min(romSize, 4_KB); - for(uInt32 i = 0; i < myConsole.cartridge().bankCount(); ++i) + for(uInt32 i = 0; i < myConsole.cartridge().romBankCount(); ++i) + myBankInfo.push_back(info); + + for(uInt32 i = 0; i < myConsole.cartridge().ramBankCount(); ++i) myBankInfo.push_back(info); info.size = 128; // ZP RAM @@ -239,9 +242,10 @@ string CartDebug::toString() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartDebug::disassemble(bool force) +bool CartDebug::disassemblePC(bool force) { uInt16 PC = myDebugger.cpuDebug().pc(); + // ROM/RAM bank or ZP-RAM? int bank = (PC & 0x1000) ? getBank(PC) : int(myBankInfo.size()) - 1; return disassemble(bank, PC, force); @@ -418,7 +422,7 @@ bool CartDebug::addDirective(Device::AccessType type, bank = (myDebugger.cpuDebug().pc() & 0x1000) ? getBank(myDebugger.cpuDebug().pc()) : int(myBankInfo.size())-1; - bank = std::min(bank, bankCount()); + bank = std::min(bank, romBankCount()); BankInfo& info = myBankInfo[bank]; DirectiveList& list = info.directiveList; @@ -550,9 +554,9 @@ int CartDebug::getPCBank() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -int CartDebug::bankCount() const +int CartDebug::romBankCount() const { - return myConsole.cartridge().bankCount(); + return myConsole.cartridge().romBankCount(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -959,7 +963,7 @@ string CartDebug::loadConfigFile() myDebugger.rom().invalidate(); stringstream retVal; - if(myConsole.cartridge().bankCount() > 1) + if(myConsole.cartridge().romBankCount() > 1) retVal << DebuggerParser::red("config file for multi-bank ROM not fully supported\n"); retVal << "config file '" << node.getShortPath() << "' loaded OK"; return retVal.str(); @@ -994,14 +998,14 @@ string CartDebug::saveConfigFile() out << "// Stella.pro: \"" << name << "\"" << endl << "// MD5: " << md5 << endl << endl; - for(uInt32 b = 0; b < myConsole.cartridge().bankCount(); ++b) + for(uInt32 b = 0; b < myConsole.cartridge().romBankCount(); ++b) { out << "[" << b << "]" << endl; getBankDirectives(out, myBankInfo[b]); } stringstream retVal; - if(myConsole.cartridge().bankCount() > 1) + if(myConsole.cartridge().romBankCount() > 1) retVal << DebuggerParser::red("config file for multi-bank ROM not fully supported\n"); retVal << "config file '" << cfg.getShortPath() << "' saved OK"; return retVal.str(); @@ -1062,14 +1066,14 @@ string CartDebug::saveDisassembly() Disassembly disasm; disasm.list.reserve(2048); - uInt16 bankCount = myConsole.cartridge().bankCount(); + uInt16 romBankCount = myConsole.cartridge().romBankCount(); uInt16 oldBank = myConsole.cartridge().getBank(); // prepare for switching banks myConsole.cartridge().unlockBank(); uInt32 origin = 0; - for(int bank = 0; bank < bankCount; ++bank) + for(int bank = 0; bank < romBankCount; ++bank) { // TODO: not every CartDebugWidget does it like that, we need a method myConsole.cartridge().unlockBank(); @@ -1086,8 +1090,8 @@ string CartDebug::saveDisassembly() buf << "\n\n;***********************************************************\n" << "; Bank " << bank; - if (bankCount > 1) - buf << " / 0.." << bankCount - 1; + if (romBankCount > 1) + buf << " / 0.." << romBankCount - 1; buf << "\n;***********************************************************\n\n"; @@ -1101,7 +1105,7 @@ string CartDebug::saveDisassembly() buf << " SEG CODE\n"; - if(bankCount == 1) + if(romBankCount == 1) buf << " ORG $" << Base::HEX4 << info.offset << "\n\n"; else buf << " ORG $" << Base::HEX4 << origin << "\n" @@ -1331,7 +1335,7 @@ string CartDebug::saveDisassembly() out << buf.str(); stringstream retVal; - if(myConsole.cartridge().bankCount() > 1) + if(myConsole.cartridge().romBankCount() > 1) retVal << DebuggerParser::red("disassembly for multi-bank ROM not fully supported\n"); retVal << "saved " << node.getShortPath() << " OK"; return retVal.str(); @@ -1371,8 +1375,8 @@ string CartDebug::saveAccessFile() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - string CartDebug::listConfig(int bank) { - uInt32 startbank = 0, endbank = bankCount(); - if(bank >= 0 && bank < bankCount()) + uInt32 startbank = 0, endbank = romBankCount(); + if(bank >= 0 && bank < romBankCount()) { startbank = bank; endbank = startbank + 1; @@ -1396,7 +1400,7 @@ string CartDebug::listConfig(int bank) getBankDirectives(buf, info); } - if(myConsole.cartridge().bankCount() > 1) + if(myConsole.cartridge().romBankCount() > 1) buf << DebuggerParser::red("config file for multi-bank ROM not fully supported") << endl; return buf.str(); @@ -1405,8 +1409,8 @@ string CartDebug::listConfig(int bank) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - string CartDebug::clearConfig(int bank) { - uInt32 startbank = 0, endbank = bankCount(); - if(bank >= 0 && bank < bankCount()) + uInt32 startbank = 0, endbank = romBankCount(); + if(bank >= 0 && bank < romBankCount()) { startbank = bank; endbank = startbank + 1; diff --git a/src/debugger/CartDebug.hxx b/src/debugger/CartDebug.hxx index c2bd03cae..486eedd79 100644 --- a/src/debugger/CartDebug.hxx +++ b/src/debugger/CartDebug.hxx @@ -95,7 +95,7 @@ class CartDebug : public DebuggerSystem int lastWriteBaseAddress(); // TODO - bool disassemble(bool force = false); + bool disassemblePC(bool force = false); bool disassembleBank(int bank); // First, a call is made to disassemble(), which updates the disassembly @@ -159,7 +159,7 @@ class CartDebug : public DebuggerSystem /** Get the total number of banks supported by the cartridge. */ - int bankCount() const; + int romBankCount() const; /** Add a label and associated address. diff --git a/src/debugger/Debugger.cxx b/src/debugger/Debugger.cxx index cfb16a1e5..b46570a6a 100644 --- a/src/debugger/Debugger.cxx +++ b/src/debugger/Debugger.cxx @@ -115,7 +115,8 @@ void Debugger::initialize() FBInitStatus Debugger::initializeVideo() { string title = string("Stella ") + STELLA_VERSION + ": Debugger mode"; - return myOSystem.frameBuffer().createDisplay(title, myWidth, myHeight); + return myOSystem.frameBuffer().createDisplay(title, FrameBuffer::BufferType::Debugger, + myWidth, myHeight); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -688,6 +689,7 @@ void Debugger::setStartState() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Debugger::setQuitState() { + myDialog->saveConfig(); saveOldState(); // Bus must be unlocked for normal operation when leaving debugger mode @@ -873,7 +875,7 @@ std::array Debugger::ourBuiltinFunctions = { { // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Names are defined here, but processed in YaccParser -std::array Debugger::ourPseudoRegisters = { { +std::array Debugger::ourPseudoRegisters = { { // Debugger::PseudoRegister Debugger::ourPseudoRegisters[NUM_PSEUDO_REGS] = { { "_bank", "Currently selected bank" }, { "_cclocks", "Color clocks on current scanline" }, @@ -881,8 +883,9 @@ std::array Debugger::ourPseudoRegisters = { { { "_cycleslo", "Lower 32 bits of number of cycles since emulation started" }, { "_fcount", "Number of frames since emulation started" }, { "_fcycles", "Number of cycles since frame started" }, - { "_icycles", "Number of cycles of last instruction" }, + { "_icycles", "Number of cycles of last instruction" }, { "_scan", "Current scanline count" }, + { "_scanend", "Scanline count at end of last frame" }, { "_scycles", "Number of cycles in current scanline" }, { "_vblank", "Whether vertical blank is enabled (1 or 0)" }, { "_vsync", "Whether vertical sync is enabled (1 or 0)" } diff --git a/src/debugger/Debugger.hxx b/src/debugger/Debugger.hxx index 2b0ce6c1c..d063f2e90 100644 --- a/src/debugger/Debugger.hxx +++ b/src/debugger/Debugger.hxx @@ -363,7 +363,7 @@ class Debugger : public DialogContainer string name, help; }; static std::array ourBuiltinFunctions; - static std::array ourPseudoRegisters; + static std::array ourPseudoRegisters; private: // rewind/unwind n states diff --git a/src/debugger/DebuggerParser.cxx b/src/debugger/DebuggerParser.cxx index 4f50be800..da3b0bc5f 100644 --- a/src/debugger/DebuggerParser.cxx +++ b/src/debugger/DebuggerParser.cxx @@ -763,7 +763,7 @@ void DebuggerParser::executeBreak() { uInt16 addr; uInt8 bank; - uInt32 bankCount = debugger.cartDebug().bankCount(); + uInt32 romBankCount = debugger.cartDebug().romBankCount(); if(argCount == 0) addr = debugger.cpuDebug().pc(); @@ -775,7 +775,7 @@ void DebuggerParser::executeBreak() else { bank = args[1]; - if(bank >= bankCount && bank != 0xff) + if(bank >= romBankCount && bank != 0xff) { commandResult << red("invalid bank"); return; @@ -791,12 +791,12 @@ void DebuggerParser::executeBreak() commandResult << "cleared"; commandResult << " breakpoint at $" << Base::HEX4 << addr << " + mirrors"; - if(bankCount > 1) + if(romBankCount > 1) commandResult << " in bank #" << std::dec << int(bank); } else { - for(int i = 0; i < debugger.cartDebug().bankCount(); ++i) + for(int i = 0; i < debugger.cartDebug().romBankCount(); ++i) { bool set = debugger.toggleBreakPoint(addr, i); @@ -809,7 +809,7 @@ void DebuggerParser::executeBreak() commandResult << "cleared"; commandResult << " breakpoint at $" << Base::HEX4 << addr << " + mirrors"; - if(bankCount > 1) + if(romBankCount > 1) commandResult << " in bank #" << std::dec << int(bank); } } @@ -1459,11 +1459,11 @@ void DebuggerParser::executeListbreaks() { stringstream buf; int count = 0; - uInt32 bankCount = debugger.cartDebug().bankCount(); + uInt32 romBankCount = debugger.cartDebug().romBankCount(); for(const auto& bp : debugger.breakPoints().getBreakpoints()) { - if(bankCount == 1) + if(romBankCount == 1) { buf << debugger.cartDebug().getLabel(bp.addr, true, 4) << " "; if(!(++count % 8)) buf << endl; diff --git a/src/debugger/gui/Cart0840Widget.cxx b/src/debugger/gui/Cart0840Widget.cxx index 6582701bc..eb9275035 100644 --- a/src/debugger/gui/Cart0840Widget.cxx +++ b/src/debugger/gui/Cart0840Widget.cxx @@ -16,80 +16,25 @@ //============================================================================ #include "Cart0840.hxx" -#include "PopUpWidget.hxx" #include "Cart0840Widget.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Cartridge0840Widget::Cartridge0840Widget( GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, int x, int y, int w, int h, Cartridge0840& cart) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h), - myCart(cart) + : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart) { - uInt16 size = 2 * 4096; + myHotspotDelta = 0x40; + initialize(); +} +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string Cartridge0840Widget::description() +{ ostringstream info; - info << "0840 ECONObanking, two 4K banks\n" - << "Startup bank = " << cart.startBank() << "\n"; - // Eventually, we should query this from the debugger/disassembler - for(uInt32 i = 0, offset = 0xFFC, spot = 0x800; i < 2; - ++i, offset += 0x1000, spot += 0x40) - { - uInt16 start = uInt16((cart.myImage[offset+1] << 8) | cart.myImage[offset]); - start -= start % 0x1000; - info << "Bank " << i << " @ $" << Common::Base::HEX4 << start << " - " - << "$" << (start + 0xFFF) << " (hotspot = $" << spot << ")\n"; - } + info << "0840 ECONObanking, two 4K banks\n"; + info << CartridgeEnhancedWidget::description(); - int xpos = 2, - ypos = addBaseInformation(size, "Fred X. Quimby", info.str()) + myLineHeight; - - VariantList items; - VarList::push_back(items, "0 ($800)"); - VarList::push_back(items, "1 ($840)"); - myBank = - new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("0 ($800)"), - myLineHeight, items, "Set bank ", - 0, kBankChanged); - myBank->setTarget(this); - addFocusWidget(myBank); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Cartridge0840Widget::loadConfig() -{ - Debugger& dbg = instance().debugger(); - CartDebug& cart = dbg.cartDebug(); - const CartState& state = static_cast(cart.getState()); - const CartState& oldstate = static_cast(cart.getOldState()); - - myBank->setSelectedIndex(myCart.getBank(), state.bank != oldstate.bank); - - CartDebugWidget::loadConfig(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Cartridge0840Widget::handleCommand(CommandSender* sender, - int cmd, int data, int id) -{ - if(cmd == kBankChanged) - { - myCart.unlockBank(); - myCart.bank(myBank->getSelected()); - myCart.lockBank(); - invalidate(); - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string Cartridge0840Widget::bankState() -{ - ostringstream& buf = buffer(); - - static const std::array spot = { "$800", "$840" }; - buf << "Bank = " << std::dec << myCart.getBank() - << ", hotspot = " << spot[myCart.getBank()]; - - return buf.str(); + return info.str(); } diff --git a/src/debugger/gui/Cart0840Widget.hxx b/src/debugger/gui/Cart0840Widget.hxx index 239f28bf8..00605b262 100644 --- a/src/debugger/gui/Cart0840Widget.hxx +++ b/src/debugger/gui/Cart0840Widget.hxx @@ -19,11 +19,10 @@ #define CARTRIDGE0840_WIDGET_HXX class Cartridge0840; -class PopUpWidget; -#include "CartDebugWidget.hxx" +#include "CartEnhancedWidget.hxx" -class Cartridge0840Widget : public CartDebugWidget +class Cartridge0840Widget : public CartridgeEnhancedWidget { public: Cartridge0840Widget(GuiObject* boss, const GUI::Font& lfont, @@ -33,17 +32,11 @@ class Cartridge0840Widget : public CartDebugWidget virtual ~Cartridge0840Widget() = default; private: - Cartridge0840& myCart; - PopUpWidget* myBank{nullptr}; + string manufacturer() override { return "Fred X. Quimby"; } - enum { kBankChanged = 'bkCH' }; + string description() override; private: - void loadConfig() override; - void handleCommand(CommandSender* sender, int cmd, int data, int id) override; - - string bankState() override; - // Following constructors and assignment operators not supported Cartridge0840Widget() = delete; Cartridge0840Widget(const Cartridge0840Widget&) = delete; diff --git a/src/debugger/gui/Cart2KWidget.cxx b/src/debugger/gui/Cart2KWidget.cxx index e90ed67f2..d87445ed3 100644 --- a/src/debugger/gui/Cart2KWidget.cxx +++ b/src/debugger/gui/Cart2KWidget.cxx @@ -22,15 +22,18 @@ Cartridge2KWidget::Cartridge2KWidget( GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, int x, int y, int w, int h, Cartridge2K& cart) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h) + : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart) { - // Eventually, we should query this from the debugger/disassembler - size_t size = cart.mySize; - uInt16 start = (cart.myImage[size-3] << 8) | cart.myImage[size-4]; - start -= start % size; - - ostringstream info; - info << "Standard 2K cartridge, non-bankswitched\n" - << "Accessible @ $" << Common::Base::HEX4 << start << " - " << "$" << (start + size - 1); - addBaseInformation(size, "Atari", info.str()); + initialize(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string Cartridge2KWidget::description() +{ + ostringstream info; + + info << "Standard 2K cartridge, non-bankswitched\n"; + info << CartridgeEnhancedWidget::description(); + + return info.str(); } diff --git a/src/debugger/gui/Cart2KWidget.hxx b/src/debugger/gui/Cart2KWidget.hxx index 0580078f7..123e3ddf5 100644 --- a/src/debugger/gui/Cart2KWidget.hxx +++ b/src/debugger/gui/Cart2KWidget.hxx @@ -20,9 +20,9 @@ class Cartridge2K; -#include "CartDebugWidget.hxx" +#include "CartEnhancedWidget.hxx" -class Cartridge2KWidget : public CartDebugWidget +class Cartridge2KWidget : public CartridgeEnhancedWidget { public: Cartridge2KWidget(GuiObject* boss, const GUI::Font& lfont, @@ -32,10 +32,11 @@ class Cartridge2KWidget : public CartDebugWidget virtual ~Cartridge2KWidget() = default; private: - // No implementation for non-bankswitched ROMs - void loadConfig() override { } - void handleCommand(CommandSender* sender, int cmd, int data, int id) override { } + string manufacturer() override { return "Atari"; } + string description() override; + + private: // Following constructors and assignment operators not supported Cartridge2KWidget() = delete; Cartridge2KWidget(const Cartridge2KWidget&) = delete; diff --git a/src/debugger/gui/Cart3EPlusWidget.cxx b/src/debugger/gui/Cart3EPlusWidget.cxx index 74935f22b..8e7cdf063 100644 --- a/src/debugger/gui/Cart3EPlusWidget.cxx +++ b/src/debugger/gui/Cart3EPlusWidget.cxx @@ -18,121 +18,124 @@ #include "Cart3EPlus.hxx" #include "EditTextWidget.hxx" #include "PopUpWidget.hxx" -#include "Cart3EPlusWidget.hxx" +#include "CartEnhancedWidget.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Cartridge3EPlusWidget::Cartridge3EPlusWidget( GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, int x, int y, int w, int h, Cartridge3EPlus& cart) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h), - myCart(cart) + : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart), + myCart3EP(cart) { - size_t size = cart.mySize; - - ostringstream info; - info << "3EPlus cartridge - (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" - " Upper 512b of bank x (+$200) (W)\n" - << "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"; - - int xpos = 2, - ypos = addBaseInformation(size, "T. Jentzsch", info.str()) + - myLineHeight; - - VariantList bankno; - for(uInt32 i = 0; i < myCart.ROM_BANK_COUNT; ++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) - { - int xpos_s, ypos_s = ypos; - - ostringstream label; - label << "Set segment " << i << " as "; - - new StaticTextWidget(boss, _font, xpos, ypos, _font.getStringWidth(label.str()), - myFontHeight, label.str(), TextAlign::Left); - 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 += 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 += myBankType[i]->getWidth() + 10; - - myBankCommit[i] = new ButtonWidget(boss, _font, xpos, ypos-4, - _font.getStringWidth(" Commit "), myButtonHeight, - "Commit", bankEnum[i]); - myBankCommit[i]->setTarget(this); - addFocusWidget(myBankCommit[i]); - - xpos_s = xpos + myBankCommit[i]->getWidth() + 20; - - StaticTextWidget* t; - int addr1 = start + (i*0x400), addr2 = addr1 + 0x1FF; - - 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); - - int xoffset = xpos_s+t->getWidth() + 10; - myBankState[2*i] = new EditTextWidget(boss, _font, xoffset, ypos_s, - w - xoffset - 10, myLineHeight, ""); - myBankState[2*i]->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); - - myBankState[2*i+1] = new EditTextWidget(boss, _font, xoffset, ypos_s, - w - xoffset - 10, myLineHeight, ""); - myBankState[2*i+1]->setEditable(false, true); - - xpos = 10; - ypos+= 2 * myLineHeight; - } + initialize(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Cartridge3EPlusWidget::saveOldState() +string Cartridge3EPlusWidget::description() { - myOldState.internalram.clear(); + ostringstream info; + size_t size; + const uInt8* image = myCart.getImage(size); + uInt16 numRomBanks = myCart.romBankCount(); + uInt16 numRamBanks = myCart.ramBankCount(); - for(uInt32 i = 0; i < internalRamSize(); ++i) - myOldState.internalram.push_back(myCart.myRAM[i]); + info << "3E+ cartridge - (4..64K ROM + RAM)\n" + << " " << numRomBanks << " 1K ROM banks + " << numRamBanks << " 512b RAM banks\n" + << " mapped into four segments\n" + "ROM bank & segment selected by writing to $3F\n" + "RAM bank & segment selected by writing to $3E\n" + " Lower 512b of segment for read access\n" + " Upper 512b of segment for write access\n" + "Startup bank = 0/-1/-1/0 (ROM)\n"; + + // Eventually, we should query this from the debugger/disassembler + uInt16 start = (image[0x400 - 3] << 8) | image[0x400 - 4]; + start -= start % 0x1000; + info << "Bank RORG" << " = $" << Common::Base::HEX4 << start << "\n"; + + return info.str(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Cartridge3EPlusWidget::bankSelect(int& ypos) +{ + size_t size; + const uInt8* image = myCart.getImage(size); + VariantList banktype; + + VarList::push_back(banktype, "ROM", "ROM"); + VarList::push_back(banktype, "RAM", "RAM"); + + myBankWidgets = make_unique(bankSegs()); + + for(uInt32 seg = 0; seg < bankSegs(); ++seg) + { + int xpos = 2, xpos_s, ypos_s = ypos + 1, width; + ostringstream label; + VariantList items; + + label << "Set segment " << seg << " as "; + + new StaticTextWidget(_boss, _font, xpos, ypos, label.str()); + ypos += myLineHeight + 8; + + xpos += _font.getMaxCharWidth() * 2; + + CartridgeEnhancedWidget::bankList(myCart.romBankCount(), seg, items, width); + myBankWidgets[seg] = + new PopUpWidget(_boss, _font, xpos, ypos - 2, width, + myLineHeight, items, "Bank "); + addFocusWidget(myBankWidgets[seg]); + + xpos += myBankWidgets[seg]->getWidth(); + myBankType[seg] = + new PopUpWidget(_boss, _font, xpos, ypos - 2, 3 * _font.getMaxCharWidth(), + myLineHeight, banktype, " of "); + addFocusWidget(myBankType[seg]); + + xpos = myBankType[seg]->getRight() + _font.getMaxCharWidth(); + + // add "Commit" button (why required?) + myBankCommit[seg] = new ButtonWidget(_boss, _font, xpos, ypos - 4, + _font.getStringWidth(" Commit "), myButtonHeight, + "Commit", bankEnum[seg]); + myBankCommit[seg]->setTarget(this); + addFocusWidget(myBankCommit[seg]); + + xpos_s = myBankCommit[seg]->getRight() + _font.getMaxCharWidth() * 2; + + StaticTextWidget* t; + uInt16 start = (image[0x400 - 3] << 8) | image[0x400 - 4]; + start -= start % 0x1000; + int addr1 = start + (seg * 0x400), addr2 = addr1 + 0x200; + + label.str(""); + label << "$" << Common::Base::HEX4 << addr1 << "-$" << Common::Base::HEX4 << (addr1 + 0x1FF); + t = new StaticTextWidget(_boss, _font, xpos_s, ypos_s + 2, label.str()); + + int xoffset = t->getRight() + _font.getMaxCharWidth(); + myBankState[2 * seg] = new EditTextWidget(_boss, _font, xoffset, ypos_s, + _w - xoffset - 10, myLineHeight, ""); + myBankState[2 * seg]->setEditable(false, true); + ypos_s += myLineHeight + 4; + + label.str(""); + label << "$" << Common::Base::HEX4 << addr2 << "-$" << Common::Base::HEX4 << (addr2 + 0x1FF); + new StaticTextWidget(_boss, _font, xpos_s, ypos_s + 2, label.str()); + + myBankState[2 * seg + 1] = new EditTextWidget(_boss, _font, xoffset, ypos_s, + _w - xoffset - 10, myLineHeight, ""); + myBankState[2 * seg + 1]->setEditable(false, true); + + ypos += 2 * myLineHeight; + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Cartridge3EPlusWidget::loadConfig() { + CartridgeEnhancedWidget::loadConfig(); updateUIState(); - CartDebugWidget::loadConfig(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -159,178 +162,79 @@ void Cartridge3EPlusWidget::handleCommand(CommandSender* sender, } // Ignore bank if either number or type hasn't been selected - if(myBankNumber[segment]->getSelected() < 0 || + if(myBankWidgets[segment]->getSelected() < 0 || myBankType[segment]->getSelected() < 0) return; - uInt8 bank = (segment << myCart.BANK_BITS) | - (myBankNumber[segment]->getSelected() & myCart.BIT_BANK_MASK); + uInt8 bank = myBankWidgets[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(); updateUIState(); } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -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) - { - uInt16 bank = myCart.bankInUse[i*2]; - - if(bank == myCart.BANK_UNDEFINED) // never accessed - { - buf << " U!"; - } - 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 << " /"; - } - - return buf.str(); -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Cartridge3EPlusWidget::updateUIState() { - // Set description for each 512b bank state (@ each index) + // Set description for each 1K segment 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 < myCart3EP.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 @ $" << Common::Base::HEX4 + << (ramBank << myCart3EP.myBankShift) << " (R)"; + myBankState[seg * 2]->setText(buf.str()); + + buf.str(""); + buf << "RAM @ $" << Common::Base::HEX4 + << ((ramBank << myCart3EP.myBankShift) + myCart3EP.myBankSize) << " (W)"; + myBankState[seg * 2 + 1]->setText(buf.str()); + + myBankWidgets[seg]->setSelectedIndex(ramBank); + myBankType[seg]->setSelected("RAM"); } else { - ostringstream buf; - int bankno = bank & myCart.BIT_BANK_MASK; + buf << "ROM @ $" << Common::Base::HEX4 + << ((bank << myCart3EP.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 @ $" << Common::Base::HEX4 + << ((bank << myCart3EP.myBankShift) + myCart3EP.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"); - } - } + myBankWidgets[seg]->setSelectedIndex(bank); + myBankType[seg]->setSelected("ROM"); } } } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 Cartridge3EPlusWidget::internalRamSize() -{ - return 32*1024; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 Cartridge3EPlusWidget::internalRamRPort(int start) -{ - return 0x0000 + 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)"; + << " $f000/$f400/$f800/$fc00 for read access\n" + << " $f200/$f600/$fa00/$fe00 for write access"; return desc.str(); } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const ByteArray& Cartridge3EPlusWidget::internalRamOld(int start, int count) -{ - myRamOld.clear(); - for(int i = 0; i < count; i++) - myRamOld.push_back(myOldState.internalram[start + i]); - return myRamOld; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const ByteArray& Cartridge3EPlusWidget::internalRamCurrent(int start, int count) -{ - myRamCurrent.clear(); - for(int i = 0; i < count; i++) - myRamCurrent.push_back(myCart.myRAM[start + i]); - return myRamCurrent; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Cartridge3EPlusWidget::internalRamSetValue(int addr, uInt8 value) -{ - myCart.myRAM[addr] = value; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 Cartridge3EPlusWidget::internalRamGetValue(int addr) -{ - return myCart.myRAM[addr]; -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - const std::array Cartridge3EPlusWidget::bankEnum = { kBank0Changed, kBank1Changed, kBank2Changed, kBank3Changed diff --git a/src/debugger/gui/Cart3EPlusWidget.hxx b/src/debugger/gui/Cart3EPlusWidget.hxx index 723003376..7cde61042 100644 --- a/src/debugger/gui/Cart3EPlusWidget.hxx +++ b/src/debugger/gui/Cart3EPlusWidget.hxx @@ -23,9 +23,9 @@ class ButtonWidget; class EditTextWidget; class PopUpWidget; -#include "CartDebugWidget.hxx" +#include "CartEnhancedWidget.hxx" -class Cartridge3EPlusWidget : public CartDebugWidget +class Cartridge3EPlusWidget : public CartridgeEnhancedWidget { public: Cartridge3EPlusWidget(GuiObject* boss, const GUI::Font& lfont, @@ -35,21 +35,27 @@ class Cartridge3EPlusWidget : public CartDebugWidget virtual ~Cartridge3EPlusWidget() = default; private: + string manufacturer() override { return "Thomas Jentzsch"; } + + string description() override; + + void bankSelect(int& ypos) override; + + void handleCommand(CommandSender* sender, int cmd, int data, int id) override; + void updateUIState(); - private: - Cartridge3EPlus& myCart; + void loadConfig() override; + + string internalRamDescription() override; + + private: + Cartridge3EPlus& myCart3EP; - std::array myBankNumber{nullptr}; std::array myBankType{nullptr}; std::array myBankCommit{nullptr}; std::array myBankState{nullptr}; - struct CartState { - ByteArray internalram; - }; - CartState myOldState; - enum BankID { kBank0Changed = 'b0CH', kBank1Changed = 'b1CH', @@ -59,22 +65,6 @@ class Cartridge3EPlusWidget : public CartDebugWidget static const std::array bankEnum; private: - void saveOldState() override; - void loadConfig() override; - void handleCommand(CommandSender* sender, int cmd, int data, int id) override; - - string bankState() override; - - // start of functions for Cartridge RAM tab - uInt32 internalRamSize() override; - uInt32 internalRamRPort(int start) override; - string internalRamDescription() override; - const ByteArray& internalRamOld(int start, int count) override; - const ByteArray& internalRamCurrent(int start, int count) override; - void internalRamSetValue(int addr, uInt8 value) override; - uInt8 internalRamGetValue(int addr) override; - // end of functions for Cartridge RAM tab - // Following constructors and assignment operators not supported Cartridge3EPlusWidget() = delete; Cartridge3EPlusWidget(const Cartridge3EPlusWidget&) = delete; diff --git a/src/debugger/gui/Cart3EWidget.cxx b/src/debugger/gui/Cart3EWidget.cxx index 8055cd23b..4683ea7d4 100644 --- a/src/debugger/gui/Cart3EWidget.cxx +++ b/src/debugger/gui/Cart3EWidget.cxx @@ -23,129 +23,134 @@ Cartridge3EWidget::Cartridge3EWidget( GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, int x, int y, int w, int h, Cartridge3E& cart) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h), - myCart(cart), - myNumRomBanks(uInt32(cart.mySize >> 11)), - myNumRamBanks(32) + : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart) { - size_t size = cart.mySize; - - ostringstream info; - info << "3E cartridge - (3F + RAM)\n" - << " 2-256 2K ROM (currently " << myNumRomBanks << "), 32 1K RAM\n" - << "First 2K (ROM) selected by writing to $3F\n" - "First 2K (RAM) selected by writing to $3E\n" - " $F000 - $F3FF (R), $F400 - $F7FF (W)\n" - "Last 2K always points to last 2K of ROM\n"; - if(cart.startBank() < myNumRomBanks) - info << "Startup bank = " << cart.startBank() << " (ROM)\n"; - else - info << "Startup bank = " << (cart.startBank()-myNumRomBanks) << " (RAM)\n"; - - // Eventually, we should query this from the debugger/disassembler - uInt16 start = (cart.myImage[size-3] << 8) | cart.myImage[size-4]; - start -= start % 0x1000; - info << "Bank RORG" << " = $" << Common::Base::HEX4 << start << "\n"; - - int xpos = 2, - ypos = addBaseInformation(size, "TigerVision", info.str()) + myLineHeight; - - VariantList romitems; - for(uInt32 i = 0; i < myNumRomBanks; ++i) - VarList::push_back(romitems, i); - VarList::push_back(romitems, "Inactive", ""); - - VariantList ramitems; - for(uInt32 i = 0; i < myNumRamBanks; ++i) - VarList::push_back(ramitems, i); - VarList::push_back(ramitems, "Inactive", ""); - - ostringstream label; - label << "Set bank ($" << Common::Base::HEX4 << start << " - $" - << (start+0x7FF) << "): "; - - new StaticTextWidget(_boss, _font, xpos, ypos, _font.getStringWidth(label.str()), - myFontHeight, label.str(), TextAlign::Left); - ypos += myLineHeight + 8; - - xpos += 40; - myROMBank = - new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("0 ($3E) "), - myLineHeight, romitems, "ROM ($3F) ", - _font.getStringWidth("ROM ($3F) "), kROMBankChanged); - myROMBank->setTarget(this); - addFocusWidget(myROMBank); - - xpos += myROMBank->getWidth() + 20; - myRAMBank = - new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("0 ($3E) "), - myLineHeight, ramitems, "RAM ($3E) ", - _font.getStringWidth("RAM ($3E) "), kRAMBankChanged); - myRAMBank->setTarget(this); - addFocusWidget(myRAMBank); + initialize(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Cartridge3EWidget::saveOldState() +string Cartridge3EWidget::description() { - myOldState.internalram.clear(); + ostringstream info; + size_t size; + const uInt8* image = myCart.getImage(size); + uInt16 numRomBanks = myCart.romBankCount(); + uInt16 numRamBanks = myCart.ramBankCount(); - for(uInt32 i = 0; i < internalRamSize(); ++i) - myOldState.internalram.push_back(myCart.myRAM[i]); - myOldState.bank = myCart.myCurrentBank; + info << "3E cartridge (3F + RAM),\n" + << " " << numRomBanks << " 2K ROM banks, " << numRamBanks << " 1K RAM banks\n" + << "First 2K (ROM) selected by writing to $3F\n" + "First 2K (RAM) selected by writing to $3E\n"; + info << CartridgeEnhancedWidget::ramDescription(); + info << "Last 2K always points to last 2K of ROM\n"; + + if(myCart.startBank() < numRomBanks) + info << "Startup bank = " << myCart.startBank() << " (ROM)\n"; + else + info << "Startup bank = " << (myCart.startBank() - numRomBanks) << " (RAM)\n"; + + // Eventually, we should query this from the debugger/disassembler + uInt16 start = (image[size-3] << 8) | image[size-4]; + start -= start % 0x1000; + info << "Bank RORG" << " = $" << Common::Base::HEX4 << start << "\n"; + + return info.str(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Cartridge3EWidget::bankList(uInt16 bankCount, int seg, VariantList& items, int& width) +{ + CartridgeEnhancedWidget::bankList(bankCount, seg, items, width); + + VarList::push_back(items, "Inactive", ""); + width = _font.getStringWidth("Inactive"); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Cartridge3EWidget::bankSelect(int& ypos) +{ + int xpos = 2; + VariantList items; + int pw; + + myBankWidgets = make_unique(2); + + bankList(myCart.romBankCount(), 0, items, pw); + myBankWidgets[0] = + new PopUpWidget(_boss, _font, xpos, ypos - 2, pw, + myLineHeight, items, "Set bank ", + _font.getStringWidth("Set bank "), kBankChanged); + myBankWidgets[0]->setTarget(this); + myBankWidgets[0]->setID(0); + addFocusWidget(myBankWidgets[0]); + + StaticTextWidget* t = new StaticTextWidget(_boss, _font, myBankWidgets[0]->getRight(), ypos - 1, " (ROM)"); + + xpos = t->getRight() + 20; + items.clear(); + bankList(myCart.ramBankCount(), 0, items, pw); + myBankWidgets[1] = + new PopUpWidget(_boss, _font, xpos, ypos - 2, pw, + myLineHeight, items, "", 0, kRAMBankChanged); + myBankWidgets[1]->setTarget(this); + myBankWidgets[1]->setID(1); + addFocusWidget(myBankWidgets[1]); + + new StaticTextWidget(_boss, _font, myBankWidgets[1]->getRight(), ypos - 1, " (RAM)"); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Cartridge3EWidget::loadConfig() { - if(myCart.myCurrentBank < 256) + uInt16 oldBank = myOldState.banks[0]; + uInt16 bank = myCart.getBank(); + + if(myCart.getBank() < myCart.romBankCount()) { - myROMBank->setSelectedIndex(myCart.myCurrentBank % myNumRomBanks, myOldState.bank != myCart.myCurrentBank); - myRAMBank->setSelectedMax(myOldState.bank >= 256); + myBankWidgets[0]->setSelectedIndex(bank, oldBank != bank); + myBankWidgets[1]->setSelectedMax(oldBank >= myCart.romBankCount()); } else { - myROMBank->setSelectedMax(myOldState.bank < 256); - myRAMBank->setSelectedIndex(myCart.myCurrentBank - 256, myOldState.bank != myCart.myCurrentBank); + myBankWidgets[0]->setSelectedMax(oldBank < myCart.romBankCount()); + myBankWidgets[1]->setSelectedIndex(bank - myCart.romBankCount(), oldBank != bank); } CartDebugWidget::loadConfig(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Cartridge3EWidget::handleCommand(CommandSender* sender, - int cmd, int data, int id) +void Cartridge3EWidget::handleCommand(CommandSender* sender, int cmd, int data, int id) { uInt16 bank = 0; - if(cmd == kROMBankChanged) + if(cmd == kBankChanged) { - if(myROMBank->getSelected() < int(myNumRomBanks)) + if(myBankWidgets[0]->getSelected() < myCart.romBankCount()) { - bank = myROMBank->getSelected(); - myRAMBank->setSelectedMax(); + bank = myBankWidgets[0]->getSelected(); + myBankWidgets[1]->setSelectedMax(); } else { - bank = 256; // default to first RAM bank - myRAMBank->setSelectedIndex(0); + bank = myCart.romBankCount(); // default to first RAM bank + myBankWidgets[1]->setSelectedIndex(0); } } else if(cmd == kRAMBankChanged) { - if(myRAMBank->getSelected() < int(myNumRamBanks)) + if(myBankWidgets[1]->getSelected() < myCart.ramBankCount()) { - myROMBank->setSelectedMax(); - bank = myRAMBank->getSelected() + 256; + myBankWidgets[0]->setSelectedMax(); + bank = myBankWidgets[1]->getSelected() + myCart.romBankCount(); } else { bank = 0; // default to first ROM bank - myROMBank->setSelectedIndex(0); + myBankWidgets[0]->setSelectedIndex(0); } } - myCart.unlockBank(); myCart.bank(bank); myCart.lockBank(); @@ -156,65 +161,13 @@ void Cartridge3EWidget::handleCommand(CommandSender* sender, string Cartridge3EWidget::bankState() { ostringstream& buf = buffer(); + uInt16 bank = myCart.getBank(); - uInt16& bank = myCart.myCurrentBank; - if(bank < 256) - buf << "ROM bank #" << std::dec << bank % myNumRomBanks << ", RAM inactive"; + if(bank < myCart.romBankCount()) + buf << "ROM bank #" << std::dec << bank % myCart.romBankCount() << ", RAM inactive"; else - buf << "ROM inactive, RAM bank #" << std::dec << bank % myNumRamBanks; + buf << "ROM inactive, RAM bank #" + << std::dec << (bank - myCart.romBankCount()) % myCart.ramBankCount(); return buf.str(); } - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 Cartridge3EWidget::internalRamSize() -{ - return 32*1024; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 Cartridge3EWidget::internalRamRPort(int start) -{ - return 0x0000 + start; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string Cartridge3EWidget::internalRamDescription() -{ - ostringstream desc; - desc << "Accessible 1K at a time via:\n" - << " $F000 - $F3FF used for Read Access\n" - << " $F400 - $F7FF used for Write Access"; - - return desc.str(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const ByteArray& Cartridge3EWidget::internalRamOld(int start, int count) -{ - myRamOld.clear(); - for(int i = 0; i < count; i++) - myRamOld.push_back(myOldState.internalram[start + i]); - return myRamOld; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const ByteArray& Cartridge3EWidget::internalRamCurrent(int start, int count) -{ - myRamCurrent.clear(); - for(int i = 0; i < count; i++) - myRamCurrent.push_back(myCart.myRAM[start + i]); - return myRamCurrent; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Cartridge3EWidget::internalRamSetValue(int addr, uInt8 value) -{ - myCart.myRAM[addr] = value; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 Cartridge3EWidget::internalRamGetValue(int addr) -{ - return myCart.myRAM[addr]; -} diff --git a/src/debugger/gui/Cart3EWidget.hxx b/src/debugger/gui/Cart3EWidget.hxx index 623a8877e..424d9a577 100644 --- a/src/debugger/gui/Cart3EWidget.hxx +++ b/src/debugger/gui/Cart3EWidget.hxx @@ -19,11 +19,12 @@ #define CARTRIDGE3E_WIDGET_HXX class Cartridge3E; -class PopUpWidget; -#include "CartDebugWidget.hxx" +#include "CartEnhancedWidget.hxx" -class Cartridge3EWidget : public CartDebugWidget +// Note: This class supports 3EX too + +class Cartridge3EWidget : public CartridgeEnhancedWidget { public: Cartridge3EWidget(GuiObject* boss, const GUI::Font& lfont, @@ -33,39 +34,28 @@ class Cartridge3EWidget : public CartDebugWidget virtual ~Cartridge3EWidget() = default; private: - Cartridge3E& myCart; - const uInt32 myNumRomBanks{0}; - const uInt32 myNumRamBanks{0}; - PopUpWidget *myROMBank{nullptr}, *myRAMBank{nullptr}; - - struct CartState { - ByteArray internalram; - uInt16 bank; - }; - CartState myOldState; - enum { - kROMBankChanged = 'rmCH', kRAMBankChanged = 'raCH' }; private: - void saveOldState() override; + string manufacturer() override { return "Andrew Davie & Thomas Jentzsch"; } + + string description() override; + + void bankList(uInt16 bankCount, int seg, VariantList& items, int& width) override; + + void bankSelect(int& ypos) override; + + uInt16 bankSegs() override { return 1; } + void loadConfig() override; + void handleCommand(CommandSender* sender, int cmd, int data, int id) override; string bankState() override; - // start of functions for Cartridge RAM tab - uInt32 internalRamSize() override; - uInt32 internalRamRPort(int start) override; - string internalRamDescription() override; - const ByteArray& internalRamOld(int start, int count) override; - const ByteArray& internalRamCurrent(int start, int count) override; - void internalRamSetValue(int addr, uInt8 value) override; - uInt8 internalRamGetValue(int addr) override; - // end of functions for Cartridge RAM tab - + private: // Following constructors and assignment operators not supported Cartridge3EWidget() = delete; Cartridge3EWidget(const Cartridge3EWidget&) = delete; diff --git a/src/debugger/gui/Cart3FWidget.cxx b/src/debugger/gui/Cart3FWidget.cxx index 223e56cc0..ff5b0d219 100644 --- a/src/debugger/gui/Cart3FWidget.cxx +++ b/src/debugger/gui/Cart3FWidget.cxx @@ -16,79 +16,33 @@ //============================================================================ #include "Cart3F.hxx" -#include "PopUpWidget.hxx" #include "Cart3FWidget.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Cartridge3FWidget::Cartridge3FWidget( GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, int x, int y, int w, int h, Cartridge3F& cart) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h), - myCart(cart) + : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart) { - size_t size = cart.mySize; + myHotspotDelta = 0; + initialize(); +} +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string Cartridge3FWidget::description() +{ ostringstream info; - info << "Tigervision 3F cartridge, 2-256 2K banks\n" - << "Startup bank = " << cart.startBank() << " or undetermined\n" - << "First 2K bank selected by writing to $3F\n" - << "Last 2K always points to last 2K of ROM\n"; + size_t size; + const uInt8* image = myCart.getImage(size); + info << "Tigervision 3F cartridge, 2 - 256 2K banks\n" + << "First 2K bank selected by writing to " << hotspotStr() << "\n" + << "Last 2K always points to last 2K of ROM\n" + << "Startup bank = " << myCart.startBank() << " or undetermined\n"; // Eventually, we should query this from the debugger/disassembler - uInt16 start = (cart.myImage[size-3] << 8) | cart.myImage[size-4]; + uInt16 start = (image[size-3] << 8) | image[size-4]; start -= start % 0x1000; - info << "Bank RORG" << " = $" << Common::Base::HEX4 << start << "\n"; + info << "Bank RORG $" << Common::Base::HEX4 << start << "\n"; - int xpos = 2, - ypos = addBaseInformation(size, "TigerVision", info.str()) + myLineHeight; - - VariantList items; - for(uInt16 i = 0; i < cart.bankCount(); ++i) - VarList::push_back(items, Variant(i).toString() + " ($3F)"); - - ostringstream label; - label << "Set bank ($" << Common::Base::HEX4 << start << " - $" << - (start+0x7FF) << ") "; - myBank = - new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("0 ($3F) "), - myLineHeight, items, label.str(), - _font.getStringWidth(label.str()), kBankChanged); - myBank->setTarget(this); - addFocusWidget(myBank); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Cartridge3FWidget::loadConfig() -{ - Debugger& dbg = instance().debugger(); - CartDebug& cart = dbg.cartDebug(); - const CartState& state = static_cast(cart.getState()); - const CartState& oldstate = static_cast(cart.getOldState()); - - myBank->setSelectedIndex(myCart.getBank(0), state.bank != oldstate.bank); - - CartDebugWidget::loadConfig(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Cartridge3FWidget::handleCommand(CommandSender* sender, - int cmd, int data, int id) -{ - if(cmd == kBankChanged) - { - myCart.unlockBank(); - myCart.bank(myBank->getSelected()); - myCart.lockBank(); - invalidate(); - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string Cartridge3FWidget::bankState() -{ - ostringstream& buf = buffer(); - - buf << "Bank = #" << std::dec << myCart.myCurrentBank << ", hotspot = $3F"; - - return buf.str(); + return info.str(); } diff --git a/src/debugger/gui/Cart3FWidget.hxx b/src/debugger/gui/Cart3FWidget.hxx index 859184057..ad24d8e56 100644 --- a/src/debugger/gui/Cart3FWidget.hxx +++ b/src/debugger/gui/Cart3FWidget.hxx @@ -19,11 +19,10 @@ #define CARTRIDGE3F_WIDGET_HXX class Cartridge3F; -class PopUpWidget; -#include "CartDebugWidget.hxx" +#include "CartEnhancedWidget.hxx" -class Cartridge3FWidget : public CartDebugWidget +class Cartridge3FWidget : public CartridgeEnhancedWidget { public: Cartridge3FWidget(GuiObject* boss, const GUI::Font& lfont, @@ -33,17 +32,13 @@ class Cartridge3FWidget : public CartDebugWidget virtual ~Cartridge3FWidget() = default; private: - Cartridge3F& myCart; - PopUpWidget* myBank{nullptr}; + string manufacturer() override { return "TigerVision"; } - enum { kBankChanged = 'bkCH' }; + string description() override; + + uInt16 bankSegs() override { return 1; } private: - void loadConfig() override; - void handleCommand(CommandSender* sender, int cmd, int data, int id) override; - - string bankState() override; - // Following constructors and assignment operators not supported Cartridge3FWidget() = delete; Cartridge3FWidget(const Cartridge3FWidget&) = delete; diff --git a/src/debugger/gui/Cart4KSCWidget.cxx b/src/debugger/gui/Cart4KSCWidget.cxx index 8729c077f..b6bc05d51 100644 --- a/src/debugger/gui/Cart4KSCWidget.cxx +++ b/src/debugger/gui/Cart4KSCWidget.cxx @@ -15,8 +15,6 @@ // this file, and for a DISCLAIMER OF ALL WARRANTIES. //============================================================================ -#include "Debugger.hxx" -#include "CartDebug.hxx" #include "Cart4KSC.hxx" #include "Cart4KSCWidget.hxx" @@ -24,87 +22,18 @@ Cartridge4KSCWidget::Cartridge4KSCWidget( GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, int x, int y, int w, int h, Cartridge4KSC& cart) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h), - myCart(cart) + : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart) { - // Eventually, we should query this from the debugger/disassembler - uInt16 start = (cart.myImage[0xFFD] << 8) | cart.myImage[0xFFC]; - start -= start % 0x1000; + initialize(); +} +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string Cartridge4KSCWidget::description() +{ ostringstream info; - info << "4KSC cartridge, non-bankswitched\n" - << "128 bytes RAM @ $F000 - $F0FF\n" - << " $F080 - $F0FF (R), $F000 - $F07F (W)\n" - << "Accessible @ $" << Common::Base::HEX4 << start << " - " - << "$" << (start + 0xFFF); - addBaseInformation(4096, "homebrew intermediate format", info.str()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Cartridge4KSCWidget::saveOldState() -{ - myOldState.internalram.clear(); - - for(uInt32 i = 0; i < internalRamSize(); ++i) - myOldState.internalram.push_back(myCart.myRAM[i]); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 Cartridge4KSCWidget::internalRamSize() -{ - return 128; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 Cartridge4KSCWidget::internalRamRPort(int start) -{ - return 0xF080 + start; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string Cartridge4KSCWidget::internalRamDescription() -{ - ostringstream desc; - desc << "$F000 - $F07F used for Write Access\n" - << "$F080 - $F0FF used for Read Access"; - - return desc.str(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const ByteArray& Cartridge4KSCWidget::internalRamOld(int start, int count) -{ - myRamOld.clear(); - for(int i = 0; i < count; i++) - myRamOld.push_back(myOldState.internalram[start + i]); - return myRamOld; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const ByteArray& Cartridge4KSCWidget::internalRamCurrent(int start, int count) -{ - myRamCurrent.clear(); - for(int i = 0; i < count; i++) - myRamCurrent.push_back(myCart.myRAM[start + i]); - return myRamCurrent; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Cartridge4KSCWidget::internalRamSetValue(int addr, uInt8 value) -{ - myCart.myRAM[addr] = value; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 Cartridge4KSCWidget::internalRamGetValue(int addr) -{ - return myCart.myRAM[addr]; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string Cartridge4KSCWidget::internalRamLabel(int addr) -{ - CartDebug& dbg = instance().debugger().cartDebug(); - return dbg.getLabel(addr + 0xF080, false); + info << "4KSC cartridge, non-bankswitched\n"; + info << CartridgeEnhancedWidget::description(); + + return info.str(); } diff --git a/src/debugger/gui/Cart4KSCWidget.hxx b/src/debugger/gui/Cart4KSCWidget.hxx index 6a6ee2d8c..624a5623a 100644 --- a/src/debugger/gui/Cart4KSCWidget.hxx +++ b/src/debugger/gui/Cart4KSCWidget.hxx @@ -20,9 +20,9 @@ class Cartridge4KSC; -#include "CartDebugWidget.hxx" +#include "CartEnhancedWidget.hxx" -class Cartridge4KSCWidget : public CartDebugWidget +class Cartridge4KSCWidget : public CartridgeEnhancedWidget { public: Cartridge4KSCWidget(GuiObject* boss, const GUI::Font& lfont, @@ -32,30 +32,11 @@ class Cartridge4KSCWidget : public CartDebugWidget virtual ~Cartridge4KSCWidget() = default; private: - Cartridge4KSC& myCart; - struct CartState { - ByteArray internalram; - }; - CartState myOldState; + string manufacturer() override { return "homebrew intermediate format"; } + + string description() override; private: - // No implementation for non-bankswitched ROMs - void loadConfig() override { } - void handleCommand(CommandSender* sender, int cmd, int data, int id) override { } - - void saveOldState() override; - - // start of functions for Cartridge RAM tab - uInt32 internalRamSize() override; - uInt32 internalRamRPort(int start) override; - string internalRamDescription() override; - const ByteArray& internalRamOld(int start, int count) override; - const ByteArray& internalRamCurrent(int start, int count) override; - void internalRamSetValue(int addr, uInt8 value) override; - uInt8 internalRamGetValue(int addr) override; - string internalRamLabel(int addr) override; - // end of functions for Cartridge RAM tab - // Following constructors and assignment operators not supported Cartridge4KSCWidget() = delete; Cartridge4KSCWidget(const Cartridge4KSCWidget&) = delete; diff --git a/src/debugger/gui/Cart4KWidget.cxx b/src/debugger/gui/Cart4KWidget.cxx index ed4ef7218..deca594fb 100644 --- a/src/debugger/gui/Cart4KWidget.cxx +++ b/src/debugger/gui/Cart4KWidget.cxx @@ -22,15 +22,29 @@ Cartridge4KWidget::Cartridge4KWidget( GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, int x, int y, int w, int h, Cartridge4K& cart) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h) + : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart) { - // Eventually, we should query this from the debugger/disassembler - uInt16 start = (cart.myImage[0xFFD] << 8) | cart.myImage[0xFFC]; - start -= start % 0x1000; + initialize(); - ostringstream info; - info << "Standard 4K cartridge, non-bankswitched\n" - << "Accessible @ $" << Common::Base::HEX4 << start << " - " - << "$" << (start + 0xFFF); - addBaseInformation(4096, "Atari", info.str()); + //// Eventually, we should query this from the debugger/disassembler + //uInt16 start = (cart.myImage[0xFFD] << 8) | cart.myImage[0xFFC]; + //start -= start % 0x1000; + + //ostringstream info; + //info << "Standard 4K cartridge, non-bankswitched\n" + // << "Accessible @ $" << Common::Base::HEX4 << start << " - " + // << "$" << (start + 0xFFF); + //addBaseInformation(4096, "Atari", info.str()); } + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string Cartridge4KWidget::description() +{ + ostringstream info; + + info << "Standard 4K cartridge, non-bankswitched\n"; + info << CartridgeEnhancedWidget::description(); + + return info.str(); +} + diff --git a/src/debugger/gui/Cart4KWidget.hxx b/src/debugger/gui/Cart4KWidget.hxx index 3ba39a3d9..3fd18eb66 100644 --- a/src/debugger/gui/Cart4KWidget.hxx +++ b/src/debugger/gui/Cart4KWidget.hxx @@ -20,9 +20,9 @@ class Cartridge4K; -#include "CartDebugWidget.hxx" +#include "CartEnhanced.hxx" -class Cartridge4KWidget : public CartDebugWidget +class Cartridge4KWidget : public CartridgeEnhancedWidget { public: Cartridge4KWidget(GuiObject* boss, const GUI::Font& lfont, @@ -32,10 +32,11 @@ class Cartridge4KWidget : public CartDebugWidget virtual ~Cartridge4KWidget() = default; private: - // No implementation for non-bankswitched ROMs - void loadConfig() override { } - void handleCommand(CommandSender* sender, int cmd, int data, int id) override { } + string manufacturer() override { return "Atari"; } + string description() override; + + private: // Following constructors and assignment operators not supported Cartridge4KWidget() = delete; Cartridge4KWidget(const Cartridge4KWidget&) = delete; diff --git a/src/debugger/gui/CartBFSCWidget.cxx b/src/debugger/gui/CartBFSCWidget.cxx index 819e72b39..15f10ff62 100644 --- a/src/debugger/gui/CartBFSCWidget.cxx +++ b/src/debugger/gui/CartBFSCWidget.cxx @@ -15,221 +15,25 @@ // this file, and for a DISCLAIMER OF ALL WARRANTIES. //============================================================================ -#include "Debugger.hxx" -#include "CartDebug.hxx" #include "CartBFSC.hxx" -#include "PopUpWidget.hxx" #include "CartBFSCWidget.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeBFSCWidget::CartridgeBFSCWidget( - GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, - int x, int y, int w, int h, CartridgeBFSC& cart) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h), - myCart(cart) + GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, + int x, int y, int w, int h, CartridgeBFSC& cart) + : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart) { - uInt32 size = 64 * 4096; + initialize(); +} +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string CartridgeBFSCWidget::description() +{ ostringstream info; - info << "256K BFSC + RAM, 64 4K banks\n" - << "128 bytes RAM @ $F000 - $F0FF\n" - << " $F080 - $F0FF (R), $F000 - $F07F (W)\n" - << "Startup bank = " << cart.startBank() << "\n"; - // Eventually, we should query this from the debugger/disassembler - for(uInt32 i = 0, offset = 0xFFC, spot = 0xF80; i < 64; ++i, offset += 0x1000) - { - uInt16 start = (cart.myImage[offset+1] << 8) | cart.myImage[offset]; - start -= start % 0x1000; - info << "Bank " << std::dec << i << " @ $" << Common::Base::HEX4 << (start + 0x100) - << " - " << "$" << (start + 0xFFF) << " (hotspot = $F" << (spot+i) << ")\n"; - } + info << "256K BFSC + RAM, 64 4K banks\n"; + info << CartridgeEnhancedWidget::description(); - int xpos = 2, - ypos = addBaseInformation(size, "CPUWIZ", info.str()) + myLineHeight; - - VariantList items; - VarList::push_back(items, " 0 ($FF80)"); - VarList::push_back(items, " 1 ($FF81)"); - VarList::push_back(items, " 2 ($FF82)"); - VarList::push_back(items, " 3 ($FF83)"); - VarList::push_back(items, " 4 ($FF84)"); - VarList::push_back(items, " 5 ($FF85)"); - VarList::push_back(items, " 6 ($FF86)"); - VarList::push_back(items, " 7 ($FF87)"); - VarList::push_back(items, " 8 ($FF88)"); - VarList::push_back(items, " 9 ($FF89)"); - VarList::push_back(items, "10 ($FF8A)"); - VarList::push_back(items, "11 ($FF8B)"); - VarList::push_back(items, "12 ($FF8C)"); - VarList::push_back(items, "13 ($FF8D)"); - VarList::push_back(items, "14 ($FF8E)"); - VarList::push_back(items, "15 ($FF8F)"); - VarList::push_back(items, "16 ($FF90)"); - VarList::push_back(items, "17 ($FF91)"); - VarList::push_back(items, "18 ($FF92)"); - VarList::push_back(items, "19 ($FF93)"); - VarList::push_back(items, "20 ($FF94)"); - VarList::push_back(items, "21 ($FF95)"); - VarList::push_back(items, "22 ($FF96)"); - VarList::push_back(items, "23 ($FF97)"); - VarList::push_back(items, "24 ($FF98)"); - VarList::push_back(items, "25 ($FF99)"); - VarList::push_back(items, "26 ($FF9A)"); - VarList::push_back(items, "27 ($FF9B)"); - VarList::push_back(items, "28 ($FF9C)"); - VarList::push_back(items, "29 ($FF9D)"); - VarList::push_back(items, "30 ($FF9E)"); - VarList::push_back(items, "31 ($FF9F)"); - VarList::push_back(items, "32 ($FFA0)"); - VarList::push_back(items, "33 ($FFA1)"); - VarList::push_back(items, "34 ($FFA2)"); - VarList::push_back(items, "35 ($FFA3)"); - VarList::push_back(items, "36 ($FFA4)"); - VarList::push_back(items, "37 ($FFA5)"); - VarList::push_back(items, "38 ($FFA6)"); - VarList::push_back(items, "39 ($FFA7)"); - VarList::push_back(items, "40 ($FFA8)"); - VarList::push_back(items, "41 ($FFA9)"); - VarList::push_back(items, "42 ($FFAA)"); - VarList::push_back(items, "43 ($FFAB)"); - VarList::push_back(items, "44 ($FFAC)"); - VarList::push_back(items, "45 ($FFAD)"); - VarList::push_back(items, "46 ($FFAE)"); - VarList::push_back(items, "47 ($FFAF)"); - VarList::push_back(items, "48 ($FFB0)"); - VarList::push_back(items, "49 ($FFB1)"); - VarList::push_back(items, "50 ($FFB2)"); - VarList::push_back(items, "51 ($FFB3)"); - VarList::push_back(items, "52 ($FFB4)"); - VarList::push_back(items, "53 ($FFB5)"); - VarList::push_back(items, "54 ($FFB6)"); - VarList::push_back(items, "55 ($FFB7)"); - VarList::push_back(items, "56 ($FFB8)"); - VarList::push_back(items, "57 ($FFB9)"); - VarList::push_back(items, "58 ($FFBA)"); - VarList::push_back(items, "59 ($FFBB)"); - VarList::push_back(items, "60 ($FFBC)"); - VarList::push_back(items, "61 ($FFBD)"); - VarList::push_back(items, "62 ($FFBE)"); - VarList::push_back(items, "63 ($FFBF)"); - - myBank = - new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("63 ($FFBF)"), - myLineHeight, items, "Set bank ", - 0, kBankChanged); - myBank->setTarget(this); - addFocusWidget(myBank); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeBFSCWidget::saveOldState() -{ - myOldState.internalram.clear(); - - for(uInt32 i = 0; i < internalRamSize(); ++i) - myOldState.internalram.push_back(myCart.myRAM[i]); - - myOldState.bank = myCart.getBank(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeBFSCWidget::loadConfig() -{ - myBank->setSelectedIndex(myCart.getBank(), myCart.getBank() != myOldState.bank); - - CartDebugWidget::loadConfig(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeBFSCWidget::handleCommand(CommandSender* sender, - int cmd, int data, int id) -{ - if(cmd == kBankChanged) - { - myCart.unlockBank(); - myCart.bank(myBank->getSelected()); - myCart.lockBank(); - invalidate(); - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeBFSCWidget::bankState() -{ - ostringstream& buf = buffer(); - - static constexpr std::array spot = { - "$FF80", "$FF81", "$FF82", "$FF83", "$FF84", "$FF85", "$FF86", "$FF87", - "$FF88", "$FF89", "$FF8A", "$FF8B", "$FF8C", "$FF8D", "$FF8E", "$FF8F", - "$FF90", "$FF91", "$FF92", "$FF93", "$FF94", "$FF95", "$FF96", "$FF97", - "$FF98", "$FF99", "$FF9A", "$FF9B", "$FF9C", "$FF9D", "$FF9E", "$FF9F", - "$FFA0", "$FFA1", "$FFA2", "$FFA3", "$FFA4", "$FFA5", "$FFA6", "$FFA7", - "$FFA8", "$FFA9", "$FFAA", "$FFAB", "$FFAC", "$FFAD", "$FFAE", "$FFAF", - "$FFB0", "$FFB1", "$FFB2", "$FFB3", "$FFB4", "$FFB5", "$FFB6", "$FFB7", - "$FFB8", "$FFB9", "$FFBA", "$FFBB", "$FFBC", "$FFBD", "$FFBE", "$FFBF" - }; - buf << "Bank = " << std::dec << myCart.getBank() - << ", hotspot = " << spot[myCart.getBank()]; - - return buf.str(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 CartridgeBFSCWidget::internalRamSize() -{ - return 128; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 CartridgeBFSCWidget::internalRamRPort(int start) -{ - return 0xF080 + start; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeBFSCWidget::internalRamDescription() -{ - ostringstream desc; - desc << "$F000 - $F07F used for Write Access\n" - << "$F080 - $F0FF used for Read Access"; - - return desc.str(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const ByteArray& CartridgeBFSCWidget::internalRamOld(int start, int count) -{ - myRamOld.clear(); - for(int i = 0; i < count; i++) - myRamOld.push_back(myOldState.internalram[start + i]); - return myRamOld; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const ByteArray& CartridgeBFSCWidget::internalRamCurrent(int start, int count) -{ - myRamCurrent.clear(); - for(int i = 0; i < count; i++) - myRamCurrent.push_back(myCart.myRAM[start + i]); - return myRamCurrent; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeBFSCWidget::internalRamSetValue(int addr, uInt8 value) -{ - myCart.myRAM[addr] = value; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeBFSCWidget::internalRamGetValue(int addr) -{ - return myCart.myRAM[addr]; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeBFSCWidget::internalRamLabel(int addr) -{ - CartDebug& dbg = instance().debugger().cartDebug(); - return dbg.getLabel(addr + 0xF080, false); + return info.str(); } diff --git a/src/debugger/gui/CartBFSCWidget.hxx b/src/debugger/gui/CartBFSCWidget.hxx index 3068672f0..739f41388 100644 --- a/src/debugger/gui/CartBFSCWidget.hxx +++ b/src/debugger/gui/CartBFSCWidget.hxx @@ -19,11 +19,10 @@ #define CARTRIDGEBFSC_WIDGET_HXX class CartridgeBFSC; -class PopUpWidget; -#include "CartDebugWidget.hxx" +#include "CartEnhancedWidget.hxx" -class CartridgeBFSCWidget : public CartDebugWidget +class CartridgeBFSCWidget : public CartridgeEnhancedWidget { public: CartridgeBFSCWidget(GuiObject* boss, const GUI::Font& lfont, @@ -33,35 +32,11 @@ class CartridgeBFSCWidget : public CartDebugWidget virtual ~CartridgeBFSCWidget() = default; private: - CartridgeBFSC& myCart; - PopUpWidget* myBank{nullptr}; + string manufacturer() override { return "CPUWIZ"; } - struct CartState { - ByteArray internalram; - uInt16 bank{0}; - }; - CartState myOldState; - - enum { kBankChanged = 'bkCH' }; + string description() override; private: - void saveOldState() override; - void loadConfig() override; - void handleCommand(CommandSender* sender, int cmd, int data, int id) override; - - string bankState() override; - - // start of functions for Cartridge RAM tab - uInt32 internalRamSize() override; - uInt32 internalRamRPort(int start) override; - string internalRamDescription() override; - const ByteArray& internalRamOld(int start, int count) override; - const ByteArray& internalRamCurrent(int start, int count) override; - void internalRamSetValue(int addr, uInt8 value) override; - uInt8 internalRamGetValue(int addr) override; - string internalRamLabel(int addr) override; - // end of functions for Cartridge RAM tab - // Following constructors and assignment operators not supported CartridgeBFSCWidget() = delete; CartridgeBFSCWidget(const CartridgeBFSCWidget&) = delete; diff --git a/src/debugger/gui/CartBFWidget.cxx b/src/debugger/gui/CartBFWidget.cxx index 96ddc5662..cbaabf076 100644 --- a/src/debugger/gui/CartBFWidget.cxx +++ b/src/debugger/gui/CartBFWidget.cxx @@ -16,151 +16,24 @@ //============================================================================ #include "CartBF.hxx" -#include "PopUpWidget.hxx" #include "CartBFWidget.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeBFWidget::CartridgeBFWidget( GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, int x, int y, int w, int h, CartridgeBF& cart) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h), - myCart(cart) + : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart) { - uInt32 size = 64 * 4096; + initialize(); +} +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string CartridgeBFWidget::description() +{ ostringstream info; - info << "BF cartridge, 64 4K banks\n" - << "Startup bank = " << cart.startBank() << "\n"; - // Eventually, we should query this from the debugger/disassembler - for(uInt32 i = 0, offset = 0xFFC, spot = 0xF80; i < 64; ++i, offset += 0x1000) - { - uInt16 start = (cart.myImage[offset+1] << 8) | cart.myImage[offset]; - start -= start % 0x1000; - info << "Bank " << std::dec << i << " @ $" << Common::Base::HEX4 << start << " - " - << "$" << (start + 0xFFF) << " (hotspot = $F" << (spot+i) << ")\n"; - } + info << "256K BF cartridge, 64 4K banks\n"; + info << CartridgeEnhancedWidget::description(); - int xpos = 2, - ypos = addBaseInformation(size, "CPUWIZ", info.str()) + myLineHeight; - - VariantList items; - VarList::push_back(items, " 0 ($FF80)"); - VarList::push_back(items, " 1 ($FF81)"); - VarList::push_back(items, " 2 ($FF82)"); - VarList::push_back(items, " 3 ($FF83)"); - VarList::push_back(items, " 4 ($FF84)"); - VarList::push_back(items, " 5 ($FF85)"); - VarList::push_back(items, " 6 ($FF86)"); - VarList::push_back(items, " 7 ($FF87)"); - VarList::push_back(items, " 8 ($FF88)"); - VarList::push_back(items, " 9 ($FF89)"); - VarList::push_back(items, "10 ($FF8A)"); - VarList::push_back(items, "11 ($FF8B)"); - VarList::push_back(items, "12 ($FF8C)"); - VarList::push_back(items, "13 ($FF8D)"); - VarList::push_back(items, "14 ($FF8E)"); - VarList::push_back(items, "15 ($FF8F)"); - VarList::push_back(items, "16 ($FF90)"); - VarList::push_back(items, "17 ($FF91)"); - VarList::push_back(items, "18 ($FF92)"); - VarList::push_back(items, "19 ($FF93)"); - VarList::push_back(items, "20 ($FF94)"); - VarList::push_back(items, "21 ($FF95)"); - VarList::push_back(items, "22 ($FF96)"); - VarList::push_back(items, "23 ($FF97)"); - VarList::push_back(items, "24 ($FF98)"); - VarList::push_back(items, "25 ($FF99)"); - VarList::push_back(items, "26 ($FF9A)"); - VarList::push_back(items, "27 ($FF9B)"); - VarList::push_back(items, "28 ($FF9C)"); - VarList::push_back(items, "29 ($FF9D)"); - VarList::push_back(items, "30 ($FF9E)"); - VarList::push_back(items, "31 ($FF9F)"); - VarList::push_back(items, "32 ($FFA0)"); - VarList::push_back(items, "33 ($FFA1)"); - VarList::push_back(items, "34 ($FFA2)"); - VarList::push_back(items, "35 ($FFA3)"); - VarList::push_back(items, "36 ($FFA4)"); - VarList::push_back(items, "37 ($FFA5)"); - VarList::push_back(items, "38 ($FFA6)"); - VarList::push_back(items, "39 ($FFA7)"); - VarList::push_back(items, "40 ($FFA8)"); - VarList::push_back(items, "41 ($FFA9)"); - VarList::push_back(items, "42 ($FFAA)"); - VarList::push_back(items, "43 ($FFAB)"); - VarList::push_back(items, "44 ($FFAC)"); - VarList::push_back(items, "45 ($FFAD)"); - VarList::push_back(items, "46 ($FFAE)"); - VarList::push_back(items, "47 ($FFAF)"); - VarList::push_back(items, "48 ($FFB0)"); - VarList::push_back(items, "49 ($FFB1)"); - VarList::push_back(items, "50 ($FFB2)"); - VarList::push_back(items, "51 ($FFB3)"); - VarList::push_back(items, "52 ($FFB4)"); - VarList::push_back(items, "53 ($FFB5)"); - VarList::push_back(items, "54 ($FFB6)"); - VarList::push_back(items, "55 ($FFB7)"); - VarList::push_back(items, "56 ($FFB8)"); - VarList::push_back(items, "57 ($FFB9)"); - VarList::push_back(items, "58 ($FFBA)"); - VarList::push_back(items, "59 ($FFBB)"); - VarList::push_back(items, "60 ($FFBC)"); - VarList::push_back(items, "61 ($FFBD)"); - VarList::push_back(items, "62 ($FFBE)"); - VarList::push_back(items, "63 ($FFBF)"); - - myBank = - new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("64 ($FFBF)"), - myLineHeight, items, "Set bank ", - 0, kBankChanged); - myBank->setTarget(this); - addFocusWidget(myBank); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeBFWidget::loadConfig() -{ - Debugger& dbg = instance().debugger(); - CartDebug& cart = dbg.cartDebug(); - const CartState& state = static_cast(cart.getState()); - const CartState& oldstate = static_cast(cart.getOldState()); - - myBank->setSelectedIndex(myCart.getBank(), state.bank != oldstate.bank); - - CartDebugWidget::loadConfig(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeBFWidget::handleCommand(CommandSender* sender, - int cmd, int data, int id) -{ - if(cmd == kBankChanged) - { - myCart.unlockBank(); - myCart.bank(myBank->getSelected()); - myCart.lockBank(); - invalidate(); - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeBFWidget::bankState() -{ - ostringstream& buf = buffer(); - - static constexpr std::array spot = { - "$FF80", "$FF81", "$FF82", "$FF83", "$FF84", "$FF85", "$FF86", "$FF87", - "$FF88", "$FF89", "$FF8A", "$FF8B", "$FF8C", "$FF8D", "$FF8E", "$FF8F", - "$FF90", "$FF91", "$FF92", "$FF93", "$FF94", "$FF95", "$FF96", "$FF97", - "$FF98", "$FF99", "$FF9A", "$FF9B", "$FF9C", "$FF9D", "$FF9E", "$FF9F", - "$FFA0", "$FFA1", "$FFA2", "$FFA3", "$FFA4", "$FFA5", "$FFA6", "$FFA7", - "$FFA8", "$FFA9", "$FFAA", "$FFAB", "$FFAC", "$FFAD", "$FFAE", "$FFAF", - "$FFB0", "$FFB1", "$FFB2", "$FFB3", "$FFB4", "$FFB5", "$FFB6", "$FFB7", - "$FFB8", "$FFB9", "$FFBA", "$FFBB", "$FFBC", "$FFBD", "$FFBE", "$FFBF" - }; - buf << "Bank = " << std::dec << myCart.getBank() - << ", hotspot = " << spot[myCart.getBank()]; - - return buf.str(); + return info.str(); } diff --git a/src/debugger/gui/CartBFWidget.hxx b/src/debugger/gui/CartBFWidget.hxx index d084dbbc3..b67391c63 100644 --- a/src/debugger/gui/CartBFWidget.hxx +++ b/src/debugger/gui/CartBFWidget.hxx @@ -19,11 +19,10 @@ #define CARTRIDGEBF_WIDGET_HXX class CartridgeBF; -class PopUpWidget; -#include "CartDebugWidget.hxx" +#include "CartEnhancedWidget.hxx" -class CartridgeBFWidget : public CartDebugWidget +class CartridgeBFWidget : public CartridgeEnhancedWidget { public: CartridgeBFWidget(GuiObject* boss, const GUI::Font& lfont, @@ -33,17 +32,11 @@ class CartridgeBFWidget : public CartDebugWidget virtual ~CartridgeBFWidget() = default; private: - CartridgeBF& myCart; - PopUpWidget* myBank{nullptr}; + string manufacturer() override { return "CPUWIZ"; } - enum { kBankChanged = 'bkCH' }; + string description() override; private: - void loadConfig() override; - void handleCommand(CommandSender* sender, int cmd, int data, int id) override; - - string bankState() override; - // Following constructors and assignment operators not supported CartridgeBFWidget() = delete; CartridgeBFWidget(const CartridgeBFWidget&) = delete; diff --git a/src/debugger/gui/CartBUSWidget.cxx b/src/debugger/gui/CartBUSWidget.cxx index b9c25a77d..8f6a9b22b 100644 --- a/src/debugger/gui/CartBUSWidget.cxx +++ b/src/debugger/gui/CartBUSWidget.cxx @@ -238,7 +238,7 @@ void CartridgeBUSWidget::saveOldState() } for(uInt32 i = 0; i < internalRamSize(); ++i) - myOldState.internalram.push_back(myCart.myBUSRAM[i]); + myOldState.internalram.push_back(myCart.myRAM[i]); myOldState.samplepointer.push_back(myCart.getSample()); } @@ -438,18 +438,18 @@ const ByteArray& CartridgeBUSWidget::internalRamCurrent(int start, int count) { myRamCurrent.clear(); for(int i = 0; i < count; i++) - myRamCurrent.push_back(myCart.myBUSRAM[start + i]); + myRamCurrent.push_back(myCart.myRAM[start + i]); return myRamCurrent; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void CartridgeBUSWidget::internalRamSetValue(int addr, uInt8 value) { - myCart.myBUSRAM[addr] = value; + myCart.myRAM[addr] = value; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uInt8 CartridgeBUSWidget::internalRamGetValue(int addr) { - return myCart.myBUSRAM[addr]; + return myCart.myRAM[addr]; } diff --git a/src/debugger/gui/CartCDFWidget.cxx b/src/debugger/gui/CartCDFWidget.cxx index 60b68d5c4..8c4fdf2fc 100644 --- a/src/debugger/gui/CartCDFWidget.cxx +++ b/src/debugger/gui/CartCDFWidget.cxx @@ -232,7 +232,7 @@ void CartridgeCDFWidget::saveOldState() } for(uInt32 i = 0; i < internalRamSize(); ++i) - myOldState.internalram.push_back(myCart.myCDFRAM[i]); + myOldState.internalram.push_back(myCart.myRAM[i]); myOldState.samplepointer.push_back(myCart.getSample()); } @@ -437,20 +437,20 @@ const ByteArray& CartridgeCDFWidget::internalRamCurrent(int start, int count) { myRamCurrent.clear(); for(int i = 0; i < count; i++) - myRamCurrent.push_back(myCart.myCDFRAM[start + i]); + myRamCurrent.push_back(myCart.myRAM[start + i]); return myRamCurrent; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void CartridgeCDFWidget::internalRamSetValue(int addr, uInt8 value) { - myCart.myCDFRAM[addr] = value; + myCart.myRAM[addr] = value; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uInt8 CartridgeCDFWidget::internalRamGetValue(int addr) { - return myCart.myCDFRAM[addr]; + return myCart.myRAM[addr]; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/debugger/gui/CartCVPlusWidget.cxx b/src/debugger/gui/CartCVPlusWidget.cxx deleted file mode 100644 index cab27d3e5..000000000 --- a/src/debugger/gui/CartCVPlusWidget.cxx +++ /dev/null @@ -1,157 +0,0 @@ -//============================================================================ -// -// 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-2020 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 "Debugger.hxx" -#include "CartDebug.hxx" -#include "CartCVPlus.hxx" -#include "PopUpWidget.hxx" -#include "CartCVPlusWidget.hxx" - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -CartridgeCVPlusWidget::CartridgeCVPlusWidget( - GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, - int x, int y, int w, int h, CartridgeCVPlus& cart) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h), - myCart(cart) -{ - size_t size = cart.mySize; - - ostringstream info; - info << "LS_Dracon CV+ cartridge, 1K RAM, 2-256 2K ROM\n" - << "1024 bytes RAM @ $F000 - $F7FF\n" - << " $F000 - $F3FF (R), $F400 - $F7FF (W)\n" - << "2048 bytes ROM @ $F800 - $FFFF, by writing to $3D\n" - << "Startup bank = " << cart.startBank() << "\n"; - - int xpos = 2, - ypos = addBaseInformation(size, "LS_Dracon / Stephen Anthony", - info.str()) + myLineHeight; - - VariantList items; - for(uInt16 i = 0; i < cart.bankCount(); ++i) - VarList::push_back(items, Variant(i).toString() + " ($3D)"); - - ostringstream label; - label << "Set bank ($F800 - $FFFF) "; - myBank = - new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("xxx ($3D)"), - myLineHeight, items, label.str(), - _font.getStringWidth(label.str()), kBankChanged); - myBank->setTarget(this); - addFocusWidget(myBank); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeCVPlusWidget::loadConfig() -{ - myBank->setSelectedIndex(myCart.getBank(), myCart.getBank() != myOldState.bank); - - CartDebugWidget::loadConfig(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeCVPlusWidget::handleCommand(CommandSender* sender, - int cmd, int data, int id) -{ - if(cmd == kBankChanged) - { - myCart.unlockBank(); - myCart.bank(myBank->getSelected()); - myCart.lockBank(); - invalidate(); - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeCVPlusWidget::bankState() -{ - ostringstream& buf = buffer(); - - buf << "Bank = " << std::dec << myCart.myCurrentBank << ", hotspot = $3D"; - - return buf.str(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeCVPlusWidget::saveOldState() -{ - myOldState.internalram.clear(); - - for(uInt32 i = 0; i < internalRamSize(); ++i) - myOldState.internalram.push_back(myCart.myRAM[i]); - - myOldState.bank = myCart.getBank(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 CartridgeCVPlusWidget::internalRamSize() -{ - return 1024; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 CartridgeCVPlusWidget::internalRamRPort(int start) -{ - return 0xF000 + start; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeCVPlusWidget::internalRamDescription() -{ - ostringstream desc; - desc << "$F000 - $F3FF used for Read Access\n" - << "$F400 - $F7FF used for Write Access"; - - return desc.str(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const ByteArray& CartridgeCVPlusWidget::internalRamOld(int start, int count) -{ - myRamOld.clear(); - for(int i = 0; i < count; i++) - myRamOld.push_back(myOldState.internalram[start + i]); - return myRamOld; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const ByteArray& CartridgeCVPlusWidget::internalRamCurrent(int start, int count) -{ - myRamCurrent.clear(); - for(int i = 0; i < count; i++) - myRamCurrent.push_back(myCart.myRAM[start + i]); - return myRamCurrent; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeCVPlusWidget::internalRamSetValue(int addr, uInt8 value) -{ - myCart.myRAM[addr] = value; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeCVPlusWidget::internalRamGetValue(int addr) -{ - return myCart.myRAM[addr]; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeCVPlusWidget::internalRamLabel(int addr) -{ - CartDebug& dbg = instance().debugger().cartDebug(); - return dbg.getLabel(addr + 0xF000, false); -} diff --git a/src/debugger/gui/CartCVWidget.cxx b/src/debugger/gui/CartCVWidget.cxx index bae69cfba..f76718375 100644 --- a/src/debugger/gui/CartCVWidget.cxx +++ b/src/debugger/gui/CartCVWidget.cxx @@ -24,88 +24,18 @@ CartridgeCVWidget::CartridgeCVWidget( GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, int x, int y, int w, int h, CartridgeCV& cart) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h), - myCart(cart) + : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart) { - // Eventually, we should query this from the debugger/disassembler - uInt16 size = 2048; - uInt16 start = (cart.myImage[size-3] << 8) | cart.myImage[size-4]; - start -= start % size; + initialize(); +} +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string CartridgeCVWidget::description() +{ ostringstream info; - info << "CV 2K ROM + 1K RAM , non-bankswitched\n" - << "1024 bytes RAM @ $F000 - $F7FF\n" - << " $F000 - $F3FF (R), $F400 - $F7FF (W)\n" - << "ROM accessible @ $" << Common::Base::HEX4 << start << " - " - << "$" << (start + size - 1); - addBaseInformation(cart.mySize, "CommaVid", info.str()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeCVWidget::saveOldState() -{ - myOldState.internalram.clear(); - - for(uInt32 i = 0; i < internalRamSize(); ++i) - myOldState.internalram.push_back(myCart.myRAM[i]); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 CartridgeCVWidget::internalRamSize() -{ - return 1024; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 CartridgeCVWidget::internalRamRPort(int start) -{ - return 0xF000 + start; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeCVWidget::internalRamDescription() -{ - ostringstream desc; - desc << "$F000 - $F3FF used for Read Access\n" - << "$F400 - $F7FF used for Write Access"; - - return desc.str(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const ByteArray& CartridgeCVWidget::internalRamOld(int start, int count) -{ - myRamOld.clear(); - for(int i = 0; i < count; i++) - myRamOld.push_back(myOldState.internalram[start + i]); - return myRamOld; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const ByteArray& CartridgeCVWidget::internalRamCurrent(int start, int count) -{ - myRamCurrent.clear(); - for(int i = 0; i < count; i++) - myRamCurrent.push_back(myCart.myRAM[start + i]); - return myRamCurrent; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeCVWidget::internalRamSetValue(int addr, uInt8 value) -{ - myCart.myRAM[addr] = value; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeCVWidget::internalRamGetValue(int addr) -{ - return myCart.myRAM[addr]; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeCVWidget::internalRamLabel(int addr) -{ - CartDebug& dbg = instance().debugger().cartDebug(); - return dbg.getLabel(addr + 0xF000, false); + info << "CV 2K ROM + 1K RAM, non-bankswitched\n"; + info << CartridgeEnhancedWidget::description(); + + return info.str(); } diff --git a/src/debugger/gui/CartCVWidget.hxx b/src/debugger/gui/CartCVWidget.hxx index f9d59812b..63fc6f237 100644 --- a/src/debugger/gui/CartCVWidget.hxx +++ b/src/debugger/gui/CartCVWidget.hxx @@ -20,9 +20,9 @@ class CartridgeCV; -#include "CartDebugWidget.hxx" +#include "CartEnhancedWidget.hxx" -class CartridgeCVWidget : public CartDebugWidget +class CartridgeCVWidget : public CartridgeEnhancedWidget { public: CartridgeCVWidget(GuiObject* boss, const GUI::Font& lfont, @@ -32,30 +32,11 @@ class CartridgeCVWidget : public CartDebugWidget virtual ~CartridgeCVWidget() = default; private: - CartridgeCV& myCart; - struct CartState { - ByteArray internalram; - }; - CartState myOldState; + string manufacturer() override { return "CommaVid"; } + + string description() override; private: - // No implementation for non-bankswitched ROMs - void loadConfig() override { } - void handleCommand(CommandSender* sender, int cmd, int data, int id) override { } - - void saveOldState() override; - - // start of functions for Cartridge RAM tab - uInt32 internalRamSize() override; - uInt32 internalRamRPort(int start) override; - string internalRamDescription() override; - const ByteArray& internalRamOld(int start, int count) override; - const ByteArray& internalRamCurrent(int start, int count) override; - void internalRamSetValue(int addr, uInt8 value) override; - uInt8 internalRamGetValue(int addr) override; - string internalRamLabel(int addr) override; - // end of functions for Cartridge RAM tab - // Following constructors and assignment operators not supported CartridgeCVWidget() = delete; CartridgeCVWidget(const CartridgeCVWidget&) = delete; diff --git a/src/debugger/gui/CartDASHWidget.cxx b/src/debugger/gui/CartDASHWidget.cxx deleted file mode 100644 index a3dfa1109..000000000 --- a/src/debugger/gui/CartDASHWidget.cxx +++ /dev/null @@ -1,371 +0,0 @@ -//============================================================================ -// -// 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-2020 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 "CartDASH.hxx" -#include "EditTextWidget.hxx" -#include "PopUpWidget.hxx" -#include "CartDASHWidget.hxx" - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -CartridgeDASHWidget::CartridgeDASHWidget( - GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, - int x, int y, int w, int h, CartridgeDASH& cart) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h), - myCart(cart) -{ - size_t size = cart.mySize; - - ostringstream info; - info << "DASH cartridge - (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" - " First 512B of bank x (R)\n" - " First 512B of bank x+4 (+$800) (W)\n" - << "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]; - start -= start % 0x1000; - info << "Bank RORG" << " = $" << Common::Base::HEX4 << start << "\n"; - - int xpos = 2, - ypos = addBaseInformation(size, "A. Davie & T. Jentzsch", info.str()) + - myLineHeight; - - VariantList bankno; - for(uInt32 i = 0; i < myCart.ROM_BANK_COUNT; ++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) - { - int xpos_s, ypos_s = ypos; - - ostringstream label; - label << "Set segment " << i << " as: "; - - new StaticTextWidget(boss, _font, xpos, ypos, _font.getStringWidth(label.str()), - myFontHeight, label.str(), TextAlign::Left); - 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 += 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 += myBankType[i]->getWidth() + 10; - - myBankCommit[i] = new ButtonWidget(boss, _font, xpos, ypos-4, - _font.getStringWidth(" Commit "), myButtonHeight, - "Commit", bankEnum[i]); - myBankCommit[i]->setTarget(this); - addFocusWidget(myBankCommit[i]); - - xpos_s = xpos + myBankCommit[i]->getWidth() + 20; - - StaticTextWidget* t; - int addr1 = start + (i*0x400), addr2 = addr1 + 0x1FF; - - 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); - - int xoffset = xpos_s+t->getWidth() + 10; - myBankState[2*i] = new EditTextWidget(boss, _font, xoffset, ypos_s, - w - xoffset - 10, myLineHeight, ""); - myBankState[2*i]->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); - - myBankState[2*i+1] = new EditTextWidget(boss, _font, xoffset, ypos_s, - w - xoffset - 10, myLineHeight, ""); - myBankState[2*i+1]->setEditable(false, true); - - xpos = 10; - ypos+= 2 * myLineHeight; - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeDASHWidget::saveOldState() -{ - myOldState.internalram.clear(); - - for(uInt32 i = 0; i < internalRamSize(); ++i) - myOldState.internalram.push_back(myCart.myRAM[i]); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeDASHWidget::loadConfig() -{ - updateUIState(); - CartDebugWidget::loadConfig(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeDASHWidget::handleCommand(CommandSender* sender, - int cmd, int data, int id) -{ - uInt16 segment = 0; - switch(cmd) - { - case kBank0Changed: - segment = 0; - break; - case kBank1Changed: - segment = 1; - break; - case kBank2Changed: - segment = 2; - break; - case kBank3Changed: - segment = 3; - break; - default: - break; - } - - // Ignore bank if either number or type hasn't been selected - if(myBankNumber[segment]->getSelected() < 0 || - myBankType[segment]->getSelected() < 0) - return; - - uInt8 bank = (segment << myCart.BANK_BITS) | - (myBankNumber[segment]->getSelected() & myCart.BIT_BANK_MASK); - - myCart.unlockBank(); - - if(myBankType[segment]->getSelectedTag() == "ROM") - myCart.bankROM(bank); - else - myCart.bankRAM(bank); - - myCart.lockBank(); - invalidate(); - updateUIState(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeDASHWidget::bankState() -{ - ostringstream& buf = buffer(); - int lastROMBank = -1; - bool lastSlotRAM = false; - - for(int i = 0; i < 8; ++i) - { - uInt16 bank = myCart.bankInUse[i]; - - if(bank == myCart.BANK_UNDEFINED) // never accessed - { - buf << " U!"; - } - else - { - int bankno = bank & myCart.BIT_BANK_MASK; - - if(bank & myCart.BITMASK_ROMRAM) // was RAM mapped here? - { - // RAM will always need a '+' placed somewhere, since it always - // consists of 512B segments - bool inFirstSlot = (i % 2 == 0); - if(!(inFirstSlot || lastSlotRAM)) - { - lastSlotRAM = false; - buf << " +"; - } - - if(bank & myCart.BITMASK_LOWERUPPER) // upper is write port - buf << " RAM " << bankno << "W"; - else - buf << " RAM " << bankno << "R"; - - if(inFirstSlot) - { - buf << " +"; - lastSlotRAM = true; - } - } - else - { - // ROM can be contiguous, since 2 512B segments can form a single - // 1K bank; in this case we only show the info once - bool highBankSame = (i % 2 == 1) && (bankno == lastROMBank); - if(!highBankSame) - { - buf << " ROM " << bankno; - lastROMBank = bankno; - } - else - lastROMBank = -1; - - lastSlotRAM = false; - } - } - - if((i+1) % 2 == 0 && i < 7) - buf << " /"; - } - - return buf.str(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeDASHWidget::updateUIState() -{ - // Set contents for actual banks number and type - for(int i = 0; i < 4; ++i) - { - uInt16 segment = myCart.segmentInUse[i]; - - if(segment == myCart.BANK_UNDEFINED) - { - myBankNumber[i]->clearSelection(); - myBankType[i]->clearSelection(); - } - else - { - int bankno = segment & myCart.BIT_BANK_MASK; - const string& banktype = (segment & myCart.BITMASK_ROMRAM) ? "RAM" : "ROM"; - - myBankNumber[i]->setSelected(bankno); - myBankType[i]->setSelected(banktype); - } - } - - // Set description for each 512b bank state - for(int i = 0; i < 8; ++i) - { - uInt16 bank = myCart.bankInUse[i]; - - if(bank == myCart.BANK_UNDEFINED) // never accessed - { - myBankState[i]->setText("Undefined"); - } - else - { - ostringstream buf; - int bankno = bank & myCart.BIT_BANK_MASK; - - 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()); - } - } - 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()); - } - } - } - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 CartridgeDASHWidget::internalRamSize() -{ - return 32*1024; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 CartridgeDASHWidget::internalRamRPort(int start) -{ - return 0x0000 + start; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeDASHWidget::internalRamDescription() -{ - ostringstream desc; - desc << "Accessible 512b at a time via:\n" - << " $F000/$F200/$F400/etc used for Read Access\n" - << " $F800/$FA00/$FC00/etc used for Write Access (+$800)"; - - return desc.str(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const ByteArray& CartridgeDASHWidget::internalRamOld(int start, int count) -{ - myRamOld.clear(); - for(int i = 0; i < count; i++) - myRamOld.push_back(myOldState.internalram[start + i]); - return myRamOld; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const ByteArray& CartridgeDASHWidget::internalRamCurrent(int start, int count) -{ - myRamCurrent.clear(); - for(int i = 0; i < count; i++) - myRamCurrent.push_back(myCart.myRAM[start + i]); - return myRamCurrent; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeDASHWidget::internalRamSetValue(int addr, uInt8 value) -{ - myCart.myRAM[addr] = value; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeDASHWidget::internalRamGetValue(int addr) -{ - return myCart.myRAM[addr]; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const std::array CartridgeDASHWidget::bankEnum = { - kBank0Changed, kBank1Changed, kBank2Changed, kBank3Changed -}; diff --git a/src/debugger/gui/CartDASHWidget.hxx b/src/debugger/gui/CartDASHWidget.hxx deleted file mode 100644 index e738c2292..000000000 --- a/src/debugger/gui/CartDASHWidget.hxx +++ /dev/null @@ -1,86 +0,0 @@ -//============================================================================ -// -// 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-2020 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 CARTRIDGEDASH_WIDGET_HXX -#define CARTRIDGEDASH_WIDGET_HXX - -class CartridgeDASH; -class ButtonWidget; -class EditTextWidget; -class PopUpWidget; - -#include "CartDebugWidget.hxx" - -class CartridgeDASHWidget : public CartDebugWidget -{ - public: - CartridgeDASHWidget(GuiObject* boss, const GUI::Font& lfont, - const GUI::Font& nfont, - int x, int y, int w, int h, - CartridgeDASH& cart); - virtual ~CartridgeDASHWidget() = default; - - private: - void updateUIState(); - - private: - CartridgeDASH& myCart; - - std::array myBankNumber{nullptr}; - std::array myBankType{nullptr}; - std::array myBankCommit{nullptr}; - std::array myBankState{nullptr}; - - struct CartState { - ByteArray internalram; - }; - CartState myOldState; - - enum BankID { - kBank0Changed = 'b0CH', - kBank1Changed = 'b1CH', - kBank2Changed = 'b2CH', - kBank3Changed = 'b3CH' - }; - static const std::array bankEnum; - - private: - void saveOldState() override; - void loadConfig() override; - void handleCommand(CommandSender* sender, int cmd, int data, int id) override; - - string bankState() override; - - // start of functions for Cartridge RAM tab - uInt32 internalRamSize() override; - uInt32 internalRamRPort(int start) override; - string internalRamDescription() override; - const ByteArray& internalRamOld(int start, int count) override; - const ByteArray& internalRamCurrent(int start, int count) override; - void internalRamSetValue(int addr, uInt8 value) override; - uInt8 internalRamGetValue(int addr) override; - // end of functions for Cartridge RAM tab - - // Following constructors and assignment operators not supported - CartridgeDASHWidget() = delete; - CartridgeDASHWidget(const CartridgeDASHWidget&) = delete; - CartridgeDASHWidget(CartridgeDASHWidget&&) = delete; - CartridgeDASHWidget& operator=(const CartridgeDASHWidget&) = delete; - CartridgeDASHWidget& operator=(CartridgeDASHWidget&&) = delete; -}; - -#endif diff --git a/src/debugger/gui/CartDFSCWidget.cxx b/src/debugger/gui/CartDFSCWidget.cxx index e03fbf1c6..ed792acb4 100644 --- a/src/debugger/gui/CartDFSCWidget.cxx +++ b/src/debugger/gui/CartDFSCWidget.cxx @@ -15,185 +15,25 @@ // this file, and for a DISCLAIMER OF ALL WARRANTIES. //============================================================================ -#include "Debugger.hxx" -#include "CartDebug.hxx" #include "CartDFSC.hxx" -#include "PopUpWidget.hxx" #include "CartDFSCWidget.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeDFSCWidget::CartridgeDFSCWidget( GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, int x, int y, int w, int h, CartridgeDFSC& cart) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h), - myCart(cart) + : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart) { - uInt32 size = 32 * 4096; + initialize(); +} +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string CartridgeDFSCWidget::description() +{ ostringstream info; - info << "128K DFSC + RAM, 32 4K banks\n" - << "128 bytes RAM @ $F000 - $F0FF\n" - << " $F080 - $F0FF (R), $F000 - $F07F (W)\n" - << "Startup bank = " << cart.startBank() << "\n"; - // Eventually, we should query this from the debugger/disassembler - for(uInt32 i = 0, offset = 0xFFC, spot = 0xFC0; i < 32; ++i, offset += 0x1000) - { - uInt16 start = (cart.myImage[offset+1] << 8) | cart.myImage[offset]; - start -= start % 0x1000; - info << "Bank " << std::dec << i << " @ $" << Common::Base::HEX4 << (start + 0x100) - << " - " << "$" << (start + 0xFFF) << " (hotspot = $F" << (spot+i) << ")\n"; - } + info << "128K DFSC + RAM, 32 4K banks\n"; + info << CartridgeEnhancedWidget::description(); - int xpos = 2, - ypos = addBaseInformation(size, "CPUWIZ", info.str()) + myLineHeight; - - VariantList items; - VarList::push_back(items, " 0 ($FFC0)"); - VarList::push_back(items, " 1 ($FFC1)"); - VarList::push_back(items, " 2 ($FFC2)"); - VarList::push_back(items, " 3 ($FFC3)"); - VarList::push_back(items, " 4 ($FFC4)"); - VarList::push_back(items, " 5 ($FFC5)"); - VarList::push_back(items, " 6 ($FFC6)"); - VarList::push_back(items, " 7 ($FFC7)"); - VarList::push_back(items, " 8 ($FFC8)"); - VarList::push_back(items, " 9 ($FFC9)"); - VarList::push_back(items, "10 ($FFCA)"); - VarList::push_back(items, "11 ($FFCB)"); - VarList::push_back(items, "12 ($FFCC)"); - VarList::push_back(items, "13 ($FFCD)"); - VarList::push_back(items, "14 ($FFCE)"); - VarList::push_back(items, "15 ($FFCF)"); - VarList::push_back(items, "16 ($FFD0)"); - VarList::push_back(items, "17 ($FFD1)"); - VarList::push_back(items, "18 ($FFD2)"); - VarList::push_back(items, "19 ($FFD3)"); - VarList::push_back(items, "20 ($FFD4)"); - VarList::push_back(items, "21 ($FFD5)"); - VarList::push_back(items, "22 ($FFD6)"); - VarList::push_back(items, "23 ($FFD7)"); - VarList::push_back(items, "24 ($FFD8)"); - VarList::push_back(items, "25 ($FFD9)"); - VarList::push_back(items, "26 ($FFDA)"); - VarList::push_back(items, "27 ($FFDB)"); - VarList::push_back(items, "28 ($FFDC)"); - VarList::push_back(items, "29 ($FFDD)"); - VarList::push_back(items, "30 ($FFDE)"); - VarList::push_back(items, "31 ($FFDF)"); - - myBank = - new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("31 ($FFE0)"), - myLineHeight, items, "Set bank ", - 0, kBankChanged); - myBank->setTarget(this); - addFocusWidget(myBank); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeDFSCWidget::saveOldState() -{ - myOldState.internalram.clear(); - - for(uInt32 i = 0; i < internalRamSize(); ++i) - myOldState.internalram.push_back(myCart.myRAM[i]); - - myOldState.bank = myCart.getBank(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeDFSCWidget::loadConfig() -{ - myBank->setSelectedIndex(myCart.getBank(), myCart.getBank() != myOldState.bank); - - CartDebugWidget::loadConfig(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeDFSCWidget::handleCommand(CommandSender* sender, - int cmd, int data, int id) -{ - if(cmd == kBankChanged) - { - myCart.unlockBank(); - myCart.bank(myBank->getSelected()); - myCart.lockBank(); - invalidate(); - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeDFSCWidget::bankState() -{ - ostringstream& buf = buffer(); - - static constexpr std::array spot = { - "$FFC0", "$FFC1", "$FFC2", "$FFC3", "$FFC4", "$FFC5", "$FFC6", "$FFC7", - "$FFC8", "$FFC9", "$FFCA", "$FFCB", "$FFCC", "$FFCD", "$FFCE", "$FFCF", - "$FFD0", "$FFD1", "$FFD2", "$FFD3", "$FFD4", "$FFD5", "$FFD6", "$FFE7", - "$FFD8", "$FFD9", "$FFDA", "$FFDB", "$FFDC", "$FFDD", "$FFDE", "$FFDF" - }; - buf << "Bank = " << std::dec << myCart.getBank() - << ", hotspot = " << spot[myCart.getBank()]; - - return buf.str(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 CartridgeDFSCWidget::internalRamSize() -{ - return 128; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 CartridgeDFSCWidget::internalRamRPort(int start) -{ - return 0xF080 + start; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeDFSCWidget::internalRamDescription() -{ - ostringstream desc; - desc << "$F000 - $F07F used for Write Access\n" - << "$F080 - $F0FF used for Read Access"; - - return desc.str(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const ByteArray& CartridgeDFSCWidget::internalRamOld(int start, int count) -{ - myRamOld.clear(); - for(int i = 0; i < count; i++) - myRamOld.push_back(myOldState.internalram[start + i]); - return myRamOld; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const ByteArray& CartridgeDFSCWidget::internalRamCurrent(int start, int count) -{ - myRamCurrent.clear(); - for(int i = 0; i < count; i++) - myRamCurrent.push_back(myCart.myRAM[start + i]); - return myRamCurrent; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeDFSCWidget::internalRamSetValue(int addr, uInt8 value) -{ - myCart.myRAM[addr] = value; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeDFSCWidget::internalRamGetValue(int addr) -{ - return myCart.myRAM[addr]; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeDFSCWidget::internalRamLabel(int addr) -{ - CartDebug& dbg = instance().debugger().cartDebug(); - return dbg.getLabel(addr + 0xF080, false); + return info.str(); } diff --git a/src/debugger/gui/CartDFSCWidget.hxx b/src/debugger/gui/CartDFSCWidget.hxx index 4d4a07feb..9e4481fa8 100644 --- a/src/debugger/gui/CartDFSCWidget.hxx +++ b/src/debugger/gui/CartDFSCWidget.hxx @@ -19,11 +19,10 @@ #define CARTRIDGEDFSC_WIDGET_HXX class CartridgeDFSC; -class PopUpWidget; -#include "CartDebugWidget.hxx" +#include "CartEnhancedWidget.hxx" -class CartridgeDFSCWidget : public CartDebugWidget +class CartridgeDFSCWidget : public CartridgeEnhancedWidget { public: CartridgeDFSCWidget(GuiObject* boss, const GUI::Font& lfont, @@ -33,35 +32,11 @@ class CartridgeDFSCWidget : public CartDebugWidget virtual ~CartridgeDFSCWidget() = default; private: - CartridgeDFSC& myCart; - PopUpWidget* myBank{nullptr}; + string manufacturer() override { return "CPUWIZ"; } - struct CartState { - ByteArray internalram; - uInt16 bank{0}; - }; - CartState myOldState; - - enum { kBankChanged = 'bkCH' }; + string description() override; private: - void saveOldState() override; - void loadConfig() override; - void handleCommand(CommandSender* sender, int cmd, int data, int id) override; - - string bankState() override; - - // start of functions for Cartridge RAM tab - uInt32 internalRamSize() override; - uInt32 internalRamRPort(int start) override; - string internalRamDescription() override; - const ByteArray& internalRamOld(int start, int count) override; - const ByteArray& internalRamCurrent(int start, int count) override; - void internalRamSetValue(int addr, uInt8 value) override; - uInt8 internalRamGetValue(int addr) override; - string internalRamLabel(int addr) override; - // end of functions for Cartridge RAM tab - // Following constructors and assignment operators not supported CartridgeDFSCWidget() = delete; CartridgeDFSCWidget(const CartridgeDFSCWidget&) = delete; diff --git a/src/debugger/gui/CartDFWidget.cxx b/src/debugger/gui/CartDFWidget.cxx index c8fde5fff..653588e56 100644 --- a/src/debugger/gui/CartDFWidget.cxx +++ b/src/debugger/gui/CartDFWidget.cxx @@ -16,115 +16,24 @@ //============================================================================ #include "CartDF.hxx" -#include "PopUpWidget.hxx" #include "CartDFWidget.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeDFWidget::CartridgeDFWidget( GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, int x, int y, int w, int h, CartridgeDF& cart) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h), - myCart(cart) + : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart) { - uInt32 size = 32 * 4096; + initialize(); +} +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string CartridgeDFWidget::description() +{ ostringstream info; - info << "EF 2 cartridge, 32 4K banks\n" - << "Startup bank = " << cart.startBank() << "\n"; - // Eventually, we should query this from the debugger/disassembler - for(uInt32 i = 0, offset = 0xFFC, spot = 0xFD0; i < 32; ++i, offset += 0x1000) - { - uInt16 start = (cart.myImage[offset+1] << 8) | cart.myImage[offset]; - start -= start % 0x1000; - info << "Bank " << std::dec << i << " @ $" << Common::Base::HEX4 << start << " - " - << "$" << (start + 0xFFF) << " (hotspot = $F" << (spot+i) << ")\n"; - } + info << "128K DF, 32 4K banks\n"; + info << CartridgeEnhancedWidget::description(); - int xpos = 2, - ypos = addBaseInformation(size, "CPUWIZ", info.str()) + myLineHeight; - - VariantList items; - VarList::push_back(items, " 0 ($FFC0)"); - VarList::push_back(items, " 1 ($FFC1)"); - VarList::push_back(items, " 2 ($FFC2)"); - VarList::push_back(items, " 3 ($FFC3)"); - VarList::push_back(items, " 4 ($FFC4)"); - VarList::push_back(items, " 5 ($FFC5)"); - VarList::push_back(items, " 6 ($FFC6)"); - VarList::push_back(items, " 7 ($FFC7)"); - VarList::push_back(items, " 8 ($FFC8)"); - VarList::push_back(items, " 9 ($FFC9)"); - VarList::push_back(items, "10 ($FFCA)"); - VarList::push_back(items, "11 ($FFCB)"); - VarList::push_back(items, "12 ($FFCC)"); - VarList::push_back(items, "13 ($FFCD)"); - VarList::push_back(items, "14 ($FFCE)"); - VarList::push_back(items, "15 ($FFCF)"); - VarList::push_back(items, "16 ($FFD0)"); - VarList::push_back(items, "17 ($FFD1)"); - VarList::push_back(items, "18 ($FFD2)"); - VarList::push_back(items, "19 ($FFD3)"); - VarList::push_back(items, "20 ($FFD4)"); - VarList::push_back(items, "21 ($FFD5)"); - VarList::push_back(items, "22 ($FFD6)"); - VarList::push_back(items, "23 ($FFD7)"); - VarList::push_back(items, "24 ($FFD8)"); - VarList::push_back(items, "25 ($FFD9)"); - VarList::push_back(items, "26 ($FFDA)"); - VarList::push_back(items, "27 ($FFDB)"); - VarList::push_back(items, "28 ($FFDC)"); - VarList::push_back(items, "29 ($FFDD)"); - VarList::push_back(items, "30 ($FFDE)"); - VarList::push_back(items, "31 ($FFDF)"); - - myBank = - new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("31 ($FFDF)"), - myLineHeight, items, "Set bank ", - 0, kBankChanged); - myBank->setTarget(this); - addFocusWidget(myBank); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeDFWidget::loadConfig() -{ - Debugger& dbg = instance().debugger(); - CartDebug& cart = dbg.cartDebug(); - const CartState& state = static_cast(cart.getState()); - const CartState& oldstate = static_cast(cart.getOldState()); - - myBank->setSelectedIndex(myCart.getBank(), state.bank != oldstate.bank); - - CartDebugWidget::loadConfig(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeDFWidget::handleCommand(CommandSender* sender, - int cmd, int data, int id) -{ - if(cmd == kBankChanged) - { - myCart.unlockBank(); - myCart.bank(myBank->getSelected()); - myCart.lockBank(); - invalidate(); - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeDFWidget::bankState() -{ - ostringstream& buf = buffer(); - - static constexpr std::array spot = { - "$FFC0", "$FFC1", "$FFC2", "$FFC3", "$FFC4", "$FFC5", "$FFC6", "$FFC7", - "$FFC8", "$FFC9", "$FFCA", "$FFCB", "$FFCC", "$FFCD", "$FFCE", "$FFCF", - "$FFD0", "$FFD1", "$FFD2", "$FFD3", "$FFD4", "$FFD5", "$FFD6", "$FFD7", - "$FFD8", "$FFD9", "$FFDA", "$FFDB", "$FFDC", "$FFDD", "$FFDE", "$FFDF" - }; - buf << "Bank = " << std::dec << myCart.getBank() - << ", hotspot = " << spot[myCart.getBank()]; - - return buf.str(); + return info.str(); } diff --git a/src/debugger/gui/CartDFWidget.hxx b/src/debugger/gui/CartDFWidget.hxx index 146763b75..9b45552b9 100644 --- a/src/debugger/gui/CartDFWidget.hxx +++ b/src/debugger/gui/CartDFWidget.hxx @@ -19,11 +19,10 @@ #define CARTRIDGEDF_WIDGET_HXX class CartridgeDF; -class PopUpWidget; -#include "CartDebugWidget.hxx" +#include "CartEnhancedWidget.hxx" -class CartridgeDFWidget : public CartDebugWidget +class CartridgeDFWidget : public CartridgeEnhancedWidget { public: CartridgeDFWidget(GuiObject* boss, const GUI::Font& lfont, @@ -33,17 +32,11 @@ class CartridgeDFWidget : public CartDebugWidget virtual ~CartridgeDFWidget() = default; private: - CartridgeDF& myCart; - PopUpWidget* myBank{nullptr}; + string manufacturer() override { return "CPUWIZ"; } - enum { kBankChanged = 'bkCH' }; + string description() override; private: - void loadConfig() override; - void handleCommand(CommandSender* sender, int cmd, int data, int id) override; - - string bankState() override; - // Following constructors and assignment operators not supported CartridgeDFWidget() = delete; CartridgeDFWidget(const CartridgeDFWidget&) = delete; diff --git a/src/debugger/gui/CartDPCWidget.cxx b/src/debugger/gui/CartDPCWidget.cxx index c07ae8739..681b3692d 100644 --- a/src/debugger/gui/CartDPCWidget.cxx +++ b/src/debugger/gui/CartDPCWidget.cxx @@ -27,12 +27,13 @@ CartridgeDPCWidget::CartridgeDPCWidget( : CartDebugWidget(boss, lfont, nfont, x, y, w, h), myCart(cart) { + const int V_GAP = 4; size_t size = cart.mySize; - ostringstream info; + info << "DPC cartridge, two 4K banks + 2K display bank\n" - << "DPC registers accessible @ $F000 - $F07F\n" - << " $F000 - $F03F (R), $F040 - $F07F (W)\n" + << "DPC registers accessible @ $" << Common::Base::HEX4 << 0xF000 << " - $" << 0xF07F << "\n" + << " $" << 0xF000 << " - " << 0xF03F << " (R), $" << 0xF040 << " - $" << 0xF07F << " (W)\n" << "Startup bank = " << cart.startBank() << " or undetermined\n"; @@ -42,7 +43,7 @@ CartridgeDPCWidget::CartridgeDPCWidget( uInt16 start = (cart.myImage[offset+1] << 8) | cart.myImage[offset]; start -= start % 0x1000; info << "Bank " << i << " @ $" << Common::Base::HEX4 << (start + 0x80) << " - " - << "$" << (start + 0xFFF) << " (hotspot = $F" << (spot+i) << ")\n"; + << "$" << (start + 0xFFF) << " (hotspot = $" << (0xF000 + spot + i) << ")\n"; } int xpos = 2, @@ -50,26 +51,30 @@ CartridgeDPCWidget::CartridgeDPCWidget( myLineHeight; VariantList items; - VarList::push_back(items, "0 ($FFF8)"); - VarList::push_back(items, "1 ($FFF9)"); + for(int bank = 0; bank < 2; ++bank) + { + ostringstream buf; + + buf << "#" << std::dec << bank << " ($" << Common::Base::HEX4 << (0xFFF8 + bank) << ")"; + VarList::push_back(items, buf.str()); + } + myBank = - new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("0 ($FFFx)"), + new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("#0 ($FFFx)"), myLineHeight, items, "Set bank ", 0, kBankChanged); myBank->setTarget(this); addFocusWidget(myBank); - ypos += myLineHeight + 8; + ypos += myLineHeight + V_GAP * 3; // Data fetchers - int lwidth = _font.getStringWidth("Data Fetchers "); - new StaticTextWidget(boss, _font, xpos, ypos, lwidth, - myFontHeight, "Data Fetchers ", TextAlign::Left); + int lwidth = _font.getStringWidth("Data fetchers "); + new StaticTextWidget(boss, _font, xpos, ypos, "Data fetchers "); // Top registers - lwidth = _font.getStringWidth("Counter Registers "); - xpos = 18; ypos += myLineHeight + 4; - new StaticTextWidget(boss, _font, xpos, ypos, lwidth, - myFontHeight, "Top Registers ", TextAlign::Left); + lwidth = _font.getStringWidth("Counter registers "); + xpos = 2 + _font.getMaxCharWidth() * 2; ypos += myLineHeight + 4; + new StaticTextWidget(boss, _font, xpos, ypos, "Top registers "); xpos += lwidth; myTops = new DataGridWidget(boss, _nfont, xpos, ypos-2, 8, 1, 2, 8, Common::Base::Fmt::_16); @@ -77,9 +82,8 @@ CartridgeDPCWidget::CartridgeDPCWidget( myTops->setEditable(false); // Bottom registers - xpos = 10; ypos += myLineHeight + 4; - new StaticTextWidget(boss, _font, xpos, ypos, lwidth, - myFontHeight, "Bottom Registers ", TextAlign::Left); + xpos = 2 + _font.getMaxCharWidth() * 2; ypos += myLineHeight + 4; + new StaticTextWidget(boss, _font, xpos, ypos, "Bottom registers "); xpos += lwidth; myBottoms = new DataGridWidget(boss, _nfont, xpos, ypos-2, 8, 1, 2, 8, Common::Base::Fmt::_16); @@ -87,9 +91,8 @@ CartridgeDPCWidget::CartridgeDPCWidget( myBottoms->setEditable(false); // Counter registers - xpos = 10; ypos += myLineHeight + 4; - new StaticTextWidget(boss, _font, xpos, ypos, lwidth, - myFontHeight, "Counter Registers ", TextAlign::Left); + xpos = 2 + _font.getMaxCharWidth() * 2; ypos += myLineHeight + 4; + new StaticTextWidget(boss, _font, xpos, ypos, "Counter registers "); xpos += lwidth; myCounters = new DataGridWidget(boss, _nfont, xpos, ypos-2, 8, 1, 4, 16, Common::Base::Fmt::_16_4); @@ -97,9 +100,8 @@ CartridgeDPCWidget::CartridgeDPCWidget( myCounters->setEditable(false); // Flag registers - xpos = 10; ypos += myLineHeight + 4; - new StaticTextWidget(boss, _font, xpos, ypos, lwidth, - myFontHeight, "Flag Registers ", TextAlign::Left); + xpos = 2 + _font.getMaxCharWidth() * 2; ypos += myLineHeight + 4; + new StaticTextWidget(boss, _font, xpos, ypos, "Flag registers "); xpos += lwidth; myFlags = new DataGridWidget(boss, _nfont, xpos, ypos-2, 8, 1, 2, 8, Common::Base::Fmt::_16); @@ -107,10 +109,9 @@ CartridgeDPCWidget::CartridgeDPCWidget( myFlags->setEditable(false); // Music mode - xpos = 2; ypos += myLineHeight + 12; + xpos = 2; ypos += myLineHeight + V_GAP * 3; lwidth = _font.getStringWidth("Music mode (DF5/DF6/DF7) "); - new StaticTextWidget(boss, _font, xpos, ypos, lwidth, - myFontHeight, "Music mode (DF5/DF6/DF7) ", TextAlign::Left); + new StaticTextWidget(boss, _font, xpos, ypos, "Music mode (DF5/DF6/DF7) "); xpos += lwidth; myMusicMode = new DataGridWidget(boss, _nfont, xpos, ypos-2, 3, 1, 2, 8, Common::Base::Fmt::_16); @@ -118,9 +119,8 @@ CartridgeDPCWidget::CartridgeDPCWidget( myMusicMode->setEditable(false); // Current random number - xpos = 10; ypos += myLineHeight + 4; - new StaticTextWidget(boss, _font, xpos, ypos, lwidth, - myFontHeight, "Current random number ", TextAlign::Left); + xpos = 2; ypos += myLineHeight + V_GAP * 3; + new StaticTextWidget(boss, _font, xpos, ypos, "Current random number "); xpos += lwidth; myRandom = new DataGridWidget(boss, _nfont, xpos, ypos-2, 1, 1, 2, 8, Common::Base::Fmt::_16); @@ -229,9 +229,8 @@ string CartridgeDPCWidget::bankState() { ostringstream& buf = buffer(); - static constexpr std::array spot = { "$FFF8", "$FFF9" }; - buf << "Bank = " << std::dec << myCart.getBank() - << ", hotspot = " << spot[myCart.getBank()]; + buf << "Bank #" << std::dec << myCart.getBank() + << " (hotspot $" << Common::Base::HEX4 << (0xFFF8 + myCart.getBank()) << ")"; return buf.str(); } @@ -252,10 +251,9 @@ uInt32 CartridgeDPCWidget::internalRamRPort(int start) string CartridgeDPCWidget::internalRamDescription() { ostringstream desc; - desc << "$0000 - $07FF - 2K display data\n" - << " indirectly accessible to 6507\n" - << " via DPC+'s Data Fetcher\n" - << " registers\n"; + desc << "2K display data @ $0000 - $" << Common::Base::HEX4 << 0x07FF << "\n" + << " indirectly accessible to 6507 via DPC's\n" + << " data fetcher registers\n"; return desc.str(); } diff --git a/src/debugger/gui/CartDPCWidget.hxx b/src/debugger/gui/CartDPCWidget.hxx index ff57ddc1d..0a675a2c8 100644 --- a/src/debugger/gui/CartDPCWidget.hxx +++ b/src/debugger/gui/CartDPCWidget.hxx @@ -75,6 +75,7 @@ class CartridgeDPCWidget : public CartDebugWidget const ByteArray& internalRamCurrent(int start, int count) override; void internalRamSetValue(int addr, uInt8 value) override; uInt8 internalRamGetValue(int addr) override; + string tabLabel() override { return " DPC Display Data "; } // end of functions for Cartridge RAM tab // Following constructors and assignment operators not supported diff --git a/src/debugger/gui/CartDebugWidget.cxx b/src/debugger/gui/CartDebugWidget.cxx index c7bfe5085..b04000328 100644 --- a/src/debugger/gui/CartDebugWidget.cxx +++ b/src/debugger/gui/CartDebugWidget.cxx @@ -42,14 +42,14 @@ 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; int x = 2, y = 8; // Add ROM size, manufacturer and bankswitch info - new StaticTextWidget(_boss, _font, x, y + 1, "ROM Size "); + new StaticTextWidget(_boss, _font, x, y + 1, "ROM size "); buf << bytes << " bytes"; if(bytes >= 1024) buf << " / " << (bytes/1024) << "KB"; diff --git a/src/debugger/gui/CartDebugWidget.hxx b/src/debugger/gui/CartDebugWidget.hxx index 7789cbb16..7bdcbf472 100644 --- a/src/debugger/gui/CartDebugWidget.hxx +++ b/src/debugger/gui/CartDebugWidget.hxx @@ -58,7 +58,7 @@ class CartDebugWidget : public Widget, public CommandSender virtual string bankState() { return "0 (non-bankswitched)"; } // To make the Cartridge RAM show up in the debugger, implement - // the following 8 functions for cartridges with internal RAM + // the following 9 functions for cartridges with internal RAM virtual uInt32 internalRamSize() { return 0; } virtual uInt32 internalRamRPort(int start) { return 0; } virtual string internalRamDescription() { return EmptyString; } @@ -67,6 +67,7 @@ class CartDebugWidget : public Widget, public CommandSender virtual void internalRamSetValue(int addr, uInt8 value) { } virtual uInt8 internalRamGetValue(int addr) { return 0; } virtual string internalRamLabel(int addr) { return "Not available/applicable"; } + virtual string tabLabel() { return " Cartridge RAM "; } protected: // Arrays used to hold current and previous internal RAM values diff --git a/src/debugger/gui/CartE0Widget.cxx b/src/debugger/gui/CartE0Widget.cxx index 9413ce5c0..12205c463 100644 --- a/src/debugger/gui/CartE0Widget.cxx +++ b/src/debugger/gui/CartE0Widget.cxx @@ -16,129 +16,59 @@ //============================================================================ #include "CartE0.hxx" -#include "PopUpWidget.hxx" #include "CartE0Widget.hxx" -static constexpr std::array seg0 = { - "0 ($FFE0)", "1 ($FFE1)", "2 ($FFE2)", "3 ($FFE3)", - "4 ($FFE4)", "5 ($FFE5)", "6 ($FFE6)", "7 ($FFE7)" -}; -static constexpr std::array seg1 = { - "0 ($FFE8)", "1 ($FFE9)", "2 ($FFEA)", "3 ($FFEB)", - "4 ($FFEC)", "5 ($FFED)", "6 ($FFEE)", "7 ($FFEF)" -}; -static constexpr std::array seg2 = { - "0 ($FFF0)", "1 ($FFF1)", "2 ($FFF2)", "3 ($FFF3)", - "4 ($FFF4)", "5 ($FFF5)", "6 ($FFF6)", "7 ($FFF7)" -}; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeE0Widget::CartridgeE0Widget( GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, int x, int y, int w, int h, CartridgeE0& cart) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h), - myCart(cart) + : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart) { - uInt32 size = 8 * 1024; + initialize(); +} - string info = - "E0 cartridge, eight 1K slices\n" - "Segment 0 accessible @ $F000 - $F3FF\n" - " Hotspots $FE0 to $FE7\n" - "Segment 1 accessible @ $F400 - $F7FF\n" - " Hotspots $FE8 to $FEF\n" - "Segment 2 accessible @ $F800 - $FBFF\n" - " Hotspots $FF0 to $FF7\n" - "Segment 3 accessible @ $FC00 - $FFFF\n" - " Always points to last 1K of ROM\n" - "Startup slices = 4 / 5 / 6 or undetermined\n"; +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string CartridgeE0Widget::description() +{ + ostringstream info; -#if 0 - // Eventually, we should query this from the debugger/disassembler - uInt16 start = (cart.myImage[size-3] << 8) | cart.myImage[size-4]; - start -= start % 0x1000; - info << "Bank RORG" << " = $" << HEX4 << start << "\n"; -#endif - int xpos = 2, - ypos = addBaseInformation(size, "Parker Brothers", info) + myLineHeight; + info << "E0 cartridge,\n eight 1K banks mapped into four segments\n"; + info << CartridgeEnhancedWidget::description(); - VariantList items0, items1, items2; - for(int i = 0; i < 8; ++i) + return info.str(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string CartridgeE0Widget::romDescription() +{ + ostringstream info; + + for(int seg = 0; seg < 4; ++seg) { - VarList::push_back(items0, seg0[i]); - VarList::push_back(items1, seg1[i]); - VarList::push_back(items2, seg2[i]); + 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"; - const int lwidth = _font.getStringWidth("Set slice for segment X "); - mySlice0 = - new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("7 ($FFF7)"), - myLineHeight, items0, "Set slice for segment 0 ", - lwidth, kSlice0Changed); - mySlice0->setTarget(this); - addFocusWidget(mySlice0); - ypos += mySlice0->getHeight() + 4; - - mySlice1 = - new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("7 ($FFF7)"), - myLineHeight, items1, "Set slice for segment 1 ", - lwidth, kSlice1Changed); - mySlice1->setTarget(this); - addFocusWidget(mySlice1); - ypos += mySlice1->getHeight() + 4; - - mySlice2 = - new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("7 ($FFF7)"), - myLineHeight, items2, "Set slice for segment 2 ", - lwidth, kSlice2Changed); - mySlice2->setTarget(this); - addFocusWidget(mySlice2); + return info.str(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeE0Widget::loadConfig() +string CartridgeE0Widget::hotspotStr(int bank, int segment, bool noBrackets) { - mySlice0->setSelectedIndex(myCart.myCurrentSlice[0]); - mySlice1->setSelectedIndex(myCart.myCurrentSlice[1]); - mySlice2->setSelectedIndex(myCart.myCurrentSlice[2]); + ostringstream info; + uInt16 hotspot = myCart.hotspot(); - CartDebugWidget::loadConfig(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeE0Widget::handleCommand(CommandSender* sender, - int cmd, int data, int id) -{ - myCart.unlockBank(); - - switch(cmd) - { - case kSlice0Changed: - myCart.segmentZero(mySlice0->getSelected()); - break; - case kSlice1Changed: - myCart.segmentOne(mySlice1->getSelected()); - break; - case kSlice2Changed: - myCart.segmentTwo(mySlice2->getSelected()); - break; - default: - break; - } - - myCart.lockBank(); - invalidate(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeE0Widget::bankState() -{ - ostringstream& buf = buffer(); - - buf << "Slices: " << std::dec - << seg0[myCart.myCurrentSlice[0]] << " / " - << seg1[myCart.myCurrentSlice[1]] << " / " - << seg2[myCart.myCurrentSlice[2]]; - - return buf.str(); + info << (noBrackets ? "" : "("); + info << "$" << Common::Base::HEX1 << (hotspot + bank + segment * 8); + info << (noBrackets ? "" : ")"); + + return info.str(); } diff --git a/src/debugger/gui/CartE0Widget.hxx b/src/debugger/gui/CartE0Widget.hxx index bdbe73182..a50893e84 100644 --- a/src/debugger/gui/CartE0Widget.hxx +++ b/src/debugger/gui/CartE0Widget.hxx @@ -19,11 +19,10 @@ #define CARTRIDGEE0_WIDGET_HXX class CartridgeE0; -class PopUpWidget; -#include "CartDebugWidget.hxx" +#include "CartEnhancedWidget.hxx" -class CartridgeE0Widget : public CartDebugWidget +class CartridgeE0Widget : public CartridgeEnhancedWidget { public: CartridgeE0Widget(GuiObject* boss, const GUI::Font& lfont, @@ -33,21 +32,17 @@ class CartridgeE0Widget : public CartDebugWidget virtual ~CartridgeE0Widget() = default; private: - CartridgeE0& myCart; - PopUpWidget *mySlice0{nullptr}, *mySlice1{nullptr}, *mySlice2{nullptr}; + string manufacturer() override { return "Parker Brothers"; } - enum { - kSlice0Changed = 's0CH', - kSlice1Changed = 's1CH', - kSlice2Changed = 's2CH' - }; + string description() override; + + string romDescription() override; + + string hotspotStr(int bank, int segment, bool noBrackets = false) override; + + uInt16 bankSegs() override { return 3; } private: - void loadConfig() override; - void handleCommand(CommandSender* sender, int cmd, int data, int id) override; - - string bankState() override; - // Following constructors and assignment operators not supported CartridgeE0Widget() = delete; CartridgeE0Widget(const CartridgeE0Widget&) = delete; diff --git a/src/debugger/gui/CartE78KWidget.cxx b/src/debugger/gui/CartE78KWidget.cxx index b14d24809..2ad1c012e 100644 --- a/src/debugger/gui/CartE78KWidget.cxx +++ b/src/debugger/gui/CartE78KWidget.cxx @@ -27,17 +27,18 @@ CartridgeE78KWidget::CartridgeE78KWidget( : CartridgeMNetworkWidget(boss, lfont, nfont, x, y, w, h, cart) { ostringstream info; - info << "E78K cartridge, 4 2K slices ROM + 2 1K RAM\n" - << "Lower 2K accessible @ $F000 - $F7FF\n" - << " Slice 0 - 2 of ROM (hotspots $FE4 to $FE6)\n" - << " Slice 7 (1K) of RAM (hotspot $FE7)\n" - << " $F400 - $F7FF (R), $F000 - $F3FF (W)\n" - << "256B RAM accessible @ $F800 - $F9FF\n" - << " Hotspots $FE8 - $FEB (256B of RAM slice 1)\n" - << " $F900 - $F9FF (R), $F800 - $F8FF (W)\n" - << "Upper 1.5K ROM accessible @ $FA00 - $FFFF\n" - << " Always points to last 1.5K of ROM\n" - << "Startup slices = 0 / 0 or undetermined\n"; + info << "E78K cartridge, four 2K banks ROM + 2K RAM,\n" + << " mapped into three segments\n" + << "Lower 2K accessible @ $F000 - $F7FF\n" + << " ROM banks 0 - 2 (hotspots $FFE4 to $FFE6)\n" + << " 1K RAM bank 3 (hotspot $FFE7)\n" + << " $F400 - $F7FF (R), $F000 - $F3FF (W)\n" + << "256B RAM accessible @ $F800 - $F9FF\n" + << " RAM banks 0 - 3 (hotspots $FFE8 - $FFEB)\n" + << " $F900 - $F9FF (R), $F800 - $F8FF (W)\n" + << "Upper 1.5K ROM accessible @ $FA00 - $FFFF\n" + << " Always points to last 1.5K of ROM\n" + << "Startup segments = 0 / 0 or undetermined\n"; #if 0 // Eventually, we should query this from the debugger/disassembler @@ -53,7 +54,7 @@ CartridgeE78KWidget::CartridgeE78KWidget( const char* CartridgeE78KWidget::getSpotLower(int idx) { static constexpr std::array spot_lower = { - "0 - ROM ($FFE4)", "1 - ROM ($FFE5)", "2 - ROM ($FFE6)", "3 - RAM ($FFE7)" + "#0 - ROM ($FFE4)", "#1 - ROM ($FFE5)", "#2 - ROM ($FFE6)", "#3 - RAM ($FFE7)" }; return spot_lower[idx]; @@ -63,7 +64,7 @@ const char* CartridgeE78KWidget::getSpotLower(int idx) const char* CartridgeE78KWidget::getSpotUpper(int idx) { static constexpr std::array spot_upper = { - "0 - RAM ($FFE8)", "1 - RAM ($FFE9)", "2 - RAM ($FFEA)", "3 - RAM ($FFEB)" + "#0 - RAM ($FFE8)", "#1 - RAM ($FFE9)", "#2 - RAM ($FFEA)", "#3 - RAM ($FFEB)" }; return spot_upper[idx]; diff --git a/src/debugger/gui/CartE7Widget.cxx b/src/debugger/gui/CartE7Widget.cxx index f4a5ea3dd..8546e84eb 100644 --- a/src/debugger/gui/CartE7Widget.cxx +++ b/src/debugger/gui/CartE7Widget.cxx @@ -26,17 +26,18 @@ CartridgeE7Widget::CartridgeE7Widget( : CartridgeMNetworkWidget(boss, lfont, nfont, x, y, w, h, cart) { ostringstream info; - info << "E7 cartridge, 8 2K slices ROM + 2 1K RAM\n" + info << "E7 cartridge, eight 2K banks ROM + 2K RAM,\n" + << " mapped into three segments\n" << "Lower 2K accessible @ $F000 - $F7FF\n" - << " Slice 0 - 6 of ROM (hotspots $FE0 to $FE6)\n" - << " Slice 7 (1K) of RAM (hotspot $FE7)\n" + << " ROM Banks 0 - 6 (hotspots $FFE0 to $FFE6)\n" + << " 1K RAM Bank 7 (hotspot $FFE7)\n" << " $F400 - $F7FF (R), $F000 - $F3FF (W)\n" << "256B RAM accessible @ $F800 - $F9FF\n" - << " Hotspots $FE8 - $FEB (256B of RAM slice 1)\n" + << " RAM banks 0 - 3 (hotspots $FFE8 - $FFEB)\n" << " $F900 - $F9FF (R), $F800 - $F8FF (W)\n" << "Upper 1.5K ROM accessible @ $FA00 - $FFFF\n" << " Always points to last 1.5K of ROM\n" - << "Startup slices = 0 / 0 or undetermined\n"; + << "Startup segments = 0 / 0 or undetermined\n"; #if 0 // Eventually, we should query this from the debugger/disassembler @@ -52,8 +53,8 @@ CartridgeE7Widget::CartridgeE7Widget( const char* CartridgeE7Widget::getSpotLower(int idx) { static constexpr std::array spot_lower = { - "0 - ROM ($FFE0)", "1 - ROM ($FFE1)", "2 - ROM ($FFE2)", "3 - ROM ($FFE3)", - "4 - ROM ($FFE4)", "5 - ROM ($FFE5)", "6 - ROM ($FFE6)", "7 - RAM ($FFE7)" + "#0 - ROM ($FFE0)", "#1 - ROM ($FFE1)", "#2 - ROM ($FFE2)", "#3 - ROM ($FFE3)", + "#4 - ROM ($FFE4)", "#5 - ROM ($FFE5)", "#6 - ROM ($FFE6)", "#7 - RAM ($FFE7)" }; return spot_lower[idx]; @@ -63,7 +64,7 @@ const char* CartridgeE7Widget::getSpotLower(int idx) const char* CartridgeE7Widget::getSpotUpper(int idx) { static constexpr std::array spot_upper = { - "0 - RAM ($FFE8)", "1 - RAM ($FFE9)", "2 - RAM ($FFEA)", "3 - RAM ($FFEB)" + "#0 - RAM ($FFE8)", "#1 - RAM ($FFE9)", "#2 - RAM ($FFEA)", "#3 - RAM ($FFEB)" }; return spot_upper[idx]; diff --git a/src/debugger/gui/CartEFSCWidget.cxx b/src/debugger/gui/CartEFSCWidget.cxx index a7bae36c7..d6f60ddba 100644 --- a/src/debugger/gui/CartEFSCWidget.cxx +++ b/src/debugger/gui/CartEFSCWidget.cxx @@ -15,167 +15,25 @@ // this file, and for a DISCLAIMER OF ALL WARRANTIES. //============================================================================ -#include "Debugger.hxx" -#include "CartDebug.hxx" #include "CartEFSC.hxx" -#include "PopUpWidget.hxx" #include "CartEFSCWidget.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeEFSCWidget::CartridgeEFSCWidget( GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, int x, int y, int w, int h, CartridgeEFSC& cart) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h), - myCart(cart) + : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart) { - uInt32 size = 16 * 4096; + initialize(); +} +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string CartridgeEFSCWidget::description() +{ ostringstream info; - info << "64K H. Runner EFSC + RAM, 16 4K banks\n" - << "128 bytes RAM @ $F000 - $F0FF\n" - << " $F080 - $F0FF (R), $F000 - $F07F (W)\n" - << "Startup bank = " << cart.startBank() << "\n"; - // Eventually, we should query this from the debugger/disassembler - for(uInt32 i = 0, offset = 0xFFC, spot = 0xFE0; i < 16; ++i, offset += 0x1000) - { - uInt16 start = (cart.myImage[offset+1] << 8) | cart.myImage[offset]; - start -= start % 0x1000; - info << "Bank " << std::dec << i << " @ $" << Common::Base::HEX4 << (start + 0x100) - << " - " << "$" << (start + 0xFFF) << " (hotspot = $F" << (spot+i) << ")\n"; - } + info << "64K H. Runner EFSC + RAM, 16 4K banks\n"; + info << CartridgeEnhancedWidget::description(); - int xpos = 2, - ypos = addBaseInformation(size, "Paul Slocum / Homestar Runner", - info.str()) + myLineHeight; - - VariantList items; - VarList::push_back(items, " 0 ($FFE0)"); - VarList::push_back(items, " 1 ($FFE1)"); - VarList::push_back(items, " 2 ($FFE2)"); - VarList::push_back(items, " 3 ($FFE3)"); - VarList::push_back(items, " 4 ($FFE4)"); - VarList::push_back(items, " 5 ($FFE5)"); - VarList::push_back(items, " 6 ($FFE6)"); - VarList::push_back(items, " 7 ($FFE7)"); - VarList::push_back(items, " 8 ($FFE8)"); - VarList::push_back(items, " 9 ($FFE9)"); - VarList::push_back(items, "10 ($FFEA)"); - VarList::push_back(items, "11 ($FFEB)"); - VarList::push_back(items, "12 ($FFEC)"); - VarList::push_back(items, "13 ($FFED)"); - VarList::push_back(items, "14 ($FFEE)"); - VarList::push_back(items, "15 ($FFEF)"); - myBank = - new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("15 ($FFE0)"), - myLineHeight, items, "Set bank ", - 0, kBankChanged); - myBank->setTarget(this); - addFocusWidget(myBank); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeEFSCWidget::saveOldState() -{ - myOldState.internalram.clear(); - - for(uInt32 i = 0; i < internalRamSize(); ++i) - myOldState.internalram.push_back(myCart.myRAM[i]); - - myOldState.bank = myCart.getBank(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeEFSCWidget::loadConfig() -{ - myBank->setSelectedIndex(myCart.getBank(), myCart.getBank() != myOldState.bank); - - CartDebugWidget::loadConfig(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeEFSCWidget::handleCommand(CommandSender* sender, - int cmd, int data, int id) -{ - if(cmd == kBankChanged) - { - myCart.unlockBank(); - myCart.bank(myBank->getSelected()); - myCart.lockBank(); - invalidate(); - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeEFSCWidget::bankState() -{ - ostringstream& buf = buffer(); - - static constexpr std::array spot = { - "$FFE0", "$FFE1", "$FFE2", "$FFE3", "$FFE4", "$FFE5", "$FFE6", "$FFE7", - "$FFE8", "$FFE9", "$FFEA", "$FFEB", "$FFEC", "$FFED", "$FFEE", "$FFEF" - }; - buf << "Bank = " << std::dec << myCart.getBank() - << ", hotspot = " << spot[myCart.getBank()]; - - return buf.str(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 CartridgeEFSCWidget::internalRamSize() -{ - return 128; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 CartridgeEFSCWidget::internalRamRPort(int start) -{ - return 0xF080 + start; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeEFSCWidget::internalRamDescription() -{ - ostringstream desc; - desc << "$F000 - $F07F used for Write Access\n" - << "$F080 - $F0FF used for Read Access"; - - return desc.str(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const ByteArray& CartridgeEFSCWidget::internalRamOld(int start, int count) -{ - myRamOld.clear(); - for(int i = 0; i < count; i++) - myRamOld.push_back(myOldState.internalram[start + i]); - return myRamOld; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const ByteArray& CartridgeEFSCWidget::internalRamCurrent(int start, int count) -{ - myRamCurrent.clear(); - for(int i = 0; i < count; i++) - myRamCurrent.push_back(myCart.myRAM[start + i]); - return myRamCurrent; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeEFSCWidget::internalRamSetValue(int addr, uInt8 value) -{ - myCart.myRAM[addr] = value; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeEFSCWidget::internalRamGetValue(int addr) -{ - return myCart.myRAM[addr]; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeEFSCWidget::internalRamLabel(int addr) -{ - CartDebug& dbg = instance().debugger().cartDebug(); - return dbg.getLabel(addr + 0xF080, false); + return info.str(); } diff --git a/src/debugger/gui/CartEFSCWidget.hxx b/src/debugger/gui/CartEFSCWidget.hxx index b9e902f69..975f17f8a 100644 --- a/src/debugger/gui/CartEFSCWidget.hxx +++ b/src/debugger/gui/CartEFSCWidget.hxx @@ -19,11 +19,10 @@ #define CARTRIDGEEFSC_WIDGET_HXX class CartridgeEFSC; -class PopUpWidget; -#include "CartDebugWidget.hxx" +#include "CartEnhancedWidget.hxx" -class CartridgeEFSCWidget : public CartDebugWidget +class CartridgeEFSCWidget : public CartridgeEnhancedWidget { public: CartridgeEFSCWidget(GuiObject* boss, const GUI::Font& lfont, @@ -33,35 +32,11 @@ class CartridgeEFSCWidget : public CartDebugWidget virtual ~CartridgeEFSCWidget() = default; private: - CartridgeEFSC& myCart; - PopUpWidget* myBank{nullptr}; + string manufacturer() override { return "Paul Slocum / Homestar Runner"; } - struct CartState { - ByteArray internalram; - uInt16 bank{0}; - }; - CartState myOldState; - - enum { kBankChanged = 'bkCH' }; + string description() override; private: - void saveOldState() override; - void loadConfig() override; - void handleCommand(CommandSender* sender, int cmd, int data, int id) override; - - string bankState() override; - - // start of functions for Cartridge RAM tab - uInt32 internalRamSize() override; - uInt32 internalRamRPort(int start) override; - string internalRamDescription() override; - const ByteArray& internalRamOld(int start, int count) override; - const ByteArray& internalRamCurrent(int start, int count) override; - void internalRamSetValue(int addr, uInt8 value) override; - uInt8 internalRamGetValue(int addr) override; - string internalRamLabel(int addr) override; - // end of functions for Cartridge RAM tab - // Following constructors and assignment operators not supported CartridgeEFSCWidget() = delete; CartridgeEFSCWidget(const CartridgeEFSCWidget&) = delete; diff --git a/src/debugger/gui/CartEFWidget.cxx b/src/debugger/gui/CartEFWidget.cxx index 165c205bf..c00d2c3f6 100644 --- a/src/debugger/gui/CartEFWidget.cxx +++ b/src/debugger/gui/CartEFWidget.cxx @@ -16,97 +16,24 @@ //============================================================================ #include "CartEF.hxx" -#include "PopUpWidget.hxx" #include "CartEFWidget.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeEFWidget::CartridgeEFWidget( GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, int x, int y, int w, int h, CartridgeEF& cart) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h), - myCart(cart) + : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart) { - uInt32 size = 16 * 4096; + initialize(); +} +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string CartridgeEFWidget::description() +{ ostringstream info; - info << "64K H. Runner EF cartridge, 16 4K banks\n" - << "Startup bank = " << cart.startBank() << "\n"; - // Eventually, we should query this from the debugger/disassembler - for(uInt32 i = 0, offset = 0xFFC, spot = 0xFE0; i < 16; ++i, offset += 0x1000) - { - uInt16 start = (cart.myImage[offset+1] << 8) | cart.myImage[offset]; - start -= start % 0x1000; - info << "Bank " << std::dec << i << " @ $" << Common::Base::HEX4 << start << " - " - << "$" << (start + 0xFFF) << " (hotspot = $F" << (spot+i) << ")\n"; - } + info << "64K H. Runner EF cartridge, 16 4K banks\n"; + info << CartridgeEnhancedWidget::description(); - int xpos = 2, - ypos = addBaseInformation(size, "Paul Slocum / Homestar Runner", - info.str()) + myLineHeight; - - VariantList items; - VarList::push_back(items, " 0 ($FFE0)"); - VarList::push_back(items, " 1 ($FFE1)"); - VarList::push_back(items, " 2 ($FFE2)"); - VarList::push_back(items, " 3 ($FFE3)"); - VarList::push_back(items, " 4 ($FFE4)"); - VarList::push_back(items, " 5 ($FFE5)"); - VarList::push_back(items, " 6 ($FFE6)"); - VarList::push_back(items, " 7 ($FFE7)"); - VarList::push_back(items, " 8 ($FFE8)"); - VarList::push_back(items, " 9 ($FFE9)"); - VarList::push_back(items, "10 ($FFEA)"); - VarList::push_back(items, "11 ($FFEB)"); - VarList::push_back(items, "12 ($FFEC)"); - VarList::push_back(items, "13 ($FFED)"); - VarList::push_back(items, "14 ($FFEE)"); - VarList::push_back(items, "15 ($FFEF)"); - myBank = - new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("15 ($FFE0)"), - myLineHeight, items, "Set bank ", - 0, kBankChanged); - myBank->setTarget(this); - addFocusWidget(myBank); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeEFWidget::loadConfig() -{ - Debugger& dbg = instance().debugger(); - CartDebug& cart = dbg.cartDebug(); - const CartState& state = static_cast(cart.getState()); - const CartState& oldstate = static_cast(cart.getOldState()); - - myBank->setSelectedIndex(myCart.getBank(), state.bank != oldstate.bank); - - CartDebugWidget::loadConfig(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeEFWidget::handleCommand(CommandSender* sender, - int cmd, int data, int id) -{ - if(cmd == kBankChanged) - { - myCart.unlockBank(); - myCart.bank(myBank->getSelected()); - myCart.lockBank(); - invalidate(); - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeEFWidget::bankState() -{ - ostringstream& buf = buffer(); - - static constexpr std::array spot = { - "$FFE0", "$FFE1", "$FFE2", "$FFE3", "$FFE4", "$FFE5", "$FFE6", "$FFE7", - "$FFE8", "$FFE9", "$FFEA", "$FFEB", "$FFEC", "$FFED", "$FFEE", "$FFEF" - }; - buf << "Bank = " << std::dec << myCart.getBank() - << ", hotspot = " << spot[myCart.getBank()]; - - return buf.str(); + return info.str(); } diff --git a/src/debugger/gui/CartEFWidget.hxx b/src/debugger/gui/CartEFWidget.hxx index 3f34fa6cb..7d173836a 100644 --- a/src/debugger/gui/CartEFWidget.hxx +++ b/src/debugger/gui/CartEFWidget.hxx @@ -19,11 +19,10 @@ #define CARTRIDGEEF_WIDGET_HXX class CartridgeEF; -class PopUpWidget; -#include "CartDebugWidget.hxx" +#include "CartEnhancedWidget.hxx" -class CartridgeEFWidget : public CartDebugWidget +class CartridgeEFWidget : public CartridgeEnhancedWidget { public: CartridgeEFWidget(GuiObject* boss, const GUI::Font& lfont, @@ -33,17 +32,11 @@ class CartridgeEFWidget : public CartDebugWidget virtual ~CartridgeEFWidget() = default; private: - CartridgeEF& myCart; - PopUpWidget* myBank{nullptr}; + string manufacturer() override { return "Paul Slocum / Homestar Runner"; } - enum { kBankChanged = 'bkCH' }; + string description() override; private: - void loadConfig() override; - void handleCommand(CommandSender* sender, int cmd, int data, int id) override; - - string bankState() override; - // Following constructors and assignment operators not supported CartridgeEFWidget() = delete; CartridgeEFWidget(const CartridgeEFWidget&) = delete; diff --git a/src/debugger/gui/CartEnhancedWidget.cxx b/src/debugger/gui/CartEnhancedWidget.cxx new file mode 100644 index 000000000..f864a21c0 --- /dev/null +++ b/src/debugger/gui/CartEnhancedWidget.cxx @@ -0,0 +1,394 @@ +//============================================================================ +// +// 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-2020 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 "PopUpWidget.hxx" +#include "CartEnhanced.hxx" +#include "CartEnhancedWidget.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeEnhancedWidget::CartridgeEnhancedWidget(GuiObject* boss, const GUI::Font& lfont, + const GUI::Font& nfont, + int x, int y, int w, int h, + CartridgeEnhanced& cart) + : CartDebugWidget(boss, lfont, nfont, x, y, w, h), + myCart(cart) +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +int CartridgeEnhancedWidget::initialize() +{ + int ypos = addBaseInformation(size(), manufacturer(), description(), descriptionLines()) + + myLineHeight; + + bankSelect(ypos); + + return ypos; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +size_t CartridgeEnhancedWidget::size() +{ + size_t size; + + myCart.getImage(size); + + return size; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string CartridgeEnhancedWidget::description() +{ + ostringstream info; + + if (myCart.myRamSize > 0) + info << ramDescription(); + info << romDescription(); + + return info.str(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +int CartridgeEnhancedWidget::descriptionLines() +{ + return 18; // should be enough for almost all types +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string CartridgeEnhancedWidget::ramDescription() +{ + ostringstream info; + + if(myCart.ramBankCount() == 0) + info << myCart.myRamSize << " bytes RAM @ " + << "$" << Common::Base::HEX4 << ADDR_BASE << " - " + << "$" << (ADDR_BASE | (myCart.myRamSize * 2 - 1)) << "\n"; + + info << " $" << Common::Base::HEX4 << (ADDR_BASE | myCart.myReadOffset) + << " - $" << (ADDR_BASE | (myCart.myReadOffset + myCart.myRamMask)) << " (R)" + << ", $" << (ADDR_BASE | myCart.myWriteOffset) + << " - $" << (ADDR_BASE | (myCart.myWriteOffset + myCart.myRamMask)) << " (W)\n"; + + return info.str(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string CartridgeEnhancedWidget::romDescription() +{ + ostringstream info; + size_t size; + const uInt8* image = myCart.getImage(size); + + if(myCart.romBankCount() > 1) + { + for(int bank = 0, offset = 0xFFC; bank < myCart.romBankCount(); ++bank, offset += 0x1000) + { + uInt16 start = (image[offset + 1] << 8) | image[offset]; + start -= start % 0x1000; + string hash = myCart.romBankCount() > 10 && bank < 10 ? " #" : "#"; + + info << "Bank " << hash << std::dec << bank << " @ $" + << Common::Base::HEX4 << (start + myCart.myRomOffset) << " - $" << (start + 0xFFF); + if(myCart.hotspot() != 0) + { + string hs = hotspotStr(bank, 0, true); + if(hs.length() > 22) + info << "\n "; + info << " " << hs; + } + info << "\n"; + } + info << "Startup bank = #" << std::dec << myCart.startBank() << " or undetermined\n"; + } + else + { + uInt16 start = (image[myCart.mySize - 3] << 8) | image[myCart.mySize - 4]; + uInt16 end; + + start -= start % std::min(int(size), 0x1000); + end = start + uInt16(myCart.mySize) - 1; + // special check for ROMs where the extra RAM is not included in the image (e.g. CV). + if((start & 0xFFF) < size) + { + start += myCart.myRomOffset; + } + info << "ROM accessible @ $" + << Common::Base::HEX4 << start << " - $" + << Common::Base::HEX4 << end; + } + + return info.str(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeEnhancedWidget::bankList(uInt16 bankCount, int seg, VariantList& items, int& width) +{ + width = 0; + + for(int bank = 0; bank < bankCount; ++bank) + { + ostringstream buf; + + buf << std::setw(bank < 10 ? 2 : 1) << "#" << std::dec << bank; + if(myCart.hotspot() != 0 && myHotspotDelta > 0) + buf << " " << hotspotStr(bank, seg); + VarList::push_back(items, buf.str()); + width = std::max(width, _font.getStringWidth(buf.str())); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeEnhancedWidget::bankSelect(int& ypos) +{ + if(myCart.romBankCount() > 1) + { + int xpos = 2; + + myBankWidgets = make_unique(bankSegs()); + + for(int seg = 0; seg < bankSegs(); ++seg) + { + // fill bank and hotspot list + VariantList items; + int pw = 0; + + bankList(myCart.romBankCount(), seg, items, pw); + + // create widgets + ostringstream buf; + + buf << "Set bank"; + if(bankSegs() > 1) + buf << " for segment #" << seg << " "; + else + buf << " "; // align with info + + myBankWidgets[seg] = new PopUpWidget(_boss, _font, xpos, ypos - 2, + pw, myLineHeight, items, buf.str(), + 0, kBankChanged); + myBankWidgets[seg]->setTarget(this); + myBankWidgets[seg]->setID(seg); + addFocusWidget(myBankWidgets[seg]); + + ypos += myBankWidgets[seg]->getHeight() + 4; + } + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string CartridgeEnhancedWidget::bankState() +{ + if(myCart.romBankCount() > 1) + { + ostringstream& buf = buffer(); + uInt16 hotspot = myCart.hotspot(); + bool hasRamBanks = myCart.myRamBankCount > 0; + + if(bankSegs() > 1) + { + buf << "Segments: "; + + for(int seg = 0; seg < bankSegs(); ++seg) + { + int bank = myCart.getSegmentBank(seg); + bool isRamBank = (bank >= myCart.romBankCount()); + + + if(seg > 0) + buf << " / "; + + buf << "#" << std::dec << (bank - (isRamBank ? myCart.romBankCount() : 0)); + + if(isRamBank) // was RAM mapped here? + buf << " RAM"; + else if (hasRamBanks) + buf << " ROM"; + + //if(hotspot >= 0x100) + if(hotspot != 0 && myHotspotDelta > 0) + buf << " " << hotspotStr(bank, 0, bankSegs() < 3); + } + } + else + { + buf << "Bank #" << std::dec << myCart.getBank(); + + if(hotspot != 0 && myHotspotDelta > 0) + buf << " " << hotspotStr(myCart.getBank(), 0, true); + } + return buf.str(); + } + return "non-bankswitched"; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string CartridgeEnhancedWidget::hotspotStr(int bank, int segment, bool prefix) +{ + ostringstream info; + uInt16 hotspot = myCart.hotspot(); + + if(hotspot & 0x1000) + hotspot |= ADDR_BASE; + + info << "(" << (prefix ? "hotspot " : ""); + info << "$" << Common::Base::HEX1 << (hotspot + bank * myHotspotDelta); + info << ")"; + + return info.str(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt16 CartridgeEnhancedWidget::bankSegs() +{ + return myCart.myBankSegs; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeEnhancedWidget::saveOldState() +{ + myOldState.internalRam.clear(); + for(uInt32 i = 0; i < myCart.myRamSize; ++i) + myOldState.internalRam.push_back(myCart.myRAM[i]); + + myOldState.banks.clear(); + if (bankSegs() > 1) + for(int seg = 0; seg < bankSegs(); ++seg) + myOldState.banks.push_back(myCart.getSegmentBank(seg)); + else + myOldState.banks.push_back(myCart.getBank()); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeEnhancedWidget::loadConfig() +{ + if(myBankWidgets != nullptr) + { + if (bankSegs() > 1) + for(int seg = 0; seg < bankSegs(); ++seg) + myBankWidgets[seg]->setSelectedIndex(myCart.getSegmentBank(seg), + myCart.getSegmentBank(seg) != myOldState.banks[seg]); + else + myBankWidgets[0]->setSelectedIndex(myCart.getBank(), + myCart.getBank() != myOldState.banks[0]); + } + CartDebugWidget::loadConfig(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeEnhancedWidget::handleCommand(CommandSender* sender, + int cmd, int data, int id) +{ + if(cmd == kBankChanged) + { + myCart.unlockBank(); + myCart.bank(myBankWidgets[id]->getSelected(), id); + myCart.lockBank(); + invalidate(); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt32 CartridgeEnhancedWidget::internalRamSize() +{ + return uInt32(myCart.myRamSize); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt32 CartridgeEnhancedWidget::internalRamRPort(int start) +{ + if(myCart.ramBankCount() == 0) + return ADDR_BASE + myCart.myReadOffset + start; + else + return start; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string CartridgeEnhancedWidget::internalRamDescription() +{ + ostringstream desc; + string indent = ""; + + if(myCart.ramBankCount()) + { + desc << "Accessible "; + if (myCart.bankSize() >> 1 >= 1024) + desc << ((myCart.bankSize() >> 1) / 1024) << "K"; + else + desc << (myCart.bankSize() >> 1) << " bytes"; + desc << " at a time via:\n"; + indent = " "; + } + + // order RW by addresses + if(myCart.myReadOffset <= myCart.myWriteOffset) + { + desc << indent << "$" << Common::Base::HEX4 << (ADDR_BASE | myCart.myReadOffset) + << " - $" << (ADDR_BASE | (myCart.myReadOffset + myCart.myRamMask)) + << " used for read access\n"; + } + + desc << indent << "$" << Common::Base::HEX4 << (ADDR_BASE | myCart.myWriteOffset) + << " - $" << (ADDR_BASE | (myCart.myWriteOffset + myCart.myRamMask)) + << " used for write access"; + + if(myCart.myReadOffset > myCart.myWriteOffset) + { + desc << indent << "\n$" << Common::Base::HEX4 << (ADDR_BASE | myCart.myReadOffset) + << " - $" << (ADDR_BASE | (myCart.myReadOffset + myCart.myRamMask)) + << " used for read access"; + } + + return desc.str(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const ByteArray& CartridgeEnhancedWidget::internalRamOld(int start, int count) +{ + myRamOld.clear(); + for(int i = 0; i < count; i++) + myRamOld.push_back(myOldState.internalRam[start + i]); + return myRamOld; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const ByteArray& CartridgeEnhancedWidget::internalRamCurrent(int start, int count) +{ + myRamCurrent.clear(); + for(int i = 0; i < count; i++) + myRamCurrent.push_back(myCart.myRAM[start + i]); + return myRamCurrent; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeEnhancedWidget::internalRamSetValue(int addr, uInt8 value) +{ + myCart.myRAM[addr] = value; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 CartridgeEnhancedWidget::internalRamGetValue(int addr) +{ + return myCart.myRAM[addr]; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string CartridgeEnhancedWidget::internalRamLabel(int addr) +{ + CartDebug& dbg = instance().debugger().cartDebug(); + return dbg.getLabel(addr + ADDR_BASE + myCart.myReadOffset, false); +} diff --git a/src/debugger/gui/CartCVPlusWidget.hxx b/src/debugger/gui/CartEnhancedWidget.hxx similarity index 50% rename from src/debugger/gui/CartCVPlusWidget.hxx rename to src/debugger/gui/CartEnhancedWidget.hxx index b1401b07c..968a4bbf0 100644 --- a/src/debugger/gui/CartCVPlusWidget.hxx +++ b/src/debugger/gui/CartEnhancedWidget.hxx @@ -15,42 +15,57 @@ // this file, and for a DISCLAIMER OF ALL WARRANTIES. //============================================================================ -#ifndef CARTRIDGECVPlus_WIDGET_HXX -#define CARTRIDGECVPlus_WIDGET_HXX +#ifndef CART_ENHANCED_WIDGET_HXX +#define CART_ENHANCED_WIDGET_HXX -class CartridgeCVPlus; +class CartridgeEnhanced; class PopUpWidget; +namespace GUI { + class Font; +} + #include "CartDebugWidget.hxx" -class CartridgeCVPlusWidget : public CartDebugWidget +class CartridgeEnhancedWidget : public CartDebugWidget { public: - CartridgeCVPlusWidget(GuiObject* boss, const GUI::Font& lfont, - const GUI::Font& nfont, - int x, int y, int w, int h, - CartridgeCVPlus& cart); - virtual ~CartridgeCVPlusWidget() = default; + CartridgeEnhancedWidget(GuiObject* boss, const GUI::Font& lfont, + const GUI::Font& nfont, + int x, int y, int w, int h, + CartridgeEnhanced& cart); + virtual ~CartridgeEnhancedWidget() = default; - private: - CartridgeCVPlus& myCart; - PopUpWidget* myBank{nullptr}; - struct CartState { - ByteArray internalram; - uInt16 bank{0}; - }; - CartState myOldState; + protected: + int initialize(); - enum { kBankChanged = 'bkCH' }; + virtual size_t size(); - private: + virtual string manufacturer() = 0; + + virtual string description(); + + virtual int descriptionLines(); + + virtual string ramDescription(); + + virtual string romDescription(); + + virtual void bankList(uInt16 bankCount, int seg, VariantList& items, int& width); + + virtual void bankSelect(int& ypos); + + virtual string hotspotStr(int bank = 0, int segment = 0, bool prefix = false); + + virtual uInt16 bankSegs(); // { return myCart.myBankSegs; } + + void saveOldState() override; void loadConfig() override; + void handleCommand(CommandSender* sender, int cmd, int data, int id) override; string bankState() override; - void saveOldState() override; - // start of functions for Cartridge RAM tab uInt32 internalRamSize() override; uInt32 internalRamRPort(int start) override; @@ -62,12 +77,34 @@ class CartridgeCVPlusWidget : public CartDebugWidget string internalRamLabel(int addr) override; // end of functions for Cartridge RAM tab + protected: + enum { kBankChanged = 'bkCH' }; + + struct CartState { + ByteArray internalRam; + ByteArray banks; + }; + CartState myOldState; + + CartridgeEnhanced& myCart; + + // Distance between two hotspots + int myHotspotDelta{1}; + + std::unique_ptr myBankWidgets{nullptr}; + + + // Display all addresses based on this + static constexpr uInt16 ADDR_BASE = 0xF000; + + private: // Following constructors and assignment operators not supported - CartridgeCVPlusWidget() = delete; - CartridgeCVPlusWidget(const CartridgeCVPlusWidget&) = delete; - CartridgeCVPlusWidget(CartridgeCVPlusWidget&&) = delete; - CartridgeCVPlusWidget& operator=(const CartridgeCVPlusWidget&) = delete; - CartridgeCVPlusWidget& operator=(CartridgeCVPlusWidget&&) = delete; + CartridgeEnhancedWidget() = delete; + CartridgeEnhancedWidget(const CartridgeEnhancedWidget&) = delete; + CartridgeEnhancedWidget(CartridgeEnhancedWidget&&) = delete; + CartridgeEnhancedWidget& operator=(const CartridgeEnhancedWidget&) = delete; + CartridgeEnhancedWidget& operator=(CartridgeEnhancedWidget&&) = delete; }; #endif + diff --git a/src/debugger/gui/CartF0Widget.cxx b/src/debugger/gui/CartF0Widget.cxx index c7564cefa..78b13f2d9 100644 --- a/src/debugger/gui/CartF0Widget.cxx +++ b/src/debugger/gui/CartF0Widget.cxx @@ -16,85 +16,28 @@ //============================================================================ #include "CartF0.hxx" -#include "PopUpWidget.hxx" #include "CartF0Widget.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeF0Widget::CartridgeF0Widget( GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, int x, int y, int w, int h, CartridgeF0& cart) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h), - myCart(cart) + : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart) { - uInt32 size = 16 * 4096; + myHotspotDelta = 0; + initialize(); +} +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string CartridgeF0Widget::description() +{ ostringstream info; - info << "64K Megaboy F0 cartridge, 16 4K banks\n" - << "Startup bank = #" << cart.startBank() << " or undetermined\n" - << "Bankswitch triggered by accessing $1FF0\n"; - // Eventually, we should query this from the debugger/disassembler - for(uInt32 i = 0, offset = 0xFFC; i < 16; ++i, offset += 0x1000) - { - uInt16 start = (cart.myImage[offset+1] << 8) | cart.myImage[offset]; - start -= start % 0x1000; - info << "Bank " << std::dec << i << " @ $" << Common::Base::HEX4 << start - << " - " << "$" << (start + 0xFFF) << "\n"; - } + info << "Megaboy F0 cartridge, 16 4K banks\n" + << "Startup bank = #" << myCart.startBank() << " or undetermined\n" + << "Bankswitch triggered by accessing $" << Common::Base::HEX4 << 0xFFF0 << "\n"; - int xpos = 2, - ypos = addBaseInformation(size, "Dynacom Megaboy", - info.str()) + myLineHeight; - - VariantList items; - VarList::push_back(items, " 0"); - VarList::push_back(items, " 1"); - VarList::push_back(items, " 2"); - VarList::push_back(items, " 3"); - VarList::push_back(items, " 4"); - VarList::push_back(items, " 5"); - VarList::push_back(items, " 6"); - VarList::push_back(items, " 7"); - VarList::push_back(items, " 8"); - VarList::push_back(items, " 9"); - VarList::push_back(items, " 10"); - VarList::push_back(items, " 11"); - VarList::push_back(items, " 12"); - VarList::push_back(items, " 13"); - VarList::push_back(items, " 14"); - VarList::push_back(items, " 15"); - myBank = - new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth(" 15"), - myLineHeight, items, "Set bank #", - 0, kBankChanged); - myBank->setTarget(this); - addFocusWidget(myBank); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF0Widget::loadConfig() -{ - Debugger& dbg = instance().debugger(); - CartDebug& cart = dbg.cartDebug(); - const CartState& state = static_cast(cart.getState()); - const CartState& oldstate = static_cast(cart.getOldState()); - - myBank->setSelectedIndex(myCart.getBank(), state.bank != oldstate.bank); - - CartDebugWidget::loadConfig(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF0Widget::handleCommand(CommandSender* sender, - int cmd, int data, int id) -{ - if(cmd == kBankChanged) - { - myCart.unlockBank(); - myCart.bank(myBank->getSelected()); - myCart.lockBank(); - invalidate(); - } + return info.str(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -102,7 +45,8 @@ string CartridgeF0Widget::bankState() { ostringstream& buf = buffer(); - buf << "Bank = #" << std::dec << myCart.getBank() << ", hotspot = $FFF0"; + buf << "Bank #" << std::dec << myCart.getBank() + << " (hotspot $" << Common::Base::HEX4 << 0xFFF0 << ")"; return buf.str(); } diff --git a/src/debugger/gui/CartF0Widget.hxx b/src/debugger/gui/CartF0Widget.hxx index 6225fff71..017d88e78 100644 --- a/src/debugger/gui/CartF0Widget.hxx +++ b/src/debugger/gui/CartF0Widget.hxx @@ -19,11 +19,10 @@ #define CARTRIDGEF0_WIDGET_HXX class CartridgeF0; -class PopUpWidget; -#include "CartDebugWidget.hxx" +#include "CartEnhancedWidget.hxx" -class CartridgeF0Widget : public CartDebugWidget +class CartridgeF0Widget : public CartridgeEnhancedWidget { public: CartridgeF0Widget(GuiObject* boss, const GUI::Font& lfont, @@ -33,17 +32,13 @@ class CartridgeF0Widget : public CartDebugWidget virtual ~CartridgeF0Widget() = default; private: - CartridgeF0& myCart; - PopUpWidget* myBank{nullptr}; + string manufacturer() override { return "Dynacom Megaboy"; } - enum { kBankChanged = 'bkCH' }; - - private: - void loadConfig() override; - void handleCommand(CommandSender* sender, int cmd, int data, int id) override; + string description() override; string bankState() override; + private: // Following constructors and assignment operators not supported CartridgeF0Widget() = delete; CartridgeF0Widget(const CartridgeF0Widget&) = delete; diff --git a/src/debugger/gui/CartF4SCWidget.cxx b/src/debugger/gui/CartF4SCWidget.cxx index 512edba55..8f75b6fce 100644 --- a/src/debugger/gui/CartF4SCWidget.cxx +++ b/src/debugger/gui/CartF4SCWidget.cxx @@ -15,157 +15,25 @@ // this file, and for a DISCLAIMER OF ALL WARRANTIES. //============================================================================ -#include "Debugger.hxx" -#include "CartDebug.hxx" #include "CartF4SC.hxx" -#include "PopUpWidget.hxx" #include "CartF4SCWidget.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeF4SCWidget::CartridgeF4SCWidget( GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, int x, int y, int w, int h, CartridgeF4SC& cart) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h), - myCart(cart) + : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart) { - uInt16 size = 8 * 4096; + initialize(); +} +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string CartridgeF4SCWidget::description() +{ ostringstream info; - info << "Standard F4SC cartridge, eight 4K banks\n" - << "128 bytes RAM @ $F000 - $F0FF\n" - << " $F080 - $F0FF (R), $F000 - $F07F (W)\n" - << "Startup bank = " << cart.startBank() << " or undetermined\n"; - // Eventually, we should query this from the debugger/disassembler - for(uInt32 i = 0, offset = 0xFFC, spot = 0xFF4; i < 8; ++i, offset += 0x1000) - { - uInt16 start = (cart.myImage[offset+1] << 8) | cart.myImage[offset]; - start -= start % 0x1000; - info << "Bank " << i << " @ $" << Common::Base::HEX4 << (start + 0x100) << " - " - << "$" << (start + 0xFFF) << " (hotspot = $F" << (spot+i) << ")\n"; - } + info << "Standard F4SC cartridge, eight 4K banks\n"; + info << CartridgeEnhancedWidget::description(); - int xpos = 2, - ypos = addBaseInformation(size, "Atari", info.str(), 15) + myLineHeight; - - VariantList items; - VarList::push_back(items, "0 ($FFF4)"); - VarList::push_back(items, "1 ($FFF5)"); - VarList::push_back(items, "2 ($FFF6)"); - VarList::push_back(items, "3 ($FFF7)"); - VarList::push_back(items, "4 ($FFF8)"); - VarList::push_back(items, "5 ($FFF9)"); - VarList::push_back(items, "6 ($FFFA)"); - VarList::push_back(items, "7 ($FFFB)"); - myBank = - new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("0 ($FFFx)"), - myLineHeight, items, "Set bank ", - 0, kBankChanged); - myBank->setTarget(this); - addFocusWidget(myBank); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF4SCWidget::saveOldState() -{ - myOldState.internalram.clear(); - - for(uInt32 i = 0; i < internalRamSize(); ++i) - myOldState.internalram.push_back(myCart.myRAM[i]); - - myOldState.bank = myCart.getBank(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF4SCWidget::loadConfig() -{ - myBank->setSelectedIndex(myCart.getBank(), myCart.getBank() != myOldState.bank); - - CartDebugWidget::loadConfig(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF4SCWidget::handleCommand(CommandSender* sender, - int cmd, int data, int id) -{ - if(cmd == kBankChanged) - { - myCart.unlockBank(); - myCart.bank(myBank->getSelected()); - myCart.lockBank(); - invalidate(); - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeF4SCWidget::bankState() -{ - ostringstream& buf = buffer(); - - static constexpr std::array spot = { - "$FFF4", "$FFF5", "$FFF6", "$FFF7", "$FFF8", "$FFF9", "$FFFA", "$FFFB" - }; - buf << "Bank = " << std::dec << myCart.getBank() - << ", hotspot = " << spot[myCart.getBank()]; - - return buf.str(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 CartridgeF4SCWidget::internalRamSize() -{ - return 128; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 CartridgeF4SCWidget::internalRamRPort(int start) -{ - return 0xF080 + start; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeF4SCWidget::internalRamDescription() -{ - ostringstream desc; - desc << "$F000 - $F07F used for Write Access\n" - << "$F080 - $F0FF used for Read Access"; - - return desc.str(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const ByteArray& CartridgeF4SCWidget::internalRamOld(int start, int count) -{ - myRamOld.clear(); - for(int i = 0; i < count; i++) - myRamOld.push_back(myOldState.internalram[start + i]); - return myRamOld; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const ByteArray& CartridgeF4SCWidget::internalRamCurrent(int start, int count) -{ - myRamCurrent.clear(); - for(int i = 0; i < count; i++) - myRamCurrent.push_back(myCart.myRAM[start + i]); - return myRamCurrent; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF4SCWidget::internalRamSetValue(int addr, uInt8 value) -{ - myCart.myRAM[addr] = value; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeF4SCWidget::internalRamGetValue(int addr) -{ - return myCart.myRAM[addr]; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeF4SCWidget::internalRamLabel(int addr) -{ - CartDebug& dbg = instance().debugger().cartDebug(); - return dbg.getLabel(addr + 0xF080, false); + return info.str(); } diff --git a/src/debugger/gui/CartF4SCWidget.hxx b/src/debugger/gui/CartF4SCWidget.hxx index e2ec21f06..c6570a9fa 100644 --- a/src/debugger/gui/CartF4SCWidget.hxx +++ b/src/debugger/gui/CartF4SCWidget.hxx @@ -19,11 +19,10 @@ #define CARTRIDGEF4SC_WIDGET_HXX class CartridgeF4SC; -class PopUpWidget; -#include "CartDebugWidget.hxx" +#include "CartEnhancedWidget.hxx" -class CartridgeF4SCWidget : public CartDebugWidget +class CartridgeF4SCWidget : public CartridgeEnhancedWidget { public: CartridgeF4SCWidget(GuiObject* boss, const GUI::Font& lfont, @@ -33,36 +32,11 @@ class CartridgeF4SCWidget : public CartDebugWidget virtual ~CartridgeF4SCWidget() = default; private: - CartridgeF4SC& myCart; - PopUpWidget* myBank{nullptr}; + string manufacturer() override { return "Atari"; } - struct CartState { - ByteArray internalram; - uInt16 bank{0}; - }; - CartState myOldState; - - - enum { kBankChanged = 'bkCH' }; + string description() override; private: - void saveOldState() override; - void loadConfig() override; - void handleCommand(CommandSender* sender, int cmd, int data, int id) override; - - string bankState() override; - - // start of functions for Cartridge RAM tab - uInt32 internalRamSize() override; - uInt32 internalRamRPort(int start) override; - string internalRamDescription() override; - const ByteArray& internalRamOld(int start, int count) override; - const ByteArray& internalRamCurrent(int start, int count) override; - void internalRamSetValue(int addr, uInt8 value) override; - uInt8 internalRamGetValue(int addr) override; - string internalRamLabel(int addr) override; - // end of functions for Cartridge RAM tab - // Following constructors and assignment operators not supported CartridgeF4SCWidget() = delete; CartridgeF4SCWidget(const CartridgeF4SCWidget&) = delete; diff --git a/src/debugger/gui/CartF4Widget.cxx b/src/debugger/gui/CartF4Widget.cxx index 3d5848cd0..5069f029d 100644 --- a/src/debugger/gui/CartF4Widget.cxx +++ b/src/debugger/gui/CartF4Widget.cxx @@ -16,87 +16,24 @@ //============================================================================ #include "CartF4.hxx" -#include "PopUpWidget.hxx" #include "CartF4Widget.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeF4Widget::CartridgeF4Widget( GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, int x, int y, int w, int h, CartridgeF4& cart) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h), - myCart(cart) + : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart) { - uInt16 size = 8 * 4096; + initialize(); +} +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string CartridgeF4Widget::description() +{ ostringstream info; - info << "Standard F4 cartridge, eight 4K banks\n" - << "Startup bank = " << cart.startBank() << " or undetermined\n"; - // Eventually, we should query this from the debugger/disassembler - for(uInt32 i = 0, offset = 0xFFC, spot = 0xFF4; i < 8; ++i, offset += 0x1000) - { - uInt16 start = (cart.myImage[offset+1] << 8) | cart.myImage[offset]; - start -= start % 0x1000; - info << "Bank " << i << " @ $" << Common::Base::HEX4 << start << " - " - << "$" << (start + 0xFFF) << " (hotspot = $F" << (spot+i) << ")\n"; - } + info << "Standard F4 cartridge, eight 4K banks\n"; + info << CartridgeEnhancedWidget::description(); - int xpos = 2, - ypos = addBaseInformation(size, "Atari", info.str(), 15) + myLineHeight; - - VariantList items; - VarList::push_back(items, "0 ($FFF4)"); - VarList::push_back(items, "1 ($FFF5)"); - VarList::push_back(items, "2 ($FFF6)"); - VarList::push_back(items, "3 ($FFF7)"); - VarList::push_back(items, "4 ($FFF8)"); - VarList::push_back(items, "5 ($FFF9)"); - VarList::push_back(items, "6 ($FFFA)"); - VarList::push_back(items, "7 ($FFFB)"); - myBank = - new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("0 ($FFFx)"), - myLineHeight, items, "Set bank ", - 0, kBankChanged); - myBank->setTarget(this); - addFocusWidget(myBank); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF4Widget::loadConfig() -{ - Debugger& dbg = instance().debugger(); - CartDebug& cart = dbg.cartDebug(); - const CartState& state = static_cast(cart.getState()); - const CartState& oldstate = static_cast(cart.getOldState()); - - myBank->setSelectedIndex(myCart.getBank(), state.bank != oldstate.bank); - - CartDebugWidget::loadConfig(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF4Widget::handleCommand(CommandSender* sender, - int cmd, int data, int id) -{ - if(cmd == kBankChanged) - { - myCart.unlockBank(); - myCart.bank(myBank->getSelected()); - myCart.lockBank(); - invalidate(); - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeF4Widget::bankState() -{ - ostringstream& buf = buffer(); - - static constexpr std::array spot = { - "$FFF4", "$FFF5", "$FFF6", "$FFF7", "$FFF8", "$FFF9", "$FFFA", "$FFFB" - }; - buf << "Bank = " << std::dec << myCart.getBank() - << ", hotspot = " << spot[myCart.getBank()]; - - return buf.str(); + return info.str(); } diff --git a/src/debugger/gui/CartF4Widget.hxx b/src/debugger/gui/CartF4Widget.hxx index 7487a1b4b..d444a5956 100644 --- a/src/debugger/gui/CartF4Widget.hxx +++ b/src/debugger/gui/CartF4Widget.hxx @@ -19,11 +19,10 @@ #define CARTRIDGEF4_WIDGET_HXX class CartridgeF4; -class PopUpWidget; -#include "CartDebugWidget.hxx" +#include "CartEnhancedWidget.hxx" -class CartridgeF4Widget : public CartDebugWidget +class CartridgeF4Widget : public CartridgeEnhancedWidget { public: CartridgeF4Widget(GuiObject* boss, const GUI::Font& lfont, @@ -33,17 +32,11 @@ class CartridgeF4Widget : public CartDebugWidget virtual ~CartridgeF4Widget() = default; private: - CartridgeF4& myCart; - PopUpWidget* myBank{nullptr}; + string manufacturer() override { return "Atari"; } - enum { kBankChanged = 'bkCH' }; + string description() override; private: - void loadConfig() override; - void handleCommand(CommandSender* sender, int cmd, int data, int id) override; - - string bankState() override; - // Following constructors and assignment operators not supported CartridgeF4Widget() = delete; CartridgeF4Widget(const CartridgeF4Widget&) = delete; diff --git a/src/debugger/gui/CartF6SCWidget.cxx b/src/debugger/gui/CartF6SCWidget.cxx index 863ff2948..d2556799c 100644 --- a/src/debugger/gui/CartF6SCWidget.cxx +++ b/src/debugger/gui/CartF6SCWidget.cxx @@ -15,151 +15,25 @@ // this file, and for a DISCLAIMER OF ALL WARRANTIES. //============================================================================ -#include "Debugger.hxx" -#include "CartDebug.hxx" #include "CartF6SC.hxx" -#include "PopUpWidget.hxx" #include "CartF6SCWidget.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeF6SCWidget::CartridgeF6SCWidget( GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, int x, int y, int w, int h, CartridgeF6SC& cart) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h), - myCart(cart) + : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart) { - uInt16 size = 4 * 4096; + initialize(); +} +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string CartridgeF6SCWidget::description() +{ ostringstream info; - info << "Standard F6SC cartridge, four 4K banks\n" - << "128 bytes RAM @ $F000 - $F0FF\n" - << " $F080 - $F0FF (R), $F000 - $F07F (W)\n" - << "Startup bank = " << cart.startBank() << " or undetermined\n"; - // Eventually, we should query this from the debugger/disassembler - for(uInt32 i = 0, offset = 0xFFC, spot = 0xFF6; i < 4; ++i, offset += 0x1000) - { - uInt16 start = (cart.myImage[offset+1] << 8) | cart.myImage[offset]; - start -= start % 0x1000; - info << "Bank " << i << " @ $" << Common::Base::HEX4 << (start + 0x100) << " - " - << "$" << (start + 0xFFF) << " (hotspot = $F" << (spot+i) << ")\n"; - } + info << "Standard F6SC cartridge, four 4K banks\n"; + info << CartridgeEnhancedWidget::description(); - int xpos = 2, - ypos = addBaseInformation(size, "Atari", info.str()) + myLineHeight; - - VariantList items; - VarList::push_back(items, "0 ($FFF6)"); - VarList::push_back(items, "1 ($FFF7)"); - VarList::push_back(items, "2 ($FFF8)"); - VarList::push_back(items, "3 ($FFF9)"); - myBank = - new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("0 ($FFFx)"), - myLineHeight, items, "Set bank ", - 0, kBankChanged); - myBank->setTarget(this); - addFocusWidget(myBank); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF6SCWidget::saveOldState() -{ - myOldState.internalram.clear(); - - for(uInt32 i = 0; i < internalRamSize(); ++i) - myOldState.internalram.push_back(myCart.myRAM[i]); - - myOldState.bank = myCart.getBank(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF6SCWidget::loadConfig() -{ - myBank->setSelectedIndex(myCart.getBank(), myCart.getBank() != myOldState.bank); - - CartDebugWidget::loadConfig(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF6SCWidget::handleCommand(CommandSender* sender, - int cmd, int data, int id) -{ - if(cmd == kBankChanged) - { - myCart.unlockBank(); - myCart.bank(myBank->getSelected()); - myCart.lockBank(); - invalidate(); - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeF6SCWidget::bankState() -{ - ostringstream& buf = buffer(); - - static constexpr std::array spot = { "$FFF6", "$FFF7", "$FFF8", "$FFF9" }; - buf << "Bank = " << std::dec << myCart.getBank() - << ", hotspot = " << spot[myCart.getBank()]; - - return buf.str(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 CartridgeF6SCWidget::internalRamSize() -{ - return 128; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 CartridgeF6SCWidget::internalRamRPort(int start) -{ - return 0xF080 + start; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeF6SCWidget::internalRamDescription() -{ - ostringstream desc; - desc << "$F000 - $F07F used for Write Access\n" - << "$F080 - $F0FF used for Read Access"; - - return desc.str(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const ByteArray& CartridgeF6SCWidget::internalRamOld(int start, int count) -{ - myRamOld.clear(); - for(int i = 0; i < count; i++) - myRamOld.push_back(myOldState.internalram[start + i]); - return myRamOld; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const ByteArray& CartridgeF6SCWidget::internalRamCurrent(int start, int count) -{ - myRamCurrent.clear(); - for(int i = 0; i < count; i++) - myRamCurrent.push_back(myCart.myRAM[start + i]); - return myRamCurrent; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF6SCWidget::internalRamSetValue(int addr, uInt8 value) -{ - myCart.myRAM[addr] = value; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeF6SCWidget::internalRamGetValue(int addr) -{ - return myCart.myRAM[addr]; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeF6SCWidget::internalRamLabel(int addr) -{ - CartDebug& dbg = instance().debugger().cartDebug(); - return dbg.getLabel(addr + 0xF080, false); + return info.str(); } diff --git a/src/debugger/gui/CartF6SCWidget.hxx b/src/debugger/gui/CartF6SCWidget.hxx index deee0e836..1ed6378a7 100644 --- a/src/debugger/gui/CartF6SCWidget.hxx +++ b/src/debugger/gui/CartF6SCWidget.hxx @@ -19,11 +19,10 @@ #define CARTRIDGEF6SC_WIDGET_HXX class CartridgeF6SC; -class PopUpWidget; -#include "CartDebugWidget.hxx" +#include "CartEnhancedWidget.hxx" -class CartridgeF6SCWidget : public CartDebugWidget +class CartridgeF6SCWidget : public CartridgeEnhancedWidget { public: CartridgeF6SCWidget(GuiObject* boss, const GUI::Font& lfont, @@ -33,34 +32,11 @@ class CartridgeF6SCWidget : public CartDebugWidget virtual ~CartridgeF6SCWidget() = default; private: - struct CartState { - ByteArray internalram; - uInt16 bank{0}; - }; - CartridgeF6SC& myCart; - PopUpWidget* myBank{nullptr}; - CartState myOldState; + string manufacturer() override { return "Atari"; } - enum { kBankChanged = 'bkCH' }; + string description() override; private: - void saveOldState() override; - void loadConfig() override; - void handleCommand(CommandSender* sender, int cmd, int data, int id) override; - - string bankState() override; - - // start of functions for Cartridge RAM tab - uInt32 internalRamSize() override; - uInt32 internalRamRPort(int start) override; - string internalRamDescription() override; - const ByteArray& internalRamOld(int start, int count) override; - const ByteArray& internalRamCurrent(int start, int count) override; - void internalRamSetValue(int addr, uInt8 value) override; - uInt8 internalRamGetValue(int addr) override; - string internalRamLabel(int addr) override; - // end of functions for Cartridge RAM tab - // Following constructors and assignment operators not supported CartridgeF6SCWidget() = delete; CartridgeF6SCWidget(const CartridgeF6SCWidget&) = delete; diff --git a/src/debugger/gui/CartF6Widget.cxx b/src/debugger/gui/CartF6Widget.cxx index 114d20e18..66bcbc6a7 100644 --- a/src/debugger/gui/CartF6Widget.cxx +++ b/src/debugger/gui/CartF6Widget.cxx @@ -16,81 +16,24 @@ //============================================================================ #include "CartF6.hxx" -#include "PopUpWidget.hxx" #include "CartF6Widget.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeF6Widget::CartridgeF6Widget( GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, int x, int y, int w, int h, CartridgeF6& cart) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h), - myCart(cart) + : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart) { - uInt16 size = 4 * 4096; + initialize(); +} +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string CartridgeF6Widget::description() +{ ostringstream info; - info << "Standard F6 cartridge, four 4K banks\n" - << "Startup bank = " << cart.startBank() << " or undetermined\n"; - // Eventually, we should query this from the debugger/disassembler - for(uInt32 i = 0, offset = 0xFFC, spot = 0xFF6; i < 4; ++i, offset += 0x1000) - { - uInt16 start = (cart.myImage[offset+1] << 8) | cart.myImage[offset]; - start -= start % 0x1000; - info << "Bank " << i << " @ $" << Common::Base::HEX4 << start << " - " - << "$" << (start + 0xFFF) << " (hotspot = $F" << (spot+i) << ")\n"; - } + info << "Standard F6 cartridge, four 4K banks\n"; + info << CartridgeEnhancedWidget::description(); - int xpos = 2, - ypos = addBaseInformation(size, "Atari", info.str()) + myLineHeight; - - VariantList items; - VarList::push_back(items, "0 ($FFF6)"); - VarList::push_back(items, "1 ($FFF7)"); - VarList::push_back(items, "2 ($FFF8)"); - VarList::push_back(items, "3 ($FFF9)"); - myBank = - new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("0 ($FFFx) "), - myLineHeight, items, "Set bank ", - 0, kBankChanged); - myBank->setTarget(this); - addFocusWidget(myBank); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF6Widget::loadConfig() -{ - Debugger& dbg = instance().debugger(); - CartDebug& cart = dbg.cartDebug(); - const CartState& state = static_cast(cart.getState()); - const CartState& oldstate = static_cast(cart.getOldState()); - - myBank->setSelectedIndex(myCart.getBank(), state.bank != oldstate.bank); - - CartDebugWidget::loadConfig(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF6Widget::handleCommand(CommandSender* sender, - int cmd, int data, int id) -{ - if(cmd == kBankChanged) - { - myCart.unlockBank(); - myCart.bank(myBank->getSelected()); - myCart.lockBank(); - invalidate(); - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeF6Widget::bankState() -{ - ostringstream& buf = buffer(); - - static constexpr std::array spot = { "$FFF6", "$FFF7", "$FFF8", "$FFF9" }; - buf << "Bank = " << std::dec << myCart.getBank() - << ", hotspot = " << spot[myCart.getBank()]; - - return buf.str(); + return info.str(); } diff --git a/src/debugger/gui/CartF6Widget.hxx b/src/debugger/gui/CartF6Widget.hxx index 392eb6a0c..2d0eb3734 100644 --- a/src/debugger/gui/CartF6Widget.hxx +++ b/src/debugger/gui/CartF6Widget.hxx @@ -19,11 +19,10 @@ #define CARTRIDGEF6_WIDGET_HXX class CartridgeF6; -class PopUpWidget; -#include "CartDebugWidget.hxx" +#include "CartEnhancedWidget.hxx" -class CartridgeF6Widget : public CartDebugWidget +class CartridgeF6Widget : public CartridgeEnhancedWidget { public: CartridgeF6Widget(GuiObject* boss, const GUI::Font& lfont, @@ -33,17 +32,11 @@ class CartridgeF6Widget : public CartDebugWidget virtual ~CartridgeF6Widget() = default; private: - CartridgeF6& myCart; - PopUpWidget* myBank{nullptr}; + string manufacturer() override { return "Atari"; } - enum { kBankChanged = 'bkCH' }; + string description() override; private: - void loadConfig() override; - void handleCommand(CommandSender* sender, int cmd, int data, int id) override; - - string bankState() override; - // Following constructors and assignment operators not supported CartridgeF6Widget() = delete; CartridgeF6Widget(const CartridgeF6Widget&) = delete; diff --git a/src/debugger/gui/CartF8SCWidget.cxx b/src/debugger/gui/CartF8SCWidget.cxx index 1f432e224..baae2360a 100644 --- a/src/debugger/gui/CartF8SCWidget.cxx +++ b/src/debugger/gui/CartF8SCWidget.cxx @@ -15,149 +15,25 @@ // this file, and for a DISCLAIMER OF ALL WARRANTIES. //============================================================================ -#include "Debugger.hxx" -#include "CartDebug.hxx" #include "CartF8SC.hxx" -#include "PopUpWidget.hxx" #include "CartF8SCWidget.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeF8SCWidget::CartridgeF8SCWidget( GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, int x, int y, int w, int h, CartridgeF8SC& cart) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h), - myCart(cart) + : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart) { - uInt16 size = 8192; + initialize(); +} +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string CartridgeF8SCWidget::description() +{ ostringstream info; - info << "Standard F8SC cartridge, two 4K banks\n" - << "128 bytes RAM @ $F000 - $F0FF\n" - << " $F080 - $F0FF (R), $F000 - $F07F (W)\n" - << "Startup bank = " << cart.startBank() << " or undetermined\n"; - // Eventually, we should query this from the debugger/disassembler - for(uInt32 i = 0, offset = 0xFFC, spot = 0xFF8; i < 2; ++i, offset += 0x1000) - { - uInt16 start = (cart.myImage[offset+1] << 8) | cart.myImage[offset]; - start -= start % 0x1000; - info << "Bank " << i << " @ $" << Common::Base::HEX4 << (start + 0x100) << " - " - << "$" << (start + 0xFFF) << " (hotspot = $F" << (spot+i) << ")\n"; - } + info << "Standard F8SC cartridge, two 4K banks\n"; + info << CartridgeEnhancedWidget::description(); - int xpos = 2, - ypos = addBaseInformation(size, "Atari", info.str()) + myLineHeight; - - VariantList items; - VarList::push_back(items, "0 ($FFF8)"); - VarList::push_back(items, "1 ($FFF9)"); - myBank = - new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("0 ($FFFx)"), - myLineHeight, items, "Set bank ", - 0, kBankChanged); - myBank->setTarget(this); - addFocusWidget(myBank); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF8SCWidget::saveOldState() -{ - myOldState.internalram.clear(); - - for(uInt32 i = 0; i < internalRamSize(); ++i) - myOldState.internalram.push_back(myCart.myRAM[i]); - - myOldState.bank = myCart.getBank(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF8SCWidget::loadConfig() -{ - myBank->setSelectedIndex(myCart.getBank(), myCart.getBank() != myOldState.bank); - - CartDebugWidget::loadConfig(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF8SCWidget::handleCommand(CommandSender* sender, - int cmd, int data, int id) -{ - if(cmd == kBankChanged) - { - myCart.unlockBank(); - myCart.bank(myBank->getSelected()); - myCart.lockBank(); - invalidate(); - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeF8SCWidget::bankState() -{ - ostringstream& buf = buffer(); - - static constexpr std::array spot = { "$FFF8", "$FFF9" }; - buf << "Bank = " << std::dec << myCart.getBank() - << ", hotspot = " << spot[myCart.getBank()]; - - return buf.str(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 CartridgeF8SCWidget::internalRamSize() -{ - return 128; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 CartridgeF8SCWidget::internalRamRPort(int start) -{ - return 0xF080 + start; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeF8SCWidget::internalRamDescription() -{ - ostringstream desc; - desc << "$F000 - $F07F used for Write Access\n" - << "$F080 - $F0FF used for Read Access"; - - return desc.str(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const ByteArray& CartridgeF8SCWidget::internalRamOld(int start, int count) -{ - myRamOld.clear(); - for(int i = 0; i < count; i++) - myRamOld.push_back(myOldState.internalram[start + i]); - return myRamOld; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const ByteArray& CartridgeF8SCWidget::internalRamCurrent(int start, int count) -{ - myRamCurrent.clear(); - for(int i = 0; i < count; i++) - myRamCurrent.push_back(myCart.myRAM[start + i]); - return myRamCurrent; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF8SCWidget::internalRamSetValue(int addr, uInt8 value) -{ - myCart.myRAM[addr] = value; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeF8SCWidget::internalRamGetValue(int addr) -{ - return myCart.myRAM[addr]; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeF8SCWidget::internalRamLabel(int addr) -{ - CartDebug& dbg = instance().debugger().cartDebug(); - return dbg.getLabel(addr + 0xF080, false); + return info.str(); } diff --git a/src/debugger/gui/CartF8SCWidget.hxx b/src/debugger/gui/CartF8SCWidget.hxx index 7bebef602..3f6199783 100644 --- a/src/debugger/gui/CartF8SCWidget.hxx +++ b/src/debugger/gui/CartF8SCWidget.hxx @@ -19,11 +19,10 @@ #define CARTRIDGEF8SC_WIDGET_HXX class CartridgeF8SC; -class PopUpWidget; -#include "CartDebugWidget.hxx" +#include "CartEnhancedWidget.hxx" -class CartridgeF8SCWidget : public CartDebugWidget +class CartridgeF8SCWidget : public CartridgeEnhancedWidget { public: CartridgeF8SCWidget(GuiObject* boss, const GUI::Font& lfont, @@ -33,35 +32,11 @@ class CartridgeF8SCWidget : public CartDebugWidget virtual ~CartridgeF8SCWidget() = default; private: - CartridgeF8SC& myCart; - PopUpWidget* myBank{nullptr}; + string manufacturer() override { return "Atari"; } - struct CartState { - ByteArray internalram; - uInt16 bank{0}; - }; - CartState myOldState; - - enum { kBankChanged = 'bkCH' }; + string description() override; private: - void saveOldState() override; - void loadConfig() override; - void handleCommand(CommandSender* sender, int cmd, int data, int id) override; - - string bankState() override; - - // start of functions for Cartridge RAM tab - uInt32 internalRamSize() override; - uInt32 internalRamRPort(int start) override; - string internalRamDescription() override; - const ByteArray& internalRamOld(int start, int count) override; - const ByteArray& internalRamCurrent(int start, int count) override; - void internalRamSetValue(int addr, uInt8 value) override; - uInt8 internalRamGetValue(int addr) override; - string internalRamLabel(int addr) override; - // end of functions for Cartridge RAM tab - // Following constructors and assignment operators not supported CartridgeF8SCWidget() = delete; CartridgeF8SCWidget(const CartridgeF8SCWidget&) = delete; diff --git a/src/debugger/gui/CartF8Widget.cxx b/src/debugger/gui/CartF8Widget.cxx index ca648b5f1..d3dd88c23 100644 --- a/src/debugger/gui/CartF8Widget.cxx +++ b/src/debugger/gui/CartF8Widget.cxx @@ -16,79 +16,24 @@ //============================================================================ #include "CartF8.hxx" -#include "PopUpWidget.hxx" #include "CartF8Widget.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeF8Widget::CartridgeF8Widget( GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, int x, int y, int w, int h, CartridgeF8& cart) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h), - myCart(cart) + : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart) { - uInt16 size = 2 * 4096; + initialize(); +} +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string CartridgeF8Widget::description() +{ ostringstream info; - info << "Standard F8 cartridge, two 4K banks\n" - << "Startup bank = " << cart.startBank() << " or undetermined\n"; - // Eventually, we should query this from the debugger/disassembler - for(uInt32 i = 0, offset = 0xFFC, spot = 0xFF8; i < 2; ++i, offset += 0x1000) - { - uInt16 start = (cart.myImage[offset+1] << 8) | cart.myImage[offset]; - start -= start % 0x1000; - info << "Bank " << i << " @ $" << Common::Base::HEX4 << start << " - " - << "$" << (start + 0xFFF) << " (hotspot = $F" << (spot+i) << ")\n"; - } + info << "Standard F8 cartridge, two 4K banks\n"; + info << CartridgeEnhancedWidget::description(); - int xpos = 2, - ypos = addBaseInformation(size, "Atari", info.str()) + myLineHeight; - - VariantList items; - VarList::push_back(items, "0 ($FFF8)"); - VarList::push_back(items, "1 ($FFF9)"); - myBank = - new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("0 ($FFFx)"), - myLineHeight, items, "Set bank ", - 0, kBankChanged); - myBank->setTarget(this); - addFocusWidget(myBank); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF8Widget::loadConfig() -{ - Debugger& dbg = instance().debugger(); - CartDebug& cart = dbg.cartDebug(); - const CartState& state = static_cast(cart.getState()); - const CartState& oldstate = static_cast(cart.getOldState()); - - myBank->setSelectedIndex(myCart.getBank(), state.bank != oldstate.bank); - - CartDebugWidget::loadConfig(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF8Widget::handleCommand(CommandSender* sender, - int cmd, int data, int id) -{ - if(cmd == kBankChanged) - { - myCart.unlockBank(); - myCart.bank(myBank->getSelected()); - myCart.lockBank(); - invalidate(); - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeF8Widget::bankState() -{ - ostringstream& buf = buffer(); - - static constexpr std::array spot = { "$FFF8", "$FFF9" }; - buf << "Bank = " << std::dec << myCart.getBank() - << ", hotspot = " << spot[myCart.getBank()]; - - return buf.str(); + return info.str(); } diff --git a/src/debugger/gui/CartF8Widget.hxx b/src/debugger/gui/CartF8Widget.hxx index 507ad464b..a0577f250 100644 --- a/src/debugger/gui/CartF8Widget.hxx +++ b/src/debugger/gui/CartF8Widget.hxx @@ -21,9 +21,9 @@ class CartridgeF8; class PopUpWidget; -#include "CartDebugWidget.hxx" +#include "CartEnhancedWidget.hxx" -class CartridgeF8Widget : public CartDebugWidget +class CartridgeF8Widget : public CartridgeEnhancedWidget { public: CartridgeF8Widget(GuiObject* boss, const GUI::Font& lfont, @@ -33,17 +33,11 @@ class CartridgeF8Widget : public CartDebugWidget virtual ~CartridgeF8Widget() = default; private: - CartridgeF8& myCart; - PopUpWidget* myBank{nullptr}; + string manufacturer() override { return "Atari"; } - enum { kBankChanged = 'bkCH' }; + string description() override; private: - void loadConfig() override; - void handleCommand(CommandSender* sender, int cmd, int data, int id) override; - - string bankState() override; - // Following constructors and assignment operators not supported CartridgeF8Widget() = delete; CartridgeF8Widget(const CartridgeF8Widget&) = delete; diff --git a/src/debugger/gui/CartFA2Widget.cxx b/src/debugger/gui/CartFA2Widget.cxx index eb2bf4d07..e7dcba933 100644 --- a/src/debugger/gui/CartFA2Widget.cxx +++ b/src/debugger/gui/CartFA2Widget.cxx @@ -15,65 +15,26 @@ // this file, and for a DISCLAIMER OF ALL WARRANTIES. //============================================================================ -#include "Debugger.hxx" -#include "CartDebug.hxx" #include "CartFA2.hxx" -#include "PopUpWidget.hxx" #include "CartFA2Widget.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeFA2Widget::CartridgeFA2Widget( GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, int x, int y, int w, int h, CartridgeFA2& cart) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h), - myCart(cart) + : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart), + myCartFA2(cart) { - size_t size = cart.mySize; - - ostringstream info; - info << "Modified FA RAM+, six or seven 4K banks\n" - << "256 bytes RAM @ $F000 - $F1FF\n" - << " $F100 - $F1FF (R), $F000 - $F0FF (W)\n" - << "RAM can be loaded/saved to Harmony flash by accessing $FFF4\n" - << "Startup bank = " << cart.startBank() << " or undetermined\n"; - - // Eventually, we should query this from the debugger/disassembler - for(uInt32 i = 0, offset = 0xFFC, spot = 0xFF5; i < cart.bankCount(); - ++i, offset += 0x1000) - { - uInt16 start = (cart.myImage[offset+1] << 8) | cart.myImage[offset]; - start -= start % 0x1000; - info << "Bank " << i << " @ $" << Common::Base::HEX4 << (start + 0x200) << " - " - << "$" << (start + 0xFFF) << " (hotspot = $F" << (spot+i) << ")\n"; - } - int xpos = 2, - ypos = addBaseInformation(size, "Chris D. Walton (Star Castle 2600)", - info.str(), 15) + myLineHeight; + ypos = initialize(); - VariantList items; - VarList::push_back(items, "0 ($FFF5)"); - VarList::push_back(items, "1 ($FFF6)"); - VarList::push_back(items, "2 ($FFF7)"); - VarList::push_back(items, "3 ($FFF8)"); - VarList::push_back(items, "4 ($FFF9)"); - VarList::push_back(items, "5 ($FFFA)"); - if(cart.bankCount() == 7) - VarList::push_back(items, "6 ($FFFB)"); - - myBank = - new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("0 ($FFFx)"), - myLineHeight, items, "Set bank ", - 0, kBankChanged); - myBank->setTarget(this); - addFocusWidget(myBank); - ypos += myLineHeight + 20; + ypos += 12; const int bwidth = _font.getStringWidth("Erase") + 20; StaticTextWidget* t = new StaticTextWidget(boss, _font, xpos, ypos, - _font.getStringWidth("Harmony Flash "), - myFontHeight, "Harmony Flash ", TextAlign::Left); + _font.getStringWidth("Harmony flash memory "), + myFontHeight, "Harmony flash memory ", TextAlign::Left); xpos += t->getWidth() + 4; myFlashErase = @@ -98,22 +59,16 @@ CartridgeFA2Widget::CartridgeFA2Widget( } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeFA2Widget::saveOldState() +string CartridgeFA2Widget::description() { - myOldState.internalram.clear(); + ostringstream info; - for(uInt32 i = 0; i < internalRamSize(); ++i) - myOldState.internalram.push_back(myCart.myRAM[i]); + info << "Modified FA RAM+, six or seven 4K banks\n"; + info << "RAM+ can be loaded/saved to Harmony flash memory by accessing $" + << Common::Base::HEX4 << 0xFFF4 << "\n"; + info << CartridgeEnhancedWidget::description(); - myOldState.bank = myCart.getBank(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeFA2Widget::loadConfig() -{ - myBank->setSelectedIndex(myCart.getBank(), myCart.getBank() != myOldState.bank); - - CartDebugWidget::loadConfig(); + return info.str(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -122,99 +77,19 @@ void CartridgeFA2Widget::handleCommand(CommandSender* sender, { switch(cmd) { - case kBankChanged: - myCart.unlockBank(); - myCart.bank(myBank->getSelected()); - myCart.lockBank(); - invalidate(); - break; - case kFlashErase: - myCart.flash(0); + myCartFA2.flash(0); break; case kFlashLoad: - myCart.flash(1); + myCartFA2.flash(1); break; case kFlashSave: - myCart.flash(2); + myCartFA2.flash(2); break; default: - break; + CartridgeEnhancedWidget::handleCommand(sender, cmd, data, id); } } - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeFA2Widget::bankState() -{ - ostringstream& buf = buffer(); - - static constexpr std::array spot = { - "$FFF5", "$FFF6", "$FFF7", "$FFF8", "$FFF9", "$FFFA", "$FFFB" - }; - buf << "Bank = " << std::dec << myCart.getBank() - << ", hotspot = " << spot[myCart.getBank()]; - - return buf.str(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 CartridgeFA2Widget::internalRamSize() -{ - return 256; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 CartridgeFA2Widget::internalRamRPort(int start) -{ - return 0xF100 + start; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeFA2Widget::internalRamDescription() -{ - ostringstream desc; - desc << "$F000 - $F0FF used for Write Access\n" - << "$F100 - $F1FF used for Read Access"; - - return desc.str(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const ByteArray& CartridgeFA2Widget::internalRamOld(int start, int count) -{ - myRamOld.clear(); - for(int i = 0; i < count; i++) - myRamOld.push_back(myOldState.internalram[start + i]); - return myRamOld; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const ByteArray& CartridgeFA2Widget::internalRamCurrent(int start, int count) -{ - myRamCurrent.clear(); - for(int i = 0; i < count; i++) - myRamCurrent.push_back(myCart.myRAM[start + i]); - return myRamCurrent; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeFA2Widget::internalRamSetValue(int addr, uInt8 value) -{ - myCart.myRAM[addr] = value; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeFA2Widget::internalRamGetValue(int addr) -{ - return myCart.myRAM[addr]; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeFA2Widget::internalRamLabel(int addr) -{ - CartDebug& dbg = instance().debugger().cartDebug(); - return dbg.getLabel(addr + 0xF100, false); -} diff --git a/src/debugger/gui/CartFA2Widget.hxx b/src/debugger/gui/CartFA2Widget.hxx index 0b5fe3fb4..06865c495 100644 --- a/src/debugger/gui/CartFA2Widget.hxx +++ b/src/debugger/gui/CartFA2Widget.hxx @@ -20,11 +20,10 @@ class CartridgeFA2; class ButtonWidget; -class PopUpWidget; -#include "CartDebugWidget.hxx" +#include "CartEnhancedWidget.hxx" -class CartridgeFA2Widget : public CartDebugWidget +class CartridgeFA2Widget : public CartridgeEnhancedWidget { public: CartridgeFA2Widget(GuiObject* boss, const GUI::Font& lfont, @@ -34,41 +33,25 @@ class CartridgeFA2Widget : public CartDebugWidget virtual ~CartridgeFA2Widget() = default; private: - CartridgeFA2& myCart; - PopUpWidget* myBank{nullptr}; + CartridgeFA2& myCartFA2; + ButtonWidget *myFlashErase{nullptr}, *myFlashLoad{nullptr}, *myFlashSave{nullptr}; - struct CartState { - ByteArray internalram; - uInt16 bank{0}; - }; - CartState myOldState; - enum { - kBankChanged = 'bkCH', kFlashErase = 'flER', kFlashLoad = 'flLD', kFlashSave = 'flSV' }; private: - void saveOldState() override; - void loadConfig() override; + string manufacturer() override { return "Chris D. Walton (Star Castle 2600 Arcade)"; } + + string description() override; + + private: void handleCommand(CommandSender* sender, int cmd, int data, int id) override; - string bankState() override; - - // start of functions for Cartridge RAM tab - uInt32 internalRamSize() override; - uInt32 internalRamRPort(int start) override; - string internalRamDescription() override; - const ByteArray& internalRamOld(int start, int count) override; - const ByteArray& internalRamCurrent(int start, int count) override; - void internalRamSetValue(int addr, uInt8 value) override; - uInt8 internalRamGetValue(int addr) override; - string internalRamLabel(int addr) override; - // end of functions for Cartridge RAM tab - + private: // Following constructors and assignment operators not supported CartridgeFA2Widget() = delete; CartridgeFA2Widget(const CartridgeFA2Widget&) = delete; diff --git a/src/debugger/gui/CartFAWidget.cxx b/src/debugger/gui/CartFAWidget.cxx index e32a3eec0..aea8a2061 100644 --- a/src/debugger/gui/CartFAWidget.cxx +++ b/src/debugger/gui/CartFAWidget.cxx @@ -15,150 +15,25 @@ // this file, and for a DISCLAIMER OF ALL WARRANTIES. //============================================================================ -#include "Debugger.hxx" -#include "CartDebug.hxx" #include "CartFA.hxx" -#include "PopUpWidget.hxx" #include "CartFAWidget.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeFAWidget::CartridgeFAWidget( GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, int x, int y, int w, int h, CartridgeFA& cart) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h), - myCart(cart) + : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart) { - uInt16 size = 3 * 4096; + initialize(); +} +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string CartridgeFAWidget::description() +{ ostringstream info; - info << "CBS RAM+ FA cartridge, three 4K banks\n" - << "256 bytes RAM @ $F000 - $F1FF\n" - << " $F100 - $F1FF (R), $F000 - $F0FF (W)\n" - << "Startup bank = " << cart.startBank() << " or undetermined\n"; - // Eventually, we should query this from the debugger/disassembler - for(uInt32 i = 0, offset = 0xFFC, spot = 0xFF8; i < 3; ++i, offset += 0x1000) - { - uInt16 start = (cart.myImage[offset+1] << 8) | cart.myImage[offset]; - start -= start % 0x1000; - info << "Bank " << i << " @ $" << Common::Base::HEX4 << (start + 0x200) << " - " - << "$" << (start + 0xFFF) << " (hotspot = $F" << (spot+i) << ")\n"; - } + info << "CBS RAM+ FA cartridge, three 4K banks\n"; + info << CartridgeEnhancedWidget::description(); - int xpos = 2, - ypos = addBaseInformation(size, "CBS", info.str()) + myLineHeight; - - VariantList items; - VarList::push_back(items, "0 ($FFF8)"); - VarList::push_back(items, "1 ($FFF9)"); - VarList::push_back(items, "2 ($FFFA)"); - myBank = - new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("0 ($FFFx)"), - myLineHeight, items, "Set bank ", - 0, kBankChanged); - myBank->setTarget(this); - addFocusWidget(myBank); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeFAWidget::saveOldState() -{ - myOldState.internalram.clear(); - - for(uInt32 i = 0; i < internalRamSize(); ++i) - myOldState.internalram.push_back(myCart.myRAM[i]); - - myOldState.bank = myCart.getBank(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeFAWidget::loadConfig() -{ - myBank->setSelectedIndex(myCart.getBank(), myCart.getBank() != myOldState.bank); - - CartDebugWidget::loadConfig(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeFAWidget::handleCommand(CommandSender* sender, - int cmd, int data, int id) -{ - if(cmd == kBankChanged) - { - myCart.unlockBank(); - myCart.bank(myBank->getSelected()); - myCart.lockBank(); - invalidate(); - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeFAWidget::bankState() -{ - ostringstream& buf = buffer(); - - static constexpr std::array spot = { "$FFF8", "$FFF9", "$FFFA" }; - buf << "Bank = " << std::dec << myCart.getBank() - << ", hotspot = " << spot[myCart.getBank()]; - - return buf.str(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 CartridgeFAWidget::internalRamSize() -{ - return 256; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 CartridgeFAWidget::internalRamRPort(int start) -{ - return 0xF100 + start; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeFAWidget::internalRamDescription() -{ - ostringstream desc; - desc << "$F000 - $F0FF used for Write Access\n" - << "$F100 - $F1FF used for Read Access"; - - return desc.str(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const ByteArray& CartridgeFAWidget::internalRamOld(int start, int count) -{ - myRamOld.clear(); - for(int i = 0; i < count; i++) - myRamOld.push_back(myOldState.internalram[start + i]); - return myRamOld; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const ByteArray& CartridgeFAWidget::internalRamCurrent(int start, int count) -{ - myRamCurrent.clear(); - for(int i = 0; i < count; i++) - myRamCurrent.push_back(myCart.myRAM[start + i]); - return myRamCurrent; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeFAWidget::internalRamSetValue(int addr, uInt8 value) -{ - myCart.myRAM[addr] = value; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeFAWidget::internalRamGetValue(int addr) -{ - return myCart.myRAM[addr]; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeFAWidget::internalRamLabel(int addr) -{ - CartDebug& dbg = instance().debugger().cartDebug(); - return dbg.getLabel(addr + 0xF100, false); + return info.str(); } diff --git a/src/debugger/gui/CartFAWidget.hxx b/src/debugger/gui/CartFAWidget.hxx index e4e1da416..2d7311a71 100644 --- a/src/debugger/gui/CartFAWidget.hxx +++ b/src/debugger/gui/CartFAWidget.hxx @@ -19,11 +19,10 @@ #define CARTRIDGEFA_WIDGET_HXX class CartridgeFA; -class PopUpWidget; -#include "CartDebugWidget.hxx" +#include "CartEnhancedWidget.hxx" -class CartridgeFAWidget : public CartDebugWidget +class CartridgeFAWidget : public CartridgeEnhancedWidget { public: CartridgeFAWidget(GuiObject* boss, const GUI::Font& lfont, @@ -33,35 +32,11 @@ class CartridgeFAWidget : public CartDebugWidget virtual ~CartridgeFAWidget() = default; private: - CartridgeFA& myCart; - PopUpWidget* myBank{nullptr}; + string manufacturer() override { return "CBS"; } - struct CartState { - ByteArray internalram; - uInt16 bank{0}; - }; - CartState myOldState; - - enum { kBankChanged = 'bkCH' }; + string description() override; private: - void saveOldState() override; - void loadConfig() override; - void handleCommand(CommandSender* sender, int cmd, int data, int id) override; - - string bankState() override; - - // start of functions for Cartridge RAM tab - uInt32 internalRamSize() override; - uInt32 internalRamRPort(int start) override; - string internalRamDescription() override; - const ByteArray& internalRamOld(int start, int count) override; - const ByteArray& internalRamCurrent(int start, int count) override; - void internalRamSetValue(int addr, uInt8 value) override; - uInt8 internalRamGetValue(int addr) override; - string internalRamLabel(int addr) override; - // end of functions for Cartridge RAM tab - // Following constructors and assignment operators not supported CartridgeFAWidget() = delete; CartridgeFAWidget(const CartridgeFAWidget&) = delete; diff --git a/src/debugger/gui/CartFCWidget.cxx b/src/debugger/gui/CartFCWidget.cxx index ce1bf9403..d206ec694 100644 --- a/src/debugger/gui/CartFCWidget.cxx +++ b/src/debugger/gui/CartFCWidget.cxx @@ -16,80 +16,44 @@ //============================================================================ #include "CartFC.hxx" -#include "PopUpWidget.hxx" #include "CartFCWidget.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeFCWidget::CartridgeFCWidget( GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, int x, int y, int w, int h, CartridgeFC& cart) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h), - myCart(cart) + : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart) { - uInt16 size = cart.bankCount() * 4096; + initialize(); +} +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string CartridgeFCWidget::description() +{ ostringstream info; - info << "FC cartridge, up to eight 4K banks\n" - << "Startup bank = " << cart.startBank() << " or undetermined\n"; + uInt16 hotspot = myCart.hotspot() | ADDR_BASE; - // Eventually, we should query this from the debugger/disassembler + info << "FC cartridge, up to eight 4K banks\n"; info << "Bank selected by hotspots\n" - << " $FFF8 (defines low 2 bits)\n" - << " $FFF9 (defines high bits)\n" - << " $FFFC (triggers bank switch)"; + << " $" << Common::Base::HEX4 << hotspot << " (defines low 2 bits)\n" + << " $" << Common::Base::HEX4 << (hotspot + 1) << " (defines high bits)\n" + << " $" << Common::Base::HEX4 << (hotspot + 4) << " (triggers bank switch)\n"; - int xpos = 2, - ypos = addBaseInformation(size, "Amiga Corp.", info.str()) + myLineHeight; + info << CartridgeEnhancedWidget::description(); - VariantList items; - for (uInt16 i = 0; i < cart.bankCount(); ++i) - VarList::push_back(items, Variant(i).toString() + - " ($FFF8 = " + Variant(i & 0b11).toString() + - "/$FFF9 = " + Variant(i >> 2).toString() +")"); - - myBank = new PopUpWidget(boss, _font, xpos, ypos - 2, - _font.getStringWidth("7 ($FFF8 = 3/$FFF9 = 1)"), - myLineHeight, items, "Set bank ", - 0, kBankChanged); - myBank->setTarget(this); - addFocusWidget(myBank); + return info.str(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeFCWidget::loadConfig() +string CartridgeFCWidget::hotspotStr(int bank, int, bool prefix) { - Debugger& dbg = instance().debugger(); - CartDebug& cart = dbg.cartDebug(); - const CartState& state = static_cast(cart.getState()); - const CartState& oldstate = static_cast(cart.getOldState()); + ostringstream info; + uInt16 hotspot = myCart.hotspot() | ADDR_BASE; - myBank->setSelectedIndex(myCart.getBank(), state.bank != oldstate.bank); + info << "(" << (prefix ? "hotspots " : ""); + info << "$" << Common::Base::HEX4 << hotspot << " = " << (bank & 0b11); + info << ", $" << Common::Base::HEX4 << (hotspot + 1) << " = " << (bank >> 2); + info << ")"; - CartDebugWidget::loadConfig(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeFCWidget::handleCommand(CommandSender* sender, - int cmd, int data, int id) -{ - if (cmd == kBankChanged) - { - myCart.unlockBank(); - myCart.bank(myBank->getSelected()); - myCart.lockBank(); - invalidate(); - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeFCWidget::bankState() -{ - ostringstream& buf = buffer(); - uInt16 bank = myCart.getBank(); - - buf << "Bank = #" << std::dec << bank - << ", hotspots $FFF8 = " << (bank & 0b11) - << "/$FF99 = " << (bank >> 2); - - return buf.str(); + return info.str(); } diff --git a/src/debugger/gui/CartFCWidget.hxx b/src/debugger/gui/CartFCWidget.hxx index 0e74246ed..f6d56af71 100644 --- a/src/debugger/gui/CartFCWidget.hxx +++ b/src/debugger/gui/CartFCWidget.hxx @@ -19,11 +19,10 @@ #define CARTRIDGEFC_WIDGET_HXX class CartridgeFC; -class PopUpWidget; -#include "CartDebugWidget.hxx" +#include "CartEnhancedWidget.hxx" -class CartridgeFCWidget : public CartDebugWidget +class CartridgeFCWidget : public CartridgeEnhancedWidget { public: CartridgeFCWidget(GuiObject* boss, const GUI::Font& lfont, @@ -33,17 +32,13 @@ class CartridgeFCWidget : public CartDebugWidget virtual ~CartridgeFCWidget() = default; private: - CartridgeFC& myCart; - PopUpWidget* myBank{nullptr}; + string manufacturer() override { return "Amiga Corp."; } - enum { kBankChanged = 'bkCH' }; + string description() override; + + string hotspotStr(int bank, int seg = 0, bool prefix = false) override; private: - void loadConfig() override; - void handleCommand(CommandSender* sender, int cmd, int data, int id) override; - - string bankState() override; - // Following constructors and assignment operators not supported CartridgeFCWidget() = delete; CartridgeFCWidget(const CartridgeFCWidget&) = delete; diff --git a/src/debugger/gui/CartFEWidget.cxx b/src/debugger/gui/CartFEWidget.cxx index afccb359f..ce7267e6e 100644 --- a/src/debugger/gui/CartFEWidget.cxx +++ b/src/debugger/gui/CartFEWidget.cxx @@ -16,72 +16,36 @@ //============================================================================ #include "CartFE.hxx" -#include "PopUpWidget.hxx" #include "CartFEWidget.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeFEWidget::CartridgeFEWidget( GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, int x, int y, int w, int h, CartridgeFE& cart) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h), - myCart(cart) + : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart) { - string info = - "FE cartridge, two 4K banks\n" - "Monitors access to hotspot $01FE, and uses " - "upper 3 bits of databus for bank number:\n" - "Bank 0 @ $F000 - $FFFF (DATA = 111, D5 = 1)\n" - "Bank 1 @ $D000 - $DFFF (DATA = 110, D5 = 0)\n"; - - int xpos = 2, - ypos = addBaseInformation(2 * 4096, "Activision", info) + myLineHeight; - - VariantList items; - VarList::push_back(items, "0 ($01FE, D5=1)"); - VarList::push_back(items, "1 ($01FE, D5=0)"); - myBank = - new PopUpWidget(boss, _font, xpos, ypos-2, - _font.getStringWidth("0 ($01FE, D5=1)"), - myLineHeight, items, "Set bank ", - 0, kBankChanged); - myBank->setTarget(this); - addFocusWidget(myBank); + initialize(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeFEWidget::loadConfig() +string CartridgeFEWidget::description() { - Debugger& dbg = instance().debugger(); - CartDebug& cart = dbg.cartDebug(); - const CartState& state = static_cast(cart.getState()); - const CartState& oldstate = static_cast(cart.getOldState()); + ostringstream info; - myBank->setSelectedIndex(myCart.getBank(), state.bank != oldstate.bank); + info << "FE cartridge, two 4K banks\n" + << "Monitors access to hotspot $01FE, and uses " + << "upper 3 bits of databus for bank number:\n"; + info << CartridgeEnhancedWidget::description(); - CartDebugWidget::loadConfig(); + return info.str(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeFEWidget::handleCommand(CommandSender* sender, - int cmd, int data, int id) +string CartridgeFEWidget::hotspotStr(int bank, int, bool) { - if(cmd == kBankChanged) - { - myCart.unlockBank(); - myCart.bank(myBank->getSelected()); - myCart.lockBank(); - invalidate(); - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeFEWidget::bankState() -{ - ostringstream& buf = buffer(); - - static constexpr std::array range = { "$F000", "$D000" }; - buf << "Bank = " << std::dec << myCart.getBank() - << ", address range = " << range[myCart.getBank()]; - - return buf.str(); + ostringstream info; + + info << "(DATA = 11" << !bank << ", D5 = " << !bank << ")"; + + return info.str(); } diff --git a/src/debugger/gui/CartFEWidget.hxx b/src/debugger/gui/CartFEWidget.hxx index a9f37190c..468fcc144 100644 --- a/src/debugger/gui/CartFEWidget.hxx +++ b/src/debugger/gui/CartFEWidget.hxx @@ -19,11 +19,10 @@ #define CARTRIDGEFE_WIDGET_HXX class CartridgeFE; -class PopUpWidget; -#include "CartDebugWidget.hxx" +#include "CartEnhancedWidget.hxx" -class CartridgeFEWidget : public CartDebugWidget +class CartridgeFEWidget : public CartridgeEnhancedWidget { public: CartridgeFEWidget(GuiObject* boss, const GUI::Font& lfont, @@ -33,18 +32,13 @@ class CartridgeFEWidget : public CartDebugWidget virtual ~CartridgeFEWidget() = default; private: - CartridgeFE& myCart; - PopUpWidget* myBank{nullptr}; + string manufacturer() override { return "Activision"; } - enum { kBankChanged = 'bkCH' }; + string description() override; + + string hotspotStr(int bank, int, bool) override; private: - // No implementation for non-bankswitched ROMs - void loadConfig() override; - void handleCommand(CommandSender* sender, int cmd, int data, int id) override; - - string bankState() override; - // Following constructors and assignment operators not supported CartridgeFEWidget() = delete; CartridgeFEWidget(const CartridgeFEWidget&) = delete; diff --git a/src/debugger/gui/CartMDMWidget.cxx b/src/debugger/gui/CartMDMWidget.cxx index 7b0f799e4..a3509cb34 100644 --- a/src/debugger/gui/CartMDMWidget.cxx +++ b/src/debugger/gui/CartMDMWidget.cxx @@ -17,44 +17,39 @@ #include "CartMDM.hxx" #include "PopUpWidget.hxx" -#include "Widget.hxx" #include "CartMDMWidget.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeMDMWidget::CartridgeMDMWidget( GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, int x, int y, int w, int h, CartridgeMDM& cart) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h), - myCart(cart) + : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart), + myCartMDM(cart) { - size_t size = myCart.mySize; + initialize(); +} +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string CartridgeMDMWidget::description() +{ ostringstream info; - info << "Menu Driven Megacart, containing up to 128 4K banks\n" - << "Startup bank = " << cart.startBank() << "\n" - << "\nBanks are selected by reading from $800 - $BFF, where the lower " - "byte determines the 4K bank to use."; - int xpos = 2, - ypos = addBaseInformation(size, "Edwin Blink", info.str(), 15) + myLineHeight; + info << "Menu Driven Megacart, " << myCart.romBankCount() << " 4K banks\n" + << "Banks are selected by reading from $800 - $" << Common::Base::HEX1 << 0xBFF + << ", where the lower byte determines the 4K bank to use.\n"; + info << CartridgeEnhancedWidget::description(); - VariantList items; - for(uInt32 i = 0x800; i < (0x800U + myCart.bankCount()); ++i) - { - info.str(""); - info << std::dec << (i & 0xFF) << " ($" << Common::Base::HEX4 << i << ")"; - VarList::push_back(items, info.str()); - } + return info.str(); +} - myBank = - new PopUpWidget(boss, _font, xpos, ypos, _font.getStringWidth("xxx ($0FFF)"), - myLineHeight, items, "Set bank ", - 0, kBankChanged); - myBank->setTarget(this); - addFocusWidget(myBank); +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeMDMWidget::bankSelect(int& ypos) +{ + CartridgeEnhancedWidget::bankSelect(ypos); + int xpos = myBankWidgets[0]->getRight() + 20; + ypos = myBankWidgets[0]->getTop(); - xpos += myBank->getWidth() + 30; - myBankDisabled = new CheckboxWidget(boss, _font, xpos, ypos + 1, + myBankDisabled = new CheckboxWidget(_boss, _font, xpos, ypos + 1, "Bankswitching is locked/disabled", kBankDisabled); myBankDisabled->setTarget(this); @@ -64,39 +59,21 @@ CartridgeMDMWidget::CartridgeMDMWidget( // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void CartridgeMDMWidget::loadConfig() { - myBank->setSelectedIndex(myCart.getBank()); - myBank->setEnabled(!myCart.myBankingDisabled); - myBankDisabled->setState(myCart.myBankingDisabled); + myBankWidgets[0]->setEnabled(!myCartMDM.myBankingDisabled); + myBankDisabled->setState(myCartMDM.myBankingDisabled); - CartDebugWidget::loadConfig(); + CartridgeEnhancedWidget::loadConfig(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void CartridgeMDMWidget::handleCommand(CommandSender* sender, int cmd, int data, int id) { - if(cmd == kBankChanged) + if(cmd == kBankDisabled) { - myCart.unlockBank(); - myCart.bank(myBank->getSelected()); - myCart.lockBank(); - invalidate(); - } - else if(cmd == kBankDisabled) - { - myCart.myBankingDisabled = myBankDisabled->getState(); - myBank->setEnabled(!myCart.myBankingDisabled); + myCartMDM.myBankingDisabled = myBankDisabled->getState(); + myBankWidgets[0]->setEnabled(!myCartMDM.myBankingDisabled); } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeMDMWidget::bankState() -{ - ostringstream& buf = buffer(); - - buf << "Bank = " << std::dec << myCart.getBank() - << ", hotspot = " << "$" << Common::Base::HEX4 - << (myCart.getBank()+0x800); - - return buf.str(); + else + CartridgeEnhancedWidget::handleCommand(sender, cmd, data, id); } diff --git a/src/debugger/gui/CartMDMWidget.hxx b/src/debugger/gui/CartMDMWidget.hxx index adf242b68..c7add24d7 100644 --- a/src/debugger/gui/CartMDMWidget.hxx +++ b/src/debugger/gui/CartMDMWidget.hxx @@ -20,11 +20,10 @@ class CartridgeMDM; class CheckboxWidget; -class PopUpWidget; -#include "CartDebugWidget.hxx" +#include "CartEnhancedWidget.hxx" -class CartridgeMDMWidget : public CartDebugWidget +class CartridgeMDMWidget : public CartridgeEnhancedWidget { public: CartridgeMDMWidget(GuiObject* boss, const GUI::Font& lfont, @@ -34,18 +33,24 @@ class CartridgeMDMWidget : public CartDebugWidget virtual ~CartridgeMDMWidget() = default; private: - CartridgeMDM& myCart; - PopUpWidget* myBank{nullptr}; + string manufacturer() override { return "Edwin Blink"; } + + string description() override; + + void bankSelect(int& ypos) override; + + CartridgeMDM& myCartMDM; CheckboxWidget* myBankDisabled{nullptr}; - enum { kBankChanged = 'bkCH', kBankDisabled = 'bkDI' }; + enum { + kBankDisabled = 'bkDI' + }; private: void loadConfig() override; void handleCommand(CommandSender* sender, int cmd, int data, int id) override; - string bankState() override; - + private: // Following constructors and assignment operators not supported CartridgeMDMWidget() = delete; CartridgeMDMWidget(const CartridgeMDMWidget&) = delete; diff --git a/src/debugger/gui/CartMNetworkWidget.cxx b/src/debugger/gui/CartMNetworkWidget.cxx index cdc1f9bce..e9cc68869 100644 --- a/src/debugger/gui/CartMNetworkWidget.cxx +++ b/src/debugger/gui/CartMNetworkWidget.cxx @@ -35,30 +35,30 @@ CartridgeMNetworkWidget::CartridgeMNetworkWidget( // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void CartridgeMNetworkWidget::initialize(GuiObject* boss, CartridgeMNetwork& cart, ostringstream& info) { - uInt32 size = cart.bankCount() * cart.BANK_SIZE; + uInt32 size = cart.romBankCount() * cart.BANK_SIZE; int xpos = 2, ypos = addBaseInformation(size, "M-Network", info.str(), 15) + myLineHeight; VariantList items0, items1; - for(int i = 0; i < cart.bankCount(); ++i) + for(int i = 0; i < cart.romBankCount(); ++i) VarList::push_back(items0, getSpotLower(i)); for(int i = 0; i < 4; ++i) VarList::push_back(items1, getSpotUpper(i)); - const int lwidth = _font.getStringWidth("Set slice for upper 256B "), - fwidth = _font.getStringWidth("3 - RAM ($FFEB)"); + const int lwidth = _font.getStringWidth("Set bank for upper 256B segment "), + fwidth = _font.getStringWidth("#3 - RAM ($FFEB)"); myLower2K = new PopUpWidget(boss, _font, xpos, ypos - 2, fwidth, myLineHeight, items0, - "Set slice for lower 2K ", lwidth, kLowerChanged); + "Set bank for lower 2K segment", lwidth, kLowerChanged); myLower2K->setTarget(this); addFocusWidget(myLower2K); ypos += myLower2K->getHeight() + 4; myUpper256B = new PopUpWidget(boss, _font, xpos, ypos - 2, fwidth, myLineHeight, items1, - "Set slice for upper 256B ", lwidth, kUpperChanged); + "Set bank for upper 256B segment ", lwidth, kUpperChanged); myUpper256B->setTarget(this); addFocusWidget(myUpper256B); } @@ -71,14 +71,14 @@ void CartridgeMNetworkWidget::saveOldState() for(uInt32 i = 0; i < internalRamSize(); ++i) myOldState.internalram.push_back(myCart.myRAM[i]); - myOldState.lowerBank = myCart.myCurrentSlice[0]; + myOldState.lowerBank = myCart.myCurrentBank[0]; myOldState.upperBank = myCart.myCurrentRAM; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void CartridgeMNetworkWidget::loadConfig() { - myLower2K->setSelectedIndex(myCart.myCurrentSlice[0], myCart.myCurrentSlice[0] != myOldState.lowerBank); + myLower2K->setSelectedIndex(myCart.myCurrentBank[0], myCart.myCurrentBank[0] != myOldState.lowerBank); myUpper256B->setSelectedIndex(myCart.myCurrentRAM, myCart.myCurrentRAM != myOldState.upperBank); CartDebugWidget::loadConfig(); @@ -111,8 +111,8 @@ string CartridgeMNetworkWidget::bankState() { ostringstream& buf = buffer(); - buf << "Slices: " << std::dec - << getSpotLower(myCart.myCurrentSlice[0]) << " / " + buf << "Segments: " << std::dec + << getSpotLower(myCart.myCurrentBank[0]) << " / " << getSpotUpper(myCart.myCurrentRAM); return buf.str(); @@ -135,11 +135,11 @@ string CartridgeMNetworkWidget::internalRamDescription() { ostringstream desc; desc << "First 1K accessible via:\n" - << " $F000 - $F3FF used for Write Access\n" - << " $F400 - $F7FF used for Read Access\n" - << "256K of second 1K accessible via:\n" - << " $F800 - $F8FF used for Write Access\n" - << " $F900 - $F9FF used for Read Access"; + << " $F000 - $F3FF used for write access\n" + << " $F400 - $F7FF used for read access\n" + << "256 bytes of second 1K accessible via:\n" + << " $F800 - $F8FF used for write access\n" + << " $F900 - $F9FF used for read access"; return desc.str(); } diff --git a/src/debugger/gui/CartRamWidget.cxx b/src/debugger/gui/CartRamWidget.cxx index be2eb984b..84cc8b9ab 100644 --- a/src/debugger/gui/CartRamWidget.cxx +++ b/src/debugger/gui/CartRamWidget.cxx @@ -47,7 +47,7 @@ CartRamWidget::CartRamWidget( int xpos = 2, ypos = 8; // Add RAM size - new StaticTextWidget(_boss, _font, xpos, ypos + 1, "RAM Size "); + new StaticTextWidget(_boss, _font, xpos, ypos + 1, "RAM size "); uInt32 ramsize = cartDebug.internalRamSize(); buf << ramsize << " bytes"; @@ -65,13 +65,14 @@ CartRamWidget::CartRamWidget( StringParser bs(desc, (fwidth - ScrollBarWidget::scrollBarWidth(_font)) / myFontWidth); const StringList& sl = bs.stringList(); uInt32 lines = uInt32(sl.size()); - if(lines < 3) lines = 3; + if(lines < 2) lines = 2; if(lines > maxlines) lines = maxlines; new StaticTextWidget(_boss, _font, xpos, ypos + 1, "Description "); myDesc = new StringListWidget(boss, nfont, xpos+lwidth, ypos - 1, fwidth, lines * myLineHeight, false); myDesc->setEditable(false); + myDesc->setEnabled(false); myDesc->setList(sl); addFocusWidget(myDesc); diff --git a/src/debugger/gui/CartSBWidget.cxx b/src/debugger/gui/CartSBWidget.cxx index a7bf7e5b2..b564781bc 100644 --- a/src/debugger/gui/CartSBWidget.cxx +++ b/src/debugger/gui/CartSBWidget.cxx @@ -16,85 +16,27 @@ //============================================================================ #include "CartSB.hxx" -#include "PopUpWidget.hxx" #include "CartSBWidget.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeSBWidget::CartridgeSBWidget( GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, int x, int y, int w, int h, CartridgeSB& cart) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h), - myCart(cart) + : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart) { - size_t size = myCart.mySize; - - VariantList items; - ostringstream info, bank; - info << "SB SUPERbanking, 32 or 64 4K banks\n" - << "Hotspots are from $800 to $" - << Common::Base::HEX2 << (0x800 + myCart.bankCount() - 1) << ", including\n" - << "mirrors ($900, $A00, $B00, ...)\n" - << "Startup bank = " << std::dec << cart.startBank() << "\n"; - - // Eventually, we should query this from the debugger/disassembler - for(uInt32 i = 0, offset = 0xFFC, spot = 0x800; i < myCart.bankCount(); - ++i, offset += 0x1000, ++spot) - { - uInt16 start = (cart.myImage[offset+1] << 8) | cart.myImage[offset]; - start -= start % 0x1000; - info << "Bank " << std::dec << i << " @ $" << Common::Base::HEX4 << start << " - " - << "$" << (start + 0xFFF) << " (hotspot = $" << spot << ")\n"; - - bank << std::dec << std::setw(2) << std::setfill(' ') << i << " ($" - << Common::Base::HEX2 << spot << ")"; - VarList::push_back(items, bank.str()); - bank.str(""); - } - - int xpos = 2, - ypos = addBaseInformation(size, "Fred X. Quimby", info.str()) + myLineHeight; - - myBank = - new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("XX ($800)"), - myLineHeight, items, "Set bank ", - 0, kBankChanged); - myBank->setTarget(this); - addFocusWidget(myBank); + initialize(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeSBWidget::loadConfig() +string CartridgeSBWidget::description() { - Debugger& dbg = instance().debugger(); - CartDebug& cart = dbg.cartDebug(); - const CartState& state = static_cast(cart.getState()); - const CartState& oldstate = static_cast(cart.getOldState()); + ostringstream info; - myBank->setSelectedIndex(myCart.getBank(), state.bank != oldstate.bank); + info << "SB SUPERbanking, " << myCart.romBankCount() << " 4K banks\n" + << "Hotspots are from $800 to $" + << Common::Base::HEX2 << (0x800 + myCart.romBankCount() - 1) << ", including\n" + << "mirrors ($900, $" << 0xA00 << ", $" << 0xB00 << ", ...)\n"; + info << CartridgeEnhancedWidget::description(); - CartDebugWidget::loadConfig(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeSBWidget::handleCommand(CommandSender* sender, - int cmd, int data, int id) -{ - if(cmd == kBankChanged) - { - myCart.unlockBank(); - myCart.bank(myBank->getSelected()); - myCart.lockBank(); - invalidate(); - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeSBWidget::bankState() -{ - ostringstream& buf = buffer(); - - buf << "Bank = " << std::dec << myCart.getBank() - << ", hotspot = $" << Common::Base::HEX2 << (myCart.getBank() + 0x800); - - return buf.str(); + return info.str(); } diff --git a/src/debugger/gui/CartSBWidget.hxx b/src/debugger/gui/CartSBWidget.hxx index 5819dec02..91cc90ec1 100644 --- a/src/debugger/gui/CartSBWidget.hxx +++ b/src/debugger/gui/CartSBWidget.hxx @@ -19,11 +19,10 @@ #define CARTRIDGESB_WIDGET_HXX class CartridgeSB; -class PopUpWidget; -#include "CartDebugWidget.hxx" +#include "CartEnhancedWidget.hxx" -class CartridgeSBWidget : public CartDebugWidget +class CartridgeSBWidget : public CartridgeEnhancedWidget { public: CartridgeSBWidget(GuiObject* boss, const GUI::Font& lfont, @@ -33,17 +32,11 @@ class CartridgeSBWidget : public CartDebugWidget virtual ~CartridgeSBWidget() = default; private: - CartridgeSB& myCart; - PopUpWidget* myBank{nullptr}; + string manufacturer() override { return "Fred X. Quimby"; } - enum { kBankChanged = 'bkCH' }; + string description() override; private: - void loadConfig() override; - void handleCommand(CommandSender* sender, int cmd, int data, int id) override; - - string bankState() override; - // Following constructors and assignment operators not supported CartridgeSBWidget() = delete; CartridgeSBWidget(const CartridgeSBWidget&) = delete; diff --git a/src/debugger/gui/CartUAWidget.cxx b/src/debugger/gui/CartUAWidget.cxx index 7417d8b44..f6ae16bc7 100644 --- a/src/debugger/gui/CartUAWidget.cxx +++ b/src/debugger/gui/CartUAWidget.cxx @@ -16,89 +16,39 @@ //============================================================================ #include "CartUA.hxx" -#include "PopUpWidget.hxx" #include "CartUAWidget.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeUAWidget::CartridgeUAWidget( GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, int x, int y, int w, int h, CartridgeUA& cart, bool swapHotspots) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h), - myCart(cart), + : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart), mySwappedHotspots(swapHotspots) { - uInt16 size = 2 * 4096; + myHotspotDelta = 0x20; + initialize(); +} +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string CartridgeUAWidget::description() +{ ostringstream info; - info << "8K UA cartridge" << (mySwappedHotspots ? " (swapped banks)" : "") << ", two 4K banks\n" - << "Startup bank = " << cart.startBank() << " or undetermined\n"; - // Eventually, we should query this from the debugger/disassembler - for(uInt32 i = 0, offset = 0xFFC, spot = mySwappedHotspots ? 0x240 : 0x220; i < 2; - ++i, offset += 0x1000, spot += mySwappedHotspots ? -0x20 : 0x20) - { - uInt16 start = (cart.myImage[offset+1] << 8) | cart.myImage[offset]; - start -= start % 0x1000; - info << "Bank " << i << " @ $" << Common::Base::HEX4 << start << " - " - << "$" << (start + 0xFFF) << " (hotspots = $" << spot << ", $" << (spot | 0x80) << ")\n"; - } + info << "8K UA cartridge" << (mySwappedHotspots ? " (swapped banks)" : "") << ", two 4K banks\n"; + info << CartridgeEnhancedWidget::description(); - int xpos = 2, - ypos = addBaseInformation(size, "UA Limited", info.str()) + myLineHeight; - - VariantList items; - if (swapHotspots) - { - VarList::push_back(items, "0 ($240, $2C0)"); - VarList::push_back(items, "1 ($220, $2A0)"); - } - else - { - VarList::push_back(items, "0 ($220, $2A0)"); - VarList::push_back(items, "1 ($240, $2C0)"); - } - myBank = - new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("0 ($FFx, $FFx)"), - myLineHeight, items, "Set bank ", - 0, kBankChanged); - myBank->setTarget(this); - addFocusWidget(myBank); + return info.str(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeUAWidget::loadConfig() +string CartridgeUAWidget::hotspotStr(int bank, int, bool prefix) { - Debugger& dbg = instance().debugger(); - CartDebug& cart = dbg.cartDebug(); - const CartState& state = static_cast(cart.getState()); - const CartState& oldstate = static_cast(cart.getOldState()); + ostringstream info; + uInt16 hotspot = myCart.hotspot() + (bank ^ (mySwappedHotspots ? 1 : 0)) * myHotspotDelta; - myBank->setSelectedIndex(myCart.getBank(), state.bank != oldstate.bank); + info << "(" << (prefix ? "hotspot " : ""); + info << "$" << Common::Base::HEX1 << hotspot << ", $" << (hotspot | 0x80); + info << ")"; - CartDebugWidget::loadConfig(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeUAWidget::handleCommand(CommandSender* sender, - int cmd, int data, int id) -{ - if(cmd == kBankChanged) - { - myCart.unlockBank(); - myCart.bank(myBank->getSelected()); - myCart.lockBank(); - invalidate(); - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeUAWidget::bankState() -{ - ostringstream& buf = buffer(); - - static constexpr std::array spot = { "$220, $2A0", "$240, $2C0" }; - buf << "Bank = " << std::dec << myCart.getBank() - << ", hotspots = " << spot[myCart.getBank() ^ (mySwappedHotspots ? 1U : 0U)]; - - return buf.str(); + return info.str(); } diff --git a/src/debugger/gui/CartUAWidget.hxx b/src/debugger/gui/CartUAWidget.hxx index 346b1c1d2..a610a1316 100644 --- a/src/debugger/gui/CartUAWidget.hxx +++ b/src/debugger/gui/CartUAWidget.hxx @@ -19,11 +19,10 @@ #define CARTRIDGEUA_WIDGET_HXX class CartridgeUA; -class PopUpWidget; -#include "CartDebugWidget.hxx" +#include "CartEnhancedWidget.hxx" -class CartridgeUAWidget : public CartDebugWidget +class CartridgeUAWidget : public CartridgeEnhancedWidget { public: CartridgeUAWidget(GuiObject* boss, const GUI::Font& lfont, @@ -33,19 +32,16 @@ class CartridgeUAWidget : public CartDebugWidget virtual ~CartridgeUAWidget() = default; private: - CartridgeUA& myCart; - PopUpWidget* myBank{nullptr}; + string manufacturer() override { return "UA Limited"; } - bool mySwappedHotspots; + string description() override; - enum { kBankChanged = 'bkCH' }; + string hotspotStr(int bank, int seg, bool prefix = false) override; private: - void loadConfig() override; - void handleCommand(CommandSender* sender, int cmd, int data, int id) override; - - string bankState() override; + const bool mySwappedHotspots; + private: // Following constructors and assignment operators not supported CartridgeUAWidget() = delete; CartridgeUAWidget(const CartridgeUAWidget&) = delete; diff --git a/src/debugger/gui/CartWDWidget.cxx b/src/debugger/gui/CartWDWidget.cxx index c89c3c8c8..268e81bf1 100644 --- a/src/debugger/gui/CartWDWidget.cxx +++ b/src/debugger/gui/CartWDWidget.cxx @@ -15,157 +15,45 @@ // this file, and for a DISCLAIMER OF ALL WARRANTIES. //============================================================================ -#include "Debugger.hxx" -#include "CartDebug.hxx" #include "CartWD.hxx" -#include "PopUpWidget.hxx" #include "CartWDWidget.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeWDWidget::CartridgeWDWidget( GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, int x, int y, int w, int h, CartridgeWD& cart) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h), - myCart(cart) + : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart), + myCartWD(cart) { - string info = - "This scheme has eight 1K slices, which can be mapped into four 1K " - "segments in various combinations. Each 'bank' selects a predefined " - "segment arrangement (indicated in square brackets)\n" - "In the third (uppermost) segment the byte at $3FC is overwritten with 0.\n\n" - "64 bytes RAM @ $F000 - $F080\n" - " $F000 - $F03F (R), $F040 - $F07F (W)\n"; - - int xpos = 2, - ypos = addBaseInformation(myCart.mySize, "Wickstead Design", info, 12) + myLineHeight; - - VariantList items; - VarList::push_back(items, "0 ($30) [0,0,1,3]", 0); - VarList::push_back(items, "1 ($31) [0,1,2,3]", 1); - VarList::push_back(items, "2 ($32) [4,5,6,7]", 2); - VarList::push_back(items, "3 ($33) [7,4,2,3]", 3); - VarList::push_back(items, "4 ($34) [0,0,6,7]", 4); - VarList::push_back(items, "5 ($35) [0,1,7,6]", 5); - VarList::push_back(items, "6 ($36) [2,3,4,5]", 6); - VarList::push_back(items, "7 ($37) [6,0,5,1]", 7); - VarList::push_back(items, "8 ($38) [0,0,1,3]", 8); - VarList::push_back(items, "9 ($39) [0,1,2,3]", 9); - VarList::push_back(items, "10 ($3A) [4,5,6,7]", 10); - VarList::push_back(items, "11 ($3B) [7,4,2,3]", 11); - VarList::push_back(items, "12 ($3C) [0,0,6,7]", 12); - VarList::push_back(items, "13 ($3D) [0,1,7,6]", 13); - VarList::push_back(items, "14 ($3E) [2,3,4,5]", 14); - VarList::push_back(items, "15 ($3F) [6,0,5,1]", 15); - myBank = new PopUpWidget(boss, _font, xpos, ypos-2, - _font.getStringWidth("15 ($3F) [6,0,5,1]"), - myLineHeight, items, "Set bank ", - 0, kBankChanged); - myBank->setTarget(this); - addFocusWidget(myBank); + initialize(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeWDWidget::saveOldState() +string CartridgeWDWidget::description() { - myOldState.internalram.clear(); + ostringstream info; - for(uInt32 i = 0; i < internalRamSize(); ++i) - myOldState.internalram.push_back(myCart.myRAM[i]); + info << "8K + RAM Wickstead Design cartridge, \n" + << " eight 1K banks, mapped into four segments\n" + << "Hotspots $" << Common::Base::HEX1 << myCart.hotspot() << " - $" << (myCart.hotspot() + 7) << ", " + << "each hotspot selects a [predefined bank mapping]\n"; + info << ramDescription(); - myOldState.bank = myCart.getBank(); + return info.str(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeWDWidget::loadConfig() +string CartridgeWDWidget::hotspotStr(int bank, int segment, bool prefix) { - myBank->setSelectedIndex(myCart.getBank(), myCart.getBank() != myOldState.bank); + ostringstream info; + CartridgeWD::BankOrg banks = myCartWD.ourBankOrg[bank]; - CartDebugWidget::loadConfig(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeWDWidget::handleCommand(CommandSender* sender, - int cmd, int data, int id) -{ - if(cmd == kBankChanged) - { - myCart.unlockBank(); - myCart.bank(myBank->getSelected()); - myCart.lockBank(); - invalidate(); - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeWDWidget::bankState() -{ - ostringstream& buf = buffer(); - - static constexpr std::array segments = { - "[0,0,1,3]", "[0,1,2,3]", "[4,5,6,7]", "[7,4,2,3]", - "[0,0,6,7]", "[0,1,7,6]", "[2,3,4,5]", "[6,0,5,1]" - }; - uInt16 bank = myCart.getBank(); - buf << "Bank = " << std::dec << bank << ", segments = " << segments[bank & 0x7]; - - return buf.str(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 CartridgeWDWidget::internalRamSize() -{ - return 64; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 CartridgeWDWidget::internalRamRPort(int start) -{ - return 0xF000 + start; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeWDWidget::internalRamDescription() -{ - ostringstream desc; - desc << "$F000 - $F03F used for Read Access\n" - << "$F040 - $F07F used for Write Access"; - - return desc.str(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const ByteArray& CartridgeWDWidget::internalRamOld(int start, int count) -{ - myRamOld.clear(); - for(int i = 0; i < count; ++i) - myRamOld.push_back(myOldState.internalram[start + i]); - return myRamOld; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const ByteArray& CartridgeWDWidget::internalRamCurrent(int start, int count) -{ - myRamCurrent.clear(); - for(int i = 0; i < count; ++i) - myRamCurrent.push_back(myCart.myRAM[start + i]); - return myRamCurrent; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeWDWidget::internalRamSetValue(int addr, uInt8 value) -{ - myCart.myRAM[addr] = value; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeWDWidget::internalRamGetValue(int addr) -{ - return myCart.myRAM[addr]; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeWDWidget::internalRamLabel(int addr) -{ - CartDebug& dbg = instance().debugger().cartDebug(); - return dbg.getLabel(addr + 0xF000, false); + info << "(" << (prefix ? "hotspot " : "") + << "$" << Common::Base::HEX1 << (myCart.hotspot() + bank) << ") [" + << uInt16(banks.zero) << ", " + << uInt16(banks.one) << ", " + << uInt16(banks.two) << ", " + << uInt16(banks.three) << "]"; + + return info.str(); } diff --git a/src/debugger/gui/CartWDWidget.hxx b/src/debugger/gui/CartWDWidget.hxx index f52f4a2a9..2ccdd7437 100644 --- a/src/debugger/gui/CartWDWidget.hxx +++ b/src/debugger/gui/CartWDWidget.hxx @@ -19,11 +19,10 @@ #define CARTRIDGEWD_WIDGET_HXX class CartridgeWD; -class PopUpWidget; -#include "CartDebugWidget.hxx" +#include "CartEnhancedWidget.hxx" -class CartridgeWDWidget : public CartDebugWidget +class CartridgeWDWidget : public CartridgeEnhancedWidget { public: CartridgeWDWidget(GuiObject* boss, const GUI::Font& lfont, @@ -32,36 +31,19 @@ class CartridgeWDWidget : public CartDebugWidget CartridgeWD& cart); virtual ~CartridgeWDWidget() = default; - private: - CartridgeWD& myCart; - PopUpWidget* myBank{nullptr}; - - struct CartState { - ByteArray internalram; - uInt16 bank{0}; // Current banking layout - }; - CartState myOldState; - - enum { kBankChanged = 'bkCH' }; +private: + CartridgeWD& myCartWD; private: - void saveOldState() override; - void loadConfig() override; - void handleCommand(CommandSender* sender, int cmd, int data, int id) override; + string manufacturer() override { return "Wickstead Design"; } - string bankState() override; + string description() override; - // start of functions for Cartridge RAM tab - uInt32 internalRamSize() override; - uInt32 internalRamRPort(int start) override; - string internalRamDescription() override; - const ByteArray& internalRamOld(int start, int count) override; - const ByteArray& internalRamCurrent(int start, int count) override; - void internalRamSetValue(int addr, uInt8 value) override; - uInt8 internalRamGetValue(int addr) override; - string internalRamLabel(int addr) override; - // end of functions for Cartridge RAM tab + string hotspotStr(int bank, int seg = 0, bool prefix = false) override; + uInt16 bankSegs() override { return 1; } + + private: // Following constructors and assignment operators not supported CartridgeWDWidget() = delete; CartridgeWDWidget(const CartridgeWDWidget&) = delete; diff --git a/src/debugger/gui/CartX07Widget.cxx b/src/debugger/gui/CartX07Widget.cxx index 78fd70d3b..2c4a167d4 100644 --- a/src/debugger/gui/CartX07Widget.cxx +++ b/src/debugger/gui/CartX07Widget.cxx @@ -16,94 +16,26 @@ //============================================================================ #include "CartX07.hxx" -#include "PopUpWidget.hxx" #include "CartX07Widget.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeX07Widget::CartridgeX07Widget( GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, int x, int y, int w, int h, CartridgeX07& cart) - : CartDebugWidget(boss, lfont, nfont, x, y, w, h), - myCart(cart) + : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart) { - uInt32 size = 16 * 4096; + initialize(); +} +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string CartridgeX07Widget::description() +{ ostringstream info; + info << "64K X07 cartridge, 16 4K banks\n" - << "Startup bank = " << cart.startBank() << "\n" << "Multiple hotspots, all below $1000\n" << "See documentation for further details\n"; + info << CartridgeEnhancedWidget::description(); - // Eventually, we should query this from the debugger/disassembler - for(uInt32 i = 0, offset = 0xFFC; i < 16; ++i, offset += 0x1000) - { - uInt16 start = (cart.myImage[offset+1] << 8) | cart.myImage[offset]; - start -= start % 0x1000; - info << "Bank " << std::dec << i << " @ $" << Common::Base::HEX4 << start - << " - " << "$" << (start + 0xFFF) << "\n"; - } - - int xpos = 2, - ypos = addBaseInformation(size, "AtariAge / John Payson / Fred Quimby", - info.str()) + myLineHeight; - - VariantList items; - VarList::push_back(items, " 0"); - VarList::push_back(items, " 1"); - VarList::push_back(items, " 2"); - VarList::push_back(items, " 3"); - VarList::push_back(items, " 4"); - VarList::push_back(items, " 5"); - VarList::push_back(items, " 6"); - VarList::push_back(items, " 7"); - VarList::push_back(items, " 8"); - VarList::push_back(items, " 9"); - VarList::push_back(items, " 10"); - VarList::push_back(items, " 11"); - VarList::push_back(items, " 12"); - VarList::push_back(items, " 13"); - VarList::push_back(items, " 14"); - VarList::push_back(items, " 15"); - myBank = - new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth(" 15"), - myLineHeight, items, "Set bank ", - 0, kBankChanged); - myBank->setTarget(this); - addFocusWidget(myBank); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeX07Widget::loadConfig() -{ - Debugger& dbg = instance().debugger(); - CartDebug& cart = dbg.cartDebug(); - const CartState& state = static_cast(cart.getState()); - const CartState& oldstate = static_cast(cart.getOldState()); - - myBank->setSelectedIndex(myCart.getBank(), state.bank != oldstate.bank); - - CartDebugWidget::loadConfig(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeX07Widget::handleCommand(CommandSender* sender, - int cmd, int data, int id) -{ - if(cmd == kBankChanged) - { - myCart.unlockBank(); - myCart.bank(myBank->getSelected()); - myCart.lockBank(); - invalidate(); - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartridgeX07Widget::bankState() -{ - ostringstream& buf = buffer(); - - buf << "Bank = " << std::dec << myCart.myCurrentBank; - - return buf.str(); + return info.str(); } diff --git a/src/debugger/gui/CartX07Widget.hxx b/src/debugger/gui/CartX07Widget.hxx index 00782dca8..58492e7e6 100644 --- a/src/debugger/gui/CartX07Widget.hxx +++ b/src/debugger/gui/CartX07Widget.hxx @@ -19,11 +19,10 @@ #define CARTRIDGEX07_WIDGET_HXX class CartridgeX07; -class PopUpWidget; -#include "CartDebugWidget.hxx" +#include "CartEnhancedWidget.hxx" -class CartridgeX07Widget : public CartDebugWidget +class CartridgeX07Widget : public CartridgeEnhancedWidget { public: CartridgeX07Widget(GuiObject* boss, const GUI::Font& lfont, @@ -33,17 +32,11 @@ class CartridgeX07Widget : public CartDebugWidget virtual ~CartridgeX07Widget() = default; private: - CartridgeX07& myCart; - PopUpWidget* myBank{nullptr}; + string manufacturer() override { return "AtariAge / John Payson / Fred Quimby"; } - enum { kBankChanged = 'bkCH' }; + string description() override; private: - void loadConfig() override; - void handleCommand(CommandSender* sender, int cmd, int data, int id) override; - - string bankState() override; - // Following constructors and assignment operators not supported CartridgeX07Widget() = delete; CartridgeX07Widget(const CartridgeX07Widget&) = delete; diff --git a/src/debugger/gui/DebuggerDialog.cxx b/src/debugger/gui/DebuggerDialog.cxx index ac3c8d6c9..5d8404d06 100644 --- a/src/debugger/gui/DebuggerDialog.cxx +++ b/src/debugger/gui/DebuggerDialog.cxx @@ -71,12 +71,11 @@ DebuggerDialog::DebuggerDialog(OSystem& osystem, DialogContainer& parent, // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void DebuggerDialog::loadConfig() { - // set initial focus to myPrompt - if (myFirstLoad) - { - setFocus(myPrompt); - myFirstLoad = false; - } + if(myFocusedWidget == nullptr) + // Set initial focus to prompt tab + myFocusedWidget = myPrompt; + // Restore focus + setFocus(myFocusedWidget); myTab->loadConfig(); myTiaInfo->loadConfig(); @@ -89,6 +88,11 @@ void DebuggerDialog::loadConfig() myMessageBox->setText(""); } +void DebuggerDialog::saveConfig() +{ + myFocusedWidget = _focusedWidget; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void DebuggerDialog::handleKeyDown(StellaKey key, StellaMod mod, bool repeated) { @@ -617,7 +621,7 @@ void DebuggerDialog::addRomArea() // The 'cart-specific' information tab (optional) - tabID = myRomTab->addTab(" " + instance().console().cartridge().name() + " ", TabWidget::AUTO_WIDTH); + tabID = myRomTab->addTab(" " + instance().console().cartridge().name() + " ", TabWidget::AUTO_WIDTH); myCartInfo = instance().console().cartridge().infoWidget( myRomTab, *myLFont, *myNFont, 2, 2, tabWidth - 1, tabHeight - myRomTab->getTabHeight() - 2); @@ -640,7 +644,7 @@ void DebuggerDialog::addRomArea() // The cartridge RAM tab if (myCartDebug->internalRamSize() > 0) { - tabID = myRomTab->addTab(" Cartridge RAM ", TabWidget::AUTO_WIDTH); + tabID = myRomTab->addTab(myCartDebug->tabLabel(), TabWidget::AUTO_WIDTH); myCartRam = new CartRamWidget(myRomTab, *myLFont, *myNFont, 2, 2, tabWidth - 1, tabHeight - myRomTab->getTabHeight() - 2, *myCartDebug); diff --git a/src/debugger/gui/DebuggerDialog.hxx b/src/debugger/gui/DebuggerDialog.hxx index b1b42211a..b66ae37a6 100644 --- a/src/debugger/gui/DebuggerDialog.hxx +++ b/src/debugger/gui/DebuggerDialog.hxx @@ -71,6 +71,7 @@ class DebuggerDialog : public Dialog ButtonWidget& unwindButton() const { return *myUnwindButton; } void showFatalMessage(const string& msg); + void saveConfig() override; private: void center() override { positionAt(0); } @@ -136,7 +137,8 @@ class DebuggerDialog : public Dialog unique_ptr myLFont; // used for labels unique_ptr myNFont; // used for normal text - bool myFirstLoad{true}; + Widget* myFocusedWidget{nullptr}; + private: // Following constructors and assignment operators not supported diff --git a/src/debugger/gui/RomWidget.cxx b/src/debugger/gui/RomWidget.cxx index 18c8b0b78..3279ca5d9 100644 --- a/src/debugger/gui/RomWidget.cxx +++ b/src/debugger/gui/RomWidget.cxx @@ -41,12 +41,9 @@ RomWidget::RomWidget(GuiObject* boss, const GUI::Font& lfont, const GUI::Font& n // Show current bank state xpos = x; ypos = y + 7; - t = new StaticTextWidget(boss, lfont, xpos, ypos, - lfont.getStringWidth("Bank"), - lfont.getFontHeight(), - "Bank", TextAlign::Left); + t = new StaticTextWidget(boss, lfont, xpos, ypos, "Info "); - xpos += t->getWidth() + 5; + xpos += t->getRight(); myBank = new EditTextWidget(boss, nfont, xpos, ypos-2, _w - 2 - xpos, nfont.getLineHeight()); myBank->setEditable(false); @@ -68,7 +65,7 @@ void RomWidget::loadConfig() const CartState& oldstate = static_cast(cart.getOldState()); // Fill romlist the current bank of source or disassembly - myListIsDirty |= cart.disassemble(myListIsDirty); + myListIsDirty |= cart.disassemblePC(myListIsDirty); if(myListIsDirty) { myRomList->setList(cart.disassembly()); diff --git a/src/debugger/gui/module.mk b/src/debugger/gui/module.mk index d80408a60..8f19ce5fa 100644 --- a/src/debugger/gui/module.mk +++ b/src/debugger/gui/module.mk @@ -22,14 +22,13 @@ MODULE_OBJS := \ src/debugger/gui/CartCDFInfoWidget.o \ src/debugger/gui/CartCMWidget.o \ src/debugger/gui/CartCTYWidget.o \ - src/debugger/gui/CartCVPlusWidget.o \ src/debugger/gui/CartCVWidget.o \ - src/debugger/gui/CartDASHWidget.o \ src/debugger/gui/CartDFSCWidget.o \ src/debugger/gui/CartDFWidget.o \ src/debugger/gui/CartDPCPlusWidget.o \ src/debugger/gui/CartDPCWidget.o \ src/debugger/gui/CartE0Widget.o \ + src/debugger/gui/CartEnhancedWidget.o \ src/debugger/gui/CartMNetworkWidget.o \ src/debugger/gui/CartE7Widget.o \ src/debugger/gui/CartE78KWidget.o \ diff --git a/src/emucore/Bankswitch.cxx b/src/emucore/Bankswitch.cxx index 231d6f73f..1b907eea7 100644 --- a/src/emucore/Bankswitch.cxx +++ b/src/emucore/Bankswitch.cxx @@ -104,8 +104,9 @@ Bankswitch::BSList = {{ { "64IN1" , "64IN1 Multicart (128/256K)" }, { "128IN1" , "128IN1 Multicart (256/512K)" }, { "2K" , "2K (32-2048 bytes Atari)" }, - { "3E" , "3E (32K Tigervision)" }, - { "3E+" , "3E+ (TJ modified DASH)" }, + { "3E" , "3E (Tigervision, 32K RAM)" }, + { "3EX" , "3EX (Tigervision, 256K RAM)" }, + { "3E+" , "3E+ (TJ modified 3E)" }, { "3F" , "3F (512K Tigervision)" }, { "4A50" , "4A50 (64K 4A50 + RAM)" }, { "4K" , "4K (4K Atari)" }, @@ -118,8 +119,6 @@ Bankswitch::BSList = {{ { "CM" , "CM (SpectraVideo CompuMate)" }, { "CTY" , "CTY (CDW - Chetiry)" }, { "CV" , "CV (Commavid extra RAM)" }, - { "CV+" , "CV+ (Extended Commavid)" }, - { "DASH" , "DASH (Experimental)" }, { "DF" , "DF (CPUWIZ 128K)" }, { "DFSC" , "DFSC (CPUWIZ 128K + RAM)" }, { "DPC" , "DPC (Pitfall II)" }, @@ -180,6 +179,7 @@ Bankswitch::ExtensionMap Bankswitch::ourExtensions = { { "128N1" , Bankswitch::Type::_128IN1 }, { "2K" , Bankswitch::Type::_2K }, { "3E" , Bankswitch::Type::_3E }, + { "3EX" , Bankswitch::Type::_3EX }, { "3EP" , Bankswitch::Type::_3EP }, { "3E+" , Bankswitch::Type::_3EP }, { "3F" , Bankswitch::Type::_3F }, @@ -197,9 +197,6 @@ Bankswitch::ExtensionMap Bankswitch::ourExtensions = { { "CM" , Bankswitch::Type::_CM }, { "CTY" , Bankswitch::Type::_CTY }, { "CV" , Bankswitch::Type::_CV }, - { "CVP" , Bankswitch::Type::_CVP }, - { "DAS" , Bankswitch::Type::_DASH }, - { "DASH" , Bankswitch::Type::_DASH }, { "DF" , Bankswitch::Type::_DF }, { "DFS" , Bankswitch::Type::_DFSC }, { "DFSC" , Bankswitch::Type::_DFSC }, @@ -250,6 +247,7 @@ Bankswitch::NameToTypeMap Bankswitch::ourNameToTypes = { { "2K" , Bankswitch::Type::_2K }, { "3E" , Bankswitch::Type::_3E }, { "3E+" , Bankswitch::Type::_3EP }, + { "3EX" , Bankswitch::Type::_3EX }, { "3F" , Bankswitch::Type::_3F }, { "4A50" , Bankswitch::Type::_4A50 }, { "4K" , Bankswitch::Type::_4K }, @@ -262,8 +260,6 @@ Bankswitch::NameToTypeMap Bankswitch::ourNameToTypes = { { "CM" , Bankswitch::Type::_CM }, { "CTY" , Bankswitch::Type::_CTY }, { "CV" , Bankswitch::Type::_CV }, - { "CV+" , Bankswitch::Type::_CVP }, - { "DASH" , Bankswitch::Type::_DASH }, { "DF" , Bankswitch::Type::_DF }, { "DFSC" , Bankswitch::Type::_DFSC }, { "DPC" , Bankswitch::Type::_DPC }, diff --git a/src/emucore/Bankswitch.hxx b/src/emucore/Bankswitch.hxx index c57f81609..0dd3871c7 100644 --- a/src/emucore/Bankswitch.hxx +++ b/src/emucore/Bankswitch.hxx @@ -38,14 +38,14 @@ class Bankswitch public: // Currently supported bankswitch schemes enum class Type { - _AUTO, _0840, _2IN1, _4IN1, _8IN1, _16IN1, _32IN1, - _64IN1, _128IN1, _2K, _3E, _3EP, _3F, _4A50, - _4K, _4KSC, _AR, _BF, _BFSC, _BUS, _CDF, - _CM, _CTY, _CV, _CVP, _DASH, _DF, _DFSC, - _DPC, _DPCP, _E0, _E7, _E78K, _EF, _EFSC, - _F0, _F4, _F4SC, _F6, _F6SC, _F8, _F8SC, - _FA, _FA2, _FC, _FE, _MDM, _SB, _UA, - _UASW, _WD, _WDSW, _X07, + _AUTO, _0840, _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, _E78K, _EF, _EFSC, _F0, + _F4, _F4SC, _F6, _F6SC, _F8, _F8SC, _FA, + _FA2, _FC, _FE, _MDM, _SB, _UA, _UASW, + _WD, _WDSW, _X07, #ifdef CUSTOM_ARM _CUSTOM, #endif diff --git a/src/emucore/Cart.cxx b/src/emucore/Cart.cxx index f85850ba2..7639101ff 100644 --- a/src/emucore/Cart.cxx +++ b/src/emucore/Cart.cxx @@ -83,7 +83,7 @@ uInt16 Cartridge::bankSize(uInt16 bank) const getImage(size); - return std::min(uInt32(size) / bankCount(), 4_KB); // assuming that each bank has the same size + return std::min(uInt32(size) / romBankCount(), 4_KB); // assuming that each bank has the same size } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -145,13 +145,13 @@ string Cartridge::getAccessCounters() const ostringstream out; uInt32 offset = 0; - for(uInt16 bank = 0; bank < bankCount(); ++bank) + for(uInt16 bank = 0; bank < romBankCount(); ++bank) { uInt16 origin = bankOrigin(bank); uInt16 bankSize = this->bankSize(bank); out << "Bank " << Common::Base::toString(bank, Common::Base::Fmt::_10_8) << " / 0.." - << Common::Base::toString(bankCount() - 1, Common::Base::Fmt::_10_8) << " reads:\n"; + << Common::Base::toString(romBankCount() - 1, Common::Base::Fmt::_10_8) << " reads:\n"; for(uInt16 addr = 0; addr < bankSize; ++addr) { out << Common::Base::HEX4 << (addr | origin) << "," @@ -159,7 +159,7 @@ string Cartridge::getAccessCounters() const } out << "\n"; out << "Bank " << Common::Base::toString(bank, Common::Base::Fmt::_10_8) << " / 0.." - << Common::Base::toString(bankCount() - 1, Common::Base::Fmt::_10_8) << " writes:\n"; + << Common::Base::toString(romBankCount() - 1, Common::Base::Fmt::_10_8) << " writes:\n"; for(uInt16 addr = 0; addr < bankSize; ++addr) { out << Common::Base::HEX4 << (addr | origin) << "," @@ -230,11 +230,11 @@ uInt16 Cartridge::initializeStartBank(uInt16 defaultBank) int propsBank = myStartBankFromPropsFunc(); if(randomStartBank()) - return myStartBank = mySystem->randGenerator().next() % bankCount(); + return myStartBank = mySystem->randGenerator().next() % romBankCount(); else if(propsBank >= 0) - return myStartBank = BSPF::clamp(propsBank, 0, bankCount() - 1); + return myStartBank = BSPF::clamp(propsBank, 0, romBankCount() - 1); else - return myStartBank = BSPF::clamp(int(defaultBank), 0, bankCount() - 1); + return myStartBank = BSPF::clamp(int(defaultBank), 0, romBankCount() - 1); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/emucore/Cart.hxx b/src/emucore/Cart.hxx index ce7d66a8b..6fbf1a68d 100644 --- a/src/emucore/Cart.hxx +++ b/src/emucore/Cart.hxx @@ -180,7 +180,7 @@ class Cartridge : public Device virtual uInt16 getBank(uInt16 address = 0) const { return 0; } /** - Query the number of 'banks' supported by the cartridge. Note that + Query the number of ROM 'banks' supported by the cartridge. Note that this information is cart-specific, where each cart basically defines what a 'bank' is. @@ -189,10 +189,18 @@ class Cartridge : public Device cases where ROMs have 2K blocks in some preset area, the bankCount is the number of such blocks. Finally, in some esoteric schemes, the number of ways that the addressing can change (multiple ROM and - RAM slices at multiple access points) is so complicated that the + RAM segments at multiple access points) is so complicated that the cart will report having only one 'virtual' bank. */ - virtual uInt16 bankCount() const { return 1; } + virtual uInt16 romBankCount() const { return 1; } + + + /** + Query the number of RAM 'banks' supported by the cartridge. Note that + this information is cart-specific, where each cart basically defines + what a 'bank' is. + */ + virtual uInt16 ramBankCount() const { return 0; } /** Get the size of a bank. diff --git a/src/emucore/Cart0840.cxx b/src/emucore/Cart0840.cxx index f10dbb7b2..e071f61a2 100644 --- a/src/emucore/Cart0840.cxx +++ b/src/emucore/Cart0840.cxx @@ -21,25 +21,14 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Cartridge0840::Cartridge0840(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5) + : CartridgeEnhanced(image, size, md5, settings) { - // Copy the ROM image into my buffer - std::copy_n(image.get(), std::min(myImage.size(), size), myImage.begin()); - createRomAccessArrays(myImage.size()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Cartridge0840::reset() -{ - // Upon reset we switch to the startup bank - initializeStartBank(0); - bank(startBank()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Cartridge0840::install(System& system) { - mySystem = &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 @@ -56,32 +45,34 @@ void Cartridge0840::install(System& system) System::PageAccess access(this, System::PageAccessType::READ); for(uInt16 addr = 0x0800; addr < 0x0FFF; addr += System::PAGE_SIZE) mySystem->setPageAccess(addr, access); +} - // Install pages for bank 0 - bank(startBank()); +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool Cartridge0840::checkSwitchBank(uInt16 address, uInt8) +{ + // Switch banks if necessary + switch(address & 0x1840) + { + case 0x0800: + // Set the current bank to the lower 4k bank + bank(0); + return true; + + case 0x0840: + // Set the current bank to the upper 4k bank + bank(1); + return true; + + default: + break; + } + return false; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uInt8 Cartridge0840::peek(uInt16 address) { - address &= 0x1840; - - // Switch banks if necessary - switch(address) - { - case 0x0800: - // Set the current bank to the lower 4k bank - bank(0); - break; - - case 0x0840: - // Set the current bank to the upper 4k bank - bank(1); - break; - - default: - break; - } + checkSwitchBank(address); // Because of the way we've set up accessing above, we can only // get here when the addresses are from 0x800 - 0xFFF @@ -92,24 +83,7 @@ uInt8 Cartridge0840::peek(uInt16 address) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool Cartridge0840::poke(uInt16 address, uInt8 value) { - address &= 0x1840; - - // Switch banks if necessary - switch(address) - { - case 0x0800: - // Set the current bank to the lower 4k bank - bank(0); - break; - - case 0x0840: - // Set the current bank to the upper 4k bank - bank(1); - break; - - default: - break; - } + checkSwitchBank(address); // Because of the way accessing is set up, we will may get here by // doing a write to 0x800 - 0xFFF or cart; we ignore the cart write @@ -121,85 +95,3 @@ bool Cartridge0840::poke(uInt16 address, uInt8 value) return false; } - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool Cartridge0840::bank(uInt16 bank) -{ - if(bankLocked()) return false; - - // Remember what bank we're in - myBankOffset = bank << 12; - - // Setup the page access methods for the current bank - System::PageAccess access(this, System::PageAccessType::READ); - - // Map ROM image into the system - for(uInt16 addr = 0x1000; addr < 0x2000; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[myBankOffset + (addr & 0x0FFF)]; - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - mySystem->setPageAccess(addr, access); - } - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 Cartridge0840::getBank(uInt16) const -{ - return myBankOffset >> 12; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 Cartridge0840::bankCount() const -{ - return 2; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool Cartridge0840::patch(uInt16 address, uInt8 value) -{ - myImage[myBankOffset + (address & 0x0fff)] = value; - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* Cartridge0840::getImage(size_t& size) const -{ - size = myImage.size(); - return myImage.data(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool Cartridge0840::save(Serializer& out) const -{ - try - { - out.putShort(myBankOffset); - } - catch(...) - { - cerr << "ERROR: Cartridge0840::save" << endl; - return false; - } - - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool Cartridge0840::load(Serializer& in) -{ - try - { - myBankOffset = in.getShort(); - } - catch(...) - { - cerr << "ERROR: Cartridge0840::load" << endl; - return false; - } - - // Remember what bank we were in - bank(myBankOffset); - - return true; -} diff --git a/src/emucore/Cart0840.hxx b/src/emucore/Cart0840.hxx index 87d372f59..af1977485 100644 --- a/src/emucore/Cart0840.hxx +++ b/src/emucore/Cart0840.hxx @@ -19,7 +19,7 @@ #define CARTRIDGE0840_HXX #include "bspf.hxx" -#include "Cart.hxx" +#include "CartEnhanced.hxx" #include "System.hxx" #ifdef DEBUGGER_SUPPORT #include "Cart0840Widget.hxx" @@ -30,9 +30,9 @@ are two 4K banks, which are switched by accessing $0800 (bank 0) and $0840 (bank 1). - @author Fred X. Quimby + @author Fred X. Quimby, Thomas Jentzsch */ -class Cartridge0840 : public Cartridge +class Cartridge0840 : public CartridgeEnhanced { friend class Cartridge0840Widget; @@ -50,11 +50,6 @@ class Cartridge0840 : public Cartridge virtual ~Cartridge0840() = default; public: - /** - Reset device to its power-on state - */ - void reset() override; - /** Install cartridge in the specified system. Invoked by the system when the cartridge is attached to it. @@ -63,58 +58,6 @@ class Cartridge0840 : public Cartridge */ void install(System& system) override; - /** - Install pages for the specified bank in the system. - - @param bank The bank that should be installed in the system - */ - bool bank(uInt16 bank) 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 bankCount() 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,12 +95,11 @@ class Cartridge0840 : public Cartridge bool poke(uInt16 address, uInt8 value) override; private: - // The 8K ROM image of the cartridge - std::array myImage; + bool checkSwitchBank(uInt16 address, uInt8 value = 0) override; - // Indicates the offset into the ROM image (aligns to current bank) - uInt16 myBankOffset{0}; + uInt16 hotspot() const override { return 0x0840; } + private: // Previous Device's page access std::array myHotSpotPageAccess; diff --git a/src/emucore/Cart2K.cxx b/src/emucore/Cart2K.cxx index ee9f2b334..f4d4ef7bd 100644 --- a/src/emucore/Cart2K.cxx +++ b/src/emucore/Cart2K.cxx @@ -21,15 +21,19 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Cartridge2K::Cartridge2K(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5) + : CartridgeEnhanced(image, size, md5, settings) { // Size can be a maximum of 2K - if(size > 2_KB) size = 2_KB; + if(size > 2_KB) + size = 2_KB; // Set image size to closest power-of-two for the given size - mySize = 1; + mySize = 1; myBankShift = 0; while(mySize < size) + { mySize <<= 1; + myBankShift++; + } // Initialize ROM with illegal 6502 opcode that causes a real 6502 to jam size_t bufSize = std::max(mySize, System::PAGE_SIZE); @@ -49,62 +53,6 @@ Cartridge2K::Cartridge2K(const ByteBuffer& image, size_t size, for(size_t i = 0; i < System::PAGE_SIZE; i += mySize) std::copy_n(image.get(), mySize, myImage.get() + i); mySize = System::PAGE_SIZE; - } - - createRomAccessArrays(mySize); - - // Set mask for accessing the image buffer - // This is guaranteed to work, as mySize is a power of two - myMask = static_cast(mySize) - 1; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Cartridge2K::reset() -{ - myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Cartridge2K::install(System& system) -{ - mySystem = &system; - - // Map ROM image into the system - // Note that we don't need our own peek/poke methods, since the mapping - // takes care of the entire address space - System::PageAccess access(this, System::PageAccessType::READ); - for(uInt16 addr = 0x1000; addr < 0x2000; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[addr & myMask]; - access.romAccessBase = &myRomAccessBase[addr & myMask]; - access.romPeekCounter = &myRomAccessCounter[addr & myMask]; - access.romPokeCounter = &myRomAccessCounter[(addr & myMask) + myAccessSize]; - mySystem->setPageAccess(addr, access); + myBankShift = 6; } } - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool Cartridge2K::patch(uInt16 address, uInt8 value) -{ - myImage[address & myMask] = value; - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* Cartridge2K::getImage(size_t& size) const -{ - size = mySize; - return myImage.get(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool Cartridge2K::save(Serializer&) const -{ - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool Cartridge2K::load(Serializer&) -{ - return true; -} diff --git a/src/emucore/Cart2K.hxx b/src/emucore/Cart2K.hxx index 22f08fbf5..0ed6fedef 100644 --- a/src/emucore/Cart2K.hxx +++ b/src/emucore/Cart2K.hxx @@ -21,7 +21,7 @@ class System; #include "bspf.hxx" -#include "Cart.hxx" +#include "CartEnhanced.hxx" #ifdef DEBUGGER_SUPPORT #include "Cart2KWidget.hxx" #endif @@ -33,9 +33,9 @@ class System; data repeats in intervals based on the size of the ROM (which will always be a power of 2). - @author Stephen Anthony + @author Stephen Anthony, Thomas Jentzsch */ -class Cartridge2K : public Cartridge +class Cartridge2K : public CartridgeEnhanced { friend class Cartridge2KWidget; @@ -53,52 +53,6 @@ class Cartridge2K : public Cartridge virtual ~Cartridge2K() = default; public: - /** - Reset cartridge to its power-on state - */ - void reset() override; - - /** - 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; - - /** - 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). @@ -118,22 +72,8 @@ class Cartridge2K : public Cartridge } #endif - /** - Get the byte at the specified address. - - @return The byte at the specified address - */ - uInt8 peek(uInt16 address) override { return myImage[address & myMask]; } - private: - // Pointer to a dynamically allocated ROM image of the cartridge - ByteBuffer myImage; - - // Size of the ROM image - size_t mySize{0}; - - // Mask to use for mirroring - uInt16 myMask{0}; + bool checkSwitchBank(uInt16 address, uInt8 value = 0) override { return false; } private: // Following constructors and assignment operators not supported diff --git a/src/emucore/Cart3E.cxx b/src/emucore/Cart3E.cxx index 963fd14de..be69d105d 100644 --- a/src/emucore/Cart3E.cxx +++ b/src/emucore/Cart3E.cxx @@ -22,268 +22,67 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Cartridge3E::Cartridge3E(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5), - mySize(size) + : CartridgeEnhanced(image, size, md5, settings) { - // 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 Cartridge3E::reset() -{ - initializeRAM(myRAM.data(), myRAM.size()); - initializeStartBank(0); - - // We'll map the startup bank into the first segment upon reset - bank(startBank()); + myBankShift = BANK_SHIFT; + myRamSize = RAM_SIZE; + myRamBankCount = RAM_BANKS; + myRamWpHigh = RAM_HIGH_WP; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Cartridge3E::install(System& system) { - mySystem = &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) mySystem->setPageAccess(addr, access); +} - // Setup the second segment to always point to the last ROM slice - access.type = System::PageAccessType::READ; - for(uInt16 addr = 0x1800; addr < 0x2000; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[(mySize - 2048) + (addr & 0x07FF)]; - access.romAccessBase = &myRomAccessBase[(mySize - 2048) + (addr & 0x07FF)]; - access.romPeekCounter = &myRomAccessCounter[(mySize - 2048) + (addr & 0x07FF)]; - access.romPokeCounter = &myRomAccessCounter[(mySize - 2048) + (addr & 0x07FF) + myAccessSize]; - mySystem->setPageAccess(addr, access); +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool Cartridge3E::checkSwitchBank(uInt16 address, uInt8 value) +{ + // Switch banks if necessary + if(address == 0x003F) { + // Switch ROM bank into segment 0 + bank(value); + return true; } - - // Install pages for the startup bank into the first segment - bank(startBank()); + else if(address == 0x003E) + { + // Switch RAM bank into segment 0 + bank(value + romBankCount()); + return true; + } + return false; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uInt8 Cartridge3E::peek(uInt16 address) { uInt16 peekAddress = address; - address &= 0x0FFF; - - // Due to the way paging is set up, the only way to get here is a TIA read or - // attempting to read from the RAM write port + address &= ROM_MASK; if(address < 0x0040) // TIA access return mySystem->tia().peek(address); - else if(myCurrentBank >= 256) - { - // Reading from the write port triggers an unwanted write - return peekRAM(myRAM[(address & 0x03FF) + ((myCurrentBank - 256) << 10)], peekAddress); - } - // Make compiler happy; should never get here - return myImage[(address & 0x07FF) + mySize - 2048]; + return CartridgeEnhanced::peek(peekAddress); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool Cartridge3E::poke(uInt16 address, uInt8 value) { uInt16 pokeAddress = address; - address &= 0x0FFF; + address &= ROM_MASK; - // Switch banks if necessary. Armin (Kroko) says there are no mirrored - // hotspots. - if(address < 0x0040) + if(address < 0x0040) // TIA access { - if(address == 0x003F) - bank(value); - else if(address == 0x003E) - bank(value + 256); - + checkSwitchBank(address, value); return mySystem->tia().poke(address, value); } - else if(myCurrentBank >= 256) - { - if(address & 0x0400) - { - pokeRAM(myRAM[(address & 0x03FF) + ((myCurrentBank - 256) << 10)], - pokeAddress, value); - return true; - } - else - { - // Writing to the read port should be ignored, but trigger a break if option enabled - uInt8 dummy; - pokeRAM(dummy, pokeAddress, value); - myRamWriteAccess = pokeAddress; - return false; - } - } - return false; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool Cartridge3E::bank(uInt16 bank) -{ - if(bankLocked()) return false; - - if(bank < 256) - { - // Make sure the bank they're asking for is reasonable - if((uInt32(bank) << 11) < mySize) - { - myCurrentBank = bank; - } - else - { - // Oops, the bank they're asking for isn't valid so let's wrap it - // around to a valid bank number - myCurrentBank = bank % (mySize >> 11); - } - - uInt32 offset = myCurrentBank << 11; - - // Setup the page access methods for the current bank - System::PageAccess access(this, System::PageAccessType::READ); - - // Map ROM image into the system - for(uInt16 addr = 0x1000; addr < 0x1800; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[offset + (addr & 0x07FF)]; - access.romAccessBase = &myRomAccessBase[offset + (addr & 0x07FF)]; - access.romPeekCounter = &myRomAccessCounter[offset + (addr & 0x07FF)]; - access.romPokeCounter = &myRomAccessCounter[offset + (addr & 0x07FF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - } - else - { - bank -= 256; - bank %= 32; - myCurrentBank = bank + 256; - - uInt32 offset = bank << 10; - - // Setup the page access methods for the current bank - System::PageAccess access(this, System::PageAccessType::READ); - - // Map read-port RAM image into the system - // Writes are mapped to poke(), to check for write to the read port - for(uInt16 addr = 0x1000; addr < 0x1400; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myRAM[offset + (addr & 0x03FF)]; - access.romAccessBase = &myRomAccessBase[mySize + offset + (addr & 0x03FF)]; - access.romPeekCounter = &myRomAccessCounter[mySize + offset + (addr & 0x03FF)]; - access.romPokeCounter = &myRomAccessCounter[mySize + offset + (addr & 0x03FF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - access.directPeekBase = nullptr; - access.type = System::PageAccessType::WRITE; - - // Map write-port RAM image into the system - // Reads are mapped to peek(), to check for read from write port - for(uInt16 addr = 0x1400; addr < 0x1800; addr += System::PAGE_SIZE) - { - access.romAccessBase = &myRomAccessBase[mySize + offset + (addr & 0x03FF)]; - access.romPeekCounter = &myRomAccessCounter[mySize + offset + (addr & 0x03FF)]; - access.romPokeCounter = &myRomAccessCounter[mySize + offset + (addr & 0x03FF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - } - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 Cartridge3E::getBank(uInt16 address) const -{ - if (address & 0x800) - return 255; // 256 - 1 // 2K slices, fixed bank - else - return myCurrentBank; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 Cartridge3E::bankCount() const -{ - // Because the RAM banks always start at 256 and above, we require the - // number of ROM banks to be 256 - // If the RAM banks were simply appended to the number of actual - // ROM banks, bank numbers would be ambiguous (ie, would bank 128 be - // the last bank of ROM, or one of the banks of RAM?) - return 256 + 32; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 Cartridge3E::bankSize(uInt16 bank) const -{ - return 2_KB; // we cannot use bankCount() here, because it delivers wrong numbers -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool Cartridge3E::patch(uInt16 address, uInt8 value) -{ - address &= 0x0FFF; - - if(address < 0x0800) - { - if(myCurrentBank < 256) - myImage[(address & 0x07FF) + (myCurrentBank << 11)] = value; - else - myRAM[(address & 0x03FF) + ((myCurrentBank - 256) << 10)] = value; - } - else - myImage[(address & 0x07FF) + mySize - 2048] = value; - - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* Cartridge3E::getImage(size_t& size) const -{ - size = mySize; - return myImage.get(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool Cartridge3E::save(Serializer& out) const -{ - try - { - out.putShort(myCurrentBank); - out.putByteArray(myRAM.data(), myRAM.size()); - } - catch(...) - { - cerr << "ERROR: Cartridge3E::save" << endl; - return false; - } - - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool Cartridge3E::load(Serializer& in) -{ - try - { - myCurrentBank = in.getShort(); - in.getByteArray(myRAM.data(), myRAM.size()); - } - catch(...) - { - cerr << "ERROR: Cartridge3E::load" << endl; - return false; - } - - // Now, go to the current bank - bank(myCurrentBank); - - return true; + return CartridgeEnhanced::poke(pokeAddress, value); } diff --git a/src/emucore/Cart3E.hxx b/src/emucore/Cart3E.hxx index e2382babb..f2ead51f5 100644 --- a/src/emucore/Cart3E.hxx +++ b/src/emucore/Cart3E.hxx @@ -21,7 +21,7 @@ class System; #include "bspf.hxx" -#include "Cart.hxx" +#include "CartEnhanced.hxx" #ifdef DEBUGGER_SUPPORT #include "Cart3EWidget.hxx" #endif @@ -47,10 +47,8 @@ class System; by storing its value into $3F. To map RAM in the first 2K segment instead, store the RAM bank number into $3E. - This implementation of 3E bankswitching numbers the ROM banks 0 to - 255, and the RAM banks 256 to 287. This is done because the public - bankswitching interface requires us to use one bank number, not one - bank number plus the knowledge of whether it's RAM or ROM. + This implementation of 3E bankswitching numbers the RAM banks (up to 32) + after the ROM banks (up to 256). All 32K of potential RAM is available to a game using this class, even though real cartridges might not have the full 32K: We have no way to @@ -58,10 +56,10 @@ class System; may add a stella.pro property for this), but for now it shouldn't cause any problems. (Famous last words...) - @author B. Watson + @author B. Watson, Thomas Jentzsch */ -class Cartridge3E : public Cartridge +class Cartridge3E : public CartridgeEnhanced { friend class Cartridge3EWidget; @@ -79,10 +77,6 @@ class Cartridge3E : public Cartridge virtual ~Cartridge3E() = default; public: - /** - Reset device to its power-on state - */ - void reset() override; /** Install cartridge in the specified system. Invoked by the system @@ -92,66 +86,6 @@ class Cartridge3E : public Cartridge */ void install(System& system) override; - /** - Install pages for the specified bank in the system. - - @param bank The bank that should be installed in the system - */ - bool bank(uInt16 bank) 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 bankCount() const override; - - /** - Get the size of a bank. - - @param bank The bank to get the size for - @return The bank's size - */ - virtual uInt16 bankSize(uInt16 bank = 0) 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). @@ -189,17 +123,20 @@ class Cartridge3E : public Cartridge bool poke(uInt16 address, uInt8 value) override; private: - // Pointer to a dynamically allocated ROM image of the cartridge - ByteBuffer myImage; + bool checkSwitchBank(uInt16 address, uInt8 value) override; - // RAM contents. For now every ROM gets all 32K of potential RAM - std::array myRAM; + protected: + // log(ROM bank segment size) / log(2) + static constexpr uInt16 BANK_SHIFT = 11; // = 2K = 0x0800 - // Size of the ROM image - size_t mySize{0}; + // The number of RAM banks + static constexpr uInt16 RAM_BANKS = 32; - // Indicates which bank is currently active for the first segment - uInt16 myCurrentBank{0}; + // RAM size + static constexpr size_t RAM_SIZE = RAM_BANKS << (BANK_SHIFT - 1); // = 32K = 0x8000; + + // 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/Cart3EPlus.cxx b/src/emucore/Cart3EPlus.cxx index 27eb94986..6c9fe7c2a 100644 --- a/src/emucore/Cart3EPlus.cxx +++ b/src/emucore/Cart3EPlus.cxx @@ -22,339 +22,63 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Cartridge3EPlus::Cartridge3EPlus(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5), - mySize(size) + : Cartridge3E(image, size, md5, settings) { - // 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()); + myBankShift = BANK_SHIFT; + myRamSize = RAM_SIZE; + myRamBankCount = RAM_BANKS; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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); + // 1st segment in mapped to start bank in CartridgeEnhanced + bank(mySystem->randGenerator().next() % romBankCount(), 1); + bank(mySystem->randGenerator().next() % romBankCount(), 2); + bank(startBank(), 3); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Cartridge3EPlus::install(System& system) +bool Cartridge3EPlus::checkSwitchBank(uInt16 address, uInt8 value) { - mySystem = &system; - - System::PageAccess access(this, System::PageAccessType::READWRITE); - - // 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 -{ - return bankInUse[(address & 0xFFF) >> 10]; // 1K slices -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 Cartridge3EPlus::bankCount() 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 &= ROM_MASK; - 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 76919b9ae..c2ce64cd8 100644 --- a/src/emucore/Cart3EPlus.hxx +++ b/src/emucore/Cart3EPlus.hxx @@ -21,7 +21,7 @@ class System; #include "bspf.hxx" -#include "Cart.hxx" +#include "Cart3E.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 Cartridge3E { friend class Cartridge3EPlusWidget; @@ -62,59 +110,6 @@ class Cartridge3EPlus: public Cartridge /** Reset device to its power-on state */ void reset() override; - /** - 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; - - /** - 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 bankCount() 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 +147,17 @@ 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. - - 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; + // RAM size + static constexpr size_t RAM_SIZE = RAM_BANKS << (BANK_SHIFT - 1); // = 32K = 0x4000; private: // Following constructors and assignment operators not supported diff --git a/src/emucore/Cart3EX.cxx b/src/emucore/Cart3EX.cxx new file mode 100644 index 000000000..c71fdd242 --- /dev/null +++ b/src/emucore/Cart3EX.cxx @@ -0,0 +1,28 @@ +//============================================================================ +// +// 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-2020 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 "Cart3EX.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Cartridge3EX::Cartridge3EX(const ByteBuffer& image, size_t size, + const string& md5, const Settings& settings) + : Cartridge3E(image, size, md5, settings) +{ + // 0xFFFA contains RAM bank count - 1; + myRamBankCount = image[size - 6] + 1; + myRamSize = (myBankSize >> 1) * myRamBankCount; +} diff --git a/src/emucore/Cart3EX.hxx b/src/emucore/Cart3EX.hxx new file mode 100644 index 000000000..7df53593e --- /dev/null +++ b/src/emucore/Cart3EX.hxx @@ -0,0 +1,68 @@ +//============================================================================ +// +// 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-2020 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 CARTRIDGE3EX_HXX +#define CARTRIDGE3EX_HXX + +class System; + +#include "Cart3E.hxx" + +/** + This is an enhanced version of 3E which supports up to 256KB RAM. + + @author Thomas Jentzsch +*/ + +class Cartridge3EX : public Cartridge3E +{ + +public: + /** + Create a new cartridge using the specified image and size + + @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) + */ + Cartridge3EX(const ByteBuffer& image, size_t size, const string& md5, + const Settings& settings); + virtual ~Cartridge3EX() = default; + +public: + /** + Get a descriptor for the device name (used in error checking). + + @return The name of the object + */ + string name() const override { return "Cartridge3EX"; } + +private: + // RAM size + static constexpr size_t RAM_SIZE = RAM_BANKS << (BANK_SHIFT - 1); // = 256K = 0x40000; + +private: + // Following constructors and assignment operators not supported + Cartridge3EX() = delete; + Cartridge3EX(const Cartridge3EX&) = delete; + Cartridge3EX(Cartridge3EX&&) = delete; + Cartridge3EX& operator=(const Cartridge3EX&) = delete; + Cartridge3EX& operator=(Cartridge3EX&&) = delete; +}; + +#endif diff --git a/src/emucore/Cart3F.cxx b/src/emucore/Cart3F.cxx index 2989e88ff..21b21b9c7 100644 --- a/src/emucore/Cart3F.cxx +++ b/src/emucore/Cart3F.cxx @@ -22,177 +22,32 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Cartridge3F::Cartridge3F(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5), - mySize(size) + : CartridgeEnhanced(image, size, md5, settings) { - // 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); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Cartridge3F::reset() -{ - initializeStartBank(bankCount() - 1); - - bank(startBank()); + myBankShift = BANK_SHIFT; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Cartridge3F::install(System& system) { - mySystem = &system; + CartridgeEnhanced::install(system); System::PageAccess access(this, System::PageAccessType::READWRITE); // The hotspot ($3F) is in TIA address space, so we claim it here for(uInt16 addr = 0x00; addr < 0x40; addr += System::PAGE_SIZE) mySystem->setPageAccess(addr, access); - - // Setup the second segment to always point to the last ROM slice - access.type = System::PageAccessType::READ; - for(uInt16 addr = 0x1800; addr < 0x2000; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[(mySize - 2048) + (addr & 0x07FF)]; - access.romAccessBase = &myRomAccessBase[(mySize - 2048) + (addr & 0x07FF)]; - access.romPeekCounter = &myRomAccessCounter[(mySize - 2048) + (addr & 0x07FF)]; - access.romPokeCounter = &myRomAccessCounter[(mySize - 2048) + (addr & 0x07FF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - bank(startBank()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 Cartridge3F::peek(uInt16 address) +bool Cartridge3F::checkSwitchBank(uInt16 address, uInt8 value) { - address &= 0x0FFF; - - if(address < 0x0800) - return myImage[(address & 0x07FF) + (myCurrentBank << 11)]; - else - return myImage[(address & 0x07FF) + mySize - 2048]; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool Cartridge3F::poke(uInt16 address, uInt8 value) -{ - address &= 0x0FFF; - // Switch banks if necessary if(address <= 0x003F) - bank(value); - - // Handle TIA space that we claimed above - mySystem->tia().poke(address, value); - + { + // Make sure the bank they're asking for is reasonable + bank(value % romBankCount(), 0); + return true; + } return false; } - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool Cartridge3F::bank(uInt16 bank) -{ - if(bankLocked()) - return false; - - // Make sure the bank they're asking for is reasonable - if((uInt32(bank) << 11) < mySize) - { - myCurrentBank = bank; - } - else - { - // Oops, the bank they're asking for isn't valid so let's wrap it - // around to a valid bank number - myCurrentBank = bank % (mySize >> 11); - } - - uInt32 offset = myCurrentBank << 11; - - // Setup the page access methods for the current bank - System::PageAccess access(this, System::PageAccessType::READ); - - // Map ROM image into the system - for(uInt16 addr = 0x1000; addr < 0x1800; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[offset + (addr & 0x07FF)]; - access.romAccessBase = &myRomAccessBase[offset + (addr & 0x07FF)]; - access.romPeekCounter = &myRomAccessCounter[offset + (addr & 0x07FF)]; - access.romPokeCounter = &myRomAccessCounter[offset + (addr & 0x07FF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 Cartridge3F::getBank(uInt16 address) const -{ - if (address & 0x800) - return uInt16((mySize >> 11) - 1); // 2K slices, fixed bank - else - return myCurrentBank; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 Cartridge3F::bankCount() const -{ - return uInt16(mySize >> 11); // 2K slices -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool Cartridge3F::patch(uInt16 address, uInt8 value) -{ - address &= 0x0FFF; - - if(address < 0x0800) - myImage[(address & 0x07FF) + (myCurrentBank << 11)] = value; - else - myImage[(address & 0x07FF) + mySize - 2048] = value; - - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* Cartridge3F::getImage(size_t& size) const -{ - size = mySize; - return myImage.get(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool Cartridge3F::save(Serializer& out) const -{ - try - { - out.putShort(myCurrentBank); - } - catch(...) - { - cerr << "ERROR: Cartridge3F::save" << endl; - return false; - } - - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool Cartridge3F::load(Serializer& in) -{ - try - { - myCurrentBank = in.getShort(); - } - catch(...) - { - cerr << "ERROR: Cartridge3F::load" << endl; - return false; - } - - // Now, go to the current bank - bank(myCurrentBank); - - return true; -} diff --git a/src/emucore/Cart3F.hxx b/src/emucore/Cart3F.hxx index cb75bcd88..7bdcbd99c 100644 --- a/src/emucore/Cart3F.hxx +++ b/src/emucore/Cart3F.hxx @@ -21,7 +21,7 @@ class System; #include "bspf.hxx" -#include "Cart.hxx" +#include "CartEnhanced.hxx" #ifdef DEBUGGER_SUPPORT #include "Cart3FWidget.hxx" #endif @@ -36,9 +36,9 @@ class System; $00 to $3F will change banks. Although, the Tigervision games only used 8K this bankswitching scheme supports up to 512K. - @author Bradford W. Mott + @author Bradford W. Mott, Thomas Jentzsch */ -class Cartridge3F : public Cartridge +class Cartridge3F : public CartridgeEnhanced { friend class Cartridge3FWidget; @@ -56,11 +56,6 @@ class Cartridge3F : public Cartridge virtual ~Cartridge3F() = default; public: - /** - Reset device to its power-on state - */ - void reset() override; - /** Install cartridge in the specified system. Invoked by the system when the cartridge is attached to it. @@ -69,57 +64,6 @@ class Cartridge3F : public Cartridge */ void install(System& system) override; - /** - Install pages for the specified bank in the system. - - @param bank The bank that should be installed in the system - */ - bool bank(uInt16 bank) 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 bankCount() 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). @@ -140,32 +84,14 @@ class Cartridge3F : public Cartridge } #endif - public: - /** - Get the byte at the specified address + private: + bool checkSwitchBank(uInt16 address, uInt8 value) override; - @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; + uInt16 hotspot() const override { return 0x003F; } private: - // Pointer to a dynamically allocated ROM image of the cartridge - ByteBuffer myImage; - - // Size of the ROM image - size_t mySize{0}; - - // Indicates which bank is currently active for the first segment - uInt16 myCurrentBank{0}; + // log(ROM bank segment size) / log(2) + static constexpr uInt16 BANK_SHIFT = 11; // = 2K = 0x0800 private: // Following constructors and assignment operators not supported diff --git a/src/emucore/Cart4K.cxx b/src/emucore/Cart4K.cxx index 4f4aab979..9e684afad 100644 --- a/src/emucore/Cart4K.cxx +++ b/src/emucore/Cart4K.cxx @@ -21,48 +21,6 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Cartridge4K::Cartridge4K(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5) + : CartridgeEnhanced(image, size, md5, settings) { - // Copy the ROM image into my buffer - std::copy_n(image.get(), std::min(myImage.size(), size), myImage.begin()); - createRomAccessArrays(myImage.size()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Cartridge4K::reset() -{ - myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Cartridge4K::install(System& system) -{ - mySystem = &system; - - // Map ROM image into the system - // Note that we don't need our own peek/poke methods, since the mapping - // takes care of the entire address space - System::PageAccess access(this, System::PageAccessType::READ); - for(uInt16 addr = 0x1000; addr < 0x2000; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[addr & 0x0FFF]; - access.romAccessBase = &myRomAccessBase[addr & 0x0FFF]; - access.romPeekCounter = &myRomAccessCounter[addr & 0x0FFF]; - access.romPokeCounter = &myRomAccessCounter[(addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool Cartridge4K::patch(uInt16 address, uInt8 value) -{ - myImage[address & 0x0FFF] = value; - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* Cartridge4K::getImage(size_t& size) const -{ - size = myImage.size(); - return myImage.data(); } diff --git a/src/emucore/Cart4K.hxx b/src/emucore/Cart4K.hxx index 2b42516f5..1a7c428f4 100644 --- a/src/emucore/Cart4K.hxx +++ b/src/emucore/Cart4K.hxx @@ -21,7 +21,7 @@ class System; #include "bspf.hxx" -#include "Cart.hxx" +#include "CartEnhanced.hxx" #ifdef DEBUGGER_SUPPORT #include "Cart4KWidget.hxx" #endif @@ -30,9 +30,9 @@ class System; This is the standard Atari 4K cartridge. These cartridges are not bankswitched. - @author Bradford W. Mott + @author Bradford W. Mott, Thomas Jentzsch */ -class Cartridge4K : public Cartridge +class Cartridge4K : public CartridgeEnhanced { friend class Cartridge4KWidget; @@ -50,52 +50,6 @@ class Cartridge4K : public Cartridge virtual ~Cartridge4K() = default; public: - /** - Reset cartridge to its power-on state - */ - void reset() override; - - /** - 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; - - /** - 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 { return true; } - - /** - 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 { return true; } - /** Get a descriptor for the device name (used in error checking). @@ -115,16 +69,8 @@ class Cartridge4K : public Cartridge } #endif - /** - Get the byte at the specified address. - - @return The byte at the specified address - */ - uInt8 peek(uInt16 address) override { return myImage[address & 0x0FFF]; } - private: - // The 4K ROM image for the cartridge - std::array myImage; + bool checkSwitchBank(uInt16 address, uInt8 value = 0) override { return false; } private: // Following constructors and assignment operators not supported diff --git a/src/emucore/Cart4KSC.cxx b/src/emucore/Cart4KSC.cxx index 09e043c94..92d2e946e 100644 --- a/src/emucore/Cart4KSC.cxx +++ b/src/emucore/Cart4KSC.cxx @@ -21,137 +21,7 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Cartridge4KSC::Cartridge4KSC(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5) + : Cartridge4K(image, size, md5, settings) { - // Copy the ROM image into my buffer - std::copy_n(image.get(), std::min(myImage.size(), size), myImage.begin()); - createRomAccessArrays(myImage.size()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Cartridge4KSC::reset() -{ - initializeRAM(myRAM.data(), myRAM.size()); - - myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Cartridge4KSC::install(System& system) -{ - mySystem = &system; - - 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 - access.type = System::PageAccessType::WRITE; - for(uInt16 addr = 0x1000; addr < 0x1080; addr += System::PAGE_SIZE) - { - access.romAccessBase = &myRomAccessBase[addr & 0x007F]; - mySystem->setPageAccess(addr, access); - } - - // Set the page accessing method for the RAM reading pages - access.type = System::PageAccessType::READ; - for(uInt16 addr = 0x1080; addr < 0x1100; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myRAM[addr & 0x007F]; - access.romAccessBase = &myRomAccessBase[0x80 + (addr & 0x007F)]; - mySystem->setPageAccess(addr, access); - } - - // Map ROM image into the system - for(uInt16 addr = 0x1100; addr < 0x2000; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[addr & 0x0FFF]; - access.romAccessBase = &myRomAccessBase[addr & 0x0FFF]; - mySystem->setPageAccess(addr, access); - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 Cartridge4KSC::peek(uInt16 address) -{ - // The only way we can get to this method is if we attempt to read from - // the write port (0xF000 - 0xF07F, 128 bytes), in which case an - // unwanted write is potentially triggered - return peekRAM(myRAM[address & 0x007F], address); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool Cartridge4KSC::poke(uInt16 address, uInt8 value) -{ - if (!(address & 0x080)) - { - pokeRAM(myRAM[address & 0x007F], 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; - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool Cartridge4KSC::patch(uInt16 address, uInt8 value) -{ - address &= 0x0FFF; - - if(address < 0x0100) - { - // Normally, a write to the read port won't do anything - // However, the patch command is special in that ignores such - // cart restrictions - myRAM[address & 0x007F] = value; - } - else - myImage[address & 0xFFF] = value; - - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* Cartridge4KSC::getImage(size_t& size) const -{ - size = myImage.size(); - return myImage.data(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool Cartridge4KSC::save(Serializer& out) const -{ - try - { - out.putByteArray(myRAM.data(), myRAM.size()); - } - catch(...) - { - cerr << "ERROR: Cartridge4KSC::save" << endl; - return false; - } - - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool Cartridge4KSC::load(Serializer& in) -{ - try - { - in.getByteArray(myRAM.data(), myRAM.size()); - } - catch(...) - { - cerr << "ERROR: Cartridge4KSC::load" << endl; - return false; - } - - return true; + myRamSize = RAM_SIZE; } diff --git a/src/emucore/Cart4KSC.hxx b/src/emucore/Cart4KSC.hxx index 4d596f47a..3f28de9ce 100644 --- a/src/emucore/Cart4KSC.hxx +++ b/src/emucore/Cart4KSC.hxx @@ -21,7 +21,7 @@ class System; #include "bspf.hxx" -#include "Cart.hxx" +#include "Cart4K.hxx" #ifdef DEBUGGER_SUPPORT #include "Cart4KSCWidget.hxx" #endif @@ -29,9 +29,11 @@ class System; /** Cartridge class used for 4K games with 128 bytes of RAM. RAM read port is $1080 - $10FF, write port is $1000 - $107F. + + @author Stephen Anthony, Thomas Jentzsch */ -class Cartridge4KSC : public Cartridge +class Cartridge4KSC : public Cartridge4K { friend class Cartridge4KSCWidget; @@ -49,52 +51,6 @@ class Cartridge4KSC : public Cartridge virtual ~Cartridge4KSC() = default; public: - /** - Reset device to its power-on state - */ - void reset() override; - - /** - 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; - - /** - 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). @@ -114,29 +70,9 @@ class Cartridge4KSC : public Cartridge } #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: - // The 4K ROM image of the cartridge - std::array myImage; - - // The 128 bytes of RAM - std::array myRAM; + // RAM size + static constexpr size_t RAM_SIZE = 0x80; private: // Following constructors and assignment operators not supported diff --git a/src/emucore/CartAR.cxx b/src/emucore/CartAR.cxx index 5201a14df..2d8b204b6 100644 --- a/src/emucore/CartAR.cxx +++ b/src/emucore/CartAR.cxx @@ -406,7 +406,7 @@ uInt16 CartridgeAR::getBank(uInt16) const } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeAR::bankCount() const +uInt16 CartridgeAR::romBankCount() const { return 32; } diff --git a/src/emucore/CartAR.hxx b/src/emucore/CartAR.hxx index 6fda35f38..0bc74a291 100644 --- a/src/emucore/CartAR.hxx +++ b/src/emucore/CartAR.hxx @@ -87,7 +87,7 @@ class CartridgeAR : public Cartridge /** Query the number of banks supported by the cartridge. */ - uInt16 bankCount() const override; + uInt16 romBankCount() const override; /** Patch the cartridge ROM. diff --git a/src/emucore/CartBF.cxx b/src/emucore/CartBF.cxx index 91d13cbb7..ce3c53198 100644 --- a/src/emucore/CartBF.cxx +++ b/src/emucore/CartBF.cxx @@ -21,145 +21,22 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeBF::CartridgeBF(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5) + : CartridgeEnhanced(image, size, md5, settings) { - // Copy the ROM image into my buffer - std::copy_n(image.get(), std::min(myImage.size(), size), myImage.begin()); - createRomAccessArrays(myImage.size()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeBF::reset() -{ - initializeStartBank(1); - - // Upon reset we switch to the startup bank - bank(startBank()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeBF::install(System& system) -{ - mySystem = &system; - - // Install pages for the startup bank - bank(startBank()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeBF::peek(uInt16 address) +bool CartridgeBF::checkSwitchBank(uInt16 address, uInt8) { // Due to the way addressing is set up, we will only get here if the // address is in the hotspot range ($1F80 - $1FFF) - address &= 0x0FFF; + address &= ROM_MASK; // Switch banks if necessary if((address >= 0x0F80) && (address <= 0x0FBF)) + { bank(address - 0x0F80); - - return myImage[myBankOffset + address]; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeBF::poke(uInt16 address, uInt8) -{ - // Due to the way addressing is set up, we will only get here if the - // address is in the hotspot range ($1F80 - $1FFF) - address &= 0x0FFF; - - // Switch banks if necessary - if((address >= 0x0F80) && (address <= 0x0FBF)) - bank(address - 0x0F80); - + return true; + } return false; } - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeBF::bank(uInt16 bank) -{ - if(bankLocked()) return false; - - // Remember what bank we're in - myBankOffset = bank << 12; - - System::PageAccess access(this, System::PageAccessType::READ); - - // Set the page accessing methods for the hot spots - for(uInt16 addr = (0x1F80 & ~System::PAGE_MASK); addr < 0x2000; - addr += System::PAGE_SIZE) - { - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - mySystem->setPageAccess(addr, access); - } - - // Setup the page access methods for the current bank - for(uInt16 addr = 0x1000; addr < static_cast(0x1F80U & ~System::PAGE_MASK); - addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[myBankOffset + (addr & 0x0FFF)]; - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - mySystem->setPageAccess(addr, access); - } - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeBF::getBank(uInt16) const -{ - return myBankOffset >> 12; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeBF::bankCount() const -{ - return 64; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeBF::patch(uInt16 address, uInt8 value) -{ - myImage[myBankOffset + (address & 0x0FFF)] = value; - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* CartridgeBF::getImage(size_t& size) const -{ - size = myImage.size(); - return myImage.data(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeBF::save(Serializer& out) const -{ - try - { - out.putInt(myBankOffset); - } - catch(...) - { - cerr << "ERROR: CartridgeBF::save" << endl; - return false; - } - - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeBF::load(Serializer& in) -{ - try - { - myBankOffset = in.getInt(); - } - catch(...) - { - cerr << "ERROR: CartridgeBF::load" << endl; - return false; - } - - // Remember what bank we were in - bank(myBankOffset >> 12); - - return true; -} diff --git a/src/emucore/CartBF.hxx b/src/emucore/CartBF.hxx index 158f43a45..23c51f760 100644 --- a/src/emucore/CartBF.hxx +++ b/src/emucore/CartBF.hxx @@ -21,7 +21,7 @@ class System; #include "bspf.hxx" -#include "Cart.hxx" +#include "CartEnhanced.hxx" #ifdef DEBUGGER_SUPPORT #include "CartBFWidget.hxx" #endif @@ -31,9 +31,9 @@ class System; There are 64 4K banks (total of 256K ROM). Accessing $1F80 - $1FBF switches to each bank. - @author Mike Saarna + @author Mike Saarna, Thomas Jentzsch */ -class CartridgeBF : public Cartridge +class CartridgeBF : public CartridgeEnhanced { friend class CartridgeBFWidget; @@ -51,71 +51,6 @@ class CartridgeBF : public Cartridge virtual ~CartridgeBF() = default; public: - /** - Reset device to its power-on state - */ - void reset() override; - - /** - 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; - - /** - Install pages for the specified bank in the system. - - @param bank The bank that should be installed in the system - */ - bool bank(uInt16 bank) 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 bankCount() 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). @@ -135,29 +70,12 @@ class CartridgeBF : public Cartridge } #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: - // The 256K ROM image of the cartridge - std::array myImage; + bool checkSwitchBank(uInt16 address, uInt8 value = 0) override; - // Indicates the offset into the ROM image (aligns to current bank) - uInt32 myBankOffset{0}; + uInt16 hotspot() const override { return 0x1F80; } + + uInt16 getStartBank() const override { return 1; } private: // Following constructors and assignment operators not supported diff --git a/src/emucore/CartBFSC.cxx b/src/emucore/CartBFSC.cxx index d0d33c79f..bd9793893 100644 --- a/src/emucore/CartBFSC.cxx +++ b/src/emucore/CartBFSC.cxx @@ -21,197 +21,7 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeBFSC::CartridgeBFSC(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5) + : CartridgeBF(image, size, md5, settings) { - // Copy the ROM image into my buffer - std::copy_n(image.get(), std::min(myImage.size(), size), myImage.begin()); - createRomAccessArrays(myImage.size()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeBFSC::reset() -{ - initializeRAM(myRAM.data(), myRAM.size()); - initializeStartBank(15); - - // Upon reset we switch to the startup bank - bank(startBank()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeBFSC::install(System& system) -{ - mySystem = &system; - - 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 - access.type = System::PageAccessType::WRITE; - for(uInt16 addr = 0x1000; addr < 0x1080; addr += System::PAGE_SIZE) - { - access.romAccessBase = &myRomAccessBase[addr & 0x007F]; - mySystem->setPageAccess(addr, access); - } - - // Set the page accessing method for the RAM reading pages - access.type = System::PageAccessType::READ; - for(uInt16 addr = 0x1080; addr < 0x1100; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myRAM[addr & 0x007F]; - access.romAccessBase = &myRomAccessBase[0x80 + (addr & 0x007F)]; - mySystem->setPageAccess(addr, access); - } - - // Install pages for the startup bank - bank(startBank()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeBFSC::peek(uInt16 address) -{ - uInt16 peekAddress = address; - address &= 0x0FFF; - - // Switch banks if necessary - if((address >= 0x0F80) && (address <= 0x0FBF)) - bank(address - 0x0F80); - - if(address < 0x0080) // Write port is at 0xF000 - 0xF07F (128 bytes) - return peekRAM(myRAM[address], peekAddress); - else - return myImage[myBankOffset + address]; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeBFSC::poke(uInt16 address, uInt8 value) -{ - uInt16 pokeAddress = address; - address &= 0x0FFF; - - // Switch banks if necessary - if((address >= 0x0F80) && (address <= 0x0FBF)) - { - bank(address - 0x0F80); - return false; - } - - if (!(address & 0x080)) - { - pokeRAM(myRAM[address & 0x007F], pokeAddress, value); - return true; - } - else - { - // Writing to the read port should be ignored, but trigger a break if option enabled - uInt8 dummy; - - pokeRAM(dummy, pokeAddress, value); - myRamWriteAccess = pokeAddress; - return false; - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeBFSC::bank(uInt16 bank) -{ - if(bankLocked()) return false; - - // Remember what bank we're in - myBankOffset = bank << 12; - - System::PageAccess access(this, System::PageAccessType::READ); - - // Set the page accessing methods for the hot spots - for(uInt16 addr = (0x1F80 & ~System::PAGE_MASK); addr < 0x2000; - addr += System::PAGE_SIZE) - { - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - mySystem->setPageAccess(addr, access); - } - - // Setup the page access methods for the current bank - for(uInt16 addr = 0x1100; addr < static_cast(0x1F80U & ~System::PAGE_MASK); - addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[myBankOffset + (addr & 0x0FFF)]; - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - mySystem->setPageAccess(addr, access); - } - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeBFSC::getBank(uInt16) const -{ - return myBankOffset >> 12; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeBFSC::bankCount() const -{ - return 64; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeBFSC::patch(uInt16 address, uInt8 value) -{ - address &= 0x0FFF; - - if(address < 0x0100) - { - // Normally, a write to the read port won't do anything - // However, the patch command is special in that ignores such - // cart restrictions - myRAM[address & 0x007F] = value; - } - else - myImage[myBankOffset + address] = value; - - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* CartridgeBFSC::getImage(size_t& size) const -{ - size = myImage.size(); - return myImage.data(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeBFSC::save(Serializer& out) const -{ - try - { - out.putInt(myBankOffset); - out.putByteArray(myRAM.data(), myRAM.size()); - } - catch(...) - { - cerr << "ERROR: CartridgeBFSC::save" << endl; - return false; - } - - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeBFSC::load(Serializer& in) -{ - try - { - myBankOffset = in.getInt(); - in.getByteArray(myRAM.data(), myRAM.size()); - } - catch(...) - { - cerr << "ERROR: CartridgeBFSC::load" << endl; - return false; - } - - // Remember what bank we were in - bank(myBankOffset >> 12); - - return true; + myRamSize = RAM_SIZE; } diff --git a/src/emucore/CartBFSC.hxx b/src/emucore/CartBFSC.hxx index b4cb72aad..9bb22aa0a 100644 --- a/src/emucore/CartBFSC.hxx +++ b/src/emucore/CartBFSC.hxx @@ -21,7 +21,7 @@ class System; #include "bspf.hxx" -#include "Cart.hxx" +#include "CartBF.hxx" #ifdef DEBUGGER_SUPPORT #include "CartBFSCWidget.hxx" #endif @@ -31,9 +31,9 @@ class System; Accessing $1F80 - $1FBF switches to each bank. RAM read port is $1080 - $10FF, write port is $1000 - $107F. - @author Stephen Anthony + @author Stephen Anthony, Thomas Jentzsch */ -class CartridgeBFSC : public Cartridge +class CartridgeBFSC : public CartridgeBF { friend class CartridgeBFSCWidget; @@ -50,72 +50,6 @@ class CartridgeBFSC : public Cartridge const Settings& settings); virtual ~CartridgeBFSC() = default; - public: - /** - Reset device to its power-on state - */ - void reset() override; - - /** - 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; - - /** - Install pages for the specified bank in the system. - - @param bank The bank that should be installed in the system - */ - bool bank(uInt16 bank) 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 bankCount() 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). @@ -135,32 +69,12 @@ class CartridgeBFSC : public Cartridge } #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: + uInt16 getStartBank() const override { return 15; } private: - // The 256K ROM image of the cartridge - std::array myImage; - - // The 128 bytes of RAM - std::array myRAM; - - // Indicates the offset into the ROM image (aligns to current bank) - uInt32 myBankOffset{0}; + // RAM size + static constexpr uInt32 RAM_SIZE = 0x80; private: // Following constructors and assignment operators not supported diff --git a/src/emucore/CartBUS.cxx b/src/emucore/CartBUS.cxx index 2c2cd803a..e2eb9b74a 100644 --- a/src/emucore/CartBUS.cxx +++ b/src/emucore/CartBUS.cxx @@ -56,16 +56,16 @@ CartridgeBUS::CartridgeBUS(const ByteBuffer& image, size_t size, myProgramImage = myImage.data() + 4_KB; // Pointer to BUS driver in RAM - myBusDriverImage = myBUSRAM.data(); + myDriverImage = myRAM.data(); // Pointer to the display RAM - myDisplayImage = myBUSRAM.data() + DSRAM; + myDisplayImage = myRAM.data() + DSRAM; // Create Thumbulator ARM emulator bool devSettings = settings.getBool("dev.settings"); myThumbEmulator = make_unique( reinterpret_cast(myImage.data()), - reinterpret_cast(myBUSRAM.data()), + reinterpret_cast(myRAM.data()), static_cast(myImage.size()), devSettings ? settings.getBool("dev.thumb.trapfatal") : false, Thumbulator::ConfigureFor::BUS, this ); @@ -76,7 +76,7 @@ CartridgeBUS::CartridgeBUS(const ByteBuffer& image, size_t size, // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void CartridgeBUS::reset() { - initializeRAM(myBUSRAM.data() + 2_KB, 6_KB); + initializeRAM(myRAM.data() + 2_KB, 6_KB); // BUS always starts in bank 6 initializeStartBank(6); @@ -95,7 +95,7 @@ void CartridgeBUS::reset() void CartridgeBUS::setInitialState() { // Copy initial BUS driver to Harmony RAM - std::copy_n(myImage.begin(), 2_KB, myBusDriverImage); + std::copy_n(myImage.begin(), 2_KB, myDriverImage); myMusicWaveformSize.fill(27); @@ -254,7 +254,7 @@ uInt8 CartridgeBUS::peek(uInt16 address) if (sampleaddress < 0x8000) peekvalue = myImage[sampleaddress]; else if (sampleaddress >= 0x40000000 && sampleaddress < 0x40002000) // check for RAM - peekvalue = myBUSRAM[sampleaddress - 0x40000000]; + peekvalue = myRAM[sampleaddress - 0x40000000]; else peekvalue = 0; @@ -457,7 +457,7 @@ uInt16 CartridgeBUS::getBank(uInt16) const } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeBUS::bankCount() const +uInt16 CartridgeBUS::romBankCount() const { return 7; } @@ -552,7 +552,7 @@ bool CartridgeBUS::save(Serializer& out) const out.putShort(myBankOffset); // Harmony RAM - out.putByteArray(myBUSRAM.data(), myBUSRAM.size()); + out.putByteArray(myRAM.data(), myRAM.size()); // Addresses for bus override logic out.putShort(myBusOverdriveAddress); @@ -593,7 +593,7 @@ bool CartridgeBUS::load(Serializer& in) myBankOffset = in.getShort(); // Harmony RAM - in.getByteArray(myBUSRAM.data(), myBUSRAM.size()); + in.getByteArray(myRAM.data(), myRAM.size()); // Addresses for bus override logic myBusOverdriveAddress = in.getShort(); @@ -633,40 +633,40 @@ uInt32 CartridgeBUS::getDatastreamPointer(uInt8 index) const { // index &= 0x0f; - return myBUSRAM[DSxPTR + index*4 + 0] + // low byte - (myBUSRAM[DSxPTR + index*4 + 1] << 8) + - (myBUSRAM[DSxPTR + index*4 + 2] << 16) + - (myBUSRAM[DSxPTR + index*4 + 3] << 24) ; // high byte + return myRAM[DSxPTR + index*4 + 0] + // low byte + (myRAM[DSxPTR + index*4 + 1] << 8) + + (myRAM[DSxPTR + index*4 + 2] << 16) + + (myRAM[DSxPTR + index*4 + 3] << 24) ; // high byte } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void CartridgeBUS::setDatastreamPointer(uInt8 index, uInt32 value) { // index &= 0x0f; - myBUSRAM[DSxPTR + index*4 + 0] = value & 0xff; // low byte - myBUSRAM[DSxPTR + index*4 + 1] = (value >> 8) & 0xff; - myBUSRAM[DSxPTR + index*4 + 2] = (value >> 16) & 0xff; - myBUSRAM[DSxPTR + index*4 + 3] = (value >> 24) & 0xff; // high byte + myRAM[DSxPTR + index*4 + 0] = value & 0xff; // low byte + myRAM[DSxPTR + index*4 + 1] = (value >> 8) & 0xff; + myRAM[DSxPTR + index*4 + 2] = (value >> 16) & 0xff; + myRAM[DSxPTR + index*4 + 3] = (value >> 24) & 0xff; // high byte } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uInt32 CartridgeBUS::getDatastreamIncrement(uInt8 index) const { // index &= 0x0f; - return myBUSRAM[DSxINC + index*4 + 0] + // low byte - (myBUSRAM[DSxINC + index*4 + 1] << 8) + - (myBUSRAM[DSxINC + index*4 + 2] << 16) + - (myBUSRAM[DSxINC + index*4 + 3] << 24) ; // high byte + return myRAM[DSxINC + index*4 + 0] + // low byte + (myRAM[DSxINC + index*4 + 1] << 8) + + (myRAM[DSxINC + index*4 + 2] << 16) + + (myRAM[DSxINC + index*4 + 3] << 24) ; // high byte } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uInt32 CartridgeBUS::getAddressMap(uInt8 index) const { // index &= 0x0f; - return myBUSRAM[DSMAPS + index*4 + 0] + // low byte - (myBUSRAM[DSMAPS + index*4 + 1] << 8) + - (myBUSRAM[DSMAPS + index*4 + 2] << 16) + - (myBUSRAM[DSMAPS + index*4 + 3] << 24) ; // high byte + return myRAM[DSMAPS + index*4 + 0] + // low byte + (myRAM[DSMAPS + index*4 + 1] << 8) + + (myRAM[DSMAPS + index*4 + 2] << 16) + + (myRAM[DSMAPS + index*4 + 3] << 24) ; // high byte } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -686,10 +686,10 @@ uInt32 CartridgeBUS::getWaveform(uInt8 index) const uInt32 result; - result = myBUSRAM[WAVEFORM + index*4 + 0] + // low byte - (myBUSRAM[WAVEFORM + index*4 + 1] << 8) + - (myBUSRAM[WAVEFORM + index*4 + 2] << 16) + - (myBUSRAM[WAVEFORM + index*4 + 3] << 24); // high byte + result = myRAM[WAVEFORM + index*4 + 0] + // low byte + (myRAM[WAVEFORM + index*4 + 1] << 8) + + (myRAM[WAVEFORM + index*4 + 2] << 16) + + (myRAM[WAVEFORM + index*4 + 3] << 24); // high byte result -= 0x40000800; @@ -704,10 +704,10 @@ uInt32 CartridgeBUS::getSample() { uInt32 result; - result = myBUSRAM[WAVEFORM + 0] + // low byte - (myBUSRAM[WAVEFORM + 1] << 8) + - (myBUSRAM[WAVEFORM + 2] << 16) + - (myBUSRAM[WAVEFORM + 3] << 24); // high byte + result = myRAM[WAVEFORM + 0] + // low byte + (myRAM[WAVEFORM + 1] << 8) + + (myRAM[WAVEFORM + 2] << 16) + + (myRAM[WAVEFORM + 3] << 24); // high byte return result; } @@ -722,10 +722,10 @@ uInt32 CartridgeBUS::getWaveformSize(uInt8 index) const void CartridgeBUS::setAddressMap(uInt8 index, uInt32 value) { // index &= 0x0f; - myBUSRAM[DSMAPS + index*4 + 0] = value & 0xff; // low byte - myBUSRAM[DSMAPS + index*4 + 1] = (value >> 8) & 0xff; - myBUSRAM[DSMAPS + index*4 + 2] = (value >> 16) & 0xff; - myBUSRAM[DSMAPS + index*4 + 3] = (value >> 24) & 0xff; // high byte + myRAM[DSMAPS + index*4 + 0] = value & 0xff; // low byte + myRAM[DSMAPS + index*4 + 1] = (value >> 8) & 0xff; + myRAM[DSMAPS + index*4 + 2] = (value >> 16) & 0xff; + myRAM[DSMAPS + index*4 + 3] = (value >> 24) & 0xff; // high byte } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/emucore/CartBUS.hxx b/src/emucore/CartBUS.hxx index d00e90133..3ab40f7ec 100644 --- a/src/emucore/CartBUS.hxx +++ b/src/emucore/CartBUS.hxx @@ -98,7 +98,7 @@ class CartridgeBUS : public Cartridge /** Query the number of banks supported by the cartridge. */ - uInt16 bankCount() const override; + uInt16 romBankCount() const override; /** Patch the cartridge ROM. @@ -220,13 +220,13 @@ class CartridgeBUS : public Cartridge uInt8* myDisplayImage{nullptr}; // Pointer to the 2K BUS driver image in RAM - uInt8* myBusDriverImage{nullptr}; + uInt8* myDriverImage{nullptr}; // The BUS 8k RAM image, used as: // $0000 - 2K BUS driver // $0800 - 4K Display Data // $1800 - 2K C Variable & Stack - std::array myBUSRAM; + std::array myRAM; // Pointer to the Thumb ARM emulator object unique_ptr myThumbEmulator; diff --git a/src/emucore/CartCDF.cxx b/src/emucore/CartCDF.cxx index b182634a3..6f9a5f4f7 100644 --- a/src/emucore/CartCDF.cxx +++ b/src/emucore/CartCDF.cxx @@ -72,10 +72,10 @@ CartridgeCDF::CartridgeCDF(const ByteBuffer& image, size_t size, myProgramImage = myImage.data() + 4_KB; // Pointer to CDF driver in RAM - myBusDriverImage = myCDFRAM.data(); + myDriverImage = myRAM.data(); // Pointer to the display RAM - myDisplayImage = myCDFRAM.data() + DSRAM; + myDisplayImage = myRAM.data() + DSRAM; setupVersion(); @@ -83,7 +83,7 @@ CartridgeCDF::CartridgeCDF(const ByteBuffer& image, size_t size, bool devSettings = settings.getBool("dev.settings"); myThumbEmulator = make_unique( reinterpret_cast(myImage.data()), - reinterpret_cast(myCDFRAM.data()), + reinterpret_cast(myRAM.data()), static_cast(myImage.size()), devSettings ? settings.getBool("dev.thumb.trapfatal") : false, thumulatorConfiguration(myCDFSubtype), this); @@ -93,7 +93,7 @@ CartridgeCDF::CartridgeCDF(const ByteBuffer& image, size_t size, // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void CartridgeCDF::reset() { - initializeRAM(myCDFRAM.data()+2_KB, myCDFRAM.size()-2_KB); + initializeRAM(myRAM.data()+2_KB, myRAM.size()-2_KB); // CDF always starts in bank 6 initializeStartBank(6); @@ -111,7 +111,7 @@ void CartridgeCDF::reset() void CartridgeCDF::setInitialState() { // Copy initial CDF driver to Harmony RAM - std::copy_n(myImage.begin(), 2_KB, myBusDriverImage); + std::copy_n(myImage.begin(), 2_KB, myDriverImage); myMusicWaveformSize.fill(27); @@ -255,7 +255,7 @@ uInt8 CartridgeCDF::peek(uInt16 address) if (sampleaddress < 0x8000) peekvalue = myImage[sampleaddress]; else if (sampleaddress >= 0x40000000 && sampleaddress < 0x40002000) // check for RAM - peekvalue = myCDFRAM[sampleaddress - 0x40000000]; + peekvalue = myRAM[sampleaddress - 0x40000000]; else peekvalue = 0; @@ -430,7 +430,7 @@ uInt16 CartridgeCDF::getBank(uInt16) const } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeCDF::bankCount() const +uInt16 CartridgeCDF::romBankCount() const { return 7; } @@ -508,7 +508,7 @@ bool CartridgeCDF::save(Serializer& out) const out.putShort(myJMPoperandAddress); // Harmony RAM - out.putByteArray(myCDFRAM.data(), myCDFRAM.size()); + out.putByteArray(myRAM.data(), myRAM.size()); // Audio info out.putIntArray(myMusicCounters.data(), myMusicCounters.size()); @@ -548,7 +548,7 @@ bool CartridgeCDF::load(Serializer& in) myJMPoperandAddress = in.getShort(); // Harmony RAM - in.getByteArray(myCDFRAM.data(), myCDFRAM.size()); + in.getByteArray(myRAM.data(), myRAM.size()); // Audio info in.getIntArray(myMusicCounters.data(), myMusicCounters.size()); @@ -577,10 +577,10 @@ uInt32 CartridgeCDF::getDatastreamPointer(uInt8 index) const { uInt16 address = myDatastreamBase + index * 4; - return myCDFRAM[address + 0] + // low byte - (myCDFRAM[address + 1] << 8) + - (myCDFRAM[address + 2] << 16) + - (myCDFRAM[address + 3] << 24) ; // high byte + return myRAM[address + 0] + // low byte + (myRAM[address + 1] << 8) + + (myRAM[address + 2] << 16) + + (myRAM[address + 3] << 24) ; // high byte } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -588,10 +588,10 @@ void CartridgeCDF::setDatastreamPointer(uInt8 index, uInt32 value) { uInt16 address = myDatastreamBase + index * 4; - myCDFRAM[address + 0] = value & 0xff; // low byte - myCDFRAM[address + 1] = (value >> 8) & 0xff; - myCDFRAM[address + 2] = (value >> 16) & 0xff; - myCDFRAM[address + 3] = (value >> 24) & 0xff; // high byte + myRAM[address + 0] = value & 0xff; // low byte + myRAM[address + 1] = (value >> 8) & 0xff; + myRAM[address + 2] = (value >> 16) & 0xff; + myRAM[address + 3] = (value >> 24) & 0xff; // high byte } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -599,10 +599,10 @@ uInt32 CartridgeCDF::getDatastreamIncrement(uInt8 index) const { uInt16 address = myDatastreamIncrementBase + index * 4; - return myCDFRAM[address + 0] + // low byte - (myCDFRAM[address + 1] << 8) + - (myCDFRAM[address + 2] << 16) + - (myCDFRAM[address + 3] << 24) ; // high byte + return myRAM[address + 0] + // low byte + (myRAM[address + 1] << 8) + + (myRAM[address + 2] << 16) + + (myRAM[address + 3] << 24) ; // high byte } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -610,10 +610,10 @@ uInt32 CartridgeCDF::getWaveform(uInt8 index) const { uInt16 address = myWaveformBase + index * 4; - uInt32 result = myCDFRAM[address + 0] + // low byte - (myCDFRAM[address + 1] << 8) + - (myCDFRAM[address + 2] << 16) + - (myCDFRAM[address + 3] << 24); // high byte + uInt32 result = myRAM[address + 0] + // low byte + (myRAM[address + 1] << 8) + + (myRAM[address + 2] << 16) + + (myRAM[address + 3] << 24); // high byte result -= (0x40000000 + DSRAM); @@ -628,10 +628,10 @@ uInt32 CartridgeCDF::getSample() { uInt16 address = myWaveformBase; - uInt32 result = myCDFRAM[address + 0] + // low byte - (myCDFRAM[address + 1] << 8) + - (myCDFRAM[address + 2] << 16) + - (myCDFRAM[address + 3] << 24); // high byte + uInt32 result = myRAM[address + 0] + // low byte + (myRAM[address + 1] << 8) + + (myRAM[address + 2] << 16) + + (myRAM[address + 3] << 24); // high byte return result; } diff --git a/src/emucore/CartCDF.hxx b/src/emucore/CartCDF.hxx index 5e65bc4d4..14b1eda26 100644 --- a/src/emucore/CartCDF.hxx +++ b/src/emucore/CartCDF.hxx @@ -104,7 +104,7 @@ class CartridgeCDF : public Cartridge /** Query the number of banks supported by the cartridge. */ - uInt16 bankCount() const override; + uInt16 romBankCount() const override; /** Patch the cartridge ROM. @@ -220,13 +220,13 @@ class CartridgeCDF : public Cartridge uInt8* myDisplayImage{nullptr}; // Pointer to the 2K CDF driver image in RAM - uInt8* myBusDriverImage{nullptr}; + uInt8* myDriverImage{nullptr}; // The CDF 8k RAM image, used as: // $0000 - 2K CDF driver // $0800 - 4K Display Data // $1800 - 2K C Variable & Stack - std::array myCDFRAM; + std::array myRAM; // Pointer to the Thumb ARM emulator object unique_ptr myThumbEmulator; diff --git a/src/emucore/CartCM.cxx b/src/emucore/CartCM.cxx index 5fa6dff3d..40ec603e5 100644 --- a/src/emucore/CartCM.cxx +++ b/src/emucore/CartCM.cxx @@ -163,7 +163,7 @@ uInt16 CartridgeCM::getBank(uInt16) const } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeCM::bankCount() const +uInt16 CartridgeCM::romBankCount() const { // We report 4 banks (of ROM), even though RAM can overlap the upper 2K // of cart address space at some times diff --git a/src/emucore/CartCM.hxx b/src/emucore/CartCM.hxx index 2c0cf54d2..bf8b2586e 100644 --- a/src/emucore/CartCM.hxx +++ b/src/emucore/CartCM.hxx @@ -155,7 +155,7 @@ class CartridgeCM : public Cartridge /** Query the number of banks supported by the cartridge. */ - uInt16 bankCount() const override; + uInt16 romBankCount() const override; /** Patch the cartridge ROM. diff --git a/src/emucore/CartCTY.cxx b/src/emucore/CartCTY.cxx index 1536b64e0..822473081 100644 --- a/src/emucore/CartCTY.cxx +++ b/src/emucore/CartCTY.cxx @@ -255,7 +255,7 @@ uInt16 CartridgeCTY::getBank(uInt16) const } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeCTY::bankCount() const +uInt16 CartridgeCTY::romBankCount() const { return 8; } diff --git a/src/emucore/CartCTY.hxx b/src/emucore/CartCTY.hxx index 5626013a1..fdd6c1e19 100644 --- a/src/emucore/CartCTY.hxx +++ b/src/emucore/CartCTY.hxx @@ -153,7 +153,7 @@ class CartridgeCTY : public Cartridge /** Query the number of banks supported by the cartridge. */ - uInt16 bankCount() const override; + uInt16 romBankCount() const override; /** Patch the cartridge ROM. diff --git a/src/emucore/CartCV.cxx b/src/emucore/CartCV.cxx index 42c74a808..8d7ac9d89 100644 --- a/src/emucore/CartCV.cxx +++ b/src/emucore/CartCV.cxx @@ -21,164 +21,40 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeCV::CartridgeCV(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5), - mySize(size) + : CartridgeEnhanced(image, size, md5, settings) { - if(mySize == myImage.size()) - { - // Copy the ROM data into my buffer - std::copy_n(image.get(), myImage.size(), myImage.begin()); - } - else if(mySize == 4_KB) + myBankShift = BANK_SHIFT; + myRamSize = RAM_SIZE; + myRamWpHigh = RAM_HIGH_WP; + + if(mySize == 4_KB) { // The game has something saved in the RAM // Useful for MagiCard program listings - // Copy the ROM data into my buffer - std::copy_n(image.get() + myImage.size(), myImage.size(), myImage.begin()); + // Allocate array for the ROM image + mySize = 2_KB; + myImage = make_unique(mySize); + // Copy the ROM image into my buffer + std::copy_n(image.get() + mySize, mySize, myImage.get()); + + myInitialRAM = make_unique(1_KB); // Copy the RAM image into a buffer for use in reset() - std::copy_n(image.get(), myInitialRAM.size(), myInitialRAM.begin()); + std::copy_n(image.get(), 1_KB, myInitialRAM.get()); } - createRomAccessArrays(myImage.size() + myRAM.size()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void CartridgeCV::reset() { - if(mySize == 4_KB) + if(myInitialRAM != nullptr) { // Copy the RAM image into my buffer - myRAM = myInitialRAM; + std::copy_n(myInitialRAM.get(), 1_KB, myRAM.get()); } else - initializeRAM(myRAM.data(), myRAM.size()); + initializeRAM(myRAM.get(), myRamSize); myBankChanged = true; } - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeCV::install(System& system) -{ - mySystem = &system; - - System::PageAccess access(this, System::PageAccessType::READ); - - // Map ROM image into the system - for(uInt16 addr = 0x1800; addr < 0x2000; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[addr & 0x07FF]; - access.romAccessBase = &myRomAccessBase[addr & 0x07FF]; - access.romPeekCounter = &myRomAccessCounter[addr & 0x07FF]; - access.romPokeCounter = &myRomAccessCounter[(addr & 0x07FF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // 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 - access.directPeekBase = nullptr; - access.romAccessBase = nullptr; - access.type = System::PageAccessType::WRITE; - for(uInt16 addr = 0x1400; addr < 0x1800; addr += System::PAGE_SIZE) - mySystem->setPageAccess(addr, access); - - // Set the page accessing method for the RAM reading pages - access.directPokeBase = nullptr; - access.type = System::PageAccessType::READ; - for(uInt16 addr = 0x1000; addr < 0x1400; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myRAM[addr & 0x03FF]; - access.romAccessBase = &myRomAccessBase[2048 + (addr & 0x03FF)]; - access.romPeekCounter = &myRomAccessCounter[2048 + (addr & 0x03FF)]; - access.romPokeCounter = &myRomAccessCounter[2048 + (addr & 0x03FF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeCV::peek(uInt16 address) -{ - // The only way we can get to this method is if we attempt to read from - // the write port (0xF400 - 0xF7FF, 1024 bytes), in which case an - // unwanted write is potentially triggered - return peekRAM(myRAM[address & 0x03FF], address); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeCV::poke(uInt16 address, uInt8 value) -{ - - if(address & 0x0400) - { - pokeRAM(myRAM[address & 0x03FF], 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; - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeCV::patch(uInt16 address, uInt8 value) -{ - address &= 0x0FFF; - - if(address < 0x0800) - { - // Normally, a write to the read port won't do anything - // However, the patch command is special in that ignores such - // cart restrictions - // The following will work for both reads and writes - myRAM[address & 0x03FF] = value; - } - else - myImage[address & 0x07FF] = value; - - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* CartridgeCV::getImage(size_t& size) const -{ - size = myImage.size(); - return myImage.data(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeCV::save(Serializer& out) const -{ - try - { - out.putByteArray(myRAM.data(), myRAM.size()); - } - catch(...) - { - cerr << "ERROR: CartridgeCV::save" << endl; - return false; - } - - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeCV::load(Serializer& in) -{ - try - { - in.getByteArray(myRAM.data(), myRAM.size()); - } - catch(...) - { - cerr << "ERROR: CartridgeCV::load" << endl; - return false; - } - - return true; -} diff --git a/src/emucore/CartCV.hxx b/src/emucore/CartCV.hxx index 3b872ce9d..a5403b19f 100644 --- a/src/emucore/CartCV.hxx +++ b/src/emucore/CartCV.hxx @@ -21,7 +21,7 @@ class System; #include "bspf.hxx" -#include "Cart.hxx" +#include "CartEnhanced.hxx" #ifdef DEBUGGER_SUPPORT #include "CartCVWidget.hxx" #endif @@ -33,9 +33,9 @@ class System; $F400-$F7FF write to RAM $F800-$FFFF ROM - @author Eckhard Stolberg + @author Eckhard Stolberg, Thomas Jentzsch */ -class CartridgeCV : public Cartridge +class CartridgeCV : public CartridgeEnhanced { friend class CartridgeCVWidget; @@ -58,47 +58,6 @@ class CartridgeCV : public Cartridge */ void reset() override; - /** - 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; - - /** - 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). @@ -118,35 +77,22 @@ class CartridgeCV : public Cartridge } #endif - public: - /** - Get the byte at the specified address + private: + bool checkSwitchBank(uInt16, uInt8 = 0) override { return false; } - @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; + protected: + // Initial RAM data from the cart (doesn't always exist) + ByteBuffer myInitialRAM{nullptr}; private: - // The 2k ROM image for the cartridge - std::array myImage; + // Calculated as: log(ROM bank segment size) / log(2) + static constexpr uInt16 BANK_SHIFT = 11; // 2K - // Initial size of the cart data - size_t mySize{0}; + // RAM size + static constexpr uInt32 RAM_SIZE = 0x400; // 1K - // The 1024 bytes of RAM - std::array myRAM; - - // Initial RAM data from the cart (doesn't always exist) - std::array myInitialRAM; + // 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/CartCVPlus.cxx b/src/emucore/CartCVPlus.cxx deleted file mode 100644 index a3258e53c..000000000 --- a/src/emucore/CartCVPlus.cxx +++ /dev/null @@ -1,235 +0,0 @@ -//============================================================================ -// -// 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-2020 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 "TIA.hxx" -#include "CartCVPlus.hxx" - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -CartridgeCVPlus::CartridgeCVPlus(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); - - // Copy the ROM image into my buffer - std::copy_n(image.get(), mySize, myImage.get()); - createRomAccessArrays(mySize + myRAM.size()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeCVPlus::reset() -{ - initializeRAM(myRAM.data(), myRAM.size()); - initializeStartBank(0); - - // We'll map the startup bank into the first segment upon reset - bank(startBank()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeCVPlus::install(System& system) -{ - mySystem = &system; - - System::PageAccess access(this, System::PageAccessType::READWRITE); - - // The hotspot ($3D) is in TIA address space, so we claim it here - for(uInt16 addr = 0x00; addr < 0x40; addr += System::PAGE_SIZE) - mySystem->setPageAccess(addr, access); - - // 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 - access.directPeekBase = access.directPokeBase = nullptr; - access.romAccessBase = nullptr; - access.type = System::PageAccessType::WRITE; - for(uInt16 addr = 0x1400; addr < 0x1800; addr += System::PAGE_SIZE) - { - access.romAccessBase = &myRomAccessBase[mySize + (addr & 0x03FF)]; - access.romPeekCounter = &myRomAccessCounter[mySize + (addr & 0x03FF)]; - access.romPokeCounter = &myRomAccessCounter[mySize + (addr & 0x03FF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Set the page accessing method for the RAM reading pages - access.type = System::PageAccessType::READ; - for(uInt16 addr = 0x1000; addr < 0x1400; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myRAM[addr & 0x03FF]; - access.romAccessBase = &myRomAccessBase[mySize + (addr & 0x03FF)]; - access.romPeekCounter = &myRomAccessCounter[mySize + (addr & 0x03FF)]; - access.romPokeCounter = &myRomAccessCounter[mySize + (addr & 0x03FF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Install pages for the startup bank into the first segment - bank(startBank()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeCVPlus::peek(uInt16 address) -{ - if((address & 0x0FFF) < 0x0800) // Write port is at 0xF400 - 0xF7FF (1024 bytes) - return peekRAM(myRAM[address & 0x03FF], address); - else - return myImage[(address & 0x07FF) + (myCurrentBank << 11)]; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeCVPlus::poke(uInt16 address, uInt8 value) -{ - uInt16 pokeAddress = address; - address &= 0x0FFF; - - if(address < 0x0040) - { - // Switch banks if necessary - if(address == 0x003D) - bank(value); - - // Handle TIA space that we claimed above - return mySystem->tia().poke(address, value); - } - else - { - if(address & 0x0400) - { - pokeRAM(myRAM[address & 0x03FF], pokeAddress, value); - return true; - } - else - { - // Writing to the read port should be ignored, but trigger a break if option enabled - uInt8 dummy; - - pokeRAM(dummy, pokeAddress, value); - myRamWriteAccess = pokeAddress; - return false; - } - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeCVPlus::bank(uInt16 bank) -{ - if(bankLocked()) return false; - - // Make sure the bank they're asking for is reasonable - if((uInt32(bank) << 11) < mySize) - { - myCurrentBank = bank; - } - else - { - // Oops, the bank they're asking for isn't valid so let's wrap it - // around to a valid bank number - myCurrentBank = bank % (mySize >> 11); - } - - uInt32 offset = myCurrentBank << 11; - - // Setup the page access methods for the current bank - System::PageAccess access(this, System::PageAccessType::READ); - - // Map ROM image into the system - for(uInt16 addr = 0x1800; addr < 0x2000; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[offset + (addr & 0x07FF)]; - access.romAccessBase = &myRomAccessBase[offset + (addr & 0x07FF)]; - mySystem->setPageAccess(addr, access); - } - - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeCVPlus::getBank(uInt16) const -{ - return myCurrentBank; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeCVPlus::bankCount() const -{ - return uInt16(mySize >> 11); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeCVPlus::patch(uInt16 address, uInt8 value) -{ - address &= 0x0FFF; - - if(address < 0x0800) - { - // Normally, a write to the read port won't do anything - // However, the patch command is special in that ignores such - // cart restrictions - // The following will work for both reads and writes - myRAM[address & 0x03FF] = value; - } - else - myImage[(address & 0x07FF) + (myCurrentBank << 11)] = value; - - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* CartridgeCVPlus::getImage(size_t& size) const -{ - size = mySize; - return myImage.get(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeCVPlus::save(Serializer& out) const -{ - try - { - out.putShort(myCurrentBank); - out.putByteArray(myRAM.data(), myRAM.size()); - } - catch(...) - { - cerr << "ERROR: CartridgeCVPlus::save" << endl; - return false; - } - - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeCVPlus::load(Serializer& in) -{ - try - { - myCurrentBank = in.getShort(); - in.getByteArray(myRAM.data(), myRAM.size()); - } - catch(...) - { - cerr << "ERROR: CartridgeCVPlus::load" << endl; - return false; - } - - // Now, go to the current bank - bank(myCurrentBank); - - return true; -} diff --git a/src/emucore/CartCVPlus.hxx b/src/emucore/CartCVPlus.hxx deleted file mode 100644 index 63fcb62da..000000000 --- a/src/emucore/CartCVPlus.hxx +++ /dev/null @@ -1,188 +0,0 @@ -//============================================================================ -// -// 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-2020 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 CARTRIDGECVPlus_HXX -#define CARTRIDGECVPlus_HXX - -class System; - -#include "bspf.hxx" -#include "Cart.hxx" -#ifdef DEBUGGER_SUPPORT - #include "CartCVPlusWidget.hxx" -#endif - -/** - Cartridge class based on both Commavid and 3F/3E schemes: - - Commavid (RAM): - $F000-$F3FF read from RAM - $F400-$F7FF write to RAM - - 3F/3E (ROM): - $F800-$FFFF ROM - - In this bankswitching scheme the 2600's 4K cartridge - address space is broken into two 2K segments. The lower 2K - is RAM, as decribed above (same as CV/Commavid scheme). - To map ROM, the desired bank number of the upper 2K segment is - selected by storing its value into $3D. - - @author Stephen Anthony, LS_Dracon -*/ - -class CartridgeCVPlus : public Cartridge -{ - friend class CartridgeCVPlusWidget; - - public: - /** - Create a new cartridge using the specified image and size - - @param image Pointer to the ROM image - @param size The size of the ROM image - @param settings A reference to the various settings (read-only) - */ - CartridgeCVPlus(const ByteBuffer& image, size_t size, const string& md5, - const Settings& settings); - virtual ~CartridgeCVPlus() = default; - - public: - /** - Reset device to its power-on state - */ - void reset() override; - - /** - 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; - - /** - Install pages for the specified bank in the system. - - @param bank The bank that should be installed in the system - */ - bool bank(uInt16 bank) 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 bankCount() 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). - - @return The name of the object - */ - string name() const override { return "CartridgeCV+"; } - - #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 CartridgeCVPlusWidget(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: - // Pointer to a dynamically allocated ROM image of the cartridge - ByteBuffer myImage; - - // The 1024 bytes of RAM - std::array myRAM; - - // Size of the ROM image - size_t mySize{0}; - - // Indicates which bank is currently active for the first segment - uInt16 myCurrentBank{0}; - - private: - // Following constructors and assignment operators not supported - CartridgeCVPlus() = delete; - CartridgeCVPlus(const CartridgeCVPlus&) = delete; - CartridgeCVPlus(CartridgeCVPlus&&) = delete; - CartridgeCVPlus& operator=(const CartridgeCVPlus&) = delete; - CartridgeCVPlus& operator=(CartridgeCVPlus&&) = delete; -}; - -#endif diff --git a/src/emucore/CartDASH.cxx b/src/emucore/CartDASH.cxx deleted file mode 100644 index 6e187d76a..000000000 --- a/src/emucore/CartDASH.cxx +++ /dev/null @@ -1,355 +0,0 @@ -//============================================================================ -// -// 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-2020 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 "TIA.hxx" -#include "CartDASH.hxx" - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -CartridgeDASH::CartridgeDASH(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); - - // Copy the ROM image into my buffer - std::copy_n(image.get(), mySize, myImage.get()); - createRomAccessArrays(mySize + myRAM.size()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeDASH::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(uInt32 b = 0; b < 8; ++b) - { - bankInUse[b] = BANK_UNDEFINED; // bank is undefined and inaccessible! - segmentInUse[b/2] = BANK_UNDEFINED; - } - 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 CartridgeDASH::install(System& system) -{ - mySystem = &system; - - System::PageAccess access(this, System::PageAccessType::READWRITE); - - // 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 (uInt32 b = 0; b < 8; ++b) - { - bankInUse[b] = BANK_UNDEFINED; // bank is undefined and inaccessible! - segmentInUse[b/2] = BANK_UNDEFINED; - } - 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); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeDASH::peek(uInt16 address) -{ - uInt16 peekAddress = address; - address &= 0x0FFF; // restrict to 4K address range - - 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(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; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeDASH::poke(uInt16 address, uInt8 value) -{ - bool changed = false; - - // 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)) - { - // 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 - - 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 CartridgeDASH::bankRAM(uInt8 bank) -{ - if (bankLocked()) // debugger can lock RAM - return false; - - // Each RAM bank uses two slots, separated by 0x800 in memory -- one read, one write. - bankRAMSlot(bank | BITMASK_ROMRAM | 0); - bankRAMSlot(bank | BITMASK_ROMRAM | BITMASK_LOWERUPPER); - - // Remember that this hotspot was accessed for RAM - segmentInUse[(bank >> BANK_BITS) & 3] = bank | BITMASK_ROMRAM; - - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeDASH::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 - - // 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 + 4] = Int16(bank); - access.type = System::PageAccessType::WRITE; - } - else // We're mapping the read port - { - bankInUse[bankNumber] = Int16(bank); - access.type = System::PageAccessType::READ; - } - - uInt16 start = 0x1000 + (bankNumber << RAM_BANK_TO_POWER) + (upper ? RAM_WRITE_OFFSET : 0); - uInt16 end = start + RAM_BANK_SIZE - 1; - - 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 CartridgeDASH::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); - - // Remember that this hotspot was accessed for ROM - segmentInUse[(bank >> BANK_BITS) & 3] = bank; - - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeDASH::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 CartridgeDASH::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 CartridgeDASH::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) - Int16 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* CartridgeDASH::getImage(size_t& size) const -{ - size = mySize; - return myImage.get(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeDASH::save(Serializer& out) const -{ - try - { - out.putShortArray(bankInUse.data(), bankInUse.size()); - out.putShortArray(segmentInUse.data(), segmentInUse.size()); - out.putByteArray(myRAM.data(), myRAM.size()); - } - catch (...) - { - cerr << "ERROR: CartridgeDASH::save" << endl; - return false; - } - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeDASH::load(Serializer& in) -{ - try - { - in.getShortArray(bankInUse.data(), bankInUse.size()); - in.getShortArray(segmentInUse.data(), segmentInUse.size()); - in.getByteArray(myRAM.data(), myRAM.size()); - } - catch (...) - { - cerr << "ERROR: CartridgeDASH::load" << endl; - return false; - } - - initializeBankState(); - return true; -} diff --git a/src/emucore/CartDASH.hxx b/src/emucore/CartDASH.hxx deleted file mode 100644 index 8bd9fa523..000000000 --- a/src/emucore/CartDASH.hxx +++ /dev/null @@ -1,277 +0,0 @@ -//============================================================================ -// -// 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-2020 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 CARTRIDGEDASH_HXX -#define CARTRIDGEDASH_HXX - -class System; - -#include "bspf.hxx" -#include "Cart.hxx" - -#ifdef DEBUGGER_SUPPORT -class CartridgeDASHWidget; - #include "CartDASHWidget.hxx" -#endif - -/** - 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. - - 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. - - 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 segments. - living at 0x1000, 0x1400, 0x1800, 0x1C00 (or, same thing, 0xF000... etc.), - and four 512 byte RAM segments, living at 0x1000, 0x1200, 0x1400, 0x1600 - with write-mirrors +0x800 of these. 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 xxxxx to $F000 - 0 1 switch a 1K ROM bank xxxxx to $F400 - 1 0 switch a 1K ROM bank xxxxx to $F800 - 1 1 switch a 1K ROM bank xxxxx 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, - $F200, $F400, $F600). Note that this allows contiguous 2K of RAM to be - configured by setting 4 consecutive RAM segments each 512 bytes with - consecutive addresses. However, as the write address of RAM is +0x800, this - invalidates ROM access as described below. - - 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 xxxxx to $F000 with write @ $F800 - 0 1 switch a 512 byte RAM bank xxxxx to $F200 with write @ $FA00 - 1 0 switch a 512 byte RAM bank xxxxx to $F400 with write @ $FC00 - 1 1 switch a 512 byte RAM bank xxxxx to $F600 with write @ $FE00 - - It is possible to switch multiple RAM banks and ROM banks together - - For example, - F000-F1FF RAM bank A (512 byte READ) - F200-F3FF high 512 bytes of ROM bank previously loaded at F000 - F400 ROM bank 0 (1K) - F800 RAM bank A (512 byte WRITE) - FA00-FBFF high 512 bytes of ROM bank previously loaded at F400 - FC00 ROM bank 1 - - This example shows 512 bytes of RAM, and 2 1K ROM banks and two 512 byte ROM - bank halves. - - Switching RAM blocks (D7D6 of $3E) partially invalidates ROM blocks, as below... - - RAM block Invalidates ROM block - 0 0 (lower half), 2 (lower half) - 1 0 (upper half), 2 (upper half) - 2 1 (lower half), 3 (upper half) - 3 1 (upper half), 3 (lower half) - - For example, RAM block 1 uses address $F200-$F3FF and $FA00-$FBFF - ROM block 0 uses address $F000-$F3FF, and ROM block 2 uses address $F800-$FBFF - Switching in RAM block 1 makes F200-F3FF ROM inaccessible, however F000-F1FF is - still readable. So, care must be paid. - - This crazy RAM layout is useful as it allows contiguous RAM to be switched in, - up to 2K in one sequentially accessible block. This means you CAN have 2K of - consecutive RAM (don't forget to copy your reset vectors!) - - @author Andrew Davie -*/ - -class CartridgeDASH: public Cartridge -{ - friend class CartridgeDASHWidget; - - public: - /** - Create a new cartridge using the specified image and size - - @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) - */ - CartridgeDASH(const ByteBuffer& image, size_t size, const string& md5, - const Settings& settings); - virtual ~CartridgeDASH() = default; - - public: - /** Reset device to its power-on state */ - void reset() override; - - /** - 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; - - /** - 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). - - @return The name of the object - */ - string name() const override { return "CartridgeDASH"; } - - #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 CartridgeDASHWidget(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 bankRAM(uInt8 bank); // switch a RAM bank - bool bankROM(uInt8 bank); // switch a ROM bank - - 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) - - void initializeBankState(); // set all banks according to current bankInUse state - - // 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. - - static constexpr uInt16 BANK_UNDEFINED = 0x8000; // bank is undefined and inaccessible - std::array bankInUse; // bank being used for ROM/RAM (eight 512 byte areas) - std::array segmentInUse; // set by bank methods, to know which hotspot was accessed - - 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 = 0x800; - - ByteBuffer myImage; // Pointer to a dynamically allocated ROM image of the cartridge - size_t mySize{0}; // Size of the ROM image - std::array myRAM; - - private: - // Following constructors and assignment operators not supported - CartridgeDASH() = delete; - CartridgeDASH(const CartridgeDASH&) = delete; - CartridgeDASH(CartridgeDASH&&) = delete; - CartridgeDASH& operator=(const CartridgeDASH&) = delete; - CartridgeDASH& operator=(CartridgeDASH&&) = delete; -}; - -#endif diff --git a/src/emucore/CartDF.cxx b/src/emucore/CartDF.cxx index b43bc0837..0f4d9a1ca 100644 --- a/src/emucore/CartDF.cxx +++ b/src/emucore/CartDF.cxx @@ -21,145 +21,20 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeDF::CartridgeDF(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5) + : CartridgeEnhanced(image, size, md5, settings) { - // Copy the ROM image into my buffer - std::copy_n(image.get(), std::min(myImage.size(), size), myImage.begin()); - createRomAccessArrays(myImage.size()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeDF::reset() +bool CartridgeDF::checkSwitchBank(uInt16 address, uInt8) { - initializeStartBank(1); - - // Upon reset we switch to the startup bank - bank(startBank()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeDF::install(System& system) -{ - mySystem = &system; - - // Install pages for the startup bank - bank(startBank()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeDF::peek(uInt16 address) -{ - address &= 0x0FFF; + address &= ROM_MASK; // Switch banks if necessary if((address >= 0x0FC0) && (address <= 0x0FDF)) + { bank(address - 0x0FC0); - - return myImage[myBankOffset + address]; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeDF::poke(uInt16 address, uInt8) -{ - address &= 0x0FFF; - - // Switch banks if necessary - if((address >= 0x0FC0) && (address <= 0x0FDF)) - bank(address - 0x0FC0); - + return true; + } return false; } - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeDF::bank(uInt16 bank) -{ - if(bankLocked()) return false; - - // Remember what bank we're in - myBankOffset = bank << 12; - - System::PageAccess access(this, System::PageAccessType::READ); - - // Set the page accessing methods for the hot spots - for(uInt16 addr = (0x1FC0 & ~System::PAGE_MASK); addr < 0x2000; - addr += System::PAGE_SIZE) - { - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Setup the page access methods for the current bank - for(uInt16 addr = 0x1000; addr < static_cast(0x1FC0U & ~System::PAGE_MASK); - addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[myBankOffset + (addr & 0x0FFF)]; - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeDF::getBank(uInt16) const -{ - return myBankOffset >> 12; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeDF::bankCount() const -{ - return 32; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeDF::patch(uInt16 address, uInt8 value) -{ - myImage[myBankOffset + (address & 0x0FFF)] = value; - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* CartridgeDF::getImage(size_t& size) const -{ - size = myImage.size(); - return myImage.data(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeDF::save(Serializer& out) const -{ - try - { - out.putInt(myBankOffset); - } - catch(...) - { - cerr << "ERROR: CartridgeDF::save" << endl; - return false; - } - - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeDF::load(Serializer& in) -{ - try - { - myBankOffset = in.getInt(); - } - catch(...) - { - cerr << "ERROR: CartridgeDF::load" << endl; - return false; - } - - // Remember what bank we were in - bank(myBankOffset >> 12); - - return true; -} diff --git a/src/emucore/CartDF.hxx b/src/emucore/CartDF.hxx index 04804b957..3c33e8bff 100644 --- a/src/emucore/CartDF.hxx +++ b/src/emucore/CartDF.hxx @@ -21,7 +21,7 @@ class System; #include "bspf.hxx" -#include "Cart.hxx" +#include "CartEnhanced.hxx" #ifdef DEBUGGER_SUPPORT #include "CartDFWidget.hxx" #endif @@ -31,9 +31,9 @@ class System; There are 32 4K banks (total of 128K ROM). Accessing $1FC0 - $1FDF switches to each bank. - @author Mike Saarna + @author Mike Saarna, Thomas Jentzsch */ -class CartridgeDF : public Cartridge +class CartridgeDF : public CartridgeEnhanced { friend class CartridgeDFWidget; @@ -51,71 +51,6 @@ class CartridgeDF : public Cartridge virtual ~CartridgeDF() = default; public: - /** - Reset device to its power-on state - */ - void reset() override; - - /** - 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; - - /** - Install pages for the specified bank in the system. - - @param bank The bank that should be installed in the system - */ - bool bank(uInt16 bank) 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 bankCount() 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). @@ -135,29 +70,12 @@ class CartridgeDF : public Cartridge } #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: - // The 128K ROM image of the cartridge - std::array myImage; + bool checkSwitchBank(uInt16 address, uInt8 value = 0) override; - // Indicates the offset into the ROM image (aligns to current bank) - uInt32 myBankOffset{0}; + uInt16 hotspot() const override { return 0x1FC0; } + + uInt16 getStartBank() const override { return 15; } private: // Following constructors and assignment operators not supported diff --git a/src/emucore/CartDFSC.cxx b/src/emucore/CartDFSC.cxx index 3661459fa..f759136a7 100644 --- a/src/emucore/CartDFSC.cxx +++ b/src/emucore/CartDFSC.cxx @@ -21,205 +21,7 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeDFSC::CartridgeDFSC(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5) + : CartridgeDF(image, size, md5, settings) { - // Copy the ROM image into my buffer - std::copy_n(image.get(), std::min(myImage.size(), size), myImage.begin()); - createRomAccessArrays(myImage.size()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeDFSC::reset() -{ - initializeRAM(myRAM.data(), myRAM.size()); - initializeStartBank(15); - - // Upon reset we switch to the startup bank - bank(startBank()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeDFSC::install(System& system) -{ - mySystem = &system; - - 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 - access.type = System::PageAccessType::WRITE; - for(uInt16 addr = 0x1000; addr < 0x1080; addr += System::PAGE_SIZE) - { - access.romAccessBase = &myRomAccessBase[addr & 0x007F]; - access.romPeekCounter = &myRomAccessCounter[addr & 0x007F]; - access.romPokeCounter = &myRomAccessCounter[(addr & 0x007F) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Set the page accessing method for the RAM reading pages - access.type = System::PageAccessType::READ; - for(uInt16 addr = 0x1080; addr < 0x1100; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myRAM[addr & 0x007F]; - access.romAccessBase = &myRomAccessBase[0x80 + (addr & 0x007F)]; - access.romPeekCounter = &myRomAccessCounter[0x80 + (addr & 0x007F)]; - access.romPokeCounter = &myRomAccessCounter[0x80 + (addr & 0x007F) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Install pages for the startup bank - bank(startBank()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeDFSC::peek(uInt16 address) -{ - uInt16 peekAddress = address; - address &= 0x0FFF; - - // Switch banks if necessary - if((address >= 0x0FC0) && (address <= 0x0FDF)) - bank(address - 0x0FC0); - - if(address < 0x0080) // Write port is at 0xF000 - 0xF07F (128 bytes) - return peekRAM(myRAM[address], peekAddress); - else - return myImage[myBankOffset + address]; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeDFSC::poke(uInt16 address, uInt8 value) -{ - uInt16 pokeAddress = address; - address &= 0x0FFF; - - // Switch banks if necessary - if((address >= 0x0FC0) && (address <= 0x0FDF)) - { - bank(address - 0x0FC0); - return false; - } - - if(!(address & 0x080)) - { - pokeRAM(myRAM[address & 0x007F], pokeAddress, value); - return true; - } - else - { - // Writing to the read port should be ignored, but trigger a break if option enabled - uInt8 dummy; - - pokeRAM(dummy, pokeAddress, value); - myRamWriteAccess = pokeAddress; - return false; - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeDFSC::bank(uInt16 bank) -{ - if(bankLocked()) return false; - - // Remember what bank we're in - myBankOffset = bank << 12; - - System::PageAccess access(this, System::PageAccessType::READ); - - // Set the page accessing methods for the hot spots - for(uInt16 addr = (0x1FC0 & ~System::PAGE_MASK); addr < 0x2000; - addr += System::PAGE_SIZE) - { - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Setup the page access methods for the current bank - for(uInt16 addr = 0x1100; addr < static_cast(0x1FC0U & ~System::PAGE_MASK); - addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[myBankOffset + (addr & 0x0FFF)]; - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeDFSC::getBank(uInt16) const -{ - return myBankOffset >> 12; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeDFSC::bankCount() const -{ - return 32; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeDFSC::patch(uInt16 address, uInt8 value) -{ - address &= 0x0FFF; - - if(address < 0x0100) - { - // Normally, a write to the read port won't do anything - // However, the patch command is special in that ignores such - // cart restrictions - myRAM[address & 0x007F] = value; - } - else - myImage[myBankOffset + address] = value; - - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* CartridgeDFSC::getImage(size_t& size) const -{ - size = myImage.size(); - return myImage.data(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeDFSC::save(Serializer& out) const -{ - try - { - out.putInt(myBankOffset); - out.putByteArray(myRAM.data(), myRAM.size()); - } - catch(...) - { - cerr << "ERROR: CartridgeDFSC::save" << endl; - return false; - } - - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeDFSC::load(Serializer& in) -{ - try - { - myBankOffset = in.getInt(); - in.getByteArray(myRAM.data(), myRAM.size()); - } - catch(...) - { - cerr << "ERROR: CartridgeDFSC::load" << endl; - return false; - } - - // Remember what bank we were in - bank(myBankOffset >> 12); - - return true; + myRamSize = RAM_SIZE; } diff --git a/src/emucore/CartDFSC.hxx b/src/emucore/CartDFSC.hxx index af35f476b..637dde3da 100644 --- a/src/emucore/CartDFSC.hxx +++ b/src/emucore/CartDFSC.hxx @@ -21,7 +21,7 @@ class System; #include "bspf.hxx" -#include "Cart.hxx" +#include "CartDF.hxx" #ifdef DEBUGGER_SUPPORT #include "CartDFSCWidget.hxx" #endif @@ -31,9 +31,9 @@ class System; Accessing $1FC0 - $1FDF switches to each bank. RAM read port is $1080 - $10FF, write port is $1000 - $107F. - @author Stephen Anthony + @author Stephen Anthony, Thomas Jentzsch */ -class CartridgeDFSC : public Cartridge +class CartridgeDFSC : public CartridgeDF { friend class CartridgeDFSCWidget; @@ -51,71 +51,6 @@ class CartridgeDFSC : public Cartridge virtual ~CartridgeDFSC() = default; public: - /** - Reset device to its power-on state - */ - void reset() override; - - /** - 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; - - /** - Install pages for the specified bank in the system. - - @param bank The bank that should be installed in the system - */ - bool bank(uInt16 bank) 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 bankCount() 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). @@ -135,34 +70,11 @@ class CartridgeDFSC : public Cartridge } #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: - // The 128K ROM image of the cartridge - std::array myImage; + // RAM size + static constexpr size_t RAM_SIZE = 0x80; - // The 128 bytes of RAM - std::array myRAM; - - // Indicates the offset into the ROM image (aligns to current bank) - uInt32 myBankOffset{0}; - - private: +private: // Following constructors and assignment operators not supported CartridgeDFSC() = delete; CartridgeDFSC(const CartridgeDFSC&) = delete; diff --git a/src/emucore/CartDPC.cxx b/src/emucore/CartDPC.cxx index 808e658d4..481e385be 100644 --- a/src/emucore/CartDPC.cxx +++ b/src/emucore/CartDPC.cxx @@ -22,45 +22,36 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeDPC::CartridgeDPC(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5), - mySize(size) + : CartridgeF8(image, size, md5, settings) { - // Make a copy of the entire image - std::copy_n(image.get(), std::min(myImage.size(), size), myImage.begin()); - createRomAccessArrays(8_KB); - - // Pointer to the program ROM (8K @ 0 byte offset) - myProgramImage = myImage.data(); - - // Pointer to the display ROM (2K @ 8K offset) - myDisplayImage = myProgramImage + 8_KB; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void CartridgeDPC::reset() { + CartridgeEnhanced::reset(); + myAudioCycles = 0; myFractionalClocks = 0.0; - - // Upon reset we switch to the startup bank - initializeStartBank(1); - bank(startBank()); - myDpcPitch = mySettings.getInt(AudioSettings::SETTING_DPC_PITCH); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void CartridgeDPC::install(System& system) { - mySystem = &system; + CartridgeEnhanced::install(system); + + myRomOffset = 0x80; + + // Pointer to the display ROM (2K @ 8K offset) + myDisplayImage = myImage.get() + 8_KB; + + createRomAccessArrays(8_KB); // Set the page accessing method for the DPC reading & writing pages System::PageAccess access(this, System::PageAccessType::READWRITE); for(uInt16 addr = 0x1000; addr < 0x1080; addr += System::PAGE_SIZE) mySystem->setPageAccess(addr, access); - - // Install pages for the startup bank - bank(startBank()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -128,12 +119,15 @@ inline void CartridgeDPC::updateMusicModeDataFetchers() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uInt8 CartridgeDPC::peek(uInt16 address) { + uInt16 peekAddress = address; + address &= 0x0FFF; // In debugger/bank-locked mode, we ignore all hotspots and in general // anything that can change the internal state of the cart if(bankLocked()) - return myProgramImage[myBankOffset + address]; + return myImage[myCurrentSegOffset[0] + address]; + // Clock the random number generator. This should be done for every // cartridge access, however, we're only doing it for the DPC and @@ -232,30 +226,14 @@ uInt8 CartridgeDPC::peek(uInt16 address) return result; } else - { - // Switch banks if necessary - switch(address) - { - case 0x0FF8: - // Set the current bank to the lower 4k bank - bank(0); - break; - - case 0x0FF9: - // Set the current bank to the upper 4k bank - bank(1); - break; - - default: - break; - } - return myProgramImage[myBankOffset + address]; - } + return CartridgeEnhanced::peek(peekAddress); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool CartridgeDPC::poke(uInt16 address, uInt8 value) { + uInt16 pokeAddress = address; + address &= 0x0FFF; // Clock the random number generator. This should be done for every @@ -338,102 +316,31 @@ bool CartridgeDPC::poke(uInt16 address, uInt8 value) } } else - { - // Switch banks if necessary - switch(address) - { - case 0x0FF8: - // Set the current bank to the lower 4k bank - bank(0); - break; + CartridgeEnhanced::poke(pokeAddress, value); - case 0x0FF9: - // Set the current bank to the upper 4k bank - bank(1); - break; - - default: - break; - } - } return false; } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeDPC::bank(uInt16 bank) -{ - if(bankLocked()) return false; - - // Remember what bank we're in - myBankOffset = bank << 12; - - System::PageAccess access(this, System::PageAccessType::READ); - - // Set the page accessing methods for the hot spots - for(uInt16 addr = (0x1FF8 & ~System::PAGE_MASK); addr < 0x2000; - addr += System::PAGE_SIZE) - { - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Setup the page access methods for the current bank - for(uInt16 addr = 0x1080; addr < static_cast(0x1FF8U & ~System::PAGE_MASK); - addr += System::PAGE_SIZE) - { - access.directPeekBase = &myProgramImage[myBankOffset + (addr & 0x0FFF)]; - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeDPC::getBank(uInt16) const -{ - return myBankOffset >> 12; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeDPC::bankCount() const -{ - return 2; -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool CartridgeDPC::patch(uInt16 address, uInt8 value) { - address &= 0x0FFF; - // For now, we ignore attempts to patch the DPC address space - if(address >= 0x0080) + if((address & ADDR_MASK) >= ROM_OFFSET + myRomOffset) { - myProgramImage[myBankOffset + (address & 0x0FFF)] = value; - return myBankChanged = true; + return CartridgeEnhanced::patch(address, value); } else return false; } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* CartridgeDPC::getImage(size_t& size) const -{ - size = mySize; - return myImage.data(); -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool CartridgeDPC::save(Serializer& out) const { + if(!CartridgeEnhanced::save(out)) + return false; + try { - // Indicates which bank is currently active - out.putShort(myBankOffset); - // The top registers for the data fetchers out.putByteArray(myTops.data(), myTops.size()); @@ -468,11 +375,11 @@ bool CartridgeDPC::save(Serializer& out) const // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool CartridgeDPC::load(Serializer& in) { + if(!CartridgeEnhanced::load(in)) + return false; + try { - // Indicates which bank is currently active - myBankOffset = in.getShort(); - // The top registers for the data fetchers in.getByteArray(myTops.data(), myTops.size()); @@ -501,9 +408,5 @@ bool CartridgeDPC::load(Serializer& in) cerr << "ERROR: CartridgeDPC::load" << endl; return false; } - - // Now, go to the current bank - bank(myBankOffset >> 12); - return true; } diff --git a/src/emucore/CartDPC.hxx b/src/emucore/CartDPC.hxx index 330a0086a..e0a1b866e 100644 --- a/src/emucore/CartDPC.hxx +++ b/src/emucore/CartDPC.hxx @@ -18,10 +18,7 @@ #ifndef CARTRIDGE_DPC_HXX #define CARTRIDGE_DPC_HXX -class System; - -#include "bspf.hxx" -#include "Cart.hxx" +#include "CartF8.hxx" #ifdef DEBUGGER_SUPPORT #include "CartDPCWidget.hxx" #endif @@ -35,9 +32,9 @@ class System; For complete details on the DPC chip see David P. Crane's United States Patent Number 4,644,495. - @author Bradford W. Mott + @author Bradford W. Mott, Thomas Jentzsch */ -class CartridgeDPC : public Cartridge +class CartridgeDPC : public CartridgeF8 { friend class CartridgeDPCWidget; @@ -55,11 +52,6 @@ class CartridgeDPC : public Cartridge virtual ~CartridgeDPC() = default; public: - /** - Reset device to its power-on state - */ - void reset() override; - /** Install cartridge in the specified system. Invoked by the system when the cartridge is attached to it. @@ -69,23 +61,9 @@ class CartridgeDPC : public Cartridge void install(System& system) override; /** - Install pages for the specified bank in the system. - - @param bank The bank that should be installed in the system + Reset device to its power-on state */ - bool bank(uInt16 bank) 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 bankCount() const override; + void reset() override; /** Patch the cartridge ROM. @@ -96,14 +74,6 @@ class CartridgeDPC : public Cartridge */ 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. @@ -127,6 +97,11 @@ class CartridgeDPC : public Cartridge */ string name() const override { return "CartridgeDPC"; } + /** + Change the DPC audio pitch + + @param pitch The new pitch value + */ void setDpcPitch(double pitch) { myDpcPitch = pitch; } #ifdef DEBUGGER_SUPPORT @@ -171,15 +146,6 @@ class CartridgeDPC : public Cartridge void updateMusicModeDataFetchers(); private: - // The ROM image - std::array myImage; - - // (Actual) Size of the ROM image - size_t mySize{0}; - - // Pointer to the 8K program ROM image of the cartridge - uInt8* myProgramImage{nullptr}; - // Pointer to the 2K display ROM image of the cartridge uInt8* myDisplayImage{nullptr}; @@ -207,9 +173,6 @@ class CartridgeDPC : public Cartridge // Fractional DPC music OSC clocks unused during the last update double myFractionalClocks{0.0}; - // Indicates the offset into the ROM image (aligns to current bank) - uInt16 myBankOffset{0}; - // DPC pitch double myDpcPitch{0.0}; diff --git a/src/emucore/CartDPCPlus.cxx b/src/emucore/CartDPCPlus.cxx index 0761eeb19..b71f5a8c0 100644 --- a/src/emucore/CartDPCPlus.cxx +++ b/src/emucore/CartDPCPlus.cxx @@ -619,7 +619,7 @@ uInt16 CartridgeDPCPlus::getBank(uInt16) const } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeDPCPlus::bankCount() const +uInt16 CartridgeDPCPlus::romBankCount() const { return 6; } diff --git a/src/emucore/CartDPCPlus.hxx b/src/emucore/CartDPCPlus.hxx index 46b18af99..9617d6545 100644 --- a/src/emucore/CartDPCPlus.hxx +++ b/src/emucore/CartDPCPlus.hxx @@ -100,7 +100,7 @@ class CartridgeDPCPlus : public Cartridge /** Query the number of banks supported by the cartridge. */ - uInt16 bankCount() const override; + uInt16 romBankCount() const override; /** Patch the cartridge ROM. diff --git a/src/emucore/CartDetector.cxx b/src/emucore/CartDetector.cxx index b6e6f150a..6c68bf6e4 100644 --- a/src/emucore/CartDetector.cxx +++ b/src/emucore/CartDetector.cxx @@ -20,6 +20,7 @@ #include "Cart0840.hxx" #include "Cart2K.hxx" #include "Cart3E.hxx" +#include "Cart3EX.hxx" #include "Cart3EPlus.hxx" #include "Cart3F.hxx" #include "Cart4A50.hxx" @@ -33,8 +34,6 @@ #include "CartCM.hxx" #include "CartCTY.hxx" #include "CartCV.hxx" -#include "CartCVPlus.hxx" -#include "CartDASH.hxx" #include "CartDF.hxx" #include "CartDFSC.hxx" #include "CartDPC.hxx" @@ -252,6 +251,8 @@ CartDetector::createFromImage(const ByteBuffer& image, size_t size, Bankswitch:: return make_unique(image, size, md5, settings); case Bankswitch::Type::_3E: return make_unique(image, size, md5, settings); + case Bankswitch::Type::_3EX: + return make_unique(image, size, md5, settings); case Bankswitch::Type::_3EP: return make_unique(image, size, md5, settings); case Bankswitch::Type::_3F: @@ -278,10 +279,6 @@ CartDetector::createFromImage(const ByteBuffer& image, size_t size, Bankswitch:: return make_unique(image, size, md5, settings); case Bankswitch::Type::_CV: return make_unique(image, size, md5, settings); - case Bankswitch::Type::_CVP: - return make_unique(image, size, md5, settings); - case Bankswitch::Type::_DASH: - return make_unique(image, size, md5, settings); case Bankswitch::Type::_DF: return make_unique(image, size, md5, settings); case Bankswitch::Type::_DFSC: @@ -346,11 +343,7 @@ Bankswitch::Type CartDetector::autodetectType(const ByteBuffer& image, size_t si // Guess type based on size Bankswitch::Type type = Bankswitch::Type::_AUTO; - if(isProbablyCVPlus(image, size)) - { - type = Bankswitch::Type::_CVP; - } - else if((size % 8448) == 0 || size == 6144) + if((size % 8448) == 0 || size == 6144) { type = Bankswitch::Type::_AR; } @@ -381,8 +374,8 @@ Bankswitch::Type CartDetector::autodetectType(const ByteBuffer& image, size_t si { 0x8D, 0xF9, 0x1F }, // STA $1FF9 { 0x8D, 0xF9, 0xFF } // STA $FFF9 }; - bool f8 = searchForBytes(image.get(), size, signature[0], 3, 2) || - searchForBytes(image.get(), size, signature[1], 3, 2); + bool f8 = searchForBytes(image, size, signature[0], 3, 2) || + searchForBytes(image, size, signature[1], 3, 2); if(isProbablySC(image, size)) type = Bankswitch::Type::_F8SC; @@ -390,6 +383,8 @@ Bankswitch::Type CartDetector::autodetectType(const ByteBuffer& image, size_t si type = Bankswitch::Type::_4K; else if(isProbablyE0(image, size)) type = Bankswitch::Type::_E0; + else if(isProbably3EX(image, size)) + type = Bankswitch::Type::_3EX; else if(isProbably3E(image, size)) type = Bankswitch::Type::_3E; else if(isProbably3F(image, size)) @@ -429,6 +424,8 @@ Bankswitch::Type CartDetector::autodetectType(const ByteBuffer& image, size_t si type = Bankswitch::Type::_E7; else if (isProbablyFC(image, size)) type = Bankswitch::Type::_FC; + else if(isProbably3EX(image, size)) + type = Bankswitch::Type::_3EX; else if(isProbably3E(image, size)) type = Bankswitch::Type::_3E; /* no known 16K 3F ROMS @@ -455,6 +452,8 @@ Bankswitch::Type CartDetector::autodetectType(const ByteBuffer& image, size_t si type = Bankswitch::Type::_CTY; else if(isProbablySC(image, size)) type = Bankswitch::Type::_F4SC; + else if(isProbably3EX(image, size)) + type = Bankswitch::Type::_3EX; else if(isProbably3E(image, size)) type = Bankswitch::Type::_3E; else if(isProbably3F(image, size)) @@ -481,7 +480,9 @@ Bankswitch::Type CartDetector::autodetectType(const ByteBuffer& image, size_t si } else if(size == 64_KB) { - if(isProbably3E(image, size)) + if(isProbably3EX(image, size)) + type = Bankswitch::Type::_3EX; + else if(isProbably3E(image, size)) type = Bankswitch::Type::_3E; else if(isProbably3F(image, size)) type = Bankswitch::Type::_3F; @@ -496,7 +497,9 @@ Bankswitch::Type CartDetector::autodetectType(const ByteBuffer& image, size_t si } else if(size == 128_KB) { - if(isProbably3E(image, size)) + if(isProbably3EX(image, size)) + type = Bankswitch::Type::_3EX; + else if(isProbably3E(image, size)) type = Bankswitch::Type::_3E; else if(isProbablyDF(image, size, type)) ; // type has been set directly in the function @@ -504,12 +507,14 @@ Bankswitch::Type CartDetector::autodetectType(const ByteBuffer& image, size_t si type = Bankswitch::Type::_3F; else if(isProbably4A50(image, size)) type = Bankswitch::Type::_4A50; - else if(isProbablySB(image, size)) + else /*if(isProbablySB(image, size))*/ type = Bankswitch::Type::_SB; } else if(size == 256_KB) { - if(isProbably3E(image, size)) + if(isProbably3EX(image, size)) + type = Bankswitch::Type::_3EX; + else if(isProbably3E(image, size)) type = Bankswitch::Type::_3E; else if(isProbablyBF(image, size, type)) ; // type has been set directly in the function @@ -520,24 +525,25 @@ Bankswitch::Type CartDetector::autodetectType(const ByteBuffer& image, size_t si } else // what else can we do? { - if(isProbably3E(image, size)) + if(isProbably3EX(image, size)) + type = Bankswitch::Type::_3EX; + else if(isProbably3E(image, size)) type = Bankswitch::Type::_3E; else if(isProbably3F(image, size)) type = Bankswitch::Type::_3F; - else - type = Bankswitch::Type::_4K; // Most common bankswitching type } // Variable sized ROM formats are independent of image size and come last - if(isProbablyDASH(image, size)) - type = Bankswitch::Type::_DASH; - else if(isProbably3EPlus(image, size)) + if(isProbably3EPlus(image, size)) type = Bankswitch::Type::_3EP; else if(isProbablyMDM(image, size)) type = Bankswitch::Type::_MDM; - ostringstream ss; + // If we get here and autodetection failed, then we force '4K' + if(type == Bankswitch::Type::_AUTO) + type = Bankswitch::Type::_4K; // Most common bankswitching type + ostringstream ss; ss << "Bankswitching type '" << Bankswitch::typeToDesc(type) << "' detected"; Logger::debug(ss.str()); @@ -550,26 +556,25 @@ bool CartDetector::searchForBytes(const uInt8* image, size_t imagesize, uInt32 minhits) { uInt32 count = 0; + for(uInt32 i = 0; i < imagesize - sigsize; ++i) { - uInt32 matches = 0; - for(uInt32 j = 0; j < sigsize; ++j) + uInt32 j; + + for(j = 0; j < sigsize; ++j) { - if(image[i+j] == signature[j]) - ++matches; - else + if(image[i + j] != signature[j]) break; } - if(matches == sigsize) + if(j == sigsize) { - ++count; + if(++count == minhits) + break; i += sigsize; // skip past this signature 'window' entirely } - if(count >= minhits) - break; } - return (count >= minhits); + return (count == minhits); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -598,10 +603,10 @@ bool CartDetector::isProbablyARM(const ByteBuffer& image, size_t size) { 0xA0, 0xC1, 0x1F, 0xE0 }, { 0x00, 0x80, 0x02, 0xE0 } }; - if(searchForBytes(image.get(), std::min(size, 1_KB), signature[0], 4, 1)) + if(searchForBytes(image, std::min(size, 1_KB), signature[0], 4)) return true; else - return searchForBytes(image.get(), std::min(size, 1_KB), signature[1], 4, 1); + return searchForBytes(image, std::min(size, 1_KB), signature[1], 4); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -615,7 +620,7 @@ bool CartDetector::isProbably0840(const ByteBuffer& image, size_t size) { 0x2C, 0x00, 0x08 } // BIT $0800 }; for(uInt32 i = 0; i < 3; ++i) - if(searchForBytes(image.get(), size, signature1[i], 3, 2)) + if(searchForBytes(image, size, signature1[i], 3, 2)) return true; uInt8 signature2[2][4] = { @@ -623,7 +628,7 @@ bool CartDetector::isProbably0840(const ByteBuffer& image, size_t size) { 0x0C, 0xFF, 0x0F, 0x4C } // NOP $0FFF; JMP ... }; for(uInt32 i = 0; i < 2; ++i) - if(searchForBytes(image.get(), size, signature2[i], 4, 2)) + if(searchForBytes(image, size, signature2[i], 4, 2)) return true; return false; @@ -632,11 +637,24 @@ bool CartDetector::isProbably0840(const ByteBuffer& image, size_t size) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool CartDetector::isProbably3E(const ByteBuffer& image, size_t size) { - // 3E cart bankswitching is triggered by storing the bank number - // in address 3E using 'STA $3E', commonly followed by an - // immediate mode LDA - uInt8 signature[] = { 0x85, 0x3E, 0xA9, 0x00 }; // STA $3E; LDA #$00 - return searchForBytes(image.get(), size, signature, 4, 1); + // 3E cart RAM bankswitching is triggered by storing the bank number + // in address 3E using 'STA $3E', ROM bankswitching is triggered by + // storing the bank number in address 3F using 'STA $3F'. + // We expect the latter will be present at least 2 times, since there + // are at least two banks + + uInt8 signature1[] = { 0x85, 0x3E }; // STA $3E + uInt8 signature2[] = { 0x85, 0x3F }; // STA $3F + return searchForBytes(image, size, signature1, 2) + && searchForBytes(image, size, signature2, 2, 2); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool CartDetector::isProbably3EX(const ByteBuffer& image, size_t size) +{ + // 3EX cart have at least 2 occurrences of the string "3EX" + uInt8 _3EX[] = { '3', 'E', 'X'}; + return searchForBytes(image, size, _3EX, 3, 2); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -644,7 +662,7 @@ bool CartDetector::isProbably3EPlus(const ByteBuffer& image, size_t size) { // 3E+ cart is identified key 'TJ3E' in the ROM uInt8 tj3e[] = { 'T', 'J', '3', 'E' }; - return searchForBytes(image.get(), size, tj3e, 4, 1); + return searchForBytes(image, size, tj3e, 4); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -655,7 +673,7 @@ bool CartDetector::isProbably3F(const ByteBuffer& image, size_t size) // We expect it will be present at least 2 times, since there are // at least two banks uInt8 signature[] = { 0x85, 0x3F }; // STA $3F - return searchForBytes(image.get(), size, signature, 2, 2); + return searchForBytes(image, size, signature, 2, 2); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -701,12 +719,12 @@ bool CartDetector::isProbablyBF(const ByteBuffer& image, size_t size, // This signature is attributed to "RevEng" of AtariAge uInt8 bf[] = { 'B', 'F', 'B', 'F' }; uInt8 bfsc[] = { 'B', 'F', 'S', 'C' }; - if(searchForBytes(image.get()+size-8, 8, bf, 4, 1)) + if(searchForBytes(image.get()+size-8, 8, bf, 4)) { type = Bankswitch::Type::_BF; return true; } - else if(searchForBytes(image.get()+size-8, 8, bfsc, 4, 1)) + else if(searchForBytes(image.get()+size-8, 8, bfsc, 4)) { type = Bankswitch::Type::_BFSC; return true; @@ -722,7 +740,7 @@ bool CartDetector::isProbablyBUS(const ByteBuffer& image, size_t size) // Note: all Harmony/Melody custom drivers also contain the value // 0x10adab1e (LOADABLE) if needed for future improvement uInt8 bus[] = { 'B', 'U', 'S'}; - return searchForBytes(image.get(), size, bus, 3, 2); + return searchForBytes(image, size, bus, 3, 2); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -732,14 +750,14 @@ bool CartDetector::isProbablyCDF(const ByteBuffer& image, size_t size) // Note: all Harmony/Melody custom drivers also contain the value // 0x10adab1e (LOADABLE) if needed for future improvement uInt8 cdf[] = { 'C', 'D', 'F' }; - return searchForBytes(image.get(), size, cdf, 3, 3); + return searchForBytes(image, size, cdf, 3, 3); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool CartDetector::isProbablyCTY(const ByteBuffer& image, size_t size) { uInt8 lenin[] = { 'L', 'E', 'N', 'I', 'N' }; - return searchForBytes(image.get(), size, lenin, 5, 1); + return searchForBytes(image, size, lenin, 5); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -751,28 +769,10 @@ bool CartDetector::isProbablyCV(const ByteBuffer& image, size_t size) { 0x9D, 0xFF, 0xF3 }, // STA $F3FF.X { 0x99, 0x00, 0xF4 } // STA $F400.Y }; - if(searchForBytes(image.get(), size, signature[0], 3, 1)) + if(searchForBytes(image, size, signature[0], 3)) return true; else - return searchForBytes(image.get(), size, signature[1], 3, 1); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartDetector::isProbablyCVPlus(const ByteBuffer& image, size_t) -{ - // CV+ cart is identified key 'commavidplus' @ $04 in the ROM - // We inspect only this area to speed up the search - uInt8 cvp[12] = { 'c', 'o', 'm', 'm', 'a', 'v', 'i', 'd', - 'p', 'l', 'u', 's' }; - return searchForBytes(image.get()+4, 24, cvp, 12, 1); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartDetector::isProbablyDASH(const ByteBuffer& image, size_t size) -{ - // DASH cart is identified key 'TJAD' in the ROM - uInt8 tjad[] = { 'T', 'J', 'A', 'D' }; - return searchForBytes(image.get(), size, tjad, 4, 1); + return searchForBytes(image, size, signature[1], 3); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -784,12 +784,12 @@ bool CartDetector::isProbablyDF(const ByteBuffer& image, size_t size, // This signature is attributed to "RevEng" of AtariAge uInt8 df[] = { 'D', 'F', 'D', 'F' }; uInt8 dfsc[] = { 'D', 'F', 'S', 'C' }; - if(searchForBytes(image.get()+size-8, 8, df, 4, 1)) + if(searchForBytes(image.get()+size-8, 8, df, 4)) { type = Bankswitch::Type::_DF; return true; } - else if(searchForBytes(image.get()+size-8, 8, dfsc, 4, 1)) + else if(searchForBytes(image.get()+size-8, 8, dfsc, 4)) { type = Bankswitch::Type::_DFSC; return true; @@ -805,7 +805,7 @@ bool CartDetector::isProbablyDPCplus(const ByteBuffer& image, size_t size) // Note: all Harmony/Melody custom drivers also contain the value // 0x10adab1e (LOADABLE) if needed for future improvement uInt8 dpcp[] = { 'D', 'P', 'C', '+' }; - return searchForBytes(image.get(), size, dpcp, 4, 2); + return searchForBytes(image, size, dpcp, 4, 2); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -828,7 +828,7 @@ bool CartDetector::isProbablyE0(const ByteBuffer& image, size_t size) { 0xAD, 0xF3, 0xBF } // LDA $BFF3 }; for(uInt32 i = 0; i < 8; ++i) - if(searchForBytes(image.get(), size, signature[i], 3, 1)) + if(searchForBytes(image, size, signature[i], 3)) return true; return false; @@ -853,7 +853,7 @@ bool CartDetector::isProbablyE7(const ByteBuffer& image, size_t size) { 0x8D, 0xE7, 0x1F } // STA $1FE7 }; for(uInt32 i = 0; i < 7; ++i) - if(searchForBytes(image.get(), size, signature[i], 3, 1)) + if(searchForBytes(image, size, signature[i], 3)) return true; return false; @@ -872,7 +872,7 @@ bool CartDetector::isProbablyE78K(const ByteBuffer& image, size_t size) { 0xAD, 0xE6, 0xFF }, // LDA $FFE6 }; for(uInt32 i = 0; i < 3; ++i) - if(searchForBytes(image.get(), size, signature[i], 3, 1)) + if(searchForBytes(image, size, signature[i], 3)) return true; return false; @@ -886,12 +886,12 @@ bool CartDetector::isProbablyEF(const ByteBuffer& image, size_t size, // This signature is attributed to "RevEng" of AtariAge uInt8 efef[] = { 'E', 'F', 'E', 'F' }; uInt8 efsc[] = { 'E', 'F', 'S', 'C' }; - if(searchForBytes(image.get()+size-8, 8, efef, 4, 1)) + if(searchForBytes(image.get()+size-8, 8, efef, 4)) { type = Bankswitch::Type::_EF; return true; } - else if(searchForBytes(image.get()+size-8, 8, efsc, 4, 1)) + else if(searchForBytes(image.get()+size-8, 8, efsc, 4)) { type = Bankswitch::Type::_EFSC; return true; @@ -909,7 +909,7 @@ bool CartDetector::isProbablyEF(const ByteBuffer& image, size_t size, }; for(uInt32 i = 0; i < 4; ++i) { - if(searchForBytes(image.get(), size, signature[i], 3, 1)) + if(searchForBytes(image, size, signature[i], 3)) { isEF = true; break; @@ -951,14 +951,13 @@ bool CartDetector::isProbablyFC(const ByteBuffer& image, size_t size) { 0x8d, 0xf8, 0xff, 0x8d, 0xfc, 0xff }, // STA $FFF8, STA $FFFC Surf's Up (4K) { 0x8c, 0xf9, 0xff, 0xad, 0xfc, 0xff } // STY $FFF9, LDA $FFFC 3-D Havoc }; - for (uInt32 i = 0; i < 3; ++i) - if (searchForBytes(image.get(), size, signature[i], 6, 1)) + for(uInt32 i = 0; i < 3; ++i) + if(searchForBytes(image, size, signature[i], 6)) return true; return false; } - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool CartDetector::isProbablyFE(const ByteBuffer& image, size_t size) { @@ -972,7 +971,7 @@ bool CartDetector::isProbablyFE(const ByteBuffer& image, size_t size) { 0x20, 0x00, 0xF0, 0x84, 0xD6 } // JSR $F000; $84, $D6 }; for(uInt32 i = 0; i < 4; ++i) - if(searchForBytes(image.get(), size, signature[i], 5, 1)) + if(searchForBytes(image, size, signature[i], 5)) return true; return false; @@ -983,7 +982,7 @@ bool CartDetector::isProbablyMDM(const ByteBuffer& image, size_t size) { // MDM cart is identified key 'MDMC' in the first 8K of ROM uInt8 mdmc[] = { 'M', 'D', 'M', 'C' }; - return searchForBytes(image.get(), std::min(size, 8_KB), mdmc, 4, 1); + return searchForBytes(image, std::min(size, 8_KB), mdmc, 4); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -994,10 +993,10 @@ bool CartDetector::isProbablySB(const ByteBuffer& image, size_t size) { 0xBD, 0x00, 0x08 }, // LDA $0800,x { 0xAD, 0x00, 0x08 } // LDA $0800 }; - if(searchForBytes(image.get(), size, signature[0], 3, 1)) + if(searchForBytes(image, size, signature[0], 3)) return true; else - return searchForBytes(image.get(), size, signature[1], 3, 1); + return searchForBytes(image, size, signature[1], 3); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1016,7 +1015,7 @@ bool CartDetector::isProbablyUA(const ByteBuffer& image, size_t size) { 0xAD, 0xC0, 0x02 } // LDA $2C0 (Mickey) }; for(uInt32 i = 0; i < 6; ++i) - if(searchForBytes(image.get(), size, signature[i], 3, 1)) + if(searchForBytes(image, size, signature[i], 3)) return true; return false; @@ -1029,10 +1028,9 @@ bool CartDetector::isProbablyWD(const ByteBuffer& image, size_t size) uInt8 signature[1][3] = { { 0xA5, 0x39, 0x4C } // LDA $39, JMP }; - return searchForBytes(image.get(), size, signature[0], 3, 1); + return searchForBytes(image, size, signature[0], 3); } - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool CartDetector::isProbablyX07(const ByteBuffer& image, size_t size) { @@ -1046,7 +1044,7 @@ bool CartDetector::isProbablyX07(const ByteBuffer& image, size_t size) { 0x0C, 0x2D, 0x08 } // NOP $082D }; for(uInt32 i = 0; i < 6; ++i) - if(searchForBytes(image.get(), size, signature[i], 3, 1)) + if(searchForBytes(image, size, signature[i], 3)) return true; return false; diff --git a/src/emucore/CartDetector.hxx b/src/emucore/CartDetector.hxx index 5f6a60674..706731b0f 100644 --- a/src/emucore/CartDetector.hxx +++ b/src/emucore/CartDetector.hxx @@ -107,7 +107,14 @@ class CartDetector */ static bool searchForBytes(const uInt8* image, size_t imagesize, const uInt8* signature, uInt32 sigsize, - uInt32 minhits); + uInt32 minhits = 1); + + static bool searchForBytes(const ByteBuffer& image, size_t imagesize, + const uInt8* signature, uInt32 sigsize, + uInt32 minhits = 1) + { + return searchForBytes(image.get(), imagesize, signature, sigsize, minhits); + } /** Returns true if the image is probably a SuperChip (128 bytes RAM) @@ -130,6 +137,11 @@ class CartDetector */ static bool isProbably3E(const ByteBuffer& image, size_t size); + /** + Returns true if the image is probably a 3EX bankswitching cartridge + */ + static bool isProbably3EX(const ByteBuffer& image, size_t size); + /** Returns true if the image is probably a 3E+ bankswitching cartridge */ @@ -175,16 +187,6 @@ class CartDetector */ static bool isProbablyCV(const ByteBuffer& image, size_t size); - /** - Returns true if the image is probably a CV+ bankswitching cartridge - */ - static bool isProbablyCVPlus(const ByteBuffer& image, size_t size); - - /** - Returns true if the image is probably a DASH bankswitching cartridge - */ - static bool isProbablyDASH(const ByteBuffer& image, size_t size); - /** Returns true if the image is probably a DF/DFSC bankswitching cartridge */ diff --git a/src/emucore/CartE0.cxx b/src/emucore/CartE0.cxx index ff3a9b52c..07afae5a2 100644 --- a/src/emucore/CartE0.cxx +++ b/src/emucore/CartE0.cxx @@ -21,230 +21,50 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeE0::CartridgeE0(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5) + : CartridgeEnhanced(image, size, md5, settings) { - // Copy the ROM image into my buffer - std::copy_n(image.get(), std::min(myImage.size(), size), myImage.begin()); - createRomAccessArrays(myImage.size()); + myBankShift = BANK_SHIFT; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void CartridgeE0::reset() { - // Setup segments to some default slices + // Setup segments to some default banks if(randomStartBank()) { - segmentZero(mySystem->randGenerator().next() % 8); - segmentOne(mySystem->randGenerator().next() % 8); - segmentTwo(mySystem->randGenerator().next() % 8); + bank(mySystem->randGenerator().next() % 8, 0); + bank(mySystem->randGenerator().next() % 8, 1); + bank(mySystem->randGenerator().next() % 8, 2); } else { - segmentZero(4); - segmentOne(5); - segmentTwo(6); + bank(4, 0); + bank(5, 1); + bank(6, 2); } - myCurrentSlice[3] = 7; // fixed - myBankChanged = true; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeE0::install(System& system) +bool CartridgeE0::checkSwitchBank(uInt16 address, uInt8) { - mySystem = &system; - - System::PageAccess access(this, System::PageAccessType::READ); - - // Set the page acessing methods for the first part of the last segment - for(uInt16 addr = 0x1C00; addr < static_cast(0x1FE0U & ~System::PAGE_MASK); - addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[0x1C00 + (addr & 0x03FF)]; - access.romAccessBase = &myRomAccessBase[0x1C00 + (addr & 0x03FF)]; - access.romPeekCounter = &myRomAccessCounter[0x1C00 + (addr & 0x03FF)]; - access.romPokeCounter = &myRomAccessCounter[0x1C00 + (addr & 0x03FF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Set the page accessing methods for the hot spots in the last segment - access.directPeekBase = nullptr; - access.romAccessBase = &myRomAccessBase[0x1FC0]; // TJ: is this the correct address (or 0x1FE0)? - access.romPeekCounter = &myRomAccessCounter[0x1FC0]; - access.romPokeCounter = &myRomAccessCounter[0x1FC0 + myAccessSize]; - access.type = System::PageAccessType::READ; - for(uInt16 addr = (0x1FE0 & ~System::PAGE_MASK); addr < 0x2000; - addr += System::PAGE_SIZE) - mySystem->setPageAccess(addr, access); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeE0::getBank(uInt16 address) const -{ - return myCurrentSlice[(address & 0xFFF) >> 10]; // 1K slices -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeE0::bankCount() const -{ - return 8; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeE0::peek(uInt16 address) -{ - address &= 0x0FFF; + address &= ROM_MASK; // Switch banks if necessary if((address >= 0x0FE0) && (address <= 0x0FE7)) { - segmentZero(address & 0x0007); + bank(address & 0x0007, 0); + return true; } else if((address >= 0x0FE8) && (address <= 0x0FEF)) { - segmentOne(address & 0x0007); + bank(address & 0x0007, 1); + return true; } else if((address >= 0x0FF0) && (address <= 0x0FF7)) { - segmentTwo(address & 0x0007); - } - - return myImage[(myCurrentSlice[address >> 10] << 10) + (address & 0x03FF)]; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeE0::poke(uInt16 address, uInt8) -{ - address &= 0x0FFF; - - // Switch banks if necessary - if((address >= 0x0FE0) && (address <= 0x0FE7)) - { - segmentZero(address & 0x0007); - } - else if((address >= 0x0FE8) && (address <= 0x0FEF)) - { - segmentOne(address & 0x0007); - } - else if((address >= 0x0FF0) && (address <= 0x0FF7)) - { - segmentTwo(address & 0x0007); + bank(address & 0x0007, 2); + return true; } return false; } - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeE0::segmentZero(uInt16 slice) -{ - if(bankLocked()) return; - - // Remember the new slice - myCurrentSlice[0] = slice; - uInt16 offset = slice << 10; - - // Setup the page access methods for the current bank - System::PageAccess access(this, System::PageAccessType::READ); - - for(uInt16 addr = 0x1000; addr < 0x1400; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[offset + (addr & 0x03FF)]; - access.romAccessBase = &myRomAccessBase[offset + (addr & 0x03FF)]; - access.romPeekCounter = &myRomAccessCounter[offset + (addr & 0x03FF)]; - access.romPokeCounter = &myRomAccessCounter[offset + (addr & 0x03FF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeE0::segmentOne(uInt16 slice) -{ - if(bankLocked()) return; - - // Remember the new slice - myCurrentSlice[1] = slice; - uInt16 offset = slice << 10; - - // Setup the page access methods for the current bank - System::PageAccess access(this, System::PageAccessType::READ); - - for(uInt16 addr = 0x1400; addr < 0x1800; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[offset + (addr & 0x03FF)]; - access.romAccessBase = &myRomAccessBase[offset + (addr & 0x03FF)]; - access.romPeekCounter = &myRomAccessCounter[offset + (addr & 0x03FF)]; - access.romPokeCounter = &myRomAccessCounter[offset + (addr & 0x03FF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeE0::segmentTwo(uInt16 slice) -{ - if(bankLocked()) return; - - // Remember the new slice - myCurrentSlice[2] = slice; - uInt16 offset = slice << 10; - - // Setup the page access methods for the current bank - System::PageAccess access(this, System::PageAccessType::READ); - - for(uInt16 addr = 0x1800; addr < 0x1C00; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[offset + (addr & 0x03FF)]; - access.romAccessBase = &myRomAccessBase[offset + (addr & 0x03FF)]; - access.romPeekCounter = &myRomAccessCounter[offset + (addr & 0x03FF)]; - access.romPokeCounter = &myRomAccessCounter[offset + (addr & 0x03FF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeE0::patch(uInt16 address, uInt8 value) -{ - address &= 0x0FFF; - myImage[(myCurrentSlice[address >> 10] << 10) + (address & 0x03FF)] = value; - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* CartridgeE0::getImage(size_t& size) const -{ - size = myImage.size(); - return myImage.data(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeE0::save(Serializer& out) const -{ - try - { - out.putShortArray(myCurrentSlice.data(), myCurrentSlice.size()); - } - catch(...) - { - cerr << "ERROR: CartridgeE0::save" << endl; - return false; - } - - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeE0::load(Serializer& in) -{ - try - { - in.getShortArray(myCurrentSlice.data(), myCurrentSlice.size()); - } - catch(...) - { - cerr << "ERROR: CartridgeE0::load" << endl; - return false; - } - - return true; -} diff --git a/src/emucore/CartE0.hxx b/src/emucore/CartE0.hxx index 0a9453ab2..84c9ae667 100644 --- a/src/emucore/CartE0.hxx +++ b/src/emucore/CartE0.hxx @@ -21,7 +21,7 @@ class System; #include "bspf.hxx" -#include "Cart.hxx" +#include "CartEnhanced.hxx" #ifdef DEBUGGER_SUPPORT #include "CartE0Widget.hxx" #endif @@ -29,19 +29,19 @@ class System; /** This is the cartridge class for Parker Brothers' 8K games. In this bankswitching scheme the 2600's 4K cartridge address space - is broken into four 1K segments. The desired 1K slice of the + is broken into four 1K segments. The desired 1K bank of the ROM is selected by accessing $1FE0 to $1FE7 for the first 1K. - $1FE8 to $1FEF selects the slice for the second 1K, and $1FF0 to - $1FF7 selects the slice for the third 1K. The last 1K segment + $1FE8 to $1FEF selects the bank for the second 1K, and $1FF0 to + $1FF7 selects the bank for the third 1K. 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 Bradford W. Mott + @author Bradford W. Mott, Thomas Jentzsch */ -class CartridgeE0 : public Cartridge +class CartridgeE0 : public CartridgeEnhanced { friend class CartridgeE0Widget; @@ -64,60 +64,6 @@ class CartridgeE0 : public Cartridge */ void reset() override; - /** - 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; - - - /** - 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 bankCount() 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). @@ -137,51 +83,14 @@ class CartridgeE0 : public Cartridge } #endif - public: - /** - Get the byte at the specified address. + private: + bool checkSwitchBank(uInt16 address, uInt8 = 0) override; - @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; + uInt16 hotspot() const override { return 0x1FE0; } private: - /** - Install the specified slice for segment zero - - @param slice The slice to map into the segment - */ - void segmentZero(uInt16 slice); - - /** - Install the specified slice for segment one - - @param slice The slice to map into the segment - */ - void segmentOne(uInt16 slice); - - /** - Install the specified slice for segment two - - @param slice The slice to map into the segment - */ - void segmentTwo(uInt16 slice); - - private: - // The 8K ROM image of the cartridge - std::array myImage; - - // Indicates the slice mapped into each of the four segments - std::array myCurrentSlice; + // log(ROM bank segment size) / log(2) + static constexpr uInt16 BANK_SHIFT = 10; // = 1K = 0x0400 private: // Following constructors and assignment operators not supported diff --git a/src/emucore/CartEF.cxx b/src/emucore/CartEF.cxx index 16635200b..039f4aa5c 100644 --- a/src/emucore/CartEF.cxx +++ b/src/emucore/CartEF.cxx @@ -21,145 +21,20 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeEF::CartridgeEF(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5) + : CartridgeEnhanced(image, size, md5, settings) { - // Copy the ROM image into my buffer - std::copy_n(image.get(), std::min(myImage.size(), size), myImage.begin()); - createRomAccessArrays(myImage.size()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeEF::reset() +bool CartridgeEF::checkSwitchBank(uInt16 address, uInt8) { - initializeStartBank(1); - - // Upon reset we switch to the startup bank - bank(startBank()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeEF::install(System& system) -{ - mySystem = &system; - - // Install pages for the startup bank - bank(startBank()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeEF::peek(uInt16 address) -{ - address &= 0x0FFF; + address &= ROM_MASK; // Switch banks if necessary if((address >= 0x0FE0) && (address <= 0x0FEF)) + { bank(address - 0x0FE0); - - return myImage[myBankOffset + address]; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeEF::poke(uInt16 address, uInt8) -{ - address &= 0x0FFF; - - // Switch banks if necessary - if((address >= 0x0FE0) && (address <= 0x0FEF)) - bank(address - 0x0FE0); - + return true; + } return false; } - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeEF::bank(uInt16 bank) -{ - if(bankLocked()) return false; - - // Remember what bank we're in - myBankOffset = bank << 12; - - System::PageAccess access(this, System::PageAccessType::READ); - - // Set the page accessing methods for the hot spots - for(uInt16 addr = (0x1FE0 & ~System::PAGE_MASK); addr < 0x2000; - addr += System::PAGE_SIZE) - { - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Setup the page access methods for the current bank - for(uInt16 addr = 0x1000; addr < static_cast(0x1FE0U & ~System::PAGE_MASK); - addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[myBankOffset + (addr & 0x0FFF)]; - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeEF::getBank(uInt16) const -{ - return myBankOffset >> 12; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeEF::bankCount() const -{ - return 16; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeEF::patch(uInt16 address, uInt8 value) -{ - myImage[myBankOffset + (address & 0x0FFF)] = value; - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* CartridgeEF::getImage(size_t& size) const -{ - size = myImage.size(); - return myImage.data(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeEF::save(Serializer& out) const -{ - try - { - out.putShort(myBankOffset); - } - catch(...) - { - cerr << "ERROR: CartridgeEF::save" << endl; - return false; - } - - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeEF::load(Serializer& in) -{ - try - { - myBankOffset = in.getShort(); - } - catch(...) - { - cerr << "ERROR: CartridgeEF::load" << endl; - return false; - } - - // Remember what bank we were in - bank(myBankOffset >> 12); - - return true; -} diff --git a/src/emucore/CartEF.hxx b/src/emucore/CartEF.hxx index 62a10975d..ff71b9909 100644 --- a/src/emucore/CartEF.hxx +++ b/src/emucore/CartEF.hxx @@ -21,7 +21,7 @@ class System; #include "bspf.hxx" -#include "Cart.hxx" +#include "CartEnhanced.hxx" #ifdef DEBUGGER_SUPPORT #include "CartEFWidget.hxx" #endif @@ -31,9 +31,9 @@ class System; There are 16 4K banks (total of 64K ROM). Accessing $1FE0 - $1FEF switches to each bank. - @author Stephen Anthony + @author Stephen Anthony, Thomas Jentzsch */ -class CartridgeEF : public Cartridge +class CartridgeEF : public CartridgeEnhanced { friend class CartridgeEFWidget; @@ -51,71 +51,6 @@ class CartridgeEF : public Cartridge virtual ~CartridgeEF() = default; public: - /** - Reset device to its power-on state - */ - void reset() override; - - /** - 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; - - /** - Install pages for the specified bank in the system. - - @param bank The bank that should be installed in the system - */ - bool bank(uInt16 bank) 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 bankCount() 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). @@ -135,29 +70,12 @@ class CartridgeEF : public Cartridge } #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: - // The 64K ROM image of the cartridge - std::array myImage; + bool checkSwitchBank(uInt16 address, uInt8 value = 0) override; - // Indicates the offset into the ROM image (aligns to current bank) - uInt16 myBankOffset{0}; + uInt16 hotspot() const override { return 0x1FE0; } + + uInt16 getStartBank() const override { return 1; } private: // Following constructors and assignment operators not supported diff --git a/src/emucore/CartEFSC.cxx b/src/emucore/CartEFSC.cxx index 3502be7a0..c70cff194 100644 --- a/src/emucore/CartEFSC.cxx +++ b/src/emucore/CartEFSC.cxx @@ -21,205 +21,7 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeEFSC::CartridgeEFSC(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5) + : CartridgeEF(image, size, md5, settings) { - // Copy the ROM image into my buffer - std::copy_n(image.get(), std::min(myImage.size(), size), myImage.begin()); - createRomAccessArrays(myImage.size()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeEFSC::reset() -{ - initializeRAM(myRAM.data(), myRAM.size()); - initializeStartBank(15); - - // Upon reset we switch to the startup bank - bank(startBank()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeEFSC::install(System& system) -{ - mySystem = &system; - - 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 - access.type = System::PageAccessType::WRITE; - for(uInt16 addr = 0x1000; addr < 0x1080; addr += System::PAGE_SIZE) - { - access.romAccessBase = &myRomAccessBase[addr & 0x007F]; - access.romPeekCounter = &myRomAccessCounter[addr & 0x007F]; - access.romPokeCounter = &myRomAccessCounter[(addr & 0x007F) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Set the page accessing method for the RAM reading pages - access.type = System::PageAccessType::READ; - for(uInt16 addr = 0x1080; addr < 0x1100; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myRAM[addr & 0x007F]; - access.romAccessBase = &myRomAccessBase[0x80 + (addr & 0x007F)]; - access.romPeekCounter = &myRomAccessCounter[0x80 + (addr & 0x007F)]; - access.romPokeCounter = &myRomAccessCounter[0x80 + (addr & 0x007F) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Install pages for the startup bank - bank(startBank()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeEFSC::peek(uInt16 address) -{ - uInt16 peekAddress = address; - address &= 0x0FFF; - - // Switch banks if necessary - if((address >= 0x0FE0) && (address <= 0x0FEF)) - bank(address - 0x0FE0); - - if(address < 0x0080) // Write port is at 0xF000 - 0xF07F (128 bytes) - return peekRAM(myRAM[address], peekAddress); - else - return myImage[myBankOffset + address]; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeEFSC::poke(uInt16 address, uInt8 value) -{ - uInt16 pokeAddress = address; - address &= 0x0FFF; - - // Switch banks if necessary - if((address >= 0x0FE0) && (address <= 0x0FEF)) - { - bank(address - 0x0FE0); - return false; - } - - if (!(address & 0x080)) - { - pokeRAM(myRAM[address & 0x007F], pokeAddress, value); - return true; - } - else - { - // Writing to the read port should be ignored, but trigger a break if option enabled - uInt8 dummy; - - pokeRAM(dummy, pokeAddress, value); - myRamWriteAccess = pokeAddress; - return false; - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeEFSC::bank(uInt16 bank) -{ - if(bankLocked()) return false; - - // Remember what bank we're in - myBankOffset = bank << 12; - - System::PageAccess access(this, System::PageAccessType::READ); - - // Set the page accessing methods for the hot spots - for(uInt16 addr = (0x1FE0 & ~System::PAGE_MASK); addr < 0x2000; - addr += System::PAGE_SIZE) - { - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Setup the page access methods for the current bank - for(uInt16 addr = 0x1100; addr < static_cast(0x1FE0U & ~System::PAGE_MASK); - addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[myBankOffset + (addr & 0x0FFF)]; - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeEFSC::getBank(uInt16) const -{ - return myBankOffset >> 12; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeEFSC::bankCount() const -{ - return 16; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeEFSC::patch(uInt16 address, uInt8 value) -{ - address &= 0x0FFF; - - if(address < 0x0100) - { - // Normally, a write to the read port won't do anything - // However, the patch command is special in that ignores such - // cart restrictions - myRAM[address & 0x007F] = value; - } - else - myImage[myBankOffset + address] = value; - - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* CartridgeEFSC::getImage(size_t& size) const -{ - size = myImage.size(); - return myImage.data(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeEFSC::save(Serializer& out) const -{ - try - { - out.putShort(myBankOffset); - out.putByteArray(myRAM.data(), myRAM.size()); - } - catch(...) - { - cerr << "ERROR: CartridgeEFSC::save" << endl; - return false; - } - - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeEFSC::load(Serializer& in) -{ - try - { - myBankOffset = in.getShort(); - in.getByteArray(myRAM.data(), myRAM.size()); - } - catch(...) - { - cerr << "ERROR: CartridgeEFSC::load" << endl; - return false; - } - - // Remember what bank we were in - bank(myBankOffset >> 12); - - return true; + myRamSize = RAM_SIZE; } diff --git a/src/emucore/CartEFSC.hxx b/src/emucore/CartEFSC.hxx index 810b27b22..f6c7e40ea 100644 --- a/src/emucore/CartEFSC.hxx +++ b/src/emucore/CartEFSC.hxx @@ -21,7 +21,7 @@ class System; #include "bspf.hxx" -#include "Cart.hxx" +#include "CartEF.hxx" #ifdef DEBUGGER_SUPPORT #include "CartEFSCWidget.hxx" #endif @@ -32,9 +32,9 @@ class System; Accessing $1FE0 - $1FEF switches to each bank. RAM read port is $1080 - $10FF, write port is $1000 - $107F. - @author Stephen Anthony + @author Stephen Anthony, Thomas Jentzsch */ -class CartridgeEFSC : public Cartridge +class CartridgeEFSC : public CartridgeEF { friend class CartridgeEFSCWidget; @@ -52,71 +52,6 @@ class CartridgeEFSC : public Cartridge virtual ~CartridgeEFSC() = default; public: - /** - Reset device to its power-on state - */ - void reset() override; - - /** - 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; - - /** - Install pages for the specified bank in the system. - - @param bank The bank that should be installed in the system - */ - bool bank(uInt16 bank) 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 bankCount() 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). @@ -136,32 +71,12 @@ class CartridgeEFSC : public Cartridge } #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: + uInt16 getStartBank() const override { return 15; } private: - // The 64K ROM image of the cartridge - std::array myImage; - - // The 128 bytes of RAM - std::array myRAM; - - // Indicates the offset into the ROM image (aligns to current bank) - uInt16 myBankOffset{0}; + // RAM size + static constexpr size_t RAM_SIZE = 0x80; private: // Following constructors and assignment operators not supported diff --git a/src/emucore/CartEnhanced.cxx b/src/emucore/CartEnhanced.cxx new file mode 100644 index 000000000..f5c38c9a3 --- /dev/null +++ b/src/emucore/CartEnhanced.cxx @@ -0,0 +1,370 @@ +//============================================================================ +// +// 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-2020 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 "CartEnhanced.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeEnhanced::CartridgeEnhanced(const ByteBuffer& image, size_t size, + const string& md5, const Settings& settings) + : Cartridge(settings, md5), + mySize(size) +{ + // Allocate array for the ROM image (at least 64 bytzes) + myImage = make_unique(std::max(uInt32(mySize), uInt32(System::PAGE_SIZE))); + + // Copy the ROM image into my buffer + std::copy_n(image.get(), mySize, myImage.get()); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeEnhanced::install(System& system) +{ + // limit banked RAM size to the size of one RAM bank + uInt32 ramSize = myRamBankCount > 0 ? 1 << (myBankShift - 1) : uInt32(myRamSize); + + // calculate bank switching and RAM sizes and masks + myBankSize = 1 << myBankShift; // e.g. = 2 ^ 12 = 4K = 0x1000 + myBankMask = myBankSize - 1; // e.g. = 0x0FFF + myBankSegs = 1 << (MAX_BANK_SHIFT - myBankShift); // e.g. = 1 + myRomOffset = myRamBankCount > 0 ? 0 : uInt32(myRamSize) * 2; + myRamMask = ramSize - 1; // e.g. = 0xFFFF (doesn't matter for RAM size 0) + myWriteOffset = myRamWpHigh ? ramSize : 0; // e.g. = 0x0000 + myReadOffset = myRamWpHigh ? 0 : ramSize; // e.g. = 0x0080 + + createRomAccessArrays(mySize + (myRomOffset > 0 ? 0 : myRamSize)); + + // Allocate array for the segment's current bank offset + myCurrentSegOffset = make_unique(myBankSegs); + + // Allocate array for the RAM area + myRAM = make_unique(myRamSize); + + mySystem = &system; + + if(myRomOffset > 0) + { + // Setup page access for extended RAM; banked RAM will be setup in bank() + 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 + access.type = System::PageAccessType::WRITE; + 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]; + mySystem->setPageAccess(addr, access); + } + + // 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; + + access.directPeekBase = &myRAM[offset]; + access.romAccessBase = &myRomAccessBase[myReadOffset + offset]; + access.romPeekCounter = &myRomAccessCounter[myReadOffset + offset]; + access.romPokeCounter = &myRomAccessCounter[myReadOffset + offset + myAccessSize]; + mySystem->setPageAccess(addr, access); + } + } + + // Install pages for the startup bank (TODO: currently only in first bank segment) + bank(startBank(), 0); + if(mySize >= 4_KB && myBankSegs > 1) + // Setup the last bank segment to always point to the last ROM segment + bank(romBankCount() - 1, myBankSegs - 1); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeEnhanced::reset() +{ + initializeRAM(myRAM.get(), myRamSize); + + initializeStartBank(getStartBank()); + + // Upon reset we switch to the reset bank + bank(startBank()); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 CartridgeEnhanced::peek(uInt16 address) +{ + uInt16 peekAddress = address; + + // hotspots in TIA range are reacting to pokes only + if (hotspot() >= 0x80) + checkSwitchBank(address & ADDR_MASK); + + if(isRamBank(address)) + { + address &= myRamMask; + + // This is a read access to a write port! + // Reading from the write port triggers an unwanted write + // The RAM banks follow the ROM banks and are half the size of a ROM bank + return peekRAM(myRAM[((myCurrentSegOffset[(peekAddress & ROM_MASK) >> myBankShift] - mySize) >> 1) + address], + peekAddress); + } + address &= ROM_MASK; + + // Write port is e.g. at 0xF000 - 0xF07F (128 bytes) + if(address < myReadOffset + myRamSize && address >= myReadOffset) + { + // This is a read access to a write port! + // Reading from the write port triggers an unwanted write + return peekRAM(myRAM[address], peekAddress); + } + + return myImage[myCurrentSegOffset[(peekAddress & ROM_MASK) >> myBankShift] + + (peekAddress & myBankMask)]; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool CartridgeEnhanced::poke(uInt16 address, uInt8 value) +{ + // Switch banks if necessary + if (checkSwitchBank(address & ADDR_MASK, value)) + return false; + + if(myRamSize > 0) + { + // Code should never get here (System::PageAccess::directPoke() handles this) + uInt16 pokeAddress = address; + + if(isRamBank(address)) + { + if(bool(address & (myBankSize >> 1)) == myRamWpHigh) + { + address &= myRamMask; + // The RAM banks follow the ROM banks and are half the size of a ROM bank + pokeRAM(myRAM[((myCurrentSegOffset[(pokeAddress & ROM_MASK) >> myBankShift] - mySize) >> 1) + address], + pokeAddress, value); + return true; + } + } + else + { + //address &= myBankMask; + if(bool(address & myRamSize) == myRamWpHigh) + { + pokeRAM(myRAM[address & myRamMask], pokeAddress, value); + return true; + } + } + // Writing to the read port should be ignored, but trigger a break if option enabled + uInt8 dummy; + + pokeRAM(dummy, pokeAddress, value); + myRamWriteAccess = pokeAddress; + } + return false; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool CartridgeEnhanced::bank(uInt16 bank, uInt16 segment) +{ + if(bankLocked()) return false; + + uInt16 segmentOffset = segment << myBankShift; + + if(myRamBankCount == 0 || bank < romBankCount()) + { + // Setup ROM bank + uInt16 romBank = bank % romBankCount(); + // Remember what bank is in this segment + uInt32 bankOffset = myCurrentSegOffset[segment] = romBank << myBankShift; + uInt16 hotspot = this->hotspot(); + uInt16 hotSpotAddr; + // 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; + + if(hotspot & 0x1000) + hotSpotAddr = (hotspot & ~System::PAGE_MASK); + else + hotSpotAddr = 0xFFFF; // none + + System::PageAccess access(this, System::PageAccessType::READ); + // Setup the page access methods for the current bank + for(uInt16 addr = fromAddr; addr < toAddr; addr += System::PAGE_SIZE) + { + uInt32 offset = bankOffset + (addr & myBankMask); + + if(myDirectPeek && addr != hotSpotAddr) + access.directPeekBase = &myImage[offset]; + else + access.directPeekBase = nullptr; + access.romAccessBase = &myRomAccessBase[offset]; + access.romPeekCounter = &myRomAccessCounter[offset]; + access.romPokeCounter = &myRomAccessCounter[offset + myAccessSize]; + mySystem->setPageAccess(addr, access); + } + } + else + { + // Setup RAM bank + uInt16 ramBank = (bank - romBankCount()) % myRamBankCount; + // The RAM banks follow the ROM banks and are half the size of a ROM bank + uInt32 bankOffset = uInt32(mySize) + (ramBank << (myBankShift - 1)); + + // Remember what bank is in this segment + myCurrentSegOffset[segment] = uInt32(mySize) + (ramBank << myBankShift); + + // Set the page accessing method for the RAM writing pages + uInt16 fromAddr = (ROM_OFFSET + segmentOffset + myWriteOffset) & ~System::PAGE_MASK; + uInt16 toAddr = (ROM_OFFSET + segmentOffset + myWriteOffset + (myBankSize >> 1)) & ~System::PAGE_MASK; + System::PageAccess access(this, System::PageAccessType::WRITE); + + for(uInt16 addr = fromAddr; addr < toAddr; addr += System::PAGE_SIZE) + { + uInt32 offset = bankOffset + (addr & myRamMask); + + access.directPokeBase = &myRAM[offset - mySize]; + access.romAccessBase = &myRomAccessBase[offset]; + access.romPeekCounter = &myRomAccessCounter[offset]; + access.romPokeCounter = &myRomAccessCounter[offset + myAccessSize]; + mySystem->setPageAccess(addr, access); + } + + // Set the page accessing method for the RAM reading pages + fromAddr = (ROM_OFFSET + segmentOffset + myReadOffset) & ~System::PAGE_MASK; + toAddr = (ROM_OFFSET + segmentOffset + myReadOffset + (myBankSize >> 1)) & ~System::PAGE_MASK; + access.type = System::PageAccessType::READ; + access.directPokeBase = nullptr; + + for(uInt16 addr = fromAddr; addr < toAddr; addr += System::PAGE_SIZE) + { + uInt32 offset = bankOffset + (addr & myRamMask); + + access.directPeekBase = &myRAM[offset - mySize]; + access.romAccessBase = &myRomAccessBase[offset]; + access.romPeekCounter = &myRomAccessCounter[offset]; + access.romPokeCounter = &myRomAccessCounter[offset + myAccessSize]; + mySystem->setPageAccess(addr, access); + } + } + return myBankChanged = true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt16 CartridgeEnhanced::getBank(uInt16 address) const +{ + return myCurrentSegOffset[(address & ROM_MASK) >> myBankShift] >> myBankShift; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt16 CartridgeEnhanced::getSegmentBank(uInt16 segment) const +{ + return myCurrentSegOffset[segment % myBankSegs] >> myBankShift; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt16 CartridgeEnhanced::romBankCount() const +{ + return uInt16(mySize >> myBankShift); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt16 CartridgeEnhanced::ramBankCount() const +{ + return myRamBankCount; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool CartridgeEnhanced::isRamBank(uInt16 address) const +{ + return myRamBankCount > 0 ? getBank(address) >= romBankCount() : false; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool CartridgeEnhanced::patch(uInt16 address, uInt8 value) +{ + if(isRamBank(address)) + { + myRAM[((myCurrentSegOffset[(address & ROM_MASK) >> myBankShift] - mySize) >> 1) + (address & myRamMask)] = value; + } + else + { + if((address & myBankMask) < myRamSize * 2) + { + // Normally, a write to the read port won't do anything + // However, the patch command is special in that ignores such + // cart restrictions + myRAM[address & myRamMask] = value; + } + else + myImage[myCurrentSegOffset[(address & ROM_MASK) >> myBankShift] + (address & myBankMask)] = value; + } + + return myBankChanged = true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const uInt8* CartridgeEnhanced::getImage(size_t& size) const +{ + size = mySize; + return myImage.get(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool CartridgeEnhanced::save(Serializer& out) const +{ + try + { + out.putIntArray(myCurrentSegOffset.get(), myBankSegs); + if(myRamSize > 0) + out.putByteArray(myRAM.get(), myRamSize); + } + catch(...) + { + cerr << "ERROR: << " << name() << "::save" << endl; + return false; + } + + return true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool CartridgeEnhanced::load(Serializer& in) +{ + try + { + in.getIntArray(myCurrentSegOffset.get(), myBankSegs); + if(myRamSize > 0) + in.getByteArray(myRAM.get(), myRamSize); + } + catch(...) + { + cerr << "ERROR: " << name() << "::load" << endl; + return false; + } + // Restore bank segments + for(uInt16 i = 0; i < myBankSegs; ++i) + bank(getSegmentBank(i), i); + + return true; +} diff --git a/src/emucore/CartEnhanced.hxx b/src/emucore/CartEnhanced.hxx new file mode 100644 index 000000000..743a4b50e --- /dev/null +++ b/src/emucore/CartEnhanced.hxx @@ -0,0 +1,287 @@ +//============================================================================ +// +// 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-2020 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 CARTRIDGEENHANCED_HXX +#define CARTRIDGEENHANCED_HXX + +class System; + +#include "bspf.hxx" +#include "Cart.hxx" +#ifdef DEBUGGER_SUPPORT + #include "CartEnhancedWidget.hxx" +#endif + +/** + Enhanced cartridge base class used for multiple cart types. + + @author Thomas Jentzsch +*/ +class CartridgeEnhanced : public Cartridge +{ + friend class CartridgeEnhancedWidget; + + 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) + */ + CartridgeEnhanced(const ByteBuffer& image, size_t size, const string& md5, + const Settings& settings); + virtual ~CartridgeEnhanced() = 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; + + /** + Install pages for the specified bank in the system. + + @param bank The bank that should be installed in the system + @param segment The segment the bank should be using + + @return true, if bank has changed + */ + virtual bool bank(uInt16 bank, uInt16 segment); + + /** + Install pages for the specified bank in the system. + + @param bank The bank that should be installed in the system + + @return true, if bank has changed + */ + bool bank(uInt16 bank) override { return this->bank(bank, 0); } + + /** + Get the current bank. + + @param address The address to use when querying the bank + */ + uInt16 getBank(uInt16 address = 0) const override; + + /** + Get the current bank for a bank segment. + + @param segment The segment to get the bank for + */ + uInt16 getSegmentBank(uInt16 segment = 0) const; + + /** + Query the number of banks supported by the cartridge. + */ + uInt16 romBankCount() const override; + + /** + Query the number of RAM 'banks' supported by the cartridge. + */ + uInt16 ramBankCount() const override; + + /** + Check if the segment at that address contains a RAM bank + + @param address The address which defines the segment + + @return true, if the segment is currently mapped to a RAM bank + */ + bool isRamBank(uInt16 address) const; + + /** + 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 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; + + /** + Get the hotspot in ROM address space. + + @return The first hotspot address (ususally in ROM) space or 0 + */ + virtual uInt16 hotspot() const { return 0; } + // TODO: handle cases where there the hotspots cover multiple pages + + protected: + // The '2 ^ N = bank segment size' exponent + uInt16 myBankShift{BANK_SHIFT}; // default 12 (-> one 4K segment) + + // The size of a bank's segment + uInt16 myBankSize{uInt16(4_KB)}; + + // The mask for a bank segment + uInt16 myBankMask{ROM_MASK}; + + protected: + // The extra RAM size + size_t myRamSize{RAM_SIZE}; // default 0 + + // The number of RAM banks + uInt16 myRamBankCount{RAM_BANKS}; // default 0 + + // The mask for the extra RAM + uInt16 myRamMask{0}; // RAM_SIZE - 1, but doesn't matter when RAM_SIZE is 0 + + // The number of segments a bank is split into (default 1) + uInt16 myBankSegs{1}; + + // The offset into ROM space for reading from ROM + // This is zero for types without RAM and with banked RAM + // - xxSC = 0x0100 + // - FA(2) = 0x0200 + // - CV = 0x0800 + uInt16 myRomOffset{0}; + + // The offset into ROM space for writing to RAM + // - xxSC = 0x0000 + // - FA(2) = 0x0000 + // - CV = 0x0400 + uInt16 myWriteOffset{0}; + + // The offset into ROM space for reading from RAM + // - xxSC = 0x0080 + // - FA(2) = 0x0100 + // - CV = 0x0000 + uInt16 myReadOffset{0}; + + // Flag, true if write port is at high and read port is at low address + bool myRamWpHigh{RAM_HIGH_WP}; + + // Pointer to a dynamically allocated ROM image of the cartridge + ByteBuffer myImage{nullptr}; + + // Contains the offset into the ROM image for each of the bank segments + DWordBuffer myCurrentSegOffset{nullptr}; + + // Indicates whether to use direct ROM peeks or not + bool myDirectPeek{true}; + + // Pointer to a dynamically allocated RAM area of the cartridge + ByteBuffer myRAM{nullptr}; + + // The size of the ROM image + size_t mySize{0}; + + protected: + // The mask for 6507 address space + static constexpr uInt16 ADDR_MASK = 0x1FFF; + + // The offset into address space for accessing ROM + static constexpr uInt16 ROM_OFFSET = 0x1000; + + // The mask for ROM address space + static constexpr uInt16 ROM_MASK = 0x0FFF; + + private: + // Calculated as: log(ROM bank segment size) / log(2) + static constexpr uInt16 BANK_SHIFT = 12; // default = 4K + + // The size of extra RAM in ROM address space + static constexpr size_t RAM_SIZE = 0; // default = none + + // The number of RAM banks + static constexpr uInt16 RAM_BANKS = 0; + + // Write port for extra RAM is at low address by default + static constexpr bool RAM_HIGH_WP = false; + + // The maximum shift (for a 4K bank size) + static constexpr uInt16 MAX_BANK_SHIFT = 12; // -> 4K + + protected: + /** + Check hotspots and switch bank if triggered. + + @param address The address to check + @param value The optional value used to determine the bank switched to + @return True if a bank switch happened. + */ + virtual bool checkSwitchBank(uInt16 address, uInt8 value = 0) = 0; + + private: + /** + Get the ROM's startup bank. + + @return The bank the ROM will start in + */ + virtual uInt16 getStartBank() const { return 0; } + + private: + // Following constructors and assignment operators not supported + CartridgeEnhanced() = delete; + CartridgeEnhanced(const CartridgeEnhanced&) = delete; + CartridgeEnhanced(CartridgeEnhanced&&) = delete; + CartridgeEnhanced& operator=(const CartridgeEnhanced&) = delete; + CartridgeEnhanced& operator=(CartridgeEnhanced&&) = delete; +}; + +#endif diff --git a/src/emucore/CartF0.cxx b/src/emucore/CartF0.cxx index a69c3c8ed..8747c57f3 100644 --- a/src/emucore/CartF0.cxx +++ b/src/emucore/CartF0.cxx @@ -15,159 +15,25 @@ // this file, and for a DISCLAIMER OF ALL WARRANTIES. //============================================================================ -#include "System.hxx" #include "CartF0.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeF0::CartridgeF0(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5) + : CartridgeEnhanced(image, size, md5, settings) { - // Copy the ROM image into my buffer - std::copy_n(image.get(), std::min(myImage.size(), size), myImage.begin()); - createRomAccessArrays(myImage.size()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF0::reset() +bool CartridgeF0::checkSwitchBank(uInt16 address, uInt8) { - // Upon reset we switch to the startup bank - initializeStartBank(15); - bank(startBank()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF0::install(System& system) -{ - mySystem = &system; - - // Install pages for the startup bank - bank(startBank()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeF0::peek(uInt16 address) -{ - address &= 0x0FFF; - - // Switch to next bank - if(address == 0x0FF0) - incbank(); - - return myImage[myBankOffset + address]; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF0::poke(uInt16 address, uInt8) -{ - address &= 0x0FFF; - - // Switch to next bank - if(address == 0x0FF0) - incbank(); - + // Switch banks if necessary + if(address == 0x1FF0) + { + // Switch to next bank + uInt8 nextBank = ((getBank()) + 1) & 0x0F; + bank(nextBank); + return true; + } return false; } - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF0::incbank() -{ - // Determine current bank, and increment to the next one - uInt8 nextBank = ((myBankOffset >> 12) + 1) & 0x0F; - bank(nextBank); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF0::bank(uInt16 bank) -{ - if(bankLocked()) return false; - - // Remember what bank we're in - myBankOffset = bank << 12; - - System::PageAccess access(this, System::PageAccessType::READ); - - // Set the page accessing methods for the hot spots - for(uInt16 addr = (0x1FF0 & ~System::PAGE_MASK); addr < 0x2000; - addr += System::PAGE_SIZE) - { - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Setup the page access methods for the current bank - for(uInt16 addr = 0x1000; addr < static_cast(0x1FF0U & ~System::PAGE_MASK); - addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[myBankOffset + (addr & 0x0FFF)]; - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeF0::getBank(uInt16) const -{ - return myBankOffset >> 12; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeF0::bankCount() const -{ - return 16; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF0::patch(uInt16 address, uInt8 value) -{ - myImage[myBankOffset + (address & 0x0FFF)] = value; - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* CartridgeF0::getImage(size_t& size) const -{ - size = myImage.size(); - return myImage.data(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF0::save(Serializer& out) const -{ - try - { - out.putShort(myBankOffset); - } - catch(...) - { - cerr << "ERROR: CartridgeF0::save" << endl; - return false; - } - - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF0::load(Serializer& in) -{ - try - { - myBankOffset = in.getShort(); - } - catch(...) - { - cerr << "ERROR: CartridgeF0::load" << endl; - return false; - } - - // Remember what bank we were in - bank(myBankOffset >> 12); - - return true; -} diff --git a/src/emucore/CartF0.hxx b/src/emucore/CartF0.hxx index d4733a784..0d5837066 100644 --- a/src/emucore/CartF0.hxx +++ b/src/emucore/CartF0.hxx @@ -18,10 +18,7 @@ #ifndef CARTRIDGEF0_HXX #define CARTRIDGEF0_HXX -class System; - -#include "bspf.hxx" -#include "Cart.hxx" +#include "CartEnhanced.hxx" #ifdef DEBUGGER_SUPPORT #include "CartF0Widget.hxx" #endif @@ -31,9 +28,9 @@ class System; There are 16 4K banks. Accessing $1FF0 switches to next bank. - @author Eckhard Stolberg + @author Eckhard Stolberg, Thomas Jentzsch */ -class CartridgeF0 : public Cartridge +class CartridgeF0 : public CartridgeEnhanced { friend class CartridgeF0Widget; @@ -51,71 +48,6 @@ class CartridgeF0 : public Cartridge virtual ~CartridgeF0() = default; public: - /** - Reset device to its power-on state - */ - void reset() override; - - /** - 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; - - /** - Install pages for the specified bank in the system. - - @param bank The bank that should be installed in the system - */ - bool bank(uInt16 bank) 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 bankCount() 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). @@ -135,35 +67,12 @@ class CartridgeF0 : public Cartridge } #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: - /** - Install pages for the next bank in the system - */ - void incbank(); + bool checkSwitchBank(uInt16 address, uInt8 value = 0) override; - private: - // The 64K ROM image of the cartridge - std::array myImage; + uInt16 hotspot() const override { return 0x1FF0; } - // Indicates the offset into the ROM image (aligns to current bank) - uInt16 myBankOffset{0}; + uInt16 getStartBank() const override { return 15; } private: // Following constructors and assignment operators not supported diff --git a/src/emucore/CartF4.cxx b/src/emucore/CartF4.cxx index 8111bb1a0..6a8cd6e00 100644 --- a/src/emucore/CartF4.cxx +++ b/src/emucore/CartF4.cxx @@ -15,155 +15,24 @@ // this file, and for a DISCLAIMER OF ALL WARRANTIES. //============================================================================ -#include "Random.hxx" -#include "System.hxx" #include "CartF4.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeF4::CartridgeF4(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5) + : CartridgeEnhanced(image, size, md5, settings) { - // Copy the ROM image into my buffer - std::copy_n(image.get(), std::min(myImage.size(), size), myImage.begin()); - createRomAccessArrays(myImage.size()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF4::reset() +bool CartridgeF4::checkSwitchBank(uInt16 address, uInt8) { - // Upon reset we switch to the startup bank - initializeStartBank(0); - bank(startBank()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF4::install(System& system) -{ - mySystem = &system; - - // Install pages for the startup bank - bank(startBank()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeF4::peek(uInt16 address) -{ - address &= 0x0FFF; - // Switch banks if necessary - if((address >= 0x0FF4) && (address <= 0x0FFB)) + // Note: addresses could be calculated from hotspot and bank count + if((address >= 0x1FF4) && (address <= 0x1FFB)) { - bank(address - 0x0FF4); + bank(address - 0x1FF4); + return true; } - - return myImage[myBankOffset + address]; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF4::poke(uInt16 address, uInt8) -{ - address &= 0x0FFF; - - // Switch banks if necessary - if((address >= 0x0FF4) && (address <= 0x0FFB)) - { - bank(address - 0x0FF4); - } - return false; } - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF4::bank(uInt16 bank) -{ - if(bankLocked()) return false; - - // Remember what bank we're in - myBankOffset = bank << 12; - - System::PageAccess access(this, System::PageAccessType::READ); - - // Set the page accessing methods for the hot spots - for(uInt16 addr = (0x1FF4 & ~System::PAGE_MASK); addr < 0x2000; - addr += System::PAGE_SIZE) - { - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Setup the page access methods for the current bank - for(uInt16 addr = 0x1000; addr < static_cast(0x1FF4U & ~System::PAGE_MASK); - addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[myBankOffset + (addr & 0x0FFF)]; - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeF4::getBank(uInt16) const -{ - return myBankOffset >> 12; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeF4::bankCount() const -{ - return 8; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF4::patch(uInt16 address, uInt8 value) -{ - myImage[myBankOffset + (address & 0x0FFF)] = value; - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* CartridgeF4::getImage(size_t& size) const -{ - size = myImage.size(); - return myImage.data(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF4::save(Serializer& out) const -{ - try - { - out.putShort(myBankOffset); - } - catch(...) - { - cerr << "ERROR: CartridgeF4::save" << endl; - return false; - } - - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF4::load(Serializer& in) -{ - try - { - myBankOffset = in.getShort(); - } - catch(...) - { - cerr << "ERROR: CartridgeF4::load" << endl; - return false; - } - - // Remember what bank we were in - bank(myBankOffset >> 12); - - return true; -} diff --git a/src/emucore/CartF4.hxx b/src/emucore/CartF4.hxx index d9c8fd346..d44e96640 100644 --- a/src/emucore/CartF4.hxx +++ b/src/emucore/CartF4.hxx @@ -18,10 +18,7 @@ #ifndef CARTRIDGEF4_HXX #define CARTRIDGEF4_HXX -class System; - -#include "bspf.hxx" -#include "Cart.hxx" +#include "CartEnhanced.hxx" #ifdef DEBUGGER_SUPPORT #include "CartF4Widget.hxx" #endif @@ -30,9 +27,9 @@ class System; Cartridge class used for Atari's 32K bankswitched games. There are eight 4K banks, accessible by read/write to $1FF4 - $1FFB. - @author Bradford W. Mott + @author Bradford W. Mott, Thomas Jentzsch */ -class CartridgeF4 : public Cartridge +class CartridgeF4 : public CartridgeEnhanced { friend class CartridgeF4Widget; @@ -50,71 +47,6 @@ class CartridgeF4 : public Cartridge virtual ~CartridgeF4() = default; public: - /** - Reset device to its power-on state - */ - void reset() override; - - /** - 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; - - /** - Install pages for the specified bank in the system. - - @param bank The bank that should be installed in the system - */ - bool bank(uInt16 bank) 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 bankCount() 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). @@ -134,31 +66,12 @@ class CartridgeF4 : public Cartridge } #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: - // The 32K ROM image of the cartridge - std::array myImage; + bool checkSwitchBank(uInt16 address, uInt8 value = 0) override; - // Indicates the offset into the ROM image (aligns to current bank) - uInt16 myBankOffset{0}; + uInt16 hotspot() const override { return 0x1FF4; } - private: +private: // Following constructors and assignment operators not supported CartridgeF4() = delete; CartridgeF4(const CartridgeF4&) = delete; diff --git a/src/emucore/CartF4SC.cxx b/src/emucore/CartF4SC.cxx index 24496b161..4fe6e13eb 100644 --- a/src/emucore/CartF4SC.cxx +++ b/src/emucore/CartF4SC.cxx @@ -15,211 +15,13 @@ // this file, and for a DISCLAIMER OF ALL WARRANTIES. //============================================================================ -#include "System.hxx" #include "CartF4SC.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeF4SC::CartridgeF4SC(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5) + : CartridgeF4(image, size, md5, settings) { - // Copy the ROM image into my buffer - std::copy_n(image.get(), std::min(myImage.size(), size), myImage.begin()); - createRomAccessArrays(myImage.size()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF4SC::reset() -{ - initializeRAM(myRAM.data(), myRAM.size()); - initializeStartBank(0); - - // Upon reset we switch to the startup bank - bank(startBank()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF4SC::install(System& system) -{ - mySystem = &system; - - 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 - access.type = System::PageAccessType::WRITE; - for(uInt16 addr = 0x1000; addr < 0x1080; addr += System::PAGE_SIZE) - { - access.romAccessBase = &myRomAccessBase[addr & 0x007F]; - access.romPeekCounter = &myRomAccessCounter[addr & 0x007F]; - access.romPokeCounter = &myRomAccessCounter[(addr & 0x07F) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Set the page accessing method for the RAM reading pages - access.type = System::PageAccessType::READ; - for(uInt16 addr = 0x1080; addr < 0x1100; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myRAM[addr & 0x007F]; - access.romAccessBase = &myRomAccessBase[0x80 + (addr & 0x007F)]; - access.romPeekCounter = &myRomAccessCounter[0x80 + (addr & 0x007F)]; - access.romPokeCounter = &myRomAccessCounter[0x80 + (addr & 0x007F) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Install pages for the startup bank - bank(startBank()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeF4SC::peek(uInt16 address) -{ - uInt16 peekAddress = address; - address &= 0x0FFF; - - // Switch banks if necessary - if((address >= 0x0FF4) && (address <= 0x0FFB)) - bank(address - 0x0FF4); - - if(address < 0x0080) // Write port is at 0xF000 - 0xF07F (128 bytes) - return peekRAM(myRAM[address], peekAddress); - else - return myImage[myBankOffset + address]; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF4SC::poke(uInt16 address, uInt8 value) -{ - uInt16 pokeAddress = address; - address &= 0x0FFF; - - // Switch banks if necessary - if((address >= 0x0FF4) && (address <= 0x0FFB)) - { - bank(address - 0x0FF4); - return false; - } - - if(!(address & 0x080)) - { - pokeRAM(myRAM[address & 0x007F], pokeAddress, value); - return true; - } - else - { - // Writing to the read port should be ignored, but trigger a break if option enabled - uInt8 dummy; - - pokeRAM(dummy, pokeAddress, value); - myRamWriteAccess = pokeAddress; - return false; - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF4SC::bank(uInt16 bank) -{ - if(bankLocked()) return false; - - // Remember what bank we're in - myBankOffset = bank << 12; - - System::PageAccess access(this, System::PageAccessType::READ); - - // Set the page accessing methods for the hot spots - for(uInt16 addr = (0x1FF4 & ~System::PAGE_MASK); addr < 0x2000; - addr += System::PAGE_SIZE) - { - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Setup the page access methods for the current bank - for(uInt16 addr = 0x1100; addr < static_cast(0x1FF4U & ~System::PAGE_MASK); - addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[myBankOffset + (addr & 0x0FFF)]; - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeF4SC::getBank(uInt16) const -{ - return myBankOffset >> 12; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeF4SC::bankCount() const -{ - return 8; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF4SC::patch(uInt16 address, uInt8 value) -{ - address &= 0x0FFF; - - if(address < 0x0100) - { - // Normally, a write to the read port won't do anything - // However, the patch command is special in that ignores such - // cart restrictions - myRAM[address & 0x007F] = value; - } - else - myImage[myBankOffset + address] = value; - - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* CartridgeF4SC::getImage(size_t& size) const -{ - size = myImage.size(); - return myImage.data(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF4SC::save(Serializer& out) const -{ - try - { - out.putShort(myBankOffset); - out.putByteArray(myRAM.data(), myRAM.size()); - } - catch(...) - { - cerr << "ERROR: CartridgeF4SC::save" << endl; - return false; - } - - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF4SC::load(Serializer& in) -{ - try - { - myBankOffset = in.getShort(); - in.getByteArray(myRAM.data(), myRAM.size()); - } - catch(...) - { - cerr << "ERROR: CartridgeF4SC::load" << endl; - return false; - } - - // Remember what bank we were in - bank(myBankOffset >> 12); - - return true; + myRamSize = RAM_SIZE; + myRamMask = RAM_SIZE - 1; } diff --git a/src/emucore/CartF4SC.hxx b/src/emucore/CartF4SC.hxx index 20c003970..58b255e42 100644 --- a/src/emucore/CartF4SC.hxx +++ b/src/emucore/CartF4SC.hxx @@ -18,10 +18,7 @@ #ifndef CARTRIDGEF4SC_HXX #define CARTRIDGEF4SC_HXX -class System; - -#include "bspf.hxx" -#include "Cart.hxx" +#include "CartF4.hxx" #ifdef DEBUGGER_SUPPORT #include "CartF4SCWidget.hxx" #endif @@ -31,9 +28,9 @@ class System; RAM. There are eight 4K banks, accessible by read/write to $1FF4 - $1FFB. RAM read port is $1080 - $10FF, write port is $1000 - $107F. - @author Bradford W. Mott + @author Bradford W. Mott, Thomas Jentzsch */ -class CartridgeF4SC : public Cartridge +class CartridgeF4SC : public CartridgeF4 { friend class CartridgeF4SCWidget; @@ -51,71 +48,6 @@ class CartridgeF4SC : public Cartridge virtual ~CartridgeF4SC() = default; public: - /** - Reset device to its power-on state - */ - void reset() override; - - /** - 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; - - /** - Install pages for the specified bank in the system. - - @param bank The bank that should be installed in the system - */ - bool bank(uInt16 bank) 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 bankCount() 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). @@ -135,32 +67,12 @@ class CartridgeF4SC : public Cartridge } #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: - // The 32K ROM image of the cartridge - std::array myImage; + // RAM size + static constexpr size_t RAM_SIZE = 0x80; - // The 128 bytes of RAM - std::array myRAM; - - // Indicates the offset into the ROM image (aligns to current bank) - uInt16 myBankOffset{0}; + // RAM mask + static constexpr uInt16 RAM_MASK = RAM_SIZE - 1; private: // Following constructors and assignment operators not supported diff --git a/src/emucore/CartF6.cxx b/src/emucore/CartF6.cxx index a7a8dec2b..50e993e26 100644 --- a/src/emucore/CartF6.cxx +++ b/src/emucore/CartF6.cxx @@ -15,195 +15,25 @@ // this file, and for a DISCLAIMER OF ALL WARRANTIES. //============================================================================ -#include "System.hxx" #include "CartF6.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeF6::CartridgeF6(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5) + : CartridgeEnhanced(image, size, md5, settings) { - // Copy the ROM image into my buffer - std::copy_n(image.get(), std::min(myImage.size(), size), myImage.begin()); - createRomAccessArrays(myImage.size()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF6::reset() +bool CartridgeF6::checkSwitchBank(uInt16 address, uInt8) { - // Upon reset we switch to the startup bank - initializeStartBank(0); - bank(startBank()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF6::install(System& system) -{ - mySystem = &system; - - // Upon install we'll setup the startup bank - bank(startBank()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeF6::peek(uInt16 address) -{ - address &= 0x0FFF; - // Switch banks if necessary - switch(address) + // Note: addresses could be calculated from hotspot and bank count + if((address >= 0x1FF6) && (address <= 0x1FF9)) { - case 0x0FF6: - // Set the current bank to the first 4k bank - bank(0); - break; - - case 0x0FF7: - // Set the current bank to the second 4k bank - bank(1); - break; - - case 0x0FF8: - // Set the current bank to the third 4k bank - bank(2); - break; - - case 0x0FF9: - // Set the current bank to the forth 4k bank - bank(3); - break; - - default: - break; - } - - return myImage[myBankOffset + address]; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF6::poke(uInt16 address, uInt8) -{ - address &= 0x0FFF; - - // Switch banks if necessary - switch(address) - { - case 0x0FF6: - // Set the current bank to the first 4k bank - bank(0); - break; - - case 0x0FF7: - // Set the current bank to the second 4k bank - bank(1); - break; - - case 0x0FF8: - // Set the current bank to the third 4k bank - bank(2); - break; - - case 0x0FF9: - // Set the current bank to the forth 4k bank - bank(3); - break; - - default: - break; + bank(address - 0x1FF6); + return true; } return false; } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF6::bank(uInt16 bank) -{ - if(bankLocked()) return false; - - // Remember what bank we're in - myBankOffset = bank << 12; - - System::PageAccess access(this, System::PageAccessType::READ); - - // Set the page accessing methods for the hot spots - for(uInt16 addr = (0x1FF6 & ~System::PAGE_MASK); addr < 0x2000; - addr += System::PAGE_SIZE) - { - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Setup the page access methods for the current bank - for(uInt16 addr = 0x1000; addr < static_cast(0x1FF6U & ~System::PAGE_MASK); - addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[myBankOffset + (addr & 0x0FFF)]; - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeF6::getBank(uInt16) const -{ - return myBankOffset >> 12; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeF6::bankCount() const -{ - return 4; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF6::patch(uInt16 address, uInt8 value) -{ - myImage[myBankOffset + (address & 0x0FFF)] = value; - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* CartridgeF6::getImage(size_t& size) const -{ - size = myImage.size(); - return myImage.data(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF6::save(Serializer& out) const -{ - try - { - out.putShort(myBankOffset); - } - catch(...) - { - cerr << "ERROR: CartridgeF6::save" << endl; - return false; - } - - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF6::load(Serializer& in) -{ - try - { - myBankOffset = in.getShort(); - } - catch(...) - { - cerr << "ERROR: CartridgeF6::load" << endl; - return false; - } - - // Remember what bank we were in - bank(myBankOffset >> 12); - - return true; -} diff --git a/src/emucore/CartF6.hxx b/src/emucore/CartF6.hxx index 4dc2c62b5..16faeebab 100644 --- a/src/emucore/CartF6.hxx +++ b/src/emucore/CartF6.hxx @@ -18,10 +18,7 @@ #ifndef CARTRIDGEF6_HXX #define CARTRIDGEF6_HXX -class System; - -#include "bspf.hxx" -#include "Cart.hxx" +#include "CartEnhanced.hxx" #ifdef DEBUGGER_SUPPORT #include "CartF6Widget.hxx" #endif @@ -30,9 +27,9 @@ class System; Cartridge class used for Atari's 16K bankswitched games. There are four 4K banks, accessible by read/write to $1FF6 - $1FF9. - @author Bradford W. Mott + @author Bradford W. Mott, Thomas Jentzsch */ -class CartridgeF6 : public Cartridge +class CartridgeF6 : public CartridgeEnhanced { friend class CartridgeF6Widget; @@ -50,71 +47,6 @@ class CartridgeF6 : public Cartridge virtual ~CartridgeF6() = default; public: - /** - Reset device to its power-on state - */ - void reset() override; - - /** - 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; - - /** - Install pages for the specified bank in the system. - - @param bank The bank that should be installed in the system - */ - bool bank(uInt16 bank) 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 bankCount() 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). @@ -134,29 +66,10 @@ class CartridgeF6 : public Cartridge } #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: - // The 16K ROM image of the cartridge - std::array myImage; + bool checkSwitchBank(uInt16 address, uInt8 value = 0) override; - // Indicates the offset into the ROM image (aligns to current bank) - uInt16 myBankOffset{0}; + uInt16 hotspot() const override { return 0x1FF6; } private: // Following constructors and assignment operators not supported diff --git a/src/emucore/CartF6SC.cxx b/src/emucore/CartF6SC.cxx index 60b998182..15613a357 100644 --- a/src/emucore/CartF6SC.cxx +++ b/src/emucore/CartF6SC.cxx @@ -15,251 +15,13 @@ // this file, and for a DISCLAIMER OF ALL WARRANTIES. //============================================================================ -#include "System.hxx" #include "CartF6SC.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeF6SC::CartridgeF6SC(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5) + : CartridgeF6(image, size, md5, settings) { - // Copy the ROM image into my buffer - std::copy_n(image.get(), std::min(myImage.size(), size), myImage.begin()); - createRomAccessArrays(myImage.size()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF6SC::reset() -{ - initializeRAM(myRAM.data(), myRAM.size()); - initializeStartBank(0); - - // Upon reset we switch to the startup bank - bank(startBank()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF6SC::install(System& system) -{ - mySystem = &system; - - 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 - access.type = System::PageAccessType::WRITE; - for(uInt16 addr = 0x1000; addr < 0x1080; addr += System::PAGE_SIZE) - { - access.romAccessBase = &myRomAccessBase[addr & 0x007F]; - access.romPeekCounter = &myRomAccessCounter[addr & 0x007F]; - access.romPokeCounter = &myRomAccessCounter[(addr & 0x07F) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Set the page accessing method for the RAM reading pages - access.type = System::PageAccessType::READ; - for(uInt16 addr = 0x1080; addr < 0x1100; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myRAM[addr & 0x007F]; - access.romAccessBase = &myRomAccessBase[0x80 + (addr & 0x007F)]; - access.romPeekCounter = &myRomAccessCounter[0x80 + (addr & 0x007F)]; - access.romPokeCounter = &myRomAccessCounter[0x80 + (addr & 0x007F) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Install pages for the startup bank - bank(startBank()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeF6SC::peek(uInt16 address) -{ - uInt16 peekAddress = address; - address &= 0x0FFF; - - // Switch banks if necessary - switch(address) - { - case 0x0FF6: - // Set the current bank to the first 4k bank - bank(0); - break; - - case 0x0FF7: - // Set the current bank to the second 4k bank - bank(1); - break; - - case 0x0FF8: - // Set the current bank to the third 4k bank - bank(2); - break; - - case 0x0FF9: - // Set the current bank to the forth 4k bank - bank(3); - break; - - default: - break; - } - - if(address < 0x0080) // Write port is at 0xF000 - 0xF07F (128 bytes) - return peekRAM(myRAM[address], peekAddress); - else - return myImage[myBankOffset + address]; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF6SC::poke(uInt16 address, uInt8 value) -{ - // Switch banks if necessary - switch(address & 0x0FFF) - { - case 0x0FF6: - // Set the current bank to the first 4k bank - bank(0); - return false; - - case 0x0FF7: - // Set the current bank to the second 4k bank - bank(1); - return false; - - case 0x0FF8: - // Set the current bank to the third 4k bank - bank(2); - return false; - - case 0x0FF9: - // Set the current bank to the forth 4k bank - bank(3); - return false; - - default: - break; - } - - if(!(address & 0x080)) - { - pokeRAM(myRAM[address & 0x007F], 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; - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF6SC::bank(uInt16 bank) -{ - if(bankLocked()) return false; - - // Remember what bank we're in - myBankOffset = bank << 12; - - System::PageAccess access(this, System::PageAccessType::READ); - - // Set the page accessing methods for the hot spots - for(uInt16 addr = (0x1FF6 & ~System::PAGE_MASK); addr < 0x2000; - addr += System::PAGE_SIZE) - { - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Setup the page access methods for the current bank - for(uInt16 addr = 0x1100; addr < static_cast(0x1FF6U & ~System::PAGE_MASK); - addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[myBankOffset + (addr & 0x0FFF)]; - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeF6SC::getBank(uInt16) const -{ - return myBankOffset >> 12; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeF6SC::bankCount() const -{ - return 4; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF6SC::patch(uInt16 address, uInt8 value) -{ - address &= 0x0FFF; - - if(address < 0x0100) - { - // Normally, a write to the read port won't do anything - // However, the patch command is special in that ignores such - // cart restrictions - myRAM[address & 0x007F] = value; - } - else - myImage[myBankOffset + address] = value; - - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* CartridgeF6SC::getImage(size_t& size) const -{ - size = myImage.size(); - return myImage.data(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF6SC::save(Serializer& out) const -{ - try - { - out.putShort(myBankOffset); - out.putByteArray(myRAM.data(), myRAM.size()); - } - catch(...) - { - cerr << "ERROR: CartridgeF6SC::save" << endl; - return false; - } - - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF6SC::load(Serializer& in) -{ - try - { - myBankOffset = in.getShort(); - in.getByteArray(myRAM.data(), myRAM.size()); - } - catch(...) - { - cerr << "ERROR: CartridgeF6SC::load" << endl; - return false; - } - - // Remember what bank we were in - bank(myBankOffset >> 12); - - return true; + myRamSize = RAM_SIZE; + myRamMask = RAM_SIZE - 1; } diff --git a/src/emucore/CartF6SC.hxx b/src/emucore/CartF6SC.hxx index 45a7c5ac3..c61c9715f 100644 --- a/src/emucore/CartF6SC.hxx +++ b/src/emucore/CartF6SC.hxx @@ -18,10 +18,7 @@ #ifndef CARTRIDGEF6SC_HXX #define CARTRIDGEF6SC_HXX -class System; - -#include "bspf.hxx" -#include "Cart.hxx" +#include "CartF6.hxx" #ifdef DEBUGGER_SUPPORT #include "CartF6SCWidget.hxx" #endif @@ -31,9 +28,9 @@ class System; RAM. There are four 4K banks, accessible by read/write to $1FF6 - $1FF9. RAM read port is $1080 - $10FF, write port is $1000 - $107F. - @author Bradford W. Mott + @author Bradford W. Mott, Thomas Jentzsch */ -class CartridgeF6SC : public Cartridge +class CartridgeF6SC : public CartridgeF6 { friend class CartridgeF6SCWidget; @@ -51,71 +48,6 @@ class CartridgeF6SC : public Cartridge virtual ~CartridgeF6SC() = default; public: - /** - Reset device to its power-on state - */ - void reset() override; - - /** - 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; - - /** - Install pages for the specified bank in the system. - - @param bank The bank that should be installed in the system - */ - bool bank(uInt16 bank) 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 bankCount() 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). @@ -135,32 +67,12 @@ class CartridgeF6SC : public Cartridge } #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: - // The 16K ROM image of the cartridge - std::array myImage; + // RAM size + static constexpr size_t RAM_SIZE = 0x80; - // The 128 bytes of RAM - std::array myRAM; - - // Indicates the offset into the ROM image (aligns to current bank) - uInt16 myBankOffset{0}; + // RAM mask + static constexpr uInt16 RAM_MASK = RAM_SIZE - 1; private: // Following constructors and assignment operators not supported diff --git a/src/emucore/CartF8.cxx b/src/emucore/CartF8.cxx index b23ed802e..5d898091c 100644 --- a/src/emucore/CartF8.cxx +++ b/src/emucore/CartF8.cxx @@ -15,176 +15,33 @@ // this file, and for a DISCLAIMER OF ALL WARRANTIES. //============================================================================ -#include "System.hxx" #include "CartF8.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeF8::CartridgeF8(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5) + : CartridgeEnhanced(image, size, md5, settings) { - // Copy the ROM image into my buffer - std::copy_n(image.get(), std::min(myImage.size(), size), myImage.begin()); - createRomAccessArrays(myImage.size()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF8::reset() +bool CartridgeF8::checkSwitchBank(uInt16 address, uInt8) { - initializeStartBank(1); - - // Upon reset we switch to the reset bank - bank(startBank()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF8::install(System& system) -{ - mySystem = &system; - - // Install pages for the startup bank - bank(startBank()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeF8::peek(uInt16 address) -{ - address &= 0x0FFF; - // Switch banks if necessary switch(address) { - case 0x0FF8: + case 0x1FF8: // Set the current bank to the lower 4k bank bank(0); - break; + return true; - case 0x0FF9: + case 0x1FF9: // Set the current bank to the upper 4k bank bank(1); - break; - - default: - break; - } - - return myImage[myBankOffset + address]; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF8::poke(uInt16 address, uInt8) -{ - address &= 0x0FFF; - - // Switch banks if necessary - switch(address) - { - case 0x0FF8: - // Set the current bank to the lower 4k bank - bank(0); - break; - - case 0x0FF9: - // Set the current bank to the upper 4k bank - bank(1); - break; + return true; default: break; } return false; } - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF8::bank(uInt16 bank) -{ - if(bankLocked()) return false; - - // Remember what bank we're in - myBankOffset = bank << 12; - - System::PageAccess access(this, System::PageAccessType::READ); - - // Set the page accessing methods for the hot spots - for(uInt16 addr = (0x1FF8 & ~System::PAGE_MASK); addr < 0x2000; - addr += System::PAGE_SIZE) - { - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Setup the page access methods for the current bank - for(uInt16 addr = 0x1000; addr < static_cast(0x1FF8U & ~System::PAGE_MASK); - addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[myBankOffset + (addr & 0x0FFF)]; - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeF8::getBank(uInt16) const -{ - return myBankOffset >> 12; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeF8::bankCount() const -{ - return 2; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF8::patch(uInt16 address, uInt8 value) -{ - myImage[myBankOffset + (address & 0x0FFF)] = value; - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* CartridgeF8::getImage(size_t& size) const -{ - size = myImage.size(); - return myImage.data(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF8::save(Serializer& out) const -{ - try - { - out.putShort(myBankOffset); - } - catch(...) - { - cerr << "ERROR: CartridgeF8::save" << endl; - return false; - } - - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF8::load(Serializer& in) -{ - try - { - myBankOffset = in.getShort(); - } - catch(...) - { - cerr << "ERROR: CartridgeF8::load" << endl; - return false; - } - - // Remember what bank we were in - bank(myBankOffset >> 12); - - return true; -} diff --git a/src/emucore/CartF8.hxx b/src/emucore/CartF8.hxx index b586d70ff..29b308808 100644 --- a/src/emucore/CartF8.hxx +++ b/src/emucore/CartF8.hxx @@ -18,10 +18,7 @@ #ifndef CARTRIDGEF8_HXX #define CARTRIDGEF8_HXX -class System; - -#include "bspf.hxx" -#include "Cart.hxx" +#include "CartEnhanced.hxx" #ifdef DEBUGGER_SUPPORT #include "CartF8Widget.hxx" #endif @@ -30,9 +27,9 @@ class System; Cartridge class used for Atari's 8K bankswitched games. There are two 4K banks, accessible by read/write to $1FF8 - $1FF9. - @author Bradford W. Mott + @author Bradford W. Mott, Thomas Jentzsch */ -class CartridgeF8 : public Cartridge +class CartridgeF8 : public CartridgeEnhanced { friend class CartridgeF8Widget; @@ -50,71 +47,6 @@ class CartridgeF8 : public Cartridge virtual ~CartridgeF8() = default; public: - /** - Reset device to its power-on state - */ - void reset() override; - - /** - 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; - - /** - Install pages for the specified bank in the system. - - @param bank The bank that should be installed in the system - */ - bool bank(uInt16 bank) 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 bankCount() 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). @@ -134,29 +66,12 @@ class CartridgeF8 : public Cartridge } #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: - // The 8K ROM image of the cartridge - std::array myImage; + bool checkSwitchBank(uInt16 address, uInt8 value = 0) override; - // Indicates the offset into the ROM image (aligns to current bank) - uInt16 myBankOffset{0}; + uInt16 hotspot() const override { return 0x1FF8; } + + uInt16 getStartBank() const override { return 1; } private: // Following constructors and assignment operators not supported diff --git a/src/emucore/CartF8SC.cxx b/src/emucore/CartF8SC.cxx index c885a9c83..98ce4357f 100644 --- a/src/emucore/CartF8SC.cxx +++ b/src/emucore/CartF8SC.cxx @@ -15,231 +15,12 @@ // this file, and for a DISCLAIMER OF ALL WARRANTIES. //============================================================================ -#include "System.hxx" #include "CartF8SC.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeF8SC::CartridgeF8SC(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5) + : CartridgeF8(image, size, md5, settings) { - // Copy the ROM image into my buffer - std::copy_n(image.get(), std::min(myImage.size(), size), myImage.begin()); - createRomAccessArrays(myImage.size()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF8SC::reset() -{ - initializeRAM(myRAM.data(), myRAM.size()); - initializeStartBank(1); - - // Upon reset we switch to the startup bank - bank(startBank()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeF8SC::install(System& system) -{ - mySystem = &system; - - 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 - access.type = System::PageAccessType::WRITE; - for(uInt16 addr = 0x1000; addr < 0x1080; addr += System::PAGE_SIZE) - { - access.romAccessBase = &myRomAccessBase[addr & 0x007F]; - access.romPeekCounter = &myRomAccessCounter[addr & 0x007F]; - access.romPokeCounter = &myRomAccessCounter[(addr & 0x07F) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Set the page accessing method for the RAM reading pages - access.type = System::PageAccessType::READ; - for(uInt16 addr = 0x1080; addr < 0x1100; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myRAM[addr & 0x007F]; - access.romAccessBase = &myRomAccessBase[0x80 + (addr & 0x007F)]; - access.romPeekCounter = &myRomAccessCounter[0x80 + (addr & 0x007F)]; - access.romPokeCounter = &myRomAccessCounter[0x80 + (addr & 0x007F) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Install pages for the startup bank - bank(startBank()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeF8SC::peek(uInt16 address) -{ - uInt16 peekAddress = address; - address &= 0x0FFF; - - // Switch banks if necessary - switch(address) - { - case 0x0FF8: - // Set the current bank to the lower 4k bank - bank(0); - break; - - case 0x0FF9: - // Set the current bank to the upper 4k bank - bank(1); - break; - - default: - break; - } - - if(address < 0x0080) // Write port is at 0xF000 - 0xF07F (128 bytes) - return peekRAM(myRAM[address], peekAddress); - else - return myImage[myBankOffset + address]; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF8SC::poke(uInt16 address, uInt8 value) -{ - // Switch banks if necessary - switch(address & 0x0FFF) - { - case 0x0FF8: - // Set the current bank to the lower 4k bank - bank(0); - return false; - - case 0x0FF9: - // Set the current bank to the upper 4k bank - bank(1); - return false; - - default: - break; - } - - if (!(address & 0x080)) - { - pokeRAM(myRAM[address & 0x007F], 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; - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF8SC::bank(uInt16 bank) -{ - if(bankLocked()) return false; - - // Remember what bank we're in - myBankOffset = bank << 12; - - System::PageAccess access(this, System::PageAccessType::READ); - - // Set the page accessing methods for the hot spots - for(uInt16 addr = (0x1FF8 & ~System::PAGE_MASK); addr < 0x2000; - addr += System::PAGE_SIZE) - { - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Setup the page access methods for the current bank - for(uInt16 addr = 0x1100; addr < static_cast(0x1FF8U & ~System::PAGE_MASK); - addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[myBankOffset + (addr & 0x0FFF)]; - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeF8SC::getBank(uInt16) const -{ - return myBankOffset >> 12; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeF8SC::bankCount() const -{ - return 2; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF8SC::patch(uInt16 address, uInt8 value) -{ - address &= 0x0FFF; - - if(address < 0x0100) - { - // Normally, a write to the read port won't do anything - // However, the patch command is special in that ignores such - // cart restrictions - myRAM[address & 0x007F] = value; - } - else - myImage[myBankOffset + address] = value; - - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* CartridgeF8SC::getImage(size_t& size) const -{ - size = myImage.size(); - return myImage.data(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF8SC::save(Serializer& out) const -{ - try - { - out.putShort(myBankOffset); - out.putByteArray(myRAM.data(), myRAM.size()); - } - catch(...) - { - cerr << "ERROR: CartridgeF8SC::save" << endl; - return false; - } - - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeF8SC::load(Serializer& in) -{ - try - { - myBankOffset = in.getShort(); - in.getByteArray(myRAM.data(), myRAM.size()); - } - catch(...) - { - cerr << "ERROR: CartridgeF8SC::load" << endl; - return false; - } - - // Remember what bank we were in - bank(myBankOffset >> 12); - - return true; + myRamSize = RAM_SIZE; } diff --git a/src/emucore/CartF8SC.hxx b/src/emucore/CartF8SC.hxx index 592637d3d..c5a005543 100644 --- a/src/emucore/CartF8SC.hxx +++ b/src/emucore/CartF8SC.hxx @@ -18,10 +18,7 @@ #ifndef CARTRIDGEF8SC_HXX #define CARTRIDGEF8SC_HXX -class System; - -#include "bspf.hxx" -#include "Cart.hxx" +#include "CartF8.hxx" #ifdef DEBUGGER_SUPPORT #include "CartF8SCWidget.hxx" #endif @@ -31,9 +28,9 @@ class System; RAM. There are two 4K banks, accessible by read/write to $1FF8 - $1FF9. RAM read port is $1080 - $10FF, write port is $1000 - $107F. - @author Bradford W. Mott + @author Bradford W. Mott, Thomas Jentzsch */ -class CartridgeF8SC : public Cartridge +class CartridgeF8SC : public CartridgeF8 { friend class CartridgeF8SCWidget; @@ -51,71 +48,6 @@ class CartridgeF8SC : public Cartridge virtual ~CartridgeF8SC() = default; public: - /** - Reset device to its power-on state - */ - void reset() override; - - /** - 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; - - /** - Install pages for the specified bank in the system. - - @param bank The bank that should be installed in the system - */ - bool bank(uInt16 bank) 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 bankCount() 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). @@ -135,32 +67,9 @@ class CartridgeF8SC : public Cartridge } #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: - // The 8K ROM image of the cartridge - std::array myImage; - - // The 128 bytes of RAM - std::array myRAM; - - // Indicates the offset into the ROM image (aligns to current bank) - uInt16 myBankOffset{0}; + // RAM size + static constexpr size_t RAM_SIZE = 0x80; private: // Following constructors and assignment operators not supported diff --git a/src/emucore/CartFA.cxx b/src/emucore/CartFA.cxx index 5a4ecb6bd..082e403f7 100644 --- a/src/emucore/CartFA.cxx +++ b/src/emucore/CartFA.cxx @@ -21,235 +21,19 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeFA::CartridgeFA(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5) + : CartridgeEnhanced(image, size, md5, settings) { - // Copy the ROM image into my buffer - std::copy_n(image.get(), std::min(myImage.size(), size), myImage.begin()); - createRomAccessArrays(myImage.size()); + myRamSize = RAM_SIZE; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeFA::reset() -{ - initializeRAM(myRAM.data(), myRAM.size()); - initializeStartBank(2); - - // Upon reset we switch to the startup bank - bank(startBank()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeFA::install(System& system) -{ - mySystem = &system; - - 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 - access.type = System::PageAccessType::WRITE; - for(uInt16 addr = 0x1000; addr < 0x1100; addr += System::PAGE_SIZE) - { - access.romAccessBase = &myRomAccessBase[addr & 0x00FF]; - access.romPeekCounter = &myRomAccessCounter[addr & 0x00FF]; - access.romPokeCounter = &myRomAccessCounter[(addr & 0x00FF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Set the page accessing method for the RAM reading pages - access.type = System::PageAccessType::READ; - for(uInt16 addr = 0x1100; addr < 0x1200; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myRAM[addr & 0x00FF]; - access.romAccessBase = &myRomAccessBase[0x100 + (addr & 0x00FF)]; - access.romPeekCounter = &myRomAccessCounter[0x100 + (addr & 0x00FF)]; - access.romPokeCounter = &myRomAccessCounter[0x100 + (addr & 0x00FF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Install pages for the startup bank - bank(startBank()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeFA::peek(uInt16 address) -{ - uInt16 peekAddress = address; - address &= 0x0FFF; - - // Switch banks if necessary - switch(address) - { - case 0x0FF8: - // Set the current bank to the lower 4k bank - bank(0); - break; - - case 0x0FF9: - // Set the current bank to the middle 4k bank - bank(1); - break; - - case 0x0FFA: - // Set the current bank to the upper 4k bank - bank(2); - break; - - default: - break; - } - - if(address < 0x0100) // Write port is at 0xF000 - 0xF0FF (256 bytes) - return peekRAM(myRAM[address], peekAddress); - else - return myImage[myBankOffset + address]; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeFA::poke(uInt16 address, uInt8 value) +bool CartridgeFA::checkSwitchBank(uInt16 address, uInt8) { // Switch banks if necessary - switch(address & 0x0FFF) + if((address >= 0x1FF8) && (address <= 0x1FFA)) { - case 0x0FF8: - // Set the current bank to the lower 4k bank - bank(0); - return false; - - case 0x0FF9: - // Set the current bank to the middle 4k bank - bank(1); - return false; - - case 0x0FFA: - // Set the current bank to the upper 4k bank - bank(2); - return false; - - default: - break; - } - - if (!(address & 0x100)) - { - pokeRAM(myRAM[address & 0x00FF], address, value); + bank(address - 0x1FF8); 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; - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeFA::bank(uInt16 bank) -{ - if(bankLocked()) return false; - - // Remember what bank we're in - myBankOffset = bank << 12; - - System::PageAccess access(this, System::PageAccessType::READ); - - // Set the page accessing methods for the hot spots - for(uInt16 addr = (0x1FF8 & ~System::PAGE_MASK); addr < 0x2000; - addr += System::PAGE_SIZE) - { - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Setup the page access methods for the current bank - for(uInt16 addr = 0x1200; addr < static_cast(0x1FF8U & ~System::PAGE_MASK); - addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[myBankOffset + (addr & 0x0FFF)]; - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeFA::getBank(uInt16) const -{ - return myBankOffset >> 12; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeFA::bankCount() const -{ - return 3; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeFA::patch(uInt16 address, uInt8 value) -{ - address &= 0x0FFF; - - if(address < 0x0200) - { - // Normally, a write to the read port won't do anything - // However, the patch command is special in that ignores such - // cart restrictions - myRAM[address & 0x00FF] = value; - } - else - myImage[myBankOffset + address] = value; - - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* CartridgeFA::getImage(size_t& size) const -{ - size = myImage.size(); - return myImage.data(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeFA::save(Serializer& out) const -{ - try - { - out.putShort(myBankOffset); - out.putByteArray(myRAM.data(), myRAM.size()); - } - catch(...) - { - cerr << "ERROR: CartridgeFA::save" << endl; - return false; - } - - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeFA::load(Serializer& in) -{ - try - { - myBankOffset = in.getShort(); - in.getByteArray(myRAM.data(), myRAM.size()); - } - catch(...) - { - cerr << "ERROR: CartridgeFA::load" << endl; - return false; - } - - // Remember what bank we were in - bank(myBankOffset >> 12); - - return true; + return false; } diff --git a/src/emucore/CartFA.hxx b/src/emucore/CartFA.hxx index 9729a366f..a2f197b75 100644 --- a/src/emucore/CartFA.hxx +++ b/src/emucore/CartFA.hxx @@ -21,7 +21,7 @@ class System; #include "bspf.hxx" -#include "Cart.hxx" +#include "CartEnhanced.hxx" #ifdef DEBUGGER_SUPPORT #include "CartFAWidget.hxx" #endif @@ -31,9 +31,9 @@ class System; banks, accessible by read/write at $1FF8 - $1FFA, and 256 bytes of RAM. RAM read port is $1100 - $11FF, write port is $1000 - $10FF. - @author Bradford W. Mott + @author Bradford W. Mott, Thomas Jentzsch */ -class CartridgeFA : public Cartridge +class CartridgeFA : public CartridgeEnhanced { friend class CartridgeFAWidget; @@ -51,71 +51,6 @@ class CartridgeFA : public Cartridge virtual ~CartridgeFA() = default; public: - /** - Reset device to its power-on state - */ - void reset() override; - - /** - 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; - - /** - Install pages for the specified bank in the system. - - @param bank The bank that should be installed in the system - */ - bool bank(uInt16 bank) 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 bankCount() 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). @@ -135,32 +70,16 @@ class CartridgeFA : public Cartridge } #endif - public: - /** - Get the byte at the specified address. + private: + bool checkSwitchBank(uInt16 address, uInt8 value = 0) override; - @return The byte at the specified address - */ - uInt8 peek(uInt16 address) override; + uInt16 hotspot() const override { return 0x1FF8; } - /** - 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; + uInt16 getStartBank() const override { return 2; } private: - // The 12K ROM image of the cartridge - std::array myImage; - - // The 256 bytes of RAM on the cartridge - std::array myRAM; - - // Indicates the offset into the ROM image (aligns to current bank) - uInt16 myBankOffset{0}; + // RAM size + static constexpr size_t RAM_SIZE = 0x100; private: // Following constructors and assignment operators not supported diff --git a/src/emucore/CartFA2.cxx b/src/emucore/CartFA2.cxx index 1630f216e..14c8f2156 100644 --- a/src/emucore/CartFA2.cxx +++ b/src/emucore/CartFA2.cxx @@ -15,308 +15,66 @@ // this file, and for a DISCLAIMER OF ALL WARRANTIES. //============================================================================ -#include "OSystem.hxx" -#include "Serializer.hxx" -#include "System.hxx" #include "TimerManager.hxx" #include "CartFA2.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeFA2::CartridgeFA2(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5) + : CartridgeFA(image, size, md5, settings) { // 29/32K version of FA2 has valid data @ 1K - 29K const uInt8* img_ptr = image.get(); if(size >= 29_KB) + { img_ptr += 1_KB; - else if(size < mySize) - mySize = size; + mySize = 28_KB; + } + + // Allocate array for the ROM image + myImage = make_unique(mySize); // Copy the ROM image into my buffer - std::copy_n(img_ptr, mySize, myImage.begin()); - createRomAccessArrays(mySize); + std::copy_n(img_ptr, mySize, myImage.get()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeFA2::reset() +bool CartridgeFA2::checkSwitchBank(uInt16 address, uInt8) { - initializeRAM(myRAM.data(), myRAM.size()); - initializeStartBank(0); - - // Upon reset we switch to the startup bank - bank(startBank()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeFA2::install(System& system) -{ - mySystem = &system; - - 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 - access.type = System::PageAccessType::WRITE; - for(uInt16 addr = 0x1000; addr < 0x1100; addr += System::PAGE_SIZE) + // Switch banks if necessary + if((address >= 0x1FF5) && (address <= 0x1FFB)) { - access.romAccessBase = &myRomAccessBase[addr & 0x00FF]; - access.romPeekCounter = &myRomAccessCounter[addr & 0x00FF]; - access.romPokeCounter = &myRomAccessCounter[(addr & 0x00FF) + myAccessSize]; - mySystem->setPageAccess(addr, access); + bank(address - 0x1FF5); + return true; } - - // Set the page accessing method for the RAM reading pages - access.directPokeBase = nullptr; - access.type = System::PageAccessType::READ; - for(uInt16 addr = 0x1100; addr < 0x1200; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myRAM[addr & 0x00FF]; - access.romAccessBase = &myRomAccessBase[0x100 + (addr & 0x00FF)]; - access.romPeekCounter = &myRomAccessCounter[0x100 + (addr & 0x00FF)]; - access.romPokeCounter = &myRomAccessCounter[0x100 + (addr & 0x00FF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Install pages for the startup bank - bank(startBank()); + return false; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uInt8 CartridgeFA2::peek(uInt16 address) { - uInt16 peekAddress = address; - address &= 0x0FFF; - - // Switch banks if necessary - switch(address) + if((address & ROM_MASK) == 0x0FF4) { - case 0x0FF4: - // Load/save RAM to/from Harmony cart flash - if(mySize == 28_KB && !bankLocked()) - return ramReadWrite(); - break; - - case 0x0FF5: - // Set the current bank to the first 4k bank - bank(0); - break; - - case 0x0FF6: - // Set the current bank to the second 4k bank - bank(1); - break; - - case 0x0FF7: - // Set the current bank to the third 4k bank - bank(2); - break; - - case 0x0FF8: - // Set the current bank to the fourth 4k bank - bank(3); - break; - - case 0x0FF9: - // Set the current bank to the fifth 4k bank - bank(4); - break; - - case 0x0FFA: - // Set the current bank to the sixth 4k bank - bank(5); - break; - - case 0x0FFB: - // Set the current bank to the seventh 4k bank - // This is only available on 28K ROMs - if(mySize == 28_KB) bank(6); - break; - - default: - break; + // Load/save RAM to/from Harmony cart flash + if(mySize == 28_KB && !bankLocked()) + return ramReadWrite(); } - if(address < 0x0100) // Write port is at 0xF000 - 0xF0FF (256 bytes) - return peekRAM(myRAM[address], peekAddress); - else - return myImage[myBankOffset + address]; + return CartridgeEnhanced::peek(address); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool CartridgeFA2::poke(uInt16 address, uInt8 value) { - // Switch banks if necessary - switch(address & 0x0FFF) + if((address & ROM_MASK) == 0x0FF4) { - case 0x0FF4: - // Load/save RAM to/from Harmony cart flash - if(mySize == 28_KB && !bankLocked()) - ramReadWrite(); - return false; - - case 0x0FF5: - // Set the current bank to the first 4k bank - bank(0); - return false; - - case 0x0FF6: - // Set the current bank to the second 4k bank - bank(1); - return false; - - case 0x0FF7: - // Set the current bank to the third 4k bank - bank(2); - return false; - - case 0x0FF8: - // Set the current bank to the fourth 4k bank - bank(3); - return false; - - case 0x0FF9: - // Set the current bank to the fifth 4k bank - bank(4); - return false; - - case 0x0FFA: - // Set the current bank to the sixth 4k bank - bank(5); - return false; - - case 0x0FFB: - // Set the current bank to the seventh 4k bank - // This is only available on 28K ROMs - if(mySize == 28_KB) bank(6); - return false; - - default: - break; - } - - if(!(address & 0x100)) - { - pokeRAM(myRAM[address & 0x00FF], 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; - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeFA2::bank(uInt16 bank) -{ - if(bankLocked()) return false; - - // Remember what bank we're in - myBankOffset = bank << 12; - - System::PageAccess access(this, System::PageAccessType::READ); - - // Set the page accessing methods for the hot spots - for(uInt16 addr = (0x1FF4 & ~System::PAGE_MASK); addr < 0x2000; - addr += System::PAGE_SIZE) - { - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Setup the page access methods for the current bank - for(uInt16 addr = 0x1200; addr < static_cast(0x1FF4U & ~System::PAGE_MASK); - addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[myBankOffset + (addr & 0x0FFF)]; - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeFA2::getBank(uInt16) const -{ - return myBankOffset >> 12; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeFA2::bankCount() const -{ - return uInt16(mySize / 4_KB); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeFA2::patch(uInt16 address, uInt8 value) -{ - address &= 0x0FFF; - - if(address < 0x0200) - { - // Normally, a write to the read port won't do anything - // However, the patch command is special in that ignores such - // cart restrictions - myRAM[address & 0x00FF] = value; - } - else - myImage[myBankOffset + address] = value; - - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* CartridgeFA2::getImage(size_t& size) const -{ - size = mySize; - return myImage.data(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeFA2::save(Serializer& out) const -{ - try - { - out.putShort(myBankOffset); - out.putByteArray(myRAM.data(), myRAM.size()); - } - catch(...) - { - cerr << "ERROR: CartridgeFA2::save" << endl; + // Load/save RAM to/from Harmony cart flash + if(mySize == 28_KB && !bankLocked()) + ramReadWrite(); return false; } - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeFA2::load(Serializer& in) -{ - try - { - myBankOffset = in.getShort(); - in.getByteArray(myRAM.data(), myRAM.size()); - } - catch(...) - { - cerr << "ERROR: CartridgeFA2::load" << endl; - return false; - } - - // Remember what bank we were in - bank(myBankOffset >> 12); - - return true; + return CartridgeEnhanced::poke(address, value); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -361,11 +119,11 @@ uInt8 CartridgeFA2::ramReadWrite() { try { - serializer.getByteArray(myRAM.data(), myRAM.size()); + serializer.getByteArray(myRAM.get(), myRamSize); } catch(...) { - myRAM.fill(0); + std::fill_n(myRAM.get(), myRamSize, 0); } myRamAccessTimeout += 500; // Add 0.5 ms delay for read } @@ -373,7 +131,7 @@ uInt8 CartridgeFA2::ramReadWrite() { try { - serializer.putByteArray(myRAM.data(), myRAM.size()); + serializer.putByteArray(myRAM.get(), myRamSize); } catch(...) { @@ -384,7 +142,7 @@ uInt8 CartridgeFA2::ramReadWrite() } } // Bit 6 is 1, busy - return myImage[myBankOffset + 0xFF4] | 0x40; + return myImage[myCurrentSegOffset[0] + 0xFF4] | 0x40; } else { @@ -395,11 +153,11 @@ uInt8 CartridgeFA2::ramReadWrite() myRAM[255] = 0; // Successful operation // Bit 6 is 0, ready/success - return myImage[myBankOffset + 0xFF4] & ~0x40; + return myImage[myCurrentSegOffset[0] + 0xFF4] & ~0x40; } else // Bit 6 is 1, busy - return myImage[myBankOffset + 0xFF4] | 0x40; + return myImage[myCurrentSegOffset[0] + 0xFF4] | 0x40; } } @@ -424,18 +182,18 @@ void CartridgeFA2::flash(uInt8 operation) { try { - serializer.getByteArray(myRAM.data(), myRAM.size()); + serializer.getByteArray(myRAM.get(), myRamSize); } catch(...) { - myRAM.fill(0); + std::fill_n(myRAM.get(), myRamSize, 0); } } else if(operation == 2) // write { try { - serializer.putByteArray(myRAM.data(), myRAM.size()); + serializer.putByteArray(myRAM.get(), myRamSize); } catch(...) { diff --git a/src/emucore/CartFA2.hxx b/src/emucore/CartFA2.hxx index ea19b0d43..16d33bf2b 100644 --- a/src/emucore/CartFA2.hxx +++ b/src/emucore/CartFA2.hxx @@ -21,7 +21,7 @@ class System; #include "bspf.hxx" -#include "Cart.hxx" +#include "CartFA.hxx" #ifdef DEBUGGER_SUPPORT #include "CartFA2Widget.hxx" #endif @@ -43,9 +43,9 @@ class System; by the emulator. Also supported is a 32K variant. In any event, only data at 1K - 29K of the ROM is used. - @author Chris D. Walton + @author Chris D. Walton, Thomas Jentzsch */ -class CartridgeFA2 : public Cartridge +class CartridgeFA2 : public CartridgeFA { friend class CartridgeFA2Widget; @@ -63,71 +63,6 @@ class CartridgeFA2 : public Cartridge virtual ~CartridgeFA2() = default; public: - /** - Reset device to its power-on state - */ - void reset() override; - - /** - 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; - - /** - Install pages for the specified bank in the system. - - @param bank The bank that should be installed in the system - */ - bool bank(uInt16 bank) 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 bankCount() 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). @@ -173,6 +108,12 @@ class CartridgeFA2 : public Cartridge bool poke(uInt16 address, uInt8 value) override; private: + bool checkSwitchBank(uInt16 address, uInt8 value = 0) override; + + uInt16 hotspot() const override { return 0x1FF5; } + + uInt16 getStartBank() const override { return 0; } + /** Either load or save internal RAM to Harmony flash (represented by a file in emulation). @@ -192,15 +133,6 @@ class CartridgeFA2 : public Cartridge void flash(uInt8 operation); private: - // The 24K/28K ROM image of the cartridge - std::array myImage; - - // Actual usable size of the ROM image - size_t mySize{28_KB}; - - // The 256 bytes of RAM on the cartridge - std::array myRAM; - // The time after which the first request of a load/save operation // will actually be completed // Due to flash RAM constraints, a read/write isn't instantaneous, @@ -211,9 +143,6 @@ class CartridgeFA2 : public Cartridge // of internal RAM to Harmony cart flash string myFlashFile; - // Indicates the offset into the ROM image (aligns to current bank) - uInt16 myBankOffset{0}; - private: // Following constructors and assignment operators not supported CartridgeFA2() = delete; diff --git a/src/emucore/CartFC.cxx b/src/emucore/CartFC.cxx index 565e577cf..c415ba1af 100644 --- a/src/emucore/CartFC.cxx +++ b/src/emucore/CartFC.cxx @@ -21,52 +21,35 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeFC::CartridgeFC(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5), - mySize(size) + : CartridgeEnhanced(image, size, md5, settings) { - // Copy the ROM image into my buffer - std::copy_n(image.get(), std::min(myImage.size(), size), myImage.begin()); - createRomAccessArrays(myImage.size()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void CartridgeFC::reset() { - initializeStartBank(0); + CartridgeEnhanced::reset(); + myTargetBank = 0; - - // Upon reset we switch to the reset bank - bank(startBank()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeFC::install(System& system) +bool CartridgeFC::checkSwitchBank(uInt16 address, uInt8) { - mySystem = &system; - - // Install pages for the startup bank - bank(startBank()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeFC::peek(uInt16 address) -{ - address &= 0x0FFF; - // Switch banks if necessary - if(address == 0x0FFC) + if(address == 0x1FFC) { // Trigger the bank switch bank(myTargetBank); + return true; } - - return myImage[myBankOffset + address]; + return false; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool CartridgeFC::poke(uInt16 address, uInt8 value) { - address &= 0x0FFF; + address &= myBankMask; // Switch banks if necessary switch (address) @@ -78,118 +61,18 @@ bool CartridgeFC::poke(uInt16 address, uInt8 value) case 0x0FF9: // Set the high bits of target 4k bank - if (value << 2 < bankCount()) + if (value << 2 < romBankCount()) { myTargetBank += value << 2; - myTargetBank %= bankCount(); + myTargetBank %= romBankCount(); } else // special handling when both values are identical (e.g. 4/4 or 5/5) - myTargetBank = value % bankCount(); - break; - - case 0x0FFC: - // Trigger the bank switch - bank(myTargetBank); + myTargetBank = value % romBankCount(); break; default: - break; + checkSwitchBank(address); } return false; } - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeFC::bank(uInt16 bank) -{ - if (bankLocked()) return false; - - // Remember what bank we're in - myBankOffset = bank << 12; - - System::PageAccess access(this, System::PageAccessType::READ); - - // Set the page accessing methods for the hot spots - for (uInt16 addr = (0x1FF8 & ~System::PAGE_MASK); addr < 0x2000; - addr += System::PAGE_SIZE) - { - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Setup the page access methods for the current bank - for (uInt16 addr = 0x1000; addr < static_cast(0x1FF8U & ~System::PAGE_MASK); - addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[myBankOffset + (addr & 0x0FFF)]; - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - myCurrentBank = myTargetBank; - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeFC::getBank(uInt16) const -{ - return myBankOffset >> 12; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeFC::bankCount() const -{ - return uInt16(mySize >> 12); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeFC::patch(uInt16 address, uInt8 value) -{ - myImage[myBankOffset + (address & 0x0FFF)] = value; - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* CartridgeFC::getImage(size_t& size) const -{ - size = mySize; - return myImage.data(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeFC::save(Serializer& out) const -{ - try - { - out.putShort(myBankOffset); - } - catch (...) - { - cerr << "ERROR: CartridgeFC::save" << endl; - return false; - } - - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeFC::load(Serializer& in) -{ - try - { - myBankOffset = in.getShort(); - } - catch (...) - { - cerr << "ERROR: CartridgeFC::load" << endl; - return false; - } - - // Remember what bank we were in - bank(myBankOffset >> 12); - - return true; -} diff --git a/src/emucore/CartFC.hxx b/src/emucore/CartFC.hxx index cc96d0131..5858ae85a 100644 --- a/src/emucore/CartFC.hxx +++ b/src/emucore/CartFC.hxx @@ -21,7 +21,7 @@ class System; #include "bspf.hxx" -#include "Cart.hxx" +#include "CartEnhanced.hxx" #ifdef DEBUGGER_SUPPORT #include "CartFCWidget.hxx" @@ -30,12 +30,12 @@ class System; /** Cartridge class used for Amiga's 32K Power Play Arcade Video Game Album. There are eight 4K banks, writing to $1FF8 definies the two lowest bits - of the wanted bank, writeing to $1FF9 defines the high bits. Reading from + of the wanted bank, writeing to $1FF9 defines the high bits. Accessing $1FFC triggers the bank switching @author Thomas Jentzsch */ -class CartridgeFC : public Cartridge +class CartridgeFC : public CartridgeEnhanced { friend class CartridgeFCWidget; @@ -58,66 +58,6 @@ class CartridgeFC : public Cartridge */ void reset() override; - /** - 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; - - /** - Install pages for the specified bank in the system. - - @param bank The bank that should be installed in the system - */ - bool bank(uInt16 bank) 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 bankCount() 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). @@ -138,13 +78,6 @@ class CartridgeFC : public Cartridge #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 @@ -155,17 +88,9 @@ class CartridgeFC : public Cartridge bool poke(uInt16 address, uInt8 value) override; private: - // The 32K ROM image of the cartridge - std::array myImage; + bool checkSwitchBank(uInt16 address, uInt8 value = 0) override; - // Size of the ROM image - size_t mySize{0}; - - // Indicates the offset into the ROM image (aligns to current bank) - uInt16 myBankOffset{0}; - - // Indicates which bank is currently active for the first segment - uInt16 myCurrentBank{0}; + uInt16 hotspot() const override { return 0x1FF8; } // Target bank defined by writing to $1FF8/9 uInt16 myTargetBank{0}; diff --git a/src/emucore/CartFE.cxx b/src/emucore/CartFE.cxx index 36cfa7c84..7c3debda3 100644 --- a/src/emucore/CartFE.cxx +++ b/src/emucore/CartFE.cxx @@ -22,27 +22,22 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeFE::CartridgeFE(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5) + : CartridgeEnhanced(image, size, md5, settings) { - // Copy the ROM image into my buffer - std::copy_n(image.get(), std::min(myImage.size(), size), myImage.begin()); - createRomAccessArrays(myImage.size()); + myDirectPeek = false; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void CartridgeFE::reset() { - // Decathlon requires this, since there is no startup vector in bank 1 - initializeStartBank(0); - - bank(startBank()); + CartridgeEnhanced::reset(); myLastAccessWasFE = false; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void CartridgeFE::install(System& system) { - mySystem = &system; + CartridgeEnhanced::install(system); // The hotspot $01FE is in a mirror of zero-page RAM // We need to claim access to it here, and deal with it in peek/poke below @@ -51,14 +46,27 @@ void CartridgeFE::install(System& system) mySystem->setPageAccess(addr, access); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool CartridgeFE::checkSwitchBank(uInt16 address, uInt8 value) +{ + if(myLastAccessWasFE) + { + bank((value & 0x20) ? 0 : 1); + myLastAccessWasFE = false; // was: address == 0x01FE; + return true; + } + myLastAccessWasFE = address == 0x01FE; + return false; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uInt8 CartridgeFE::peek(uInt16 address) { uInt8 value = (address < 0x200) ? mySystem->m6532().peek(address) : - myImage[myBankOffset + (address & 0x0FFF)]; + myImage[myCurrentSegOffset[(address & myBankMask) >> myBankShift] + (address & myBankMask)]; // Check if we hit hotspot - checkBankSwitch(address, value); + checkSwitchBank(address, value); return value; } @@ -70,83 +78,17 @@ bool CartridgeFE::poke(uInt16 address, uInt8 value) mySystem->m6532().poke(address, value); // Check if we hit hotspot - checkBankSwitch(address, value); + checkSwitchBank(address, value); return false; } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeFE::checkBankSwitch(uInt16 address, uInt8 value) -{ - if(bankLocked()) - return; - - // Did we detect $01FE on the last address bus access? - // If so, we bankswitch according to the upper 3 bits of the data bus - // NOTE: see the header file for the significance of 'value & 0x20' - if(myLastAccessWasFE) - bank((value & 0x20) ? 0 : 1); - - // On the next cycle, we use the (then) current data bus value to decode - // the bank to use - myLastAccessWasFE = address == 0x01FE; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeFE::bank(uInt16 bank) -{ - if(bankLocked()) - return false; - - myBankOffset = bank << 12; - - System::PageAccess access(this, System::PageAccessType::READ); - - // Setup the page access methods for the current bank - // Map all of the cart accesses to call peek and poke - for(uInt16 addr = 0x1000; addr < 0x2000; addr += System::PAGE_SIZE) - { - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeFE::getBank(uInt16) const -{ - return myBankOffset >> 12; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeFE::bankCount() const -{ - return 2; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeFE::patch(uInt16 address, uInt8 value) -{ - myImage[myBankOffset + (address & 0x0FFF)] = value; - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* CartridgeFE::getImage(size_t& size) const -{ - size = myImage.size(); - return myImage.data(); -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool CartridgeFE::save(Serializer& out) const { + CartridgeEnhanced::save(out); try { - out.putShort(myBankOffset); out.putBool(myLastAccessWasFE); } catch(...) @@ -161,9 +103,9 @@ bool CartridgeFE::save(Serializer& out) const // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool CartridgeFE::load(Serializer& in) { + CartridgeEnhanced::load(in); try { - myBankOffset = in.getShort(); myLastAccessWasFE = in.getBool(); } catch(...) diff --git a/src/emucore/CartFE.hxx b/src/emucore/CartFE.hxx index dabfafdf4..6c453d284 100644 --- a/src/emucore/CartFE.hxx +++ b/src/emucore/CartFE.hxx @@ -21,7 +21,7 @@ class System; #include "bspf.hxx" -#include "Cart.hxx" +#include "CartEnhanced.hxx" #ifdef DEBUGGER_SUPPORT #include "CartFEWidget.hxx" #endif @@ -72,10 +72,10 @@ class System; particular *why* the address $01FE will be placed on the address bus after both the JSR and RTS opcodes. - @author Stephen Anthony; with ideas/research from Christian Speckner and - alex_79 and TomSon (of AtariAge) + @author Stephen Anthony, Thomas Jentzsch; with ideas/research from Christian + Speckner and alex_79 and TomSon (of AtariAge) */ -class CartridgeFE : public Cartridge +class CartridgeFE : public CartridgeEnhanced { friend class CartridgeFEWidget; @@ -106,42 +106,6 @@ class CartridgeFE : public Cartridge */ void install(System& system) override; - /** - Install pages for the specified bank in the system. - - @param bank The bank that should be installed in the system - */ - bool bank(uInt16 bank) 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 bankCount() 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. @@ -194,20 +158,16 @@ class CartridgeFE : public Cartridge */ bool poke(uInt16 address, uInt8 value) override; - private: + protected: /** Perform bankswitch when necessary, by monitoring for $01FE on the address bus and getting the bank number from the data bus. */ - void checkBankSwitch(uInt16 address, uInt8 value); + bool checkSwitchBank(uInt16 address, uInt8 value) override; + + uInt16 hotspot() const override { return 0x01FE; } private: - // The 8K ROM image of the cartridge - std::array myImage; - - // Indicates the offset into the ROM image (aligns to current bank) - uInt16 myBankOffset{0}; - // Whether previous address by peek/poke equals $01FE (hotspot) bool myLastAccessWasFE{false}; diff --git a/src/emucore/CartMDM.cxx b/src/emucore/CartMDM.cxx index 4c69f7c7c..decb634ec 100644 --- a/src/emucore/CartMDM.cxx +++ b/src/emucore/CartMDM.cxx @@ -21,30 +21,14 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeMDM::CartridgeMDM(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5), - mySize(size) + : CartridgeEnhanced(image, size, md5, settings) { - // 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); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeMDM::reset() -{ - initializeStartBank(0); - - // Upon reset we switch to the startup bank - bank(startBank()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void CartridgeMDM::install(System& system) { - mySystem = &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 @@ -61,9 +45,18 @@ void CartridgeMDM::install(System& system) System::PageAccess access(this, System::PageAccessType::READWRITE); for(uInt16 addr = 0x0800; addr < 0x0BFF; addr += System::PAGE_SIZE) mySystem->setPageAccess(addr, access); +} - // Install pages for bank 0 - bank(startBank()); +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool CartridgeMDM::checkSwitchBank(uInt16 address, uInt8) +{ + // Switch banks if necessary + if((address & 0x1C00) == 0x0800) + { + bank(address & 0x0FF); + return true; + } + return false; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -71,8 +64,8 @@ uInt8 CartridgeMDM::peek(uInt16 address) { // Because of the way we've set up accessing above, we can only // get here when the addresses are from 0x800 - 0xBFF - if((address & 0x1C00) == 0x0800) - bank(address & 0x0FF); + + checkSwitchBank(address); int hotspot = ((address & 0x0F00) >> 8) - 8; return myHotSpotPageAccess[hotspot].device->peek(address); @@ -85,8 +78,7 @@ bool CartridgeMDM::poke(uInt16 address, uInt8 value) // about those below $1000 if(!(address & 0x1000)) { - if((address & 0x1C00) == 0x0800) - bank(address & 0x0FF); + checkSwitchBank(address); int hotspot = ((address & 0x0F00) >> 8) - 8; myHotSpotPageAccess[hotspot].device->poke(address, value); @@ -100,22 +92,7 @@ bool CartridgeMDM::bank(uInt16 bank) { if(bankLocked() || myBankingDisabled) return false; - // Remember what bank we're in - // Wrap around to a valid bank number if necessary - myBankOffset = (bank % bankCount()) << 12; - - // Setup the page access methods for the current bank - System::PageAccess access(this, System::PageAccessType::READ); - - // Map ROM image into the system - for(uInt16 addr = 0x1000; addr < 0x2000; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[myBankOffset + (addr & 0x0FFF)]; - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } + CartridgeEnhanced::bank(bank); // Accesses above bank 127 disable further bankswitching; we're only // concerned with the lower byte @@ -123,38 +100,12 @@ bool CartridgeMDM::bank(uInt16 bank) return myBankChanged = true; } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeMDM::getBank(uInt16) const -{ - return myBankOffset >> 12; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeMDM::bankCount() const -{ - return uInt16(mySize >> 12); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeMDM::patch(uInt16 address, uInt8 value) -{ - myImage[myBankOffset + (address & 0x0FFF)] = value; - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* CartridgeMDM::getImage(size_t& size) const -{ - size = mySize; - return myImage.get(); -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool CartridgeMDM::save(Serializer& out) const { + CartridgeEnhanced::save(out); try { - out.putInt(myBankOffset); out.putBool(myBankingDisabled); } catch(...) @@ -169,9 +120,9 @@ bool CartridgeMDM::save(Serializer& out) const // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool CartridgeMDM::load(Serializer& in) { + CartridgeEnhanced::load(in); try { - myBankOffset = in.getInt(); myBankingDisabled = in.getBool(); } catch(...) @@ -180,8 +131,5 @@ bool CartridgeMDM::load(Serializer& in) return false; } - // Remember what bank we were in - bank(myBankOffset >> 12); - return true; } diff --git a/src/emucore/CartMDM.hxx b/src/emucore/CartMDM.hxx index 1c2a34cb6..4b0786e42 100644 --- a/src/emucore/CartMDM.hxx +++ b/src/emucore/CartMDM.hxx @@ -19,7 +19,7 @@ #define CARTRIDGEMDM_HXX #include "bspf.hxx" -#include "Cart.hxx" +#include "CartEnhanced.hxx" #include "System.hxx" #ifdef DEBUGGER_SUPPORT #include "CartMDMWidget.hxx" @@ -42,9 +42,9 @@ Therefore, there are 128 banks / 512K possible in total. - @author Stephen Anthony, based on 0840 scheme by Fred X. Quimby + @author Stephen Anthony, Thomas Jentzsch, based on 0840 scheme by Fred X. Quimby */ -class CartridgeMDM : public Cartridge +class CartridgeMDM : public CartridgeEnhanced { friend class CartridgeMDMWidget; @@ -62,11 +62,6 @@ class CartridgeMDM : public Cartridge virtual ~CartridgeMDM() = default; public: - /** - Reset device to its power-on state - */ - void reset() override; - /** Install cartridge in the specified system. Invoked by the system when the cartridge is attached to it. @@ -82,35 +77,6 @@ class CartridgeMDM : public Cartridge */ bool bank(uInt16 bank) 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 bankCount() 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. @@ -134,6 +100,8 @@ class CartridgeMDM : public Cartridge */ string name() const override { return "CartridgeMDM"; } + uInt16 hotspot() const override { return 0x0800; } + #ifdef DEBUGGER_SUPPORT /** Get debugger widget responsible for accessing the inner workings @@ -164,18 +132,12 @@ class CartridgeMDM : public Cartridge bool poke(uInt16 address, uInt8 value) override; private: - // Pointer to a dynamically allocated ROM image of the cartridge - ByteBuffer myImage; - - // Size of the ROM image - size_t mySize{0}; + bool checkSwitchBank(uInt16 address, uInt8 value = 0) override; + private: // Previous Device's page access std::array myHotSpotPageAccess; - // Indicates the offset into the ROM image (aligns to current bank) - uInt32 myBankOffset{0}; - // Indicates whether banking has been disabled due to a bankswitch // above bank 127 bool myBankingDisabled{false}; diff --git a/src/emucore/CartMNetwork.cxx b/src/emucore/CartMNetwork.cxx index c5ff0cfab..c6542b2b8 100644 --- a/src/emucore/CartMNetwork.cxx +++ b/src/emucore/CartMNetwork.cxx @@ -36,7 +36,7 @@ void CartridgeMNetwork::initialize(const ByteBuffer& image, size_t size) std::copy_n(image.get(), std::min(romSize(), size), myImage.get()); createRomAccessArrays(romSize() + myRAM.size()); - myRAMSlice = bankCount() - 1; + myRAMBank = romBankCount() - 1; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -96,10 +96,10 @@ void CartridgeMNetwork::install(System& system) /*setAccess(0x1FE0 & ~System::PAGE_MASK, System::PAGE_SIZE, 0, nullptr, 0x1fc0, System::PA_NONE, 0x1fc0);*/ - // Setup the second segment to always point to the last ROM slice + // Setup the second segment to always point to the last ROM bank setAccess(0x1A00, 0x1FE0U & (~System::PAGE_MASK - 0x1A00), - myRAMSlice * BANK_SIZE, myImage.get(), myRAMSlice * BANK_SIZE, System::PageAccessType::READ, BANK_SIZE - 1); - myCurrentSlice[1] = myRAMSlice; + myRAMBank * BANK_SIZE, myImage.get(), myRAMBank * BANK_SIZE, System::PageAccessType::READ, BANK_SIZE - 1); + myCurrentBank[1] = myRAMBank; // Install some default banks for the RAM and first segment bankRAM(0); @@ -115,7 +115,7 @@ uInt8 CartridgeMNetwork::peek(uInt16 address) // Switch banks if necessary checkSwitchBank(address); - if((myCurrentSlice[0] == myRAMSlice) && (address < BANK_SIZE / 2)) + if((myCurrentBank[0] == myRAMBank) && (address < BANK_SIZE / 2)) { // Reading from the 1K write port @ $1000 triggers an unwanted write return peekRAM(myRAM[address & (BANK_SIZE / 2 - 1)], peekAddress); @@ -126,7 +126,7 @@ uInt8 CartridgeMNetwork::peek(uInt16 address) return peekRAM(myRAM[0x0400 + (myCurrentRAM << 8) + (address & 0x00FF)], peekAddress); } else - return myImage[(myCurrentSlice[address >> 11] << 11) + (address & (BANK_SIZE - 1))]; + return myImage[(myCurrentBank[address >> 11] << 11) + (address & (BANK_SIZE - 1))]; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -139,9 +139,9 @@ bool CartridgeMNetwork::poke(uInt16 address, uInt8 value) checkSwitchBank(address); // All RAM writes are mapped here - if((myCurrentSlice[0] == myRAMSlice) && (address < BANK_SIZE / 2)) + if((myCurrentBank[0] == myRAMBank) && (address < BANK_SIZE / 2)) { - // RAM slices + // RAM banks if(!(address & 0x0400)) { pokeRAM(myRAM[address & (BANK_SIZE / 2 - 1)], pokeAddress, value); @@ -189,7 +189,7 @@ void CartridgeMNetwork::bankRAM(uInt16 bank) // Remember what bank we're in myCurrentRAM = bank; - uInt16 offset = bank << 8; // * RAM_SLICE_SIZE (256) + uInt16 offset = bank << 8; // * RAM_BANK_SIZE (256) // Setup the page access methods for the current bank // Set the page accessing method for the 256 bytes of RAM reading pages @@ -201,26 +201,26 @@ void CartridgeMNetwork::bankRAM(uInt16 bank) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeMNetwork::bank(uInt16 slice) +bool CartridgeMNetwork::bank(uInt16 bank) { if(bankLocked()) return false; // Remember what bank we're in - myCurrentSlice[0] = slice; + myCurrentBank[0] = bank; // Setup the page access methods for the current bank - if(slice != myRAMSlice) + if(bank != myRAMBank) { - uInt16 offset = slice << 11; // * BANK_SIZE (2048) + uInt16 offset = bank << 11; // * BANK_SIZE (2048) // Map ROM image into first segment setAccess(0x1000, BANK_SIZE, offset, myImage.get(), offset, System::PageAccessType::READ); } else { - // Set the page accessing method for the 1K slice of RAM writing pages + // Set the page accessing method for the 1K bank of RAM writing pages setAccess(0x1000, BANK_SIZE / 2, 0, myRAM.data(), romSize(), System::PageAccessType::WRITE); - // Set the page accessing method for the 1K slice of RAM reading pages + // Set the page accessing method for the 1K bank of RAM reading pages setAccess(0x1000 + BANK_SIZE / 2, BANK_SIZE / 2, 0, myRAM.data(), romSize(), System::PageAccessType::READ); } return myBankChanged = true; @@ -229,7 +229,7 @@ bool CartridgeMNetwork::bank(uInt16 slice) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uInt16 CartridgeMNetwork::getBank(uInt16 address) const { - return myCurrentSlice[(address & 0xFFF) >> 11]; // 2K slices + return myCurrentBank[(address & 0xFFF) >> 11]; // 2K segments } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -239,7 +239,7 @@ bool CartridgeMNetwork::patch(uInt16 address, uInt8 value) if(address < 0x0800) { - if(myCurrentSlice[0] == myRAMSlice) + if(myCurrentBank[0] == myRAMBank) { // Normally, a write to the read port won't do anything // However, the patch command is special in that ignores such @@ -247,7 +247,7 @@ bool CartridgeMNetwork::patch(uInt16 address, uInt8 value) myRAM[address & 0x03FF] = value; } else - myImage[(myCurrentSlice[0] << 11) + (address & (BANK_SIZE-1))] = value; + myImage[(myCurrentBank[0] << 11) + (address & (BANK_SIZE-1))] = value; } else if(address < 0x0900) { @@ -257,7 +257,7 @@ bool CartridgeMNetwork::patch(uInt16 address, uInt8 value) myRAM[0x0400 + (myCurrentRAM << 8) + (address & 0x00FF)] = value; } else - myImage[(myCurrentSlice[address >> 11] << 11) + (address & (BANK_SIZE-1))] = value; + myImage[(myCurrentBank[address >> 11] << 11) + (address & (BANK_SIZE-1))] = value; return myBankChanged = true; } @@ -265,7 +265,7 @@ bool CartridgeMNetwork::patch(uInt16 address, uInt8 value) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - const uInt8* CartridgeMNetwork::getImage(size_t& size) const { - size = bankCount() * BANK_SIZE; + size = romBankCount() * BANK_SIZE; return myImage.get(); } @@ -274,7 +274,7 @@ bool CartridgeMNetwork::save(Serializer& out) const { try { - out.putShortArray(myCurrentSlice.data(), myCurrentSlice.size()); + out.putShortArray(myCurrentBank.data(), myCurrentBank.size()); out.putShort(myCurrentRAM); out.putByteArray(myRAM.data(), myRAM.size()); } @@ -292,7 +292,7 @@ bool CartridgeMNetwork::load(Serializer& in) { try { - in.getShortArray(myCurrentSlice.data(), myCurrentSlice.size()); + in.getShortArray(myCurrentBank.data(), myCurrentBank.size()); myCurrentRAM = in.getShort(); in.getByteArray(myRAM.data(), myRAM.size()); } @@ -304,13 +304,13 @@ bool CartridgeMNetwork::load(Serializer& in) // Set up the previously used banks for the RAM and segment bankRAM(myCurrentRAM); - bank(myCurrentSlice[0]); + bank(myCurrentBank[0]); return true; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeMNetwork::bankCount() const +uInt16 CartridgeMNetwork::romBankCount() const { return uInt16(mySize >> 11); } @@ -318,5 +318,5 @@ uInt16 CartridgeMNetwork::bankCount() const // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uInt16 CartridgeMNetwork::romSize() const { - return bankCount() * BANK_SIZE; + return romBankCount() * BANK_SIZE; } diff --git a/src/emucore/CartMNetwork.hxx b/src/emucore/CartMNetwork.hxx index ad0decad9..95cd24b56 100644 --- a/src/emucore/CartMNetwork.hxx +++ b/src/emucore/CartMNetwork.hxx @@ -51,7 +51,7 @@ here by accessing 1FE8 to 1FEB. This cart reports having 8 banks; 1 for each of the possible 7 - slices in the lower 2K area, and the last for RAM in the lower + bank in the lower 2K area, and the last for RAM in the lower 2K area." There are 8K, 12K and 16K variations, with or without RAM. @@ -106,9 +106,9 @@ class CartridgeMNetwork : public Cartridge uInt16 getBank(uInt16 address = 0) const override; /** - Query the number of banks supported by the cartridge. + Query the number of banks supported by the cartridge. */ - uInt16 bankCount() const override; + uInt16 romBankCount() const override; /** Patch the cartridge ROM. @@ -179,7 +179,7 @@ class CartridgeMNetwork : public Cartridge private: // Size of RAM in the cart static constexpr uInt32 RAM_SIZE = 0x800; // 1K + 4 * 256B = 2K - // Number of slices with 4K address space + // Number of segment within the 4K address space static constexpr uInt32 NUM_SEGMENTS = 2; /** @@ -198,8 +198,6 @@ class CartridgeMNetwork : public Cartridge private: // Pointer to a dynamically allocated ROM image of the cartridge ByteBuffer myImage; - // The 16K ROM image of the cartridge (works for E78K too) - //uInt8 myImage[BANK_SIZE * 8]; // Size of the ROM image size_t mySize{0}; @@ -207,14 +205,14 @@ class CartridgeMNetwork : public Cartridge // The 2K of RAM std::array myRAM; - // Indicates which slice is in the segment - std::array myCurrentSlice; + // Indicates which bank is in the segment + std::array myCurrentBank; // Indicates which 256 byte bank of RAM is being used uInt16 myCurrentRAM{0}; - // The number of the RAM slice (== bankCount() - 1) - uInt32 myRAMSlice{0}; + // The number of the RAM bank (== bankCount() - 1) + uInt32 myRAMBank{0}; private: // Following constructors and assignment operators not supported diff --git a/src/emucore/CartSB.cxx b/src/emucore/CartSB.cxx index 44ff9381f..5caa01be7 100644 --- a/src/emucore/CartSB.cxx +++ b/src/emucore/CartSB.cxx @@ -21,30 +21,14 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeSB::CartridgeSB(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5), - mySize(size) + : CartridgeEnhanced(image, size, md5, settings) { - // 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); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeSB::reset() -{ - initializeStartBank(bankCount() - 1); - - // Upon reset we switch to the startup bank - bank(startBank()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void CartridgeSB::install(System& system) { - mySystem = &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 @@ -62,19 +46,26 @@ void CartridgeSB::install(System& system) // Set the page accessing methods for the hot spots for(uInt16 addr = 0x0800; addr < 0x0FFF; addr += System::PAGE_SIZE) mySystem->setPageAccess(addr, access); +} - // Install pages for startup bank - bank(startBank()); +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool CartridgeSB::checkSwitchBank(uInt16 address, uInt8) +{ + // Switch banks if necessary + if((address & 0x1800) == 0x0800) + { + bank(address & (romBankCount() - 1)); + return true; + } + return false; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uInt8 CartridgeSB::peek(uInt16 address) { - address &= (0x17FF + (mySize >> 12)); + address &= (0x17FF + romBankCount()); - // Switch banks if necessary - if ((address & 0x1800) == 0x0800) - bank(address & startBank()); + checkSwitchBank(address); if(!(address & 0x1000)) { @@ -90,11 +81,9 @@ uInt8 CartridgeSB::peek(uInt16 address) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool CartridgeSB::poke(uInt16 address, uInt8 value) { - address &= (0x17FF + (mySize >> 12)); + address &= (0x17FF + romBankCount()); - // Switch banks if necessary - if((address & 0x1800) == 0x0800) - bank(address & startBank()); + checkSwitchBank(address); if(!(address & 0x1000)) { @@ -105,87 +94,3 @@ bool CartridgeSB::poke(uInt16 address, uInt8 value) } return false; } - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeSB::bank(uInt16 bank) -{ - if(bankLocked()) return false; - - // Remember what bank we're in - myBankOffset = bank << 12; - - // Setup the page access methods for the current bank - System::PageAccess access(this, System::PageAccessType::READ); - - // Map ROM image into the system - for(uInt16 addr = 0x1000; addr < 0x2000; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[myBankOffset + (addr & 0x0FFF)]; - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeSB::getBank(uInt16) const -{ - return myBankOffset >> 12; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeSB::bankCount() const -{ - return uInt16(mySize >> 12); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeSB::patch(uInt16 address, uInt8 value) -{ - myImage[myBankOffset + (address & 0x0FFF)] = value; - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* CartridgeSB::getImage(size_t& size) const -{ - size = mySize; - return myImage.get(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeSB::save(Serializer& out) const -{ - try - { - out.putInt(myBankOffset); - } - catch(...) - { - cerr << "ERROR: CartridgeSB::save" << endl; - return false; - } - - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeSB::load(Serializer& in) -{ - try - { - myBankOffset = in.getInt(); - } - catch(...) - { - cerr << "ERROR: CartridgeSB::load" << endl; - return false; - } - - // Remember what bank we were in - bank(myBankOffset >> 12); - - return true; -} diff --git a/src/emucore/CartSB.hxx b/src/emucore/CartSB.hxx index fe8f8b75d..85c8e9ccb 100644 --- a/src/emucore/CartSB.hxx +++ b/src/emucore/CartSB.hxx @@ -19,7 +19,7 @@ #define CARTRIDGESB_HXX #include "bspf.hxx" -#include "Cart.hxx" +#include "CartEnhanced.hxx" #include "System.hxx" #ifdef DEBUGGER_SUPPORT #include "CartSBWidget.hxx" @@ -31,9 +31,9 @@ (32 banks) and $800 - $83F (64 banks). All mirrors up to $FFF are also used ($900, $A00, ...). - @author Fred X. Quimby + @author Fred X. Quimby, Thomas Jentzsch */ -class CartridgeSB : public Cartridge +class CartridgeSB : public CartridgeEnhanced { friend class CartridgeSBWidget; @@ -51,11 +51,6 @@ class CartridgeSB : public Cartridge virtual ~CartridgeSB() = default; public: - /** - Reset device to its power-on state - */ - void reset() override; - /** Install cartridge in the specified system. Invoked by the system when the cartridge is attached to it. @@ -64,58 +59,6 @@ class CartridgeSB : public Cartridge */ void install(System& system) override; - /** - Install pages for the specified bank in the system. - - @param bank The bank that should be installed in the system - */ - bool bank(uInt16 bank) 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 bankCount() 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). @@ -153,13 +96,13 @@ class CartridgeSB : public Cartridge bool poke(uInt16 address, uInt8 value) override; private: - // The 128-256K ROM image and size of the cartridge - ByteBuffer myImage; - size_t mySize{0}; + bool checkSwitchBank(uInt16 address, uInt8 value = 0) override; - // Indicates the offset into the ROM image (aligns to current bank) - uInt32 myBankOffset{0}; + uInt16 hotspot() const override { return 0x0800; } + uInt16 getStartBank() const override { return romBankCount() - 1; } + + private: // Previous Device's page access std::array myHotSpotPageAccess; diff --git a/src/emucore/CartUA.cxx b/src/emucore/CartUA.cxx index bfa1321bb..74ccc3773 100644 --- a/src/emucore/CartUA.cxx +++ b/src/emucore/CartUA.cxx @@ -22,26 +22,15 @@ CartridgeUA::CartridgeUA(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings, bool swapHotspots) - : Cartridge(settings, md5), + : CartridgeEnhanced(image, size, md5, settings), mySwappedHotspots(swapHotspots) { - // Copy the ROM image into my buffer - std::copy_n(image.get(), std::min(myImage.size(), size), myImage.begin()); - createRomAccessArrays(myImage.size()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeUA::reset() -{ - // Upon reset we switch to the startup bank - initializeStartBank(0); - bank(startBank()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void CartridgeUA::install(System& system) { - mySystem = &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 @@ -60,26 +49,33 @@ void CartridgeUA::install(System& system) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeUA::peek(uInt16 address) +bool CartridgeUA::checkSwitchBank(uInt16 address, uInt8) { - address &= 0x1FFF; - // Switch banks if necessary switch(address & 0x1260) { case 0x0220: // Set the current bank to the lower 4k bank bank(mySwappedHotspots ? 1 : 0); - break; + return true; case 0x0240: // Set the current bank to the upper 4k bank bank(mySwappedHotspots ? 0 : 1); - break; + return true; default: break; } + return false; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 CartridgeUA::peek(uInt16 address) +{ + address &= myBankMask; + + checkSwitchBank(address); // Because of the way accessing is set up, we will only get here // when doing a TIA read @@ -90,24 +86,9 @@ uInt8 CartridgeUA::peek(uInt16 address) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool CartridgeUA::poke(uInt16 address, uInt8 value) { - address &= 0x1FFF; + address &= myBankMask; - // Switch banks if necessary - switch(address & 0x1260) - { - case 0x0220: - // Set the current bank to the lower 4k bank - bank(mySwappedHotspots ? 1 : 0); - break; - - case 0x0240: - // Set the current bank to the upper 4k bank - bank(mySwappedHotspots ? 0 : 1); - break; - - default: - break; - } + checkSwitchBank(address); // Because of the way accessing is set up, we will may get here by // doing a write to TIA or cart; we ignore the cart write @@ -119,87 +100,3 @@ bool CartridgeUA::poke(uInt16 address, uInt8 value) return false; } - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeUA::bank(uInt16 bank) -{ - if(bankLocked()) return false; - - // Remember what bank we're in - myBankOffset = bank << 12; - - // Setup the page access methods for the current bank - System::PageAccess access(this, System::PageAccessType::READ); - - // Map ROM image into the system - for(uInt16 addr = 0x1000; addr < 0x2000; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[myBankOffset + (addr & 0x0FFF)]; - access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeUA::getBank(uInt16) const -{ - return myBankOffset >> 12; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeUA::bankCount() const -{ - return 2; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeUA::patch(uInt16 address, uInt8 value) -{ - myImage[myBankOffset + (address & 0x0FFF)] = value; - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* CartridgeUA::getImage(size_t& size) const -{ - size = myImage.size(); - return myImage.data(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeUA::save(Serializer& out) const -{ - try - { - out.putShort(myBankOffset); - } - catch(...) - { - cerr << "ERROR: " << name() << "::save" << endl; - return false; - } - - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeUA::load(Serializer& in) -{ - try - { - myBankOffset = in.getShort(); - } - catch(...) - { - cerr << "ERROR: " << name() << "::load" << endl; - return false; - } - - // Remember what bank we were in - bank(myBankOffset >> 12); - - return true; -} diff --git a/src/emucore/CartUA.hxx b/src/emucore/CartUA.hxx index 38588e4a9..77b83d10b 100644 --- a/src/emucore/CartUA.hxx +++ b/src/emucore/CartUA.hxx @@ -19,7 +19,7 @@ #define CARTRIDGEUA_HXX #include "bspf.hxx" -#include "Cart.hxx" +#include "CartEnhanced.hxx" #include "System.hxx" #ifdef DEBUGGER_SUPPORT #include "CartUAWidget.hxx" @@ -30,9 +30,9 @@ are two 4K banks, which are switched by accessing $0220 (bank 0) and $0240 (bank 1). - @author Bradford W. Mott + @author Bradford W. Mott, Thomas Jentzsch */ -class CartridgeUA : public Cartridge +class CartridgeUA : public CartridgeEnhanced { friend class CartridgeUAWidget; @@ -51,11 +51,6 @@ class CartridgeUA : public Cartridge virtual ~CartridgeUA() = default; public: - /** - Reset device to its power-on state - */ - void reset() override; - /** Install cartridge in the specified system. Invoked by the system when the cartridge is attached to it. @@ -64,64 +59,14 @@ class CartridgeUA : public Cartridge */ void install(System& system) override; - /** - Install pages for the specified bank in the system. - - @param bank The bank that should be installed in the system - */ - bool bank(uInt16 bank) 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 bankCount() 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). @return The name of the object */ - string name() const override { return mySwappedHotspots ? "CartridgeUASW" : "CartridgeUA"; } + string name() const override { + return mySwappedHotspots ? "CartridgeUASW" : "CartridgeUA"; + } #ifdef DEBUGGER_SUPPORT /** @@ -153,15 +98,14 @@ class CartridgeUA : public Cartridge bool poke(uInt16 address, uInt8 value) override; private: - // The 8K ROM image of the cartridge - std::array myImage; + bool checkSwitchBank(uInt16 address, uInt8 value = 0) override; + uInt16 hotspot() const override { return 0x0220; } + + private: // Previous Device's page access std::array myHotSpotPageAccess; - // Indicates the offset into the ROM image (aligns to current bank) - uInt16 myBankOffset{0}; - // Indicates if banks are swapped ("Mickey" cart) bool mySwappedHotspots{false}; diff --git a/src/emucore/CartWD.cxx b/src/emucore/CartWD.cxx index c6373fa05..11ebc653e 100644 --- a/src/emucore/CartWD.cxx +++ b/src/emucore/CartWD.cxx @@ -23,70 +23,46 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeWD::CartridgeWD(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5), - mySize(std::min(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 banks 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::bankCount() 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::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 diff --git a/src/emucore/CartWD.hxx b/src/emucore/CartWD.hxx index 0e2aad45c..398571a52 100644 --- a/src/emucore/CartWD.hxx +++ b/src/emucore/CartWD.hxx @@ -21,7 +21,7 @@ class System; #include "bspf.hxx" -#include "Cart.hxx" +#include "CartEnhanced.hxx" #ifdef DEBUGGER_SUPPORT #include "CartWDWidget.hxx" #endif @@ -30,8 +30,8 @@ class System; This is the cartridge class for a "Wickstead Design" prototype cart. The ROM has 64 bytes of RAM. In this bankswitching scheme the 2600's 4K cartridge address space - is broken into four 1K segments. The desired arrangement of 1K slices - is selected by accessing $30 - $3F of TIA address space. The slices + is broken into four 1K segments. The desired arrangement of 1K banks + is selected by accessing $30 - $3F of TIA address space. The banks are mapped into all 4 segments at once as follows: $0030, $0038: 0,0,1,3 @@ -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 bankCount() 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,11 @@ class CartridgeWD : public Cartridge bool poke(uInt16 address, uInt8 value) override; private: - /** - Install the specified slice for segment zero. + bool checkSwitchBank(uInt16, uInt8 = 0) override { return false; } - @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); + uInt16 hotspot() const override { return 0x0030; } private: - // The 8K ROM image of the cartridge - std::array 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 myRAM; - - // The 1K ROM mirror of segment 3 (sometimes contains extra 3 bytes) - std::array mySegment3; - - // Indicates the offset for each of the four segments - std::array myOffset; - // Indicates the cycle at which a bankswitch was initiated uInt64 myCyclesAtBankswitchInit{0}; @@ -233,6 +171,16 @@ class CartridgeWD : public Cartridge }; static const std::array ourBankOrg; + private: + // log(ROM bank segment size) / log(2) + static constexpr uInt16 BANK_SHIFT = 10; // = 1K = 0x0400 + + // RAM size + static constexpr size_t 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; diff --git a/src/emucore/CartX07.cxx b/src/emucore/CartX07.cxx index 96bf5e4ed..ac40b6ac6 100644 --- a/src/emucore/CartX07.cxx +++ b/src/emucore/CartX07.cxx @@ -23,25 +23,14 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeX07::CartridgeX07(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5) + : CartridgeEnhanced(image, size, md5, settings) { - // Copy the ROM image into my buffer - std::copy_n(image.get(), std::min(myImage.size(), size), myImage.begin()); - createRomAccessArrays(myImage.size()); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeX07::reset() -{ - // Upon reset we switch to the startup bank - initializeStartBank(0); - bank(startBank()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void CartridgeX07::install(System& system) { - mySystem = &system; + CartridgeEnhanced::install(system); // Set the page accessing methods for the hot spots // The hotspots use almost all addresses below 0x1000, so we simply grab them @@ -49,31 +38,42 @@ void CartridgeX07::install(System& system) System::PageAccess access(this, System::PageAccessType::READWRITE); for(uInt16 addr = 0x00; addr < 0x1000; addr += System::PAGE_SIZE) mySystem->setPageAccess(addr, access); +} - // Install pages for the startup bank - bank(startBank()); +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool CartridgeX07::checkSwitchBank(uInt16 address, uInt8) +{ + // Switch banks if necessary + if((address & 0x180f) == 0x080d) + { + bank((address & 0xf0) >> 4); + return true; + } + else if((address & 0x1880) == 0) + { + if((getBank() & 0xe) == 0xe) + { + bank(((address & 0x40) >> 6) | 0xe); + return true; + } + } + + return false; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uInt8 CartridgeX07::peek(uInt16 address) { - uInt8 value = 0; - + uInt8 value = 0; // JTZ: is this correct? // Check for RAM or TIA mirroring uInt16 lowAddress = address & 0x3ff; + if(lowAddress & 0x80) value = mySystem->m6532().peek(address); else if(!(lowAddress & 0x200)) value = mySystem->tia().peek(address); - // Switch banks if necessary - if((address & 0x180f) == 0x080d) - bank((address & 0xf0) >> 4); - else if((address & 0x1880) == 0) - { - if((myCurrentBank & 0xe) == 0xe) - bank(((address & 0x40) >> 6) | (myCurrentBank & 0xe)); - } + checkSwitchBank(address); return value; } @@ -83,103 +83,13 @@ bool CartridgeX07::poke(uInt16 address, uInt8 value) { // Check for RAM or TIA mirroring uInt16 lowAddress = address & 0x3ff; + if(lowAddress & 0x80) mySystem->m6532().poke(address, value); else if(!(lowAddress & 0x200)) mySystem->tia().poke(address, value); - // Switch banks if necessary - if((address & 0x180f) == 0x080d) - bank((address & 0xf0) >> 4); - else if((address & 0x1880) == 0) - { - if((myCurrentBank & 0xe) == 0xe) - bank(((address & 0x40) >> 6) | (myCurrentBank & 0xe)); - } + checkSwitchBank(address); + return false; } - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeX07::bank(uInt16 bank) -{ - if(bankLocked()) return false; - - // Remember what bank we're in - myCurrentBank = (bank & 0x0f); - uInt32 offset = myCurrentBank << 12; - - // Setup the page access methods for the current bank - System::PageAccess access(this, System::PageAccessType::READ); - - // Map ROM image into the system - for(uInt16 addr = 0x1000; addr < 0x2000; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[offset + (addr & 0x0FFF)]; - access.romAccessBase = &myRomAccessBase[offset + (addr & 0x0FFF)]; - access.romPeekCounter = &myRomAccessCounter[offset + (addr & 0x0FFF)]; - access.romPokeCounter = &myRomAccessCounter[offset + (addr & 0x0FFF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeX07::getBank(uInt16) const -{ - return myCurrentBank; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 CartridgeX07::bankCount() const -{ - return 16; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeX07::patch(uInt16 address, uInt8 value) -{ - myImage[(myCurrentBank << 12) + (address & 0x0FFF)] = value; - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* CartridgeX07::getImage(size_t& size) const -{ - size = myImage.size(); - return myImage.data(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeX07::save(Serializer& out) const -{ - try - { - out.putShort(myCurrentBank); - } - catch(...) - { - cerr << "ERROR: CartridgeX07::save" << endl; - return false; - } - - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeX07::load(Serializer& in) -{ - try - { - myCurrentBank = in.getShort(); - } - catch(...) - { - cerr << "ERROR: CartridgeX07::load" << endl; - return false; - } - - // Remember what bank we were in - bank(myCurrentBank); - - return true; -} diff --git a/src/emucore/CartX07.hxx b/src/emucore/CartX07.hxx index 0beff0e0b..3cde4efdf 100644 --- a/src/emucore/CartX07.hxx +++ b/src/emucore/CartX07.hxx @@ -21,7 +21,7 @@ class System; #include "bspf.hxx" -#include "Cart.hxx" +#include "CartEnhanced.hxx" #ifdef DEBUGGER_SUPPORT #include "CartX07Widget.hxx" #endif @@ -40,9 +40,9 @@ class System; Note that the latter will hit on almost any TIA access. - @author Eckhard Stolberg + @author Eckhard Stolberg, Thomas Jentzsch */ -class CartridgeX07 : public Cartridge +class CartridgeX07 : public CartridgeEnhanced { friend class CartridgeX07Widget; @@ -60,11 +60,6 @@ class CartridgeX07 : public Cartridge virtual ~CartridgeX07() = default; public: - /** - Reset device to its power-on state - */ - void reset() override; - /** Install cartridge in the specified system. Invoked by the system when the cartridge is attached to it. @@ -73,58 +68,6 @@ class CartridgeX07 : public Cartridge */ void install(System& system) override; - /** - Install pages for the specified bank in the system. - - @param bank The bank that should be installed in the system - */ - bool bank(uInt16 bank) 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 bankCount() 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). @@ -162,11 +105,7 @@ class CartridgeX07 : public Cartridge bool poke(uInt16 address, uInt8 value) override; private: - // The 64K ROM image of the cartridge - std::array myImage; - - // Indicates which bank is currently active - uInt16 myCurrentBank{0}; + bool checkSwitchBank(uInt16 address, uInt8 value = 0) override; private: // Following constructors and assignment operators not supported diff --git a/src/emucore/Console.cxx b/src/emucore/Console.cxx index fc5c1e9be..3fe69b147 100644 --- a/src/emucore/Console.cxx +++ b/src/emucore/Console.cxx @@ -574,7 +574,7 @@ void Console::toggleTurbo() initializeAudio(); // update VSync - myOSystem.createFrameBuffer(); + initializeVideo(); ostringstream ss; ss << "Turbo mode " << (!enabled ? "enabled" : "disabled"); @@ -651,7 +651,7 @@ FBInitStatus Console::initializeVideo(bool full) bool devSettings = myOSystem.settings().getBool("dev.settings"); const string& title = string("Stella ") + STELLA_VERSION + ": \"" + myProperties.get(PropType::Cart_Name) + "\""; - fbstatus = myOSystem.frameBuffer().createDisplay(title, + fbstatus = myOSystem.frameBuffer().createDisplay(title, FrameBuffer::BufferType::Emulator, TIAConstants::viewableWidth, TIAConstants::viewableHeight, false); if(fbstatus != FBInitStatus::Success) return fbstatus; diff --git a/src/emucore/DefProps.hxx b/src/emucore/DefProps.hxx index b8db6bf24..fc0fe6155 100644 --- a/src/emucore/DefProps.hxx +++ b/src/emucore/DefProps.hxx @@ -25,7 +25,7 @@ regenerated and the application recompiled. */ -static constexpr uInt32 DEF_PROPS_SIZE = 3510; +static constexpr uInt32 DEF_PROPS_SIZE = 3514; static const BSPF::array2D DefProps = {{ { "000509d1ed2b8d30a9d94be1b3b5febb", "Greg Zumwalt", "", "Jungle Jane (2003) (Greg Zumwalt) (Hack)", "Hack of Pitfall!", "Hack", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, @@ -341,6 +341,7 @@ static const BSPF::array2D DefProps = {{ { "15fe28d0c8893be9223e8cb2d032e557", "", "", "Towering Inferno (208 in 1) (Unknown) (PAL)", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "YES", "" }, { "1619bc27632f9148d8480cd813aa74c3", "Thomas Jentzsch", "", "Steeple Chase (Thomas Jentzsch)", "NTSC Conversion", "Homebrew", "", "", "", "", "A", "", "", "", "", "", "", "", "", "", "", "YES", "" }, { "161ded4a85d3c78e44fffd40426f537f", "Thomas Jentzsch", "", "JtzBall (Alpha) (TJ)", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, + { "16229d61d7b0c89b01853660a8da22bb", "", "", "spin4a50", "", "", "", "", "4A50", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, { "163e7e757e2dc44469123ff0e5daec5e", "", "", "Many Blue Bars and Text Demo 2 (PD)", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, { "169d4c7bd3a4d09e184a3b993823d048", "", "", "Superman (Unknown) (PAL) [a]", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, { "16cb43492987d2f32b423817cdaaf7c4", "Atari, Larry Kaplan - Sears", "CX2602 - 99802, 6-99802, 49-75102", "Air-Sea Battle (1977) (Atari)", "AKA Target Fun (Anti-Aircraft)", "Uncommon", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, @@ -1032,6 +1033,7 @@ static const BSPF::array2D DefProps = {{ { "4904a2550759b9b4570e886374f9d092", "Parker Brothers, Charlie Heath", "931506", "Reactor (1983) (Parker Bros) (PAL)", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "YES", "" }, { "490e3cc59d82f85fae817cdf767ea7a0", "", "", "Berzerk (Unknown) (PAL) [a]", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, { "490eed07d4691b27f473953fbea6541a", "Activision, Steve Cartwright, David Crane", "AB-035-04", "Pitfall II (1983) (Activision) [a]", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, + { "49366f41aa7a54baf263426e99ce4312", "", "", "POP-MDM-Test (PAL) (63 games)", "", "", "", "", "MDM", "", "", "", "", "JOYSTICK", "JOYSTICK", "", "", "", "", "", "", "", "" }, { "493daaf9fb1ba450eba6b8ed53ffb37d", "", "", "3-D Corridor Demo (27-03-2003) (MP)", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, { "493de059b32f84ab29cde6213964aeee", "Atari, Bill Aspromonte, Andrew Fuchs", "CX26120", "Stargate (1984) (Atari) (PAL)", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, { "493e90602a4434b117c91c95e73828d1", "Telegames", "", "Lock 'n' Chase (1988) (Telegames) (PAL)", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, @@ -1366,6 +1368,7 @@ static const BSPF::array2D DefProps = {{ { "61719a8bdafbd8dab3ca9ce7b171b9e2", "", "", "Enduro (Unknown) (PAL)", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, { "61728c6cfb052e62a9ed088c5bf407ba", "", "", "Sprite Demo 4 (PD)", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, { "619de46281eb2e0adbb98255732483b4", "", "", "Time Warp (Unknown)", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, + { "61baadddc2c8f6e5faa57d4d0f285462", "", "", "208-in-1 MDM-Test (PAL) (127 games)", "", "", "", "", "MDM", "", "", "", "", "JOYSTICK", "JOYSTICK", "", "", "", "", "", "", "", "" }, { "61dbe94f110f30ca4ec524ae5ce2d026", "CCE", "C-820", "Space Invaders (1983) (CCE)", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, { "61e0f5e1cc207e98704d0758c68df317", "Star Game", "007", "Tennis (Star Game)", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, { "61ef8c2fc43be9a04fe13fdb79ff2bd9", "", "", "Gas Gauge Demo - Revisited (2001) (Joe Grand) (PD)", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, @@ -2326,6 +2329,7 @@ static const BSPF::array2D DefProps = {{ { "a89a3e0547d6887279c34aba4b17a560", "M Network, Steve Crandall, Patricia Lewis Du Long", "MT4646", "Rocky & Bullwinkle (1983) (Mattel) (Prototype)", "", "Prototype", "", "", "4K", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, { "a8a703e073183a89c94d4d99b9661b7f", "Franklin Cruz", "", "Spice Invaders (Franklin Cruz) (Hack)", "Hack of Space Invaders", "Hack", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, { "a8b3ea6836b99bea77c8f603cf1ea187", "CCE", "C-861", "Boxing (1983) (CCE)", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, + { "a8c447efbec3a2b5d08b05a09999bd92", "", "", "MegaCart Menu", "", "", "", "", "", "", "", "", "", "JOYSTICK", "JOYSTICK", "", "", "", "", "", "", "", "" }, { "a8c48b4e0bf35fe97cc84fdd2c507f78", "Puzzy - Bit Corporation", "PG201", "Seamonster (1982) (Puzzy)", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, { "a8d0a4a77cd71ac601bd71df5a060e4c", "", "", "Space Shuttle (1983) (Activision) [t2] (Fuel)", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, { "a8d4a9500b18b0a067a1f272f869e094", "", "", "Red And White Checkerboard Demo (PD)", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, diff --git a/src/emucore/FrameBuffer.cxx b/src/emucore/FrameBuffer.cxx index eb35cfe5c..250a895cf 100644 --- a/src/emucore/FrameBuffer.cxx +++ b/src/emucore/FrameBuffer.cxx @@ -203,10 +203,14 @@ FontDesc FrameBuffer::getFontDesc(const string& name) const #endif // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -FBInitStatus FrameBuffer::createDisplay(const string& title, +FBInitStatus FrameBuffer::createDisplay(const string& title, BufferType type, uInt32 width, uInt32 height, bool honourHiDPI) { + // always save, maybe only the mode of the window has changed + saveCurrentWindowPosition(); + myBufferType = type; + ++myInitializedCount; myScreenTitle = title; @@ -789,6 +793,58 @@ void FrameBuffer::stateChanged(EventHandlerState state) update(true); // force full update } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string FrameBuffer::getDisplayKey() +{ + // save current window's display and position + switch(myBufferType) + { + case BufferType::Launcher: + return "launcherdisplay"; + + case BufferType::Emulator: + return "display"; + + #ifdef DEBUGGER_SUPPORT + case BufferType::Debugger: + return "dbg.display"; + #endif + + default: + return ""; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string FrameBuffer::getPositionKey() +{ + // save current window's display and position + switch(myBufferType) + { + case BufferType::Launcher: + return "launcherpos"; + + case BufferType::Emulator: + return "windowedpos"; + + #ifdef DEBUGGER_SUPPORT + case BufferType::Debugger: + return "dbg.pos"; + #endif + + default: + return ""; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void FrameBuffer::saveCurrentWindowPosition() +{ + myOSystem.settings().setValue(getDisplayKey(), getCurrentDisplayIndex()); + if(isCurrentWindowPositioned()) + myOSystem.settings().setValue(getPositionKey(), getCurrentWindowPos()); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void FrameBuffer::setFullscreen(bool enable) { diff --git a/src/emucore/FrameBuffer.hxx b/src/emucore/FrameBuffer.hxx index 35c656cf0..a73b01ffb 100644 --- a/src/emucore/FrameBuffer.hxx +++ b/src/emucore/FrameBuffer.hxx @@ -81,6 +81,13 @@ class FrameBuffer } }; + enum class BufferType { + None, + Launcher, + Emulator, + Debugger + }; + enum class ScalingInterpolation { none, sharp, @@ -115,7 +122,8 @@ class FrameBuffer @return Status of initialization (see FBInitStatus 'enum') */ - FBInitStatus createDisplay(const string& title, uInt32 width, uInt32 height, + FBInitStatus createDisplay(const string& title, BufferType type, + uInt32 width, uInt32 height, bool honourHiDPI = true); /** @@ -299,6 +307,13 @@ class FrameBuffer bool hidpiEnabled() const { return myHiDPIEnabled; } uInt32 hidpiScaleFactor() const { return myHiDPIEnabled ? 2 : 1; } + /** + These methods are used to load/save position and display of the current window. + */ + string getPositionKey(); + string getDisplayKey(); + void saveCurrentWindowPosition(); + #ifdef GUI_SUPPORT /** Get the font object(s) of the framebuffer @@ -371,6 +386,21 @@ class FrameBuffer */ virtual void readPixels(uInt8* buffer, uInt32 pitch, const Common::Rect& rect) const = 0; + /** + This method is called to query if the current window is not centered or fullscreen. + + @return True, if the current window is positioned + */ + virtual bool isCurrentWindowPositioned() const = 0; + + /** + This method is called to query the video hardware for position of + the current window + + @return The position of the currently displayed window + */ + virtual Common::Point getCurrentWindowPos() const = 0; + /** This method is called to query the video hardware for the index of the display the current window is displayed on @@ -378,12 +408,7 @@ class FrameBuffer @return the current display index or a negative value if no window is displayed */ - virtual Int32 getCurrentDisplayIndex() = 0; - - /** - This method is called to preserve the last current windowed position. - */ - virtual void updateWindowedPos() = 0; + virtual Int32 getCurrentDisplayIndex() const = 0; /** Clear the framebuffer. @@ -554,6 +579,9 @@ class FrameBuffer // Title of the main window/screen string myScreenTitle; + // Type of the frame buffer + BufferType myBufferType{BufferType::None}; + // Number of displays int myNumDisplays{1}; diff --git a/src/emucore/OSystem.cxx b/src/emucore/OSystem.cxx index bbaa72aa5..ccf6838cb 100644 --- a/src/emucore/OSystem.cxx +++ b/src/emucore/OSystem.cxx @@ -239,8 +239,7 @@ void OSystem::saveConfig() if(myFrameBuffer) { // Save the last windowed position and display on system shutdown - myFrameBuffer->updateWindowedPos(); - settings().setValue("display", myFrameBuffer->getCurrentDisplayIndex()); + myFrameBuffer->saveCurrentWindowPosition(); Logger::debug("Saving TV effects options ..."); myFrameBuffer->tiaSurface().ntsc().saveConfig(settings()); diff --git a/src/emucore/Settings.cxx b/src/emucore/Settings.cxx index 599c8badf..b0e5cd7a6 100644 --- a/src/emucore/Settings.cxx +++ b/src/emucore/Settings.cxx @@ -126,6 +126,8 @@ Settings::Settings() // ROM browser options setPermanent("exitlauncher", "false"); setPermanent("followlauncher", "false"); + setPermanent("launcherpos", Common::Point(50, 50)); + setPermanent("launcherdisplay", 0); setPermanent("launcherres", Common::Size(900, 600)); setPermanent("launcherfont", "medium"); setPermanent("launcherroms", "true"); @@ -137,6 +139,8 @@ Settings::Settings() setPermanent("dbg.res", Common::Size(DebuggerDialog::kMediumFontMinW, DebuggerDialog::kMediumFontMinH)); + setPermanent("dbg.pos", Common::Point(50, 50)); + setPermanent("dbg.display", 0); #endif setPermanent("uipalette", "standard"); setPermanent("hidpi", "false"); @@ -396,8 +400,8 @@ void Settings::usage() const << " -vsync <1|0> Enable 'synchronize to vertical blank interrupt'\n" << " -fullscreen <1|0> Enable fullscreen mode\n" << " -center <1|0> Centers game window in windowed modes\n" - << " -windowedpos Sets the window position in windowed modes\n" - << " -display Sets the display for Stella\n" + << " -windowedpos Sets the window position in windowed emulator mode\n" + << " -display Sets the display for Stella's emulator\n" << " -palette \n" @@ -493,8 +497,10 @@ void Settings::usage() const << " -rominfo Display detailed information for the given ROM\n" << " -listrominfo Display contents of stella.pro, one line per ROM\n" << " entry\n" - << endl + << endl << " -exitlauncher <1|0> On exiting a ROM, go back to the ROM launcher\n" + << " -launcherpos Sets the window position in windowed EOM launcher mode\n" + << " -launcherdisplay Sets the display for the ROM launcher\n" << " -launcherres The resolution to use in ROM launcher mode\n" << " -launcherfont Relocate calls out of address range in\n" << " disassembler\n" << endl + << " -dbg.pos Sets the window position in windowed debugger mode\n" + << " -dbg.display Sets the display for the debugger\n" << " -dbg.res The resolution to use in debugger mode\n" << " -dbg.fontsize \n" diff --git a/src/emucore/module.mk b/src/emucore/module.mk index e02fcdc2d..e0dd4f96f 100644 --- a/src/emucore/module.mk +++ b/src/emucore/module.mk @@ -6,10 +6,12 @@ MODULE_OBJS := \ src/emucore/Booster.o \ src/emucore/Cart.o \ src/emucore/CartDetector.o \ + src/emucore/CartEnhanced.o \ src/emucore/Cart0840.o \ src/emucore/Cart2K.o \ src/emucore/Cart3E.o \ src/emucore/Cart3EPlus.o \ + src/emucore/Cart3EX.o \ src/emucore/Cart3F.o \ src/emucore/Cart4A50.o \ src/emucore/Cart4K.o \ @@ -20,8 +22,6 @@ MODULE_OBJS := \ src/emucore/CartCM.o \ src/emucore/CartCTY.o \ src/emucore/CartCV.o \ - src/emucore/CartCVPlus.o \ - src/emucore/CartDASH.o \ src/emucore/CartDPC.o \ src/emucore/CartDPCPlus.o \ src/emucore/CartE0.o \ diff --git a/src/emucore/stella.pro b/src/emucore/stella.pro index 6a7944a91..8b783bba4 100644 --- a/src/emucore/stella.pro +++ b/src/emucore/stella.pro @@ -1900,6 +1900,11 @@ "Cart.Name" "JtzBall (Alpha) (TJ)" "" +"Cart.MD5" "16229d61d7b0c89b01853660a8da22bb" +"Cart.Name" "spin4a50" +"Cart.Type" "4A50" +"" + "Cart.MD5" "163e7e757e2dc44469123ff0e5daec5e" "Cart.Name" "Many Blue Bars and Text Demo 2 (PD)" "" @@ -6131,6 +6136,13 @@ "Cart.Name" "Pitfall II (1983) (Activision) [a]" "" +"Cart.MD5" "49366f41aa7a54baf263426e99ce4312" +"Cart.Name" "POP-MDM-Test (PAL) (63 games)" +"Cart.Type" "MDM" +"Controller.Left" "JOYSTICK" +"Controller.Right" "JOYSTICK" +"" + "Cart.MD5" "493daaf9fb1ba450eba6b8ed53ffb37d" "Cart.Name" "3-D Corridor Demo (27-03-2003) (MP)" "" @@ -8158,6 +8170,13 @@ "Cart.Name" "Time Warp (Unknown)" "" +"Cart.MD5" "61baadddc2c8f6e5faa57d4d0f285462" +"Cart.Name" "208-in-1 MDM-Test (PAL) (127 games)" +"Cart.Type" "MDM" +"Controller.Left" "JOYSTICK" +"Controller.Right" "JOYSTICK" +"" + "Cart.MD5" "61dbe94f110f30ca4ec524ae5ce2d026" "Cart.Manufacturer" "CCE" "Cart.ModelNo" "C-820" @@ -13973,6 +13992,12 @@ "Cart.Name" "Boxing (1983) (CCE)" "" +"Cart.MD5" "a8c447efbec3a2b5d08b05a09999bd92" +"Cart.Name" "MegaCart Menu" +"Controller.Left" "JOYSTICK" +"Controller.Right" "JOYSTICK" +"" + "Cart.MD5" "a8c48b4e0bf35fe97cc84fdd2c507f78" "Cart.Manufacturer" "Puzzy - Bit Corporation" "Cart.ModelNo" "PG201" diff --git a/src/gui/AboutDialog.cxx b/src/gui/AboutDialog.cxx index 05db4b695..8393e2009 100644 --- a/src/gui/AboutDialog.cxx +++ b/src/gui/AboutDialog.cxx @@ -34,7 +34,6 @@ AboutDialog::AboutDialog(OSystem& osystem, DialogContainer& parent, buttonHeight = font.getLineHeight() * 1.25; const int VBORDER = fontHeight / 2; const int HBORDER = fontWidth * 1.25; - const int INDENT = fontWidth * 2; const int VGAP = fontHeight / 4; int xpos, ypos; diff --git a/src/gui/ComboDialog.cxx b/src/gui/ComboDialog.cxx index 524a1a3e2..e8b087c53 100644 --- a/src/gui/ComboDialog.cxx +++ b/src/gui/ComboDialog.cxx @@ -37,7 +37,6 @@ ComboDialog::ComboDialog(GuiObject* boss, const GUI::Font& font, buttonHeight = font.getLineHeight() * 1.25; const int VBORDER = fontHeight / 2; const int HBORDER = fontWidth * 1.25; - const int INDENT = fontWidth * 2; const int VGAP = fontHeight / 4; int xpos, ypos; diff --git a/src/gui/CommandDialog.cxx b/src/gui/CommandDialog.cxx index 86fc6f8c9..934b9e645 100644 --- a/src/gui/CommandDialog.cxx +++ b/src/gui/CommandDialog.cxx @@ -33,8 +33,7 @@ CommandDialog::CommandDialog(OSystem& osystem, DialogContainer& parent) : Dialog(osystem, parent, osystem.frameBuffer().font(), "Commands") { - const int lineHeight = _font.getLineHeight(), - fontHeight = _font.getFontHeight(), + const int fontHeight = _font.getFontHeight(), fontWidth = _font.getMaxCharWidth(), buttonHeight = _font.getLineHeight() * 1.25, buttonWidth = _font.getStringWidth("Time Machine On") + fontWidth * 2; diff --git a/src/gui/DeveloperDialog.cxx b/src/gui/DeveloperDialog.cxx index 0bcbe32e6..2dd607a4b 100644 --- a/src/gui/DeveloperDialog.cxx +++ b/src/gui/DeveloperDialog.cxx @@ -54,7 +54,6 @@ DeveloperDialog::DeveloperDialog(OSystem& osystem, DialogContainer& parent, buttonHeight = font.getLineHeight() * 1.25; const int VBORDER = fontHeight / 2; const int HBORDER = fontWidth * 1.25; - const int INDENT = fontWidth * 2; const int VGAP = fontHeight / 4; int xpos, ypos; @@ -90,8 +89,7 @@ void DeveloperDialog::addEmulationTab(const GUI::Font& font) { const int lineHeight = font.getLineHeight(), fontHeight = font.getFontHeight(), - fontWidth = font.getMaxCharWidth(), - buttonHeight = font.getLineHeight() * 1.25; + fontWidth = font.getMaxCharWidth(); const int VBORDER = fontHeight / 2; const int HBORDER = fontWidth * 1.25; const int INDENT = fontWidth * 2; @@ -197,8 +195,7 @@ void DeveloperDialog::addTiaTab(const GUI::Font& font) { const int lineHeight = font.getLineHeight(), fontHeight = font.getFontHeight(), - fontWidth = font.getMaxCharWidth(), - buttonHeight = font.getLineHeight() * 1.25; + fontWidth = font.getMaxCharWidth(); const int VBORDER = fontHeight / 2; const int HBORDER = fontWidth * 1.25; const int INDENT = fontWidth * 2; @@ -287,8 +284,7 @@ void DeveloperDialog::addVideoTab(const GUI::Font& font) { const int lineHeight = font.getLineHeight(), fontHeight = font.getFontHeight(), - fontWidth = font.getMaxCharWidth(), - buttonHeight = font.getLineHeight() * 1.25; + fontWidth = font.getMaxCharWidth(); const int VBORDER = fontHeight / 2; const int HBORDER = fontWidth * 1.25; const int INDENT = fontWidth * 2; @@ -428,8 +424,7 @@ void DeveloperDialog::addTimeMachineTab(const GUI::Font& font) }; const int lineHeight = font.getLineHeight(), fontHeight = font.getFontHeight(), - fontWidth = font.getMaxCharWidth(), - buttonHeight = font.getLineHeight() * 1.25; + fontWidth = font.getMaxCharWidth(); const int VBORDER = fontHeight / 2; const int HBORDER = fontWidth * 1.25; const int INDENT = fontWidth * 2; @@ -548,11 +543,9 @@ void DeveloperDialog::addDebuggerTab(const GUI::Font& font) #ifdef DEBUGGER_SUPPORT const int lineHeight = font.getLineHeight(), fontHeight = font.getFontHeight(), - fontWidth = font.getMaxCharWidth(), - buttonHeight = font.getLineHeight() * 1.25; + fontWidth = font.getMaxCharWidth(); const int VBORDER = fontHeight / 2; const int HBORDER = fontWidth * 1.25; - const int INDENT = fontWidth * 2; const int VGAP = fontHeight / 4; VariantList items; diff --git a/src/gui/Dialog.cxx b/src/gui/Dialog.cxx index 0195fa8f9..1cc0b3b8c 100644 --- a/src/gui/Dialog.cxx +++ b/src/gui/Dialog.cxx @@ -772,8 +772,7 @@ void Dialog::addOKCancelBGroup(WidgetArray& wid, const GUI::Font& font, const string& okText, const string& cancelText, bool focusOKButton, int buttonWidth) { - const int lineHeight = font.getLineHeight(), - fontWidth = font.getMaxCharWidth(), + const int fontWidth = font.getMaxCharWidth(), fontHeight = font.getFontHeight(), buttonHeight = font.getLineHeight() * 1.25; const int VBORDER = fontHeight / 2; @@ -821,8 +820,7 @@ void Dialog::addDefaultsOKCancelBGroup(WidgetArray& wid, const GUI::Font& font, const string& defaultsText, bool focusOKButton) { - const int lineHeight = font.getLineHeight(), - fontWidth = font.getMaxCharWidth(), + const int fontWidth = font.getMaxCharWidth(), fontHeight = font.getFontHeight(), buttonHeight = font.getLineHeight() * 1.25; const int VBORDER = fontHeight / 2; diff --git a/src/gui/EventMappingWidget.cxx b/src/gui/EventMappingWidget.cxx index bb4088173..8051e4a56 100644 --- a/src/gui/EventMappingWidget.cxx +++ b/src/gui/EventMappingWidget.cxx @@ -86,7 +86,7 @@ EventMappingWidget::EventMappingWidget(GuiObject* boss, const GUI::Font& font, addFocusWidget(myActionsList); // Add remap, erase, cancel and default buttons - xpos = _w - HBORDER - buttonWidth; + xpos = _w - HBORDER - buttonWidth + 2; myMapButton = new ButtonWidget(boss, font, xpos, ypos, buttonWidth, buttonHeight, "Map" + ELLIPSIS, kStartMapCmd); diff --git a/src/gui/GameInfoDialog.cxx b/src/gui/GameInfoDialog.cxx index cfa50e193..fd646c6ed 100644 --- a/src/gui/GameInfoDialog.cxx +++ b/src/gui/GameInfoDialog.cxx @@ -448,7 +448,7 @@ void GameInfoDialog::loadEmulationProperties(const Properties& props) VarList::push_back(items, "Auto", "AUTO"); if(instance().hasConsole()) { - uInt16 numBanks = instance().console().cartridge().bankCount(); + uInt16 numBanks = instance().console().cartridge().romBankCount(); for(uInt16 i = 0; i < numBanks; ++i) VarList::push_back(items, i, i); diff --git a/src/gui/GlobalPropsDialog.cxx b/src/gui/GlobalPropsDialog.cxx index d8226a2d5..232968f1c 100644 --- a/src/gui/GlobalPropsDialog.cxx +++ b/src/gui/GlobalPropsDialog.cxx @@ -39,7 +39,6 @@ GlobalPropsDialog::GlobalPropsDialog(GuiObject* boss, const GUI::Font& font) buttonHeight = lineHeight * 1.25; const int VBORDER = fontHeight / 2; const int HBORDER = fontWidth * 1.25; - const int INDENT = fontWidth * 2; const int VGAP = fontHeight / 4; int xpos, ypos; diff --git a/src/gui/HelpDialog.cxx b/src/gui/HelpDialog.cxx index 9a0bd2bdd..5c9a6b1d3 100644 --- a/src/gui/HelpDialog.cxx +++ b/src/gui/HelpDialog.cxx @@ -35,7 +35,6 @@ HelpDialog::HelpDialog(OSystem& osystem, DialogContainer& parent, buttonHeight = font.getLineHeight() * 1.25; const int VBORDER = fontHeight / 2; const int HBORDER = fontWidth * 1.25; - const int INDENT = fontWidth * 2; const int VGAP = fontHeight / 4; int xpos, ypos; @@ -73,7 +72,7 @@ HelpDialog::HelpDialog(OSystem& osystem, DialogContainer& parent, int lwidth = 15 * fontWidth; ypos += lineHeight + VGAP * 2; - for(int i = 0; i < LINES_PER_PAGE; ++i) + for(uInt32 i = 0; i < LINES_PER_PAGE; ++i) { myKey[i] = new StaticTextWidget(this, font, xpos, ypos, lwidth, diff --git a/src/gui/InputDialog.cxx b/src/gui/InputDialog.cxx index bfa914c63..03cedb644 100644 --- a/src/gui/InputDialog.cxx +++ b/src/gui/InputDialog.cxx @@ -54,7 +54,7 @@ InputDialog::InputDialog(OSystem& osystem, DialogContainer& parent, // Set real dimensions setSize(50 * fontWidth + HBORDER * 2, - _th + VGAP * 3 + lineHeight + 13 * (lineHeight + VGAP) + VGAP * 7 + buttonHeight + VBORDER * 3, + _th + VGAP * 3 + lineHeight + 13 * (lineHeight + VGAP) + VGAP * 8 + buttonHeight + VBORDER * 3, max_w, max_h); // The tab widget @@ -68,7 +68,7 @@ InputDialog::InputDialog(OSystem& osystem, DialogContainer& parent, tabID = myTab->addTab(" Emul. Events ", TabWidget::AUTO_WIDTH); myEmulEventMapper = new EventMappingWidget(myTab, _font, 2, 2, myTab->getWidth(), - myTab->getHeight() - 4, + myTab->getHeight() - VGAP, EventMode::kEmulationMode); myTab->setParentWidget(tabID, myEmulEventMapper); addToFocusList(myEmulEventMapper->getFocusList(), myTab, tabID); @@ -77,7 +77,7 @@ InputDialog::InputDialog(OSystem& osystem, DialogContainer& parent, tabID = myTab->addTab(" UI Events ", TabWidget::AUTO_WIDTH); myMenuEventMapper = new EventMappingWidget(myTab, _font, 2, 2, myTab->getWidth(), - myTab->getHeight() - 4, + myTab->getHeight() - VGAP, EventMode::kMenuMode); myTab->setParentWidget(tabID, myMenuEventMapper); addToFocusList(myMenuEventMapper->getFocusList(), myTab, tabID); @@ -108,7 +108,8 @@ void InputDialog::addDevicePortTab() { const int lineHeight = _font.getLineHeight(), fontWidth = _font.getMaxCharWidth(), - fontHeight = _font.getFontHeight(); + fontHeight = _font.getFontHeight(), + buttonHeight = _font.getLineHeight() * 1.25; const int VGAP = fontHeight / 4; const int VBORDER = fontHeight / 2; const int HBORDER = fontWidth * 1.25; @@ -213,26 +214,26 @@ void InputDialog::addDevicePortTab() // Add EEPROM erase (part 1/2) ypos += VGAP * 3; fwidth = _font.getStringWidth("AtariVox/SaveKey"); - lwidth = _font.getStringWidth("AtariVox/SaveKey"); - new StaticTextWidget(myTab, _font, _w - HBORDER - 4 - (fwidth + lwidth) / 2, ypos, + new StaticTextWidget(myTab, _font, _w - HBORDER - 2 - fwidth, ypos, "AtariVox/SaveKey"); // Show joystick database ypos += lineHeight; - myJoyDlgButton = new ButtonWidget(myTab, _font, HBORDER, ypos, 20, + lwidth = _font.getStringWidth("Joystick Database" + ELLIPSIS) + fontWidth * 2.5; + myJoyDlgButton = new ButtonWidget(myTab, _font, HBORDER, ypos, lwidth, buttonHeight, "Joystick Database" + ELLIPSIS, kDBButtonPressed); wid.push_back(myJoyDlgButton); // Add EEPROM erase (part 1/2) - myEraseEEPROMButton = new ButtonWidget(myTab, _font, _w - HBORDER - 4 - fwidth, ypos, - fwidth, lineHeight+4, + myEraseEEPROMButton = new ButtonWidget(myTab, _font, _w - HBORDER - 2 - fwidth, ypos, + fwidth, buttonHeight, "Erase EEPROM", kEEButtonPressed); wid.push_back(myEraseEEPROMButton); // Add AtariVox serial port ypos += lineHeight + VGAP * 2; lwidth = _font.getStringWidth("AVox serial port "); - fwidth = _w - HBORDER * 2 - 4 - lwidth; + fwidth = _w - HBORDER * 2 - 2 - lwidth; new StaticTextWidget(myTab, _font, HBORDER, ypos + 2, "AVox serial port "); myAVoxPort = new EditTextWidget(myTab, _font, HBORDER + lwidth, ypos, fwidth, fontHeight); diff --git a/src/gui/JoystickDialog.cxx b/src/gui/JoystickDialog.cxx index 266822eda..218179e72 100644 --- a/src/gui/JoystickDialog.cxx +++ b/src/gui/JoystickDialog.cxx @@ -39,8 +39,6 @@ JoystickDialog::JoystickDialog(GuiObject* boss, const GUI::Font& font, buttonHeight = font.getLineHeight() * 1.25; const int VBORDER = fontHeight / 2; const int HBORDER = fontWidth * 1.25; - const int INDENT = fontWidth * 2; - const int VGAP = fontHeight / 4; // Joystick list xpos = HBORDER; ypos = VBORDER + _th; diff --git a/src/gui/Launcher.cxx b/src/gui/Launcher.cxx index 379d9841e..315a14f41 100644 --- a/src/gui/Launcher.cxx +++ b/src/gui/Launcher.cxx @@ -59,7 +59,8 @@ Launcher::~Launcher() FBInitStatus Launcher::initializeVideo() { string title = string("Stella ") + STELLA_VERSION; - return myOSystem.frameBuffer().createDisplay(title, myWidth, myHeight); + return myOSystem.frameBuffer().createDisplay(title, FrameBuffer::BufferType::Launcher, + myWidth, myHeight); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/gui/LoggerDialog.cxx b/src/gui/LoggerDialog.cxx index 57a0161fc..f9b05911b 100644 --- a/src/gui/LoggerDialog.cxx +++ b/src/gui/LoggerDialog.cxx @@ -43,7 +43,6 @@ LoggerDialog::LoggerDialog(OSystem& osystem, DialogContainer& parent, buttonHeight = font.getLineHeight() * 1.25; const int VBORDER = fontHeight / 2; const int HBORDER = fontWidth * 1.25; - const int INDENT = fontWidth * 2; const int VGAP = fontHeight / 4; int xpos, ypos; diff --git a/src/gui/OptionsDialog.cxx b/src/gui/OptionsDialog.cxx index 9b8087b34..84ea9f605 100644 --- a/src/gui/OptionsDialog.cxx +++ b/src/gui/OptionsDialog.cxx @@ -54,7 +54,7 @@ OptionsDialog::OptionsDialog(OSystem& osystem, DialogContainer& parent, { // do not show basic settings options in debugger bool minSettings = osystem.settings().getBool("minimal_ui") && mode != Menu::AppMode::debugger; - const int lineHeight = _font.getLineHeight(), + const int fontWidth = _font.getMaxCharWidth(), fontHeight = _font.getFontHeight(), buttonHeight = _font.getLineHeight() * 1.25, diff --git a/src/gui/RomAuditDialog.cxx b/src/gui/RomAuditDialog.cxx index 580263730..890da7e95 100644 --- a/src/gui/RomAuditDialog.cxx +++ b/src/gui/RomAuditDialog.cxx @@ -48,8 +48,6 @@ RomAuditDialog::RomAuditDialog(OSystem& osystem, DialogContainer& parent, lwidth = font.getStringWidth("ROMs without properties (skipped) "); const int VBORDER = _th + fontHeight / 2; const int HBORDER = fontWidth * 1.25; - const int INDENT = fontWidth * 2; - const int VGAP = fontHeight / 4; int xpos, ypos = VBORDER; WidgetArray wid; diff --git a/src/gui/RomInfoWidget.cxx b/src/gui/RomInfoWidget.cxx index c935f54dc..8b1852528 100644 --- a/src/gui/RomInfoWidget.cxx +++ b/src/gui/RomInfoWidget.cxx @@ -205,7 +205,7 @@ void RomInfoWidget::drawWidget(bool hilite) int xpos = _x + 8, ypos = _y + yoff + 5; for(const auto& info : myRomInfo) { - if(info.length() * _font.getMaxCharWidth() <= _w - 16) + if(info.length() * _font.getMaxCharWidth() <= uInt64(_w - 16)) { // 1 line for next entry diff --git a/src/libretro/FrameBufferLIBRETRO.hxx b/src/libretro/FrameBufferLIBRETRO.hxx index 6b2035397..4ac6dbb30 100644 --- a/src/libretro/FrameBufferLIBRETRO.hxx +++ b/src/libretro/FrameBufferLIBRETRO.hxx @@ -115,6 +115,21 @@ class FrameBufferLIBRETRO : public FrameBuffer vector& windowedRes, VariantList& renderers) override; + /** + This method is called to query if the current window is not centered or fullscreen. + + @return True, if the current window is positioned + */ + bool isCurrentWindowPositioned() const override { return true; } + + /** + This method is called to query the video hardware for position of + the current window + + @return The position of the currently displayed window + */ + Common::Point getCurrentWindowPos() const override { return Common::Point{}; } + /** This method is called to query the video hardware for the index of the display the current window is displayed on @@ -122,12 +137,7 @@ class FrameBufferLIBRETRO : public FrameBuffer @return the current display index or a negative value if no window is displayed */ - Int32 getCurrentDisplayIndex() override { return 0; } - - /** - This method is called to preserve the last current windowed position. - */ - void updateWindowedPos() override { } + Int32 getCurrentDisplayIndex() const override { return 0; } /** This method is called to change to the given video mode. diff --git a/src/libretro/Makefile.common b/src/libretro/Makefile.common index 34268a29d..1893fa57b 100644 --- a/src/libretro/Makefile.common +++ b/src/libretro/Makefile.common @@ -37,10 +37,14 @@ SOURCES_CXX := \ $(CORE_DIR)/emucore/AtariVox.cxx \ $(CORE_DIR)/emucore/Bankswitch.cxx \ $(CORE_DIR)/emucore/Booster.cxx \ + $(CORE_DIR)/emucore/Cart.cxx \ + $(CORE_DIR)/emucore/CartDetector.cxx \ + $(CORE_DIR)/emucore/CartEnhanced.cxx \ $(CORE_DIR)/emucore/Cart0840.cxx \ $(CORE_DIR)/emucore/Cart2K.cxx \ $(CORE_DIR)/emucore/Cart3E.cxx \ $(CORE_DIR)/emucore/Cart3EPlus.cxx \ + $(CORE_DIR)/emucore/Cart3EX.cxx \ $(CORE_DIR)/emucore/Cart3F.cxx \ $(CORE_DIR)/emucore/Cart4A50.cxx \ $(CORE_DIR)/emucore/Cart4K.cxx \ @@ -53,10 +57,6 @@ SOURCES_CXX := \ $(CORE_DIR)/emucore/CartCM.cxx \ $(CORE_DIR)/emucore/CartCTY.cxx \ $(CORE_DIR)/emucore/CartCV.cxx \ - $(CORE_DIR)/emucore/CartCVPlus.cxx \ - $(CORE_DIR)/emucore/Cart.cxx \ - $(CORE_DIR)/emucore/CartDASH.cxx \ - $(CORE_DIR)/emucore/CartDetector.cxx \ $(CORE_DIR)/emucore/CartDF.cxx \ $(CORE_DIR)/emucore/CartDFSC.cxx \ $(CORE_DIR)/emucore/CartDPC.cxx \ diff --git a/src/windows/Stella.vcxproj b/src/windows/Stella.vcxproj index 9aa49cb84..1095f2c09 100644 --- a/src/windows/Stella.vcxproj +++ b/src/windows/Stella.vcxproj @@ -585,15 +585,9 @@ true - - true - true - - true - true @@ -624,6 +618,7 @@ true + true @@ -716,15 +711,15 @@ + + - - @@ -1595,15 +1590,9 @@ true - - true - true - - true - true @@ -1634,6 +1623,7 @@ true + true @@ -1738,15 +1728,15 @@ + + - - diff --git a/src/windows/Stella.vcxproj.filters b/src/windows/Stella.vcxproj.filters index 4a2fe6691..fb763da4c 100644 --- a/src/windows/Stella.vcxproj.filters +++ b/src/windows/Stella.vcxproj.filters @@ -723,9 +723,6 @@ Source Files\emucore - - Source Files\emucore - Source Files\debugger @@ -735,9 +732,6 @@ Source Files\debugger - - Source Files\debugger - Source Files\emucore @@ -759,12 +753,6 @@ Source Files\debugger - - Source Files\emucore - - - Source Files\debugger - Source Files\debugger @@ -1005,6 +993,15 @@ Source Files\gui + + Source Files\emucore + + + Source Files\debugger + + + Source Files\emucore + @@ -1706,9 +1703,6 @@ Header Files\emucore - - Header Files\emucore - Header Files\debugger @@ -1718,9 +1712,6 @@ Header Files\debugger - - Header Files\debugger - Header Files\emucore @@ -1745,12 +1736,6 @@ Header Files\debugger - - Header Files\debugger - - - Header Files\emucore - Header Files\debugger @@ -2063,6 +2048,15 @@ Header Files\gui + + Header Files\emucore + + + Header Files\debugger + + + Header Files\emucore + diff --git a/src/yacc/YaccParser.cxx b/src/yacc/YaccParser.cxx index 82163c433..8bc728ca0 100644 --- a/src/yacc/YaccParser.cxx +++ b/src/yacc/YaccParser.cxx @@ -234,7 +234,9 @@ TiaMethod getTiaSpecial(char* ch) { if(BSPF::equalsIgnoreCase(ch, "_scan")) return &TIADebug::scanlines; - if(BSPF::equalsIgnoreCase(ch, "_scycles")) + else if(BSPF::equalsIgnoreCase(ch, "_scanend")) + return &TIADebug::scanlinesLastFrame; + else if(BSPF::equalsIgnoreCase(ch, "_scycles")) return &TIADebug::cyclesThisLine; else if(BSPF::equalsIgnoreCase(ch, "_fcount")) return &TIADebug::frameCount; diff --git a/test/roms/bankswitching/0840/0840_EconoBanking.bin b/test/roms/bankswitching/0840/0840_EconoBanking.bin new file mode 100644 index 000000000..45dbbb187 Binary files /dev/null and b/test/roms/bankswitching/0840/0840_EconoBanking.bin differ diff --git a/test/roms/bankswitching/0840/Toyshop Trouble (0840) (2008) (John Payson, Zach Matley, Bob Montgomery, Thomas Jentzsch, Nathan Strum).bin b/test/roms/bankswitching/0840/Toyshop Trouble (0840) (2008) (John Payson, Zach Matley, Bob Montgomery, Thomas Jentzsch, Nathan Strum).bin new file mode 100644 index 000000000..4c4751700 Binary files /dev/null and b/test/roms/bankswitching/0840/Toyshop Trouble (0840) (2008) (John Payson, Zach Matley, Bob Montgomery, Thomas Jentzsch, Nathan Strum).bin differ diff --git a/test/roms/bankswitching/2K/2-in-1 - Freeway and Tennis [p1].a26 b/test/roms/bankswitching/2K/2-in-1 - Freeway and Tennis [p1].a26 new file mode 100644 index 000000000..5d02224be Binary files /dev/null and b/test/roms/bankswitching/2K/2-in-1 - Freeway and Tennis [p1].a26 differ diff --git a/test/roms/bankswitching/2K/Air-Sea Battle (1977) (Atari) (PAL) [!].a26 b/test/roms/bankswitching/2K/Air-Sea Battle (1977) (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..874df52d7 Binary files /dev/null and b/test/roms/bankswitching/2K/Air-Sea Battle (1977) (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/2K/Air-Sea Battle (1977) (Atari) (PAL) [p1][!].a26 b/test/roms/bankswitching/2K/Air-Sea Battle (1977) (Atari) (PAL) [p1][!].a26 new file mode 100644 index 000000000..20278eb6c Binary files /dev/null and b/test/roms/bankswitching/2K/Air-Sea Battle (1977) (Atari) (PAL) [p1][!].a26 differ diff --git a/test/roms/bankswitching/2K/Air-Sea Battle (1977) (Atari) [!].a26 b/test/roms/bankswitching/2K/Air-Sea Battle (1977) (Atari) [!].a26 new file mode 100644 index 000000000..96465e288 Binary files /dev/null and b/test/roms/bankswitching/2K/Air-Sea Battle (1977) (Atari) [!].a26 differ diff --git a/test/roms/bankswitching/2K/Air-Sea Battle (1977) (Atari) [o1].a26 b/test/roms/bankswitching/2K/Air-Sea Battle (1977) (Atari) [o1].a26 new file mode 100644 index 000000000..f0323e474 Binary files /dev/null and b/test/roms/bankswitching/2K/Air-Sea Battle (1977) (Atari) [o1].a26 differ diff --git a/test/roms/bankswitching/2K/Air-Sea Battle (1977) (Atari) [o1][h1].a26 b/test/roms/bankswitching/2K/Air-Sea Battle (1977) (Atari) [o1][h1].a26 new file mode 100644 index 000000000..309430160 Binary files /dev/null and b/test/roms/bankswitching/2K/Air-Sea Battle (1977) (Atari) [o1][h1].a26 differ diff --git a/test/roms/bankswitching/2K/Air-Sea Battle (32-in-1) (Atari) (PAL) [!].a26 b/test/roms/bankswitching/2K/Air-Sea Battle (32-in-1) (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..1d7018021 Binary files /dev/null and b/test/roms/bankswitching/2K/Air-Sea Battle (32-in-1) (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/2K/Combat (1977) (Atari) [!].a26 b/test/roms/bankswitching/2K/Combat (1977) (Atari) [!].a26 new file mode 100644 index 000000000..5c52aef01 Binary files /dev/null and b/test/roms/bankswitching/2K/Combat (1977) (Atari) [!].a26 differ diff --git a/test/roms/bankswitching/2K/Combat (32-in-1) (Atari) (PAL) [!].a26 b/test/roms/bankswitching/2K/Combat (32-in-1) (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..a71ae9a42 Binary files /dev/null and b/test/roms/bankswitching/2K/Combat (32-in-1) (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/2K/Combat (Atari) (PAL) [!].a26 b/test/roms/bankswitching/2K/Combat (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..18646de33 Binary files /dev/null and b/test/roms/bankswitching/2K/Combat (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/2K/Combat - Tank AI (19-04-2003) (Zach Matley).a26 b/test/roms/bankswitching/2K/Combat - Tank AI (19-04-2003) (Zach Matley).a26 new file mode 100644 index 000000000..c53da0041 Binary files /dev/null and b/test/roms/bankswitching/2K/Combat - Tank AI (19-04-2003) (Zach Matley).a26 differ diff --git a/test/roms/bankswitching/2K/Combat AI (16-02-2003) (Zach Matley).a26 b/test/roms/bankswitching/2K/Combat AI (16-02-2003) (Zach Matley).a26 new file mode 100644 index 000000000..4a6741bae Binary files /dev/null and b/test/roms/bankswitching/2K/Combat AI (16-02-2003) (Zach Matley).a26 differ diff --git a/test/roms/bankswitching/2K/Combat Rock (PD) [a1].a26 b/test/roms/bankswitching/2K/Combat Rock (PD) [a1].a26 new file mode 100644 index 000000000..b81122971 Binary files /dev/null and b/test/roms/bankswitching/2K/Combat Rock (PD) [a1].a26 differ diff --git a/test/roms/bankswitching/2K/Combat Rock (PD).a26 b/test/roms/bankswitching/2K/Combat Rock (PD).a26 new file mode 100644 index 000000000..847d99835 Binary files /dev/null and b/test/roms/bankswitching/2K/Combat Rock (PD).a26 differ diff --git a/test/roms/bankswitching/2K/Combat TC (v0.1).a26 b/test/roms/bankswitching/2K/Combat TC (v0.1).a26 new file mode 100644 index 000000000..cc76498b6 Binary files /dev/null and b/test/roms/bankswitching/2K/Combat TC (v0.1).a26 differ diff --git a/test/roms/bankswitching/2K/Freeway (1981) (Activision) (PAL) [!].a26 b/test/roms/bankswitching/2K/Freeway (1981) (Activision) (PAL) [!].a26 new file mode 100644 index 000000000..dbafe87b5 Binary files /dev/null and b/test/roms/bankswitching/2K/Freeway (1981) (Activision) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/2K/Freeway (1981) (Activision) [!].a26 b/test/roms/bankswitching/2K/Freeway (1981) (Activision) [!].a26 new file mode 100644 index 000000000..895e05760 Binary files /dev/null and b/test/roms/bankswitching/2K/Freeway (1981) (Activision) [!].a26 differ diff --git a/test/roms/bankswitching/2K/Freeway (1981) (Activision) [o1].a26 b/test/roms/bankswitching/2K/Freeway (1981) (Activision) [o1].a26 new file mode 100644 index 000000000..51f0febc3 Binary files /dev/null and b/test/roms/bankswitching/2K/Freeway (1981) (Activision) [o1].a26 differ diff --git a/test/roms/bankswitching/2K/Freeway (1981) (Activision) [o2].a26 b/test/roms/bankswitching/2K/Freeway (1981) (Activision) [o2].a26 new file mode 100644 index 000000000..10e525cac Binary files /dev/null and b/test/roms/bankswitching/2K/Freeway (1981) (Activision) [o2].a26 differ diff --git a/test/roms/bankswitching/2K/Freeway (32-in-1) (Atari) (PAL) [!].a26 b/test/roms/bankswitching/2K/Freeway (32-in-1) (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..bda078131 Binary files /dev/null and b/test/roms/bankswitching/2K/Freeway (32-in-1) (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/2K/Freeway (AKA Rabbits) (PAL) [p1][!].a26 b/test/roms/bankswitching/2K/Freeway (AKA Rabbits) (PAL) [p1][!].a26 new file mode 100644 index 000000000..3ee95e9d9 Binary files /dev/null and b/test/roms/bankswitching/2K/Freeway (AKA Rabbits) (PAL) [p1][!].a26 differ diff --git a/test/roms/bankswitching/2K/Freeway (CCE).a26 b/test/roms/bankswitching/2K/Freeway (CCE).a26 new file mode 100644 index 000000000..738083931 Binary files /dev/null and b/test/roms/bankswitching/2K/Freeway (CCE).a26 differ diff --git a/test/roms/bankswitching/2K/Freeway (Dactar) (PAL) [p1][!].a26 b/test/roms/bankswitching/2K/Freeway (Dactar) (PAL) [p1][!].a26 new file mode 100644 index 000000000..4fd295f81 Binary files /dev/null and b/test/roms/bankswitching/2K/Freeway (Dactar) (PAL) [p1][!].a26 differ diff --git a/test/roms/bankswitching/2K/Kaboom! (1981) (Activision) (PAL) [!].a26 b/test/roms/bankswitching/2K/Kaboom! (1981) (Activision) (PAL) [!].a26 new file mode 100644 index 000000000..44fe84378 Binary files /dev/null and b/test/roms/bankswitching/2K/Kaboom! (1981) (Activision) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/2K/Kaboom! (1981) (Activision) [o1].a26 b/test/roms/bankswitching/2K/Kaboom! (1981) (Activision) [o1].a26 new file mode 100644 index 000000000..45ef3ba2b Binary files /dev/null and b/test/roms/bankswitching/2K/Kaboom! (1981) (Activision) [o1].a26 differ diff --git a/test/roms/bankswitching/2K/Kaboom! (1981) (Activision) [o2].a26 b/test/roms/bankswitching/2K/Kaboom! (1981) (Activision) [o2].a26 new file mode 100644 index 000000000..a6d0612f8 Binary files /dev/null and b/test/roms/bankswitching/2K/Kaboom! (1981) (Activision) [o2].a26 differ diff --git a/test/roms/bankswitching/2K/Kaboom! (CCE).a26 b/test/roms/bankswitching/2K/Kaboom! (CCE).a26 new file mode 100644 index 000000000..4f18cb0bb Binary files /dev/null and b/test/roms/bankswitching/2K/Kaboom! (CCE).a26 differ diff --git a/test/roms/bankswitching/2K/Kabul! by Jess Ragan (Kaboom! Hack).a26 b/test/roms/bankswitching/2K/Kabul! by Jess Ragan (Kaboom! Hack).a26 new file mode 100644 index 000000000..72dcc7e52 Binary files /dev/null and b/test/roms/bankswitching/2K/Kabul! by Jess Ragan (Kaboom! Hack).a26 differ diff --git a/test/roms/bankswitching/2K/Okie Dokie (4K) (PD).a26 b/test/roms/bankswitching/2K/Okie Dokie (4K) (PD).a26 new file mode 100644 index 000000000..bb746eacb Binary files /dev/null and b/test/roms/bankswitching/2K/Okie Dokie (4K) (PD).a26 differ diff --git a/test/roms/bankswitching/2K/Okie Dokie (Older) (PD).a26 b/test/roms/bankswitching/2K/Okie Dokie (Older) (PD).a26 new file mode 100644 index 000000000..fc754ff18 Binary files /dev/null and b/test/roms/bankswitching/2K/Okie Dokie (Older) (PD).a26 differ diff --git a/test/roms/bankswitching/2K/Okie Dokie (PD).a26 b/test/roms/bankswitching/2K/Okie Dokie (PD).a26 new file mode 100644 index 000000000..c2bc1c036 Binary files /dev/null and b/test/roms/bankswitching/2K/Okie Dokie (PD).a26 differ diff --git a/test/roms/bankswitching/2K/Stampede (1981) (Activision) (PAL) [!].a26 b/test/roms/bankswitching/2K/Stampede (1981) (Activision) (PAL) [!].a26 new file mode 100644 index 000000000..8fb0c970f Binary files /dev/null and b/test/roms/bankswitching/2K/Stampede (1981) (Activision) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/2K/Stampede (1981) (Activision) (PAL) [p1][!].a26 b/test/roms/bankswitching/2K/Stampede (1981) (Activision) (PAL) [p1][!].a26 new file mode 100644 index 000000000..41b7523df Binary files /dev/null and b/test/roms/bankswitching/2K/Stampede (1981) (Activision) (PAL) [p1][!].a26 differ diff --git a/test/roms/bankswitching/2K/Stampede (1981) (Activision) (PAL) [p1][o1][!].a26 b/test/roms/bankswitching/2K/Stampede (1981) (Activision) (PAL) [p1][o1][!].a26 new file mode 100644 index 000000000..9e0d5f555 Binary files /dev/null and b/test/roms/bankswitching/2K/Stampede (1981) (Activision) (PAL) [p1][o1][!].a26 differ diff --git a/test/roms/bankswitching/2K/Stampede (1981) (Activision) [!].a26 b/test/roms/bankswitching/2K/Stampede (1981) (Activision) [!].a26 new file mode 100644 index 000000000..263b5922b Binary files /dev/null and b/test/roms/bankswitching/2K/Stampede (1981) (Activision) [!].a26 differ diff --git a/test/roms/bankswitching/2K/Stampede (1981) (Activision) [o1].a26 b/test/roms/bankswitching/2K/Stampede (1981) (Activision) [o1].a26 new file mode 100644 index 000000000..e79c73e08 Binary files /dev/null and b/test/roms/bankswitching/2K/Stampede (1981) (Activision) [o1].a26 differ diff --git a/test/roms/bankswitching/2K/Stampede (1981) (Activision) [o2].a26 b/test/roms/bankswitching/2K/Stampede (1981) (Activision) [o2].a26 new file mode 100644 index 000000000..521feb6b0 Binary files /dev/null and b/test/roms/bankswitching/2K/Stampede (1981) (Activision) [o2].a26 differ diff --git a/test/roms/bankswitching/2K/Stampede (32-in-1) (Atari) (PAL) [!].a26 b/test/roms/bankswitching/2K/Stampede (32-in-1) (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..c18e2c47f Binary files /dev/null and b/test/roms/bankswitching/2K/Stampede (32-in-1) (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/2K/Tank Plus (1977) (Sears) [a1].a26 b/test/roms/bankswitching/2K/Tank Plus (1977) (Sears) [a1].a26 new file mode 100644 index 000000000..86ffe6027 Binary files /dev/null and b/test/roms/bankswitching/2K/Tank Plus (1977) (Sears) [a1].a26 differ diff --git a/test/roms/bankswitching/2K/Tank Plus (1977) (Sears).a26 b/test/roms/bankswitching/2K/Tank Plus (1977) (Sears).a26 new file mode 100644 index 000000000..a2bde7013 Binary files /dev/null and b/test/roms/bankswitching/2K/Tank Plus (1977) (Sears).a26 differ diff --git a/test/roms/bankswitching/2K/Tennis (1981) (Activision) (PAL) [!].a26 b/test/roms/bankswitching/2K/Tennis (1981) (Activision) (PAL) [!].a26 new file mode 100644 index 000000000..8826ee2ee Binary files /dev/null and b/test/roms/bankswitching/2K/Tennis (1981) (Activision) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/2K/Tennis (1981) (Activision) (PAL) [p1][o1].a26 b/test/roms/bankswitching/2K/Tennis (1981) (Activision) (PAL) [p1][o1].a26 new file mode 100644 index 000000000..89bdbff4b Binary files /dev/null and b/test/roms/bankswitching/2K/Tennis (1981) (Activision) (PAL) [p1][o1].a26 differ diff --git a/test/roms/bankswitching/2K/Tennis (1981) (Activision) [!].a26 b/test/roms/bankswitching/2K/Tennis (1981) (Activision) [!].a26 new file mode 100644 index 000000000..faef0f0d9 Binary files /dev/null and b/test/roms/bankswitching/2K/Tennis (1981) (Activision) [!].a26 differ diff --git a/test/roms/bankswitching/2K/Tennis (1981) (Activision) [o1].a26 b/test/roms/bankswitching/2K/Tennis (1981) (Activision) [o1].a26 new file mode 100644 index 000000000..fab280258 Binary files /dev/null and b/test/roms/bankswitching/2K/Tennis (1981) (Activision) [o1].a26 differ diff --git a/test/roms/bankswitching/2K/Tennis (1981) (Activision) [o2].a26 b/test/roms/bankswitching/2K/Tennis (1981) (Activision) [o2].a26 new file mode 100644 index 000000000..f9c3c08b5 Binary files /dev/null and b/test/roms/bankswitching/2K/Tennis (1981) (Activision) [o2].a26 differ diff --git a/test/roms/bankswitching/2K/Tennis (32-in-1) (Atari) (PAL) [!].a26 b/test/roms/bankswitching/2K/Tennis (32-in-1) (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..e5d5764bf Binary files /dev/null and b/test/roms/bankswitching/2K/Tennis (32-in-1) (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/2K/Tennis (Dactar) (PAL) [p1][!].a26 b/test/roms/bankswitching/2K/Tennis (Dactar) (PAL) [p1][!].a26 new file mode 100644 index 000000000..a0ace2700 Binary files /dev/null and b/test/roms/bankswitching/2K/Tennis (Dactar) (PAL) [p1][!].a26 differ diff --git a/test/roms/bankswitching/2K/Tennis (Pet Boat) (PAL) [p1][!].a26 b/test/roms/bankswitching/2K/Tennis (Pet Boat) (PAL) [p1][!].a26 new file mode 100644 index 000000000..d6edac37d Binary files /dev/null and b/test/roms/bankswitching/2K/Tennis (Pet Boat) (PAL) [p1][!].a26 differ diff --git a/test/roms/bankswitching/2K/Tennis (Starsoft) (PAL) [!].a26 b/test/roms/bankswitching/2K/Tennis (Starsoft) (PAL) [!].a26 new file mode 100644 index 000000000..1c0eb0afd Binary files /dev/null and b/test/roms/bankswitching/2K/Tennis (Starsoft) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/2K/Video Olympics (1978) (Atari) (PAL) [!].a26 b/test/roms/bankswitching/2K/Video Olympics (1978) (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..2e62ed980 Binary files /dev/null and b/test/roms/bankswitching/2K/Video Olympics (1978) (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/2K/Video Olympics (1978) (Atari) [o1].a26 b/test/roms/bankswitching/2K/Video Olympics (1978) (Atari) [o1].a26 new file mode 100644 index 000000000..caa20dfcb Binary files /dev/null and b/test/roms/bankswitching/2K/Video Olympics (1978) (Atari) [o1].a26 differ diff --git a/test/roms/bankswitching/2K/Video Olympics (1978) (Atari).a26 b/test/roms/bankswitching/2K/Video Olympics (1978) (Atari).a26 new file mode 100644 index 000000000..14a5bdfc7 Binary files /dev/null and b/test/roms/bankswitching/2K/Video Olympics (1978) (Atari).a26 differ diff --git a/test/roms/bankswitching/2K/Xaxyrax Road by Charles Morgan (Freeway Hack).a26 b/test/roms/bankswitching/2K/Xaxyrax Road by Charles Morgan (Freeway Hack).a26 new file mode 100644 index 000000000..448ab3054 Binary files /dev/null and b/test/roms/bankswitching/2K/Xaxyrax Road by Charles Morgan (Freeway Hack).a26 differ diff --git a/test/roms/bankswitching/3E+/3E+ram.bin b/test/roms/bankswitching/3E+/3E+ram.bin new file mode 100644 index 000000000..2c8b5ef2a Binary files /dev/null and b/test/roms/bankswitching/3E+/3E+ram.bin differ diff --git a/test/roms/bankswitching/3E+/3E+ram_I.bin b/test/roms/bankswitching/3E+/3E+ram_I.bin new file mode 100644 index 000000000..23c0e2a9e Binary files /dev/null and b/test/roms/bankswitching/3E+/3E+ram_I.bin differ diff --git a/test/roms/bankswitching/3E+/3E+ram_NI.bin b/test/roms/bankswitching/3E+/3E+ram_NI.bin new file mode 100644 index 000000000..ece5dea86 Binary files /dev/null and b/test/roms/bankswitching/3E+/3E+ram_NI.bin differ diff --git a/test/roms/bankswitching/3E+/3E+rom.bin b/test/roms/bankswitching/3E+/3E+rom.bin new file mode 100644 index 000000000..da9b96d27 Binary files /dev/null and b/test/roms/bankswitching/3E+/3E+rom.bin differ diff --git a/test/roms/bankswitching/3E/256K RAM/badapple6-30-14.bin b/test/roms/bankswitching/3E/256K RAM/badapple6-30-14.bin new file mode 100644 index 000000000..f5b39e869 Binary files /dev/null and b/test/roms/bankswitching/3E/256K RAM/badapple6-30-14.bin differ diff --git a/test/roms/bankswitching/3E/3E Bankswitch Test (TIA @ $00).bin b/test/roms/bankswitching/3E/3E Bankswitch Test (TIA @ $00).bin new file mode 100644 index 000000000..fa79f1d00 Binary files /dev/null and b/test/roms/bankswitching/3E/3E Bankswitch Test (TIA @ $00).bin differ diff --git a/test/roms/bankswitching/3E/Andrew Davies early notBoulderDash demo (NTSC).bin b/test/roms/bankswitching/3E/Andrew Davies early notBoulderDash demo (NTSC).bin new file mode 100644 index 000000000..b55d6a896 Binary files /dev/null and b/test/roms/bankswitching/3E/Andrew Davies early notBoulderDash demo (NTSC).bin differ diff --git a/test/roms/bankswitching/3E/Andrew Davies early notBoulderDash demo (PAL).bin b/test/roms/bankswitching/3E/Andrew Davies early notBoulderDash demo (PAL).bin new file mode 100644 index 000000000..3330b3c33 Binary files /dev/null and b/test/roms/bankswitching/3E/Andrew Davies early notBoulderDash demo (PAL).bin differ diff --git a/test/roms/bankswitching/3E/BadApple1_0.bin b/test/roms/bankswitching/3E/BadApple1_0.bin new file mode 100644 index 000000000..65f0f90d5 Binary files /dev/null and b/test/roms/bankswitching/3E/BadApple1_0.bin differ diff --git a/test/roms/bankswitching/3E/Boulder Dash (Demo 2).bin b/test/roms/bankswitching/3E/Boulder Dash (Demo 2).bin new file mode 100644 index 000000000..c0530c8bb Binary files /dev/null and b/test/roms/bankswitching/3E/Boulder Dash (Demo 2).bin differ diff --git a/test/roms/bankswitching/3F/Espial (1984) (Tigervision) (PAL).a26 b/test/roms/bankswitching/3F/Espial (1984) (Tigervision) (PAL).a26 new file mode 100644 index 000000000..5484ff61b Binary files /dev/null and b/test/roms/bankswitching/3F/Espial (1984) (Tigervision) (PAL).a26 differ diff --git a/test/roms/bankswitching/3F/Espial (1984) (Tigervision).a26 b/test/roms/bankswitching/3F/Espial (1984) (Tigervision).a26 new file mode 100644 index 000000000..e5aa8380f Binary files /dev/null and b/test/roms/bankswitching/3F/Espial (1984) (Tigervision).a26 differ diff --git a/test/roms/bankswitching/3F/Miner 2049er (1982) (Tigervision).a26 b/test/roms/bankswitching/3F/Miner 2049er (1982) (Tigervision).a26 new file mode 100644 index 000000000..40812263d Binary files /dev/null and b/test/roms/bankswitching/3F/Miner 2049er (1982) (Tigervision).a26 differ diff --git a/test/roms/bankswitching/3F/Miner 2049er Volume II (1983) (Tigervision) (PAL).a26 b/test/roms/bankswitching/3F/Miner 2049er Volume II (1983) (Tigervision) (PAL).a26 new file mode 100644 index 000000000..758070c83 Binary files /dev/null and b/test/roms/bankswitching/3F/Miner 2049er Volume II (1983) (Tigervision) (PAL).a26 differ diff --git a/test/roms/bankswitching/3F/Polaris (1983) (Thomas Jentzsch).a26 b/test/roms/bankswitching/3F/Polaris (1983) (Thomas Jentzsch).a26 new file mode 100644 index 000000000..73c5bc521 Binary files /dev/null and b/test/roms/bankswitching/3F/Polaris (1983) (Thomas Jentzsch).a26 differ diff --git a/test/roms/bankswitching/3F/Polaris (1983) (Tigervision) (PAL).a26 b/test/roms/bankswitching/3F/Polaris (1983) (Tigervision) (PAL).a26 new file mode 100644 index 000000000..053d3ec52 Binary files /dev/null and b/test/roms/bankswitching/3F/Polaris (1983) (Tigervision) (PAL).a26 differ diff --git a/test/roms/bankswitching/3F/Polaris (1983) (Tigervision).a26 b/test/roms/bankswitching/3F/Polaris (1983) (Tigervision).a26 new file mode 100644 index 000000000..053d3ec52 Binary files /dev/null and b/test/roms/bankswitching/3F/Polaris (1983) (Tigervision).a26 differ diff --git a/test/roms/bankswitching/3F/River Patrol (1984) (Tigervision).a26 b/test/roms/bankswitching/3F/River Patrol (1984) (Tigervision).a26 new file mode 100644 index 000000000..aeec4821b Binary files /dev/null and b/test/roms/bankswitching/3F/River Patrol (1984) (Tigervision).a26 differ diff --git a/test/roms/bankswitching/3F/Springer (1982) (Tigervision).a26 b/test/roms/bankswitching/3F/Springer (1982) (Tigervision).a26 new file mode 100644 index 000000000..aabd6a45f Binary files /dev/null and b/test/roms/bankswitching/3F/Springer (1982) (Tigervision).a26 differ diff --git a/test/roms/bankswitching/3F/Untitled.a26 b/test/roms/bankswitching/3F/Untitled.a26 new file mode 100644 index 000000000..47ec041bc Binary files /dev/null and b/test/roms/bankswitching/3F/Untitled.a26 differ diff --git a/test/roms/bankswitching/3F/badapple6-25-14.bin b/test/roms/bankswitching/3F/badapple6-25-14.bin new file mode 100644 index 000000000..791cc04ad Binary files /dev/null and b/test/roms/bankswitching/3F/badapple6-25-14.bin differ diff --git a/test/roms/bankswitching/4A50/Ruby Runner 4A50.bin b/test/roms/bankswitching/4A50/Ruby Runner 4A50.bin new file mode 100644 index 000000000..0c2b88297 Binary files /dev/null and b/test/roms/bankswitching/4A50/Ruby Runner 4A50.bin differ diff --git a/test/roms/bankswitching/4A50/rr3.bin b/test/roms/bankswitching/4A50/rr3.bin new file mode 100644 index 000000000..511cd62c2 --- /dev/null +++ b/test/roms/bankswitching/4A50/rr3.bin @@ -0,0 +1,7 @@ +êÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿ ÿâÒÂòÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÂÒâòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿL²·¹ê…*ê…L%°†ˆì©U…©ª……* ©U¹…©Œ…©*… ©€… …¹…¹…¹…ˆŽ…+©\……*©Ú ¹…¹…©€†"† †!†#…¹…¹…¹…L±¹!°L€±L°¹ê…*ê…L%±†ˆì©U…©ª……* ©U¹…©Œ…©*… ©€… …¹…¹…¹…ˆŽ…+©\……*©Ú ¹…¹…©€†"† †!†#…¹…¹…¹…L²¹!±L€²L±¹ê…*ê…L%²†ˆì©U…©ª……* ©U¹…©Œ…©*… ©€… …¹…¹…¹…ˆŽ…+©\……*©Ú ¹…¹…©€†"† †!†#…¹…¹…¹…L³¹!²L€³L²¹ê…*ê…L%³†ˆì©U…©ª……* ©U¹…©Œ…©*… ©€… …¹…¹…¹…ˆŽ…+©\……*©Ú ¹…¹…©€†"† †!†#…¹…¹…¹…L´¹!³L€´L³¹ê…*ê…L%´†ˆì©U…©ª……* ©U¹…©Œ…©*… ©€… …¹…¹…¹…ˆŽ…+©\……*©Ú ¹…¹…©€†"† †!†#…¹…¹…¹…Lµ¹!´L€µL´¹ê…*ê…L%µ†ˆì©U…©ª……* ©U¹…©Œ…©*… ©€… …¹…¹…¹…ˆŽ…+©\……*©Ú ¹…¹…©€†"† †!†#…¹…¹…¹…L¶¹!µL€¶Lµ¹ê…*ê…L%¶†ˆì©U…©ª……* ©U¹…©Œ…©*… ©€… …¹…¹…¹…ˆŽ…+©\……*©Ú ¹…¹…©€†"† †!†#…¹…¹…¹…L·¹!¶L€·L¶¹ê…*ê…L%·†ˆì©U…©ª……* ©U¹…©Œ…©*… ©€… …¹…¹…¹…ˆŽ…+©\……*©Ú ¹…¹…©€†"† †!†#…¹…¹…¹…L­·¹!·L”·……û L€°­°…­°…­!°…¢‚…L°L·†ˆø… oLï¸ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿL±×…*¹¹L(Іˆê ©U…*…©ª…©ª…©U…©Œ…©*… ©€ …¹…¹…¹…ˆŽê†"† †!†#…*©\…©Ú ¹…¹…©…+…¹…¹…¹…Lѹ#й'ÐL‚ÑLÐ…*¹¹L(цˆê ©U…*…©ª…©ª…©U…©Œ…©*… ©€ …¹…¹…¹…ˆŽê†"† †!†#…*©\…©Ú ¹…¹…©…+…¹…¹…¹…LÒ¹#ѹ'ÑL‚ÒLÑ…*¹¹L(Ò†ˆê ©U…*…©ª…©ª…©U…©Œ…©*… ©€ …¹…¹…¹…ˆŽê†"† †!†#…*©\…©Ú ¹…¹…©…+…¹…¹…¹…LÓ¹#Ò¹'ÒL‚ÓLÒ…*¹¹L(Ó†ˆê ©U…*…©ª…©ª…©U…©Œ…©*… ©€ …¹…¹…¹…ˆŽê†"† †!†#…*©\…©Ú ¹…¹…©…+…¹…¹…¹…LÔ¹#Ó¹'ÓL‚ÔLÓ…*¹¹L(Ô†ˆê ©U…*…©ª…©ª…©U…©Œ…©*… ©€ …¹…¹…¹…ˆŽê†"† †!†#…*©\…©Ú ¹…¹…©…+…¹…¹…¹…LÕ¹#Ô¹'ÔL‚ÕLÔ…*¹¹L(Õ†ˆê ©U…*…©ª…©ª…©U…©Œ…©*… ©€ …¹…¹…¹…ˆŽê†"† †!†#…*©\…©Ú ¹…¹…©…+…¹…¹…¹…LÖ¹#Õ¹'ÕL‚ÖLÕ…*¹¹L(Ö†ˆê ©U…*…©ª…©ª…©U…©Œ…©*… ©€ …¹…¹…¹…ˆŽê†"† †!†#…*©\…©Ú ¹…¹…©…+…¹…¹…¹…L×¹#Ö¹'ÖL‚×LÖ…*¹¹L(׆ˆê ©U…*…©ª…©ª…©U…©Œ…©*… ©€ …¹…¹…¹…ˆŽê†"† †!†#…*©\…©Ú ¹…¹…©…+…¹…¹…¹…L¬×¹#×¹'×Lš×…û L‚ЭÐ…­Т‚…L ÐL׆ˆø… oLï¸ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ*EQ*ŠQTŠ¢T¢¨E¨*EQ*ŠQTŠ¢T¢¨E¨ÿ““×ÇÇ}EEƒïÇÇïÿÿǃ99}}mm}}99ƒÇÿÿïÇ«£ )AM££ÇïÿÿDDDÿÿHHHÿ"""ÿïÇ«£ )AM££ÇïÿÇ«£ )AM££ÇïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïÿßWGSƒ›+#GGßÿÿÿÿÿÿþþþþþþÿÿÿÿÿÿ÷ãÕÑ„” ¦ŠˆÑÑã÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿǃ99}}mm}}99ƒÇÿƒ99}}mm}}99ƒÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÿssûûÛÛûûssÿÿÿÿþþþþþþþþþþÿÿÿÿãÁœœ¾¾¶¶¾¾œœÁãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ''¯û‹‹ßßÿÿÿÿÿÿÿþþþþÿÿÿÿÿÿÿÉÉëãã¾¢¢€Á÷ãã÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ““×ÇÇ}EEƒïÇÇïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ“×ÇÇ}EEƒïÇÇïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ“ÿÿÇ»»}}mmmm««ÇÿÿÿÿÇ»»}}aa}}»»ÇÿÿÿÿÇ««mmmm}}»»ÇÿÿÿÿÇ»»}} }}»»ÇÿÿÿÿÇ»»}}mmmm««ÇÿÿÿÿÇ»»}}aa}}»»ÇÿÿÿÿÇ««mmmm}}»»ÇÿÿÿÿÇ»»}} }}»»ÇÿÿÿÿÇ»»}}mmmm««ÇÿÿÿÿÇ»»}}aa}}»»ÇÿÿÿÿÇ««mmmm}}»»ÇÿÿÿÿÇ»»}} }}»»ÇÿÿÿÿÿÿÇ»»}}mmmm««ÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿãÝݾ¾°°¾¾ÝÝãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇ««mmmm}}»»Çÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿwwûûûûwwÿÿÿÿÿÿÿþþþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ*EQ*ŠQTŠ¢T¢¨E¨*EQ*ŠQTŠ¢T¢¨E¨ÿ““×ÇÇ}EEƒïÇÇïÿÿǃ9}mmUUmm}9ƒÇÿÿïÇ«£ )AM££ÇïÿÿDDDÿÿHHHÿ"""ÿïÇ«£ )AM££Çïÿ)AM££ÇïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïÇ«£ ÿ?_OOo¯?ÿÿÿþýýøùúúøøýýþÿÿÿýøõôáåèéââôôøýÿÿÿÿ???¿¿?ÿÿÿÿǃ9}mmUUmm}9ƒÇÿmUUmm}9ƒÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿǃ9}mÿ?Ïïoo¯¯ooïÏ?ÿÿþüùûûûúúûûûùüþÿÿøðçïííêêííïçðøÿÿÿ?¿¿¿¿¿¿¿¿?ÿÿÿŸŸ¿??ï//??ÿÿüüþþþûúúøüÿþþÿÿÿòòúøøïèèàðýøøýÿÿÿÿÿ¿¿¿?ÿÿÿÿÿÿÿÿÿÿÿÿ““×ÇÇ}EEƒïÇÇïÿÿÿÿÿÿÿÿÿÿÿ}EEƒïÇÇïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ““×ÇÇÿÿÇ»»}}mmmu³³ÇÿÿÿÿÇ»»yqem}}»»ÇÿÿÿÿÇ››]mmm}}»»ÇÿÿÿÿÇ»»}}mM=»»ÇÿÿÿÿÇ»»}}mmm]››ÇÿÿÿÿÇ»»}}meqy»»Çÿÿÿÿdz³ummm}}»»ÇÿÿÿÿÇ»»=Mm}}»»ÇÿÿÿÿÇ»»}}mmmm««ÇÿÿÿÿÇ»»}}aa}}»»ÇÿÿÿÿÇ««mmmm}}»»ÇÿÿÿÿÇ»»}} }}»»ÇÿÿÿÿÿÿÿÿÿÿÇ»»}}mmmm««Çÿÿÿÿÿÿÿÿÿÿÿÿÿÿø÷÷ïïììïï÷÷øÿÿÿÿÿ¿¿??¿¿ÿÿÿmmm}}»»ÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇ««mÿÿ?ßßïïooïïßß?ÿÿÿÿþýýûûøøûûýýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ*EQ*ŠQTŠ¢T¢¨E¨*EQ*ŠQTŠ¢T¢¨E¨ÿ““×ÇÇ}EEƒïÇÇïÿÿǃ)mUU99UUm)ƒÇÿÿïÇ«£ )AM££ÇïÿÿDDDÿÿHHHÿ"""ÿïÇ«£ )AM££Çïÿ££ÇïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïÇ«£ )AMÿÿÿ???¿¿?ÿÿÿÿýøõôáåèéââôôøýÿÿÿþýýøùúúøøýýþÿÿÿ?_OOo¯?ÿÿǃ)mUU99UUm)ƒÇÿUm)ƒÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿǃ)mUU99Uÿÿ?¿¿¿??¿¿¿?ÿÿÿøðåíêêççêêíåðøÿÿþüùûúúùùúúûùüþÿÿ?Oo¯¯Ïϯ¯oO?ÿÿÿÿÿ¿¿¿?ÿÿÿÿÿÿòòúøøïèèàðýøøýÿÿüüþþþûúúøüÿþþÿÿÿŸŸ¿??ï//??ÿÿÿÿÿÿÿÿÿÿÿÿ““×ÇÇ}EEƒïÇÇïÿÿÿÿÿÿÿƒïÇÇïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ““×ÇÇ}EEÿÿÇ»»}}mmuu»»ÇÿÿÿÿÇ»»uumm}}»»ÇÿÿÿÿÇ»»]]mm}}»»ÇÿÿÿÿÇ»»}}mm]]»»ÇÿÿÿÿÇ»»}}mm]]»»ÇÿÿÿÿÇ»»}}mmuu»»ÇÿÿÿÿÇ»»uumm}}»»ÇÿÿÿÿÇ»»]]mm}}»»ÇÿÿÿÿÇ»»}}mmmm««ÇÿÿÿÿÇ»»}}aa}}»»ÇÿÿÿÿÇ««mmmm}}»»ÇÿÿÿÿÇ»»}} }}»»ÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇ»»}}mmmm««Çÿÿÿÿÿÿÿÿÿÿþýýûûûûûûýýþÿÿÿÿ?ßßïïïïßß?ÿÿ}»»ÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇ««mmmm}ÿÿÿ¿¿¿¿¿¿ÿÿÿÿÿø÷÷ïïááïï÷÷øÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ*EQ*ŠQTŠ¢T¢¨E¨*EQ*ŠQTŠ¢T¢¨E¨ÿ““×ÇÇ}EEƒïÇÇïÿÿǃU99}}99UƒÇÿÿïÇ«£ )AM££ÇïÿÿDDDÿÿHHHÿ"""ÿïÇ«£ )AM££ÇïÿïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïÇ«£ )AM££Çÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷ãÕÑ„” ¦ŠˆÑÑã÷ÿÿÿÿÿÿþþþþþþÿÿÿÿÿÿßWGSƒ›+#GGßÿÿǃU99}}99UƒÇÿÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿǃU99}}99UƒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿãÁˆªœœ¾¾œœªˆÁãÿÿÿÿþþþþþþþþþþÿÿÿÿ#«ssûûss«#ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÉÉëãã¾¢¢€Á÷ãã÷ÿÿÿÿÿÿÿþþþþÿÿÿÿÿÿÿ''¯û‹‹ßßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ““×ÇÇ}EEƒïÇÇïÿÿÿïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ““×ÇÇ}EEƒïÇÇÿÿÇ»»}}meqy»»Çÿÿÿÿdz³ummm}}»»ÇÿÿÿÿÇ»»=Mm}}»»ÇÿÿÿÿÇ»»}}mmm]››ÇÿÿÿÿÇ»»}}mM=»»ÇÿÿÿÿÇ»»}}mmmu³³ÇÿÿÿÿÇ»»yqem}}»»ÇÿÿÿÿÇ››]mmm}}»»ÇÿÿÿÿÇ»»}}mmmm««ÇÿÿÿÿÇ»»}}aa}}»»ÇÿÿÿÿÇ««mmmm}}»»ÇÿÿÿÿÇ»»}} }}»»ÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇ»»}}mmmm««ÇÿÿÿÿÿÿÿÿÿþþþþþþÿÿÿÿÿÿÿwwûûÃÃûûwwÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇ««mmmm}}»»ÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿãÝݾ¾††¾¾ÝÝãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿxØ¢Š¨ÊšHÐû¢óš©…€©ø…©`…‚ An¢ l½ÊÐ÷ l½ÊÐ÷ l½ÊÐ÷ l½ÊÐ÷ l½ÊÐ÷ l½ÊÐ÷ l½ÊÐ÷ l½ÊÐ÷ En¢ 8l½ÊÐ÷ 9l½ÊÐ÷ :l½ÊÐ÷ ;l½ÊÐ÷ l½ÊÐ÷ ?l½ÊÐ÷ Dn¢ 0l½ÊÐ÷ 1l½ÊÐ÷ 2l½ÊÐ÷ 3l½ÊÐ÷ 4l½ÊÐ÷ 5l½ÊÐ÷ 6l½ÊÐ÷ 7l½ÊÐ÷ Fn¢ l½ÊÐ÷ !l½ÊÐ÷ "l½ÊÐ÷ #l½ÊÐ÷ $l½ÊÐ÷ %l½ÊÐ÷ &l½ÊÐ÷ 'l½ÊÐ÷©…Œ……Ž…©……©…©€…ˆLÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©•…øLÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ +  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ +  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ¡£¥§‘“‘“BRbr‘“¼½¾¿ÀÁRÃÄÅrÇÈÉÊËÌÍÎÏÐÑbÓÔÕBרÙÚÛÜÝÞßàárãäåRçèéêëìíîïðñBóôõb÷øùúûüýþÿ©–…ø©…˜©@…–©€…’©À…”©…“…•…— Fn ,„0 ÿ Fn³’½‘’ȳ’½‘’ȳ’½‘’ȳ’½‘’ÈÀ<É¥’i¿…’°Æ“8¥”i¿…”°Æ•8¥–i¿…–°ƗƘУ©…˜©@…’©€…”©…–©…“…•…— Fn  ÀlLþ¥’¥“ ÿ Àl FnLþLÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥’¥“,„0LŸȳ’ȳ’ȳ’ȳ’Lþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥’¥“,„0LŸȳ’L$þL$þL$þ)}ÉÐÖ±”Ð ±’ ¸‘”I‘’Lþ±”)ð4ˆ±’”Ðȱ’ ¸I‘’ˆI‘’ÈLþÈȱ’”Ј±’ ¼I‘’ÈI‘’ˆLþˆ±’)‘’Lþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥’¥“,„0LŸȳ’ȳ’ȳ’ȳ’Lþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥’¥“,„0LŸȳ’ȳ’ȳ’ȳ’Lþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥’¥“,„0LŸȳ’L$þL$þL$þ±–Ð ©´‘–©°‘’Lþˆ±’ðȩ‘’LþȩƑ’Lþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥’¥“,„0LŸȳ’L$þL$þL$þȱ’Ð ©µ‘’ˆ©±‘’Lþˆ±–ð©Ò‘’Lþ©Ö‘’Lþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥’¥“,„0LŸȳ’L$þL$þL$þ±”Ð ©¶‘”©²‘’Lþȱ’ðˆ©â‘’Lþˆ©æ‘’Lþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥’¥“,„0LŸȳ’L$þL$þL$þˆ±’Ð ©·‘’È©³‘’Lþȱ”ð©ò‘’Lþ©ö‘’Lþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥’¥“,„0LŸȳ’ȳ’ȳ’ȳ’Lþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥’¥“,„0LŸȳ’ȳ’ȳ’ȳ’Lþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥’¥“,„0LŸȳ’ȳ’ȳ’ȳ’Lþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥’¥“,„0LŸȳ’ȳ’ȳ’ȳ’Lþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥’¥“,„0LŸȳ’ȳ’ȳ’ȳ’Lþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥’¥“,„0LŸȳ’ȳ’ȳ’ȳ’Lþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥’¥“,„0LŸȳ’ȳ’ȳ’ȳ’Lþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥’¥“,„0LŸȳ’L$þL$þL$þ¥’i@…’æ“¥”i@…”æ•¥–i@…–无ƘдL¯ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©š…ø¥ˆ0 ÿL)…ˆ¥Œ…Ž¥…©"…,€0 ¥ŒÉ?°挩¢…,€p +¥ŒðÆŒ© …© ,€Ð ¥É°橦…©,€Ð +¥ðÆ©¤…¦Œ¤ ÿ½É©"…¥Ž…Œ¥…L‡¥i¦Ž¤ ÿ¥­‚)Ðlüÿ ÿ¦Š¤‹ ÿ Bn ½™½™½™½™½™ ½™(½™0½™8½™@½ ™HŠËÀæÿˆº O¾½ò™½ò™½ò™ˆè ÿ¥Œ8é°©ÅаÆŠ¥Œ8é É5©5ÅŠ报8é°©Å‹°Æ‹¥8é É5©Å‹æ‹ Bn©(…ý© …ÿ¢ sæýæÿÊö ÿ âLÿÿ½JJJJJJ ½(JJ03½JJHJJ@JJ8ý½JJJJJJ ½(JJ03½JJHJJ@JJ8` Bn©(…ý© …ÿ¢½Yýƒ) „ÿZ½ýa) bÿ‚½^ý ‰)  Šÿ_½&ýf) gÿ'½ oý:) ;ÿp½(9ýq) rÿ:½0tý?) @ÿu½8>ýv) wÿ?½@yýD) Eÿz½HCý{) |ÿDæýæÿÊ0Lï`ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ0 €p`P@’¢²Â‘¡±ÁÑáÀÐAQàaqÒ#Câ3S °!1R"b2rB‚ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ……†ƒ„„©……©}Í„÷©…F†°懩……JÐù©Ã–憩…¦ƒ¤„¥…L€ An…©……©… +©6…………¥‰) û¥ˆ©JJJJJ…‰©$‡ÐL¬¸…© …$©0… …#©…"©P…!…………………*……©*… ©Œ…  DnL‰·…©6…………©0…!…#©P… ……………"©À…$êêêêê………*……©*… ©Œ…  EnL‘×…©…©……………………©Ã–…©,‚Ð ©…‡,‚P懩,‚0©P +eˆ…ˆ©… …©ÿ……¦ƒ¤„¥…L€©…‘˜Jf‘Jf‘i0…ÿŠe‘ªL€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ oLx…ø oL¸ oL oL oL oLF¹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ \ No newline at end of file diff --git a/test/roms/bankswitching/4A50/rr4.bin b/test/roms/bankswitching/4A50/rr4.bin new file mode 100644 index 000000000..ebbb4bf9d --- /dev/null +++ b/test/roms/bankswitching/4A50/rr4.bin @@ -0,0 +1,5 @@ +êÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿ ÿâÒÂòÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòÿÿÿÿÿÿ  ÿÿÿÿÿÿÿÿÿÿÂÒâòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿL²·¹ê…*ê…L%°†ˆì©U…©ª……* ©U¹…©Œ…©*… ©€… …¹…¹…¹…ˆŽ…+©\……*©Ú ¹…¹…©€†"† †!†#…¹…¹…¹…L±¹!°L€±L°¹ê…*ê…L%±†ˆì©U…©ª……* ©U¹…©Œ…©*… ©€… …¹…¹…¹…ˆŽ…+©\……*©Ú ¹…¹…©€†"† †!†#…¹…¹…¹…L²¹!±L€²L±¹ê…*ê…L%²†ˆì©U…©ª……* ©U¹…©Œ…©*… ©€… …¹…¹…¹…ˆŽ…+©\……*©Ú ¹…¹…©€†"† †!†#…¹…¹…¹…L³¹!²L€³L²¹ê…*ê…L%³†ˆì©U…©ª……* ©U¹…©Œ…©*… ©€… …¹…¹…¹…ˆŽ…+©\……*©Ú ¹…¹…©€†"† †!†#…¹…¹…¹…L´¹!³L€´L³¹ê…*ê…L%´†ˆì©U…©ª……* ©U¹…©Œ…©*… ©€… …¹…¹…¹…ˆŽ…+©\……*©Ú ¹…¹…©€†"† †!†#…¹…¹…¹…Lµ¹!´L€µL´¹ê…*ê…L%µ†ˆì©U…©ª……* ©U¹…©Œ…©*… ©€… …¹…¹…¹…ˆŽ…+©\……*©Ú ¹…¹…©€†"† †!†#…¹…¹…¹…L¶¹!µL€¶Lµ¹ê…*ê…L%¶†ˆì©U…©ª……* ©U¹…©Œ…©*… ©€… …¹…¹…¹…ˆŽ…+©\……*©Ú ¹…¹…©€†"† †!†#…¹…¹…¹…L·¹!¶L€·L¶¹ê…*ê…L%·†ˆì©U…©ª……* ©U¹…©Œ…©*… ©€… …¹…¹…¹…ˆŽ…+©\……*©Ú ¹…¹…©€†"† †!†#…¹…¹…¹…L­·¹!·L”·……û L€°­°…­°…­!°…¢‚…L°L·†ˆø… oLï¸ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿL±×…*¹¹L(Іˆê ©U…*…©ª…©ª…©U…©Œ…©*… ©€ …¹…¹…¹…ˆŽê†"† †!†#…*©\…©Ú ¹…¹…©…+…¹…¹…¹…Lѹ#й'ÐL‚ÑLÐ…*¹¹L(цˆê ©U…*…©ª…©ª…©U…©Œ…©*… ©€ …¹…¹…¹…ˆŽê†"† †!†#…*©\…©Ú ¹…¹…©…+…¹…¹…¹…LÒ¹#ѹ'ÑL‚ÒLÑ…*¹¹L(Ò†ˆê ©U…*…©ª…©ª…©U…©Œ…©*… ©€ …¹…¹…¹…ˆŽê†"† †!†#…*©\…©Ú ¹…¹…©…+…¹…¹…¹…LÓ¹#Ò¹'ÒL‚ÓLÒ…*¹¹L(Ó†ˆê ©U…*…©ª…©ª…©U…©Œ…©*… ©€ …¹…¹…¹…ˆŽê†"† †!†#…*©\…©Ú ¹…¹…©…+…¹…¹…¹…LÔ¹#Ó¹'ÓL‚ÔLÓ…*¹¹L(Ô†ˆê ©U…*…©ª…©ª…©U…©Œ…©*… ©€ …¹…¹…¹…ˆŽê†"† †!†#…*©\…©Ú ¹…¹…©…+…¹…¹…¹…LÕ¹#Ô¹'ÔL‚ÕLÔ…*¹¹L(Õ†ˆê ©U…*…©ª…©ª…©U…©Œ…©*… ©€ …¹…¹…¹…ˆŽê†"† †!†#…*©\…©Ú ¹…¹…©…+…¹…¹…¹…LÖ¹#Õ¹'ÕL‚ÖLÕ…*¹¹L(Ö†ˆê ©U…*…©ª…©ª…©U…©Œ…©*… ©€ …¹…¹…¹…ˆŽê†"† †!†#…*©\…©Ú ¹…¹…©…+…¹…¹…¹…L×¹#Ö¹'ÖL‚×LÖ…*¹¹L(׆ˆê ©U…*…©ª…©ª…©U…©Œ…©*… ©€ …¹…¹…¹…ˆŽê†"† †!†#…*©\…©Ú ¹…¹…©…+…¹…¹…¹…L¬×¹#×¹'×Lš×…û L‚ЭÐ…­Т‚…L ÐL׆ˆø… oLï¸ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ*EQ*ŠQTŠ¢T¢¨E¨*EQ*ŠQTŠ¢T¢¨E¨ÿ““×ÇÇ}EEƒïÇÇïÿÿǃ99}}mm}}99ƒÇÿÿïÇ«£ )AM££ÇïÿÿDDDÿÿHHHÿ"""ÿïÇ«£ )AM££ÇïÿÇ«£ )AM££ÇïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïÿßWGSƒ›+#GGßÿÿÿÿÿÿþþþþþþÿÿÿÿÿÿ÷ãÕÑ„” ¦ŠˆÑÑã÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿǃ99}}mm}}99ƒÇÿƒ99}}mm}}99ƒÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÿssûûÛÛûûssÿÿÿÿþþþþþþþþþþÿÿÿÿãÁœœ¾¾¶¶¾¾œœÁãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ''¯û‹‹ßßÿÿÿÿÿÿÿþþþþÿÿÿÿÿÿÿÉÉëãã¾¢¢€Á÷ãã÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ““×ÇÇ}EEƒïÇÇïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ“×ÇÇ}EEƒïÇÇïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ“ÿÿÇ»»}}mmmm««ÇÿÿÿÿÇ»»}}aa}}»»ÇÿÿÿÿÇ««mmmm}}»»ÇÿÿÿÿÇ»»}} }}»»ÇÿÿÿÿÇ»»}}mmmm««ÇÿÿÿÿÇ»»}}aa}}»»ÇÿÿÿÿÇ««mmmm}}»»ÇÿÿÿÿÇ»»}} }}»»ÇÿÿÿÿÇ»»}}mmmm««ÇÿÿÿÿÇ»»}}aa}}»»ÇÿÿÿÿÇ««mmmm}}»»ÇÿÿÿÿÇ»»}} }}»»ÇÿÿÿÿÿÿÇ»»}}mmmm««ÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿãÝݾ¾°°¾¾ÝÝãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇ««mmmm}}»»Çÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿwwûûûûwwÿÿÿÿÿÿÿþþþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ*EQ*ŠQTŠ¢T¢¨E¨*EQ*ŠQTŠ¢T¢¨E¨ÿ““×ÇÇ}EEƒïÇÇïÿÿǃ9}mmUUmm}9ƒÇÿÿïÇ«£ )AM££ÇïÿÿDDDÿÿHHHÿ"""ÿïÇ«£ )AM££Çïÿ)AM££ÇïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïÇ«£ ÿ?_OOo¯?ÿÿÿþýýøùúúøøýýþÿÿÿýøõôáåèéââôôøýÿÿÿÿ???¿¿?ÿÿÿÿǃ9}mmUUmm}9ƒÇÿmUUmm}9ƒÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿǃ9}mÿ?Ïïoo¯¯ooïÏ?ÿÿþüùûûûúúûûûùüþÿÿøðçïííêêííïçðøÿÿÿ?¿¿¿¿¿¿¿¿?ÿÿÿŸŸ¿??ï//??ÿÿüüþþþûúúøüÿþþÿÿÿòòúøøïèèàðýøøýÿÿÿÿÿ¿¿¿?ÿÿÿÿÿÿÿÿÿÿÿÿ““×ÇÇ}EEƒïÇÇïÿÿÿÿÿÿÿÿÿÿÿ}EEƒïÇÇïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ““×ÇÇÿÿÇ»»}}mmmu³³ÇÿÿÿÿÇ»»yqem}}»»ÇÿÿÿÿÇ››]mmm}}»»ÇÿÿÿÿÇ»»}}mM=»»ÇÿÿÿÿÇ»»}}mmm]››ÇÿÿÿÿÇ»»}}meqy»»Çÿÿÿÿdz³ummm}}»»ÇÿÿÿÿÇ»»=Mm}}»»ÇÿÿÿÿÇ»»}}mmmm««ÇÿÿÿÿÇ»»}}aa}}»»ÇÿÿÿÿÇ««mmmm}}»»ÇÿÿÿÿÇ»»}} }}»»ÇÿÿÿÿÿÿÿÿÿÿÇ»»}}mmmm««Çÿÿÿÿÿÿÿÿÿÿÿÿÿÿø÷÷ïïììïï÷÷øÿÿÿÿÿ¿¿??¿¿ÿÿÿmmm}}»»ÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇ««mÿÿ?ßßïïooïïßß?ÿÿÿÿþýýûûøøûûýýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ*EQ*ŠQTŠ¢T¢¨E¨*EQ*ŠQTŠ¢T¢¨E¨ÿ““×ÇÇ}EEƒïÇÇïÿÿǃ)mUU99UUm)ƒÇÿÿïÇ«£ )AM££ÇïÿÿDDDÿÿHHHÿ"""ÿïÇ«£ )AM££Çïÿ££ÇïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïÇ«£ )AMÿÿÿ???¿¿?ÿÿÿÿýøõôáåèéââôôøýÿÿÿþýýøùúúøøýýþÿÿÿ?_OOo¯?ÿÿǃ)mUU99UUm)ƒÇÿUm)ƒÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿǃ)mUU99Uÿÿ?¿¿¿??¿¿¿?ÿÿÿøðåíêêççêêíåðøÿÿþüùûúúùùúúûùüþÿÿ?Oo¯¯Ïϯ¯oO?ÿÿÿÿÿ¿¿¿?ÿÿÿÿÿÿòòúøøïèèàðýøøýÿÿüüþþþûúúøüÿþþÿÿÿŸŸ¿??ï//??ÿÿÿÿÿÿÿÿÿÿÿÿ““×ÇÇ}EEƒïÇÇïÿÿÿÿÿÿÿƒïÇÇïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ““×ÇÇ}EEÿÿÇ»»}}mmuu»»ÇÿÿÿÿÇ»»uumm}}»»ÇÿÿÿÿÇ»»]]mm}}»»ÇÿÿÿÿÇ»»}}mm]]»»ÇÿÿÿÿÇ»»}}mm]]»»ÇÿÿÿÿÇ»»}}mmuu»»ÇÿÿÿÿÇ»»uumm}}»»ÇÿÿÿÿÇ»»]]mm}}»»ÇÿÿÿÿÇ»»}}mmmm««ÇÿÿÿÿÇ»»}}aa}}»»ÇÿÿÿÿÇ««mmmm}}»»ÇÿÿÿÿÇ»»}} }}»»ÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇ»»}}mmmm««Çÿÿÿÿÿÿÿÿÿÿþýýûûûûûûýýþÿÿÿÿ?ßßïïïïßß?ÿÿ}»»ÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇ««mmmm}ÿÿÿ¿¿¿¿¿¿ÿÿÿÿÿø÷÷ïïááïï÷÷øÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ*EQ*ŠQTŠ¢T¢¨E¨*EQ*ŠQTŠ¢T¢¨E¨ÿ““×ÇÇ}EEƒïÇÇïÿÿǃU99}}99UƒÇÿÿïÇ«£ )AM££ÇïÿÿDDDÿÿHHHÿ"""ÿïÇ«£ )AM££ÇïÿïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïÇ«£ )AM££Çÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷ãÕÑ„” ¦ŠˆÑÑã÷ÿÿÿÿÿÿþþþþþþÿÿÿÿÿÿßWGSƒ›+#GGßÿÿǃU99}}99UƒÇÿÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿǃU99}}99UƒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿãÁˆªœœ¾¾œœªˆÁãÿÿÿÿþþþþþþþþþþÿÿÿÿ#«ssûûss«#ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÉÉëãã¾¢¢€Á÷ãã÷ÿÿÿÿÿÿÿþþþþÿÿÿÿÿÿÿ''¯û‹‹ßßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ““×ÇÇ}EEƒïÇÇïÿÿÿïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ““×ÇÇ}EEƒïÇÇÿÿÇ»»}}meqy»»Çÿÿÿÿdz³ummm}}»»ÇÿÿÿÿÇ»»=Mm}}»»ÇÿÿÿÿÇ»»}}mmm]››ÇÿÿÿÿÇ»»}}mM=»»ÇÿÿÿÿÇ»»}}mmmu³³ÇÿÿÿÿÇ»»yqem}}»»ÇÿÿÿÿÇ››]mmm}}»»ÇÿÿÿÿÇ»»}}mmmm««ÇÿÿÿÿÇ»»}}aa}}»»ÇÿÿÿÿÇ««mmmm}}»»ÇÿÿÿÿÇ»»}} }}»»ÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇ»»}}mmmm««ÇÿÿÿÿÿÿÿÿÿþþþþþþÿÿÿÿÿÿÿwwûûÃÃûûwwÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇ««mmmm}}»»ÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿãÝݾ¾††¾¾ÝÝãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿxØ¢Š¨ÊšHÐû¢óš©…€©ø…©`…‚ An¢ l½ÊÐ÷ l½ÊÐ÷ l½ÊÐ÷ l½ÊÐ÷ l½ÊÐ÷ l½ÊÐ÷ l½ÊÐ÷ l½ÊÐ÷ En¢ 8l½ÊÐ÷ 9l½ÊÐ÷ :l½ÊÐ÷ ;l½ÊÐ÷ l½ÊÐ÷ ?l½ÊÐ÷ Dn¢ 0l½ÊÐ÷ 1l½ÊÐ÷ 2l½ÊÐ÷ 3l½ÊÐ÷ 4l½ÊÐ÷ 5l½ÊÐ÷ 6l½ÊÐ÷ 7l½ÊÐ÷ Fn¢ l½ÊÐ÷ !l½ÊÐ÷ "l½ÊÐ÷ #l½ÊÐ÷ $l½ÊÐ÷ %l½ÊÐ÷ &l½ÊÐ÷ 'l½ÊÐ÷©…Œ……Ž…©……©…©€…ˆLÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©•…øLÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ +  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ""""‘“‘“BRbr‘“¼½¾¿ÀÁRÃÄÅrÇÈÉÊËÌÍÎÏÐÑbÓÔÕBרÙÚÛÜÝÞßàárãäåRçèéêëìíîïðñBóôõb÷øùúûüýþÿ©–…ø©…˜©@…–©€…’©À…”©…“…•…— Fn ,„0 ÿ Fn³’½‘’ȳ’½‘’ȳ’½‘’ȳ’½‘’ÈÀ<É¥’i¿…’°Æ“8¥”i¿…”°Æ•8¥–i¿…–°ƗƘУ©…˜©€…’©À…”©@…–©…“…•…— Fn  ÀlLþ¥’¥“ ÿ Àl FnLþLÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥’¥“,„0LŸȳ’ȳ’ȳ’ȳ’Lþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥’¥“,„0LŸȳ’L$þL$þL$þ)}ÉÐÖ±”Ð ±’ ¸‘”I‘’Lþ³”½)ð4ˆ±’”Ðȱ’ ¸I‘’ˆI‘’ÈLþÈȱ’”Ј±’ ¼I‘’ÈI‘’ˆLþˆ±’)‘’Lþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥’¥“,„0LŸȳ’ȳ’ȳ’ȳ’Lþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥’¥“,„0LŸȳ’ȳ’ȳ’ȳ’Lþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥’¥“,„0LŸȳ’L$þL$þL$þ±–Ð ©´‘–©°‘’Lþˆ±’ðȩ‘’LþȩƑ’Lþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥’¥“,„0LŸȳ’L$þL$þL$þȱ’Ð ©µ‘’ˆ©±‘’Lþˆ±–ð©Ò‘’Lþ©Ö‘’Lþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥’¥“,„0LŸȳ’L$þL$þL$þ±”Ð ©¶‘”©²‘’Lþȱ’ðˆ©â‘’Lþˆ©æ‘’Lþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥’¥“,„0LŸȳ’L$þL$þL$þˆ±’Ð ©·‘’È©³‘’Lþȱ”ð©ò‘’Lþ©ö‘’Lþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥’¥“,„0LŸȳ’ȳ’ȳ’ȳ’Lþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥’¥“,„0LŸȳ’ȳ’ȳ’ȳ’Lþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥’¥“,„0LŸȳ’ȳ’ȳ’ȳ’Lþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥’¥“,„0LŸȳ’ȳ’ȳ’ȳ’Lþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥’¥“,„0LŸȳ’ȳ’ȳ’ȳ’Lþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥’¥“,„0LŸȳ’ȳ’ȳ’ȳ’Lþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥’¥“,„0LŸȳ’ȳ’ȳ’ȳ’Lþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥’¥“,„0LŸȳ’L$þL$þL$þ8¥’i¿…’°Æ“8¥”i¿…”°Æ•8¥–i¿…–°Æ— Æ˜ÐµL¯ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©š…ø¥ˆ0 ÿL)…ˆ¥Œ…Ž¥…©"…,€0.¥ŒÉ?°(挩¢…¦Œ¤ &ÿ½)ÉÐh½Ðc©­©¬L¸,€p-¥Œð)ÆŒ© …¦Œ¤ &ÿ½)ÉÐ7ʽÐ1©©©¨L¸© ,€Ð ¥É°橦…©,€Ð +¥ðÆ©¤…¦Œ¤ &ÿ¼¹)Щ"…¥Ž…Œ¥…LÏ¥i¦Ž¤ &ÿ¥­‚)ÐlüÿL ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©›…ø ÿ¦Š¤‹ &ÿ Bn ½™½™½™½™½™ ½™(½™0½™8½™@½ ™HŠËÀæÿˆº O¾½Y™½Y™½Y™ˆè ÿ¥Œ8é°©ÅаÆŠ¥Œ8é É5©5ÅŠ报8é°©Å‹°Æ‹¥8é É5©Å‹æ‹ Bn©(…ý© …ÿ¢ ÚæýæÿÊö ÿ ILÿÿ½JJJJJJ ½(JJ03½JJHJJ@JJ8ý½JJJJJJ ½(JJ03½JJHJJ@JJ8` Bn©(…ý© …ÿ¢½Yýƒ) „ÿZ½ýa) bÿ‚½^ý ‰)  Šÿ_½&ýf) gÿ'½ oý:) ;ÿp½(9ýq) rÿ:½0tý?) @ÿu½8>ýv) wÿ?½@yýD) Eÿz½HCý{) |ÿDæýæÿÊ0LV`ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ0 €p`P@’¢²Â‘¡±ÁÑáÀÐAQàaqÒ#Câ3S °!1R"b2rB‚ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ……†ƒ„„©……©}Í„÷©…F†°懩……JÐù©Ã–憩…¦ƒ¤„¥…L€ An…©……©… +©6…………¥‰) û¥ˆ©JJJJJ…‰©$‡ÐL¬¸…© …$©0… …#©…"©P…!…………………*……©*… ©Œ…  DnL‰·…©6…………©0…!…#©P… ……………"©À…$êêêêê………*……©*… ©Œ…  EnL‘×…©…©……………………©Ã–…©,‚Ð ©…‡,‚P懩,‚0©P +eˆ…ˆ©… …©ÿ……¦ƒ¤„¥…L€©…‘˜Jf‘Jf‘i0…ÿŠe‘ªL€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ oLx…ø oL¸ oL oL oL oL oLF¹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ \ No newline at end of file diff --git a/test/roms/bankswitching/4A50/spin4a50.bin b/test/roms/bankswitching/4A50/spin4a50.bin new file mode 100644 index 000000000..2abd7550f Binary files /dev/null and b/test/roms/bankswitching/4A50/spin4a50.bin differ diff --git a/test/roms/bankswitching/4A50/test4a50fix.bin b/test/roms/bankswitching/4A50/test4a50fix.bin new file mode 100644 index 000000000..1592006db Binary files /dev/null and b/test/roms/bankswitching/4A50/test4a50fix.bin differ diff --git a/test/roms/bankswitching/4A50/test_includes_file.bas.bin b/test/roms/bankswitching/4A50/test_includes_file.bas.bin new file mode 100644 index 000000000..5c530eec6 Binary files /dev/null and b/test/roms/bankswitching/4A50/test_includes_file.bas.bin differ diff --git a/test/roms/bankswitching/4KSC/4KSC_test.bin b/test/roms/bankswitching/4KSC/4KSC_test.bin new file mode 100644 index 000000000..3e2949441 Binary files /dev/null and b/test/roms/bankswitching/4KSC/4KSC_test.bin differ diff --git a/test/roms/bankswitching/AR/Dragonstomper (1982) (Starpath).bin b/test/roms/bankswitching/AR/Dragonstomper (1982) (Starpath).bin new file mode 100644 index 000000000..560fd7bf5 Binary files /dev/null and b/test/roms/bankswitching/AR/Dragonstomper (1982) (Starpath).bin differ diff --git a/test/roms/bankswitching/AR/Escape from the Mindmaster (1982) (Arcadia).bin b/test/roms/bankswitching/AR/Escape from the Mindmaster (1982) (Arcadia).bin new file mode 100644 index 000000000..035dff529 Binary files /dev/null and b/test/roms/bankswitching/AR/Escape from the Mindmaster (1982) (Arcadia).bin differ diff --git a/test/roms/bankswitching/AR/Official Frogger, The (1983) (Starpath).bin b/test/roms/bankswitching/AR/Official Frogger, The (1983) (Starpath).bin new file mode 100644 index 000000000..f074f9420 Binary files /dev/null and b/test/roms/bankswitching/AR/Official Frogger, The (1983) (Starpath).bin differ diff --git a/test/roms/bankswitching/AR/Party Mix (1983) (Arcadia).bin b/test/roms/bankswitching/AR/Party Mix (1983) (Arcadia).bin new file mode 100644 index 000000000..7a05916ac Binary files /dev/null and b/test/roms/bankswitching/AR/Party Mix (1983) (Arcadia).bin differ diff --git a/test/roms/bankswitching/AR/Phaser Patrol (1982) (Arcadia).bin b/test/roms/bankswitching/AR/Phaser Patrol (1982) (Arcadia).bin new file mode 100644 index 000000000..d6c5d5c4d Binary files /dev/null and b/test/roms/bankswitching/AR/Phaser Patrol (1982) (Arcadia).bin differ diff --git a/test/roms/bankswitching/BF/BF_256k_test.bin b/test/roms/bankswitching/BF/BF_256k_test.bin new file mode 100644 index 000000000..314f8c268 Binary files /dev/null and b/test/roms/bankswitching/BF/BF_256k_test.bin differ diff --git a/test/roms/bankswitching/BFSC/BFSC_256k_ramtest.bin b/test/roms/bankswitching/BFSC/BFSC_256k_ramtest.bin new file mode 100644 index 000000000..3b0636ac0 Binary files /dev/null and b/test/roms/bankswitching/BFSC/BFSC_256k_ramtest.bin differ diff --git a/test/roms/bankswitching/BUS/128bus_20170120.bin b/test/roms/bankswitching/BUS/128bus_20170120.bin new file mode 100644 index 000000000..dee840a35 Binary files /dev/null and b/test/roms/bankswitching/BUS/128bus_20170120.bin differ diff --git a/test/roms/bankswitching/BUS/128chronocolour_20170101.bin b/test/roms/bankswitching/BUS/128chronocolour_20170101.bin new file mode 100644 index 000000000..c930ad492 Binary files /dev/null and b/test/roms/bankswitching/BUS/128chronocolour_20170101.bin differ diff --git a/test/roms/bankswitching/BUS/draconian_20161102.bin b/test/roms/bankswitching/BUS/draconian_20161102.bin new file mode 100644 index 000000000..b21509761 Binary files /dev/null and b/test/roms/bankswitching/BUS/draconian_20161102.bin differ diff --git a/test/roms/bankswitching/BUS/parrot_20161231_NTSC.bin b/test/roms/bankswitching/BUS/parrot_20161231_NTSC.bin new file mode 100644 index 000000000..10f54bd1a Binary files /dev/null and b/test/roms/bankswitching/BUS/parrot_20161231_NTSC.bin differ diff --git a/test/roms/bankswitching/BUS/rpg_20161019.bin b/test/roms/bankswitching/BUS/rpg_20161019.bin new file mode 100644 index 000000000..de2076c7a Binary files /dev/null and b/test/roms/bankswitching/BUS/rpg_20161019.bin differ diff --git a/test/roms/bankswitching/BUS/rpg_20161231_NTSC.bin b/test/roms/bankswitching/BUS/rpg_20161231_NTSC.bin new file mode 100644 index 000000000..187f4edef Binary files /dev/null and b/test/roms/bankswitching/BUS/rpg_20161231_NTSC.bin differ diff --git a/test/roms/bankswitching/BUS/test0_stuff.bin b/test/roms/bankswitching/BUS/test0_stuff.bin new file mode 100644 index 000000000..6765ba78d Binary files /dev/null and b/test/roms/bankswitching/BUS/test0_stuff.bin differ diff --git a/test/roms/bankswitching/BUS/test1_music.bin b/test/roms/bankswitching/BUS/test1_music.bin new file mode 100644 index 000000000..1b0ff8ff4 Binary files /dev/null and b/test/roms/bankswitching/BUS/test1_music.bin differ diff --git a/test/roms/bankswitching/BUS/test2_speech.bin b/test/roms/bankswitching/BUS/test2_speech.bin new file mode 100644 index 000000000..42d1266b8 Binary files /dev/null and b/test/roms/bankswitching/BUS/test2_speech.bin differ diff --git a/test/roms/bankswitching/BUS/test3_bigscroll.bin b/test/roms/bankswitching/BUS/test3_bigscroll.bin new file mode 100644 index 000000000..c40b7c9e5 Binary files /dev/null and b/test/roms/bankswitching/BUS/test3_bigscroll.bin differ diff --git a/test/roms/bankswitching/BUS/test4_xevious.bin b/test/roms/bankswitching/BUS/test4_xevious.bin new file mode 100644 index 000000000..464e38747 Binary files /dev/null and b/test/roms/bankswitching/BUS/test4_xevious.bin differ diff --git a/test/roms/bankswitching/BUS/test_20161231.bin b/test/roms/bankswitching/BUS/test_20161231.bin new file mode 100644 index 000000000..718695adf Binary files /dev/null and b/test/roms/bankswitching/BUS/test_20161231.bin differ diff --git a/test/roms/bankswitching/CDF/cdf0_fast.bin b/test/roms/bankswitching/CDF/cdf0_fast.bin new file mode 100644 index 000000000..2c14cbba1 Binary files /dev/null and b/test/roms/bankswitching/CDF/cdf0_fast.bin differ diff --git a/test/roms/bankswitching/CDF/cdf1_music.bin b/test/roms/bankswitching/CDF/cdf1_music.bin new file mode 100644 index 000000000..7ebbd03a4 Binary files /dev/null and b/test/roms/bankswitching/CDF/cdf1_music.bin differ diff --git a/test/roms/bankswitching/CDF/cdf2_speech.bin b/test/roms/bankswitching/CDF/cdf2_speech.bin new file mode 100644 index 000000000..e18dd3f0b Binary files /dev/null and b/test/roms/bankswitching/CDF/cdf2_speech.bin differ diff --git a/test/roms/bankswitching/CDF/cdf3_fastjump.bin b/test/roms/bankswitching/CDF/cdf3_fastjump.bin new file mode 100644 index 000000000..9c37d5862 Binary files /dev/null and b/test/roms/bankswitching/CDF/cdf3_fastjump.bin differ diff --git a/test/roms/bankswitching/CDF/draconian_20170309.bin b/test/roms/bankswitching/CDF/draconian_20170309.bin new file mode 100644 index 000000000..11bc211d3 Binary files /dev/null and b/test/roms/bankswitching/CDF/draconian_20170309.bin differ diff --git a/test/roms/bankswitching/CDF/draconian_20170314.bin b/test/roms/bankswitching/CDF/draconian_20170314.bin new file mode 100644 index 000000000..ffda5825a Binary files /dev/null and b/test/roms/bankswitching/CDF/draconian_20170314.bin differ diff --git a/test/roms/bankswitching/CDF/draconian_20170318.bin b/test/roms/bankswitching/CDF/draconian_20170318.bin new file mode 100644 index 000000000..21b667dbf Binary files /dev/null and b/test/roms/bankswitching/CDF/draconian_20170318.bin differ diff --git a/test/roms/bankswitching/CM/CompuMate (1983) (Spectravideo).bin b/test/roms/bankswitching/CM/CompuMate (1983) (Spectravideo).bin new file mode 100644 index 000000000..564751370 Binary files /dev/null and b/test/roms/bankswitching/CM/CompuMate (1983) (Spectravideo).bin differ diff --git a/test/roms/bankswitching/CTY/chetiry_NTSC_STELLA.bin b/test/roms/bankswitching/CTY/chetiry_NTSC_STELLA.bin new file mode 100644 index 000000000..9760390cf Binary files /dev/null and b/test/roms/bankswitching/CTY/chetiry_NTSC_STELLA.bin differ diff --git a/test/roms/bankswitching/CV/ColorGotcha2600.bin b/test/roms/bankswitching/CV/ColorGotcha2600.bin new file mode 100644 index 000000000..318a2d41d Binary files /dev/null and b/test/roms/bankswitching/CV/ColorGotcha2600.bin differ diff --git a/test/roms/bankswitching/CV/Magicard (CommaVid).a26 b/test/roms/bankswitching/CV/Magicard (CommaVid).a26 new file mode 100644 index 000000000..d4c7f5b21 Binary files /dev/null and b/test/roms/bankswitching/CV/Magicard (CommaVid).a26 differ diff --git a/test/roms/bankswitching/CV/Magicard Sample Program 1 - Display of Character Set $f100 (1982) (CommaVid).a26 b/test/roms/bankswitching/CV/Magicard Sample Program 1 - Display of Character Set $f100 (1982) (CommaVid).a26 new file mode 100644 index 000000000..6eafcbe51 Binary files /dev/null and b/test/roms/bankswitching/CV/Magicard Sample Program 1 - Display of Character Set $f100 (1982) (CommaVid).a26 differ diff --git a/test/roms/bankswitching/CV/Magicard Sample Program 2 - Memo Pad $f100 (1982) (CommaVid).a26 b/test/roms/bankswitching/CV/Magicard Sample Program 2 - Memo Pad $f100 (1982) (CommaVid).a26 new file mode 100644 index 000000000..14bfe5df1 Binary files /dev/null and b/test/roms/bankswitching/CV/Magicard Sample Program 2 - Memo Pad $f100 (1982) (CommaVid).a26 differ diff --git a/test/roms/bankswitching/CV/Magicard Sample Program 3 - Target Practice $f100 (1982) (CommaVid).a26 b/test/roms/bankswitching/CV/Magicard Sample Program 3 - Target Practice $f100 (1982) (CommaVid).a26 new file mode 100644 index 000000000..e650f2ce4 Binary files /dev/null and b/test/roms/bankswitching/CV/Magicard Sample Program 3 - Target Practice $f100 (1982) (CommaVid).a26 differ diff --git a/test/roms/bankswitching/CV/Magicard Sample Program 4 - Generating Your Own Display $f200 (1982) (CommaVid).a26 b/test/roms/bankswitching/CV/Magicard Sample Program 4 - Generating Your Own Display $f200 (1982) (CommaVid).a26 new file mode 100644 index 000000000..25a2264d5 Binary files /dev/null and b/test/roms/bankswitching/CV/Magicard Sample Program 4 - Generating Your Own Display $f200 (1982) (CommaVid).a26 differ diff --git a/test/roms/bankswitching/CV/Magicard Sample Program 5 - Life $f15e (1982) (CommaVid).a26 b/test/roms/bankswitching/CV/Magicard Sample Program 5 - Life $f15e (1982) (CommaVid).a26 new file mode 100644 index 000000000..3cf466119 Binary files /dev/null and b/test/roms/bankswitching/CV/Magicard Sample Program 5 - Life $f15e (1982) (CommaVid).a26 differ diff --git a/test/roms/bankswitching/CV/Video Life (CommaVid).a26 b/test/roms/bankswitching/CV/Video Life (CommaVid).a26 new file mode 100644 index 000000000..4e880920f Binary files /dev/null and b/test/roms/bankswitching/CV/Video Life (CommaVid).a26 differ diff --git a/test/roms/bankswitching/DF/DF_128k_test.bin b/test/roms/bankswitching/DF/DF_128k_test.bin new file mode 100644 index 000000000..8678c2029 Binary files /dev/null and b/test/roms/bankswitching/DF/DF_128k_test.bin differ diff --git a/test/roms/bankswitching/DFSC/DFSC_128k_ramtest.bin b/test/roms/bankswitching/DFSC/DFSC_128k_ramtest.bin new file mode 100644 index 000000000..d94f6536b Binary files /dev/null and b/test/roms/bankswitching/DFSC/DFSC_128k_ramtest.bin differ diff --git a/test/roms/bankswitching/DFSC/penult-demo-9-NTSC.bin b/test/roms/bankswitching/DFSC/penult-demo-9-NTSC.bin new file mode 100644 index 000000000..164bb0838 Binary files /dev/null and b/test/roms/bankswitching/DFSC/penult-demo-9-NTSC.bin differ diff --git a/test/roms/bankswitching/DPC+/Space Rocks (2012-11-29) (NTSC) (Encore).bin b/test/roms/bankswitching/DPC+/Space Rocks (2012-11-29) (NTSC) (Encore).bin new file mode 100644 index 000000000..6a71c28d8 Binary files /dev/null and b/test/roms/bankswitching/DPC+/Space Rocks (2012-11-29) (NTSC) (Encore).bin differ diff --git a/test/roms/bankswitching/DPC/Pitfall II (1984) (Activision).bin b/test/roms/bankswitching/DPC/Pitfall II (1984) (Activision).bin new file mode 100644 index 000000000..9d0c0dd6e Binary files /dev/null and b/test/roms/bankswitching/DPC/Pitfall II (1984) (Activision).bin differ diff --git a/test/roms/bankswitching/E0/Frogger II (1984) (Parker Bros) (PAL).a26 b/test/roms/bankswitching/E0/Frogger II (1984) (Parker Bros) (PAL).a26 new file mode 100644 index 000000000..0b88a15f1 Binary files /dev/null and b/test/roms/bankswitching/E0/Frogger II (1984) (Parker Bros) (PAL).a26 differ diff --git a/test/roms/bankswitching/E0/Frogger II (1984) (Parker Bros).a26 b/test/roms/bankswitching/E0/Frogger II (1984) (Parker Bros).a26 new file mode 100644 index 000000000..9789bbe69 Binary files /dev/null and b/test/roms/bankswitching/E0/Frogger II (1984) (Parker Bros).a26 differ diff --git a/test/roms/bankswitching/E0/Gyruss (1984) (Parker Bros).a26 b/test/roms/bankswitching/E0/Gyruss (1984) (Parker Bros).a26 new file mode 100644 index 000000000..653a6c7e5 Binary files /dev/null and b/test/roms/bankswitching/E0/Gyruss (1984) (Parker Bros).a26 differ diff --git a/test/roms/bankswitching/E0/James Bond 007 (1983) (Parker Bros).a26 b/test/roms/bankswitching/E0/James Bond 007 (1983) (Parker Bros).a26 new file mode 100644 index 000000000..a9aa5fc39 Binary files /dev/null and b/test/roms/bankswitching/E0/James Bond 007 (1983) (Parker Bros).a26 differ diff --git a/test/roms/bankswitching/E0/Lord of the Rings (1983) (Parker Bros) (Prototype).a26 b/test/roms/bankswitching/E0/Lord of the Rings (1983) (Parker Bros) (Prototype).a26 new file mode 100644 index 000000000..5b0b9e4f8 Binary files /dev/null and b/test/roms/bankswitching/E0/Lord of the Rings (1983) (Parker Bros) (Prototype).a26 differ diff --git a/test/roms/bankswitching/E0/Montezuma's Revenge (1984) (Parker Bros).a26 b/test/roms/bankswitching/E0/Montezuma's Revenge (1984) (Parker Bros).a26 new file mode 100644 index 000000000..858309a4e Binary files /dev/null and b/test/roms/bankswitching/E0/Montezuma's Revenge (1984) (Parker Bros).a26 differ diff --git a/test/roms/bankswitching/E0/Montezuma's Revenge (Thomas Jentzsch) (PAL60).a26 b/test/roms/bankswitching/E0/Montezuma's Revenge (Thomas Jentzsch) (PAL60).a26 new file mode 100644 index 000000000..bc2b7397c Binary files /dev/null and b/test/roms/bankswitching/E0/Montezuma's Revenge (Thomas Jentzsch) (PAL60).a26 differ diff --git a/test/roms/bankswitching/E0/Mr. Do!'s Castle (1984) (Parker Bros).a26 b/test/roms/bankswitching/E0/Mr. Do!'s Castle (1984) (Parker Bros).a26 new file mode 100644 index 000000000..d6e316ca6 Binary files /dev/null and b/test/roms/bankswitching/E0/Mr. Do!'s Castle (1984) (Parker Bros).a26 differ diff --git a/test/roms/bankswitching/E0/Popeye (1983) (Parker Bros) (PAL).a26 b/test/roms/bankswitching/E0/Popeye (1983) (Parker Bros) (PAL).a26 new file mode 100644 index 000000000..3b44e7e41 Binary files /dev/null and b/test/roms/bankswitching/E0/Popeye (1983) (Parker Bros) (PAL).a26 differ diff --git a/test/roms/bankswitching/E0/Popeye (1983) (Parker Bros).a26 b/test/roms/bankswitching/E0/Popeye (1983) (Parker Bros).a26 new file mode 100644 index 000000000..cedefbdce Binary files /dev/null and b/test/roms/bankswitching/E0/Popeye (1983) (Parker Bros).a26 differ diff --git a/test/roms/bankswitching/E0/Q-bert's Qubes (1984) (Parker Bros).a26 b/test/roms/bankswitching/E0/Q-bert's Qubes (1984) (Parker Bros).a26 new file mode 100644 index 000000000..6d991eb05 Binary files /dev/null and b/test/roms/bankswitching/E0/Q-bert's Qubes (1984) (Parker Bros).a26 differ diff --git a/test/roms/bankswitching/E0/Star Wars - Death Star Battle (1983) (Parker Bros) (PAL).a26 b/test/roms/bankswitching/E0/Star Wars - Death Star Battle (1983) (Parker Bros) (PAL).a26 new file mode 100644 index 000000000..9a0369738 Binary files /dev/null and b/test/roms/bankswitching/E0/Star Wars - Death Star Battle (1983) (Parker Bros) (PAL).a26 differ diff --git a/test/roms/bankswitching/E0/Star Wars - Death Star Battle (1983) (Parker Bros).a26 b/test/roms/bankswitching/E0/Star Wars - Death Star Battle (1983) (Parker Bros).a26 new file mode 100644 index 000000000..d651e3b0a Binary files /dev/null and b/test/roms/bankswitching/E0/Star Wars - Death Star Battle (1983) (Parker Bros).a26 differ diff --git a/test/roms/bankswitching/E0/Star Wars - Ewok Adventure (1983) (Parker Bros) (Prototype) (PAL).a26 b/test/roms/bankswitching/E0/Star Wars - Ewok Adventure (1983) (Parker Bros) (Prototype) (PAL).a26 new file mode 100644 index 000000000..249824a24 Binary files /dev/null and b/test/roms/bankswitching/E0/Star Wars - Ewok Adventure (1983) (Parker Bros) (Prototype) (PAL).a26 differ diff --git a/test/roms/bankswitching/E0/Star Wars - Ewok Adventure (Thomas Jentzsch) (Prototype).a26 b/test/roms/bankswitching/E0/Star Wars - Ewok Adventure (Thomas Jentzsch) (Prototype).a26 new file mode 100644 index 000000000..c4c659f8f Binary files /dev/null and b/test/roms/bankswitching/E0/Star Wars - Ewok Adventure (Thomas Jentzsch) (Prototype).a26 differ diff --git a/test/roms/bankswitching/E0/Star Wars - The Arcade Game (01-03-1984) (Parker Bros) (Prototype).a26 b/test/roms/bankswitching/E0/Star Wars - The Arcade Game (01-03-1984) (Parker Bros) (Prototype).a26 new file mode 100644 index 000000000..4dcc1f846 Binary files /dev/null and b/test/roms/bankswitching/E0/Star Wars - The Arcade Game (01-03-1984) (Parker Bros) (Prototype).a26 differ diff --git a/test/roms/bankswitching/E0/Star Wars - The Arcade Game (04-05-1984) (Parker Bros) (Prototype) (8K).a26 b/test/roms/bankswitching/E0/Star Wars - The Arcade Game (04-05-1984) (Parker Bros) (Prototype) (8K).a26 new file mode 100644 index 000000000..9c9249836 Binary files /dev/null and b/test/roms/bankswitching/E0/Star Wars - The Arcade Game (04-05-1984) (Parker Bros) (Prototype) (8K).a26 differ diff --git a/test/roms/bankswitching/E0/Star Wars - The Arcade Game (12-05-1983) (Parker Bros) (Prototype).a26 b/test/roms/bankswitching/E0/Star Wars - The Arcade Game (12-05-1983) (Parker Bros) (Prototype).a26 new file mode 100644 index 000000000..807f50d70 Binary files /dev/null and b/test/roms/bankswitching/E0/Star Wars - The Arcade Game (12-05-1983) (Parker Bros) (Prototype).a26 differ diff --git a/test/roms/bankswitching/E0/Star Wars - The Arcade Game (12-15-1983) (Parker Bros) (Prototype).a26 b/test/roms/bankswitching/E0/Star Wars - The Arcade Game (12-15-1983) (Parker Bros) (Prototype).a26 new file mode 100644 index 000000000..5ac517497 Binary files /dev/null and b/test/roms/bankswitching/E0/Star Wars - The Arcade Game (12-15-1983) (Parker Bros) (Prototype).a26 differ diff --git a/test/roms/bankswitching/E0/Star Wars - The Arcade Game (12-23-1983) (Parker Bros) (Prototype).a26 b/test/roms/bankswitching/E0/Star Wars - The Arcade Game (12-23-1983) (Parker Bros) (Prototype).a26 new file mode 100644 index 000000000..3c266fdc3 Binary files /dev/null and b/test/roms/bankswitching/E0/Star Wars - The Arcade Game (12-23-1983) (Parker Bros) (Prototype).a26 differ diff --git a/test/roms/bankswitching/E0/Star Wars - The Arcade Game (1984) (Parker Bros) (PAL).a26 b/test/roms/bankswitching/E0/Star Wars - The Arcade Game (1984) (Parker Bros) (PAL).a26 new file mode 100644 index 000000000..679a98c9c Binary files /dev/null and b/test/roms/bankswitching/E0/Star Wars - The Arcade Game (1984) (Parker Bros) (PAL).a26 differ diff --git a/test/roms/bankswitching/E0/Star Wars - The Arcade Game (1984) (Parker Bros).a26 b/test/roms/bankswitching/E0/Star Wars - The Arcade Game (1984) (Parker Bros).a26 new file mode 100644 index 000000000..c03bda5bf Binary files /dev/null and b/test/roms/bankswitching/E0/Star Wars - The Arcade Game (1984) (Parker Bros).a26 differ diff --git a/test/roms/bankswitching/E0/Super Cobra (1982) (Parker Bros) (PAL).a26 b/test/roms/bankswitching/E0/Super Cobra (1982) (Parker Bros) (PAL).a26 new file mode 100644 index 000000000..5ea207651 Binary files /dev/null and b/test/roms/bankswitching/E0/Super Cobra (1982) (Parker Bros) (PAL).a26 differ diff --git a/test/roms/bankswitching/E0/Super Cobra (1982) (Parker Bros).a26 b/test/roms/bankswitching/E0/Super Cobra (1982) (Parker Bros).a26 new file mode 100644 index 000000000..96132d050 Binary files /dev/null and b/test/roms/bankswitching/E0/Super Cobra (1982) (Parker Bros).a26 differ diff --git a/test/roms/bankswitching/E0/Tooth Protectors (1983) (DSD-Camelot).a26 b/test/roms/bankswitching/E0/Tooth Protectors (1983) (DSD-Camelot).a26 new file mode 100644 index 000000000..d38ecf7e2 Binary files /dev/null and b/test/roms/bankswitching/E0/Tooth Protectors (1983) (DSD-Camelot).a26 differ diff --git a/test/roms/bankswitching/E0/Tutankham (1983) (Parker Bros) (PAL).a26 b/test/roms/bankswitching/E0/Tutankham (1983) (Parker Bros) (PAL).a26 new file mode 100644 index 000000000..5ab6e6fbe Binary files /dev/null and b/test/roms/bankswitching/E0/Tutankham (1983) (Parker Bros) (PAL).a26 differ diff --git a/test/roms/bankswitching/E0/Tutankham (1983) (Parker Bros).a26 b/test/roms/bankswitching/E0/Tutankham (1983) (Parker Bros).a26 new file mode 100644 index 000000000..cbe3f285f Binary files /dev/null and b/test/roms/bankswitching/E0/Tutankham (1983) (Parker Bros).a26 differ diff --git a/test/roms/bankswitching/E0/bad/Q-bert's Qubes (1983) (Parker Bros).a26 b/test/roms/bankswitching/E0/bad/Q-bert's Qubes (1983) (Parker Bros).a26 new file mode 100644 index 000000000..de43788f1 Binary files /dev/null and b/test/roms/bankswitching/E0/bad/Q-bert's Qubes (1983) (Parker Bros).a26 differ diff --git a/test/roms/bankswitching/E0/bad/Star Wars - The Arcade Game (Parker Bros) (Prototype 122283).a26 b/test/roms/bankswitching/E0/bad/Star Wars - The Arcade Game (Parker Bros) (Prototype 122283).a26 new file mode 100644 index 000000000..4d6f16f19 Binary files /dev/null and b/test/roms/bankswitching/E0/bad/Star Wars - The Arcade Game (Parker Bros) (Prototype 122283).a26 differ diff --git a/test/roms/bankswitching/E0/bad/Super Cobra (1982) (Parker Bros) [b1].a26 b/test/roms/bankswitching/E0/bad/Super Cobra (1982) (Parker Bros) [b1].a26 new file mode 100644 index 000000000..30b57793c Binary files /dev/null and b/test/roms/bankswitching/E0/bad/Super Cobra (1982) (Parker Bros) [b1].a26 differ diff --git a/test/roms/bankswitching/E7/Breaking News (2002) (Ric Pryor) (Bump 'n' Jump Hack).a26 b/test/roms/bankswitching/E7/Breaking News (2002) (Ric Pryor) (Bump 'n' Jump Hack).a26 new file mode 100644 index 000000000..77dccc9cd --- /dev/null +++ b/test/roms/bankswitching/E7/Breaking News (2002) (Ric Pryor) (Bump 'n' Jump Hack).a26 @@ -0,0 +1,65 @@ +ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿff~ff$|ff|ff| ±Ûª±Ñ……*…±Ó…±×…ıՅ„ñ٤Ą…††¤ÃˆÖ……*©…………©`…Ñ…Ó…Õ…×…Ù…Û©ý…Ò…Ô…Ö…Ø…Ú…Ü……*`¢……*¹Œð•ÑÈèèàÐô……*¹Œð•ÑÈèèà Ðô……*©ðLñ`…*¢þêê……*Lò…°……*†Â©Äΰð±Ê…¦¦Ñ©•€ˆÄϰ±Ì…©¦¦Ó•€¦Â…,……*©Äΰ±Ê…ˆÄϰ±Ì…©Äΰ±Ê„¨¥ã…+ð-%É……*„¨¥ê…±Å…±Ç…©¤ÂˆÄϰ±Ì…ÀðOÆã©Lò¥ä%é……*„¨¥ë…±å…±ç…©¤ÂˆÄϰ±Ì…Àð ©Æä……*Äΰ±Êð…ˆÄϰ±Ì…ðnLVñ¦áÐ`L +óL%ó…µ³ªµ‡…Îðò†Ñµ™…ʈÄÏ𠩈Р±Ì…ˆ±Ì…Ä……*©…µ€)Éz¥Ä…µ“…µ€… )é8éÐü………*µ«…©……Ä…,L•ñµ³ªµ‡…Ïð‰†Ó©Äΰ±Êˆ……*…µ€)ÉCµ™…Ì¥µ“…µ€…!)ééÐü………*µ«…Äΰ±Ê…ˆ…,Lžñéü…¥Ä…µ“…µ€… L|ò8éÐü…µ™…̵“…µ€…!LÉò……*©Äΰ±Ê…ˆÄϰ±Ì…ÊÐæ`ˆÐL$òÄϰ±Ì…Ð…ÏLVñ……*ÊÐù`……*¥Ãê¥ê…êêê êˆÐü……©…%…&©À… ©Ð…!©……*………… … ©€…… ©………+……*¢ :ó  ñ……* ¾ð¥ñ¢Ù Öô……*©,……¢ :ó ¾ð©…Ê…Ë…Ì¥ñÐ ©…Ê……*Lúóø¥ËeñæÊ……*eñæÊeñæÊeñæÊeñæÊ…ËØ……*¢ :ó  ñ……*©€…… ¾ð……*¥Ê¢Ñ Öô……*¥Ë¢Õ Öô……*¥Ì¢Ù Öô……*¢`©ÅÑІÑÅÓІÓÅÕІՅ…*©,…… ¾ð  ñ……*¢ + :ó©€…… ¾ð¥ði…ÃJJJJª¥Ã)É +0¥Ãi…Ãø¥Ã}ñôآم…* Öô……*©,……¢ :ó ¾ð¥ð)ª¼¶ð½ºð…… ñ¢ :ó……* ¾ð¢ :ó`……*…ÃJJJJ¨¹i÷•¥Ã)¨¹i÷•`$06BHT`frx… „ „©À… ©Ð…!ê êˆÐü……©…%…&¢©……*…………… … ©Š ©………+……*©… …±Ûª±Ñ……*…±Ó…±×…ıՅ„ñ٤Ą…††¤ÃˆÖ……*©…%…&……©D…… ¢……*¹Mý……¥âj°8íh÷ûê¥â††ˆàLÅõ8íh÷û¥âê¥â††ˆË……*©……¥î)ª½i÷…Ñ¥íJJJJª½i÷…Ó¥í)ª½i÷…Õ……*©… …©… ©…!©……ê……©…©…©¶…… ……*©… …©ý…Ø…Ú¢`¥ó¢R†×¢`¥î)ð¥¿)ð¢Y†Ù©…!……*±Ñ…±Ó…±×ª­h÷ê±Õ…©,…†±Ù……+©¶…ˆÖ……*©…………Ä¢þµ³ªµ³…×¥ÎÉÿЩ)…Ù +…ÕI¨……*µ™™Ê–јJ¨µ‡™Îµ“™µ«™µ€™Â¦×˜¤ÕÅÙÐÕ……*¥Â)¨¥Â¥Â… ˆÐý………*¥Ã)¨¥Ã¥Ã…!ˆÐý…„ ……*¤ãȘ%ɨ±Å…±Ç…©…%…,©…&©¡åᨅ+……*©… …¥òÉðð É0ÉÐLBó……*©… ¥ê…¥áÉðÄϰ±Ì…¥áÉð +Éð LòL–ñL‡ñLVñLÜñ`#*18?ððð`ýFýýýý`ý¥øÆø……¢ ½y÷•ÑÊø`©Š……©ý…Ò…Ô…Ö…Ø…Ú…Ü¥Ý¢Ñ Úô¥Þ¢Õ Úô¥ß¢ÙLÚôCopyright 1983 Mattel Dave Akers Jeff Ratcliff Pat Dulong €€€BBBG€€€JNEEEŠ +€€€BBBG€€Š + + +JNNRT€‚„„@@OO€€€BBBC€‚„„@@OO„„BBBC€‚€€ + + + +–BBG€€@@SRER € + + +Š–VNSEEERTE€„„€@@SRER€€@@SE € €€Š + + + + + + + +VASEŽ„ŠŠŠVAGCIIICBBGCHCCIICC@€ + + + +–NNSEER€ ŠVASRERRTRŠ +Š–NNSRR €@@GCIIIIC€€€BBS€„€BBGCHCCIICC@€@@SE€€@@SE€€@@SREEETEŠ + + +Š + +–NNC€@@G€€„„@@SRERRTRUR@BG€€@@S€ €BBS€€‚‚‚BBG‚@@G€Š + + +€–VNSEEEETEUE€€€BBG€ € € €BBGÀ@G€‚‚‚‚B@SEE€€ + + +€€@@SEEERTRR€BBGHHCC IIICQC€„„„––D@SRERRTRUREERRTR€ €€B@SEER€ü@üü@üü@üJüJü„ü@üËünüÿ„¾ü@üJüEüÿ„Jünüÿ„Jü|ü„¿ü4üü0üü0üü0ü¶ü4ü¿ü¿ü„Jü|üÿ„JüXüÿ„ËüJü„ËüEüÿ„Ëü|üÿ„ËüXüÿ„¿ü üLtôLêô¥òÉðÐ#­‚)Щ…ò…Ý…Þ…ß…ï…ñ©…à©…âL¯ôLŽþÉL×ô¥«)ðÉððmɰð É€ðºÉPð¹¥€Ð+ ¥ì + +e‡8é¥ȹêɄТL#ô¢ —þ©ð…«©@…¹L^ô¥ ¥ 0­î𢠗þ©€…«©x…¹¥8é…L^ô¥«É ÐLêô©@…“`¥¹ðHƹ©íîLŽþ¥¹Ð%¥i…©P…«©…¹ø­í8é í­îéîØLbôƹ©……«©…ŸLbô…“LbôÆâ¥â©ð…ò©…‡©…â…ê…ë…óLŽþLøô©î© í©P…©"…‡©…«©@…“LŽþ¥¹Ð…«LbôƹLbô¥ð)ª½µö…ý½ö…Ĥïðˆ±Ã0ˆÐù„ï±Ã)…ó + + +¨¢¹ó•ÅÈèàÐõ¹ó¦ðÐJi²LFõ¥ð)yóª½Åö…ê©-…ì…ã…ä ©¢™«–‡™ŸˆÐõLÍô¥ò0LÉÐLö¥«Éðð? ¥îР¥íÉP¥¿)Ð+¥íÉu0ÈLžõÉð¥íÉ20Ȅ¥á8åÂ0…áL´õi…áL¿õ¥ì…ã©0…äLðþæì ¹«)É_ ¹‡8陇ÈÀÐé¥ìÉ0ðL´õ©…ã…ì¥ê…ëæï¥ïÉ`Ð;©…ï…óæð¥ðÉcЩ…ð©ÿ…ò©…ô…ö©…÷…õL´õ©…ò¢øµÝuÊ•ÝÊ÷Ø©…ñ¥ð)ª½µö…ѽ½ö…Ò¤ï±Ñ)…ó + + +i…Ó©ió…Ô ¹Å™å±Ó™Åˆò ±ÓɄЅêLŠö¦ðÐJi²Lˆö¥ð)qÓª½Åö…êø©eß…ß©eÞ…Þ©eÝ…ÝÅà0¥ài…à¥âÉðæâØL´õ`À €à@ ðððñññòòDà*ุ4©…Ä¢þµ³ªµ³¨Éÿð+¹‡ð&µ‡Éð 8ù‡É °µ8ù…ÃIÿiÉ0˜ªLÖö¥Äð¢L—þ`µ«0í¹«0è©`Õ«ðÙ«ð©PÕ«ðÙ«ðLR÷©°™«L÷©À™«L÷©°•«L÷©À•«L÷µ«àðÉ ð¹«ÀðÉ ðLÄ÷©…Ä© •«™«„µ¥)¨¹ô÷•¹¤Â†Â¹¥)ª½ô÷™¹¦Â©ú™Ÿ©•Ÿ¥Ã0µ¥) P•¥¹¥)  Lé÷µ¥)  •¥¹¥) PLé÷¥Ã0µ¥) (•¥¹¥) ØLé÷µ¥) Ø•¥¹¥) (™¥˜ªµ³¨L÷-¥òÉððÉТ††`Æõð ¥òÐLcòÆ÷ð,`¦ô¥òð0½òLFð½IòLFð­‚)Щ…Lcò½"ñ _ðLð¦ö¥ò0½1òL[ð½Vò Æð`ÉÿÐ(¢¥òð†††ò†ô†öÉÿÐæò©<…õ…÷`†ô©…õhhL#ð…Ã)ª½ñ¨)…˜0©Ð© …¥ÃJJJJÉ¢ð +¤òð¢ Т†ª½ñ…õæô`ÉÿТ††`…Ã)ª½ñ¨)…˜0©Ð© …¥ÃJJJJɢ𢠆ª½ñ…÷æö` :8b †‡ˆ‹Œ›4ðD9ðI5ðEx4ðD9ðI5ðE|4ðD9ðI5ðEDH7ðGBGDGbðBvHCwDFxEGt9ðID7ðGEH6ðFGCA@‚ðR#xS&dðT(w+)$'X'v*%(&4ðD8ðH7ðGyHEDHDEy4ðð$9ð ð)HDu4ðð$9ð ð)DEy4ðð$9ð ð)(&s4ðð$9ð ð)HDu4ðð$9ð ð)+)u4ðð$9ð ð)$'v3ðð#7ðð'##bðBCHGCFDHFGEDGDyrFH3ðCGDvHE7ðGDIÿ4à”à”à444ਤ¨4à”à”à´àð¤ÿ7à—à—à777ধ¦7à—à—à·àð§ÿÇ7¦§¨Ç¤¨¦§¨×ÿÄ4¨¤¥Ä©¥¨¤¥Ôÿ¥öЩ…À©…©…¦î½{ò…`Æ÷Ðæö¦ö½°òÉÿЩ……ö`½vó…÷¦ö½°ò…½ó¨)…˜JJJJ…` +  + + + ÿÿÿ ÿÿÿÿO@O@O@O@O@O@O@O@O@O@O@O@O@O@O@O@O@O@ÿ‰ŠŠ‰…ƒÿ…‰‰…ÿÅÅÅEEEEÅÿMÿ‡‰ŠŒ‹Š‰ˆ‡†…ƒ‚ÿ…‡‰ŠŒ‹Š‰ˆ‡†…„ƒ‚ÿ + + +ÿÿÿÿÿ +ÿ +ÿ¥ó)…ó)@ð¥¿)Ð¥ÀÉ𢠗þ¥ó €…ó¢¥êÉ„ð¥ëɄТÿ†Ó¥³Ð`¢þ µ³™×ȪÀÐõ¥ðð nö „¹תµ¥)…ÄàÐLõ¥òLˆõµ«ð +Éð7LæõL6öµ‡ÉÐL"õµ¹Ð"¥îЩ•¥L€ô¥ÄÉРµ‡8几û”Ÿ©ÿ•¹Ö¹µ€Ð (µÉT°ÉG°L›ôÉ` Ø”¥¥Ó¥Â)ðLˆõ©p•«Lõµ«Éð—¥ïÉ^Lˆõ¤ÂÀ0 ¹Õ¨¹‡8õ‡É°¹ŸÉìð©ú•Ÿ©•¹Lõ¤ÂÀ%¹Ù¨µ‡8ù‡É°¥îЩР¹ŸÉð©•Ÿ©•¹µ¥Ä•¥¤ÂÈÀðL)ô`Še¿)ÐQ¤×¹‡É–°H¥êÉ„ðB©àð µEÁ)i…Ĩ¹£ö•“¥îÐ àð#©•‡©Lkõ©­•‡©P•©ð•Ÿ©ÿ•¹ Ø¥¿0 (”¥Lõ©•¥•Ÿµ¹Ð©•‡©•¥•Ÿ©•«LõÖ¹©•¥•Ÿµ‡0ãÉ0ßLõàðµ€ÐQ©ÿ…ø©øeÞ…Þ©eÝ…Ý¥ñÉ™ði…ñ؆â —þ¦Ã©ð•«©@•¹LõÉðð”Épð­É ðºÉ°ðÜÉÀðºÉ`ð5ÉðLõµ¹ðÖ¹Lõ (¹¥0 Ø˜•¥L’õµ¹ð Ö¹©•¥•ŸLõ©•«LõL›õ¥¾ðƾLõ©ô…˜¤Ü©`™«¥Œ8陇¥’™©™¥©™“…°©…Œ©ü…¤Lõ¥°Éð.¥ŒÉ‚Ð(¤Ü¹‡ÉЩ™«©#™¹©…°©…˜©…¾©…¤¢L—þ`ü²š€ô!ñáÑÁ±¡‘rbRB2"òâÒ²¢’scSC3#óãÓó£“tdTD4$ôäÔÄ´¤”ueUE5%õåÕŵ¥•vfVF6&öæÖƶ¦–wgWG7'÷ç×Ç·§—xhXH8(øèØÈ¸¨˜yiYI9) ùšššššššš ¹«)pÉ_° ¹‡8åᙇÈÀðL1÷©ú…Ë…Í ¹«Éðй¹JJJª½µþ™“¢¹¹É轺÷L‰÷¹¥) +ª¥¿9´÷Ðè½¼÷8ù‡™™ˆÀ­«É…Э¹ÉZÉ0¥‡i…‡©¬8凅™`ÀÔèü8L`t$$$ˆˆÔÔ „±¢ÿ–³¢þˆµ³†Âª¹‡Õ‡ô–³¦Â”³¢þˆé ¢þȵ³ªÐú„Î`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ<~BfZZZfB~~<<~BfZZZfB~~<ff<~~ÿ¥îÐ¥íÉ!ø¥í8é…í¥îé…îØ`¥Ái)…Á¢ à0µŸ +8j8j8j +LeÿJJ)þLeÿµŸIÿieÁJJJJJIÿiL„ÿeÁJJJJJu‡ÝÅÿà àð©L§ÿ½ÅÿL§ÿݹÿÐi•‡Êž¢´¹—ö•€Êö` T®®®®® !"#gýgýgý \ No newline at end of file diff --git a/test/roms/bankswitching/E7/Bump 'n' Jump (1983) (M Network).bin b/test/roms/bankswitching/E7/Bump 'n' Jump (1983) (M Network).bin new file mode 100644 index 000000000..71b4048f8 Binary files /dev/null and b/test/roms/bankswitching/E7/Bump 'n' Jump (1983) (M Network).bin differ diff --git a/test/roms/bankswitching/E7/Bump 'n' Jump (1988) (Telegames) (7045 A015) (PAL).bin b/test/roms/bankswitching/E7/Bump 'n' Jump (1988) (Telegames) (7045 A015) (PAL).bin new file mode 100644 index 000000000..20cfc1551 Binary files /dev/null and b/test/roms/bankswitching/E7/Bump 'n' Jump (1988) (Telegames) (7045 A015) (PAL).bin differ diff --git a/test/roms/bankswitching/E7/Burgertime (1982) (Mattel).a26 b/test/roms/bankswitching/E7/Burgertime (1982) (Mattel).a26 new file mode 100644 index 000000000..65cd1acb2 Binary files /dev/null and b/test/roms/bankswitching/E7/Burgertime (1982) (Mattel).a26 differ diff --git a/test/roms/bankswitching/E7/Masters of the Universe - The Power of He-Man (1983) (Mattel).a26 b/test/roms/bankswitching/E7/Masters of the Universe - The Power of He-Man (1983) (Mattel).a26 new file mode 100644 index 000000000..74a946621 Binary files /dev/null and b/test/roms/bankswitching/E7/Masters of the Universe - The Power of He-Man (1983) (Mattel).a26 differ diff --git a/test/roms/bankswitching/E7/bad/Bump 'N' Jump (1983) (Mattel) [b1].a26 b/test/roms/bankswitching/E7/bad/Bump 'N' Jump (1983) (Mattel) [b1].a26 new file mode 100644 index 000000000..1dc3344de --- /dev/null +++ b/test/roms/bankswitching/E7/bad/Bump 'N' Jump (1983) (Mattel) [b1].a26 @@ -0,0 +1,63 @@ +ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿff~fff~~ff|ff~~f```f~|fffff|~``~``~```|``~~ffn`f~~~~``````BZZ~ffBbfn~vfF~fffff~``~fff~ff|fff~~f~`f~~ ±Ûª±Ñ……*…±Ó…±×…ıՅ„ñ٤Ą…††¤ÃˆÖ……*©…………©`…Ñ…Ó…Õ…×…Ù…Û©ý…Ò…Ô…Ö…Ø…Ú…Ü……*`¢……*¹Œð•ÑÈèèàÐô……*¹Œð•ÑÈèèà Ðô……*©ðLñ`…*¢þêê……*Lò…°……*†Â©Äΰð±Ê…¦¦Ñ©•€ˆÄϰ±Ì…©¦¦Ó•€¦Â…,……*©Äΰ±Ê…ˆÄϰ±Ì…©Äΰ±Ê„¨¥ã…+ð-%É……*„¨¥ê…±Å…±Ç…©¤ÂˆÄϰ±Ì…ÀðOÆã©Lò¥ä%é……*„¨¥ë…±å…±ç…©¤ÂˆÄϰ±Ì…Àð ©Æä……*Äΰ±Êð…ˆÄϰ±Ì…ðnLVñ¦áÐ`L +óL%ó…µ³ªµ‡…Îðò†Ñµ™…ʈÄÏ𠩈Р±Ì…ˆ±Ì…Ä……*©…µ€)Éz¥Ä…µ“…µ€… )é8éÐü………*µ«…©……Ä…,L•ñµ³ªµ‡…Ïð‰†Ó©Äΰ±Êˆ……*…µ€)ÉCµ™…Ì¥µ“…µ€…!)ééÐü………*µ«…Äΰ±Ê…ˆ…,Lžñéü…¥Ä…µ“…µ€… L|ò8éÐü…µ™…̵“…µ€…!LÉò……*©Äΰ±Ê…ˆÄϰ±Ì…ÊÐæ`ˆÐL$òÄϰ±Ì…Ð…ÏLVñ……*ÊÐù`……*¥Ãê¥ê…êêê êˆÐü……©…%…&©À… ©Ð…!©……*………… … ©€…… ©………+……*¢ :ó  ñ……* ¾ð¥ñ¢Ù Öô……*©,……¢ :ó ¾ð©…Ê…Ë…Ì¥ñÐ ©…Ê……*Lúóø¥ËeñæÊ……*eñæÊeñæÊeñæÊeñæÊ…ËØ……*¢ :ó  ñ……*©€…… ¾ð……*¥Ê¢Ñ Öô……*¥Ë¢Õ Öô……*¥Ì¢Ù Öô……*¢`©ÅÑІÑÅÓІÓÅÕІՅ…*©,…… ¾ð  ñ……*¢ + :ó©€…… ¾ð¥ði…ÃJJJJª¥Ã)É +0¥Ãi…Ãø¥Ã}ñôآم…* Öô……*©,……¢ :ó ¾ð¥ð)ª¼¶ð½ºð…… ñ¢ :ó……* ¾ð¢ :ó`……*…ÃJJJJ¨¹i÷•¥Ã)¨¹i÷•`$06BHT`frx… „ „©À… ©Ð…!ê êˆÐü……©…%…&¢©……*…………… … ©Š ©………+……*©… …±Ûª±Ñ……*…±Ó…±×…ıՅ„ñ٤Ą…††¤ÃˆÖ……*©…%…&……©D…… ¢……*¹Mý……¥âj°8íh÷ûê¥â††ˆàLÅõ8íh÷û¥âê¥â††ˆË……*©……¥î)ª½i÷…Ñ¥íJJJJª½i÷…Ó¥í)ª½i÷…Õ……*©… …©… ©…!©……ê……©…©…©¶…… ……*©… …©ý…Ø…Ú¢`¥ó¢R†×¢`¥î)ð¥¿)ð¢Y†Ù©…!……*±Ñ…±Ó…±×ª­h÷ê±Õ…©,…†±Ù……+©¶…ˆÖ……*©…………Ä¢þµ³ªµ³…×¥ÎÉÿЩ)…Ù +…ÕI¨……*µ™™Ê–јJ¨µ‡™Îµ“™µ«™µ€™Â¦×˜¤ÕÅÙÐÕ……*¥Â)¨¥Â¥Â… ˆÐý………*¥Ã)¨¥Ã¥Ã…!ˆÐý…„ ……*¤ãȘ%ɨ±Å…±Ç…©…%…,©…&©¡åᨅ+……*©… …¥òÉðð É0ÉÐLBó……*©… ¥ê…¥áÉðÄϰ±Ì…¥áÉð +Éð LòL–ñL‡ñLVñLÜñ`#*18?ððð`ýFýýýý`ý¥øÆø……¢ ½y÷•ÑÊø`©Š……©ý…Ò…Ô…Ö…Ø…Ú…Ü¥Ý¢Ñ Úô¥Þ¢Õ Úô¥ß¢ÙLÚôCopyright 1983 Mattel Dave Akers Jeff Ratcliff Pat Dulong €€€BBBG€€€JNEEEŠ +€€€BBBG€€Š + + +JNNRT€‚„„@@OO€€€BBBC€‚„„@@OO„„BBBC€‚€€ + + + +–BBG€€@@SRER € + + +Š–VNSEEERTE€„„€@@SRER€€@@SE € €€Š + + + + + + + +VASEŽ„ŠŠŠVAGCIIICBBGCHCCIICC@€ + + + +–NNSEER€ ŠVASRERRTRŠ +Š–NNSRR €@@GCIIIIC€€€BBS€„€BBGCHCCIICC@€@@SE€€@@SE€€@@SREEETEŠ + + +Š + +–NNC€@@G€€„„@@SRERRTRUR@BG€€@@S€ €BBS€€‚‚‚BBG‚@@G€Š + + +€–VNSEEEETEUE€€€BBG€ € € €BBGÀ@G€‚‚‚‚B@SEE€€ + + +€€@@SEEERTRR€BBGHHCC IIICQC€„„„––D@SRERRTRUREERRTR€ €€B@SEER€ü@üü@üü@üJüJü„ü@üËünüÿ„¾ü@üJüEüÿ„Jünüÿ„Jü|ü„¿ü4üü0üü0üü0ü¶ü4ü¿ü¿ü„Jü|üÿ„JüXüÿ„ËüJü„ËüEüÿ„Ëü|üÿ„ËüXüÿ„¿ü üLtôLêô¥òÉðÐ#­‚)Щ…ò…Ý…Þ…ß…ï…ñ©…à©…âL¯ôLŽþÉL×ô¥«)ðÉððmɰð É€ðºÉPð¹¥€Ð+ ¥ì + +e‡8é¥ȹêɄТL#ô¢ —þ©ð…«©@…¹L^ô¥ ¥ 0­î𢠗þ©€…«©x…¹¥8é…L^ô¥«É ÐLêô©@…“`¥¹ðHƹ©íîLŽþ¥¹Ð%¥i…©P…«©…¹ø­í8é í­îéîØLbôƹ©……«©…ŸLbô…“LbôÆâ¥â©ð…ò©…‡©…â…ê…ë…óLŽþLøô©î© í©P…©"…‡©…«©@…“LŽþ¥¹Ð…«LbôƹLbô¥ð)ª½µö…ý½ö…Ĥïðˆ±Ã0ˆÐù„ï±Ã)…ó + + +¨¢¹ó•ÅÈèàÐõ¹ó¦ðÐJi²LFõ¥ð)yóª½Åö…ê©-…ì…ã…ä ©¢™«–‡™ŸˆÐõLÍô¥ò0LÉÐLö¥«Éðð? ¥îР¥íÉP¥¿)Ð+¥íÉu0ÈLžõÉð¥íÉ20Ȅ¥á8åÂ0…áL´õi…áL¿õ¥ì…ã©0…äLðþæì ¹«)É_ ¹‡8陇ÈÀÐé¥ìÉ0ðL´õ©…ã…ì¥ê…ëæï¥ïÉ`Ð;©…ï…óæð¥ðÉcЩ…ð©ÿ…ò©…ô…ö©…÷…õL´õ©…ò¢øµÝuÊ•ÝÊ÷Ø©…ñ¥ð)ª½µö…ѽ½ö…Ò¤ï±Ñ)…ó + + +i…Ó©ió…Ô ¹Å™å±Ó™Åˆò ±ÓɄЅêLŠö¦ðÐJi²Lˆö¥ð)qÓª½Åö…êø©eß…ß©eÞ…Þ©eÝ…ÝÅà0¥ài…à¥âÉðæâØL´õ`À €à@ ðððñññòòDà*ุ4©…Ä¢þµ³ªµ³¨Éÿð+¹‡ð&µ‡Éð 8ù‡É °µ8ù…ÃIÿiÉ0˜ªLÖö¥Äð¢L—þ`µ«0í¹«0è©`Õ«ðÙ«ð©PÕ«ðÙ«ðLR÷©°™«L÷©À™«L÷©°•«L÷©À•«L÷µ«àðÉ ð¹«ÀðÉ ðLÄ÷©…Ä© •«™«„µ¥)¨¹ô÷•¹¤Â†Â¹¥)ª½ô÷™¹¦Â©ú™Ÿ©•Ÿ¥Ã0µ¥) P•¥¹¥)  Lé÷µ¥)  •¥¹¥) PLé÷¥Ã0µ¥) (•¥¹¥) ØLé÷µ¥) Ø•¥¹¥) (™¥˜ªµ³¨L÷-¥òÉððÉТ††`Æõð ¥òÐLcòÆ÷ð,`¦ô¥òð0½òLFð½IòLFð­‚)Щ…Lcò½"ñ _ðLð¦ö¥ò0½1òL[ð½Vò Æð`ÉÿÐ(¢¥òð†††ò†ô†öÉÿÐæò©<…õ…÷`†ô©…õhhL#ð…Ã)ª½ñ¨)…˜0©Ð© …¥ÃJJJJÉ¢ð +¤òð¢ Т†ª½ñ…õæô`ÉÿТ††`…Ã)ª½ñ¨)…˜0©Ð© …¥ÃJJJJɢ𢠆ª½ñ…÷æö` :8b †‡ˆ‹Œ›4ðD9ðI5ðEx4ðD9ðI5ðE|4ðD9ðI5ðEDH7ðGBGDGbðBvHCwDFxEGt9ðID7ðGEH6ðFGCA@‚ðR#xS&dðT(w+)$'X'v*%(&4ðD8ðH7ðGyHEDHDEy4ðð$9ð ð)HDu4ðð$9ð ð)DEy4ðð$9ð ð)(&s4ðð$9ð ð)HDu4ðð$9ð ð)+)u4ðð$9ð ð)$'v3ðð#7ðð'##bðBCHGCFDHFGEDGDyrFH3ðCGDvHE7ðGDIÿ4à”à”à444ਤ¨4à”à”à´àð¤ÿ7à—à—à777ধ¦7à—à—à·àð§ÿÇ7¦§¨Ç¤¨¦§¨×ÿÄ4¨¤¥Ä©¥¨¤¥Ôÿ¥öЩ…À©…©…¦î½{ò…`Æ÷Ðæö¦ö½°òÉÿЩ……ö`½vó…÷¦ö½°ò…½ó¨)…˜JJJJ…` +  + + + ÿÿÿ ÿÿÿÿO@O@O@O@O@O@O@O@O@O@O@O@O@O@O@O@O@O@ÿ‰ŠŠ‰…ƒÿ…‰‰…ÿÅÅÅEEEEÅÿMÿ‡‰ŠŒ‹Š‰ˆ‡†…ƒ‚ÿ…‡‰ŠŒ‹Š‰ˆ‡†…„ƒ‚ÿ + + +ÿÿÿÿÿ +ÿ +ÿ¥ó)…ó)@ð¥¿)Ð¥ÀÉ𢠗þ¥ó €…ó¢¥êÉ„ð¥ëɄТÿ†Ó¥³Ð`¢þ µ³™×ȪÀÐõ¥ðð nö „¹תµ¥)…ÄàÐLõ¥òLˆõµ«ð +Éð7LæõL6öµ‡ÉÐL"õµ¹Ð"¥îЩ•¥L€ô¥ÄÉРµ‡8几û”Ÿ©ÿ•¹Ö¹µ€Ð (µÉT°ÉG°L›ôÉ` Ø”¥¥Ó¥Â)ðLˆõ©p•«Lõµ«Éð—¥ïÉ^Lˆõ¤ÂÀ0 ¹Õ¨¹‡8õ‡É°¹ŸÉìð©ú•Ÿ©•¹Lõ¤ÂÀ%¹Ù¨µ‡8ù‡É°¥îЩР¹ŸÉð©•Ÿ©•¹µ¥Ä•¥¤ÂÈÀðL)ô`Še¿)ÐQ¤×¹‡É–°H¥êÉ„ðB©àð µEÁ)i…Ĩ¹£ö•“¥îÐ àð#©•‡©Lkõ©­•‡©P•©ð•Ÿ©ÿ•¹ Ø¥¿0 (”¥Lõ©•¥•Ÿµ¹Ð©•‡©•¥•Ÿ©•«LõÖ¹©•¥•Ÿµ‡0ãÉ0ßLõàðµ€ÐQ©ÿ…ø©øeÞ…Þ©eÝ…Ý¥ñÉ™ði…ñ؆â —þ¦Ã©ð•«©@•¹LõÉðð”Épð­É ðºÉ°ðÜÉÀðºÉ`ð5ÉðLõµ¹ðÖ¹Lõ (¹¥0 Ø˜•¥L’õµ¹ð Ö¹©•¥•ŸLõ©•«LõL›õ¥¾ðƾLõ©ô…˜¤Ü©`™«¥Œ8陇¥’™©™¥©™“…°©…Œ©ü…¤Lõ¥°Éð.¥ŒÉ‚Ð(¤Ü¹‡ÉЩ™«©#™¹©…°©…˜©…¾©…¤¢L—þ`ü²š€ô!ñáÑÁ±¡‘rbRB2"òâÒ²¢’scSC3#óãÓó£“tdTD4$ôäÔÄ´¤”ueUE5%õåÕŵ¥•vfVF6&öæÖƶ¦–wgWG7'÷ç×Ç·§—xhXH8(øèØÈ¸¨˜yiYI9) ùšššššššš ¹«)pÉ_° ¹‡8åᙇÈÀðL1÷©ú…Ë…Í ¹«Éðй¹JJJª½µþ™“¢¹¹É轺÷L‰÷¹¥) +ª¥¿9´÷Ðè½¼÷8ù‡™™ˆÀ­«É…Э¹ÉZÉ0¥‡i…‡©¬8凅™`ÀÔèü8L`t$$$ˆˆÔÔ „±¢ÿ–³¢þˆµ³†Âª¹‡Õ‡ô–³¦Â”³¢þˆé ¢þȵ³ªÐú„Î`ÿÿý`Ÿím©Êœ{@ÞÞäÿèvÖô®ò8×hûHkéÞØÝê'ºù@’@ß²¿Ýn@üêûí½2Î_®ÿ}]·Ñ}¯Iüw¿ôÿ2âþIùSÿó÷Y¿ýÿ€dŽ¿ÏÞyÙÕçùÿß~|í{îøˆ:ßÂÿ9o€«¯ûÅÿ³î}Þ>þv¿_ÿ€OüzÿϽýÿý¯õ÷(e#¿oÿÚïÍ[ó€ýÿ|ï§§€€ÆÿÝ·òöÿ¿5ߢ#òôÿoû"ýÿ?ÿê÷„cÿIÿŸo5·VÏý¿ÙºÏÝûß&ÿäÞ—ëùÿÆÞ÷Ùš¿[‹Ùá€vopïŸý7_¼²¿ú@?²Ü¹öüüwwJå·ÿ½çkCç\&ÿÎä×ïÿîùT^ÔþŸ®~þþþr‘úÙ÷„¯™¾Þþüýç÷®‰ßüõÿ¹üû¾½¾ÏÿTÿžÿ@vÿ&ý]ŸÐûþÿuï í¾®z™ÿÑïí÷7û €÷ûaÝïûPYïç¾ÿÿþëoþÿÝûÿïõý@yûZýŸîßßþ÷·ÿ€ÿ¹´ÿÿ­Ñ¿\Nè> íÿ³Î÷÷aÿø¿ˆgùFáÓÿ§nSs~7ý»½ûÉÿ¨_–Oýâï«ÿžß»ÿ[òý¯×æÿj½•ý~ÿ;ûnì¿T»ï.ÏŸñëxjÿ÷÷W¿yŸe}@˵ýÿoÿ {gÿûï@•÷ûûÿïÿvæwõ€}ÿÙ8ìÿæß–ïûææÏ}sŸÿ"úûõÿôÿûÕ·Ýgoßtüôÿïþ÷þÿþÿ¯véÿA÷ ÿàÿ€ÕÿÖßÃÿvsaîØî¶ÿÿ]sÿýþwìý æyÿMw€óÿÿÿûÿÿÿS½@é÷]¼÷ˆ‡ß0äuñÛï²÷¿ÿ +2·½þ¯Þz•ï½×íÿ@î®–æ½_Çüï>¿·¿:š™Ï¶þ ûý›û«~ûÿò÷Úý<¿ú ÿû^wÞ;÷¹Ÿöïÿÿ|ßdäçö@vÿ|¦ÿ¤SùÛÿo»ËÿüËŠ¿ {þ÷ÿGÙE6m÷ï[·ûwè§ïÿÜù¾ÿYÞ½ÿÛ½ç—ß¿ÿy·ýNýÍ¿îû?þµþŸÇúÛþïŸý¿õúvÿ—ÿ}ÍÞ¿Ÿÿ ~o­ÿïˆc-Ìöûgý†vóýâÏtcsÉ5¾ô¿jÊ7ÞËïN¯©àñÓÛ€ZËûôwÝ>ÿÎõÎÿ@×ÇÇËÂû5ϠʼnÍDÈû½¶ôõ –½ÿê® {lŸ®Ç_ÿ?ïŸíso×s®}__÷ï»÷½þýÿ¦ÿÂÚäü ïîyoÓßìYÿúÏß(«lU]>ÿ@Ïßßʆÿ²­Ú÷å1iå|ÿÆ>׿þû·w/_Ùü_ÿ‹5úï®ï¿·«î@»ÿþ×>ÿRï·Ë|ÿ|Rtó£þûñ ÷þÿÿÝüßÞ}­Ó ª&…›þ Où,·¿}øßŸ»ÿ¼ßÈËýdû—º÷ÞöþOy=ûæ÷û­+û XþËÿö–€Öw}ÇûþëÉò×êÜ÷³õ¯¨ý)ÿÞ /÷ûï}ç½û,ßÎÞw·íÿýþfþRý-ÝÿØïý÷eoíßé{ ®»ÛI¾ÿÿNÿýÝwýã×ùÛA[÷o}¯Rûöÿ}hrŸÏ­½ë€ëyÏýÿ ßûív«ïP‚®=]¿ú¬ÿ÷ÿ@óëÿÿKÿÛãºÿ·ÿþ}û8ÿ0YßÝ8ÿ¨úËÿ€wÿ½–£ÿAšY?hïÑû>÷ÉÆÛ˜@Kw{ýÅê¨ÿïÿûÿ.·½÷êÿ¯ÿÌ~Ýõ„Ã~Ì]¿ä$ŒówwÚïëŸ½Û³ß }ÿúÿwÿ·ï™×ùýPØ»ÿ¿ß`Ë÷Óÿ4õöÿ÷Ýý^ûߟïK»ûWk¾ÿºîÿ$ý÷øüþ: ¹ùÿî|û¢ÿÇrÿu¿_ÿ¦ÿþïoÚ³ù÷{€?ï»ÿ„ÿ@OßÐÿ¾Þ÷ÿ÷ëŸ(ÿ÷ÿßÚ½¿;ÿŽþC´ÿî¿ýüÛõÌ÷4î +ÏtÛ}¯Ž¾C¸-÷Éýïýÿw7ßD>»ó€€µ7¿¾ñÿÆ=ÙVùÿ—ÿûÿÿ¾€ìïÉíßÚü?Mëóf@ÿ°Ïëëïíÿ®ü-ÿî¿Ò÷V/òç÷þ {í÷þûúúŸßÿÿÿ ÷ÿKÿðþµÎÈÿýâˆÿ®ùýÿ€Ý¾ÎÍË»…ÿí÷W¾ÿ o÷úº¾æ ˧®ÿzæ½ÿmÿÝýÙîîþýߟÿãz²ÿò{öw§ÿùù‡Þxþ­íßÿ^ïŒü›ÿ]3îFï »[ßïGo \ No newline at end of file diff --git a/test/roms/bankswitching/E7/fruitcake29Oct09.bin b/test/roms/bankswitching/E7/fruitcake29Oct09.bin new file mode 100644 index 000000000..6516164fa Binary files /dev/null and b/test/roms/bankswitching/E7/fruitcake29Oct09.bin differ diff --git a/test/roms/bankswitching/E7/fruitcake_22Oct09.bin b/test/roms/bankswitching/E7/fruitcake_22Oct09.bin new file mode 100644 index 000000000..456c2a6e2 Binary files /dev/null and b/test/roms/bankswitching/E7/fruitcake_22Oct09.bin differ diff --git a/test/roms/bankswitching/EF/64kbb.EFEF_tagged.bin b/test/roms/bankswitching/EF/64kbb.EFEF_tagged.bin new file mode 100644 index 000000000..1c574f8f9 Binary files /dev/null and b/test/roms/bankswitching/EF/64kbb.EFEF_tagged.bin differ diff --git a/test/roms/bankswitching/EF/EF.bin b/test/roms/bankswitching/EF/EF.bin new file mode 100644 index 000000000..8038b3c55 Binary files /dev/null and b/test/roms/bankswitching/EF/EF.bin differ diff --git a/test/roms/bankswitching/EF/RobotZed_07.bas.bin b/test/roms/bankswitching/EF/RobotZed_07.bas.bin new file mode 100644 index 000000000..5ea0d9953 Binary files /dev/null and b/test/roms/bankswitching/EF/RobotZed_07.bas.bin differ diff --git a/test/roms/bankswitching/EF/Zippy_V2_FINAL_NTSC.bas.bin b/test/roms/bankswitching/EF/Zippy_V2_FINAL_NTSC.bas.bin new file mode 100644 index 000000000..3ca1a12ac Binary files /dev/null and b/test/roms/bankswitching/EF/Zippy_V2_FINAL_NTSC.bas.bin differ diff --git a/test/roms/bankswitching/EF/megaboyEF.bin b/test/roms/bankswitching/EF/megaboyEF.bin new file mode 100644 index 000000000..cb8cde22a Binary files /dev/null and b/test/roms/bankswitching/EF/megaboyEF.bin differ diff --git a/test/roms/bankswitching/EFSC/64kSC.EFSC_tagged.bin b/test/roms/bankswitching/EFSC/64kSC.EFSC_tagged.bin new file mode 100644 index 000000000..89fb09f12 Binary files /dev/null and b/test/roms/bankswitching/EFSC/64kSC.EFSC_tagged.bin differ diff --git a/test/roms/bankswitching/EFSC/Distopia0.95_NTSC.bas.bin b/test/roms/bankswitching/EFSC/Distopia0.95_NTSC.bas.bin new file mode 100644 index 000000000..ff4edd986 Binary files /dev/null and b/test/roms/bankswitching/EFSC/Distopia0.95_NTSC.bas.bin differ diff --git a/test/roms/bankswitching/EFSC/EFSC.bin b/test/roms/bankswitching/EFSC/EFSC.bin new file mode 100644 index 000000000..8025a8145 Binary files /dev/null and b/test/roms/bankswitching/EFSC/EFSC.bin differ diff --git a/test/roms/bankswitching/F0/F0_MegaBoy.bin b/test/roms/bankswitching/F0/F0_MegaBoy.bin new file mode 100644 index 000000000..d4ec07408 Binary files /dev/null and b/test/roms/bankswitching/F0/F0_MegaBoy.bin differ diff --git a/test/roms/bankswitching/F0/MegaBoy (Dynacom).bin b/test/roms/bankswitching/F0/MegaBoy (Dynacom).bin new file mode 100644 index 000000000..5dcf295cf Binary files /dev/null and b/test/roms/bankswitching/F0/MegaBoy (Dynacom).bin differ diff --git a/test/roms/bankswitching/F4/AVGN KO Boxing 2009-09-06 NTSC.bin b/test/roms/bankswitching/F4/AVGN KO Boxing 2009-09-06 NTSC.bin new file mode 100644 index 000000000..64ac6011b Binary files /dev/null and b/test/roms/bankswitching/F4/AVGN KO Boxing 2009-09-06 NTSC.bin differ diff --git a/test/roms/bankswitching/F4/Asteroids Attack (2020) (Demo, Game Select).bin b/test/roms/bankswitching/F4/Asteroids Attack (2020) (Demo, Game Select).bin new file mode 100644 index 000000000..af58232bb Binary files /dev/null and b/test/roms/bankswitching/F4/Asteroids Attack (2020) (Demo, Game Select).bin differ diff --git a/test/roms/bankswitching/F4SC/Fatal Run (NTSC prototype).bin b/test/roms/bankswitching/F4SC/Fatal Run (NTSC prototype).bin new file mode 100644 index 000000000..d936473e6 Binary files /dev/null and b/test/roms/bankswitching/F4SC/Fatal Run (NTSC prototype).bin differ diff --git a/test/roms/bankswitching/F6/Acid Drop (1992) (Salu) (PAL) [!].a26 b/test/roms/bankswitching/F6/Acid Drop (1992) (Salu) (PAL) [!].a26 new file mode 100644 index 000000000..2f0d2767d Binary files /dev/null and b/test/roms/bankswitching/F6/Acid Drop (1992) (Salu) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F6/Battlezone TC by Thomas Jentzsch (2 joystick Hack).a26 b/test/roms/bankswitching/F6/Battlezone TC by Thomas Jentzsch (2 joystick Hack).a26 new file mode 100644 index 000000000..8f97a3a0e Binary files /dev/null and b/test/roms/bankswitching/F6/Battlezone TC by Thomas Jentzsch (2 joystick Hack).a26 differ diff --git a/test/roms/bankswitching/F6/California Games (1988) (Epyx) (PAL) [!].a26 b/test/roms/bankswitching/F6/California Games (1988) (Epyx) (PAL) [!].a26 new file mode 100644 index 000000000..cf76cd7b1 Binary files /dev/null and b/test/roms/bankswitching/F6/California Games (1988) (Epyx) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F6/California Games (1988) (Epyx) [!].a26 b/test/roms/bankswitching/F6/California Games (1988) (Epyx) [!].a26 new file mode 100644 index 000000000..1024f14a0 Binary files /dev/null and b/test/roms/bankswitching/F6/California Games (1988) (Epyx) [!].a26 differ diff --git a/test/roms/bankswitching/F6/Double Dragon (1989) (Activision) (PAL) [!].a26 b/test/roms/bankswitching/F6/Double Dragon (1989) (Activision) (PAL) [!].a26 new file mode 100644 index 000000000..1d0afb0c4 Binary files /dev/null and b/test/roms/bankswitching/F6/Double Dragon (1989) (Activision) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F6/Double Dragon (1989) (Activision).a26 b/test/roms/bankswitching/F6/Double Dragon (1989) (Activision).a26 new file mode 100644 index 000000000..4f75f5a00 Binary files /dev/null and b/test/roms/bankswitching/F6/Double Dragon (1989) (Activision).a26 differ diff --git a/test/roms/bankswitching/F6/Garfield (Prototype).a26 b/test/roms/bankswitching/F6/Garfield (Prototype).a26 new file mode 100644 index 000000000..3b0e268d7 --- /dev/null +++ b/test/roms/bankswitching/F6/Garfield (Prototype).a26 @@ -0,0 +1,23 @@ +­ùÿ¥¼°Ž©€…ƒ…Ñ©……©ƒ…§©¦…©ð…‚Ì🩀……›…œ……Ž…©¤…‰¢À…‚ÊÐû©½…©ð…‚Ì🥪©ÿЈ©………©…ª©Ú…©ð…‚Ì🀀€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€­ö쀭÷¿ì€­øßì€ùÿ쀀€€€€ð€ð€ð­ùÿ¥¼°Ž©€…ƒ…Ñ©……©ƒ…§©¦…©ð…‚Ìð¿©€……›…œ……Ž…©„…‰¢À…‚ÊÐû©½…©ð…‚Ì𿥪©ÿЈ©€……©…ª©Ú…©ð…‚Ìð¿€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€­ö쀭÷¿ì€­øßì€ùÿ쀀€€€€ð€ð€ð­ùÿ©…………©…¥Õ… ©… + }Ó¢  ãÒ¢ Ô¥Õ… ©… ¸Ò ªÓ _Ò ÚÒ ÔLðß©………¥Õ… ©… +¢ ½ÇÓ•´Êø©'…… ¶Ô…¢ …¹Û…¹¡Ú…¹²Ú…ÈÊÐë¢ Ô t yÔ¢ Ô¢ …¹Û…¹öÚ…¹Û…ÈÊÐë ÔLðß©………© +…… ¶Ô¢W Ô  yÔ¢V Ô ÔLðß©…¥Õ… }Ó¢d ÓÓ¦œ ïÓ©¯…Š¥ÑÐ¥†)@ЩLþЩ… ©%…Ù¢ Ô¢  !Ô¥Ó… OÓ……, ªÓ _Ò ÚÒ ÔLðß©…¥Õ… ¢ Ô¥†) Щ?LDÑ©[…Š…¥†)@Щ…ÙL]Ñ©…Ù©0…Š¥Ó…¢l ÓÓ¦œ ïÓ OÓ©…… ¢  OÔ¥ÙÉð +¥7©…¢…Ú¢Q ÓÓ¢Q ïÓ¥Ó…©ÿ… ©…Ø©……©$…¥†)hЩL¸Ñ© +…¢$ ©…À Þ…©… òÝ ûÝ¢ ( .Þ àÝ éÝ žÒ ÚÒ ÔLðß©…¥Õ… ¸Ò ªÓ¢  òÝ ûÝ¥†)@ЩLÒ©… .Þ àÝ é݆¥‹…‰¥Œ…Š ¸Ò ªÓ¢  òÝ ûÝ¥†)PЩLAÒ©… .Þ àÝ é݆¢ Ô žÒ ÚÒ ÔLðߥɅÁ¢ ƒ ZÞ¢…¥É…©ÿ……ˆÊÐð Ô¢ + ZÞ¢…¥É…©ÿ……ˆÊÐð Ô¢ ZÞ¢ …¹Û…¹öÚ…¹Û…ÈÊÐë`¥Ó…¦ ÓÓ¦œ ïÓ ÍÒ OÓ`¥Ð¥×ð©… ` ùÒ¢ Ô`…¹9Û…¹)Û…¹1Û…ÈÊÐë`…¤§¹ê×Éÿð…¢< ïÓ©…¥§É°©…—©'…¢ …¤ˆ±«…ðƈ¥—Щ…ÊÐé©ÿ…—…©…¥˜…… ¶Ô  yÔ`©'…¢$©äÙ°¤Š±­ðÆŠäš……±¯…¤‰°±«…ðƉÊÐÙ`©…Á© …À¦À½AÛ…“½MÛ…”½YÛ…•½eÛ…–¦À¼qÛ¢ ZÞÆÀÐÜ`©…“©…”©…•©…–`êÙùÙÚÚ&Ú5Ú2Õ§Õ×u×ÙuÙ8¥Žé…޽=ß)¨½=ß…ˆý……!…`½=ß)¨½=ß…ˆý…… ……*`…©………ˆ`…ÊÐû`©½…©ð…‚`©… +…©… ……êêêêêêêê¹åÚ…¹¡Ú…¹²Ú…ÈÊÐÛ©… +`©… +…¹åÚ…¹¡Ú…¹²Ú…êêêêêê¹ÃÚ…¹ÔÚ…ÈÊÐÛ`„À¤À±´……±¶…±¸…±º…Á±¼ª±¾¨¥Á…†„„ÆÀØ©……………%…&……`…© „ ………%…&„„„„„!ê……©ð… ……*`"MQQM"?;3766<|næÆÏÏÆÀÀæ~~6LBBNRRRL Ëì÷ûüþþokI{Ž ///?ogW\MWW_;o_?>>>? ;++=>/75 mýÍÍí}= Íûs `cs:¼„6vn^>H¶ËµµJ66ÆÎ\=!lnvz|mÓ­­RllHvöèüœlìÜØpP0ppðpppp0<ÜÞ.~rlnv6xpþ’’’’’’’’’þ’’’’’’’’’þllllllllllllllllll`—”d’’aðøøìÞ^oo·÷{;]oç÷ûøüýÿÿÿÿÿÿÿüãÏ¿?ÿÿÿÿÿÿñäÎΰ¹;ÇïOÿÿÿÿÿÿÿø÷OŸø÷¯_ß¾¼€¿¿¿¿¿ßßO ¼¯Õêer9Ïφ†††Ïö¶6‰‰É‰‰‰†?}~~||¼Øæÿÿ£¼?ÿÿÿÿÿÿÿÿ?¿ßÏîåó÷ûûýþ?ŸÏïòùÿÿÿÿÿÿÿÿÿü{§ß/pßßÞ\ïïïÏÏßß–„yïoõzý^ÏÇÁ<=››99ÀÀؘÿÿlÈÈlþþþlŒˆ‹Ž6/?2hØØ88||TªTªT(GG((((2222ÖÖÖØÖØÖØÖ$$$'$$$û ÐØÔ´tÌøà€€À@€ÀÀààààððððððààøüüþþ~¾Ž¼¼xøðððà Àððøðx¼ÜÌüü\ ØØXÐØØÐØ0`ÀÀÀà` ` `ààóûÛƒƒûû‹›ó㥥¥¹¥¥¥9;wffffv~> @@@ „Ì´„„„„AAB|BB|„„„ü„H0BBB~B$(D‚‚‚‚þ@ þ~@@~@@~~@@|@@~xDBBBDxff<?ÿ€Ààðøüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ56789:;<%&'()*+,ÿ?ÿÿÿÿÿÿÿÿÿþüøðàÀ€ÒÓÔÕÖרÙÂÃÄÅÆÇÈÉ +ƒÇïÿ?ÿ..,*(&$‰†‰†‰†‰†‰††üxè4ô”èxüÿ~JUUUJ~ÿïï÷÷ßßûûlŒˆ‹Ž6/?2hØØïï÷÷ßßûû61ÑqlôüL ªªªªþþªªªªªªªªªªªþþªªª(€ÿÿÿÿÿÿ~ÿÿÿÿÿÿÿ~PXȰúú~|6/:55:-% xxxüü  +Xx xXØ8 8Ø22222222GG((((22222222GG((((DDDDDDDDDDD&&&""×&&&222222×××××ש…­©Ü…®`©ð…¯©Ü…°`©…­©Ø…®`©0…¯©Ø…°`…¥À… ¹zÖ…¹žÖ…êêêêêêêêêêê¥Ø… ÈÊÐÝ…©… `…±­…±¯…¥“……꥔……꥕……꥖……ˆÊÐ׆ `©… …±­…¥Á…꥓……꥔……꥕……꥖……ˆÊÐÖ`3#óãÓ󣓃dTD4$ôäÔÄ´¤”„eUE5%õåÕŵ¥•…fVF6&öæÖƶ¦–†gWG7'÷ç×Ç·§—‡hXH8(øèØÈ¸¨˜ˆiYI9) ùéÙɹ©™‰jZJ:* +úêÚʺªšŠk[K;+ ûëÛË»«›‹l\L<, üìÜ̼¬œŒm]M=- ýíÝͽ­­öŸl­÷¿l­øßl­ùÿlððð­ùÿØx¢ÿš¢ÿ©•ÊÐû©…ƒ………  ù Zø©…„©…‡…ª©……………¢†©,–­‚)ð+­‚)ÐB¥‡ÉÐ +¥ I… ©?…‡¥ Ð ZøLið iø©……Lwð ~ø©……©…ƒ…§…Ä…Æ……©…„…ªL’𩅇Ƈ¥…)ª½³ù…½ºù…‚LÆÿ­„Ðû¥…)ª½Áù…½Èù…‚LÆÿ…©…©#–¥…)ª½Ïù…½Öù…‚LÆÿ­„ÐûæÏÐæÐæ†æªL)ð¥ƒÐ ù­‚)ÐL‘ò­‚)ÐL‘ò¥¤ðF¥¤É~Щ…Â¥¤ÉP©…Ó¥Âð+8¥œé…œ©…ÂLNñ¥¤ÉOЩ…©…Ó¥Â𠥜i…œ©…ÂL‘ò¦Ì¥œÝ•öÐ /õ¢¥Ë=ËöÐ:¢¥Ë=ËöÐ% ø0 ¢¥ËËö…Ë¥Öð ¥ÌÉð ÆÌL”ñ¥ÌÉ ðæÌ¦Ì¥œÝ•öÐ Fõ Yõ©…Ù¥œÉÐ ñô¥ÍÉ +ðæÍ ñôLÂñ©…Í¥¨iÔ…¨°L‘ò¦Ì¥œÝ•öð¥¨iÔ…¨¥ÒÐ¥ÖðÆœæ™Lóñæœæ™©…È¥×ðE¦ÈµÉЦͼñö¦ÈµöˆÐû¦ÈµÉ$¦È©Œ•¥†E¦È•‘¦È¥=Óö…¥ØðU¥×I…×L‰òL‚ò¦ÈµÉЦͼñö¦ÈµÖˆÐû¦ÈµÉ°$¦È©•¥†E¦È•‘¦È¥=Óö…¥Øð¥×I…×L‰ò©…ØLò©…ØÆœæœ­€ ø)ð.­€ ø) ð$­€ ø)@ð ­€ ø)€ðLÉòæ¥Ð©…LÓò¥†©……†¦È¥=ËöÐ;¥¢¦ÈݯöÐ2¦È¥Ëö…¦ÈµŠÝÁöð ¦Í½ö¼-ö ªø Ý÷¦È¥¡Ëö…¡¦Í½#÷¦È•¦È¥=ËöÐ'¦Í½æö)ЦͽFö¦È•Š…ŒLó¦È½·ö•Š…Œ©…Ló¦ÈµŠÝÁöð*¦È֦ȵð¦Í½_ö¦È•Š…ŒLó¦È¥¡=Óö…¡©¦È•L…ó©…£©…¤ ê÷©¦È•Š…Œ¥£ÐLÀ󯤥¤Ð&¥§ðƧ¥§Ð©…Ÿ ñô 3ø© …Š©ÿ…©…©…£…˦ÆÐ ó÷½÷…½<÷…¦ÆðÆÆ¥ÃÉЦÄÐ ø½t÷…© …ÆÄ¥ÖЩLúó©-…‰…‹…ˆ¥šÉ¦Í½æö)€ð¥ŠÉ›Ð ¥ÖЩEL ô©]…‰¥§Ð. 3ø¥¤Ð'©…‰…‹…ˆ¥©Ð©…ª…†©…©¥ª)ÿЩ……©…ª ˆø¥Ÿð¥„Ð Cø¥ƒð©…„¥Ÿð „ÀLvô „À¥ÑÐ0¥ÍÉÐ*¥œÉ\Ð$©…υЩ…Ñ…Ò þ÷ø¥³e³…³¥²e²…²¥±e±…±Ø¥Ñð)春Ð)ð!©…Ò…Ñ ñô©'…˜¢¥Ë=Óö…ËæÍ©…ɤÀL×ô Ýø©…¢¦Í¥§ð½Ûö……½©ö…ÉL¦ð©…œ©…š©…Ì…©…×…©…`­€ ø)@Ð¥œÉÆœ­€ ø)€Ð¥œÉ„°æœ`­€)@Щ…Ö­€)€Ð©…Ö`© +…™ ø ¢¥Ë=Óö…Ë`¢¥Ë=ËöÐ2¢¥Ë=ËöÐ *øÐ$¢¥ËËö…Ë¥™ðÆ™L”õ *øð ¢¥Ë=Óö…Ë© +…™¦™½xö…š` ˆøL¦ð¥7©…¢……,LÚð¥ª)ÿЦͽÛö……©… ©…ª¢ ½èù•´ÊøLÚð¥ª)ÿÐ%©……… ø ó÷ Mø©…œ©…š©…ª¢ ½èù•´ÊøLÚð……,¥Úð©Ø…Š©…ÚLÚðPPPPPPPPPPPPPPPPPPPPP›››››››››››››››››››››››››####  "$$" "2DTdt„2ÿääääääääääØØØØØØØØØØ€@ ¿ßï÷ûýþ€€€€‡‡C!ÿ‡C!ÿ‡C! !"#$%&'()*+,*)('&%$#!  + ()*+,()*+,()*+,()*+,()*+,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýüûúùø÷öõôóòñðïîíìëêéèçæåäãâáàßÞÝÜÛÚÙØ×¥¨iÔ…¨`¥£Ð©…Æ© …`©9…Æ©…`©…………Æ`©…é*…Ä©ÿ…`©…………Ã`¥Ÿð¥=`¥<`¦Ÿð8****`­€ ø) `¥Ÿð© …˜LBø©'…˜`©’©…ž©…©……’…‘©…`©…¶…³©…²…±… `©…¶…³©…±…²…´…µ©… `¢©•±Êù`­‚)ð¥<0©…ƒ…„…Ñ©…… ~ø©…§ ù`¦±øe³…³˜e²…²¥±i…±Øä±ð榦ͥ¦Ý +÷Щ…¦¥§Éðæ§ þ÷`© +…À¹±)ª½Ýù¦À•´ÆÀÆÀ¹±JJJJª½Ýù¦À•´ÆÀˆÆÀÙ¢µ´ÉDÐ +©”•´èèà +ð` Cø©…Â…Ú…Ö…Ó…Ò©…Í©…Ñ…Ì…Ë…Ï…Ð…†…©………Ä…Æ………¢…£…¤…¦…¥…Ÿ…×…Ù ø ó÷©…Ô©…É©…Õ©…œ© +…™©…š©'…˜©…©ÿ…—©…‰©…ˆ©…«©Ö…¬©…­©Ü…®©ð…¯©Ü…°¢ ½èù•´Êø©…ƒ`œììììõððð°ðBÔ* êÐÐÐѰѱ¢¢DD¢õõõö°õDLT\dlt|„Œ””ÚœÚÚžÚŸÚ Ú¥‚)ðÉðð"¥‚)ÐÉÐð¥‚)°É°ð­öŸl­÷¿l­øßl­ùÿlððð \ No newline at end of file diff --git a/test/roms/bankswitching/F6/H.E.R.O. (1984) (Activision) [o1].a26 b/test/roms/bankswitching/F6/H.E.R.O. (1984) (Activision) [o1].a26 new file mode 100644 index 000000000..1e6c425f7 Binary files /dev/null and b/test/roms/bankswitching/F6/H.E.R.O. (1984) (Activision) [o1].a26 differ diff --git a/test/roms/bankswitching/F6/Midnight Magic (1984) (Atari) (PAL) [!].a26 b/test/roms/bankswitching/F6/Midnight Magic (1984) (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..2b2a74845 Binary files /dev/null and b/test/roms/bankswitching/F6/Midnight Magic (1984) (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F6/Midnight Magic (1984) (Atari).a26 b/test/roms/bankswitching/F6/Midnight Magic (1984) (Atari).a26 new file mode 100644 index 000000000..e20291c04 Binary files /dev/null and b/test/roms/bankswitching/F6/Midnight Magic (1984) (Atari).a26 differ diff --git a/test/roms/bankswitching/F6/Pete Rose Baseball (1988) (Absolute).a26 b/test/roms/bankswitching/F6/Pete Rose Baseball (1988) (Absolute).a26 new file mode 100644 index 000000000..b3fcc7982 Binary files /dev/null and b/test/roms/bankswitching/F6/Pete Rose Baseball (1988) (Absolute).a26 differ diff --git a/test/roms/bankswitching/F6/Rampage! (1989) (Activision) (PAL) [!].a26 b/test/roms/bankswitching/F6/Rampage! (1989) (Activision) (PAL) [!].a26 new file mode 100644 index 000000000..34c279f4a Binary files /dev/null and b/test/roms/bankswitching/F6/Rampage! (1989) (Activision) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F6/Rampage! (1989) (Activision) [!].a26 b/test/roms/bankswitching/F6/Rampage! (1989) (Activision) [!].a26 new file mode 100644 index 000000000..927dde5ed Binary files /dev/null and b/test/roms/bankswitching/F6/Rampage! (1989) (Activision) [!].a26 differ diff --git a/test/roms/bankswitching/F6/Rampage! (1989) (Activision) [b1].a26 b/test/roms/bankswitching/F6/Rampage! (1989) (Activision) [b1].a26 new file mode 100644 index 000000000..c9effb11b Binary files /dev/null and b/test/roms/bankswitching/F6/Rampage! (1989) (Activision) [b1].a26 differ diff --git a/test/roms/bankswitching/F6/RealSports Boxing (1987) (Atari) (PAL) [!].a26 b/test/roms/bankswitching/F6/RealSports Boxing (1987) (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..f25030fce Binary files /dev/null and b/test/roms/bankswitching/F6/RealSports Boxing (1987) (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F6/RealSports Boxing (1987) (Atari).a26 b/test/roms/bankswitching/F6/RealSports Boxing (1987) (Atari).a26 new file mode 100644 index 000000000..d51abf023 Binary files /dev/null and b/test/roms/bankswitching/F6/RealSports Boxing (1987) (Atari).a26 differ diff --git a/test/roms/bankswitching/F6/Sentinel (1990) (Atari).a26 b/test/roms/bankswitching/F6/Sentinel (1990) (Atari).a26 new file mode 100644 index 000000000..b0647c5b5 Binary files /dev/null and b/test/roms/bankswitching/F6/Sentinel (1990) (Atari).a26 differ diff --git a/test/roms/bankswitching/F6/Solaris (1986) (Atari) (PAL) [!].a26 b/test/roms/bankswitching/F6/Solaris (1986) (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..57d3132d9 Binary files /dev/null and b/test/roms/bankswitching/F6/Solaris (1986) (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F6/Solaris (1986) (Atari).a26 b/test/roms/bankswitching/F6/Solaris (1986) (Atari).a26 new file mode 100644 index 000000000..0ed3d8d3d Binary files /dev/null and b/test/roms/bankswitching/F6/Solaris (1986) (Atari).a26 differ diff --git a/test/roms/bankswitching/F6/Summer Games (1987) (Epyx) (PAL) [!].a26 b/test/roms/bankswitching/F6/Summer Games (1987) (Epyx) (PAL) [!].a26 new file mode 100644 index 000000000..d1c1ed725 Binary files /dev/null and b/test/roms/bankswitching/F6/Summer Games (1987) (Epyx) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F6/Summer Games (1987) (Epyx).a26 b/test/roms/bankswitching/F6/Summer Games (1987) (Epyx).a26 new file mode 100644 index 000000000..3e0946eda Binary files /dev/null and b/test/roms/bankswitching/F6/Summer Games (1987) (Epyx).a26 differ diff --git a/test/roms/bankswitching/F6/Super Box (CCE).a26 b/test/roms/bankswitching/F6/Super Box (CCE).a26 new file mode 100644 index 000000000..d3269e859 Binary files /dev/null and b/test/roms/bankswitching/F6/Super Box (CCE).a26 differ diff --git a/test/roms/bankswitching/F6/Tomcat - The F-14 Flight Simulator (1988) (Absolute) [!].a26 b/test/roms/bankswitching/F6/Tomcat - The F-14 Flight Simulator (1988) (Absolute) [!].a26 new file mode 100644 index 000000000..a02dc8763 Binary files /dev/null and b/test/roms/bankswitching/F6/Tomcat - The F-14 Flight Simulator (1988) (Absolute) [!].a26 differ diff --git a/test/roms/bankswitching/F6/Tomcat - The F-14 Flight Simulator (1988) (Absolute) [a1].a26 b/test/roms/bankswitching/F6/Tomcat - The F-14 Flight Simulator (1988) (Absolute) [a1].a26 new file mode 100644 index 000000000..a81368dd3 Binary files /dev/null and b/test/roms/bankswitching/F6/Tomcat - The F-14 Flight Simulator (1988) (Absolute) [a1].a26 differ diff --git a/test/roms/bankswitching/F6/Tomcat - The F-14 Flight Simulator (2002) (Skyworks) [!].a26 b/test/roms/bankswitching/F6/Tomcat - The F-14 Flight Simulator (2002) (Skyworks) [!].a26 new file mode 100644 index 000000000..93b762782 Binary files /dev/null and b/test/roms/bankswitching/F6/Tomcat - The F-14 Flight Simulator (2002) (Skyworks) [!].a26 differ diff --git a/test/roms/bankswitching/F6/bad/Acid Drop (1992) (Salu) (PAL) [b1].a26 b/test/roms/bankswitching/F6/bad/Acid Drop (1992) (Salu) (PAL) [b1].a26 new file mode 100644 index 000000000..c9ffe790e Binary files /dev/null and b/test/roms/bankswitching/F6/bad/Acid Drop (1992) (Salu) (PAL) [b1].a26 differ diff --git a/test/roms/bankswitching/F6SC/Crystal Castles (1984) (Atari) (NTSC) (Prototype) [!].a26 b/test/roms/bankswitching/F6SC/Crystal Castles (1984) (Atari) (NTSC) (Prototype) [!].a26 new file mode 100644 index 000000000..060b2151e Binary files /dev/null and b/test/roms/bankswitching/F6SC/Crystal Castles (1984) (Atari) (NTSC) (Prototype) [!].a26 differ diff --git a/test/roms/bankswitching/F6SC/Crystal Castles (1984) (Atari) (PAL) [!].a26 b/test/roms/bankswitching/F6SC/Crystal Castles (1984) (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..ecba7c2c2 Binary files /dev/null and b/test/roms/bankswitching/F6SC/Crystal Castles (1984) (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F6SC/Crystal Castles (1984) (Atari) [p1].a26 b/test/roms/bankswitching/F6SC/Crystal Castles (1984) (Atari) [p1].a26 new file mode 100644 index 000000000..9259262e6 Binary files /dev/null and b/test/roms/bankswitching/F6SC/Crystal Castles (1984) (Atari) [p1].a26 differ diff --git a/test/roms/bankswitching/F6SC/Crystal Castles (1984) (Atari).a26 b/test/roms/bankswitching/F6SC/Crystal Castles (1984) (Atari).a26 new file mode 100644 index 000000000..83ca22ee5 Binary files /dev/null and b/test/roms/bankswitching/F6SC/Crystal Castles (1984) (Atari).a26 differ diff --git a/test/roms/bankswitching/F6SC/Dark Chambers (1988) (Atari) (PAL) [!].a26 b/test/roms/bankswitching/F6SC/Dark Chambers (1988) (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..e07cf7f29 Binary files /dev/null and b/test/roms/bankswitching/F6SC/Dark Chambers (1988) (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F6SC/Dark Chambers (1988) (Atari).a26 b/test/roms/bankswitching/F6SC/Dark Chambers (1988) (Atari).a26 new file mode 100644 index 000000000..f7277e85c Binary files /dev/null and b/test/roms/bankswitching/F6SC/Dark Chambers (1988) (Atari).a26 differ diff --git a/test/roms/bankswitching/F6SC/Dig Dug (1983) (Atari).a26 b/test/roms/bankswitching/F6SC/Dig Dug (1983) (Atari).a26 new file mode 100644 index 000000000..ad8e9e639 Binary files /dev/null and b/test/roms/bankswitching/F6SC/Dig Dug (1983) (Atari).a26 differ diff --git a/test/roms/bankswitching/F6SC/Dig Dug (V1) (1983) (Atari) (PAL) [!].a26 b/test/roms/bankswitching/F6SC/Dig Dug (V1) (1983) (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..326a101f6 Binary files /dev/null and b/test/roms/bankswitching/F6SC/Dig Dug (V1) (1983) (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F6SC/Dig Dug (V2) (1983) (Atari) (PAL) [!].a26 b/test/roms/bankswitching/F6SC/Dig Dug (V2) (1983) (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..3ba7706ac Binary files /dev/null and b/test/roms/bankswitching/F6SC/Dig Dug (V2) (1983) (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F6SC/Jr. Pac-Man (1984) (Atari) (PAL) [!].a26 b/test/roms/bankswitching/F6SC/Jr. Pac-Man (1984) (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..59046f136 Binary files /dev/null and b/test/roms/bankswitching/F6SC/Jr. Pac-Man (1984) (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F6SC/Jr. Pac-Man (1984) (Atari) [!].a26 b/test/roms/bankswitching/F6SC/Jr. Pac-Man (1984) (Atari) [!].a26 new file mode 100644 index 000000000..79e4f382f Binary files /dev/null and b/test/roms/bankswitching/F6SC/Jr. Pac-Man (1984) (Atari) [!].a26 differ diff --git a/test/roms/bankswitching/F6SC/Jr. Pac-Man (1986) (Atari) [!].a26 b/test/roms/bankswitching/F6SC/Jr. Pac-Man (1986) (Atari) [!].a26 new file mode 100644 index 000000000..48508e163 Binary files /dev/null and b/test/roms/bankswitching/F6SC/Jr. Pac-Man (1986) (Atari) [!].a26 differ diff --git a/test/roms/bankswitching/F6SC/Millipede (1984) (Atari) (PAL) [!].a26 b/test/roms/bankswitching/F6SC/Millipede (1984) (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..927edf935 Binary files /dev/null and b/test/roms/bankswitching/F6SC/Millipede (1984) (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F6SC/Millipede (1984) (Atari).a26 b/test/roms/bankswitching/F6SC/Millipede (1984) (Atari).a26 new file mode 100644 index 000000000..f2acc1e47 Binary files /dev/null and b/test/roms/bankswitching/F6SC/Millipede (1984) (Atari).a26 differ diff --git a/test/roms/bankswitching/F6SC/Radar Lock (1989) (Atari) (PAL) [!].a26 b/test/roms/bankswitching/F6SC/Radar Lock (1989) (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..f1840a3e2 Binary files /dev/null and b/test/roms/bankswitching/F6SC/Radar Lock (1989) (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F6SC/Radar Lock (1989) (Atari).a26 b/test/roms/bankswitching/F6SC/Radar Lock (1989) (Atari).a26 new file mode 100644 index 000000000..7aafe653a Binary files /dev/null and b/test/roms/bankswitching/F6SC/Radar Lock (1989) (Atari).a26 differ diff --git a/test/roms/bankswitching/F6SC/Secret Quest (1989) (Atari) (PAL) [!].a26 b/test/roms/bankswitching/F6SC/Secret Quest (1989) (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..f190e4f04 Binary files /dev/null and b/test/roms/bankswitching/F6SC/Secret Quest (1989) (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F6SC/Secret Quest (1989) (Atari).a26 b/test/roms/bankswitching/F6SC/Secret Quest (1989) (Atari).a26 new file mode 100644 index 000000000..f25c7fa43 Binary files /dev/null and b/test/roms/bankswitching/F6SC/Secret Quest (1989) (Atari).a26 differ diff --git a/test/roms/bankswitching/F6SC/Sprint Master (1988) (Atari) (PAL) [!].a26 b/test/roms/bankswitching/F6SC/Sprint Master (1988) (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..22556c382 Binary files /dev/null and b/test/roms/bankswitching/F6SC/Sprint Master (1988) (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F6SC/Sprint Master (1988) (Atari).a26 b/test/roms/bankswitching/F6SC/Sprint Master (1988) (Atari).a26 new file mode 100644 index 000000000..648d319d7 Binary files /dev/null and b/test/roms/bankswitching/F6SC/Sprint Master (1988) (Atari).a26 differ diff --git a/test/roms/bankswitching/F6SC/Super Football (1988) (Atari) (PAL) [!].a26 b/test/roms/bankswitching/F6SC/Super Football (1988) (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..67d59ca6d Binary files /dev/null and b/test/roms/bankswitching/F6SC/Super Football (1988) (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F6SC/Super Football (1988) (Atari).a26 b/test/roms/bankswitching/F6SC/Super Football (1988) (Atari).a26 new file mode 100644 index 000000000..62eaae4a4 Binary files /dev/null and b/test/roms/bankswitching/F6SC/Super Football (1988) (Atari).a26 differ diff --git a/test/roms/bankswitching/F8/Aquaventure (1983) (Atari) (Prototype).a26 b/test/roms/bankswitching/F8/Aquaventure (1983) (Atari) (Prototype).a26 new file mode 100644 index 000000000..387de9a39 Binary files /dev/null and b/test/roms/bankswitching/F8/Aquaventure (1983) (Atari) (Prototype).a26 differ diff --git a/test/roms/bankswitching/F8/Aquaventure (CCE).a26 b/test/roms/bankswitching/F8/Aquaventure (CCE).a26 new file mode 100644 index 000000000..81bac0a71 Binary files /dev/null and b/test/roms/bankswitching/F8/Aquaventure (CCE).a26 differ diff --git a/test/roms/bankswitching/F8/Asteroids (1979) (Atari) (PAL) [!].a26 b/test/roms/bankswitching/F8/Asteroids (1979) (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..0ed4431aa Binary files /dev/null and b/test/roms/bankswitching/F8/Asteroids (1979) (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F8/Asteroids (1979) (Atari) (PAL) [a1][!].a26 b/test/roms/bankswitching/F8/Asteroids (1979) (Atari) (PAL) [a1][!].a26 new file mode 100644 index 000000000..0e8d5acf9 Binary files /dev/null and b/test/roms/bankswitching/F8/Asteroids (1979) (Atari) (PAL) [a1][!].a26 differ diff --git a/test/roms/bankswitching/F8/Asteroids (1979) (Atari) (PAL) [a2][!].a26 b/test/roms/bankswitching/F8/Asteroids (1979) (Atari) (PAL) [a2][!].a26 new file mode 100644 index 000000000..f10c499a2 Binary files /dev/null and b/test/roms/bankswitching/F8/Asteroids (1979) (Atari) (PAL) [a2][!].a26 differ diff --git a/test/roms/bankswitching/F8/Asteroids (1979) (Atari) [!].a26 b/test/roms/bankswitching/F8/Asteroids (1979) (Atari) [!].a26 new file mode 100644 index 000000000..73599c405 Binary files /dev/null and b/test/roms/bankswitching/F8/Asteroids (1979) (Atari) [!].a26 differ diff --git a/test/roms/bankswitching/F8/Asteroids (1979) (Atari) [a1][!].a26 b/test/roms/bankswitching/F8/Asteroids (1979) (Atari) [a1][!].a26 new file mode 100644 index 000000000..ccfe4ccbd Binary files /dev/null and b/test/roms/bankswitching/F8/Asteroids (1979) (Atari) [a1][!].a26 differ diff --git a/test/roms/bankswitching/F8/Asteroids 2 (Asteroids Hack).a26 b/test/roms/bankswitching/F8/Asteroids 2 (Asteroids Hack).a26 new file mode 100644 index 000000000..5854105c4 Binary files /dev/null and b/test/roms/bankswitching/F8/Asteroids 2 (Asteroids Hack).a26 differ diff --git a/test/roms/bankswitching/F8/Asteroids DC+ by Thomas Jentzsch (Asteroids Hack).a26 b/test/roms/bankswitching/F8/Asteroids DC+ by Thomas Jentzsch (Asteroids Hack).a26 new file mode 100644 index 000000000..478d1d408 Binary files /dev/null and b/test/roms/bankswitching/F8/Asteroids DC+ by Thomas Jentzsch (Asteroids Hack).a26 differ diff --git a/test/roms/bankswitching/F8/Asteroids SS (Asteroids Hack).a26 b/test/roms/bankswitching/F8/Asteroids SS (Asteroids Hack).a26 new file mode 100644 index 000000000..58e75c075 Binary files /dev/null and b/test/roms/bankswitching/F8/Asteroids SS (Asteroids Hack).a26 differ diff --git a/test/roms/bankswitching/F8/Asteroids [p1].a26 b/test/roms/bankswitching/F8/Asteroids [p1].a26 new file mode 100644 index 000000000..f53843df3 Binary files /dev/null and b/test/roms/bankswitching/F8/Asteroids [p1].a26 differ diff --git a/test/roms/bankswitching/F8/Battlezone (1983) (Atari) (PAL) [!].a26 b/test/roms/bankswitching/F8/Battlezone (1983) (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..c26c2b17b Binary files /dev/null and b/test/roms/bankswitching/F8/Battlezone (1983) (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F8/Battlezone (1983) (Atari) (PAL) [a1][!].a26 b/test/roms/bankswitching/F8/Battlezone (1983) (Atari) (PAL) [a1][!].a26 new file mode 100644 index 000000000..9b942d84f Binary files /dev/null and b/test/roms/bankswitching/F8/Battlezone (1983) (Atari) (PAL) [a1][!].a26 differ diff --git a/test/roms/bankswitching/F8/Battlezone (1983) (Atari) [!].a26 b/test/roms/bankswitching/F8/Battlezone (1983) (Atari) [!].a26 new file mode 100644 index 000000000..8dee5f262 Binary files /dev/null and b/test/roms/bankswitching/F8/Battlezone (1983) (Atari) [!].a26 differ diff --git a/test/roms/bankswitching/F8/Berenstain Bears (1982) (Coleco).a26 b/test/roms/bankswitching/F8/Berenstain Bears (1982) (Coleco).a26 new file mode 100644 index 000000000..ccf863c54 Binary files /dev/null and b/test/roms/bankswitching/F8/Berenstain Bears (1982) (Coleco).a26 differ diff --git a/test/roms/bankswitching/F8/Centipede (1982) (Atari) (PAL) [!].a26 b/test/roms/bankswitching/F8/Centipede (1982) (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..ed1ce5fcb Binary files /dev/null and b/test/roms/bankswitching/F8/Centipede (1982) (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F8/Centipede (1982) (Atari) (Prototype) (PAL) [!].a26 b/test/roms/bankswitching/F8/Centipede (1982) (Atari) (Prototype) (PAL) [!].a26 new file mode 100644 index 000000000..4232271ef Binary files /dev/null and b/test/roms/bankswitching/F8/Centipede (1982) (Atari) (Prototype) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F8/Centipede (1982) (Atari) [!].a26 b/test/roms/bankswitching/F8/Centipede (1982) (Atari) [!].a26 new file mode 100644 index 000000000..6befff16b Binary files /dev/null and b/test/roms/bankswitching/F8/Centipede (1982) (Atari) [!].a26 differ diff --git a/test/roms/bankswitching/F8/Centipede 2k (2000) (PD).a26 b/test/roms/bankswitching/F8/Centipede 2k (2000) (PD).a26 new file mode 100644 index 000000000..210d5195f Binary files /dev/null and b/test/roms/bankswitching/F8/Centipede 2k (2000) (PD).a26 differ diff --git a/test/roms/bankswitching/F8/Cyber Goth Galaxian by Manuel Polik (Galaxian Hack).a26 b/test/roms/bankswitching/F8/Cyber Goth Galaxian by Manuel Polik (Galaxian Hack).a26 new file mode 100644 index 000000000..aea07c07c Binary files /dev/null and b/test/roms/bankswitching/F8/Cyber Goth Galaxian by Manuel Polik (Galaxian Hack).a26 differ diff --git a/test/roms/bankswitching/F8/Elk Attack (1987) (Atari).a26 b/test/roms/bankswitching/F8/Elk Attack (1987) (Atari).a26 new file mode 100644 index 000000000..0b6f9b8cc Binary files /dev/null and b/test/roms/bankswitching/F8/Elk Attack (1987) (Atari).a26 differ diff --git a/test/roms/bankswitching/F8/Galaxian (1983) (Atari) (PAL) [!].a26 b/test/roms/bankswitching/F8/Galaxian (1983) (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..caa8cf4e5 Binary files /dev/null and b/test/roms/bankswitching/F8/Galaxian (1983) (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F8/Galaxian (1983) (Atari) (PAL) [a1][!].a26 b/test/roms/bankswitching/F8/Galaxian (1983) (Atari) (PAL) [a1][!].a26 new file mode 100644 index 000000000..1180a78b8 Binary files /dev/null and b/test/roms/bankswitching/F8/Galaxian (1983) (Atari) (PAL) [a1][!].a26 differ diff --git a/test/roms/bankswitching/F8/Galaxian (1983) (Atari) [!].a26 b/test/roms/bankswitching/F8/Galaxian (1983) (Atari) [!].a26 new file mode 100644 index 000000000..44078febb Binary files /dev/null and b/test/roms/bankswitching/F8/Galaxian (1983) (Atari) [!].a26 differ diff --git a/test/roms/bankswitching/F8/Galaxian (1983) (CCE).a26 b/test/roms/bankswitching/F8/Galaxian (1983) (CCE).a26 new file mode 100644 index 000000000..77472a313 Binary files /dev/null and b/test/roms/bankswitching/F8/Galaxian (1983) (CCE).a26 differ diff --git a/test/roms/bankswitching/F8/Galaxian by Jess Ragan (Enhanced Graphics).a26 b/test/roms/bankswitching/F8/Galaxian by Jess Ragan (Enhanced Graphics).a26 new file mode 100644 index 000000000..f07a72755 Binary files /dev/null and b/test/roms/bankswitching/F8/Galaxian by Jess Ragan (Enhanced Graphics).a26 differ diff --git a/test/roms/bankswitching/F8/Gravitar (1988) (Atari) [!].a26 b/test/roms/bankswitching/F8/Gravitar (1988) (Atari) [!].a26 new file mode 100644 index 000000000..9dbd14cdf Binary files /dev/null and b/test/roms/bankswitching/F8/Gravitar (1988) (Atari) [!].a26 differ diff --git a/test/roms/bankswitching/F8/Gravitar (1988) (Atari) [a1][!].a26 b/test/roms/bankswitching/F8/Gravitar (1988) (Atari) [a1][!].a26 new file mode 100644 index 000000000..bee28da54 Binary files /dev/null and b/test/roms/bankswitching/F8/Gravitar (1988) (Atari) [a1][!].a26 differ diff --git a/test/roms/bankswitching/F8/Gravitar (1988) (CCE).a26 b/test/roms/bankswitching/F8/Gravitar (1988) (CCE).a26 new file mode 100644 index 000000000..5c5725fa9 Binary files /dev/null and b/test/roms/bankswitching/F8/Gravitar (1988) (CCE).a26 differ diff --git a/test/roms/bankswitching/F8/H.E.R.O. (1984) (Activision) (PAL) [!].a26 b/test/roms/bankswitching/F8/H.E.R.O. (1984) (Activision) (PAL) [!].a26 new file mode 100644 index 000000000..322c3146a Binary files /dev/null and b/test/roms/bankswitching/F8/H.E.R.O. (1984) (Activision) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F8/H.E.R.O. (1984) (Activision) (PAL) [a1][!].a26 b/test/roms/bankswitching/F8/H.E.R.O. (1984) (Activision) (PAL) [a1][!].a26 new file mode 100644 index 000000000..ac7a8837a Binary files /dev/null and b/test/roms/bankswitching/F8/H.E.R.O. (1984) (Activision) (PAL) [a1][!].a26 differ diff --git a/test/roms/bankswitching/F8/H.E.R.O. (1984) (Activision) (PAL) [a2][!].a26 b/test/roms/bankswitching/F8/H.E.R.O. (1984) (Activision) (PAL) [a2][!].a26 new file mode 100644 index 000000000..93e44d84e Binary files /dev/null and b/test/roms/bankswitching/F8/H.E.R.O. (1984) (Activision) (PAL) [a2][!].a26 differ diff --git a/test/roms/bankswitching/F8/H.E.R.O. (1984) (Activision) [!].a26 b/test/roms/bankswitching/F8/H.E.R.O. (1984) (Activision) [!].a26 new file mode 100644 index 000000000..4242bd9ad Binary files /dev/null and b/test/roms/bankswitching/F8/H.E.R.O. (1984) (Activision) [!].a26 differ diff --git a/test/roms/bankswitching/F8/H.E.R.O. (1984) (CCE).a26 b/test/roms/bankswitching/F8/H.E.R.O. (1984) (CCE).a26 new file mode 100644 index 000000000..af262b9bb Binary files /dev/null and b/test/roms/bankswitching/F8/H.E.R.O. (1984) (CCE).a26 differ diff --git a/test/roms/bankswitching/F8/Joust (1982) (Atari) (PAL) [!].a26 b/test/roms/bankswitching/F8/Joust (1982) (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..70caa6125 Binary files /dev/null and b/test/roms/bankswitching/F8/Joust (1982) (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F8/Joust (1982) (Atari) (PAL) [a1][!].a26 b/test/roms/bankswitching/F8/Joust (1982) (Atari) (PAL) [a1][!].a26 new file mode 100644 index 000000000..68a8c9c78 Binary files /dev/null and b/test/roms/bankswitching/F8/Joust (1982) (Atari) (PAL) [a1][!].a26 differ diff --git a/test/roms/bankswitching/F8/Joust (1982) (Atari).a26 b/test/roms/bankswitching/F8/Joust (1982) (Atari).a26 new file mode 100644 index 000000000..9b0972aa8 Binary files /dev/null and b/test/roms/bankswitching/F8/Joust (1982) (Atari).a26 differ diff --git a/test/roms/bankswitching/F8/Jungle Hunt (1982) (Atari) (PAL) [!].a26 b/test/roms/bankswitching/F8/Jungle Hunt (1982) (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..19df8e2bb Binary files /dev/null and b/test/roms/bankswitching/F8/Jungle Hunt (1982) (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F8/Jungle Hunt (1982) (Atari) (PAL) [a1][!].a26 b/test/roms/bankswitching/F8/Jungle Hunt (1982) (Atari) (PAL) [a1][!].a26 new file mode 100644 index 000000000..990f7faf7 Binary files /dev/null and b/test/roms/bankswitching/F8/Jungle Hunt (1982) (Atari) (PAL) [a1][!].a26 differ diff --git a/test/roms/bankswitching/F8/Jungle Hunt (1982) (Atari) [!].a26 b/test/roms/bankswitching/F8/Jungle Hunt (1982) (Atari) [!].a26 new file mode 100644 index 000000000..eef2cb0be Binary files /dev/null and b/test/roms/bankswitching/F8/Jungle Hunt (1982) (Atari) [!].a26 differ diff --git a/test/roms/bankswitching/F8/Jungle Hunt (1982) (CCE).a26 b/test/roms/bankswitching/F8/Jungle Hunt (1982) (CCE).a26 new file mode 100644 index 000000000..94b8a2288 Binary files /dev/null and b/test/roms/bankswitching/F8/Jungle Hunt (1982) (CCE).a26 differ diff --git a/test/roms/bankswitching/F8/Kung Fu Master (1984) (Activision) (PAL) [!].a26 b/test/roms/bankswitching/F8/Kung Fu Master (1984) (Activision) (PAL) [!].a26 new file mode 100644 index 000000000..73ee2ff89 Binary files /dev/null and b/test/roms/bankswitching/F8/Kung Fu Master (1984) (Activision) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F8/Kung Fu Master (1984) (Activision) (PAL) [a1][!].a26 b/test/roms/bankswitching/F8/Kung Fu Master (1984) (Activision) (PAL) [a1][!].a26 new file mode 100644 index 000000000..62b89096d Binary files /dev/null and b/test/roms/bankswitching/F8/Kung Fu Master (1984) (Activision) (PAL) [a1][!].a26 differ diff --git a/test/roms/bankswitching/F8/Kung Fu Master (1984) (Activision).a26 b/test/roms/bankswitching/F8/Kung Fu Master (1984) (Activision).a26 new file mode 100644 index 000000000..387582156 Binary files /dev/null and b/test/roms/bankswitching/F8/Kung Fu Master (1984) (Activision).a26 differ diff --git a/test/roms/bankswitching/F8/Kung Fu Master (CCE).a26 b/test/roms/bankswitching/F8/Kung Fu Master (CCE).a26 new file mode 100644 index 000000000..74378e62a Binary files /dev/null and b/test/roms/bankswitching/F8/Kung Fu Master (CCE).a26 differ diff --git a/test/roms/bankswitching/F8/Mr. Roboto by Paul Slocum (Berzerk Hack).a26 b/test/roms/bankswitching/F8/Mr. Roboto by Paul Slocum (Berzerk Hack).a26 new file mode 100644 index 000000000..2fe4629e1 Binary files /dev/null and b/test/roms/bankswitching/F8/Mr. Roboto by Paul Slocum (Berzerk Hack).a26 differ diff --git a/test/roms/bankswitching/F8/Ms. Pac-Man (1982) (Atari) (PAL) [!].a26 b/test/roms/bankswitching/F8/Ms. Pac-Man (1982) (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..2f2ab11ed Binary files /dev/null and b/test/roms/bankswitching/F8/Ms. Pac-Man (1982) (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F8/Ms. Pac-Man (1982) (Atari) (PAL) [a1][!].a26 b/test/roms/bankswitching/F8/Ms. Pac-Man (1982) (Atari) (PAL) [a1][!].a26 new file mode 100644 index 000000000..18cd7b190 Binary files /dev/null and b/test/roms/bankswitching/F8/Ms. Pac-Man (1982) (Atari) (PAL) [a1][!].a26 differ diff --git a/test/roms/bankswitching/F8/Ms. Pac-Man (1982) (Atari) [h1].a26 b/test/roms/bankswitching/F8/Ms. Pac-Man (1982) (Atari) [h1].a26 new file mode 100644 index 000000000..11084fadf Binary files /dev/null and b/test/roms/bankswitching/F8/Ms. Pac-Man (1982) (Atari) [h1].a26 differ diff --git a/test/roms/bankswitching/F8/Ms. Pac-Man (1982) (Atari).a26 b/test/roms/bankswitching/F8/Ms. Pac-Man (1982) (Atari).a26 new file mode 100644 index 000000000..4b10657b8 Binary files /dev/null and b/test/roms/bankswitching/F8/Ms. Pac-Man (1982) (Atari).a26 differ diff --git a/test/roms/bankswitching/F8/Ms. Pac-Man (1982) (CCE).a26 b/test/roms/bankswitching/F8/Ms. Pac-Man (1982) (CCE).a26 new file mode 100644 index 000000000..35d1ed351 Binary files /dev/null and b/test/roms/bankswitching/F8/Ms. Pac-Man (1982) (CCE).a26 differ diff --git a/test/roms/bankswitching/F8/Pengo (1984) (Atari).a26 b/test/roms/bankswitching/F8/Pengo (1984) (Atari).a26 new file mode 100644 index 000000000..f65d859df Binary files /dev/null and b/test/roms/bankswitching/F8/Pengo (1984) (Atari).a26 differ diff --git a/test/roms/bankswitching/F8/Pengo - 1 Player Only (1984) (Atari).a26 b/test/roms/bankswitching/F8/Pengo - 1 Player Only (1984) (Atari).a26 new file mode 100644 index 000000000..c0a3639c8 Binary files /dev/null and b/test/roms/bankswitching/F8/Pengo - 1 Player Only (1984) (Atari).a26 differ diff --git a/test/roms/bankswitching/F8/Phoenix (1982) (Atari) (PAL) [!].a26 b/test/roms/bankswitching/F8/Phoenix (1982) (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..5417c5ab1 Binary files /dev/null and b/test/roms/bankswitching/F8/Phoenix (1982) (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F8/Phoenix (1982) (Atari) (PAL) [a1][!].a26 b/test/roms/bankswitching/F8/Phoenix (1982) (Atari) (PAL) [a1][!].a26 new file mode 100644 index 000000000..805ebe572 Binary files /dev/null and b/test/roms/bankswitching/F8/Phoenix (1982) (Atari) (PAL) [a1][!].a26 differ diff --git a/test/roms/bankswitching/F8/Phoenix (1982) (Atari).a26 b/test/roms/bankswitching/F8/Phoenix (1982) (Atari).a26 new file mode 100644 index 000000000..b4bb21f3d Binary files /dev/null and b/test/roms/bankswitching/F8/Phoenix (1982) (Atari).a26 differ diff --git a/test/roms/bankswitching/F8/Phoenix (1982) (CCE).a26 b/test/roms/bankswitching/F8/Phoenix (1982) (CCE).a26 new file mode 100644 index 000000000..40347e597 Binary files /dev/null and b/test/roms/bankswitching/F8/Phoenix (1982) (CCE).a26 differ diff --git a/test/roms/bankswitching/F8/Raiders of the Lost Ark (1982) (Atari) (PAL) [!].a26 b/test/roms/bankswitching/F8/Raiders of the Lost Ark (1982) (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..1bc00d280 Binary files /dev/null and b/test/roms/bankswitching/F8/Raiders of the Lost Ark (1982) (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F8/Raiders of the Lost Ark (1982) (Atari).a26 b/test/roms/bankswitching/F8/Raiders of the Lost Ark (1982) (Atari).a26 new file mode 100644 index 000000000..5274c722f Binary files /dev/null and b/test/roms/bankswitching/F8/Raiders of the Lost Ark (1982) (Atari).a26 differ diff --git a/test/roms/bankswitching/F8/Spy Hunter (1983) (Sega) [!].a26 b/test/roms/bankswitching/F8/Spy Hunter (1983) (Sega) [!].a26 new file mode 100644 index 000000000..dc189113c Binary files /dev/null and b/test/roms/bankswitching/F8/Spy Hunter (1983) (Sega) [!].a26 differ diff --git a/test/roms/bankswitching/F8/Vanguard (1982) (Atari) (PAL) [!].a26 b/test/roms/bankswitching/F8/Vanguard (1982) (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..c6317774c Binary files /dev/null and b/test/roms/bankswitching/F8/Vanguard (1982) (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F8/Vanguard (1982) (Atari) (PAL) [a1][!].a26 b/test/roms/bankswitching/F8/Vanguard (1982) (Atari) (PAL) [a1][!].a26 new file mode 100644 index 000000000..e3d215427 Binary files /dev/null and b/test/roms/bankswitching/F8/Vanguard (1982) (Atari) (PAL) [a1][!].a26 differ diff --git a/test/roms/bankswitching/F8/Vanguard (1982) (Atari).a26 b/test/roms/bankswitching/F8/Vanguard (1982) (Atari).a26 new file mode 100644 index 000000000..943973765 Binary files /dev/null and b/test/roms/bankswitching/F8/Vanguard (1982) (Atari).a26 differ diff --git a/test/roms/bankswitching/F8/Vanguard (CCE).a26 b/test/roms/bankswitching/F8/Vanguard (CCE).a26 new file mode 100644 index 000000000..f5d6e905a Binary files /dev/null and b/test/roms/bankswitching/F8/Vanguard (CCE).a26 differ diff --git a/test/roms/bankswitching/F8/cfg/a2600.cfg b/test/roms/bankswitching/F8/cfg/a2600.cfg new file mode 100644 index 000000000..90dde01f6 --- /dev/null +++ b/test/roms/bankswitching/F8/cfg/a2600.cfg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/test/roms/bankswitching/F8/cfg/default.cfg b/test/roms/bankswitching/F8/cfg/default.cfg new file mode 100644 index 000000000..02f098671 --- /dev/null +++ b/test/roms/bankswitching/F8/cfg/default.cfg @@ -0,0 +1,5 @@ + + + + + diff --git a/test/roms/bankswitching/F8SC/Defender II (1984) (Atari) (PAL) [!].a26 b/test/roms/bankswitching/F8SC/Defender II (1984) (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..2bc9c59a4 Binary files /dev/null and b/test/roms/bankswitching/F8SC/Defender II (1984) (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F8SC/Defender II (1984) (Atari).a26 b/test/roms/bankswitching/F8SC/Defender II (1984) (Atari).a26 new file mode 100644 index 000000000..06b4d24a2 Binary files /dev/null and b/test/roms/bankswitching/F8SC/Defender II (1984) (Atari).a26 differ diff --git a/test/roms/bankswitching/F8SC/Elevator Action (Atari) (Prototype).a26 b/test/roms/bankswitching/F8SC/Elevator Action (Atari) (Prototype).a26 new file mode 100644 index 000000000..4a44ee2d3 Binary files /dev/null and b/test/roms/bankswitching/F8SC/Elevator Action (Atari) (Prototype).a26 differ diff --git a/test/roms/bankswitching/F8SC/Stargate (1984) (Atari) (PAL) [!].a26 b/test/roms/bankswitching/F8SC/Stargate (1984) (Atari) (PAL) [!].a26 new file mode 100644 index 000000000..0d2322855 Binary files /dev/null and b/test/roms/bankswitching/F8SC/Stargate (1984) (Atari) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/F8SC/Stargate (1984) (Atari).a26 b/test/roms/bankswitching/F8SC/Stargate (1984) (Atari).a26 new file mode 100644 index 000000000..9ad3f1cd6 Binary files /dev/null and b/test/roms/bankswitching/F8SC/Stargate (1984) (Atari).a26 differ diff --git a/test/roms/bankswitching/FA/Mountain King (1983) (CBS Electronics).bin b/test/roms/bankswitching/FA/Mountain King (1983) (CBS Electronics).bin new file mode 100644 index 000000000..56b870d6a Binary files /dev/null and b/test/roms/bankswitching/FA/Mountain King (1983) (CBS Electronics).bin differ diff --git a/test/roms/bankswitching/FA2/Star Castle Arcade (165, Harmony).cu.bin b/test/roms/bankswitching/FA2/Star Castle Arcade (165, Harmony).cu.bin new file mode 100644 index 000000000..3a94dbbf3 Binary files /dev/null and b/test/roms/bankswitching/FA2/Star Castle Arcade (165, Harmony).cu.bin differ diff --git a/test/roms/bankswitching/FA2/fa2plus_test.bin b/test/roms/bankswitching/FA2/fa2plus_test.bin new file mode 100644 index 000000000..ccb94b4fe Binary files /dev/null and b/test/roms/bankswitching/FA2/fa2plus_test.bin differ diff --git a/test/roms/bankswitching/FC/Power Play Arcade Video Game Album, The - Ghost Attack, Genesis, Havoc (1983) (Amiga) (1110) (Prototype).bin b/test/roms/bankswitching/FC/Power Play Arcade Video Game Album, The - Ghost Attack, Genesis, Havoc (1983) (Amiga) (1110) (Prototype).bin new file mode 100644 index 000000000..8f40df7e7 Binary files /dev/null and b/test/roms/bankswitching/FC/Power Play Arcade Video Game Album, The - Ghost Attack, Genesis, Havoc (1983) (Amiga) (1110) (Prototype).bin differ diff --git a/test/roms/bankswitching/FE/Decathlon (1983) (Activision) (PAL) [!].a26 b/test/roms/bankswitching/FE/Decathlon (1983) (Activision) (PAL) [!].a26 new file mode 100644 index 000000000..dcfabf850 Binary files /dev/null and b/test/roms/bankswitching/FE/Decathlon (1983) (Activision) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/FE/Decathlon (1983) (Activision) [!].a26 b/test/roms/bankswitching/FE/Decathlon (1983) (Activision) [!].a26 new file mode 100644 index 000000000..4be7efb82 Binary files /dev/null and b/test/roms/bankswitching/FE/Decathlon (1983) (Activision) [!].a26 differ diff --git a/test/roms/bankswitching/FE/Robot Tank (1983) (Activision) (PAL) [!].a26 b/test/roms/bankswitching/FE/Robot Tank (1983) (Activision) (PAL) [!].a26 new file mode 100644 index 000000000..6caf4cc36 Binary files /dev/null and b/test/roms/bankswitching/FE/Robot Tank (1983) (Activision) (PAL) [!].a26 differ diff --git a/test/roms/bankswitching/FE/Robot Tank (1983) (Activision) [!].a26 b/test/roms/bankswitching/FE/Robot Tank (1983) (Activision) [!].a26 new file mode 100644 index 000000000..4471fa1e8 Binary files /dev/null and b/test/roms/bankswitching/FE/Robot Tank (1983) (Activision) [!].a26 differ diff --git a/test/roms/bankswitching/FE/Robot Tank TV by Thomas Jentzsch (2 Joystick Hack).a26 b/test/roms/bankswitching/FE/Robot Tank TV by Thomas Jentzsch (2 Joystick Hack).a26 new file mode 100644 index 000000000..7e81acb2f Binary files /dev/null and b/test/roms/bankswitching/FE/Robot Tank TV by Thomas Jentzsch (2 Joystick Hack).a26 differ diff --git a/test/roms/bankswitching/FE/Space Shuttle (1983) (Activision) [FE].bin b/test/roms/bankswitching/FE/Space Shuttle (1983) (Activision) [FE].bin new file mode 100644 index 000000000..030c77fd2 Binary files /dev/null and b/test/roms/bankswitching/FE/Space Shuttle (1983) (Activision) [FE].bin differ diff --git a/test/roms/bankswitching/FE/Thwocker (Activision) (Prototype) [!].a26 b/test/roms/bankswitching/FE/Thwocker (Activision) (Prototype) [!].a26 new file mode 100644 index 000000000..844c840f8 Binary files /dev/null and b/test/roms/bankswitching/FE/Thwocker (Activision) (Prototype) [!].a26 differ diff --git a/test/roms/bankswitching/FE/bad/Decathlon (1983) (Activision) (PAL) [b1].a26 b/test/roms/bankswitching/FE/bad/Decathlon (1983) (Activision) (PAL) [b1].a26 new file mode 100644 index 000000000..c15334bc3 Binary files /dev/null and b/test/roms/bankswitching/FE/bad/Decathlon (1983) (Activision) (PAL) [b1].a26 differ diff --git a/test/roms/bankswitching/FE/bad/Robot Tank (1983) (Activision) (PAL) [b1].a26 b/test/roms/bankswitching/FE/bad/Robot Tank (1983) (Activision) (PAL) [b1].a26 new file mode 100644 index 000000000..643dd64d1 Binary files /dev/null and b/test/roms/bankswitching/FE/bad/Robot Tank (1983) (Activision) (PAL) [b1].a26 differ diff --git a/test/roms/bankswitching/MDM/208in1_MDMC_test_PAL-127games.bin b/test/roms/bankswitching/MDM/208in1_MDMC_test_PAL-127games.bin new file mode 100644 index 000000000..61f758068 Binary files /dev/null and b/test/roms/bankswitching/MDM/208in1_MDMC_test_PAL-127games.bin differ diff --git a/test/roms/bankswitching/MDM/MDM-test.bin b/test/roms/bankswitching/MDM/MDM-test.bin new file mode 100644 index 000000000..2a172f1ed Binary files /dev/null and b/test/roms/bankswitching/MDM/MDM-test.bin differ diff --git a/test/roms/bankswitching/MDM/POP_MDMC_test_PAL_63gamees.bin b/test/roms/bankswitching/MDM/POP_MDMC_test_PAL_63gamees.bin new file mode 100644 index 000000000..13ccb9175 Binary files /dev/null and b/test/roms/bankswitching/MDM/POP_MDMC_test_PAL_63gamees.bin differ diff --git a/test/roms/bankswitching/MDM/arqsaida2.bin b/test/roms/bankswitching/MDM/arqsaida2.bin new file mode 100644 index 000000000..560cad6b0 Binary files /dev/null and b/test/roms/bankswitching/MDM/arqsaida2.bin differ diff --git a/test/roms/bankswitching/SB/menu_multicart128k.bin b/test/roms/bankswitching/SB/menu_multicart128k.bin new file mode 100644 index 000000000..ba51838f0 Binary files /dev/null and b/test/roms/bankswitching/SB/menu_multicart128k.bin differ diff --git a/test/roms/bankswitching/SB/t128.bin b/test/roms/bankswitching/SB/t128.bin new file mode 100644 index 000000000..03441590d Binary files /dev/null and b/test/roms/bankswitching/SB/t128.bin differ diff --git a/test/roms/bankswitching/SB/t256.bin b/test/roms/bankswitching/SB/t256.bin new file mode 100644 index 000000000..72da79783 Binary files /dev/null and b/test/roms/bankswitching/SB/t256.bin differ diff --git a/test/roms/bankswitching/Sub2K/2k.bin b/test/roms/bankswitching/Sub2K/2k.bin new file mode 100644 index 000000000..bcaafe540 Binary files /dev/null and b/test/roms/bankswitching/Sub2K/2k.bin differ diff --git a/test/roms/bankswitching/Sub2K/4k.bin b/test/roms/bankswitching/Sub2K/4k.bin new file mode 100644 index 000000000..2eae2f8b7 Binary files /dev/null and b/test/roms/bankswitching/Sub2K/4k.bin differ diff --git a/test/roms/bankswitching/Sub2K/Harte32.bin b/test/roms/bankswitching/Sub2K/Harte32.bin new file mode 100644 index 000000000..4c7dce8c0 Binary files /dev/null and b/test/roms/bankswitching/Sub2K/Harte32.bin differ diff --git a/test/roms/bankswitching/Sub2K/Pong256B.bin b/test/roms/bankswitching/Sub2K/Pong256B.bin new file mode 100644 index 000000000..4947e18fa Binary files /dev/null and b/test/roms/bankswitching/Sub2K/Pong256B.bin differ diff --git a/test/roms/bankswitching/Sub2K/Pong512B.bin b/test/roms/bankswitching/Sub2K/Pong512B.bin new file mode 100644 index 000000000..fcb23fee8 Binary files /dev/null and b/test/roms/bankswitching/Sub2K/Pong512B.bin differ diff --git a/test/roms/bankswitching/Sub2K/Vong_1K.bin b/test/roms/bankswitching/Sub2K/Vong_1K.bin new file mode 100644 index 000000000..f1933d80f Binary files /dev/null and b/test/roms/bankswitching/Sub2K/Vong_1K.bin differ diff --git a/test/roms/bankswitching/UA/Fathon (Digivision).bin b/test/roms/bankswitching/UA/Fathon (Digivision).bin new file mode 100644 index 000000000..12a8e787b Binary files /dev/null and b/test/roms/bankswitching/UA/Fathon (Digivision).bin differ diff --git a/test/roms/bankswitching/UA/FunkyFish.bin b/test/roms/bankswitching/UA/FunkyFish.bin new file mode 100644 index 000000000..f10a64de3 Binary files /dev/null and b/test/roms/bankswitching/UA/FunkyFish.bin differ diff --git a/test/roms/bankswitching/UA/Gingerbread Man SEGA-controller.ua.bin b/test/roms/bankswitching/UA/Gingerbread Man SEGA-controller.ua.bin new file mode 100644 index 000000000..aa869c4b9 Binary files /dev/null and b/test/roms/bankswitching/UA/Gingerbread Man SEGA-controller.ua.bin differ diff --git a/test/roms/bankswitching/UA/Mickey (Digivision).bin b/test/roms/bankswitching/UA/Mickey (Digivision).bin new file mode 100644 index 000000000..3e8293ff4 Binary files /dev/null and b/test/roms/bankswitching/UA/Mickey (Digivision).bin differ diff --git a/test/roms/bankswitching/UA/Pleiades.bin b/test/roms/bankswitching/UA/Pleiades.bin new file mode 100644 index 000000000..0878ebb72 Binary files /dev/null and b/test/roms/bankswitching/UA/Pleiades.bin differ diff --git a/test/roms/bankswitching/UA/Time Pilot (Rentacom).bin b/test/roms/bankswitching/UA/Time Pilot (Rentacom).bin new file mode 100644 index 000000000..90f86e7f7 Binary files /dev/null and b/test/roms/bankswitching/UA/Time Pilot (Rentacom).bin differ diff --git a/test/roms/bankswitching/UA/UA_Limited.bin b/test/roms/bankswitching/UA/UA_Limited.bin new file mode 100644 index 000000000..5cb088b29 Binary files /dev/null and b/test/roms/bankswitching/UA/UA_Limited.bin differ diff --git a/test/roms/bankswitching/UA/Vanguard (Rentacom).bin b/test/roms/bankswitching/UA/Vanguard (Rentacom).bin new file mode 100644 index 000000000..6b9429868 Binary files /dev/null and b/test/roms/bankswitching/UA/Vanguard (Rentacom).bin differ diff --git a/test/roms/bankswitching/UA/ginger.zip b/test/roms/bankswitching/UA/ginger.zip new file mode 100644 index 000000000..4246d4f50 Binary files /dev/null and b/test/roms/bankswitching/UA/ginger.zip differ diff --git a/test/roms/bankswitching/UA/gingerUA.bin b/test/roms/bankswitching/UA/gingerUA.bin new file mode 100644 index 000000000..2f6a6f932 Binary files /dev/null and b/test/roms/bankswitching/UA/gingerUA.bin differ diff --git a/test/roms/bankswitching/WD/Pursuit of the Pink Panther (Probe) (Prototype) [bad dump].bin b/test/roms/bankswitching/WD/Pursuit of the Pink Panther (Probe) (Prototype) [bad dump].bin new file mode 100644 index 000000000..3970d0602 Binary files /dev/null and b/test/roms/bankswitching/WD/Pursuit of the Pink Panther (Probe) (Prototype) [bad dump].bin differ diff --git a/test/roms/bankswitching/WD/Pursuit of the Pink Panther (Probe) (Prototype) [fixed].bin b/test/roms/bankswitching/WD/Pursuit of the Pink Panther (Probe) (Prototype) [fixed].bin new file mode 100644 index 000000000..187d81d57 Binary files /dev/null and b/test/roms/bankswitching/WD/Pursuit of the Pink Panther (Probe) (Prototype) [fixed].bin differ diff --git a/test/roms/bankswitching/X07/X07 Bankswitching Conversion.bin b/test/roms/bankswitching/X07/X07 Bankswitching Conversion.bin new file mode 100644 index 000000000..14732a9ff Binary files /dev/null and b/test/roms/bankswitching/X07/X07 Bankswitching Conversion.bin differ diff --git a/test/roms/bankswitching/X07/x07.bin b/test/roms/bankswitching/X07/x07.bin new file mode 100644 index 000000000..2bccd1618 Binary files /dev/null and b/test/roms/bankswitching/X07/x07.bin differ diff --git a/test/roms/bankswitching/_code/0840_EconoBanking.asm b/test/roms/bankswitching/_code/0840_EconoBanking.asm new file mode 100644 index 000000000..b655664cc --- /dev/null +++ b/test/roms/bankswitching/_code/0840_EconoBanking.asm @@ -0,0 +1,102 @@ +;;A bankswitching demo for the 0840 EconoBanking technique. 2 4K banks (8K) +;;By: Rick Skrbina 3/29/09 + + processor 6502 + include "vcs.h" + include "macro.h" + + + seg.u vars + org $80 +BG_Color ds 1 + + seg Bank0 + org $E000 + rorg $F000 +Start0 + nop + nop + nop + + CLEAN_START + +Start_Frame + lda #2 + sta VBLANK + sta VSYNC + sta WSYNC + sta WSYNC + sta WSYNC + lsr + sta VSYNC + ldy #37 +VerticalBlank + sta WSYNC + dey + bne VerticalBlank + + sta VBLANK + + ldy #192 +Picture + sta WSYNC + dey + bne Picture + + lda #2 + sta VBLANK + + ldy #30 +OverScan + sta WSYNC + dey + bne OverScan + + jsr SwchTo1 + + lda BG_Color + sta COLUBK + + jmp Start_Frame + + org $EFE0 + rorg $FFE0 +SwchTo1 + lda $0FFF + nop + nop + nop + nop + nop + nop + rts + + org $EFFC + rorg $FFFC + .word Start0 + .byte "B0" + + seg Bank1 + org $F000 + rorg $F000 +Start1 + lda $0800 + +Bank1Sub + + inc BG_Color + rts + + org $FFE0 + rorg $FFE0 + + nop + nop + nop + jsr Bank1Sub + lda $0800 + + org $FFFC + rorg $FFFC + .word Start1 + .byte "B1" diff --git a/test/roms/bankswitching/_code/0840_EconoBanking.bin b/test/roms/bankswitching/_code/0840_EconoBanking.bin new file mode 100644 index 000000000..45dbbb187 Binary files /dev/null and b/test/roms/bankswitching/_code/0840_EconoBanking.bin differ diff --git a/test/roms/bankswitching/_code/3F.asm b/test/roms/bankswitching/_code/3F.asm new file mode 100644 index 000000000..c0f2ac9d7 --- /dev/null +++ b/test/roms/bankswitching/_code/3F.asm @@ -0,0 +1,118 @@ +;;A Bankswitcing demo for TigerVision's 3F scheme. 4 2K slices +;;By Rick Skrbina 5/4/09 + +TIA_BASE_ADDRESS = $40 ;Use $40-$7F for TIA access so there is no undesired Bankswitch + + processor 6502 + include "vcs.h" + include "macro.h" + + + seg.u vars + org $80 + + + seg slice0 + org $0000 + rorg $F000 +Slice0 + lda #$1F + sta COLUBK + rts + + seg slice1 + org $0800 + rorg $F000 +Slice1 + lda #$2F + sta COLUBK + rts + + seg slice2 + org $1000 + rorg $F000 +Slice2 + lda #$3F + sta COLUBK + rts + + seg slice3 + org $1800 + rorg $F800 +Slice3 + CLEAN_START + +Start_Frame + lda #2 + sta VBLANK + sta VSYNC + sta WSYNC + sta WSYNC + sta WSYNC + lsr + sta VSYNC + + ldy #37 +VerticalBlank + sta WSYNC + dey + bne VerticalBlank + + lda #0 + sta VBLANK + + lda #$0F + sta COLUBK + + ldy #48 +Picture0 + sta WSYNC + dey + bne Picture0 + + lda #0 + sta $3F + jsr Slice0 + + ldy #48 +Picture1 + sta WSYNC + dey + bne Picture1 + + lda #1 + sta $3F + jsr Slice1 + + ldy #48 +Picture2 + sta WSYNC + dey + bne Picture2 + + lda #2 + sta $3F + jsr Slice2 + + ldy #48 +Picture3 + sta WSYNC + dey + bne Picture3 + + lda #2 + sta VBLANK + + ldy #30 +OverScan + sta WSYNC + dey + bne OverScan + + jmp Start_Frame + + + org $1FFC + rorg $FFFC + .word Slice3 + .byte "RS" diff --git a/test/roms/bankswitching/_code/3F.bin b/test/roms/bankswitching/_code/3F.bin new file mode 100644 index 000000000..028691848 Binary files /dev/null and b/test/roms/bankswitching/_code/3F.bin differ diff --git a/test/roms/bankswitching/_code/E0.asm b/test/roms/bankswitching/_code/E0.asm new file mode 100644 index 000000000..58fed489d --- /dev/null +++ b/test/roms/bankswitching/_code/E0.asm @@ -0,0 +1,178 @@ +;;A bankswtiching demo for Parker Bros. E0 scheme. 8 1K slices. +;;By Rick Skrbina 5/3/09 + + processor 6502 + include "vcs.h" + include "macro.h" + + seg.u vars + org $80 + + seg slice0 + org $0000 + rorg $F000 +Slice0 + lda #$1F + sta COLUBK + rts + + seg slice1 + org $0400 + rorg $F000 +Slice1 + lda #$2F + sta COLUBK + rts + + seg slice2 + org $0800 + rorg $F000 +Slice2 + lda #$3F + sta COLUBK + rts + + seg slice3 + org $0C00 + rorg $F000 +Slice3 + lda #$4F + sta COLUBK + rts + + seg slice4 + org $1000 + rorg $F000 +Slice4 + lda #$5F + sta COLUBK + rts + + seg slice5 + org $1400 + rorg $F000 +Slice5 + lda #$6F + sta COLUBK + rts + + seg slice6 + org $1800 + rorg $F000 +Slice6 + lda #$7F + sta COLUBK + rts + + seg slice7 + org $1C00 + rorg $FC00 +Slice7 + CLEAN_START + +Start_Frame + lda #2 + sta VBLANK + sta VSYNC + sta WSYNC + sta WSYNC + sta WSYNC + lda #0 + sta VSYNC + + ldy #37 +VerticalBlank + sta WSYNC + dey + bne VerticalBlank + + lda #0 + sta VBLANK + + lda #$0F + sta COLUBK + + ldy #24 +Picture0 + sta WSYNC + dey + bne Picture0 + + lda $1FE0 + jsr Slice0 + + ldy #24 +Picture1 + sta WSYNC + dey + bne Picture1 + + lda $1FE1 + jsr Slice1 + + ldy #24 +Picture2 + sta WSYNC + dey + bne Picture2 + + lda $1FE2 + jsr Slice2 + + ldy #24 +Picture3 + sta WSYNC + dey + bne Picture3 + + lda $1FE3 + jsr Slice3 + + ldy #24 +Picture4 + sta WSYNC + dey + bne Picture4 + + lda $1FE4 + jsr Slice4 + + ldy #24 +Picture5 + sta WSYNC + dey + bne Picture5 + + lda $1FE5 + jsr Slice5 + + ldy #24 +Picture6 + sta WSYNC + dey + bne Picture6 + + lda $1FE6 + jsr Slice6 + + ldy #24 +Picture7 + sta WSYNC + dey + bne Picture7 + + lda #2 + sta VBLANK + + ldy #30 +OverScan + sta WSYNC + dey + bne OverScan + + jmp Start_Frame + + org $1FFC + rorg $FFFC + .word Slice7 + .byte "RS" diff --git a/test/roms/bankswitching/_code/E0.bin b/test/roms/bankswitching/_code/E0.bin new file mode 100644 index 000000000..bd4a31b8b Binary files /dev/null and b/test/roms/bankswitching/_code/E0.bin differ diff --git a/test/roms/bankswitching/_code/E7.asm b/test/roms/bankswitching/_code/E7.asm new file mode 100644 index 000000000..3ae1a340d --- /dev/null +++ b/test/roms/bankswitching/_code/E7.asm @@ -0,0 +1,250 @@ +;;A bankswtiching Demo using M-Network's E7 Scheme. 8 2K Banks + 2K RAM +;;By: Rick Skrbina 4/9/09 +;; +;;ROM Banks accesed via $1FE0-$1FE6 and mapped from $F000-$F7FF +;;First 1K RAM block accessed via $1FE7 and loaded from $F000-$F7FF +;;Second 1K block is split up into 256b parts, selected by $FFF8-$FFFB +;;The 256b chuncks are loaded from $F800-$F9FF + + processor 6502 + include "vcs.h" + include "macro.h" + +Bank0_Color_Read equ Bank0_Color_Write+1024 + + + seg.u RIOT_RAM + org $80 + + + seg.u E7_RAM_0 ;1K Block + org $F000 + +Bank0_Color_Write ds 1 + + seg.u E7_RAM_1 ;256b Blocks + org $F800 + + + seg.u E7_RAM_2 + org $F800 + + + seg.u E7_RAM_3 + org $F800 + + seg.u E7_RAM_4 + org $F800 + + seg bank0 + org $0000 + rorg $F000 +Start0 + + lda #$10 + sta COLUBK + + rts + + + + + seg bank1 + org $0800 + rorg $F000 +Start1 + lda #$20 + sta COLUBK + + rts + + + + + seg bank2 + org $1000 + rorg $F000 +Start2 + lda #$30 + sta COLUBK + + rts + + + + + seg bank3 + org $1800 + rorg $F000 +Start3 + lda #$40 + sta COLUBK + + rts + + + + + seg bank4 + org $2000 + rorg $F000 +Start4 + lda #$50 + sta COLUBK + + rts + + + + + seg bank5 + org $2800 + rorg $F000 +Start5 + lda #$60 + sta COLUBK + + rts + + + + + seg bank6 + org $3000 + rorg $F000 +Start6 + lda #$70 + sta COLUBK + + rts + + + + + seg bank7 + org $3800 + rorg $F800 + + repeat 512 + .byte $00 + repend +Start7 + + CLEAN_START + + +Start_Frame + lda #2 + sta VBLANK + sta VSYNC + sta WSYNC + sta WSYNC + sta WSYNC + lsr + sta VSYNC + + ldy #37 +VerticalBlank + sta WSYNC + dey + bne VerticalBlank + + lda #0 + sta VBLANK + + lda $1FE7 + + clc + lda Bank0_Color_Read + adc #1 + sta Bank0_Color_Write + lda Bank0_Color_Read + sta COLUBK + + ldy #24 +Picture0 + sta WSYNC + dey + bne Picture0 + + lda $1FE0 + jsr Start0 + + + ldy #24 +Picture1 + sta WSYNC + dey + bne Picture1 + + lda $1FE1 + jsr Start1 + + ldy #24 +Picture2 + sta WSYNC + dey + bne Picture2 + + lda $1FE2 + jsr Start2 + + ldy #24 +Picture3 + sta WSYNC + dey + bne Picture3 + + lda $1FE3 + jsr Start3 + + ldy #24 +Picture4 + sta WSYNC + dey + bne Picture4 + + lda $1FE4 + jsr Start4 + + ldy #24 +Picture5 + sta WSYNC + dey + bne Picture5 + + lda $1FE5 + jsr Start5 + + ldy #24 +Picture6 + sta WSYNC + dey + bne Picture6 + + lda $1FE6 + jsr Start6 + + ldy #24 +Picture7 + sta WSYNC + dey + bne Picture7 + + + lda #2 + sta VBLANK + + ldy #30 +OverScan + sta WSYNC + dey + bne OverScan + + jmp Start_Frame + + org $3FFC + rorg $FFFC + .word Start7 + .byte "07" + diff --git a/test/roms/bankswitching/_code/E7.bin b/test/roms/bankswitching/_code/E7.bin new file mode 100644 index 000000000..993ce9fe9 --- /dev/null +++ b/test/roms/bankswitching/_code/E7.bin @@ -0,0 +1 @@ +©… `ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ© … `ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©0… `ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©@… `ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©P… `ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©`… `ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©p… `ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿxØ¢Š¨ÊšHÐû©……………J… %…ˆÐû©…­ç­ôið­ô…  …ˆÐû­à ð …ˆÐû­á ð …ˆÐû­â ð …ˆÐû­ã ð …ˆÐû­ä ð …ˆÐû­å ð …ˆÐû­æ ð …ˆÐû©… …ˆÐûL úÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿú07 \ No newline at end of file diff --git a/test/roms/bankswitching/_code/EF.asm b/test/roms/bankswitching/_code/EF.asm new file mode 100644 index 000000000..76eb41e65 --- /dev/null +++ b/test/roms/bankswitching/_code/EF.asm @@ -0,0 +1,674 @@ +;;A bankswitching demo for the EF scheme. 16 4K banks accessed by $FFE0-$FFEF +;;By: Rick Skrbina 4/4/09 + + processor 6502 + include "vcs.h" + include "macro.h" + + seg.u vars + org $80 + + + seg bank0 + org $0000 + rorg $F000 +Start0 + nop + nop + nop + CLEAN_START + +Start_Frame + lda #2 + sta VSYNC + sta VBLANK + sta WSYNC + sta WSYNC + sta WSYNC + lsr + sta VSYNC + + ldy #37 +VerticalBlank + sta WSYNC + dey + bne VerticalBlank + + lda #0 + sta VBLANK + + lda #$0F + sta COLUBK + + ldy #12 +Picture0 + sta WSYNC + dey + bne Picture0 + + jsr Swch1 + + ldy #12 +Picture1 + sta WSYNC + dey + bne Picture1 + + jsr Swch2 + + ldy #12 +Picture2 + sta WSYNC + dey + bne Picture2 + + jsr Swch3 + + ldy #12 +Picture3 + sta WSYNC + dey + bne Picture3 + + jsr Swch4 + + ldy #12 +Picture4 + sta WSYNC + dey + bne Picture4 + + jsr Swch5 + + ldy #12 +Picture5 + sta WSYNC + dey + bne Picture5 + + jsr Swch6 + + ldy #12 +Picture6 + sta WSYNC + dey + bne Picture6 + + jsr Swch7 + + ldy #12 +Picture7 + sta WSYNC + dey + bne Picture7 + + jsr Swch8 + + ldy #12 +Picture8 + sta WSYNC + dey + bne Picture8 + + jsr Swch9 + + ldy #12 +Picture9 + sta WSYNC + dey + bne Picture9 + + jsr SwchA + + ldy #12 +PictureA + sta WSYNC + dey + bne PictureA + + jsr SwchB + + ldy #12 +PictureB + sta WSYNC + dey + bne PictureB + + jsr SwchC + + ldy #12 +PictureC + sta WSYNC + dey + bne PictureC + + jsr SwchD + + ldy #12 +PictureD + sta WSYNC + dey + bne PictureD + + jsr SwchE + + ldy #12 +PictureE + sta WSYNC + dey + bne PictureE + + jsr SwchF + + ldy #12 +PictureF + sta WSYNC + dey + bne PictureF + + lda #2 + sta VBLANK + + ldy #30 +OverScan + sta WSYNC + dey + bne OverScan + + jmp Start_Frame + + org $0400 + rorg $F400 +Swch1 + lda $FFE1 +Swch2 + lda $FFE2 +Swch3 + lda $FFE3 + rts + rts + rts + rts + rts + rts + rts + rts + + org $0415 + rorg $F415 +Swch4 + lda $FFE4 +Swch5 + lda $FFE5 +Swch6 + lda $FFE6 + rts + rts + rts + rts + rts + rts + rts + rts + + org $0430 + rorg $F430 +Swch7 + lda $FFE7 +Swch8 + lda $FFE8 +Swch9 + lda $FFE9 + rts + rts + rts + rts + rts + rts + rts + rts + + org $0445 + rorg $F445 +SwchA + lda $FFEA +SwchB + lda $FFEB +SwchC + lda $FFEC + rts + rts + rts + rts + rts + rts + rts + rts + + org $0460 + rorg $F460 +SwchD + lda $FFED +SwchE + lda $FFEE +SwchF + lda $FFEF + rts + rts + rts + rts + rts + rts + rts + rts + + org $0FFC + .word Start0 + .byte "00" + + seg bank1 + org $1000 + rorg $F000 +Start1 + lda $FFE0 + +Sub1 + lda #$1F + sta COLUBK + rts + + org $1400 + rorg $F400 + nop + nop + nop + jsr Sub1 + lda $FFE0 + + org $1FFC + rorg $FFFC + .word Start1 + .byte "01" + + seg bank2 + org $2000 + rorg $F000 +Start2 + lda $FFE0 + +Sub2 + lda #$2F + sta COLUBK + rts + + org $2400 + rorg $F400 + nop + nop + nop + nop + nop + nop + jsr Sub2 + lda $FFE0 + + org $2FFC + rorg $FFFC + .word Start2 + .byte "02" + + seg bank3 + org $3000 + rorg $F000 +Start3 + lda $FFE0 + +Sub3 + lda #$3F + sta COLUBK + rts + + org $3400 + rorg $F400 + nop + nop + nop + nop + nop + nop + nop + nop + nop + jsr Sub3 + lda $FFE0 + + org $3FFC + rorg $FFFC + .word Start3 + .byte "03" + + seg bank4 + org $4000 + rorg $F000 +Start4 + lda $FFE0 + +Sub4 + lda #$4F + sta COLUBK + rts + + org $4415 + rorg $F415 + nop + nop + nop + jsr Sub4 + lda $FFE0 + + org $4FFC + rorg $FFFC + .word Start4 + .byte "04" + + seg bank5 + org $5000 + rorg $F000 +Start5 + lda $FFE0 + +Sub5 + lda #$5F + sta COLUBK + rts + + org $5415 + rorg $F415 + nop + nop + nop + nop + nop + nop + jsr Sub5 + lda $FFE0 + + org $5FFC + rorg $FFFC + .word Start5 + .byte "05" + + seg bank6 + org $6000 + rorg $F000 +Start6 + lda $FFE0 + +Sub6 + lda #$66 + sta COLUBK + rts + + org $6415 + rorg $F415 + nop + nop + nop + nop + nop + nop + nop + nop + nop + jsr Sub6 + lda $FFE0 + + org $6FFC + rorg $FFFC + .word Start6 + .byte "06" + + seg bank7 + org $7000 + rorg $F000 +Start7 + + lda $FFE0 + +Sub7 + lda #$7F + sta COLUBK + rts + + org $7430 + rorg $F430 + nop + nop + nop + jsr Sub7 + lda $FFE0 + + org $7FFC + rorg $FFFC + .word Start7 + .byte "07" + + seg bank8 + org $8000 + rorg $F000 +Start8 + lda $FFE0 + +Sub8 + lda #$8F + sta COLUBK + rts + + org $8430 + rorg $F430 + nop + nop + nop + nop + nop + nop + jsr Sub8 + lda $FFE0 + + org $8FFC + rorg $FFFC + .word Start8 + .byte "08" + + seg bank9 + org $9000 + rorg $F000 +Start9 + lda $FFE0 + +Sub9 + lda #$9F + sta COLUBK + rts + + org $9430 + rorg $F430 + nop + nop + nop + nop + nop + nop + nop + nop + nop + jsr Sub9 + lda $FFE0 + + org $9FFC + rorg $FFFC + .word Start9 + .byte "09" + + seg bankA + org $A000 + rorg $F000 +StartA + lda $FFE0 + +SubA + lda #$AF + sta COLUBK + rts + + org $A445 + rorg $F445 + nop + nop + nop + jsr SubA + lda $FFE0 + + org $AFFC + rorg $FFFC + .word StartA + .byte "0A" + + seg bankB + org $B000 + rorg $F000 +StartB + lda $FFE0 + +SubB + lda #$BF + sta COLUBK + rts + + org $B445 + rorg $F445 + nop + nop + nop + nop + nop + nop + jsr SubB + lda $FFE0 + + org $BFFC + rorg $FFFC + .word StartB + .byte "0B" + + seg bankC + org $C000 + rorg $F000 +StartC + lda $FFE0 + +SubC + lda #$CF + sta COLUBK + rts + + org $C445 + rorg $F445 + nop + nop + nop + nop + nop + nop + nop + nop + nop + jsr SubC + lda $FFE0 + + + org $CFFC + rorg $FFFC + .word StartC + .byte "0C" + + seg bankD + org $D000 + rorg $F000 +StartD + lda $FFE0 + +SubD + lda #$DF + sta COLUBK + rts + + org $D460 + rorg $F460 + nop + nop + nop + jsr SubD + lda $FFE0 + + org $DFFC + rorg $FFFC + .word StartD + .byte "0D" + + seg bankE + org $E000 + rorg $F000 +StartE + lda $FFE0 + +SubE + lda #$EF + sta COLUBK + rts + + org $E460 + rorg $F460 + nop + nop + nop + nop + nop + nop + jsr SubE + lda $FFE0 + + org $EFFC + rorg $FFFC + .word StartE + .byte "0E" + + seg bankF + org $F000 + rorg $F000 +StartF + lda $FFE0 + +SubF + lda #$FF + sta COLUBK + rts + + org $F460 + rorg $F460 + nop + nop + nop + nop + nop + nop + nop + nop + nop + jsr SubF + lda $FFE0 + + org $FFFC + rorg $FFFC + .word StartF + .byte "0F" diff --git a/test/roms/bankswitching/_code/EF.bin b/test/roms/bankswitching/_code/EF.bin new file mode 100644 index 000000000..8038b3c55 Binary files /dev/null and b/test/roms/bankswitching/_code/EF.bin differ diff --git a/test/roms/bankswitching/_code/EFSC.asm b/test/roms/bankswitching/_code/EFSC.asm new file mode 100644 index 000000000..30c8674c0 --- /dev/null +++ b/test/roms/bankswitching/_code/EFSC.asm @@ -0,0 +1,762 @@ +;;A bankswitching demo for the EFSC scheme. 16 4K banks accessed by $FFE0-$FFEF +;;Also includes 128b RAM mapped at $1000 for writes and $1100 for reads +;;By: Rick Skrbina 4/4/09 + + processor 6502 + include "vcs.h" + include "macro.h" + +Bank0_Color_Read equ Bank0_Color_Write+128 + + seg.u SC_RAM + org $1000 + +Bank0_Color_Write ds 1 + + seg.u vars + org $80 + + + seg bank0 + org $0000 + rorg $F000 + + repeat 256 + .byte $00 + repend +Start0 + nop + nop + nop + CLEAN_START + +Start_Frame + lda #2 + sta VSYNC + sta VBLANK + sta WSYNC + sta WSYNC + sta WSYNC + lsr + sta VSYNC + + ldy #37 +VerticalBlank + sta WSYNC + dey + bne VerticalBlank + + lda #0 + sta VBLANK + + clc + lda Bank0_Color_Read + adc #1 + sta Bank0_Color_Write + lda Bank0_Color_Read + sta COLUBK + + ldy #12 +Picture0 + sta WSYNC + dey + bne Picture0 + + jsr Swch1 + + ldy #12 +Picture1 + sta WSYNC + dey + bne Picture1 + + jsr Swch2 + + ldy #12 +Picture2 + sta WSYNC + dey + bne Picture2 + + jsr Swch3 + + ldy #12 +Picture3 + sta WSYNC + dey + bne Picture3 + + jsr Swch4 + + ldy #12 +Picture4 + sta WSYNC + dey + bne Picture4 + + jsr Swch5 + + ldy #12 +Picture5 + sta WSYNC + dey + bne Picture5 + + jsr Swch6 + + ldy #12 +Picture6 + sta WSYNC + dey + bne Picture6 + + jsr Swch7 + + ldy #12 +Picture7 + sta WSYNC + dey + bne Picture7 + + jsr Swch8 + + ldy #12 +Picture8 + sta WSYNC + dey + bne Picture8 + + jsr Swch9 + + ldy #12 +Picture9 + sta WSYNC + dey + bne Picture9 + + jsr SwchA + + ldy #12 +PictureA + sta WSYNC + dey + bne PictureA + + jsr SwchB + + ldy #12 +PictureB + sta WSYNC + dey + bne PictureB + + jsr SwchC + + ldy #12 +PictureC + sta WSYNC + dey + bne PictureC + + jsr SwchD + + ldy #12 +PictureD + sta WSYNC + dey + bne PictureD + + jsr SwchE + + ldy #12 +PictureE + sta WSYNC + dey + bne PictureE + + jsr SwchF + + ldy #12 +PictureF + sta WSYNC + dey + bne PictureF + + lda #2 + sta VBLANK + + ldy #30 +OverScan + sta WSYNC + dey + bne OverScan + + jmp Start_Frame + + org $0400 + rorg $F400 +Swch1 + lda $FFE1 +Swch2 + lda $FFE2 +Swch3 + lda $FFE3 + rts + rts + rts + rts + rts + rts + rts + rts + + org $0415 + rorg $F415 +Swch4 + lda $FFE4 +Swch5 + lda $FFE5 +Swch6 + lda $FFE6 + rts + rts + rts + rts + rts + rts + rts + rts + + org $0430 + rorg $F430 +Swch7 + lda $FFE7 +Swch8 + lda $FFE8 +Swch9 + lda $FFE9 + rts + rts + rts + rts + rts + rts + rts + rts + + org $0445 + rorg $F445 +SwchA + lda $FFEA +SwchB + lda $FFEB +SwchC + lda $FFEC + rts + rts + rts + rts + rts + rts + rts + rts + + org $0460 + rorg $F460 +SwchD + lda $FFED +SwchE + lda $FFEE +SwchF + lda $FFEF + rts + rts + rts + rts + rts + rts + rts + rts + + org $0FFC + .word Start0 + .byte "00" + + seg bank1 + org $1000 + rorg $F000 + repeat 256 + .byte $00 + repend +Start1 + lda $FFE0 + +Sub1 + lda #$1F + sta COLUBK + rts + + org $1400 + rorg $F400 + nop + nop + nop + jsr Sub1 + lda $FFE0 + + org $1FFC + rorg $FFFC + .word Start1 + .byte "01" + + seg bank2 + org $2000 + rorg $F000 + + repeat 256 + .byte $00 + repend +Start2 + lda $FFE0 + +Sub2 + lda #$2F + sta COLUBK + rts + + org $2400 + rorg $F400 + nop + nop + nop + nop + nop + nop + jsr Sub2 + lda $FFE0 + + org $2FFC + rorg $FFFC + .word Start2 + .byte "02" + + seg bank3 + org $3000 + rorg $F000 + + repeat 256 + .byte $00 + repend + +Start3 + lda $FFE0 + +Sub3 + lda #$3F + sta COLUBK + rts + + org $3400 + rorg $F400 + nop + nop + nop + nop + nop + nop + nop + nop + nop + jsr Sub3 + lda $FFE0 + + org $3FFC + rorg $FFFC + .word Start3 + .byte "03" + + seg bank4 + org $4000 + rorg $F000 + + repeat 256 + .byte $00 + repend + +Start4 + lda $FFE0 + +Sub4 + lda #$4F + sta COLUBK + rts + + org $4415 + rorg $F415 + nop + nop + nop + jsr Sub4 + lda $FFE0 + + org $4FFC + rorg $FFFC + .word Start4 + .byte "04" + + seg bank5 + org $5000 + rorg $F000 + + repeat 256 + .byte $00 + repend + +Start5 + lda $FFE0 + +Sub5 + lda #$5F + sta COLUBK + rts + + org $5415 + rorg $F415 + nop + nop + nop + nop + nop + nop + jsr Sub5 + lda $FFE0 + + org $5FFC + rorg $FFFC + .word Start5 + .byte "05" + + seg bank6 + org $6000 + rorg $F000 + + repeat 256 + .byte $00 + repend + +Start6 + lda $FFE0 + +Sub6 + lda #$66 + sta COLUBK + rts + + org $6415 + rorg $F415 + nop + nop + nop + nop + nop + nop + nop + nop + nop + jsr Sub6 + lda $FFE0 + + org $6FFC + rorg $FFFC + .word Start6 + .byte "06" + + seg bank7 + org $7000 + rorg $F000 + + repeat 256 + .byte $00 + repend + +Start7 + + lda $FFE0 + +Sub7 + lda #$7F + sta COLUBK + rts + + org $7430 + rorg $F430 + nop + nop + nop + jsr Sub7 + lda $FFE0 + + org $7FFC + rorg $FFFC + .word Start7 + .byte "07" + + seg bank8 + org $8000 + rorg $F000 + + repeat 256 + .byte $00 + repend + +Start8 + lda $FFE0 + +Sub8 + lda #$8F + sta COLUBK + rts + + org $8430 + rorg $F430 + nop + nop + nop + nop + nop + nop + jsr Sub8 + lda $FFE0 + + org $8FFC + rorg $FFFC + .word Start8 + .byte "08" + + seg bank9 + org $9000 + rorg $F000 + + repeat 256 + .byte $00 + repend + +Start9 + lda $FFE0 + +Sub9 + lda #$9F + sta COLUBK + rts + + org $9430 + rorg $F430 + nop + nop + nop + nop + nop + nop + nop + nop + nop + jsr Sub9 + lda $FFE0 + + org $9FFC + rorg $FFFC + .word Start9 + .byte "09" + + seg bankA + org $A000 + rorg $F000 + + repeat 256 + .byte $00 + repend + +StartA + lda $FFE0 + +SubA + lda #$AF + sta COLUBK + rts + + org $A445 + rorg $F445 + nop + nop + nop + jsr SubA + lda $FFE0 + + org $AFFC + rorg $FFFC + .word StartA + .byte "0A" + + seg bankB + org $B000 + rorg $F000 + + repeat 256 + .byte $00 + repend + +StartB + lda $FFE0 + +SubB + lda #$BF + sta COLUBK + rts + + org $B445 + rorg $F445 + nop + nop + nop + nop + nop + nop + jsr SubB + lda $FFE0 + + org $BFFC + rorg $FFFC + .word StartB + .byte "0B" + + seg bankC + org $C000 + rorg $F000 + + repeat 256 + .byte $00 + repend + +StartC + lda $FFE0 + +SubC + lda #$CF + sta COLUBK + rts + + org $C445 + rorg $F445 + nop + nop + nop + nop + nop + nop + nop + nop + nop + jsr SubC + lda $FFE0 + + + org $CFFC + rorg $FFFC + .word StartC + .byte "0C" + + seg bankD + org $D000 + rorg $F000 + + repeat 256 + .byte $00 + repend + +StartD + lda $FFE0 + +SubD + lda #$DF + sta COLUBK + rts + + org $D460 + rorg $F460 + nop + nop + nop + jsr SubD + lda $FFE0 + + org $DFFC + rorg $FFFC + .word StartD + .byte "0D" + + seg bankE + org $E000 + rorg $F000 + + repeat 256 + .byte $00 + repend + +StartE + lda $FFE0 + +SubE + lda #$EF + sta COLUBK + rts + + org $E460 + rorg $F460 + nop + nop + nop + nop + nop + nop + jsr SubE + lda $FFE0 + + org $EFFC + rorg $FFFC + .word StartE + .byte "0E" + + seg bankF + org $F000 + rorg $F000 + + repeat 256 + .byte $00 + repend + +StartF + lda $FFE0 + +SubF + lda #$FF + sta COLUBK + rts + + org $F460 + rorg $F460 + nop + nop + nop + nop + nop + nop + nop + nop + nop + jsr SubF + lda $FFE0 + + org $FFFC + rorg $FFFC + .word StartF + .byte "0F" diff --git a/test/roms/bankswitching/_code/EFSC.bin b/test/roms/bankswitching/_code/EFSC.bin new file mode 100644 index 000000000..8025a8145 Binary files /dev/null and b/test/roms/bankswitching/_code/EFSC.bin differ diff --git a/test/roms/bankswitching/_code/F0_MegaBoy.asm b/test/roms/bankswitching/_code/F0_MegaBoy.asm new file mode 100644 index 000000000..e21b8d5ff --- /dev/null +++ b/test/roms/bankswitching/_code/F0_MegaBoy.asm @@ -0,0 +1,761 @@ +;;A bankswitching demo for the F0 (Dynacom MegaBoy) BS technique. 16 4K banks (64K total) +;;3/26/09 By: Rick Skrbina + + processor 6502 + include "vcs.h" + include "macro.h" + + seg.u vars + org $80 + + seg bank0 + org $0000 + rorg $F000 +Start0 + CLEAN_START + + + lda #$0F + sta COLUBK + +Start_Frame + lda #2 + sta VBLANK + sta VSYNC + sta WSYNC + sta WSYNC + sta WSYNC + lsr + sta VSYNC + + ldy #37 +VerticalBlank + sta WSYNC + dey + bne VerticalBlank + + sta VBLANK + + lda #$0F + sta COLUBK + + ldy #9 +Picture0 + sta WSYNC + dey + bne Picture0 + + lda #1 + jsr Switch_Banks + + ldy #9 +Picture1 + sta WSYNC + dey + bne Picture1 + + lda #2 + jsr Switch_Banks + + ldy #9 +Picture2 + sta WSYNC + dey + bne Picture2 + + lda #3 + jsr Switch_Banks + + ldy #9 +Picture3 + sta WSYNC + dey + bne Picture3 + + lda #4 + jsr Switch_Banks + + ldy #9 +Picture4 + sta WSYNC + dey + bne Picture4 + + lda #5 + jsr Switch_Banks + + ldy #9 +Picture5 + sta WSYNC + dey + bne Picture5 + + lda #6 + jsr Switch_Banks + + ldy #9 +Picture6 + sta WSYNC + dey + bne Picture6 + + lda #7 + jsr Switch_Banks + + ldy #9 +Picture7 + sta WSYNC + dey + bne Picture7 + + lda #8 + jsr Switch_Banks + + ldy #9 +Picture8 + sta WSYNC + dey + bne Picture8 + + lda #9 + jsr Switch_Banks + + ldy #9 +Picture9 + sta WSYNC + dey + bne Picture9 + + lda #10 + jsr Switch_Banks + + ldy #9 +Picture10 + sta WSYNC + dey + bne Picture10 + + lda #11 + jsr Switch_Banks + + ldy #9 +Picture11 + sta WSYNC + dey + bne Picture11 + + lda #9 + jsr Switch_Banks + + ldy #9 +Picture12 + sta WSYNC + dey + bne Picture12 + + lda #13 + jsr Switch_Banks + + ldy #9 +Picture13 + sta WSYNC + dey + bne Picture13 + + lda #14 + jsr Switch_Banks + + ldy #9 +Picture14 + sta WSYNC + dey + bne Picture14 + + lda #15 + jsr Switch_Banks + + ldy #9 +Picture15 + sta WSYNC + dey + bne Picture15 + + lda #2 + sta VBLANK + + ldy #30 +OverScan + sta WSYNC + dey + bne OverScan + + jmp Start_Frame + + org $0E00 + rorg $FE00 + +Switch_Banks + cmp Identity0 + beq Stayin0 + sta $1FF0 + jmp Switch_Banks +Stayin0 + rts + + +Identity0 + .byte $00 + + org $0FFC + rorg $FFFC + .word Start0 + .byte "B0" + + seg Bank1 + org $1000 + rorg $F000 +Start1 + CLEAN_START + lda #$F0 + sta $FE + lda #0 + jmp B1_swch + +B1_Color + lda #$1F + sta COLUBK + + lda #0 + jmp B1_swch + + org $1E00 + rorg $FE00 +B1_swch + cmp Identity1 + beq Stayin1 + sta $1FF0 + jmp B1_swch +Stayin1 + jmp B1_Color + + +Identity1 + .byte $01 + + org $1FFC + .word Start1 + .byte "B1" + + seg Bank2 + org $2000 + rorg $F000 +Start2 + CLEAN_START + lda #$F0 + sta $FE + lda #0 + jmp B2_swch + +B2_Color + + lda #$2F + sta COLUBK + + lda #0 + jmp B2_swch + + org $2E00 + rorg $FE00 +B2_swch + cmp Identity2 + beq Stayin2 + sta $1FF0 + jmp B2_swch +Stayin2 + jmp B2_Color + +Identity2 + .byte $02 + + org $2FFC + rorg $FFFC + .word Start2 + .byte "B2" + + seg Bank3 + org $3000 +Start3 + CLEAN_START + lda #$F0 + sta $FE + lda #0 + jmp B3_swch + +B3_Color + lda #$3F + sta COLUBK + lda #0 + jmp B3_swch + + org $3E00 + rorg $FE00 +B3_swch + cmp Identity3 + beq Stayin2 + sta $1FF0 + jmp B3_swch +Stayin3 + jmp B3_Color + +Identity3 + .byte $03 + + org $3FFC + rorg $FFFC + .word Start3 + .byte "B3" + + seg Bank4 + org $4000 + rorg $F000 +Start4 + CLEAN_START + lda #$F0 + sta $FE + lda #0 + jmp B4_swch + +B4_Color + + lda #$4F + sta COLUBK + + lda #0 + jmp B4_swch + + org $4E00 + rorg $FE00 + +B4_swch + cmp Identity4 + beq Stayin4 + sta $1FF0 + jmp B4_swch +Stayin4 + jmp B4_Color + +Identity4 + .byte $04 + + org $4FFC + rorg $FFFC + .word Start4 + .byte "B4" + + seg Bank5 + org $5000 + rorg $F000 +Start5 + CLEAN_START + lda #$F0 + sta $FE + lda #0 + jmp B5_swch + +B5_Color + + lda #$5F + sta COLUBK + + lda #0 + jmp B5_swch + + org $5E00 + rorg $FE00 + +B5_swch + cmp Identity5 + beq Stayin5 + sta $1FF0 + jmp B5_swch +Stayin5 + jmp B5_Color + +Identity5 + .byte $05 + + org $5FFC + rorg $FFFC + .word Start5 + .byte "B5" + + seg Bank6 + org $6000 + rorg $F000 +Start6 + CLEAN_START + lda #$F0 + sta $FE + lda #0 + jmp B6_swch + +B6_Color + + lda #$6F + sta COLUBK + + lda #0 + jmp B6_swch + + org $6E00 + rorg $FE00 + +B6_swch + cmp Identity6 + beq Stayin6 + sta $1FF0 + jmp B6_swch +Stayin6 + jmp B6_Color + +Identity6 + .byte $06 + + org $6FFC + rorg $FFFC + .word Start6 + .byte "B6" + + seg Bank7 + org $7000 + rorg $F000 +Start7 + CLEAN_START + lda #$F0 + sta $FE + lda #0 + jmp B7_swch + +B7_Color + + lda #$7F + sta COLUBK + + lda #0 + jmp B7_swch + + org $7E00 + rorg $FE00 + +B7_swch + cmp Identity7 + beq Stayin7 + sta $1FF0 + jmp B7_swch +Stayin7 + jmp B7_Color + +Identity7 + .byte $07 + + org $7FFC + rorg $FFFC + .word Start7 + .byte "B7" + + seg Bank8 + org $8000 + rorg $F000 +Start8 + CLEAN_START + lda #$F0 + sta $FE + lda #0 + jmp B8_swch + +B8_Color + + lda #$8F + sta COLUBK + + lda #0 + jmp B8_swch + + org $8E00 + rorg $FE00 + +B8_swch + cmp Identity8 + beq Stayin8 + sta $1FF0 + jmp B8_swch +Stayin8 + jmp B8_Color + +Identity8 + .byte $08 + + org $8FFC + rorg $FFFC + .word Start8 + .byte "B8" + + seg Bank9 + org $9000 + rorg $F000 +Start9 + CLEAN_START + lda #$F0 + sta $FE + lda #0 + jmp B9_swch + +B9_Color + + lda #$9F + sta COLUBK + + lda #0 + jmp B9_swch + + org $9E00 + rorg $FE00 + +B9_swch + cmp Identity9 + beq Stayin9 + sta $1FF0 + jmp B9_swch +Stayin9 + jmp B9_Color + +Identity9 + .byte $09 + + org $9FFC + rorg $FFFC + .word Start9 + .byte "B9" + + seg BankA + org $A000 + rorg $F000 +StartA + CLEAN_START + lda #$F0 + sta $FE + lda #0 + jmp BA_swch + +BA_Color + + lda #$AF + sta COLUBK + + lda #0 + jmp BA_swch + + org $AE00 + rorg $FE00 + +BA_swch + cmp IdentityA + beq StayinA + sta $1FF0 + jmp BA_swch +StayinA + jmp BA_Color + +IdentityA + .byte $0A + + org $AFFC + rorg $FFFC + .word StartA + .byte "BA" + + seg BankB + org $B000 + rorg $F000 +StartB + CLEAN_START + lda #$F0 + sta $FE + lda #0 + jmp BB_swch + +BB_Color + + lda #$BF + sta COLUBK + lda #0 + jmp BB_swch + + org $BE00 + rorg $FE00 + +BB_swch + cmp IdentityB + beq StayinB + sta $1FF0 + jmp BB_swch +StayinB + jmp BB_Color + +IdentityB + .byte $0B + + org $BFFC + rorg $FFFC + .word StartB + .byte "BB" + + seg BankC + org $C000 + rorg $F000 +StartC + CLEAN_START + lda #$F0 + sta $FE + lda #0 + jmp BC_swch + +BC_Color + + lda #$CF + sta COLUBK + + lda #0 + jmp BC_swch + + org $CE00 + rorg $FE00 + +BC_swch + cmp IdentityC + beq StayinC + sta $1FF0 + jmp BC_swch +StayinC + jmp BC_Color + +IdentityC + .byte $0C + + org $CFFC + rorg $FFFC + .word StartC + .byte "BC" + + seg BankD + org $D000 + rorg $F000 +StartD + CLEAN_START + lda #$F0 + sta $FE + lda #0 + jmp BD_swch + +BD_Color + + lda #$DF + sta COLUBK + + lda #0 + jmp BD_swch + + org $DE00 + rorg $FE00 + +BD_swch + cmp IdentityD + beq StayinD + sta $1FF0 + jmp BD_swch +StayinD + jmp BD_Color + +IdentityD + .byte $0D + + org $DFFC + rorg $FFFC + .word StartD + .byte "BD" + + seg BankE + org $E000 + rorg $F000 +StartE + CLEAN_START + lda #$F0 + sta $FE + lda #0 + jmp BE_swch + +BE_Color + + lda #$EF + sta COLUBK + + lda #0 + jmp BE_swch + + org $EE00 + rorg $FE00 + +BE_swch + cmp IdentityE + beq StayinE + sta $1FF0 + jmp BE_swch +StayinE + jmp BE_Color + +IdentityE + + .byte $0E + + org $EFFC + rorg $FFFC + .word StartE + .byte "BE" + + seg BankF + org $F000 + rorg $F000 +StartF + CLEAN_START + lda #$F0 + sta $FE + lda #0 + jmp BF_swch + +BF_Color + + lda #$FF + sta COLUBK + + lda #0 + jmp BF_swch + + org $FE00 + rorg $FE00 + +BF_swch + cmp IdentityF + beq StayinF + sta $1FF0 + jmp BF_swch +StayinF + jmp BF_Color + +IdentityF + .byte $0F + + org $FFFC + rorg $FFFC + .word StartF + .byte "BF" + diff --git a/test/roms/bankswitching/_code/F0_MegaBoy.bin b/test/roms/bankswitching/_code/F0_MegaBoy.bin new file mode 100644 index 000000000..d4ec07408 Binary files /dev/null and b/test/roms/bankswitching/_code/F0_MegaBoy.bin differ diff --git a/test/roms/bankswitching/_code/F4.asm b/test/roms/bankswitching/_code/F4.asm new file mode 100644 index 000000000..987f33930 --- /dev/null +++ b/test/roms/bankswitching/_code/F4.asm @@ -0,0 +1,381 @@ +;;A demo for the F4 banking scheme. 8 4K banks (32K total) +;;By: Rick Skrbina 3/31/09 + + processor 6502 + include "vcs.h" + include "macro.h" + + seg.u vars + org $80 + + + seg bank0 + org $8000 + rorg $F000 + +Start0 + nop + nop + nop + + CLEAN_START + +Start_Frame + lda #2 + sta VBLANK + sta VSYNC + sta WSYNC + sta WSYNC + sta WSYNC + lsr + sta VSYNC + + lda #$0F + sta COLUBK + + ldy #37 +VerticalBlank + sta WSYNC + dey + bne VerticalBlank + + lda #0 + sta VBLANK + + sta WSYNC + + ldy #23 +Picture0 + sta WSYNC + dey + bne Picture0 + + jsr Swch1 + + ldy #23 +Picture1 + sta WSYNC + dey + bne Picture1 + + jsr Swch2 + + ldy #23 +Picture2 + sta WSYNC + dey + bne Picture2 + + jsr Swch3 + + ldy #23 +Picture3 + sta WSYNC + dey + bne Picture3 + + jsr Swch4 + + ldy #23 +Picture4 + sta WSYNC + dey + bne Picture4 + + jsr Swch5 + + ldy #23 +Picture5 + sta WSYNC + dey + bne Picture5 + + jsr Swch6 + + ldy #23 +Picture6 + sta WSYNC + dey + bne Picture6 + + jsr Swch7 + + ldy #23 +Picture7 + sta WSYNC + dey + bne Picture7 + + lda #2 + sta VBLANK + + ldy #30 +OverScan + sta WSYNC + dey + bne OverScan + + jmp Start_Frame + + org $8FC0 + rorg $FFC0 + +Swch7 + lda $FFFB + nop + nop + nop + nop + nop + nop + rts + + + org $8FD0 + rorg $FFD0 +Swch1 + lda $FFF5 +Swch2 + lda $FFF6 +Swch3 + lda $FFF7 + + rts + rts + rts + rts + rts + rts + rts + + org $8FE0 + rorg $FFE0 +Swch4 + lda $FFF8 +Swch5 + lda $FFF9 +Swch6 + lda $FFFA + + rts + rts + rts + rts + rts + rts + rts + + + org $8FFC + rorg $FFFC + .word Start0 + .byte "B0" + + seg bank1 + org $9000 + rorg $F000 + +Start1 + lda $FFF4 + +Bank1Sub + lda #$1F + sta WSYNC + sta COLUBK + rts + + org $9FD0 + rorg $FFD0 + + nop + nop + nop + jsr Bank1Sub + sta $FFF4 + + org $9FFC + rorg $FFFC + .word Start1 + .byte "B1" + + seg bank2 + org $A000 + rorg $F000 +Start2 + lda $FFF4 + +Bank2Sub + lda #$2F + sta WSYNC + sta COLUBK + rts + + org $AFD0 + rorg $FFD0 + + nop + nop + nop + nop + nop + nop + jsr Bank2Sub + sta $FFF4 + + org $AFFC + rorg $FFFC + .word Start2 + .byte "B2" + + seg bank3 + org $B000 + rorg $F000 +Start3 + lda $FFF4 + +Bank3Sub + lda #$3F + sta WSYNC + sta COLUBK + rts + + org $BFD0 + rorg $FFD0 + + nop + nop + nop + nop + nop + nop + nop + nop + nop + jsr Bank3Sub + sta $FFF4 + + org $BFFC + rorg $FFFC + .word Start3 + .byte "B3" + + seg bank4 + org $C000 + rorg $F000 + +Start4 + lda $FFF4 + +Bank4Sub + lda #$4F + sta WSYNC + sta COLUBK + rts + + org $CFE0 + rorg $FFE0 + + nop + nop + nop + jsr Bank4Sub + sta $FFF4 + + org $CFFC + rorg $FFFC + .word Start4 + .byte "B4" + + seg bank5 + org $D000 + rorg $F000 + +Start5 + lda $FFF4 + +Bank5Sub + lda #$5F + sta WSYNC + sta COLUBK + rts + + org $DFE0 + rorg $FFE0 + + nop + nop + nop + nop + nop + nop + jsr Bank5Sub + sta $FFF4 + + org $DFFC + rorg $FFFC + .word Start5 + .byte "B5" + + seg bank6 + org $E000 + rorg $F000 + +Start6 + lda $FFF4 + +Bank6Sub + + lda #$6F + sta WSYNC + sta COLUBK + rts + + org $EFE0 + rorg $FFE0 + + nop + nop + nop + nop + nop + nop + nop + nop + nop + jsr Bank6Sub + sta $FFF4 + + org $EFFC + rorg $FFFC + .word Start6 + .byte "B6" + + seg bank7 + org $F000 + rorg $F000 + +Start7 + lda $FFF4 + +Bank7Sub + + lda #$8F + sta WSYNC + sta COLUBK + rts + + org $FFC0 + rorg $FFC0 + + nop + nop + nop + jsr Bank7Sub + sta $FFF4 + + org $FFFC + rorg $FFFC + .word Start7 + .byte "B7" + + diff --git a/test/roms/bankswitching/_code/F4.bin b/test/roms/bankswitching/_code/F4.bin new file mode 100644 index 000000000..1a2470daa Binary files /dev/null and b/test/roms/bankswitching/_code/F4.bin differ diff --git a/test/roms/bankswitching/_code/F4SC.asm b/test/roms/bankswitching/_code/F4SC.asm new file mode 100644 index 000000000..b5f1aed37 --- /dev/null +++ b/test/roms/bankswitching/_code/F4SC.asm @@ -0,0 +1,427 @@ +;;A demo for the F4 banking scheme. 8 4K banks (32K total) +;;By: Rick Skrbina 4/30/09 + + processor 6502 + include "vcs.h" + include "macro.h" + +Bank0_Read equ Bank0_Write+128 + + seg.u vars + org $80 + + seg.u SC_RAM + org $1000 +Bank0_Write ds 1 + + seg bank0 + org $8000 + rorg $F000 + + repeat 256 + .byte $FF + repend + +Start0 + nop + nop + nop + + CLEAN_START + +Start_Frame + lda #2 + sta VBLANK + sta VSYNC + sta WSYNC + sta WSYNC + sta WSYNC + lsr + sta VSYNC + +; lda #$0F +; sta COLUBK + + ldy #37 +VerticalBlank + sta WSYNC + dey + bne VerticalBlank + + lda #0 + sta VBLANK + + clc + lda Bank0_Read + adc #1 + sta Bank0_Write + lda Bank0_Read + sta COLUBK + + sta WSYNC + + ldy #23 +Picture0 + sta WSYNC + dey + bne Picture0 + + jsr Swch1 + + ldy #23 +Picture1 + sta WSYNC + dey + bne Picture1 + + jsr Swch2 + + ldy #23 +Picture2 + sta WSYNC + dey + bne Picture2 + + jsr Swch3 + + ldy #23 +Picture3 + sta WSYNC + dey + bne Picture3 + + jsr Swch4 + + ldy #23 +Picture4 + sta WSYNC + dey + bne Picture4 + + jsr Swch5 + + ldy #23 +Picture5 + sta WSYNC + dey + bne Picture5 + + jsr Swch6 + + ldy #23 +Picture6 + sta WSYNC + dey + bne Picture6 + + jsr Swch7 + + ldy #23 +Picture7 + sta WSYNC + dey + bne Picture7 + + lda #2 + sta VBLANK + + ldy #30 +OverScan + sta WSYNC + dey + bne OverScan + + jmp Start_Frame + + org $8FC0 + rorg $FFC0 + +Swch7 + lda $FFFB + nop + nop + nop + nop + nop + nop + rts + + + org $8FD0 + rorg $FFD0 +Swch1 + lda $FFF5 +Swch2 + lda $FFF6 +Swch3 + lda $FFF7 + + rts + rts + rts + rts + rts + rts + rts + + org $8FE0 + rorg $FFE0 +Swch4 + lda $FFF8 +Swch5 + lda $FFF9 +Swch6 + lda $FFFA + + rts + rts + rts + rts + rts + rts + rts + + + org $8FFC + rorg $FFFC + .word Start0 + .byte "B0" + + seg bank1 + org $9000 + rorg $F000 + + repeat 256 + .byte $FF + repend + +Start1 + lda $FFF4 + +Bank1Sub + lda #$1F + sta WSYNC + sta COLUBK + rts + + org $9FD0 + rorg $FFD0 + + nop + nop + nop + jsr Bank1Sub + sta $FFF4 + + org $9FFC + rorg $FFFC + .word Start1 + .byte "B1" + + seg bank2 + org $A000 + rorg $F000 + + repeat 256 + .byte $FF + repend + +Start2 + lda $FFF4 + +Bank2Sub + lda #$2F + sta WSYNC + sta COLUBK + rts + + org $AFD0 + rorg $FFD0 + + nop + nop + nop + nop + nop + nop + jsr Bank2Sub + sta $FFF4 + + org $AFFC + rorg $FFFC + .word Start2 + .byte "B2" + + seg bank3 + org $B000 + rorg $F000 + + repeat 256 + .byte $FF + repend + +Start3 + lda $FFF4 + +Bank3Sub + lda #$3F + sta WSYNC + sta COLUBK + rts + + org $BFD0 + rorg $FFD0 + + nop + nop + nop + nop + nop + nop + nop + nop + nop + jsr Bank3Sub + sta $FFF4 + + org $BFFC + rorg $FFFC + .word Start3 + .byte "B3" + + seg bank4 + org $C000 + rorg $F000 + + repeat 256 + .byte $FF + repend + +Start4 + lda $FFF4 + +Bank4Sub + lda #$4F + sta WSYNC + sta COLUBK + rts + + org $CFE0 + rorg $FFE0 + + nop + nop + nop + jsr Bank4Sub + sta $FFF4 + + org $CFFC + rorg $FFFC + .word Start4 + .byte "B4" + + seg bank5 + org $D000 + rorg $F000 + + repeat 256 + .byte $FF + repend + +Start5 + lda $FFF4 + +Bank5Sub + lda #$5F + sta WSYNC + sta COLUBK + rts + + org $DFE0 + rorg $FFE0 + + nop + nop + nop + nop + nop + nop + jsr Bank5Sub + sta $FFF4 + + org $DFFC + rorg $FFFC + .word Start5 + .byte "B5" + + seg bank6 + org $E000 + rorg $F000 + + repeat 256 + .byte $FF + repend + +Start6 + lda $FFF4 + +Bank6Sub + + lda #$6F + sta WSYNC + sta COLUBK + rts + + org $EFE0 + rorg $FFE0 + + nop + nop + nop + nop + nop + nop + nop + nop + nop + jsr Bank6Sub + sta $FFF4 + + org $EFFC + rorg $FFFC + .word Start6 + .byte "B6" + + seg bank7 + org $F000 + rorg $F000 + + repeat 256 + .byte $FF + repend + +Start7 + lda $FFF4 + +Bank7Sub + + lda #$8F + sta WSYNC + sta COLUBK + rts + + org $FFC0 + rorg $FFC0 + + nop + nop + nop + jsr Bank7Sub + sta $FFF4 + + org $FFFC + rorg $FFFC + .word Start7 + .byte "B7" + + diff --git a/test/roms/bankswitching/_code/F4SC.bin b/test/roms/bankswitching/_code/F4SC.bin new file mode 100644 index 000000000..a8cdd0b6a Binary files /dev/null and b/test/roms/bankswitching/_code/F4SC.bin differ diff --git a/test/roms/bankswitching/_code/F6.asm b/test/roms/bankswitching/_code/F6.asm new file mode 100644 index 000000000..02da38f30 --- /dev/null +++ b/test/roms/bankswitching/_code/F6.asm @@ -0,0 +1,189 @@ +;;A bankswitching demo fhr the F6 BS technique. 4 4K banks (16K total) +;;By: Rick Skrbina 3/28/09 + + processor 6502 + include "vcs.h" + include "macro.h" + + seg.u vars + org $80 + + + seg bank0 + org $C000 + rorg $F000 + + nop + nop + nop +Start + CLEAN_START + +Start_Frame + lda #2 + sta VBLANK + sta VSYNC + sta WSYNC + sta WSYNC + sta WSYNC + lsr + sta VSYNC + + ldy #37 +VerticalBlank + sta WSYNC + dey + bne VerticalBlank + + lda #0 + sta VBLANK + + lda #$0F + sta COLUBK + + ldy #48 +Picture1 + sta WSYNC + dey + bne Picture1 + + jsr Swch1 + + ldy #48 +Picture2 + sta WSYNC + dey + bne Picture2 + + jsr Swch2 + + ldy #48 +Picture3 + sta WSYNC + dey + bne Picture3 + + jsr Swch3 + + ldy #48 +Picture4 + sta WSYNC + dey + bne Picture4 + + lda #2 + sta VBLANK + + ldy #30 +OverScan + sta WSYNC + dey + bne OverScan + + jmp Start_Frame + + org $CFE0 + rorg $FFE0 +Swch1 + lda $FFF7 +Swch2 + lda $FFF8 +Swch3 + lda $FFF9 + + rts + rts + rts + rts + rts + rts + rts + + org $CFFC + rorg $FFFC + .word Start + .byte "B0" + + seg bank1 + org $D000 + rorg $F000 +Start1 + lda $1FF6 + +Bank1Sub + lda #$1F + sta COLUBK + rts + + org $DFE0 + rorg $FFE0 + + nop + nop + nop + jsr Bank1Sub + lda $FFF6 + + org $DFFC + rorg $FFFC + .word Start + .byte "B1" + + seg bank2 + org $E000 + rorg $F000 +Start2 + lda $1FF6 + +Bank2Sub + lda #$4F + sta COLUBK + rts + + org $EFE0 + rorg $FFE0 + + nop + nop + nop + nop + nop + nop + jsr Bank2Sub + lda $FFF6 + + org $EFFC + rorg $FFFC + .word Start2 + .byte "B2" + + seg bank3 + org $F000 + rorg $F000 +Start3 + lda $1FF6 + +Bank3Sub + lda #$8F + sta COLUBK + rts + + org $FFE0 + rorg $FFE0 + + nop + nop + nop + nop + nop + nop + nop + nop + nop + jsr Bank3Sub + lda $FFF6 + + org $FFFC + rorg $FFFC + .word Start3 + .byte "B3" diff --git a/test/roms/bankswitching/_code/F6.bin b/test/roms/bankswitching/_code/F6.bin new file mode 100644 index 000000000..853a3e97d Binary files /dev/null and b/test/roms/bankswitching/_code/F6.bin differ diff --git a/test/roms/bankswitching/_code/F6SC.asm b/test/roms/bankswitching/_code/F6SC.asm new file mode 100644 index 000000000..ee5f5ea90 --- /dev/null +++ b/test/roms/bankswitching/_code/F6SC.asm @@ -0,0 +1,247 @@ +;;A bankswitching demo fhr the F6SC BS technique. 4 4K banks + 128b RAM (16K total) +;;By: Rick Skrbina 3/31/09 + + processor 6502 + include "vcs.h" + include "macro.h" + +Color0_Read equ Color0_Write+128 +Color1_Read equ Color1_Write+128 +Color2_Read equ Color2_Write+128 +Color3_Read equ Color3_Write+128 + + seg.u vars + org $80 + + + seg.u SuperChip_RAM + org $1000 +Color0_Write ds 1 +Color1_Write ds 1 +Color2_Write ds 1 +Color3_Write ds 1 + + seg bank0 + org $C000 + rorg $F000 + + repeat 256 + .byte $00 + repend + +Start + nop + nop + nop + + CLEAN_START + + lda #$0F + sta Color0_Write + lda #$1F + sta Color1_Write + lda #$4F + sta Color2_Write + lda #$8F + sta Color3_Write + +Start_Frame + lda #2 + sta VBLANK + sta VSYNC + sta WSYNC + sta WSYNC + sta WSYNC + lsr + sta VSYNC + + clc + lda Color0_Read + adc #1 + sta Color0_Write + sta COLUBK + + ldy #37 +VerticalBlank + sta WSYNC + dey + bne VerticalBlank + + + + lda #0 + sta VBLANK + + ldy #48 +Picture1 + sta WSYNC + dey + bne Picture1 + + jsr Swch1 + + ldy #47 +Picture2 + sta WSYNC + dey + bne Picture2 + + jsr Swch2 + + ldy #47 +Picture3 + sta WSYNC + dey + bne Picture3 + + jsr Swch3 + + ldy #47 +Picture4 + sta WSYNC + dey + bne Picture4 + + lda #2 + sta VBLANK + + ldy #30 +OverScan + sta WSYNC + dey + bne OverScan + + jmp Start_Frame + + org $CFE0 + rorg $FFE0 +Swch1 + lda $FFF7 +Swch2 + lda $FFF8 +Swch3 + lda $FFF9 + + rts + rts + rts + rts + rts + rts + rts + + org $CFFC + rorg $FFFC + .word Start + .byte "B0" + + seg bank1 + org $D000 + rorg $F000 + + repeat 256 + .byte $00 + repend +Start1 + lda $1FF6 + +Bank1Sub + clc + lda Color1_Read + adc #1 + sta Color1_Write + sta WSYNC + sta COLUBK + rts + + org $DFE0 + rorg $FFE0 + + nop + nop + nop + jsr Bank1Sub + lda $FFF6 + + org $DFFC + rorg $FFFC + .word Start + .byte "B1" + + seg bank2 + org $E000 + rorg $F000 + + repeat 256 + .byte $00 + repend + +Start2 + lda $1FF6 + +Bank2Sub + clc + lda Color2_Read + adc #1 + sta Color2_Write + sta WSYNC + sta COLUBK + rts + + org $EFE0 + rorg $FFE0 + + nop + nop + nop + nop + nop + nop + jsr Bank2Sub + lda $FFF6 + + org $EFFC + rorg $FFFC + .word Start2 + .byte "B2" + + seg bank3 + org $F000 + rorg $F000 + + repeat 256 + .byte $00 + repend + +Start3 + lda $1FF6 + +Bank3Sub + clc + lda Color3_Read + adc #1 + sta Color3_Write + lda Color3_Read + sta WSYNC + sta COLUBK + rts + + org $FFE0 + rorg $FFE0 + + nop + nop + nop + nop + nop + nop + nop + nop + nop + jsr Bank3Sub + lda $FFF6 + + org $FFFC + rorg $FFFC + .word Start3 + .byte "B3" diff --git a/test/roms/bankswitching/_code/F6SC.bin b/test/roms/bankswitching/_code/F6SC.bin new file mode 100644 index 000000000..a1938ca42 Binary files /dev/null and b/test/roms/bankswitching/_code/F6SC.bin differ diff --git a/test/roms/bankswitching/_code/F8.asm b/test/roms/bankswitching/_code/F8.asm new file mode 100644 index 000000000..0405af8e4 --- /dev/null +++ b/test/roms/bankswitching/_code/F8.asm @@ -0,0 +1,116 @@ +;;A bankswitching demo for the F8 BS technique. 2 4K banks (8K total) +;;By: Rick Skrbina + processor 6502 + include "vcs.h" + include "macro.h" + + seg.u vars + org $80 + +PF_Color ds 1 + + seg Bank_0 + + org $E000 + rorg $F000 + + nop + nop + nop +Start_0 + CLEAN_START +; jsr Call_1 + lda #$FF + sta PF1 +StartFrame + lda #2 + sta VBLANK + sta VSYNC + + sta WSYNC + sta WSYNC + sta WSYNC + + lda #0 + sta VSYNC + + ldy #37 +Vert + sta WSYNC + dey + bne Vert + + lda #0 + sta VBLANK + + ldy #192 +Pic + sta WSYNC + dey + bne Pic + + lda #2 + sta VBLANK + + ldy #30 +Over + sta WSYNC + dey + bne Over + + jsr Call_1 + + lda PF_Color + sta COLUPF + + + jmp StartFrame + + + + + org $EFE0 + rorg $FFE0 +Call_1 + stx $FFF9 + + nop + nop + nop + nop + nop + nop + + rts + + org $EFF8 + rorg $FFF8 + .word $FFFF + .word Start_0 + .word Start_0 + .word Start_0 + + seg Bank_1 + + org $F000 + rorg $F000 + +Init_1 + sta $FFF8 +Bank1_Sub +; lda #$0F +; sta COLUPF + inc PF_Color + rts + + org $FFE3 + rorg $FFE3 + jsr Bank1_Sub + stx $FFF8 + + org $FFF8 + .word $FFFF + .word Init_1 + .word Init_1 + .word Init_1 + diff --git a/test/roms/bankswitching/_code/F8.bin b/test/roms/bankswitching/_code/F8.bin new file mode 100644 index 000000000..d6d5928d2 Binary files /dev/null and b/test/roms/bankswitching/_code/F8.bin differ diff --git a/test/roms/bankswitching/_code/F8SC.asm b/test/roms/bankswitching/_code/F8SC.asm new file mode 100644 index 000000000..26aa08552 --- /dev/null +++ b/test/roms/bankswitching/_code/F8SC.asm @@ -0,0 +1,135 @@ +;;A bankswitching demo for the F8SC scheme. 2 4K banks of ROM + 128 bytes of RAM +;;By: Rick Skrbina 3/29/09 + + processor 6502 + include "vcs.h" + include "macro.h" + +PF_Color_Read equ PF_Color_Write+128 + seg.u vars + org $80 + + + + seg.u SC_RAM_vars + org $1000 +PF_Color_Write ds 1 + + seg Bank_0 + + org $E000 + rorg $F000 + + repeat 256 + .byte $00 + repend + +Start_0 + nop + nop + nop + + CLEAN_START +; jsr Call_1 + lda #$FF + sta PF1 +StartFrame + lda #2 + sta VBLANK + sta VSYNC + + sta WSYNC + sta WSYNC + sta WSYNC + + lda #0 + sta VSYNC + + ldy #37 +Vert + sta WSYNC + dey + bne Vert + + lda #0 + sta VBLANK + + ldy #192 +Pic + sta WSYNC + dey + bne Pic + + lda #2 + sta VBLANK + + ldy #30 +Over + sta WSYNC + dey + bne Over + + jsr Call_1 + + lda PF_Color_Read + sta COLUPF + + + jmp StartFrame + + + + + org $EFE0 + rorg $FFE0 +Call_1 + stx $FFF9 + + nop + nop + nop + nop + nop + nop + + rts + + org $EFF8 + rorg $FFF8 + .word $FFFF + .word Start_0 + .word Start_0 + .word Start_0 + + seg Bank_1 + + org $F000 + rorg $F000 + + repeat 256 + .byte $00 + repend + +Init_1 + sta $FFF8 +Bank1_Sub +; lda #$0F +; sta COLUPF +; inc PF_Color + clc + lda PF_Color_Read + adc #1 + sta PF_Color_Write + rts + + org $FFE3 + rorg $FFE3 + jsr Bank1_Sub + stx $FFF8 + + org $FFF8 + .word $FFFF + .word Init_1 + .word Init_1 + .word Init_1 + diff --git a/test/roms/bankswitching/_code/F8SC.bin b/test/roms/bankswitching/_code/F8SC.bin new file mode 100644 index 000000000..7c3cfba1e Binary files /dev/null and b/test/roms/bankswitching/_code/F8SC.bin differ diff --git a/test/roms/bankswitching/_code/FA_CBS+RAM.asm b/test/roms/bankswitching/_code/FA_CBS+RAM.asm new file mode 100644 index 000000000..a38d240ea --- /dev/null +++ b/test/roms/bankswitching/_code/FA_CBS+RAM.asm @@ -0,0 +1,192 @@ +;;A demo using CBS Electronic's FA (12K) + RAM (256 bytes) bankswitching scheme +;;3/20/09 By: Rick Skrbina + + processor 6502 + include "vcs.h" + include "macro.h" + +;BG_Color_Read equ BG_Color_Write+256 +;PF_Color_Read equ PF_Color_Write+256 +;BG_Write equ $1000 +BG_Read equ BG_Write+256 +PF_Read equ PF_Write+256 + + seg.u RIOT_vars + org $80 + + + seg.u FA_RAM_vars + org $1000 +BG_Write ds 1 +PF_Write ds 1 + + seg bank_0 + org $D000 + rorg $F000 + + repeat 512 + .byte $D0 + repend +Bank0 + + nop + nop + nop + + CLEAN_START + + lda #$FF + sta PF_Write + sta PF1 + + lda #$0F + sta COLUBK + +Start_Frame + + lda #2 + sta VBLANK + sta VSYNC + + sta WSYNC + sta WSYNC + sta WSYNC + + lda #0 + sta VSYNC + + ldy #37 +VerticalBlank + sta WSYNC + dey + bne VerticalBlank + + lda #0 + sta VBLANK + + ldy #192 +Picture + sta WSYNC + dey + bne Picture + + lda #2 + sta VBLANK + + ldy #30 +Overscan + sta WSYNC + dey + bne Overscan + + jsr Switch_To_1 + jsr Switch_To_2 + + lda BG_Read + sta COLUBK + + lda PF_Read + sta COLUPF + + jmp Start_Frame + + org $D300 + rorg $F300 + +Switch_To_1 ;$F300 + + lda $FFF9 + + nop + nop + nop + + nop + nop + nop + + rts + +Switch_To_2 ;$ + + lda $FFFA + + nop + nop + nop + + nop + nop + nop + + rts + + + org $DFFC + rorg $FFFC + .word Bank0 + .byte "B0" + + seg bank_1 + org $E000 + rorg $F000 + + repeat 512 + .byte $00 + repend + +Bank1 + lda $FFF8 + +Change_BG_Color + + clc + lda BG_Read + adc #1 + sta BG_Write + + rts + + org $E303 + rorg $F303 + + + jsr Change_BG_Color + lda $FFF8 + + org $EFFC + rorg $FFFC + .word Bank1 + .byte "B1" + + seg bank_2 + org $F000 + rorg $F000 + + repeat 512 + .byte $00 + repend + +Bank2 + + lda $FFF8 + +Change_PF_Color + + sei + lda PF_Read + sbc #1 + sta PF_Write + + rts + + org $F30D + rorg $F30D + + jsr Change_PF_Color + lda $FFF8 + + org $FFFC + rorg $FFFC + .word Bank2 + .byte "B2" diff --git a/test/roms/bankswitching/_code/FA_CBS+RAM.bin b/test/roms/bankswitching/_code/FA_CBS+RAM.bin new file mode 100644 index 000000000..a20cab291 Binary files /dev/null and b/test/roms/bankswitching/_code/FA_CBS+RAM.bin differ diff --git a/test/roms/bankswitching/_code/SB_128K.asm b/test/roms/bankswitching/_code/SB_128K.asm new file mode 100644 index 000000000..39b6d113d --- /dev/null +++ b/test/roms/bankswitching/_code/SB_128K.asm @@ -0,0 +1,1135 @@ +;;A bankswitching demo for the 128K SuperBanking scheme. 32 4K banks +;;By: Rick Skrbina 4/4/09 + + processor 6502 + include "vcs.h" + include "macro.h" + + seg.u vars + org $80 + + + seg bank0 + org $0000 + rorg $F000 +Start0 + nop + nop + nop + CLEAN_START + +; lda #$1F +; sta COLUBK + +Start_Frame + lda #2 + sta VSYNC + sta VBLANK + sta WSYNC + sta WSYNC + sta WSYNC + lsr + sta VSYNC + + ldy #37 +Vertical_Blank + sta WSYNC + dey + bne Vertical_Blank + + lda #0 + sta VBLANK + + lda #$10 + sta COLUBK + + ldy #6 +Picture0 + sta WSYNC + dey + bne Picture0 + + ldx #1 + jsr Swch1 + + ldy #6 +Picture1 + sta WSYNC + dey + bne Picture1 + + inx + jsr Swch1 + + ldy #6 +Picture2 + sta WSYNC + dey + bne Picture2 + + inx + jsr Swch1 + + ldy #6 +Picture3 + sta WSYNC + dey + bne Picture3 + + inx + jsr Swch1 + + ldy #6 +Picture4 + sta WSYNC + dey + bne Picture4 + + inx + jsr Swch1 + + ldy #6 +Picture5 + sta WSYNC + dey + bne Picture5 + + inx + jsr Swch1 + + ldy #6 +Picture6 + sta WSYNC + dey + bne Picture6 + + inx + jsr Swch1 + + ldy #6 +Picture7 + sta WSYNC + dey + bne Picture7 + + inx + jsr Swch1 + + ldy #6 +Picture8 + sta WSYNC + dey + bne Picture8 + + inx + jsr Swch1 + + ldy #6 +Picture9 + sta WSYNC + dey + bne Picture9 + + inx + jsr Swch1 + + ldy #6 +PictureA + sta WSYNC + dey + bne PictureA + + inx + jsr Swch1 + + ldy #6 +PictureB + sta WSYNC + dey + bne PictureB + + inx + jsr Swch1 + + ldy #6 +PictureC + sta WSYNC + dey + bne PictureC + + inx + jsr Swch1 + + ldy #6 +PictureD + sta WSYNC + dey + bne PictureD + + inx + jsr Swch1 + + ldy #6 +PictureE + sta WSYNC + dey + bne PictureE + + inx + jsr Swch1 + + ldy #6 +PictureF + sta WSYNC + dey + bne PictureF + + inx + jsr Swch1 + + ldy #6 +Picture10 + sta WSYNC + dey + bne Picture10 + + inx + jsr Swch1 + + ldy #6 +Picture11 + sta WSYNC + dey + bne Picture11 + + inx + jsr Swch1 + + ldy #6 +Picture12 + sta WSYNC + dey + bne Picture12 + + inx + jsr Swch1 + + ldy #6 +Picture13 + sta WSYNC + dey + bne Picture13 + + inx + jsr Swch1 + + ldy #6 +Picture14 + sta WSYNC + dey + bne Picture14 + + inx + jsr Swch1 + + ldy #6 +Picture15 + sta WSYNC + dey + bne Picture15 + + inx + jsr Swch1 + + ldy #6 +Picture16 + sta WSYNC + dey + bne Picture16 + + inx + jsr Swch1 + + ldy #6 +Picture17 + sta WSYNC + dey + bne Picture17 + + inx + jsr Swch1 + + ldy #6 +Picture18 + sta WSYNC + dey + bne Picture18 + + inx + jsr Swch1 + + ldy #6 +Picture19 + sta WSYNC + dey + bne Picture19 + + inx + jsr Swch1 + + ldy #6 +Picture1A + sta WSYNC + dey + bne Picture1A + + inx + jsr Swch1 + + ldy #6 +Picture1B + sta WSYNC + dey + bne Picture1B + + inx + jsr Swch1 + + ldy #6 +Picture1C + sta WSYNC + dey + bne Picture1C + + inx + jsr Swch1 + + ldy #6 +Picture1D + sta WSYNC + dey + bne Picture1D + + inx + jsr Swch1 + + ldy #6 +Picture1E + sta WSYNC + dey + bne Picture1E + + inx + jsr Swch1 + + ldy #6 +Picture1F + sta WSYNC + dey + bne Picture1F + + inx + jsr Swch1 + + lda #2 + sta VBLANK + + ldy #30 +OverScan + sta WSYNC + dey + bne OverScan + + jmp Start_Frame + + + org $0200 + rorg $F200 + +Swch1 + lda $0800,x + nop + nop + nop + nop + nop + nop + rts + + + org $0FFC + rorg $FFFC + .word Start0 + .byte "00" + + seg bank1 + org $1000 + rorg $F000 +Start1 + lda $0800 +Sub1 + lda #$12 + sta COLUBK + rts + + org $1200 + rorg $F200 + + nop + nop + nop + jsr Sub1 + lda $0800 + + org $1FFC + rorg $FFFC + .word Start1 + .byte "01" + + seg bank2 + org $2000 + rorg $F000 +Start2 + lda $0800 + +Sub2 + lda #$14 + sta COLUBK + rts + + org $2200 + rorg $F1200 + + nop + nop + nop + jsr Sub2 + lda $0800 + + org $2FFC + rorg $FFFC + .word Start2 + .byte "02" + + seg bank3 + org $3000 + rorg $F000 +Start3 + lda $0800 + +Sub3 + lda #$16 + sta COLUBK + rts + + org $3200 + rorg $F200 + + nop + nop + nop + jsr Sub3 + lda $0800 + + org $3FFC + rorg $FFFC + .word Start3 + .byte "03" + + seg bank4 + org $4000 + rorg $F000 +Start4 + lda $0800 + +Sub4 + lda #$18 + sta COLUBK + rts + + org $4200 + rorg $F200 + + nop + nop + nop + jsr Sub4 + lda $0800 + + org $4FFC + rorg $FFFC + .word Start4 + .byte "04" + + seg bank5 + org $5000 + rorg $F000 +Start5 + lda $0800 + +Sub5 + lda #$1A + sta COLUBK + rts + + org $5200 + rorg $F200 + + nop + nop + nop + jsr Sub5 + lda $0800 + + org $5FFC + rorg $FFFC + .word Start5 + .byte "05" + + seg bank6 + org $6000 + rorg $F000 +Start6 + lda $0800 + +Sub6 + lda #$1C + sta COLUBK + rts + + org $6200 + rorg $F200 + + nop + nop + nop + jsr Sub6 + lda $0800 + + org $6FFC + rorg $FFFC + .word Start6 + .byte "06" + + seg bank7 + org $7000 + rorg $F000 +Start7 + lda $0800 + +Sub7 + lda #$1D + sta COLUBK + rts + + org $7200 + rorg $F200 + + nop + nop + nop + jsr Sub7 + lda $0800 + + org $7FFC + rorg $FFFC + .word Start7 + .byte "07" + + seg bank8 + org $8000 + rorg $F000 +Start8 + lda $0800 +Sub8 + lda #$1F + sta COLUBK + rts + + org $8200 + rorg $F200 + + nop + nop + nop + jsr Sub8 + lda $0800 + + org $8FFC + rorg $FFFC + .word Start8 + .byte "08" + + seg bank9 + org $9000 + rorg $F000 +Start9 + lda $0800 + +Sub9 + lda #$20 + sta COLUBK + rts + + org $9200 + rorg $F200 + + nop + nop + nop + jsr Sub9 + lda $0800 + + org $9FFC + rorg $FFFC + .word Start9 + .byte "09" + + seg bankA + org $A000 + rorg $F000 +StartA + lda $0800 + +SubA + lda #$22 + sta COLUBK + rts + + org $A200 + rorg $F200 + + nop + nop + nop + jsr SubA + lda $0800 + + org $AFFC + rorg $FFFC + .word StartA + .byte "0A" + + seg bankB + org $B000 + rorg $F000 +StartB + lda $0800 + +SubB + lda #$24 + sta COLUBK + rts + + org $B200 + rorg $F200 + + nop + nop + nop + jsr SubB + lda $0800 + + org $BFFC + rorg $FFFC + .word StartB + .byte "0B" + + seg bankC + org $C000 + rorg $F000 +StartC + lda $0800 + +SubC + lda #$26 + sta COLUBK + rts + + org $C200 + rorg $F200 + + nop + nop + nop + jsr SubC + lda $0800 + + org $CFFC + rorg $FFFC + .word StartC + .byte "0C" + + seg bankD + org $D000 + rorg $F000 +StartD + lda $0800 + +SubD + lda #$28 + sta COLUBK + rts + + org $D200 + rorg $F200 + + nop + nop + nop + jsr SubD + lda $0800 + + org $DFFC + rorg $FFFC + .word StartD + .byte "0D" + + seg bankE + org $E000 + rorg $F000 +StartE + lda $0800 + +SubE + lda #$2A + sta COLUBK + rts + + org $E200 + rorg $F200 + + nop + nop + nop + jsr SubE + lda $0800 + + org $EFFC + rorg $FFFC + .word StartE + .byte "0E" + + seg bankF + org $F000 + rorg $F000 +StartF + lda $0800 + +SubF + lda #$2C + sta COLUBK + rts + + org $F200 + rorg $F200 + + nop + nop + nop + jsr SubF + lda $0800 + + org $FFFC + rorg $FFFC + .word StartF + .byte "0F" + + seg bank10 + org $10000 + rorg $F000 +Start10 + lda $0800 + +Sub10 + lda #$2E + sta COLUBK + rts + + org $10200 + rorg $F200 + + nop + nop + nop + jsr Sub10 + lda $0800 + + org $10FFC + rorg $FFFC + .word Start10 + .byte "10" + + seg bank11 + org $11000 + rorg $F000 +Start11 + lda $0800 + +Sub11 + lda #$30 + sta COLUBK + rts + + org $11200 + rorg $F200 + + nop + nop + nop + jsr Sub11 + lda $0800 + + org $11FFC + rorg $FFFC + .word Start11 + .byte "11" + + seg bank12 + org $12000 + rorg $F000 +Start12 + lda $0800 + +Sub12 + lda #$32 + sta COLUBK + rts + + org $12200 + rorg $F200 + + nop + nop + nop + jsr Sub12 + lda $0800 + + org $12FFC + rorg $FFFC + .word Start12 + .byte "12" + + seg bank13 + org $13000 + rorg $F000 +Start13 + lda $0800 + +Sub13 + lda #$34 + sta COLUBK + rts + + org $13200 + rorg $F200 + + nop + nop + nop + jsr Sub13 + lda $0800 + + org $13FFC + rorg $FFFC + .word Start13 + .byte "13" + + seg bank14 + org $14000 + rorg $F000 +Start14 + lda $0800 + +Sub14 + lda #$36 + sta COLUBK + rts + + org $14200 + rorg $F200 + + nop + nop + nop + jsr Sub14 + lda $0800 + + org $14FFC + rorg $FFFC + .word Start14 + .byte "14" + + seg bank15 + org $15000 + rorg $F000 +Start15 + lda $0800 + +Sub15 lda #$38 + sta COLUBK + rts + + org $15200 + rorg $F200 + + nop + nop + nop + jsr Sub15 + lda $0800 + + org $15FFC + rorg $FFFC + .word Start15 + .byte "15" + + seg bank16 + org $16000 + rorg $F000 +Start16 + lda $0800 + +Sub16 + lda #$3A + sta COLUBK + rts + + org $16200 + rorg $F200 + + nop + nop + nop + jsr Sub16 + lda $0800 + + org $16FFC + rorg $FFFC + .word Start16 + .byte "16" + + seg bank17 + org $17000 + rorg $F000 +Start17 + lda $0800 + +Sub17 + lda #$3C + sta COLUBK + rts + + org $17200 + rorg $F200 + + nop + nop + nop + jsr Sub17 + lda $0800 + + org $17FFC + rorg $FFFC + .word Start17 + .byte "17" + + seg bank18 + org $18000 + rorg $F000 +Start18 + lda $0800 + +Sub18 + lda #$3E + sta COLUBK + rts + + org $18200 + rorg $F200 + + nop + nop + nop + jsr Sub18 + lda $0800 + + org $18FFC + rorg $FFFC + .word Start18 + .byte "18" + + seg bank19 + org $19000 + rorg $F000 +Start19 + lda $0800 + +Sub19 + lda #$40 + sta COLUBK + rts + + org $19200 + rorg $F200 + + nop + nop + nop + jsr Sub19 + lda $0800 + + org $19FFC + rorg $FFFC + .word Start19 + .byte "19" + + seg bank1A + org $1A000 + rorg $F000 +Start1A + lda $0800 + +Sub1A + lda #$42 + sta COLUBK + rts + + org $1A200 + rorg $F200 + + nop + nop + nop + jsr Sub1A + lda $0800 + + org $1AFFC + rorg $FFFC + .word Start1A + .byte "1A" + + seg bank1B + org $1B000 + rorg $F000 +Start1B + lda $0800 + +Sub1B + lda #$44 + sta COLUBK + rts + + org $1B200 + rorg $F200 + + nop + nop + nop + jsr Sub1B + lda $0800 + + org $1BFFC + rorg $FFFC + .word Start1B + .byte "1B" + + seg bank1C + org $1C000 + rorg $F000 +Start1C + lda $0800 + +Sub1C + lda #$46 + sta COLUBK + rts + + org $1C200 + rorg $F200 + + nop + nop + nop + jsr Sub1C + lda $0800 + + org $1CFFC + rorg $FFFC + .word Start1C + .byte "1C" + + seg bank1D + org $1D000 + rorg $F000 +Start1D + lda $0800 + +Sub1D + lda #$48 + sta COLUBK + rts + + org $1D200 + rorg $F200 + + nop + nop + nop + jsr Sub1D + lda $0800 + + org $1DFFC + rorg $FFFC + .word Start1D + .byte "1D" + + seg bank1E + org $1E000 + rorg $F000 +Start1E + lda $0800 + +Sub1E + lda #$4A + sta COLUBK + rts + + org $1E200 + rorg $F200 + + nop + nop + nop + jsr Sub1E + lda $0800 + + org $1EFFC + rorg $FFFC + .word Start1E + .byte "1E" + + seg bank1F + org $1F000 + rorg $F000 +Start1F + lda $0800 + +Sub1F + lda #$4C + sta COLUBK + rts + + org $1F200 + rorg $F200 + + nop + nop + nop + jsr Sub1F + lda $0800 + + org $1FFFC + rorg $FFFC + .word Start1F + .byte "1F" diff --git a/test/roms/bankswitching/_code/SB_128K.bin b/test/roms/bankswitching/_code/SB_128K.bin new file mode 100644 index 000000000..f6cb60f66 Binary files /dev/null and b/test/roms/bankswitching/_code/SB_128K.bin differ diff --git a/test/roms/bankswitching/_code/SB_256K.asm b/test/roms/bankswitching/_code/SB_256K.asm new file mode 100644 index 000000000..31cf528cc --- /dev/null +++ b/test/roms/bankswitching/_code/SB_256K.asm @@ -0,0 +1,1658 @@ +;;A bankswitching demo for the 256K SuperBanking scheme. 64 4K banks +;;By: Rick Skrbina 5/4/09 + + processor 6502 + include "vcs.h" + include "macro.h" + + seg.u vars + org $80 + + + seg bank0 + org $0000 + rorg $F000 +Start0 + nop + nop + nop + CLEAN_START + + +Start_Frame + lda #2 + sta VSYNC + sta VBLANK + sta WSYNC + sta WSYNC + sta WSYNC + lsr + sta VSYNC + + ldy #37 +Vertical_Blank + sta WSYNC + dey + bne Vertical_Blank + + lda #0 + sta VBLANK + + lda #$10 + sta COLUBK + + ldx #1 + ldy #3 + +Picture_Loop + sta WSYNC + dey + bne Picture_Loop + ldy #3 + jsr Swch1 + inx + cpx #$40 + bne Picture_Loop + + lda #2 + sta VBLANK + + ldy #30 +OverScan + sta WSYNC + dey + bne OverScan + + jmp Start_Frame + + + org $0200 + rorg $F200 + +Swch1 + lda $0800,x + nop + nop + nop + nop + nop + nop + rts + + + org $0FFC + rorg $FFFC + .word Start0 + .byte "00" + + seg bank1 + org $1000 + rorg $F000 +Start1 + lda $0800 +Sub1 + lda #$12 + sta COLUBK + rts + + org $1200 + rorg $F200 + + nop + nop + nop + jsr Sub1 + lda $0800 + + org $1FFC + rorg $FFFC + .word Start1 + .byte "01" + + seg bank2 + org $2000 + rorg $F000 +Start2 + lda $0800 + +Sub2 + lda #$14 + sta COLUBK + rts + + org $2200 + rorg $F1200 + + nop + nop + nop + jsr Sub2 + lda $0800 + + org $2FFC + rorg $FFFC + .word Start2 + .byte "02" + + seg bank3 + org $3000 + rorg $F000 +Start3 + lda $0800 + +Sub3 + lda #$16 + sta COLUBK + rts + + org $3200 + rorg $F200 + + nop + nop + nop + jsr Sub3 + lda $0800 + + org $3FFC + rorg $FFFC + .word Start3 + .byte "03" + + seg bank4 + org $4000 + rorg $F000 +Start4 + lda $0800 + +Sub4 + lda #$18 + sta COLUBK + rts + + org $4200 + rorg $F200 + + nop + nop + nop + jsr Sub4 + lda $0800 + + org $4FFC + rorg $FFFC + .word Start4 + .byte "04" + + seg bank5 + org $5000 + rorg $F000 +Start5 + lda $0800 + +Sub5 + lda #$1A + sta COLUBK + rts + + org $5200 + rorg $F200 + + nop + nop + nop + jsr Sub5 + lda $0800 + + org $5FFC + rorg $FFFC + .word Start5 + .byte "05" + + seg bank6 + org $6000 + rorg $F000 +Start6 + lda $0800 + +Sub6 + lda #$1C + sta COLUBK + rts + + org $6200 + rorg $F200 + + nop + nop + nop + jsr Sub6 + lda $0800 + + org $6FFC + rorg $FFFC + .word Start6 + .byte "06" + + seg bank7 + org $7000 + rorg $F000 +Start7 + lda $0800 + +Sub7 + lda #$1D + sta COLUBK + rts + + org $7200 + rorg $F200 + + nop + nop + nop + jsr Sub7 + lda $0800 + + org $7FFC + rorg $FFFC + .word Start7 + .byte "07" + + seg bank8 + org $8000 + rorg $F000 +Start8 + lda $0800 +Sub8 + lda #$1F + sta COLUBK + rts + + org $8200 + rorg $F200 + + nop + nop + nop + jsr Sub8 + lda $0800 + + org $8FFC + rorg $FFFC + .word Start8 + .byte "08" + + seg bank9 + org $9000 + rorg $F000 +Start9 + lda $0800 + +Sub9 + lda #$20 + sta COLUBK + rts + + org $9200 + rorg $F200 + + nop + nop + nop + jsr Sub9 + lda $0800 + + org $9FFC + rorg $FFFC + .word Start9 + .byte "09" + + seg bankA + org $A000 + rorg $F000 +StartA + lda $0800 + +SubA + lda #$22 + sta COLUBK + rts + + org $A200 + rorg $F200 + + nop + nop + nop + jsr SubA + lda $0800 + + org $AFFC + rorg $FFFC + .word StartA + .byte "0A" + + seg bankB + org $B000 + rorg $F000 +StartB + lda $0800 + +SubB + lda #$24 + sta COLUBK + rts + + org $B200 + rorg $F200 + + nop + nop + nop + jsr SubB + lda $0800 + + org $BFFC + rorg $FFFC + .word StartB + .byte "0B" + + seg bankC + org $C000 + rorg $F000 +StartC + lda $0800 + +SubC + lda #$26 + sta COLUBK + rts + + org $C200 + rorg $F200 + + nop + nop + nop + jsr SubC + lda $0800 + + org $CFFC + rorg $FFFC + .word StartC + .byte "0C" + + seg bankD + org $D000 + rorg $F000 +StartD + lda $0800 + +SubD + lda #$28 + sta COLUBK + rts + + org $D200 + rorg $F200 + + nop + nop + nop + jsr SubD + lda $0800 + + org $DFFC + rorg $FFFC + .word StartD + .byte "0D" + + seg bankE + org $E000 + rorg $F000 +StartE + lda $0800 + +SubE + lda #$2A + sta COLUBK + rts + + org $E200 + rorg $F200 + + nop + nop + nop + jsr SubE + lda $0800 + + org $EFFC + rorg $FFFC + .word StartE + .byte "0E" + + seg bankF + org $F000 + rorg $F000 +StartF + lda $0800 + +SubF + lda #$2C + sta COLUBK + rts + + org $F200 + rorg $F200 + + nop + nop + nop + jsr SubF + lda $0800 + + org $FFFC + rorg $FFFC + .word StartF + .byte "0F" + + seg bank10 + org $10000 + rorg $F000 +Start10 + lda $0800 + +Sub10 + lda #$2E + sta COLUBK + rts + + org $10200 + rorg $F200 + + nop + nop + nop + jsr Sub10 + lda $0800 + + org $10FFC + rorg $FFFC + .word Start10 + .byte "10" + + seg bank11 + org $11000 + rorg $F000 +Start11 + lda $0800 + +Sub11 + lda #$30 + sta COLUBK + rts + + org $11200 + rorg $F200 + + nop + nop + nop + jsr Sub11 + lda $0800 + + org $11FFC + rorg $FFFC + .word Start11 + .byte "11" + + seg bank12 + org $12000 + rorg $F000 +Start12 + lda $0800 + +Sub12 + lda #$32 + sta COLUBK + rts + + org $12200 + rorg $F200 + + nop + nop + nop + jsr Sub12 + lda $0800 + + org $12FFC + rorg $FFFC + .word Start12 + .byte "12" + + seg bank13 + org $13000 + rorg $F000 +Start13 + lda $0800 + +Sub13 + lda #$34 + sta COLUBK + rts + + org $13200 + rorg $F200 + + nop + nop + nop + jsr Sub13 + lda $0800 + + org $13FFC + rorg $FFFC + .word Start13 + .byte "13" + + seg bank14 + org $14000 + rorg $F000 +Start14 + lda $0800 + +Sub14 + lda #$36 + sta COLUBK + rts + + org $14200 + rorg $F200 + + nop + nop + nop + jsr Sub14 + lda $0800 + + org $14FFC + rorg $FFFC + .word Start14 + .byte "14" + + seg bank15 + org $15000 + rorg $F000 +Start15 + lda $0800 + +Sub15 lda #$38 + sta COLUBK + rts + + org $15200 + rorg $F200 + + nop + nop + nop + jsr Sub15 + lda $0800 + + org $15FFC + rorg $FFFC + .word Start15 + .byte "15" + + seg bank16 + org $16000 + rorg $F000 +Start16 + lda $0800 + +Sub16 + lda #$3A + sta COLUBK + rts + + org $16200 + rorg $F200 + + nop + nop + nop + jsr Sub16 + lda $0800 + + org $16FFC + rorg $FFFC + .word Start16 + .byte "16" + + seg bank17 + org $17000 + rorg $F000 +Start17 + lda $0800 + +Sub17 + lda #$3C + sta COLUBK + rts + + org $17200 + rorg $F200 + + nop + nop + nop + jsr Sub17 + lda $0800 + + org $17FFC + rorg $FFFC + .word Start17 + .byte "17" + + seg bank18 + org $18000 + rorg $F000 +Start18 + lda $0800 + +Sub18 + lda #$3E + sta COLUBK + rts + + org $18200 + rorg $F200 + + nop + nop + nop + jsr Sub18 + lda $0800 + + org $18FFC + rorg $FFFC + .word Start18 + .byte "18" + + seg bank19 + org $19000 + rorg $F000 +Start19 + lda $0800 + +Sub19 + lda #$40 + sta COLUBK + rts + + org $19200 + rorg $F200 + + nop + nop + nop + jsr Sub19 + lda $0800 + + org $19FFC + rorg $FFFC + .word Start19 + .byte "19" + + seg bank1A + org $1A000 + rorg $F000 +Start1A + lda $0800 + +Sub1A + lda #$42 + sta COLUBK + rts + + org $1A200 + rorg $F200 + + nop + nop + nop + jsr Sub1A + lda $0800 + + org $1AFFC + rorg $FFFC + .word Start1A + .byte "1A" + + seg bank1B + org $1B000 + rorg $F000 +Start1B + lda $0800 + +Sub1B + lda #$44 + sta COLUBK + rts + + org $1B200 + rorg $F200 + + nop + nop + nop + jsr Sub1B + lda $0800 + + org $1BFFC + rorg $FFFC + .word Start1B + .byte "1B" + + seg bank1C + org $1C000 + rorg $F000 +Start1C + lda $0800 + +Sub1C + lda #$46 + sta COLUBK + rts + + org $1C200 + rorg $F200 + + nop + nop + nop + jsr Sub1C + lda $0800 + + org $1CFFC + rorg $FFFC + .word Start1C + .byte "1C" + + seg bank1D + org $1D000 + rorg $F000 +Start1D + lda $0800 + +Sub1D + lda #$48 + sta COLUBK + rts + + org $1D200 + rorg $F200 + + nop + nop + nop + jsr Sub1D + lda $0800 + + org $1DFFC + rorg $FFFC + .word Start1D + .byte "1D" + + seg bank1E + org $1E000 + rorg $F000 +Start1E + lda $0800 + +Sub1E + lda #$4A + sta COLUBK + rts + + org $1E200 + rorg $F200 + + nop + nop + nop + jsr Sub1E + lda $0800 + + org $1EFFC + rorg $FFFC + .word Start1E + .byte "1E" + + seg bank1F + org $1F000 + rorg $F000 +Start1F + lda $0800 + +Sub1F + lda #$4C + sta COLUBK + rts + + org $1F200 + rorg $F200 + + nop + nop + nop + jsr Sub1F + lda $0800 + + org $1FFFC + rorg $FFFC + .word Start1F + .byte "1F" + + seg bank20 + org $20000 + rorg $F000 +Start20 + lda $0800 + +Sub20 + lda #$4E + sta COLUBK + rts + + org $20200 + rorg $F200 + + nop + nop + nop + jsr Sub20 + lda $0800 + + org $20FFC + rorg $FFFC + .word Start20 + .byte "20" + + seg bank21 + org $21000 + rorg $F000 +Start21 + lda $0800 + +Sub21 + lda #$50 + sta COLUBK + rts + + org $21200 + rorg $F200 + + nop + nop + nop + jsr Sub21 + lda $0800 + + org $21FFC + rorg $FFFC + .word Start21 + .byte "21" + + seg bank22 + org $22000 + rorg $F000 +Start22 + lda $0800 + +Sub22 + lda #$52 + sta COLUBK + rts + + org $22200 + rorg $F200 + + nop + nop + nop + jsr Sub22 + lda $0800 + + org $22FFC + rorg $FFFC + .word Start22 + .byte "22" + + seg bank23 + org $23000 + rorg $F000 +Start23 + lda $0800 + +Sub23 + lda #$54 + sta COLUBK + rts + + org $23200 + rorg $F200 + + nop + nop + nop + jsr Sub23 + lda $0800 + + org $23FFC + rorg $FFFC + .word Start23 + .byte "23" + + seg bank24 + org $24000 + rorg $F000 +Start24 + lda $0800 + +Sub24 + lda #$56 + sta COLUBK + rts + + org $24200 + rorg $F200 + + nop + nop + nop + jsr Sub24 + lda $0800 + + org $24FFC + rorg $FFFC + .word Start24 + .byte "24" + + seg bank25 + org $25000 + rorg $F000 +Start25 + lda $0800 + +Sub25 + lda #$58 + sta COLUBK + rts + + org $25200 + rorg $F200 + + nop + nop + nop + jsr Sub25 + lda $0800 + + org $25FFC + rorg $FFFC + .word Start25 + .byte "25" + + seg bank26 + org $26000 + rorg $F000 +Start26 + lda $0800 + +Sub26 + lda #$5A + sta COLUBK + rts + + org $26200 + rorg $F200 + + nop + nop + nop + jsr Sub26 + lda $0800 + + org $26FFC + rorg $FFFC + .word Start26 + .byte "26" + + seg bank27 + org $27000 + rorg $F000 +Start27 + lda $0800 + +Sub27 + lda #$5C + sta COLUBK + rts + + org $27200 + rorg $F200 + + nop + nop + nop + jsr Sub27 + lda $0800 + + org $27FFC + rorg $FFFC + .word Start27 + .byte "27" + + seg bank28 + org $28000 + rorg $F000 +Start28 + lda $0800 + +Sub28 + lda #$5E + sta COLUBK + rts + + org $28200 + rorg $F200 + + nop + nop + nop + jsr Sub28 + lda $0800 + + org $28FFC + rorg $FFFC + .word Start28 + .byte "28" + + seg bank29 + org $29000 + rorg $F000 +Start29 + lda $0800 + +Sub29 + lda #$60 + sta COLUBK + rts + + org $29200 + rorg $F200 + + nop + nop + nop + jsr Sub29 + lda $0800 + + org $29FFC + rorg $FFFC + .word Start29 + .byte "29" + + seg bank2A + org $2A000 + rorg $F000 +Start2A + lda $0800 + +Sub2A + lda #$62 + sta COLUBK + rts + + org $2A200 + rorg $F200 + + nop + nop + nop + jsr Sub2A + lda $0800 + + org $2AFFC + rorg $FFFC + .word Start2A + .byte "2A" + + seg bank2B + org $2B000 + rorg $F000 +Start2B + lda $0800 + +Sub2B + lda #$64 + sta COLUBK + rts + + org $2B200 + rorg $F200 + + nop + nop + nop + jsr Sub2B + lda $0800 + + org $2BFFC + rorg $FFFC + .word Start2B + .byte "2B" + + seg bank2C + org $2C000 + rorg $F000 +Start2C + lda $0800 + +Sub2C + lda #$66 + sta COLUBK + rts + + org $2C200 + rorg $F200 + + nop + nop + nop + jsr Sub2C + lda $0800 + + org $2CFFC + rorg $FFFC + .word Start2C + .byte "2C" + + seg bank2D + org $2D000 + rorg $F000 +Start2D + lda $0800 + +Sub2D + lda #$68 + sta COLUBK + rts + + org $2D200 + rorg $F200 + + nop + nop + nop + jsr Sub2D + lda $0800 + + org $2DFFC + rorg $FFFC + .word Start2D + .byte "2D" + + seg bank2E + org $2E000 + rorg $F000 +Start2E + lda $0800 + +Sub2E + lda #$6A + sta COLUBK + rts + + org $2E200 + rorg $F200 + + nop + nop + nop + jsr Sub2E + lda $0800 + + org $2EFFC + rorg $FFFC + .word Start2E + .byte "2E" + + seg bank2F + org $2F000 + rorg $F000 +Start2F + lda $0800 + +Sub2F + lda #$6C + sta COLUBK + rts + + org $2F200 + rorg $F200 + + nop + nop + nop + jsr Sub2F + lda $0800 + + org $2FFFC + rorg $FFFC + .word Start2F + .byte "2F" + + seg bank30 + org $30000 + rorg $F000 +Start30 + lda $0800 + +Sub30 + lda #$6E + sta COLUBK + rts + + org $30200 + rorg $F200 + + nop + nop + nop + jsr Sub30 + lda $0800 + + org $30FFC + rorg $FFFC + .word Start30 + .byte "30" + + seg bank31 + org $31000 + rorg $F000 +Start31 + lda $0800 + +Sub31 + lda #$70 + sta COLUBK + rts + + org $31200 + rorg $F200 + + nop + nop + nop + jsr Sub31 + lda $0800 + + org $31FFC + rorg $FFFC + .word Start31 + .byte "31" + + seg bank32 + org $32000 + rorg $F000 +Start32 + lda $0800 + +Sub32 + lda #$72 + sta COLUBK + rts + + org $32200 + rorg $F200 + + nop + nop + nop + jsr Sub32 + lda $0800 + + org $32FFC + rorg $FFFC + .word Start32 + .byte "32" + + seg bank33 + org $33000 + rorg $F000 +Start33 + lda $0800 + +Sub33 + lda #$74 + sta COLUBK + rts + + org $33200 + rorg $F200 + + nop + nop + nop + jsr Sub33 + lda $0800 + + org $33FFC + rorg $FFFC + .word Start33 + .byte "33" + + seg bank34 + org $34000 + rorg $F000 +Start34 + lda $0800 + +Sub34 + lda #$76 + sta COLUBK + rts + + org $34200 + rorg $F200 + + nop + nop + nop + jsr Sub34 + lda $0800 + + org $34FFC + rorg $FFFC + .word Start34 + .byte "34" + + seg bank35 + org $35000 + rorg $F000 +Start35 + lda $0800 + +Sub35 + lda #$78 + sta COLUBK + rts + + org $35200 + rorg $F200 + + nop + nop + nop + jsr Sub35 + lda $0800 + + org $35FFC + rorg $FFFC + .word Start35 + .byte "35" + + seg bank36 + org $36000 + rorg $F000 +Start36 + lda $0800 + +Sub36 + lda #$7A + sta COLUBK + rts + + org $36200 + rorg $F200 + + nop + nop + nop + jsr Sub36 + lda $0800 + + org $36FFC + rorg $FFFC + .word Start36 + .byte "36" + + seg bank37 + org $37000 + rorg $F000 +Start37 + lda $0800 + +Sub37 + lda #$7C + sta COLUBK + rts + + org $37200 + rorg $F200 + + nop + nop + nop + jsr Sub37 + lda $0800 + + org $37FFC + rorg $FFFC + .word Start37 + .byte "37" + + seg bank38 + org $38000 + rorg $F000 +Start38 + lda $0800 + +Sub38 + lda #$7E + sta COLUBK + rts + + org $38200 + rorg $F200 + + nop + nop + nop + jsr Sub38 + lda $0800 + + org $38FFC + rorg $FFFC + .word Start38 + .byte "38" + + seg bank39 + org $39000 + rorg $F000 +Start39 + lda $0800 + +Sub39 + lda #$80 + sta COLUBK + rts + + org $39200 + rorg $F200 + + nop + nop + nop + jsr Sub39 + lda $0800 + + org $39FFC + rorg $FFFC + .word Start39 + .byte "39" + + seg bank3A + org $3A000 + rorg $F000 +Start3A + lda $0800 + +Sub3A + lda #$82 + sta COLUBK + rts + + org $3A200 + rorg $F200 + + nop + nop + nop + jsr Sub3A + lda $0800 + + org $3AFFC + rorg $FFFC + .word Start3A + .byte "3A" + + seg bank3B + org $3B000 + rorg $F000 +Start3B + lda $0800 + +Sub3B + lda #$84 + sta COLUBK + rts + + org $3B200 + rorg $F200 + + nop + nop + nop + jsr Sub3B + lda $0800 + + org $3BFFC + rorg $FFFC + .word Start3B + .byte "3B" + + seg bank3C + org $3C000 + rorg $F000 +Start3C + lda $0800 + +Sub3C + lda #$86 + sta COLUBK + rts + + org $3C200 + rorg $F200 + + nop + nop + nop + jsr Sub3C + lda $0800 + + org $3CFFC + rorg $FFFC + .word Start3C + .byte "3C" + + seg bank3D + org $3D000 + rorg $F000 +Start3D + lda $0800 + +Sub3D + lda #$88 + sta COLUBK + rts + + org $3D200 + rorg $F200 + + nop + nop + nop + jsr Sub3D + lda $0800 + + org $3DFFC + rorg $FFFC + .word Start3D + .byte "3D" + + seg bank3E + org $3E000 + rorg $F000 +Start3E + lda $0800 + +Sub3E + lda #$8A + sta COLUBK + rts + + org $3E200 + rorg $F200 + + nop + nop + nop + jsr Sub3E + lda $0800 + + org $3EFFC + rorg $FFFC + .word Start3E + .byte "3E" + + seg bank3F + org $3F000 + rorg $F000 +Start3F + lda $0800 + +Sub3F + lda #$8C + sta COLUBK + rts + + org $3F200 + rorg $F200 + + nop + nop + nop + jsr Sub3F + lda $0800 + + org $3FFFC + rorg $FFFC + .word Start3F + .byte "3F" diff --git a/test/roms/bankswitching/_code/SB_256K.bin b/test/roms/bankswitching/_code/SB_256K.bin new file mode 100644 index 000000000..f8c818282 Binary files /dev/null and b/test/roms/bankswitching/_code/SB_256K.bin differ diff --git a/test/roms/bankswitching/_code/UA_Limited.asm b/test/roms/bankswitching/_code/UA_Limited.asm new file mode 100644 index 000000000..a79347e53 --- /dev/null +++ b/test/roms/bankswitching/_code/UA_Limited.asm @@ -0,0 +1,102 @@ +;;A bankswitching demo for the UA limited technique. 2 4K banks (8K) +;;By: Rick Skrbina 3/29/09 + + processor 6502 + include "vcs.h" + include "macro.h" + + + seg.u vars + org $80 +BG_Color ds 1 + + seg Bank0 + org $E000 + rorg $F000 +Start0 + nop + nop + nop + + CLEAN_START + +Start_Frame + lda #2 + sta VBLANK + sta VSYNC + sta WSYNC + sta WSYNC + sta WSYNC + lsr + sta VSYNC + ldy #37 +VerticalBlank + sta WSYNC + dey + bne VerticalBlank + + sta VBLANK + + ldy #192 +Picture + sta WSYNC + dey + bne Picture + + lda #2 + sta VBLANK + + ldy #30 +OverScan + sta WSYNC + dey + bne OverScan + + jsr SwchTo1 + + lda BG_Color + sta COLUBK + + jmp Start_Frame + + org $EFE0 + rorg $FFE0 +SwchTo1 + lda $0240 + nop + nop + nop + nop + nop + nop + rts + + org $EFFC + rorg $FFFC + .word Start0 + .byte "B0" + + seg Bank1 + org $F000 + rorg $F000 +Start1 + lda $0220 + +Bank1Sub + + inc BG_Color + rts + + org $FFE0 + rorg $FFE0 + + nop + nop + nop + jsr Bank1Sub + lda $0220 + + org $FFFC + rorg $FFFC + .word Start1 + .byte "B1" diff --git a/test/roms/bankswitching/_code/UA_Limited.bin b/test/roms/bankswitching/_code/UA_Limited.bin new file mode 100644 index 000000000..5cb088b29 Binary files /dev/null and b/test/roms/bankswitching/_code/UA_Limited.bin differ diff --git a/test/roms/bankswitching/_code/macro.h b/test/roms/bankswitching/_code/macro.h new file mode 100644 index 000000000..ce7b9a15c --- /dev/null +++ b/test/roms/bankswitching/_code/macro.h @@ -0,0 +1,165 @@ +; MACRO.H +; Version 1.06, 3/SEPTEMBER/2004 + +VERSION_MACRO = 106 + +; +; THIS FILE IS EXPLICITLY SUPPORTED AS A DASM-PREFERRED COMPANION FILE +; PLEASE DO *NOT* REDISTRIBUTE MODIFIED VERSIONS OF THIS FILE! +; +; This file defines DASM macros useful for development for the Atari 2600. +; It is distributed as a companion machine-specific support package +; for the DASM compiler. Updates to this file, DASM, and associated tools are +; available at at http://www.atari2600.org/dasm +; +; Many thanks to the people who have contributed. If you take issue with the +; contents, or would like to add something, please write to me +; (atari2600@taswegian.com) with your contribution. +; +; Latest Revisions... +; +; 1.06 03/SEP/2004 - nice revision of VERTICAL_BLANK (Edwin Blink) +; 1.05 14/NOV/2003 - Added VERSION_MACRO equate (which will reflect 100x version #) +; This will allow conditional code to verify MACRO.H being +; used for code assembly. +; 1.04 13/NOV/2003 - SET_POINTER macro added (16-bit address load) +; +; 1.03 23/JUN/2003 - CLEAN_START macro added - clears TIA, RAM, registers +; +; 1.02 14/JUN/2003 - VERTICAL_SYNC macro added +; (standardised macro for vertical synch code) +; 1.01 22/MAR/2003 - SLEEP macro added. +; - NO_ILLEGAL_OPCODES switch implemented +; 1.0 22/MAR/2003 Initial release + +; Note: These macros use illegal opcodes. To disable illegal opcode usage, +; define the symbol NO_ILLEGAL_OPCODES (-DNO_ILLEGAL_OPCODES=1 on command-line). +; If you do not allow illegal opcode usage, you must include this file +; *after* including VCS.H (as the non-illegal opcodes access hardware +; registers and require them to be defined first). + +; Available macros... +; SLEEP n - sleep for n cycles +; VERTICAL_SYNC - correct 3 scanline vertical synch code +; CLEAN_START - set machine to known state on startup +; SET_POINTER - load a 16-bit absolute to a 16-bit variable + +;------------------------------------------------------------------------------- +; SLEEP duration +; Original author: Thomas Jentzsch +; Inserts code which takes the specified number of cycles to execute. This is +; useful for code where precise timing is required. +; ILLEGAL-OPCODE VERSION DOES NOT AFFECT FLAGS OR REGISTERS. +; LEGAL OPCODE VERSION MAY AFFECT FLAGS +; Uses illegal opcode (DASM 2.20.01 onwards). + + MAC SLEEP ;usage: SLEEP n (n>1) +.CYCLES SET {1} + + IF .CYCLES < 2 + ECHO "MACRO ERROR: 'SLEEP': Duration must be > 1" + ERR + ENDIF + + IF .CYCLES & 1 + IFNCONST NO_ILLEGAL_OPCODES + nop 0 + ELSE + bit VSYNC + ENDIF +.CYCLES SET .CYCLES - 3 + ENDIF + + REPEAT .CYCLES / 2 + nop + REPEND + ENDM + +;------------------------------------------------------------------------------- +; VERTICAL_SYNC +; revised version by Edwin Blink -- saves bytes! +; Inserts the code required for a proper 3 scanline vertical sync sequence +; Note: Alters the accumulator + +; OUT: A = 0 + + MAC VERTICAL_SYNC + lda #%1110 ; each '1' bits generate a VSYNC ON line (bits 1..3) +.VSLP1 sta WSYNC ; 1st '0' bit resets Vsync, 2nd '0' bit exit loop + sta VSYNC + lsr + bne .VSLP1 ; branch until VYSNC has been reset + ENDM + +;------------------------------------------------------------------------------- +; CLEAN_START +; Original author: Andrew Davie +; Standardised start-up code, clears stack, all TIA registers and RAM to 0 +; Sets stack pointer to $FF, and all registers to 0 +; Sets decimal mode off, sets interrupt flag (kind of un-necessary) +; Use as very first section of code on boot (ie: at reset) +; Code written to minimise total ROM usage - uses weird 6502 knowledge :) + + MAC CLEAN_START + sei + cld + + ldx #0 + txa + tay +.CLEAR_STACK dex + txs + pha + bne .CLEAR_STACK ; SP=$FF, X = A = Y = 0 + + ENDM + +;------------------------------------------------------- +; SET_POINTER +; Original author: Manuel Rotschkar +; +; Sets a 2 byte RAM pointer to an absolute address. +; +; Usage: SET_POINTER pointer, address +; Example: SET_POINTER SpritePTR, SpriteData +; +; Note: Alters the accumulator, NZ flags +; IN 1: 2 byte RAM location reserved for pointer +; IN 2: absolute address + + MAC SET_POINTER +.POINTER SET {1} +.ADDRESS SET {2} + + LDA #<.ADDRESS ; Get Lowbyte of Address + STA .POINTER ; Store in pointer + LDA #>.ADDRESS ; Get Hibyte of Address + STA .POINTER+1 ; Store in pointer+1 + + ENDM + +;------------------------------------------------------- +; BOUNDARY byte# +; Original author: Denis Debro (borrowed from Bob Smith / Thomas) +; +; Push data to a certain position inside a page and keep count of how +; many free bytes the programmer will have. +; +; eg: BOUNDARY 5 ; position at byte #5 in page + +__DASM__TOTAL_FREE_MEMORY SET 0 +.FREE_BYTES SET 0 + MAC BOUNDARY + REPEAT 256 + IF <. % {1} = 0 + MEXIT + ELSE +.FREE_BYTES SET .FREE_BYTES + 1 + .byte $00 + ENDIF + REPEND +__DASM__TOTAL_FREE_MEMORY SET __DASM__TOTAL_FREE_MEMORY + .FREE_BYTES + ENDM + + +; EOF diff --git a/test/roms/bankswitching/_code/test.bin b/test/roms/bankswitching/_code/test.bin new file mode 100644 index 000000000..7c3cfba1e Binary files /dev/null and b/test/roms/bankswitching/_code/test.bin differ diff --git a/test/roms/bankswitching/_code/test.lst b/test/roms/bankswitching/_code/test.lst new file mode 100644 index 000000000..ccc974d97 --- /dev/null +++ b/test/roms/bankswitching/_code/test.lst @@ -0,0 +1,1493 @@ +------- FILE F8SC.asm LEVEL 1 PASS 2 + 1 10000 ???? ;;A bankswitching demo for the F8SC scheme. 2 4K banks of ROM + 128 bytes of RAM + 2 10000 ???? ;;By: Rick Skrbina 3/29/09 + 3 10000 ???? + 4 10000 ???? processor 6502 +------- FILE vcs.h LEVEL 2 PASS 2 + 0 10000 ???? include "vcs.h" + 1 10000 ???? ; + 2 10000 ???? ; VCS system equates + 3 10000 ???? ; + 4 10000 ???? ; Vertical blank registers + 5 10000 ???? ; + 6 10000 ???? 00 00 VSYNC = $00 + 7 10000 ???? 00 02 VS_Enable = 2 + 8 10000 ???? ; + 9 10000 ???? 00 01 VBLANK = $01 + 10 10000 ???? 00 02 VB_Enable = 2 + 11 10000 ???? 00 00 VB_Disable = 0 + 12 10000 ???? 00 40 VB_LatchEnable = 64 + 13 10000 ???? 00 00 VB_LatchDisable = 0 + 14 10000 ???? 00 80 VB_DumpPots = 128 + 15 10000 ???? ; I don't know a good name to un-dump the pots, + 16 10000 ???? ; at least that makes sense. + 17 10000 ???? + 18 10000 ???? 00 02 WSYNC = $02 + 19 10000 ???? 00 03 RSYNC = $03 ;for sadists + 20 10000 ???? ; + 21 10000 ???? ; Size registers for players and missiles + 22 10000 ???? ; + 23 10000 ???? 00 04 NUSIZ0 = $04 + 24 10000 ???? 00 05 NUSIZ1 = $05 + 25 10000 ???? 00 00 P_Single = 0 + 26 10000 ???? 00 01 P_TwoClose = 1 + 27 10000 ???? 00 02 P_TwoMedium = 2 + 28 10000 ???? 00 03 P_ThreeClose = 3 + 29 10000 ???? 00 04 P_TwoFar = 4 + 30 10000 ???? 00 05 P_Double = 5 + 31 10000 ???? 00 06 P_ThreeMedium = 6 + 32 10000 ???? 00 07 P_Quad = 7 + 33 10000 ???? + 34 10000 ???? 00 00 M_Single = $00 + 35 10000 ???? 00 10 M_Double = $10 + 36 10000 ???? 00 20 M_Quad = $20 + 37 10000 ???? 00 40 M_Oct = $40 + 38 10000 ???? + 39 10000 ???? ; + 40 10000 ???? ; Color registers + 41 10000 ???? ; + 42 10000 ???? 00 06 COLUP0 = $06 + 43 10000 ???? 00 07 COLUP1 = $07 + 44 10000 ???? 00 08 COLUPF = $08 + 45 10000 ???? 00 09 COLUBK = $09 + 46 10000 ???? + 47 10000 ???? ; + 48 10000 ???? ; Playfield Control + 49 10000 ???? ; + 50 10000 ???? 00 0a CTRLPF = $0A + 51 10000 ???? 00 01 PF_Reflect = $01 + 52 10000 ???? 00 02 PF_Score = $02 + 53 10000 ???? 00 04 PF_Priority = $04 + 54 10000 ???? ; Use missile equates to set ball width. + 55 10000 ???? + 56 10000 ???? 00 0b REFP0 = $0B + 57 10000 ???? 00 0c REFP1 = $0C + 58 10000 ???? 00 08 P_Reflect = $08 + 59 10000 ???? + 60 10000 ???? 00 0d PF0 = $0D + 61 10000 ???? 00 0e PF1 = $0E + 62 10000 ???? 00 0f PF2 = $0F + 63 10000 ???? 00 10 RESP0 = $10 + 64 10000 ???? 00 11 RESP1 = $11 + 65 10000 ???? 00 12 RESM0 = $12 + 66 10000 ???? 00 13 RESM1 = $13 + 67 10000 ???? 00 14 RESBL = $14 + 68 10000 ???? 00 15 AUDC0 = $15 + 69 10000 ???? 00 16 AUDC1 = $16 + 70 10000 ???? 00 17 AUDF0 = $17 + 71 10000 ???? 00 18 AUDF1 = $18 + 72 10000 ???? 00 19 AUDV0 = $19 + 73 10000 ???? 00 1a AUDV1 = $1A ;duh + 74 10000 ???? + 75 10000 ???? ; + 76 10000 ???? ; Players + 77 10000 ???? ; + 78 10000 ???? 00 1b GRP0 = $1B + 79 10000 ???? 00 1c GRP1 = $1C + 80 10000 ???? + 81 10000 ???? ; + 82 10000 ???? ; Single-bit objects + 83 10000 ???? ; + 84 10000 ???? 00 1d ENAM0 = $1D + 85 10000 ???? 00 1e ENAM1 = $1E + 86 10000 ???? 00 1f ENABL = $1F + 87 10000 ???? 00 02 M_Enable = 2 + 88 10000 ???? + 89 10000 ???? 00 20 HMP0 = $20 + 90 10000 ???? 00 21 HMP1 = $21 + 91 10000 ???? 00 22 HMM0 = $22 + 92 10000 ???? 00 23 HMM1 = $23 + 93 10000 ???? 00 24 HMBL = $24 + 94 10000 ???? + 95 10000 ???? ; Miscellaneous + 96 10000 ???? 00 25 VDELP0 = $25 + 97 10000 ???? 00 26 VDEL01 = $26 + 98 10000 ???? 00 26 VDELP1 = $26 + 99 10000 ???? 00 27 VDELBL = $27 + 100 10000 ???? 00 28 RESMP0 = $28 + 101 10000 ???? 00 29 RESMP1 = $29 + 102 10000 ???? 00 2a HMOVE = $2A + 103 10000 ???? 00 2b HMCLR = $2B + 104 10000 ???? 00 2c CXCLR = $2C + 105 10000 ???? 00 30 CXM0P = $30 + 106 10000 ???? 00 31 CXM1P = $31 + 107 10000 ???? 00 32 CXP0FB = $32 + 108 10000 ???? 00 33 CXP1FB = $33 + 109 10000 ???? 00 34 CXM0FB = $34 + 110 10000 ???? 00 35 CXM1FB = $35 + 111 10000 ???? 00 36 CXBLPF = $36 + 112 10000 ???? 00 37 CXPPMM = $37 + 113 10000 ???? 00 38 INPT0 = $38 + 114 10000 ???? 00 39 INPT1 = $39 + 115 10000 ???? 00 3a INPT2 = $3A + 116 10000 ???? 00 3b INPT3 = $3B + 117 10000 ???? 00 3c INPT4 = $3C + 118 10000 ???? 00 3d INPT5 = $3D + 119 10000 ???? + 120 10000 ???? ; + 121 10000 ???? ; Switch A equates. + 122 10000 ???? ; + 123 10000 ???? ; There are more elegant ways than using all eight of these. :-) + 124 10000 ???? ; + 125 10000 ???? 02 80 SWCHA = $0280 + 126 10000 ???? 00 80 J0_Right = $80 + 127 10000 ???? 00 40 J0_Left = $40 + 128 10000 ???? 00 20 J0_Down = $20 + 129 10000 ???? 00 10 J0_Up = $10 + 130 10000 ???? 00 08 J1_Right = $08 + 131 10000 ???? 00 04 J1_Left = $04 + 132 10000 ???? 00 02 J1_Down = $02 + 133 10000 ???? 00 01 J1_up = $01 + 134 10000 ???? ; + 135 10000 ???? ; Switch B equates + 136 10000 ???? ; + 137 10000 ???? 02 82 SWCHB = $0282 + 138 10000 ???? 00 80 P0_Diff = $80 + 139 10000 ???? 00 40 P1_Diff = $40 + 140 10000 ???? 00 08 Con_Color = $08 + 141 10000 ???? 00 02 Con_Select = $02 + 142 10000 ???? 00 01 Con_Start = $01 + 143 10000 ???? + 144 10000 ???? ; + 145 10000 ???? ; Timer + 146 10000 ???? ; + 147 10000 ???? 02 80 SWCHA = $0280 + 148 10000 ???? 02 81 SWACNT = $0281 + 149 10000 ???? 02 82 SWCHB = $0282 + 150 10000 ???? 02 83 SWBCNT = $0283 + 151 10000 ???? 02 84 INTIM = $0284 + 152 10000 ???? 02 94 TIM1T = $0294 + 153 10000 ???? 02 95 TIM8T = $0295 + 154 10000 ???? 02 96 TIM64T = $0296 + 155 10000 ???? 02 97 TIM1024T = $0297 + 156 10000 ???? + 157 10000 ???? +------- FILE F8SC.asm +------- FILE macro.h LEVEL 2 PASS 2 + 0 10000 ???? include "macro.h" + 1 10000 ???? ; MACRO.H + 2 10000 ???? ; Version 1.06, 3/SEPTEMBER/2004 + 3 10000 ???? + 4 10000 ???? 00 6a VERSION_MACRO = 106 + 5 10000 ???? + 6 10000 ???? ; + 7 10000 ???? ; THIS FILE IS EXPLICITLY SUPPORTED AS A DASM-PREFERRED COMPANION FILE + 8 10000 ???? ; PLEASE DO *NOT* REDISTRIBUTE MODIFIED VERSIONS OF THIS FILE! + 9 10000 ???? ; + 10 10000 ???? ; This file defines DASM macros useful for development for the Atari 2600. + 11 10000 ???? ; It is distributed as a companion machine-specific support package + 12 10000 ???? ; for the DASM compiler. Updates to this file, DASM, and associated tools are + 13 10000 ???? ; available at at http://www.atari2600.org/dasm + 14 10000 ???? ; + 15 10000 ???? ; Many thanks to the people who have contributed. If you take issue with the + 16 10000 ???? ; contents, or would like to add something, please write to me + 17 10000 ???? ; (atari2600@taswegian.com) with your contribution. + 18 10000 ???? ; + 19 10000 ???? ; Latest Revisions... + 20 10000 ???? ; + 21 10000 ???? ; 1.06 03/SEP/2004 - nice revision of VERTICAL_BLANK (Edwin Blink) + 22 10000 ???? ; 1.05 14/NOV/2003 - Added VERSION_MACRO equate (which will reflect 100x version #) + 23 10000 ???? ; This will allow conditional code to verify MACRO.H being + 24 10000 ???? ; used for code assembly. + 25 10000 ???? ; 1.04 13/NOV/2003 - SET_POINTER macro added (16-bit address load) + 26 10000 ???? ; + 27 10000 ???? ; 1.03 23/JUN/2003 - CLEAN_START macro added - clears TIA, RAM, registers + 28 10000 ???? ; + 29 10000 ???? ; 1.02 14/JUN/2003 - VERTICAL_SYNC macro added + 30 10000 ???? ; (standardised macro for vertical synch code) + 31 10000 ???? ; 1.01 22/MAR/2003 - SLEEP macro added. + 32 10000 ???? ; - NO_ILLEGAL_OPCODES switch implemented + 33 10000 ???? ; 1.0 22/MAR/2003 Initial release + 34 10000 ???? + 35 10000 ???? ; Note: These macros use illegal opcodes. To disable illegal opcode usage, + 36 10000 ???? ; define the symbol NO_ILLEGAL_OPCODES (-DNO_ILLEGAL_OPCODES=1 on command-line). + 37 10000 ???? ; If you do not allow illegal opcode usage, you must include this file + 38 10000 ???? ; *after* including VCS.H (as the non-illegal opcodes access hardware + 39 10000 ???? ; registers and require them to be defined first). + 40 10000 ???? + 41 10000 ???? ; Available macros... + 42 10000 ???? ; SLEEP n - sleep for n cycles + 43 10000 ???? ; VERTICAL_SYNC - correct 3 scanline vertical synch code + 44 10000 ???? ; CLEAN_START - set machine to known state on startup + 45 10000 ???? ; SET_POINTER - load a 16-bit absolute to a 16-bit variable + 46 10000 ???? + 47 10000 ???? ;------------------------------------------------------------------------------- + 48 10000 ???? ; SLEEP duration + 49 10000 ???? ; Original author: Thomas Jentzsch + 50 10000 ???? ; Inserts code which takes the specified number of cycles to execute. This is + 51 10000 ???? ; useful for code where precise timing is required. + 52 10000 ???? ; ILLEGAL-OPCODE VERSION DOES NOT AFFECT FLAGS OR REGISTERS. + 53 10000 ???? ; LEGAL OPCODE VERSION MAY AFFECT FLAGS + 54 10000 ???? ; Uses illegal opcode (DASM 2.20.01 onwards). + 55 10000 ???? + 56 10000 ???? MAC sleep + 57 10000 ???? .CYCLES SET {1} + 58 10000 ???? + 59 10000 ???? IF .CYCLES < 2 + 60 10000 ???? ECHO "MACRO ERROR: 'SLEEP': Duration must be > 1" + 61 10000 ???? ERR + 62 10000 ???? ENDIF + 63 10000 ???? + 64 10000 ???? IF .CYCLES & 1 + 65 10000 ???? IFNCONST NO_ILLEGAL_OPCODES + 66 10000 ???? nop 0 + 67 10000 ???? ELSE + 68 10000 ???? bit VSYNC + 69 10000 ???? ENDIF + 70 10000 ???? .CYCLES SET .CYCLES - 3 + 71 10000 ???? ENDIF + 72 10000 ???? + 73 10000 ???? REPEAT .CYCLES / 2 + 74 10000 ???? nop + 75 10000 ???? REPEND + 76 10000 ???? ENDM ;usage: SLEEP n (n>1) + 77 10000 ???? + 78 10000 ???? ;------------------------------------------------------------------------------- + 79 10000 ???? ; VERTICAL_SYNC + 80 10000 ???? ; revised version by Edwin Blink -- saves bytes! + 81 10000 ???? ; Inserts the code required for a proper 3 scanline vertical sync sequence + 82 10000 ???? ; Note: Alters the accumulator + 83 10000 ???? + 84 10000 ???? ; OUT: A = 0 + 85 10000 ???? + 86 10000 ???? MAC vertical_sync + 87 10000 ???? lda #%1110 ; each '1' bits generate a VSYNC ON line (bits 1..3) + 88 10000 ???? .VSLP1 sta WSYNC ; 1st '0' bit resets Vsync, 2nd '0' bit exit loop + 89 10000 ???? sta VSYNC + 90 10000 ???? lsr + 91 10000 ???? bne .VSLP1 ; branch until VYSNC has been reset + 92 10000 ???? ENDM + 93 10000 ???? + 94 10000 ???? ;------------------------------------------------------------------------------- + 95 10000 ???? ; CLEAN_START + 96 10000 ???? ; Original author: Andrew Davie + 97 10000 ???? ; Standardised start-up code, clears stack, all TIA registers and RAM to 0 + 98 10000 ???? ; Sets stack pointer to $FF, and all registers to 0 + 99 10000 ???? ; Sets decimal mode off, sets interrupt flag (kind of un-necessary) + 100 10000 ???? ; Use as very first section of code on boot (ie: at reset) + 101 10000 ???? ; Code written to minimise total ROM usage - uses weird 6502 knowledge :) + 102 10000 ???? + 103 10000 ???? MAC clean_start + 104 10000 ???? sei + 105 10000 ???? cld + 106 10000 ???? + 107 10000 ???? ldx #0 + 108 10000 ???? txa + 109 10000 ???? tay + 110 10000 ???? .CLEAR_STACK dex + 111 10000 ???? txs + 112 10000 ???? pha + 113 10000 ???? bne .CLEAR_STACK ; SP=$FF, X = A = Y = 0 + 114 10000 ???? + 115 10000 ???? ENDM + 116 10000 ???? + 117 10000 ???? ;------------------------------------------------------- + 118 10000 ???? ; SET_POINTER + 119 10000 ???? ; Original author: Manuel Rotschkar + 120 10000 ???? ; + 121 10000 ???? ; Sets a 2 byte RAM pointer to an absolute address. + 122 10000 ???? ; + 123 10000 ???? ; Usage: SET_POINTER pointer, address + 124 10000 ???? ; Example: SET_POINTER SpritePTR, SpriteData + 125 10000 ???? ; + 126 10000 ???? ; Note: Alters the accumulator, NZ flags + 127 10000 ???? ; IN 1: 2 byte RAM location reserved for pointer + 128 10000 ???? ; IN 2: absolute address + 129 10000 ???? + 130 10000 ???? MAC set_pointer + 131 10000 ???? .POINTER SET {1} + 132 10000 ???? .ADDRESS SET {2} + 133 10000 ???? + 134 10000 ???? LDA #<.ADDRESS ; Get Lowbyte of Address + 135 10000 ???? STA .POINTER ; Store in pointer + 136 10000 ???? LDA #>.ADDRESS ; Get Hibyte of Address + 137 10000 ???? STA .POINTER+1 ; Store in pointer+1 + 138 10000 ???? + 139 10000 ???? ENDM + 140 10000 ???? + 141 10000 ???? ;------------------------------------------------------- + 142 10000 ???? ; BOUNDARY byte# + 143 10000 ???? ; Original author: Denis Debro (borrowed from Bob Smith / Thomas) + 144 10000 ???? ; + 145 10000 ???? ; Push data to a certain position inside a page and keep count of how + 146 10000 ???? ; many free bytes the programmer will have. + 147 10000 ???? ; + 148 10000 ???? ; eg: BOUNDARY 5 ; position at byte #5 in page + 149 10000 ???? + 150 10000 ???? __DASM__TOTAL_FREE_MEMORY SET 0 + 151 10000 ???? .FREE_BYTES SET 0 + 152 10000 ???? MAC boundary + 153 10000 ???? REPEAT 256 + 154 10000 ???? IF <. % {1} = 0 + 155 10000 ???? MEXIT + 156 10000 ???? ELSE + 157 10000 ???? .FREE_BYTES SET .FREE_BYTES + 1 + 158 10000 ???? .byte $00 + 159 10000 ???? ENDIF + 160 10000 ???? REPEND + 161 10000 ???? __DASM__TOTAL_FREE_MEMORY SET __DASM__TOTAL_FREE_MEMORY + .FREE_BYTES + 162 10000 ???? ENDM + 163 10000 ???? + 164 10000 ???? + 165 10000 ???? ; EOF +------- FILE F8SC.asm + 7 10000 ???? + 8 10000 ???? 10 80 PF_Color_Read equ PF_Color_Write+128 + 9 U0080 ???? seg.u vars + 10 U0080 org $80 + 11 U0080 + 12 U0080 + 13 U0080 + 14 U1001 ???? seg.u SC_RAM_vars + 15 U1000 org $1000 + 16 U1000 00 PF_Color_Write ds 1 + 17 U1001 + 18 f000 ???? seg Bank_0 + 19 f000 ???? + 20 e000 org $E000 + 21 e000 rorg $F000 + 22 e000 + 23 e000 repeat 256 + 24 e000 00 .byte.b $00 + 23 e000 repend + 24 e001 00 .byte.b $00 + 23 e001 repend + 24 e002 00 .byte.b $00 + 23 e002 repend + 24 e003 00 .byte.b $00 + 23 e003 repend + 24 e004 00 .byte.b $00 + 23 e004 repend + 24 e005 00 .byte.b $00 + 23 e005 repend + 24 e006 00 .byte.b $00 + 23 e006 repend + 24 e007 00 .byte.b $00 + 23 e007 repend + 24 e008 00 .byte.b $00 + 23 e008 repend + 24 e009 00 .byte.b $00 + 23 e009 repend + 24 e00a 00 .byte.b $00 + 23 e00a repend + 24 e00b 00 .byte.b $00 + 23 e00b repend + 24 e00c 00 .byte.b $00 + 23 e00c repend + 24 e00d 00 .byte.b $00 + 23 e00d repend + 24 e00e 00 .byte.b $00 + 23 e00e repend + 24 e00f 00 .byte.b $00 + 23 e00f repend + 24 e010 00 .byte.b $00 + 23 e010 repend + 24 e011 00 .byte.b $00 + 23 e011 repend + 24 e012 00 .byte.b $00 + 23 e012 repend + 24 e013 00 .byte.b $00 + 23 e013 repend + 24 e014 00 .byte.b $00 + 23 e014 repend + 24 e015 00 .byte.b $00 + 23 e015 repend + 24 e016 00 .byte.b $00 + 23 e016 repend + 24 e017 00 .byte.b $00 + 23 e017 repend + 24 e018 00 .byte.b $00 + 23 e018 repend + 24 e019 00 .byte.b $00 + 23 e019 repend + 24 e01a 00 .byte.b $00 + 23 e01a repend + 24 e01b 00 .byte.b $00 + 23 e01b repend + 24 e01c 00 .byte.b $00 + 23 e01c repend + 24 e01d 00 .byte.b $00 + 23 e01d repend + 24 e01e 00 .byte.b $00 + 23 e01e repend + 24 e01f 00 .byte.b $00 + 23 e01f repend + 24 e020 00 .byte.b $00 + 23 e020 repend + 24 e021 00 .byte.b $00 + 23 e021 repend + 24 e022 00 .byte.b $00 + 23 e022 repend + 24 e023 00 .byte.b $00 + 23 e023 repend + 24 e024 00 .byte.b $00 + 23 e024 repend + 24 e025 00 .byte.b $00 + 23 e025 repend + 24 e026 00 .byte.b $00 + 23 e026 repend + 24 e027 00 .byte.b $00 + 23 e027 repend + 24 e028 00 .byte.b $00 + 23 e028 repend + 24 e029 00 .byte.b $00 + 23 e029 repend + 24 e02a 00 .byte.b $00 + 23 e02a repend + 24 e02b 00 .byte.b $00 + 23 e02b repend + 24 e02c 00 .byte.b $00 + 23 e02c repend + 24 e02d 00 .byte.b $00 + 23 e02d repend + 24 e02e 00 .byte.b $00 + 23 e02e repend + 24 e02f 00 .byte.b $00 + 23 e02f repend + 24 e030 00 .byte.b $00 + 23 e030 repend + 24 e031 00 .byte.b $00 + 23 e031 repend + 24 e032 00 .byte.b $00 + 23 e032 repend + 24 e033 00 .byte.b $00 + 23 e033 repend + 24 e034 00 .byte.b $00 + 23 e034 repend + 24 e035 00 .byte.b $00 + 23 e035 repend + 24 e036 00 .byte.b $00 + 23 e036 repend + 24 e037 00 .byte.b $00 + 23 e037 repend + 24 e038 00 .byte.b $00 + 23 e038 repend + 24 e039 00 .byte.b $00 + 23 e039 repend + 24 e03a 00 .byte.b $00 + 23 e03a repend + 24 e03b 00 .byte.b $00 + 23 e03b repend + 24 e03c 00 .byte.b $00 + 23 e03c repend + 24 e03d 00 .byte.b $00 + 23 e03d repend + 24 e03e 00 .byte.b $00 + 23 e03e repend + 24 e03f 00 .byte.b $00 + 23 e03f repend + 24 e040 00 .byte.b $00 + 23 e040 repend + 24 e041 00 .byte.b $00 + 23 e041 repend + 24 e042 00 .byte.b $00 + 23 e042 repend + 24 e043 00 .byte.b $00 + 23 e043 repend + 24 e044 00 .byte.b $00 + 23 e044 repend + 24 e045 00 .byte.b $00 + 23 e045 repend + 24 e046 00 .byte.b $00 + 23 e046 repend + 24 e047 00 .byte.b $00 + 23 e047 repend + 24 e048 00 .byte.b $00 + 23 e048 repend + 24 e049 00 .byte.b $00 + 23 e049 repend + 24 e04a 00 .byte.b $00 + 23 e04a repend + 24 e04b 00 .byte.b $00 + 23 e04b repend + 24 e04c 00 .byte.b $00 + 23 e04c repend + 24 e04d 00 .byte.b $00 + 23 e04d repend + 24 e04e 00 .byte.b $00 + 23 e04e repend + 24 e04f 00 .byte.b $00 + 23 e04f repend + 24 e050 00 .byte.b $00 + 23 e050 repend + 24 e051 00 .byte.b $00 + 23 e051 repend + 24 e052 00 .byte.b $00 + 23 e052 repend + 24 e053 00 .byte.b $00 + 23 e053 repend + 24 e054 00 .byte.b $00 + 23 e054 repend + 24 e055 00 .byte.b $00 + 23 e055 repend + 24 e056 00 .byte.b $00 + 23 e056 repend + 24 e057 00 .byte.b $00 + 23 e057 repend + 24 e058 00 .byte.b $00 + 23 e058 repend + 24 e059 00 .byte.b $00 + 23 e059 repend + 24 e05a 00 .byte.b $00 + 23 e05a repend + 24 e05b 00 .byte.b $00 + 23 e05b repend + 24 e05c 00 .byte.b $00 + 23 e05c repend + 24 e05d 00 .byte.b $00 + 23 e05d repend + 24 e05e 00 .byte.b $00 + 23 e05e repend + 24 e05f 00 .byte.b $00 + 23 e05f repend + 24 e060 00 .byte.b $00 + 23 e060 repend + 24 e061 00 .byte.b $00 + 23 e061 repend + 24 e062 00 .byte.b $00 + 23 e062 repend + 24 e063 00 .byte.b $00 + 23 e063 repend + 24 e064 00 .byte.b $00 + 23 e064 repend + 24 e065 00 .byte.b $00 + 23 e065 repend + 24 e066 00 .byte.b $00 + 23 e066 repend + 24 e067 00 .byte.b $00 + 23 e067 repend + 24 e068 00 .byte.b $00 + 23 e068 repend + 24 e069 00 .byte.b $00 + 23 e069 repend + 24 e06a 00 .byte.b $00 + 23 e06a repend + 24 e06b 00 .byte.b $00 + 23 e06b repend + 24 e06c 00 .byte.b $00 + 23 e06c repend + 24 e06d 00 .byte.b $00 + 23 e06d repend + 24 e06e 00 .byte.b $00 + 23 e06e repend + 24 e06f 00 .byte.b $00 + 23 e06f repend + 24 e070 00 .byte.b $00 + 23 e070 repend + 24 e071 00 .byte.b $00 + 23 e071 repend + 24 e072 00 .byte.b $00 + 23 e072 repend + 24 e073 00 .byte.b $00 + 23 e073 repend + 24 e074 00 .byte.b $00 + 23 e074 repend + 24 e075 00 .byte.b $00 + 23 e075 repend + 24 e076 00 .byte.b $00 + 23 e076 repend + 24 e077 00 .byte.b $00 + 23 e077 repend + 24 e078 00 .byte.b $00 + 23 e078 repend + 24 e079 00 .byte.b $00 + 23 e079 repend + 24 e07a 00 .byte.b $00 + 23 e07a repend + 24 e07b 00 .byte.b $00 + 23 e07b repend + 24 e07c 00 .byte.b $00 + 23 e07c repend + 24 e07d 00 .byte.b $00 + 23 e07d repend + 24 e07e 00 .byte.b $00 + 23 e07e repend + 24 e07f 00 .byte.b $00 + 23 e07f repend + 24 e080 00 .byte.b $00 + 23 e080 repend + 24 e081 00 .byte.b $00 + 23 e081 repend + 24 e082 00 .byte.b $00 + 23 e082 repend + 24 e083 00 .byte.b $00 + 23 e083 repend + 24 e084 00 .byte.b $00 + 23 e084 repend + 24 e085 00 .byte.b $00 + 23 e085 repend + 24 e086 00 .byte.b $00 + 23 e086 repend + 24 e087 00 .byte.b $00 + 23 e087 repend + 24 e088 00 .byte.b $00 + 23 e088 repend + 24 e089 00 .byte.b $00 + 23 e089 repend + 24 e08a 00 .byte.b $00 + 23 e08a repend + 24 e08b 00 .byte.b $00 + 23 e08b repend + 24 e08c 00 .byte.b $00 + 23 e08c repend + 24 e08d 00 .byte.b $00 + 23 e08d repend + 24 e08e 00 .byte.b $00 + 23 e08e repend + 24 e08f 00 .byte.b $00 + 23 e08f repend + 24 e090 00 .byte.b $00 + 23 e090 repend + 24 e091 00 .byte.b $00 + 23 e091 repend + 24 e092 00 .byte.b $00 + 23 e092 repend + 24 e093 00 .byte.b $00 + 23 e093 repend + 24 e094 00 .byte.b $00 + 23 e094 repend + 24 e095 00 .byte.b $00 + 23 e095 repend + 24 e096 00 .byte.b $00 + 23 e096 repend + 24 e097 00 .byte.b $00 + 23 e097 repend + 24 e098 00 .byte.b $00 + 23 e098 repend + 24 e099 00 .byte.b $00 + 23 e099 repend + 24 e09a 00 .byte.b $00 + 23 e09a repend + 24 e09b 00 .byte.b $00 + 23 e09b repend + 24 e09c 00 .byte.b $00 + 23 e09c repend + 24 e09d 00 .byte.b $00 + 23 e09d repend + 24 e09e 00 .byte.b $00 + 23 e09e repend + 24 e09f 00 .byte.b $00 + 23 e09f repend + 24 e0a0 00 .byte.b $00 + 23 e0a0 repend + 24 e0a1 00 .byte.b $00 + 23 e0a1 repend + 24 e0a2 00 .byte.b $00 + 23 e0a2 repend + 24 e0a3 00 .byte.b $00 + 23 e0a3 repend + 24 e0a4 00 .byte.b $00 + 23 e0a4 repend + 24 e0a5 00 .byte.b $00 + 23 e0a5 repend + 24 e0a6 00 .byte.b $00 + 23 e0a6 repend + 24 e0a7 00 .byte.b $00 + 23 e0a7 repend + 24 e0a8 00 .byte.b $00 + 23 e0a8 repend + 24 e0a9 00 .byte.b $00 + 23 e0a9 repend + 24 e0aa 00 .byte.b $00 + 23 e0aa repend + 24 e0ab 00 .byte.b $00 + 23 e0ab repend + 24 e0ac 00 .byte.b $00 + 23 e0ac repend + 24 e0ad 00 .byte.b $00 + 23 e0ad repend + 24 e0ae 00 .byte.b $00 + 23 e0ae repend + 24 e0af 00 .byte.b $00 + 23 e0af repend + 24 e0b0 00 .byte.b $00 + 23 e0b0 repend + 24 e0b1 00 .byte.b $00 + 23 e0b1 repend + 24 e0b2 00 .byte.b $00 + 23 e0b2 repend + 24 e0b3 00 .byte.b $00 + 23 e0b3 repend + 24 e0b4 00 .byte.b $00 + 23 e0b4 repend + 24 e0b5 00 .byte.b $00 + 23 e0b5 repend + 24 e0b6 00 .byte.b $00 + 23 e0b6 repend + 24 e0b7 00 .byte.b $00 + 23 e0b7 repend + 24 e0b8 00 .byte.b $00 + 23 e0b8 repend + 24 e0b9 00 .byte.b $00 + 23 e0b9 repend + 24 e0ba 00 .byte.b $00 + 23 e0ba repend + 24 e0bb 00 .byte.b $00 + 23 e0bb repend + 24 e0bc 00 .byte.b $00 + 23 e0bc repend + 24 e0bd 00 .byte.b $00 + 23 e0bd repend + 24 e0be 00 .byte.b $00 + 23 e0be repend + 24 e0bf 00 .byte.b $00 + 23 e0bf repend + 24 e0c0 00 .byte.b $00 + 23 e0c0 repend + 24 e0c1 00 .byte.b $00 + 23 e0c1 repend + 24 e0c2 00 .byte.b $00 + 23 e0c2 repend + 24 e0c3 00 .byte.b $00 + 23 e0c3 repend + 24 e0c4 00 .byte.b $00 + 23 e0c4 repend + 24 e0c5 00 .byte.b $00 + 23 e0c5 repend + 24 e0c6 00 .byte.b $00 + 23 e0c6 repend + 24 e0c7 00 .byte.b $00 + 23 e0c7 repend + 24 e0c8 00 .byte.b $00 + 23 e0c8 repend + 24 e0c9 00 .byte.b $00 + 23 e0c9 repend + 24 e0ca 00 .byte.b $00 + 23 e0ca repend + 24 e0cb 00 .byte.b $00 + 23 e0cb repend + 24 e0cc 00 .byte.b $00 + 23 e0cc repend + 24 e0cd 00 .byte.b $00 + 23 e0cd repend + 24 e0ce 00 .byte.b $00 + 23 e0ce repend + 24 e0cf 00 .byte.b $00 + 23 e0cf repend + 24 e0d0 00 .byte.b $00 + 23 e0d0 repend + 24 e0d1 00 .byte.b $00 + 23 e0d1 repend + 24 e0d2 00 .byte.b $00 + 23 e0d2 repend + 24 e0d3 00 .byte.b $00 + 23 e0d3 repend + 24 e0d4 00 .byte.b $00 + 23 e0d4 repend + 24 e0d5 00 .byte.b $00 + 23 e0d5 repend + 24 e0d6 00 .byte.b $00 + 23 e0d6 repend + 24 e0d7 00 .byte.b $00 + 23 e0d7 repend + 24 e0d8 00 .byte.b $00 + 23 e0d8 repend + 24 e0d9 00 .byte.b $00 + 23 e0d9 repend + 24 e0da 00 .byte.b $00 + 23 e0da repend + 24 e0db 00 .byte.b $00 + 23 e0db repend + 24 e0dc 00 .byte.b $00 + 23 e0dc repend + 24 e0dd 00 .byte.b $00 + 23 e0dd repend + 24 e0de 00 .byte.b $00 + 23 e0de repend + 24 e0df 00 .byte.b $00 + 23 e0df repend + 24 e0e0 00 .byte.b $00 + 23 e0e0 repend + 24 e0e1 00 .byte.b $00 + 23 e0e1 repend + 24 e0e2 00 .byte.b $00 + 23 e0e2 repend + 24 e0e3 00 .byte.b $00 + 23 e0e3 repend + 24 e0e4 00 .byte.b $00 + 23 e0e4 repend + 24 e0e5 00 .byte.b $00 + 23 e0e5 repend + 24 e0e6 00 .byte.b $00 + 23 e0e6 repend + 24 e0e7 00 .byte.b $00 + 23 e0e7 repend + 24 e0e8 00 .byte.b $00 + 23 e0e8 repend + 24 e0e9 00 .byte.b $00 + 23 e0e9 repend + 24 e0ea 00 .byte.b $00 + 23 e0ea repend + 24 e0eb 00 .byte.b $00 + 23 e0eb repend + 24 e0ec 00 .byte.b $00 + 23 e0ec repend + 24 e0ed 00 .byte.b $00 + 23 e0ed repend + 24 e0ee 00 .byte.b $00 + 23 e0ee repend + 24 e0ef 00 .byte.b $00 + 23 e0ef repend + 24 e0f0 00 .byte.b $00 + 23 e0f0 repend + 24 e0f1 00 .byte.b $00 + 23 e0f1 repend + 24 e0f2 00 .byte.b $00 + 23 e0f2 repend + 24 e0f3 00 .byte.b $00 + 23 e0f3 repend + 24 e0f4 00 .byte.b $00 + 23 e0f4 repend + 24 e0f5 00 .byte.b $00 + 23 e0f5 repend + 24 e0f6 00 .byte.b $00 + 23 e0f6 repend + 24 e0f7 00 .byte.b $00 + 23 e0f7 repend + 24 e0f8 00 .byte.b $00 + 23 e0f8 repend + 24 e0f9 00 .byte.b $00 + 23 e0f9 repend + 24 e0fa 00 .byte.b $00 + 23 e0fa repend + 24 e0fb 00 .byte.b $00 + 23 e0fb repend + 24 e0fc 00 .byte.b $00 + 23 e0fc repend + 24 e0fd 00 .byte.b $00 + 23 e0fd repend + 24 e0fe 00 .byte.b $00 + 23 e0fe repend + 24 e0ff 00 .byte.b $00 + 25 e100 repend + 26 e100 + 27 e100 Start_0 + 28 e100 ea nop + 29 e101 ea nop + 30 e102 ea nop + 31 e103 + 0 e103 CLEAN_START + 1 e103 78 sei + 2 e104 d8 cld + 3 e105 + 4 e105 a2 00 ldx #0 + 5 e107 8a txa + 6 e108 a8 tay + 7 e109 ca .CLEAR_STACK dex + 8 e10a 9a txs + 9 e10b 48 pha + 10 e10c d0 fb bne .CLEAR_STACK + 11 e10e + 33 e10e ; jsr Call_1 + 34 e10e a9 ff lda #$FF + 35 e110 85 0e sta PF1 + 36 e112 StartFrame + 37 e112 a9 02 lda #2 + 38 e114 85 01 sta VBLANK + 39 e116 85 00 sta VSYNC + 40 e118 + 41 e118 85 02 sta WSYNC + 42 e11a 85 02 sta WSYNC + 43 e11c 85 02 sta WSYNC + 44 e11e + 45 e11e a9 00 lda #0 + 46 e120 85 00 sta VSYNC + 47 e122 + 48 e122 a0 25 ldy #37 + 49 e124 Vert + 50 e124 85 02 sta WSYNC + 51 e126 88 dey + 52 e127 d0 fb bne Vert + 53 e129 + 54 e129 a9 00 lda #0 + 55 e12b 85 01 sta VBLANK + 56 e12d + 57 e12d a0 c0 ldy #192 + 58 e12f Pic + 59 e12f 85 02 sta WSYNC + 60 e131 88 dey + 61 e132 d0 fb bne Pic + 62 e134 + 63 e134 a9 02 lda #2 + 64 e136 85 01 sta VBLANK + 65 e138 + 66 e138 a0 1e ldy #30 + 67 e13a Over + 68 e13a 85 02 sta WSYNC + 69 e13c 88 dey + 70 e13d d0 fb bne Over + 71 e13f + 72 e13f 20 e0 ff jsr Call_1 + 73 e142 + 74 e142 ad 80 10 lda PF_Color_Read + 75 e145 85 08 sta COLUPF + 76 e147 + 77 e147 + 78 e147 4c 12 f1 jmp StartFrame + 79 e14a + 80 e14a + 81 e14a + 82 e14a + 83 efe0 org $EFE0 + 84 efe0 rorg $FFE0 + 85 efe0 Call_1 + 86 efe0 8e f9 ff stx $FFF9 + 87 efe3 + 88 efe3 ea nop + 89 efe4 ea nop + 90 efe5 ea nop + 91 efe6 ea nop + 92 efe7 ea nop + 93 efe8 ea nop + 94 efe9 + 95 efe9 60 rts + 96 efea + 97 eff8 org $EFF8 + 98 eff8 rorg $FFF8 + 99 eff8 ff ff .word.w $FFFF + 100 effa 00 f1 .word.w Start_0 + 101 effc 00 f1 .word.w Start_0 + 102 effe 00 f1 .word.w Start_0 + 103 f000 + 104 10000 ???? seg Bank_1 + 105 10000 ???? + 106 f000 org $F000 + 107 f000 rorg $F000 + 108 f000 + 109 f000 repeat 256 + 110 f000 00 .byte.b $00 + 109 f000 repend + 110 f001 00 .byte.b $00 + 109 f001 repend + 110 f002 00 .byte.b $00 + 109 f002 repend + 110 f003 00 .byte.b $00 + 109 f003 repend + 110 f004 00 .byte.b $00 + 109 f004 repend + 110 f005 00 .byte.b $00 + 109 f005 repend + 110 f006 00 .byte.b $00 + 109 f006 repend + 110 f007 00 .byte.b $00 + 109 f007 repend + 110 f008 00 .byte.b $00 + 109 f008 repend + 110 f009 00 .byte.b $00 + 109 f009 repend + 110 f00a 00 .byte.b $00 + 109 f00a repend + 110 f00b 00 .byte.b $00 + 109 f00b repend + 110 f00c 00 .byte.b $00 + 109 f00c repend + 110 f00d 00 .byte.b $00 + 109 f00d repend + 110 f00e 00 .byte.b $00 + 109 f00e repend + 110 f00f 00 .byte.b $00 + 109 f00f repend + 110 f010 00 .byte.b $00 + 109 f010 repend + 110 f011 00 .byte.b $00 + 109 f011 repend + 110 f012 00 .byte.b $00 + 109 f012 repend + 110 f013 00 .byte.b $00 + 109 f013 repend + 110 f014 00 .byte.b $00 + 109 f014 repend + 110 f015 00 .byte.b $00 + 109 f015 repend + 110 f016 00 .byte.b $00 + 109 f016 repend + 110 f017 00 .byte.b $00 + 109 f017 repend + 110 f018 00 .byte.b $00 + 109 f018 repend + 110 f019 00 .byte.b $00 + 109 f019 repend + 110 f01a 00 .byte.b $00 + 109 f01a repend + 110 f01b 00 .byte.b $00 + 109 f01b repend + 110 f01c 00 .byte.b $00 + 109 f01c repend + 110 f01d 00 .byte.b $00 + 109 f01d repend + 110 f01e 00 .byte.b $00 + 109 f01e repend + 110 f01f 00 .byte.b $00 + 109 f01f repend + 110 f020 00 .byte.b $00 + 109 f020 repend + 110 f021 00 .byte.b $00 + 109 f021 repend + 110 f022 00 .byte.b $00 + 109 f022 repend + 110 f023 00 .byte.b $00 + 109 f023 repend + 110 f024 00 .byte.b $00 + 109 f024 repend + 110 f025 00 .byte.b $00 + 109 f025 repend + 110 f026 00 .byte.b $00 + 109 f026 repend + 110 f027 00 .byte.b $00 + 109 f027 repend + 110 f028 00 .byte.b $00 + 109 f028 repend + 110 f029 00 .byte.b $00 + 109 f029 repend + 110 f02a 00 .byte.b $00 + 109 f02a repend + 110 f02b 00 .byte.b $00 + 109 f02b repend + 110 f02c 00 .byte.b $00 + 109 f02c repend + 110 f02d 00 .byte.b $00 + 109 f02d repend + 110 f02e 00 .byte.b $00 + 109 f02e repend + 110 f02f 00 .byte.b $00 + 109 f02f repend + 110 f030 00 .byte.b $00 + 109 f030 repend + 110 f031 00 .byte.b $00 + 109 f031 repend + 110 f032 00 .byte.b $00 + 109 f032 repend + 110 f033 00 .byte.b $00 + 109 f033 repend + 110 f034 00 .byte.b $00 + 109 f034 repend + 110 f035 00 .byte.b $00 + 109 f035 repend + 110 f036 00 .byte.b $00 + 109 f036 repend + 110 f037 00 .byte.b $00 + 109 f037 repend + 110 f038 00 .byte.b $00 + 109 f038 repend + 110 f039 00 .byte.b $00 + 109 f039 repend + 110 f03a 00 .byte.b $00 + 109 f03a repend + 110 f03b 00 .byte.b $00 + 109 f03b repend + 110 f03c 00 .byte.b $00 + 109 f03c repend + 110 f03d 00 .byte.b $00 + 109 f03d repend + 110 f03e 00 .byte.b $00 + 109 f03e repend + 110 f03f 00 .byte.b $00 + 109 f03f repend + 110 f040 00 .byte.b $00 + 109 f040 repend + 110 f041 00 .byte.b $00 + 109 f041 repend + 110 f042 00 .byte.b $00 + 109 f042 repend + 110 f043 00 .byte.b $00 + 109 f043 repend + 110 f044 00 .byte.b $00 + 109 f044 repend + 110 f045 00 .byte.b $00 + 109 f045 repend + 110 f046 00 .byte.b $00 + 109 f046 repend + 110 f047 00 .byte.b $00 + 109 f047 repend + 110 f048 00 .byte.b $00 + 109 f048 repend + 110 f049 00 .byte.b $00 + 109 f049 repend + 110 f04a 00 .byte.b $00 + 109 f04a repend + 110 f04b 00 .byte.b $00 + 109 f04b repend + 110 f04c 00 .byte.b $00 + 109 f04c repend + 110 f04d 00 .byte.b $00 + 109 f04d repend + 110 f04e 00 .byte.b $00 + 109 f04e repend + 110 f04f 00 .byte.b $00 + 109 f04f repend + 110 f050 00 .byte.b $00 + 109 f050 repend + 110 f051 00 .byte.b $00 + 109 f051 repend + 110 f052 00 .byte.b $00 + 109 f052 repend + 110 f053 00 .byte.b $00 + 109 f053 repend + 110 f054 00 .byte.b $00 + 109 f054 repend + 110 f055 00 .byte.b $00 + 109 f055 repend + 110 f056 00 .byte.b $00 + 109 f056 repend + 110 f057 00 .byte.b $00 + 109 f057 repend + 110 f058 00 .byte.b $00 + 109 f058 repend + 110 f059 00 .byte.b $00 + 109 f059 repend + 110 f05a 00 .byte.b $00 + 109 f05a repend + 110 f05b 00 .byte.b $00 + 109 f05b repend + 110 f05c 00 .byte.b $00 + 109 f05c repend + 110 f05d 00 .byte.b $00 + 109 f05d repend + 110 f05e 00 .byte.b $00 + 109 f05e repend + 110 f05f 00 .byte.b $00 + 109 f05f repend + 110 f060 00 .byte.b $00 + 109 f060 repend + 110 f061 00 .byte.b $00 + 109 f061 repend + 110 f062 00 .byte.b $00 + 109 f062 repend + 110 f063 00 .byte.b $00 + 109 f063 repend + 110 f064 00 .byte.b $00 + 109 f064 repend + 110 f065 00 .byte.b $00 + 109 f065 repend + 110 f066 00 .byte.b $00 + 109 f066 repend + 110 f067 00 .byte.b $00 + 109 f067 repend + 110 f068 00 .byte.b $00 + 109 f068 repend + 110 f069 00 .byte.b $00 + 109 f069 repend + 110 f06a 00 .byte.b $00 + 109 f06a repend + 110 f06b 00 .byte.b $00 + 109 f06b repend + 110 f06c 00 .byte.b $00 + 109 f06c repend + 110 f06d 00 .byte.b $00 + 109 f06d repend + 110 f06e 00 .byte.b $00 + 109 f06e repend + 110 f06f 00 .byte.b $00 + 109 f06f repend + 110 f070 00 .byte.b $00 + 109 f070 repend + 110 f071 00 .byte.b $00 + 109 f071 repend + 110 f072 00 .byte.b $00 + 109 f072 repend + 110 f073 00 .byte.b $00 + 109 f073 repend + 110 f074 00 .byte.b $00 + 109 f074 repend + 110 f075 00 .byte.b $00 + 109 f075 repend + 110 f076 00 .byte.b $00 + 109 f076 repend + 110 f077 00 .byte.b $00 + 109 f077 repend + 110 f078 00 .byte.b $00 + 109 f078 repend + 110 f079 00 .byte.b $00 + 109 f079 repend + 110 f07a 00 .byte.b $00 + 109 f07a repend + 110 f07b 00 .byte.b $00 + 109 f07b repend + 110 f07c 00 .byte.b $00 + 109 f07c repend + 110 f07d 00 .byte.b $00 + 109 f07d repend + 110 f07e 00 .byte.b $00 + 109 f07e repend + 110 f07f 00 .byte.b $00 + 109 f07f repend + 110 f080 00 .byte.b $00 + 109 f080 repend + 110 f081 00 .byte.b $00 + 109 f081 repend + 110 f082 00 .byte.b $00 + 109 f082 repend + 110 f083 00 .byte.b $00 + 109 f083 repend + 110 f084 00 .byte.b $00 + 109 f084 repend + 110 f085 00 .byte.b $00 + 109 f085 repend + 110 f086 00 .byte.b $00 + 109 f086 repend + 110 f087 00 .byte.b $00 + 109 f087 repend + 110 f088 00 .byte.b $00 + 109 f088 repend + 110 f089 00 .byte.b $00 + 109 f089 repend + 110 f08a 00 .byte.b $00 + 109 f08a repend + 110 f08b 00 .byte.b $00 + 109 f08b repend + 110 f08c 00 .byte.b $00 + 109 f08c repend + 110 f08d 00 .byte.b $00 + 109 f08d repend + 110 f08e 00 .byte.b $00 + 109 f08e repend + 110 f08f 00 .byte.b $00 + 109 f08f repend + 110 f090 00 .byte.b $00 + 109 f090 repend + 110 f091 00 .byte.b $00 + 109 f091 repend + 110 f092 00 .byte.b $00 + 109 f092 repend + 110 f093 00 .byte.b $00 + 109 f093 repend + 110 f094 00 .byte.b $00 + 109 f094 repend + 110 f095 00 .byte.b $00 + 109 f095 repend + 110 f096 00 .byte.b $00 + 109 f096 repend + 110 f097 00 .byte.b $00 + 109 f097 repend + 110 f098 00 .byte.b $00 + 109 f098 repend + 110 f099 00 .byte.b $00 + 109 f099 repend + 110 f09a 00 .byte.b $00 + 109 f09a repend + 110 f09b 00 .byte.b $00 + 109 f09b repend + 110 f09c 00 .byte.b $00 + 109 f09c repend + 110 f09d 00 .byte.b $00 + 109 f09d repend + 110 f09e 00 .byte.b $00 + 109 f09e repend + 110 f09f 00 .byte.b $00 + 109 f09f repend + 110 f0a0 00 .byte.b $00 + 109 f0a0 repend + 110 f0a1 00 .byte.b $00 + 109 f0a1 repend + 110 f0a2 00 .byte.b $00 + 109 f0a2 repend + 110 f0a3 00 .byte.b $00 + 109 f0a3 repend + 110 f0a4 00 .byte.b $00 + 109 f0a4 repend + 110 f0a5 00 .byte.b $00 + 109 f0a5 repend + 110 f0a6 00 .byte.b $00 + 109 f0a6 repend + 110 f0a7 00 .byte.b $00 + 109 f0a7 repend + 110 f0a8 00 .byte.b $00 + 109 f0a8 repend + 110 f0a9 00 .byte.b $00 + 109 f0a9 repend + 110 f0aa 00 .byte.b $00 + 109 f0aa repend + 110 f0ab 00 .byte.b $00 + 109 f0ab repend + 110 f0ac 00 .byte.b $00 + 109 f0ac repend + 110 f0ad 00 .byte.b $00 + 109 f0ad repend + 110 f0ae 00 .byte.b $00 + 109 f0ae repend + 110 f0af 00 .byte.b $00 + 109 f0af repend + 110 f0b0 00 .byte.b $00 + 109 f0b0 repend + 110 f0b1 00 .byte.b $00 + 109 f0b1 repend + 110 f0b2 00 .byte.b $00 + 109 f0b2 repend + 110 f0b3 00 .byte.b $00 + 109 f0b3 repend + 110 f0b4 00 .byte.b $00 + 109 f0b4 repend + 110 f0b5 00 .byte.b $00 + 109 f0b5 repend + 110 f0b6 00 .byte.b $00 + 109 f0b6 repend + 110 f0b7 00 .byte.b $00 + 109 f0b7 repend + 110 f0b8 00 .byte.b $00 + 109 f0b8 repend + 110 f0b9 00 .byte.b $00 + 109 f0b9 repend + 110 f0ba 00 .byte.b $00 + 109 f0ba repend + 110 f0bb 00 .byte.b $00 + 109 f0bb repend + 110 f0bc 00 .byte.b $00 + 109 f0bc repend + 110 f0bd 00 .byte.b $00 + 109 f0bd repend + 110 f0be 00 .byte.b $00 + 109 f0be repend + 110 f0bf 00 .byte.b $00 + 109 f0bf repend + 110 f0c0 00 .byte.b $00 + 109 f0c0 repend + 110 f0c1 00 .byte.b $00 + 109 f0c1 repend + 110 f0c2 00 .byte.b $00 + 109 f0c2 repend + 110 f0c3 00 .byte.b $00 + 109 f0c3 repend + 110 f0c4 00 .byte.b $00 + 109 f0c4 repend + 110 f0c5 00 .byte.b $00 + 109 f0c5 repend + 110 f0c6 00 .byte.b $00 + 109 f0c6 repend + 110 f0c7 00 .byte.b $00 + 109 f0c7 repend + 110 f0c8 00 .byte.b $00 + 109 f0c8 repend + 110 f0c9 00 .byte.b $00 + 109 f0c9 repend + 110 f0ca 00 .byte.b $00 + 109 f0ca repend + 110 f0cb 00 .byte.b $00 + 109 f0cb repend + 110 f0cc 00 .byte.b $00 + 109 f0cc repend + 110 f0cd 00 .byte.b $00 + 109 f0cd repend + 110 f0ce 00 .byte.b $00 + 109 f0ce repend + 110 f0cf 00 .byte.b $00 + 109 f0cf repend + 110 f0d0 00 .byte.b $00 + 109 f0d0 repend + 110 f0d1 00 .byte.b $00 + 109 f0d1 repend + 110 f0d2 00 .byte.b $00 + 109 f0d2 repend + 110 f0d3 00 .byte.b $00 + 109 f0d3 repend + 110 f0d4 00 .byte.b $00 + 109 f0d4 repend + 110 f0d5 00 .byte.b $00 + 109 f0d5 repend + 110 f0d6 00 .byte.b $00 + 109 f0d6 repend + 110 f0d7 00 .byte.b $00 + 109 f0d7 repend + 110 f0d8 00 .byte.b $00 + 109 f0d8 repend + 110 f0d9 00 .byte.b $00 + 109 f0d9 repend + 110 f0da 00 .byte.b $00 + 109 f0da repend + 110 f0db 00 .byte.b $00 + 109 f0db repend + 110 f0dc 00 .byte.b $00 + 109 f0dc repend + 110 f0dd 00 .byte.b $00 + 109 f0dd repend + 110 f0de 00 .byte.b $00 + 109 f0de repend + 110 f0df 00 .byte.b $00 + 109 f0df repend + 110 f0e0 00 .byte.b $00 + 109 f0e0 repend + 110 f0e1 00 .byte.b $00 + 109 f0e1 repend + 110 f0e2 00 .byte.b $00 + 109 f0e2 repend + 110 f0e3 00 .byte.b $00 + 109 f0e3 repend + 110 f0e4 00 .byte.b $00 + 109 f0e4 repend + 110 f0e5 00 .byte.b $00 + 109 f0e5 repend + 110 f0e6 00 .byte.b $00 + 109 f0e6 repend + 110 f0e7 00 .byte.b $00 + 109 f0e7 repend + 110 f0e8 00 .byte.b $00 + 109 f0e8 repend + 110 f0e9 00 .byte.b $00 + 109 f0e9 repend + 110 f0ea 00 .byte.b $00 + 109 f0ea repend + 110 f0eb 00 .byte.b $00 + 109 f0eb repend + 110 f0ec 00 .byte.b $00 + 109 f0ec repend + 110 f0ed 00 .byte.b $00 + 109 f0ed repend + 110 f0ee 00 .byte.b $00 + 109 f0ee repend + 110 f0ef 00 .byte.b $00 + 109 f0ef repend + 110 f0f0 00 .byte.b $00 + 109 f0f0 repend + 110 f0f1 00 .byte.b $00 + 109 f0f1 repend + 110 f0f2 00 .byte.b $00 + 109 f0f2 repend + 110 f0f3 00 .byte.b $00 + 109 f0f3 repend + 110 f0f4 00 .byte.b $00 + 109 f0f4 repend + 110 f0f5 00 .byte.b $00 + 109 f0f5 repend + 110 f0f6 00 .byte.b $00 + 109 f0f6 repend + 110 f0f7 00 .byte.b $00 + 109 f0f7 repend + 110 f0f8 00 .byte.b $00 + 109 f0f8 repend + 110 f0f9 00 .byte.b $00 + 109 f0f9 repend + 110 f0fa 00 .byte.b $00 + 109 f0fa repend + 110 f0fb 00 .byte.b $00 + 109 f0fb repend + 110 f0fc 00 .byte.b $00 + 109 f0fc repend + 110 f0fd 00 .byte.b $00 + 109 f0fd repend + 110 f0fe 00 .byte.b $00 + 109 f0fe repend + 110 f0ff 00 .byte.b $00 + 111 f100 repend + 112 f100 + 113 f100 Init_1 + 114 f100 8d f8 ff sta $FFF8 + 115 f103 Bank1_Sub + 116 f103 ; lda #$0F + 117 f103 ; sta COLUPF + 118 f103 ; inc PF_Color + 119 f103 18 clc + 120 f104 ad 80 10 lda PF_Color_Read + 121 f107 69 01 adc #1 + 122 f109 8d 00 10 sta PF_Color_Write + 123 f10c 60 rts + 124 f10d + 125 ffe3 org $FFE3 + 126 ffe3 rorg $FFE3 + 127 ffe3 20 03 f1 jsr Bank1_Sub + 128 ffe6 8e f8 ff stx $FFF8 + 129 ffe9 + 130 fff8 org $FFF8 + 131 fff8 ff ff .word.w $FFFF + 132 fffa 00 f1 .word.w Init_1 + 133 fffc 00 f1 .word.w Init_1 + 134 fffe 00 f1 .word.w Init_1 + 135 10000 diff --git a/test/roms/bankswitching/_code/test.sym b/test/roms/bankswitching/_code/test.sym new file mode 100644 index 000000000..262823b9c --- /dev/null +++ b/test/roms/bankswitching/_code/test.sym @@ -0,0 +1,121 @@ +--- Symbol List (sorted by symbol) +0.FREE_BYTES 0000 +1.CLEAR_STACK f109 (R ) +__DASM__TOTAL_FREE_MEMORY 0000 +AUDC0 0015 +AUDC1 0016 +AUDF0 0017 +AUDF1 0018 +AUDV0 0019 +AUDV1 001a +Bank1_Sub f103 (R ) +Call_1 ffe0 (R ) +COLUBK 0009 +COLUP0 0006 +COLUP1 0007 +COLUPF 0008 (R ) +Con_Color 0008 +Con_Select 0002 +Con_Start 0001 +CTRLPF 000a +CXBLPF 0036 +CXCLR 002c +CXM0FB 0034 +CXM0P 0030 +CXM1FB 0035 +CXM1P 0031 +CXP0FB 0032 +CXP1FB 0033 +CXPPMM 0037 +ENABL 001f +ENAM0 001d +ENAM1 001e +GRP0 001b +GRP1 001c +HMBL 0024 +HMCLR 002b +HMM0 0022 +HMM1 0023 +HMOVE 002a +HMP0 0020 +HMP1 0021 +Init_1 f100 (R ) +INPT0 0038 +INPT1 0039 +INPT2 003a +INPT3 003b +INPT4 003c +INPT5 003d +INTIM 0284 +J0_Down 0020 +J0_Left 0040 +J0_Right 0080 +J0_Up 0010 +J1_Down 0002 +J1_Left 0004 +J1_Right 0008 +J1_up 0001 +M_Double 0010 +M_Enable 0002 +M_Oct 0040 +M_Quad 0020 +M_Single 0000 +NUSIZ0 0004 +NUSIZ1 0005 +Over f13a (R ) +P0_Diff 0080 +P1_Diff 0040 +P_Double 0005 +P_Quad 0007 +P_Reflect 0008 +P_Single 0000 +P_ThreeClose 0003 +P_ThreeMedium 0006 +P_TwoClose 0001 +P_TwoFar 0004 +P_TwoMedium 0002 +PF0 000d +PF1 000e (R ) +PF2 000f +PF_Color_Read 1080 (R ) +PF_Color_Write 1000 (R ) +PF_Priority 0004 +PF_Reflect 0001 +PF_Score 0002 +Pic f12f (R ) +REFP0 000b +REFP1 000c +RESBL 0014 +RESM0 0012 +RESM1 0013 +RESMP0 0028 +RESMP1 0029 +RESP0 0010 +RESP1 0011 +RSYNC 0003 +Start_0 f100 (R ) +StartFrame f112 (R ) +SWACNT 0281 +SWBCNT 0283 +SWCHA 0280 +SWCHB 0282 +TIM1024T 0297 +TIM1T 0294 +TIM64T 0296 +TIM8T 0295 +VB_Disable 0000 +VB_DumpPots 0080 +VB_Enable 0002 +VB_LatchDisable 0000 +VB_LatchEnable 0040 +VBLANK 0001 (R ) +VDEL01 0026 +VDELBL 0027 +VDELP0 0025 +VDELP1 0026 +VERSION_MACRO 006a +Vert f124 (R ) +VS_Enable 0002 +VSYNC 0000 (R ) +WSYNC 0002 (R ) +--- End of Symbol List. diff --git a/test/roms/bankswitching/_code/vcs.h b/test/roms/bankswitching/_code/vcs.h new file mode 100644 index 000000000..c26d887bb --- /dev/null +++ b/test/roms/bankswitching/_code/vcs.h @@ -0,0 +1,157 @@ +; +; VCS system equates +; +; Vertical blank registers +; +VSYNC = $00 +VS_Enable = 2 +; +VBLANK = $01 +VB_Enable = 2 +VB_Disable = 0 +VB_LatchEnable = 64 +VB_LatchDisable = 0 +VB_DumpPots = 128 +; I don't know a good name to un-dump the pots, +; at least that makes sense. + +WSYNC = $02 +RSYNC = $03 ;for sadists +; +; Size registers for players and missiles +; +NUSIZ0 = $04 +NUSIZ1 = $05 +P_Single = 0 +P_TwoClose = 1 +P_TwoMedium = 2 +P_ThreeClose = 3 +P_TwoFar = 4 +P_Double = 5 +P_ThreeMedium = 6 +P_Quad = 7 + +M_Single = $00 +M_Double = $10 +M_Quad = $20 +M_Oct = $40 + +; +; Color registers +; +COLUP0 = $06 +COLUP1 = $07 +COLUPF = $08 +COLUBK = $09 + +; +; Playfield Control +; +CTRLPF = $0A +PF_Reflect = $01 +PF_Score = $02 +PF_Priority = $04 +; Use missile equates to set ball width. + +REFP0 = $0B +REFP1 = $0C +P_Reflect = $08 + +PF0 = $0D +PF1 = $0E +PF2 = $0F +RESP0 = $10 +RESP1 = $11 +RESM0 = $12 +RESM1 = $13 +RESBL = $14 +AUDC0 = $15 +AUDC1 = $16 +AUDF0 = $17 +AUDF1 = $18 +AUDV0 = $19 +AUDV1 = $1A ;duh + +; +; Players +; +GRP0 = $1B +GRP1 = $1C + +; +; Single-bit objects +; +ENAM0 = $1D +ENAM1 = $1E +ENABL = $1F +M_Enable = 2 + +HMP0 = $20 +HMP1 = $21 +HMM0 = $22 +HMM1 = $23 +HMBL = $24 + +; Miscellaneous +VDELP0 = $25 +VDEL01 = $26 +VDELP1 = $26 +VDELBL = $27 +RESMP0 = $28 +RESMP1 = $29 +HMOVE = $2A +HMCLR = $2B +CXCLR = $2C +CXM0P = $30 +CXM1P = $31 +CXP0FB = $32 +CXP1FB = $33 +CXM0FB = $34 +CXM1FB = $35 +CXBLPF = $36 +CXPPMM = $37 +INPT0 = $38 +INPT1 = $39 +INPT2 = $3A +INPT3 = $3B +INPT4 = $3C +INPT5 = $3D + +; +; Switch A equates. +; +; There are more elegant ways than using all eight of these. :-) +; +SWCHA = $0280 +J0_Right = $80 +J0_Left = $40 +J0_Down = $20 +J0_Up = $10 +J1_Right = $08 +J1_Left = $04 +J1_Down = $02 +J1_up = $01 +; +; Switch B equates +; +SWCHB = $0282 +P0_Diff = $80 +P1_Diff = $40 +Con_Color = $08 +Con_Select = $02 +Con_Start = $01 + +; +; Timer +; +SWCHA = $0280 +SWACNT = $0281 +SWCHB = $0282 +SWBCNT = $0283 +INTIM = $0284 +TIM1T = $0294 +TIM8T = $0295 +TIM64T = $0296 +TIM1024T = $0297 + + diff --git a/test/roms/bankswitching/_code/x07.asm b/test/roms/bankswitching/_code/x07.asm new file mode 100644 index 000000000..0be8bdb36 --- /dev/null +++ b/test/roms/bankswitching/_code/x07.asm @@ -0,0 +1,754 @@ +;;A Bankswitching demo for the x07 scheme. 16 4K banks (64K total) +;;By: Rick Skrbina 6/11/09 + + + processor 6502 + include "vcs.h" + include "macro.h" + + seg.u vars + org $80 + + + seg bank0 + org $0000 + rorg $F000 + +Bank0 + nop + nop + nop + + CLEAN_START + + +Start_Frame + + lda #2 + sta VBLANK + sta VSYNC + sta WSYNC + sta WSYNC + sta WSYNC + lsr + sta VSYNC + + ldy #37 +VerticalBlank + sta WSYNC + dey + bne VerticalBlank + + sty VBLANK + + lda #$0F + sta COLUBK + + ldy #12 +Picture0 + sta WSYNC + dey + bne Picture0 + + jsr Swch1 + + ldy #12 +Picture1 + sta WSYNC + dey + bne Picture1 + + jsr Swch2 + + ldy #12 +Picture2 + sta WSYNC + dey + bne Picture2 + + jsr Swch3 + + ldy #12 +Picture3 + sta WSYNC + dey + bne Picture3 + + jsr Swch4 + + ldy #12 +Picture4 + sta WSYNC + dey + bne Picture4 + + jsr Swch5 + + ldy #12 +Picture5 + sta WSYNC + dey + bne Picture5 + + jsr Swch6 + + ldy #12 +Picture6 + sta WSYNC + dey + bne Picture6 + + jsr Swch7 + + ldy #12 +Picture7 + sta WSYNC + dey + bne Picture7 + + jsr Swch8 + + ldy #12 +Picture8 + sta WSYNC + dey + bne Picture8 + + jsr Swch9 + + ldy #12 +Picture9 + sta WSYNC + dey + bne Picture9 + + jsr SwchA + + ldy #12 +PictureA + sta WSYNC + dey + bne PictureA + + jsr SwchB + + ldy #12 +PictureB + sta WSYNC + dey + bne PictureB + + jsr SwchC + + ldy #12 +PictureC + sta WSYNC + dey + bne PictureC + + jsr SwchD + + ldy #12 +PictureD + sta WSYNC + dey + bne PictureD + + jsr SwchE + +; ldy #12 +;PictureE +; sta WSYNC +; dey +; bne PictureE + +; ldy #12 +;PictureF +; sta WSYNC +; dey +; bne PictureF + + lda #2 + sta VBLANK + + ldy #30 +OverScan + sta WSYNC + dey + bne OverScan + + jmp Start_Frame + + org $0500 + rorg $F500 + +Swch1 + lda $081D + nop + nop + nop + nop + nop + nop + rts + + ;$F50A + +Swch2 + lda $082D + nop + nop + nop + nop + nop + nop + rts + + ;$F514 + +Swch3 + lda $083D + nop + nop + nop + nop + nop + nop + rts + + ;$F51E + +Swch4 + lda $084D + nop + nop + nop + nop + nop + nop + rts + + ;$F528 + +Swch5 + lda $085D + nop + nop + nop + nop + nop + nop + rts + + ;$F532 + +Swch6 + lda $086D + nop + nop + nop + nop + nop + nop + rts + + ;$F53C + +Swch7 + lda $087D + nop + nop + nop + nop + nop + nop + rts + + ;$F543 + +Swch8 + lda $088D + nop + nop + nop + nop + nop + nop + rts + + ;$F550 + +Swch9 + lda $089D + nop + nop + nop + nop + nop + nop + rts + + ;$F55A + +SwchA + lda $08AD + nop + nop + nop + nop + nop + nop + rts + + ;$F564 + +SwchB + lda $08BD + nop + nop + nop + nop + nop + nop + rts + + ;$F56E + +SwchC + lda $08CD + nop + nop + nop + nop + nop + nop + rts + + ;$F578 + +SwchD + lda $08DD + nop + nop + nop + nop + nop + nop + rts + + ;$F582 + +SwchE + lda $08ED + nop + nop + nop + nop + nop + nop + rts + + + org $0FFC + rorg $FFFC + .word Bank0 + .byte "00" + + seg bank1 + org $1000 + rorg $F000 + +Bank1 + nop $080D +Sub1 + lda #$1F + sta COLUBK +; jmp Sub1 + rts + + org $1500 + rorg $F500 + nop + nop + nop + jsr Sub1 + nop $080D + + org $1FFC + rorg $FFFC + .word Bank1 + .byte "01" + + seg bank2 + org $2000 + rorg $F000 + +Bank2 + nop $080D +Sub2 + lda #$2F + sta COLUBK + rts + + org $250A + rorg $F50A + nop + nop + nop + jsr Sub2 + lda $080D + + org $2FFC + rorg $FFFC + .word Bank2 + .byte "02" + + seg bank3 + org $3000 + rorg $F000 + +Bank3 + nop $080D + +Sub3 + lda #$3F + sta COLUBK + rts + + org $3514 + rorg $F514 + nop + nop + nop + jsr Sub3 + lda $080D + + org $3FFC + rorg $FFFC + .word Bank3 + .byte "03" + + seg bank4 + org $4000 + rorg $F000 + +Bank4 + nop $080D + +Sub4 + lda #$4F + sta COLUBK + rts + + org $451E + rorg $F51E + nop + nop + nop + jsr Sub4 + lda $080D + + org $4FFC + rorg $FFFC + .word Bank4 + .byte "04" + + seg bank5 + org $5000 + rorg $F000 + +Bank5 + nop $080D + +Sub5 + lda #$5F + sta COLUBK + rts + + org $5528 + rorg $F528 + nop + nop + nop + jsr Sub5 + lda $080D + + org $5FFC + rorg $FFFC + .word Bank5 + .byte "05" + + seg bank6 + org $6000 + rorg $F000 + +Bank6 + nop $080D + +Sub6 + lda #$6F + sta COLUBK + rts + + org $6532 + rorg $F532 + nop + nop + nop + jsr Sub6 + lda $080D + + org $6FFC + rorg $FFFC + .word Bank6 + .byte "06" + + seg bank7 + org $7000 + rorg $F000 + +Bank7 + nop $080D + +Sub7 + lda #$7F + sta COLUBK + rts + + org $753C + rorg $F53C + nop + nop + nop + jsr Sub7 + lda $080D + + org $7FFC + rorg $FFFC + .word Bank7 + .byte "07" + + seg bank8 + org $8000 + rorg $F000 + +Bank8 + nop $080D + +Sub8 + lda #$8F + sta COLUBK + rts + + org $8546 + rorg $F546 + nop + nop + nop + jsr Sub8 + lda $080D + + org $8FFC + rorg $FFFC + .word Bank8 + .byte "08" + + seg bank9 + org $9000 + rorg $F000 + +Bank9 + nop $080D + +Sub9 + lda #$9F + sta COLUBK + rts + + org $9550 + rorg $F550 + nop + nop + nop + jsr Sub9 + lda $080D + + org $9FFC + rorg $FFFC + .word Bank9 + .byte "09" + + seg bankA + org $A000 + rorg $F000 + +BankA + nop $080D + +SubA + lda #$AF + sta COLUBK + rts + + org $A55A + rorg $F55A + nop + nop + nop + jsr SubA + lda $080D + + org $AFFC + rorg $FFFC + .word BankA + .byte "0A" + + seg bankB + org $B000 + rorg $F000 + +BankB + nop $080D + +SubB + lda #$BF + sta COLUBK + rts + + org $B564 + rorg $F564 + nop + nop + nop + jsr SubB + lda $080D + + org $BFFC + rorg $FFFC + .word BankB + .byte "0B" + + seg bankC + org $C000 + rorg $F000 + +BankC + nop $080D + +SubC + lda #$CF + sta COLUBK + rts + + org $C56E + rorg $F56E + nop + nop + nop + jsr SubC + lda $080D + + org $CFFC + rorg $FFFC + .word BankC + .byte "0C" + + seg bankD + org $D000 + rorg $F000 + +BankD + nop $080D + +SubD + lda #$DF + sta COLUBK + rts + + org $D578 + rorg $F578 + nop + nop + nop + jsr SubD + lda $080D + + org $DFFC + rorg $FFFC + .word BankD + .byte "0D" + + seg bankE + org $E000 + rorg $F000 + +BankE + nop $080D + +SubE + + lda #$EF + sta COLUBK+$40 ;Switch to bank F + + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + + ldy #12 +PictureF + sta WSYNC + dey + bne PictureF + + rts ;go back to bank 0 + + + org $E582 + rorg $F582 + nop + nop + nop + jsr SubE + lda $080D + + + org $EFFC + rorg $FFFC + .word BankE + .byte "0E" + + seg bankF + org $F000 + rorg $F000 + +BankF + nop $080D + +SubF + nop + nop + nop + nop + + ldy #12 +PictureE + sta WSYNC+$40 ;+$40 so no undesired BS + dey + bne PictureE + + lda #$FF + sta COLUBK ;switch to bank E + + + org $FFFC + rorg $FFFC + .word BankF + .byte "0F" diff --git a/test/roms/bankswitching/_code/x07.bin b/test/roms/bankswitching/_code/x07.bin new file mode 100644 index 000000000..2bccd1618 Binary files /dev/null and b/test/roms/bankswitching/_code/x07.bin differ diff --git a/test/roms/bankswitching/_code/xaa b/test/roms/bankswitching/_code/xaa new file mode 100644 index 000000000..8a1e161c3 Binary files /dev/null and b/test/roms/bankswitching/_code/xaa differ diff --git a/test/roms/bankswitching/_code/xab b/test/roms/bankswitching/_code/xab new file mode 100644 index 000000000..c759b2af0 Binary files /dev/null and b/test/roms/bankswitching/_code/xab differ diff --git a/test/roms/controller/genesis/Asteroids (Genesis).bin b/test/roms/controller/genesis/Asteroids (Genesis).bin new file mode 100644 index 000000000..733b10a7f Binary files /dev/null and b/test/roms/controller/genesis/Asteroids (Genesis).bin differ diff --git a/test/roms/controller/genesis/Defender (Genesis).bin b/test/roms/controller/genesis/Defender (Genesis).bin new file mode 100644 index 000000000..aeede0986 Binary files /dev/null and b/test/roms/controller/genesis/Defender (Genesis).bin differ diff --git a/test/roms/controller/genesis/Defender 2 (Genesis).bin b/test/roms/controller/genesis/Defender 2 (Genesis).bin new file mode 100644 index 000000000..850eb6070 Binary files /dev/null and b/test/roms/controller/genesis/Defender 2 (Genesis).bin differ diff --git a/test/roms/controller/genesis/Defender Arcade (Genesis).bin b/test/roms/controller/genesis/Defender Arcade (Genesis).bin new file mode 100644 index 000000000..28206ce62 Binary files /dev/null and b/test/roms/controller/genesis/Defender Arcade (Genesis).bin differ diff --git a/test/roms/controller/genesis/Defender MegaDrive (PAL) (Genesis).bin b/test/roms/controller/genesis/Defender MegaDrive (PAL) (Genesis).bin new file mode 100644 index 000000000..3d801a7b7 Binary files /dev/null and b/test/roms/controller/genesis/Defender MegaDrive (PAL) (Genesis).bin differ diff --git a/test/roms/controller/genesis/Double Dragon (PAL) (Genesis).zip b/test/roms/controller/genesis/Double Dragon (PAL) (Genesis).zip new file mode 100644 index 000000000..a1a84179d Binary files /dev/null and b/test/roms/controller/genesis/Double Dragon (PAL) (Genesis).zip differ diff --git a/test/roms/controller/genesis/Enduro (Genesis).zip b/test/roms/controller/genesis/Enduro (Genesis).zip new file mode 100644 index 000000000..a9d16238a Binary files /dev/null and b/test/roms/controller/genesis/Enduro (Genesis).zip differ diff --git a/test/roms/controller/genesis/Gingerbread Man (Fred Quimby) (Genesis).zip b/test/roms/controller/genesis/Gingerbread Man (Fred Quimby) (Genesis).zip new file mode 100644 index 000000000..4246d4f50 Binary files /dev/null and b/test/roms/controller/genesis/Gingerbread Man (Fred Quimby) (Genesis).zip differ diff --git a/test/roms/controller/genesis/H.E.R.O. (Genesis).bin b/test/roms/controller/genesis/H.E.R.O. (Genesis).bin new file mode 100644 index 000000000..af39f64d1 Binary files /dev/null and b/test/roms/controller/genesis/H.E.R.O. (Genesis).bin differ diff --git a/test/roms/controller/genesis/Kangaroo (Genesis).zip b/test/roms/controller/genesis/Kangaroo (Genesis).zip new file mode 100644 index 000000000..fe2be5050 Binary files /dev/null and b/test/roms/controller/genesis/Kangaroo (Genesis).zip differ diff --git a/test/roms/controller/genesis/Kung Fu Master (Genesis).zip b/test/roms/controller/genesis/Kung Fu Master (Genesis).zip new file mode 100644 index 000000000..8b81d1ddb Binary files /dev/null and b/test/roms/controller/genesis/Kung Fu Master (Genesis).zip differ diff --git a/test/roms/controller/genesis/Mission Survive (PAL) (Genesis).zip b/test/roms/controller/genesis/Mission Survive (PAL) (Genesis).zip new file mode 100644 index 000000000..655b99880 Binary files /dev/null and b/test/roms/controller/genesis/Mission Survive (PAL) (Genesis).zip differ diff --git a/test/roms/controller/genesis/Montezuma's Revenge (PAL) (Genesis).zip b/test/roms/controller/genesis/Montezuma's Revenge (PAL) (Genesis).zip new file mode 100644 index 000000000..e06228a72 Binary files /dev/null and b/test/roms/controller/genesis/Montezuma's Revenge (PAL) (Genesis).zip differ diff --git a/test/roms/controller/genesis/Moon Patrol (Genesis).bin b/test/roms/controller/genesis/Moon Patrol (Genesis).bin new file mode 100644 index 000000000..5fa4ce30d Binary files /dev/null and b/test/roms/controller/genesis/Moon Patrol (Genesis).bin differ diff --git a/test/roms/controller/genesis/Moon Patrol Arcade (Genesis).bin b/test/roms/controller/genesis/Moon Patrol Arcade (Genesis).bin new file mode 100644 index 000000000..43400e646 Binary files /dev/null and b/test/roms/controller/genesis/Moon Patrol Arcade (Genesis).bin differ diff --git a/test/roms/controller/genesis/Mouse Trap (Genesis).bin b/test/roms/controller/genesis/Mouse Trap (Genesis).bin new file mode 100644 index 000000000..6865fc938 Binary files /dev/null and b/test/roms/controller/genesis/Mouse Trap (Genesis).bin differ diff --git a/test/roms/controller/genesis/Omega Race (Genesis).zip b/test/roms/controller/genesis/Omega Race (Genesis).zip new file mode 100644 index 000000000..40a64f1d3 Binary files /dev/null and b/test/roms/controller/genesis/Omega Race (Genesis).zip differ diff --git a/test/roms/controller/genesis/Sea Hawk (Genesis).bin b/test/roms/controller/genesis/Sea Hawk (Genesis).bin new file mode 100644 index 000000000..186d753fe Binary files /dev/null and b/test/roms/controller/genesis/Sea Hawk (Genesis).bin differ diff --git a/test/roms/controller/genesis/Solaris (Genesis).bin b/test/roms/controller/genesis/Solaris (Genesis).bin new file mode 100644 index 000000000..e6a3318e3 Binary files /dev/null and b/test/roms/controller/genesis/Solaris (Genesis).bin differ diff --git a/test/roms/controller/genesis/Spy Hunter (Genesis).bin b/test/roms/controller/genesis/Spy Hunter (Genesis).bin new file mode 100644 index 000000000..f0390dcba Binary files /dev/null and b/test/roms/controller/genesis/Spy Hunter (Genesis).bin differ diff --git a/test/roms/controller/genesis/Star Trek (Genesis).zip b/test/roms/controller/genesis/Star Trek (Genesis).zip new file mode 100644 index 000000000..5e837a445 Binary files /dev/null and b/test/roms/controller/genesis/Star Trek (Genesis).zip differ diff --git a/test/roms/controller/genesis/Star Voyager (Genesis).bin b/test/roms/controller/genesis/Star Voyager (Genesis).bin new file mode 100644 index 000000000..743b0c69c Binary files /dev/null and b/test/roms/controller/genesis/Star Voyager (Genesis).bin differ diff --git a/test/roms/controller/genesis/Starmaster (Genesis).zip b/test/roms/controller/genesis/Starmaster (Genesis).zip new file mode 100644 index 000000000..2fd11673d Binary files /dev/null and b/test/roms/controller/genesis/Starmaster (Genesis).zip differ diff --git a/test/roms/controller/genesis/Super Cobra (Genesis).zip b/test/roms/controller/genesis/Super Cobra (Genesis).zip new file mode 100644 index 000000000..d79e75d9d Binary files /dev/null and b/test/roms/controller/genesis/Super Cobra (Genesis).zip differ diff --git a/test/roms/controller/genesis/Yars' Revenge (Genesis).bin b/test/roms/controller/genesis/Yars' Revenge (Genesis).bin new file mode 100644 index 000000000..8c72f32c4 Binary files /dev/null and b/test/roms/controller/genesis/Yars' Revenge (Genesis).bin differ diff --git a/profile/128.bin b/test/roms/profile/128.bin similarity index 100% rename from profile/128.bin rename to test/roms/profile/128.bin diff --git a/profile/README.md b/test/roms/profile/README.md similarity index 100% rename from profile/README.md rename to test/roms/profile/README.md diff --git a/profile/catharsis_theory.bin b/test/roms/profile/catharsis_theory.bin similarity index 100% rename from profile/catharsis_theory.bin rename to test/roms/profile/catharsis_theory.bin
Cartridge.Name: