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
This commit is contained in:
stephena 2013-04-15 16:17:51 +00:00
parent 2872b6be03
commit b97b643d17
11 changed files with 331 additions and 10 deletions

View File

@ -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!

View File

@ -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();
}
}

View File

@ -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

View File

@ -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 \

View File

@ -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
{

View File

@ -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)

View File

@ -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.

View File

@ -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();
}

View File

@ -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();

View File

@ -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;
}

View File

@ -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);