From b97b643d175161d398f7842d013688d220421056 Mon Sep 17 00:00:00 2001 From: stephena Date: Mon, 15 Apr 2013 16:17:51 +0000 Subject: [PATCH] Added SB scheme to the debugger ROM tab. Improved keyboard and mouse navigation for PopupWidget and associated ContextMenu dialogs. Tweaked bankswitch autodetection for 29K ROMs; the only possibilities are ARM (FA2) or DPC+. git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@2692 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba --- Changes.txt | 9 +++ src/debugger/gui/CartSBWidget.cxx | 85 +++++++++++++++++++++ src/debugger/gui/CartSBWidget.hxx | 46 +++++++++++ src/debugger/gui/module.mk | 1 + src/emucore/Cart.cxx | 8 +- src/emucore/CartSB.cxx | 6 +- src/emucore/CartSB.hxx | 21 ++++- src/gui/ContextMenu.cxx | 122 +++++++++++++++++++++++++++++- src/gui/ContextMenu.hxx | 18 +++++ src/gui/PopUpWidget.cxx | 24 ++++++ src/gui/PopUpWidget.hxx | 1 + 11 files changed, 331 insertions(+), 10 deletions(-) create mode 100644 src/debugger/gui/CartSBWidget.cxx create mode 100644 src/debugger/gui/CartSBWidget.hxx diff --git a/Changes.txt b/Changes.txt index 6553f085b..adbd56b85 100644 --- a/Changes.txt +++ b/Changes.txt @@ -15,6 +15,11 @@ 3.8.1 to 3.9: (XXXXX xxx, 2013) * Greatly extended functionality of the debugger disassembly: + - There is now a new tab which lists information specific to the + cartridge bankswitching scheme in use. This includes the ability + to modify internal state even for esoteric ROMs which don't + follow the standard layout of 4K per bank. + - The debugger now generates DASM-compatible disassembled code, which can be saved to an external file. This disassembly is based on both a static and runtime analysis, and is extremely @@ -46,6 +51,10 @@ match the one used for line selection. This makes it easier to see for those with problems seeing lighter colours. + * Improved functionality of the various pop-up dialogs and context + menus in the UI; they can now be navigated more fully by the keyboard + and mouse. + * Updated included PNG library to latest stable version. -Have fun! diff --git a/src/debugger/gui/CartSBWidget.cxx b/src/debugger/gui/CartSBWidget.cxx new file mode 100644 index 000000000..52d5ebbd6 --- /dev/null +++ b/src/debugger/gui/CartSBWidget.cxx @@ -0,0 +1,85 @@ +//============================================================================ +// +// 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-2013 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. +// +// $Id$ +//============================================================================ + +#include "CartSB.hxx" +#include "PopUpWidget.hxx" +#include "CartSBWidget.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeSBWidget::CartridgeSBWidget( + GuiObject* boss, const GUI::Font& font, + int x, int y, int w, int h, CartridgeSB& cart) + : CartDebugWidget(boss, font, x, y, w, h), + myCart(cart) +{ + uInt32 size = myCart.mySize; + + StringMap items; + ostringstream info, bank; + info << "SB SUPERbanking, 32 or 64 4K banks\n" + << "Hotspots are from $800 to $" + << HEX2 << (0x800 + myCart.bankCount() - 1) << ", including\n" + << "mirrors ($900, $A00, $B00, ...)\n" + << "Startup bank = " << dec << cart.myStartBank << "\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 " << dec << i << " @ $" << HEX4 << start << " - " + << "$" << (start + 0xFFF) << " (hotspot = $" << spot << ")\n"; + + bank << dec << setw(2) << setfill(' ') << i << " ($" << HEX2 << spot << ")"; + items.push_back(bank.str(), BSPF_toString(i)); + bank.str(""); + } + + int xpos = 10, + 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: ", + font.getStringWidth("Set bank: "), kBankChanged); + myBank->setTarget(this); + addFocusWidget(myBank); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeSBWidget::loadConfig() +{ + myBank->setSelected(myCart.myCurrentBank); + + 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(); + } +} diff --git a/src/debugger/gui/CartSBWidget.hxx b/src/debugger/gui/CartSBWidget.hxx new file mode 100644 index 000000000..6f75afd3f --- /dev/null +++ b/src/debugger/gui/CartSBWidget.hxx @@ -0,0 +1,46 @@ +//============================================================================ +// +// 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-2013 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. +// +// $Id$ +//============================================================================ + +#ifndef CARTRIDGESB_WIDGET_HXX +#define CARTRIDGESB_WIDGET_HXX + +class CartridgeSB; +class PopUpWidget; + +#include "CartDebugWidget.hxx" + +class CartridgeSBWidget : public CartDebugWidget +{ + public: + CartridgeSBWidget(GuiObject* boss, const GUI::Font& font, + int x, int y, int w, int h, + CartridgeSB& cart); + virtual ~CartridgeSBWidget() { } + + void loadConfig(); + void handleCommand(CommandSender* sender, int cmd, int data, int id); + + private: + CartridgeSB& myCart; + PopUpWidget* myBank; + + enum { kBankChanged = 'bkCH' }; +}; + +#endif diff --git a/src/debugger/gui/module.mk b/src/debugger/gui/module.mk index f6f61bcbe..ee5805907 100644 --- a/src/debugger/gui/module.mk +++ b/src/debugger/gui/module.mk @@ -37,6 +37,7 @@ MODULE_OBJS := \ src/debugger/gui/CartF4SCWidget.o \ src/debugger/gui/CartF6SCWidget.o \ src/debugger/gui/CartF8SCWidget.o \ + src/debugger/gui/CartSBWidget.o \ src/debugger/gui/CartUAWidget.o \ src/debugger/gui/CartX07Widget.o \ src/debugger/gui/JoystickWidget.o \ diff --git a/src/emucore/Cart.cxx b/src/emucore/Cart.cxx index 857328731..6dcf39d32 100644 --- a/src/emucore/Cart.cxx +++ b/src/emucore/Cart.cxx @@ -430,12 +430,10 @@ string Cartridge::autodetectType(const uInt8* image, uInt32 size) } else if(size == 29*1024) // 29K { - if(isProbablyDPCplus(image, size)) - type = "DPC+"; - else if(isProbablyARM(image, size)) + if(isProbablyARM(image, size)) type = "FA2"; - else - type = "4K"; // probably a bad ROM + else /*if(isProbablyDPCplus(image, size))*/ + type = "DPC+"; } else if(size == 32*1024) // 32K { diff --git a/src/emucore/CartSB.cxx b/src/emucore/CartSB.cxx index 8e131c8c8..5400fcb0a 100644 --- a/src/emucore/CartSB.cxx +++ b/src/emucore/CartSB.cxx @@ -37,7 +37,7 @@ CartridgeSB::CartridgeSB(const uInt8* image, uInt32 size, createCodeAccessBase(mySize); // Remember startup bank - myStartBank = (mySize >> 12) - 1; + myStartBank = bankCount() - 1; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -87,7 +87,7 @@ void CartridgeSB::install(System& system) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uInt8 CartridgeSB::peek(uInt16 address) { - address = address & (0x17FF + (mySize >> 12)); + address &= (0x17FF + (mySize >> 12)); // Switch banks if necessary if ((address & 0x1800) == 0x0800) @@ -107,7 +107,7 @@ uInt8 CartridgeSB::peek(uInt16 address) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool CartridgeSB::poke(uInt16 address, uInt8 value) { - address = address & (0x17FF + (mySize >> 12)); + address &= (0x17FF + (mySize >> 12)); // Switch banks if necessary if((address & 0x1800) == 0x0800) diff --git a/src/emucore/CartSB.hxx b/src/emucore/CartSB.hxx index a5d180a9e..b416f8977 100644 --- a/src/emucore/CartSB.hxx +++ b/src/emucore/CartSB.hxx @@ -23,15 +23,22 @@ #include "bspf.hxx" #include "Cart.hxx" #include "System.hxx" +#ifdef DEBUGGER_SUPPORT + #include "CartSBWidget.hxx" +#endif /** Cartridge class used for SB "SUPERbanking" 128k-256k bankswitched games. - There are either 32 or 64 4K banks. + There are either 32 or 64 4K banks, accessible at hotspots $800 - $81F + (32 banks) and $800 - $83F (64 banks). All mirrors up to $FFF are + also used ($900, $A00, ...). @author Fred X. Quimby */ class CartridgeSB : public Cartridge { + friend class CartridgeSBWidget; + public: /** Create a new cartridge using the specified image @@ -118,6 +125,18 @@ class CartridgeSB : public Cartridge */ string name() const { return "CartridgeSB"; } + #ifdef DEBUGGER_SUPPORT + /** + Get debugger widget responsible for accessing the inner workings + of the cart. + */ + CartDebugWidget* debugWidget(GuiObject* boss, + const GUI::Font& font, int x, int y, int w, int h) + { + return new CartridgeSBWidget(boss, font, x, y, w, h, *this); + } + #endif + public: /** Get the byte at the specified address. diff --git a/src/gui/ContextMenu.cxx b/src/gui/ContextMenu.cxx index 33cb412bc..ff3c311b4 100644 --- a/src/gui/ContextMenu.cxx +++ b/src/gui/ContextMenu.cxx @@ -88,6 +88,7 @@ void ContextMenu::show(uInt32 x, uInt32 y, int item) recalc(instance().frameBuffer().imageRect()); parent().addDialog(this); setSelected(item); + moveToSelected(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -194,6 +195,50 @@ const string& ContextMenu::getSelectedTag() const return (_selectedItem >= 0) ? _entries[_selectedItem].second : EmptyString; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool ContextMenu::sendSelectionUp() +{ + if(isVisible() || _selectedItem <= 0) + return false; + + _selectedItem--; + sendCommand(_cmd ? _cmd : kCMenuItemSelectedCmd, _selectedItem, -1); + return true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool ContextMenu::sendSelectionDown() +{ + if(isVisible() || _selectedItem >= (int)_entries.size() - 1) + return false; + + _selectedItem++; + sendCommand(_cmd ? _cmd : kCMenuItemSelectedCmd, _selectedItem, -1); + return true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool ContextMenu::sendSelectionFirst() +{ + if(isVisible()) + return false; + + _selectedItem = 0; + sendCommand(_cmd ? _cmd : kCMenuItemSelectedCmd, _selectedItem, -1); + return true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool ContextMenu::sendSelectionLast() +{ + if(isVisible()) + return false; + + _selectedItem = _entries.size() - 1; + sendCommand(_cmd ? _cmd : kCMenuItemSelectedCmd, _selectedItem, -1); + return true; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void ContextMenu::handleMouseDown(int x, int y, int button, int clickCount) { @@ -287,6 +332,18 @@ void ContextMenu::handleEvent(Event::Type e) case Event::UIRight: moveDown(); break; + case Event::UIPgUp: + movePgUp(); + break; + case Event::UIPgDown: + movePgDown(); + break; + case Event::UIHome: + moveToFirst(); + break; + case Event::UIEnd: + moveToLast(); + break; case Event::UICancel: close(); break; @@ -382,6 +439,69 @@ void ContextMenu::moveDown() } } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void ContextMenu::movePgUp() +{ + if(_firstEntry == 0) + moveToFirst(); + else + scrollUp(_numEntries); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void ContextMenu::movePgDown() +{ + if(_firstEntry == (int)(_entries.size() - _numEntries)) + moveToLast(); + else + scrollDown(_numEntries); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void ContextMenu::moveToFirst() +{ + _firstEntry = 0; + _scrollUpColor = kColor; + _scrollDnColor = kScrollColor; + + drawCurrentSelection(_firstEntry + (_showScroll ? 1 : 0)); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void ContextMenu::moveToLast() +{ + _firstEntry = _entries.size() - _numEntries; + _scrollUpColor = kScrollColor; + _scrollDnColor = kColor; + + drawCurrentSelection(_numEntries - (_showScroll ? 0 : 1)); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void ContextMenu::moveToSelected() +{ + if(_selectedItem < 0 || _selectedItem >= (int)_entries.size()) + return; + + // First jump immediately to the item + _firstEntry = _selectedItem; + int offset = 0; + + // Now check if we've gone past the current 'window' size, and scale + // back accordingly + int max_offset = _entries.size() - _numEntries; + if(_firstEntry > max_offset) + { + offset = _firstEntry - max_offset; + _firstEntry -= offset; + } + + _scrollUpColor = _firstEntry > 0 ? kScrollColor : kColor; + _scrollDnColor = _firstEntry < max_offset ? kScrollColor : kColor; + + drawCurrentSelection(offset + (_showScroll ? 1 : 0)); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void ContextMenu::scrollUp(int distance) { @@ -404,7 +524,7 @@ void ContextMenu::scrollDown(int distance) _firstEntry = BSPF_min(_firstEntry + distance, max_offset); _scrollUpColor = kScrollColor; - _scrollDnColor = (_firstEntry < max_offset) ? kScrollColor : kColor; + _scrollDnColor = _firstEntry < max_offset ? kScrollColor : kColor; setDirty(); } diff --git a/src/gui/ContextMenu.hxx b/src/gui/ContextMenu.hxx index 2892a91de..621a3d71a 100644 --- a/src/gui/ContextMenu.hxx +++ b/src/gui/ContextMenu.hxx @@ -37,6 +37,9 @@ enum { * * Implementation wise, when the user selects an item, then the given 'cmd' * is broadcast, with data being equal to the tag value of the selected entry. + * + * There are also several utility methods (named as sendSelectionXXX) that + * allow to cycle through the current items without actually opening the dialog. */ class ContextMenu : public Dialog, public CommandSender { @@ -71,6 +74,16 @@ class ContextMenu : public Dialog, public CommandSender /** This dialog uses its own positioning, so we override Dialog::center() */ void center(); + /** The following methods are used when we want to select *and* + send a command for the new selection. They are only to be used + when the dialog *isn't* open, and are basically a shortcut so + that a PopUpWidget has some basic functionality without forcing + to open its associated ContextMenu. */ + bool sendSelectionUp(); + bool sendSelectionDown(); + bool sendSelectionFirst(); + bool sendSelectionLast(); + protected: void handleMouseDown(int x, int y, int button, int clickCount); void handleMouseMoved(int x, int y, int button); @@ -92,6 +105,11 @@ class ContextMenu : public Dialog, public CommandSender void moveUp(); void moveDown(); + void movePgUp(); + void movePgDown(); + void moveToFirst(); + void moveToLast(); + void moveToSelected(); void scrollUp(int distance = 1); void scrollDown(int distance = 1); void sendSelection(); diff --git a/src/gui/PopUpWidget.cxx b/src/gui/PopUpWidget.cxx index 6a333e04f..c62e8b2e7 100644 --- a/src/gui/PopUpWidget.cxx +++ b/src/gui/PopUpWidget.cxx @@ -91,6 +91,18 @@ void PopUpWidget::handleMouseDown(int x, int y, int button, int clickCount) } } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PopUpWidget::handleMouseWheel(int x, int y, int direction) +{ + if(isEnabled() && !myMenu->isVisible()) + { + if(direction < 0) + myMenu->sendSelectionUp(); + else + myMenu->sendSelectionDown(); + } +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool PopUpWidget::handleEvent(Event::Type e) { @@ -102,6 +114,18 @@ bool PopUpWidget::handleEvent(Event::Type e) case Event::UISelect: handleMouseDown(0, 0, 1, 0); return true; + case Event::UIUp: + case Event::UILeft: + case Event::UIPgUp: + return myMenu->sendSelectionUp(); + case Event::UIDown: + case Event::UIRight: + case Event::UIPgDown: + return myMenu->sendSelectionDown(); + case Event::UIHome: + return myMenu->sendSelectionFirst(); + case Event::UIEnd: + return myMenu->sendSelectionLast(); default: return false; } diff --git a/src/gui/PopUpWidget.hxx b/src/gui/PopUpWidget.hxx index 7f2703f3c..ab755d97d 100644 --- a/src/gui/PopUpWidget.hxx +++ b/src/gui/PopUpWidget.hxx @@ -68,6 +68,7 @@ class PopUpWidget : public Widget, public CommandSender protected: void handleMouseDown(int x, int y, int button, int clickCount); + void handleMouseWheel(int x, int y, int direction); bool handleEvent(Event::Type e); void handleCommand(CommandSender* sender, int cmd, int data, int id); void drawWidget(bool hilite);