stella/src/debugger/gui/DebuggerDialog.cxx

444 lines
13 KiB
C++

//============================================================================
//
// 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 "Widget.hxx"
#include "Dialog.hxx"
#include "TabWidget.hxx"
#include "TiaInfoWidget.hxx"
#include "TiaOutputWidget.hxx"
#include "TiaZoomWidget.hxx"
#include "AudioWidget.hxx"
#include "PromptWidget.hxx"
#include "CpuWidget.hxx"
#include "RamWidget.hxx"
#include "RiotWidget.hxx"
#include "RomWidget.hxx"
#include "TiaWidget.hxx"
#include "CartDebugWidget.hxx"
#include "DataGridOpsWidget.hxx"
#include "EditTextWidget.hxx"
#include "MessageBox.hxx"
#include "Debugger.hxx"
#include "DebuggerParser.hxx"
#include "ConsoleFont.hxx"
#include "ConsoleBFont.hxx"
#include "ConsoleMediumFont.hxx"
#include "ConsoleMediumBFont.hxx"
#include "StellaMediumFont.hxx"
#include "DebuggerDialog.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DebuggerDialog::DebuggerDialog(OSystem* osystem, DialogContainer* parent,
int x, int y, int w, int h)
: Dialog(osystem, parent, x, y, w, h, true), // use base surface
myTab(NULL),
myRomTab(NULL),
myFont(NULL),
myFatalError(NULL)
{
createFont(); // Font is sized according to available space
addTiaArea();
addTabArea();
addStatusArea();
addRomArea();
// Inform the TIA output widget about its associated zoom widget
myTiaOutput->setZoomWidget(myTiaZoom);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DebuggerDialog::~DebuggerDialog()
{
delete myFont;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::loadConfig()
{
myTab->loadConfig();
myTiaInfo->loadConfig();
myTiaOutput->loadConfig();
myTiaZoom->loadConfig();
myCpu->loadConfig();
myRam->loadConfig();
myRomTab->loadConfig();
myMessageBox->setText("");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::handleKeyDown(StellaKey key, StellaMod mod, char ascii)
{
bool handled = instance().eventHandler().kbdAlt(mod);
if(handled)
{
switch(ascii)
{
case 's':
doStep();
break;
case 't':
doTrace();
break;
case 'f':
doAdvance();
break;
case 'l':
doScanlineAdvance();
break;
case 'r':
doRewind();
break;
default:
handled = false;
break;
}
}
if(!handled)
Dialog::handleKeyDown(key, mod, ascii);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::handleCommand(CommandSender* sender, int cmd,
int data, int id)
{
// We reload the tabs in the cases where the actions could possibly
// change their contents
switch(cmd)
{
case kDDStepCmd:
doStep();
break;
case kDDTraceCmd:
doTrace();
break;
case kDDAdvCmd:
doAdvance();
break;
case kDDSAdvCmd:
doScanlineAdvance();
break;
case kDDRewindCmd:
doRewind();
break;
case kDDExitCmd:
doExitDebugger();
break;
case kDDExitFatalCmd:
doExitRom();
break;
case RomWidget::kInvalidateListing:
myRom->invalidate();
break;
default:
Dialog::handleCommand(sender, cmd, data, id);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::doStep()
{
instance().debugger().parser().run("step");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::doTrace()
{
instance().debugger().parser().run("trace");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::doAdvance()
{
instance().debugger().parser().run("frame #1");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::doScanlineAdvance()
{
instance().debugger().parser().run("scanline #1");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::doRewind()
{
instance().debugger().parser().run("rewind");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::doExitDebugger()
{
instance().debugger().parser().run("run");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::doExitRom()
{
instance().debugger().parser().run("exitrom");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::createFont()
{
bool bold = instance().settings().getBool("dbg.boldfont");
// For now, these sizes are hardcoded based on actual font size
if(_w >= kLargeFontMinW && _h >= kLargeFontMinH)
myFont = new GUI::Font(GUI::stellaMediumDesc);
else if(_w >= kMediumFontMinW && _h >= kMediumFontMinH)
myFont = new GUI::Font(bold ? GUI::consoleMediumBDesc : GUI::consoleMediumDesc);
else
myFont = new GUI::Font(bold ? GUI::consoleBDesc : GUI::consoleDesc);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::showFatalMessage(const string& msg)
{
delete myFatalError;
myFatalError =
new GUI::MessageBox(this, *myFont, msg, _w/2, _h/2, kDDExitFatalCmd,
"Exit ROM", "Continue");
myFatalError->show();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::addTiaArea()
{
const GUI::Rect& r = getTiaBounds();
myTiaOutput = new TiaOutputWidget(this, *myFont,
r.left, r.top, r.width(), r.height());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::addTabArea()
{
const GUI::Rect& r = getTabBounds();
const int vBorder = 4;
// The tab widget
// Since there are two tab widgets in this dialog, we specifically
// assign an ID of 0
myTab = new TabWidget(this, *myFont, r.left, r.top + vBorder,
r.width(), r.height() - vBorder);
myTab->setID(0);
addTabWidget(myTab);
const int widWidth = r.width() - vBorder;
const int widHeight = r.height() - myTab->getTabHeight() - vBorder - 4;
int tabID;
// The Prompt/console tab
tabID = myTab->addTab(" Prompt ");
myPrompt = new PromptWidget(myTab, *myFont,
2, 2, widWidth, widHeight);
myTab->setParentWidget(tabID, myPrompt);
addToFocusList(myPrompt->getFocusList(), myTab, tabID);
// The TIA tab
tabID = myTab->addTab("TIA");
TiaWidget* tia = new TiaWidget(myTab, *myFont,
2, 2, widWidth, widHeight);
myTab->setParentWidget(tabID, tia);
addToFocusList(tia->getFocusList(), myTab, tabID);
// The input/output tab (includes RIOT and INPTx from TIA)
tabID = myTab->addTab("I/O");
RiotWidget* riot = new RiotWidget(myTab, *myFont,
2, 2, widWidth, widHeight);
myTab->setParentWidget(tabID, riot);
addToFocusList(riot->getFocusList(), myTab, tabID);
// The Audio tab
tabID = myTab->addTab("Audio");
AudioWidget* aud = new AudioWidget(myTab, *myFont,
2, 2, widWidth, widHeight);
myTab->setParentWidget(tabID, aud);
addToFocusList(aud->getFocusList(), myTab, tabID);
myTab->setActiveTab(0);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::addStatusArea()
{
const GUI::Font& font = *myFont;
const int lineHeight = font.getLineHeight();
const GUI::Rect& r = getStatusBounds();
int xpos, ypos;
xpos = r.left; ypos = r.top;
myTiaInfo = new TiaInfoWidget(this, *myFont, xpos, ypos, r.width());
ypos += myTiaInfo->getHeight() + 10;
myTiaZoom = new TiaZoomWidget(this, *myFont, xpos+10, ypos,
r.width()-10, r.height()-lineHeight-ypos-10);
addToFocusList(myTiaZoom->getFocusList());
xpos += 10; ypos += myTiaZoom->getHeight() + 10;
myMessageBox = new EditTextWidget(this, *myFont,
xpos, ypos, myTiaZoom->getWidth(),
font.getLineHeight(), "");
myMessageBox->setEditable(false);
myMessageBox->clearFlags(WIDGET_RETAIN_FOCUS);
myMessageBox->setTextColor(kTextColorEm);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::addRomArea()
{
const GUI::Rect& r = getRomBounds();
const int vBorder = 4;
int bwidth = myFont->getStringWidth("Frame +1 "),
bheight = myFont->getLineHeight() + 2;
int buttonX = r.right - bwidth - 5, buttonY = r.top + 5;
new ButtonWidget(this, *myFont, buttonX, buttonY,
bwidth, bheight, "Step", kDDStepCmd);
buttonY += bheight + 4;
new ButtonWidget(this, *myFont, buttonX, buttonY,
bwidth, bheight, "Trace", kDDTraceCmd);
buttonY += bheight + 4;
new ButtonWidget(this, *myFont, buttonX, buttonY,
bwidth, bheight, "Scan +1", kDDSAdvCmd);
buttonY += bheight + 4;
new ButtonWidget(this, *myFont, buttonX, buttonY,
bwidth, bheight, "Frame +1", kDDAdvCmd);
buttonY += bheight + 4;
new ButtonWidget(this, *myFont, buttonX, buttonY,
bwidth, bheight, "Exit", kDDExitCmd);
bwidth = myFont->getStringWidth("< ") + 4;
bheight = bheight * 5 + 4*4;
buttonX -= (bwidth + 5);
buttonY = r.top + 5;
myRewindButton =
new ButtonWidget(this, *myFont, buttonX, buttonY,
bwidth, bheight, "<", kDDRewindCmd);
myRewindButton->clearFlags(WIDGET_ENABLED);
int xpos = buttonX - 8*myFont->getMaxCharWidth() - 20, ypos = 20;
DataGridOpsWidget* ops = new DataGridOpsWidget(this, *myFont, xpos, ypos);
int max_w = xpos - r.left - 10;
xpos = r.left + 10; ypos = 10;
myCpu = new CpuWidget(this, *myFont, xpos, ypos, max_w);
addToFocusList(myCpu->getFocusList());
xpos = r.left + 10; ypos += myCpu->getHeight() + 10;
myRam = new RamWidget(this, *myFont, xpos, ypos);
addToFocusList(myRam->getFocusList());
// Add the DataGridOpsWidget to any widgets which contain a
// DataGridWidget which we want controlled
myCpu->setOpsWidget(ops);
myRam->setOpsWidget(ops);
////////////////////////////////////////////////////////////////////
// Disassembly area
xpos = r.left + vBorder; ypos += myRam->getHeight() + 5;
const int tabWidth = r.width() - vBorder - 1;
const int tabHeight = r.height() - ypos - 1;
int tabID;
// Since there are two tab widgets in this dialog, we specifically
// assign an ID of 1
myRomTab = new TabWidget(
this, *myFont, xpos, ypos, tabWidth, tabHeight);
myRomTab->setID(1);
addTabWidget(myRomTab);
// The main disassembly tab
tabID = myRomTab->addTab(" Disassembly ");
myRom = new RomWidget(myRomTab, *myFont,
2, 2, tabWidth - 1,
tabHeight - myRomTab->getTabHeight() - 2);
myRomTab->setParentWidget(tabID, myRom);
addToFocusList(myRom->getFocusList(), myRomTab, tabID);
// The 'cart-specific' information tab
tabID = myRomTab->addTab(instance().console().cartridge().name());
myCartDebug = instance().console().cartridge().debugWidget(
myRomTab, *myFont, 2, 2, tabWidth - 1,
tabHeight - myRomTab->getTabHeight() - 2);
if(myCartDebug) // TODO - make this always non-null
{
myRomTab->setParentWidget(tabID, myCartDebug);
addToFocusList(myCartDebug->getFocusList(), myRomTab, tabID);
}
myRomTab->setActiveTab(0);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
GUI::Rect DebuggerDialog::getTiaBounds() const
{
// The area showing the TIA image (NTSC and PAL supported, up to 260 lines)
GUI::Rect r(0, 0, 320, BSPF_max(260, (int)(_h * 0.35)));
return r;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
GUI::Rect DebuggerDialog::getRomBounds() const
{
// The ROM area is the full area to the right of the tabs
const GUI::Rect& status = getStatusBounds();
GUI::Rect r(status.right + 1, 0, _w, _h);
return r;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
GUI::Rect DebuggerDialog::getStatusBounds() const
{
// The status area is the full area to the right of the TIA image
// extending as far as necessary
// 30% of any space above 1030 pixels will be allocated to this area
const GUI::Rect& tia = getTiaBounds();
int x1 = tia.right + 1;
int y1 = 0;
int x2 = tia.right + 225 + (_w > 1030 ? (int) (0.35 * (_w - 1030)) : 0);
int y2 = tia.bottom;
GUI::Rect r(x1, y1, x2, y2);
return r;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
GUI::Rect DebuggerDialog::getTabBounds() const
{
// The tab area is the full area below the TIA image
const GUI::Rect& tia = getTiaBounds();
const GUI::Rect& status = getStatusBounds();
GUI::Rect r(0, tia.bottom + 1, status.right + 1, _h);
return r;
}