stella/src/debugger/gui/RomWidget.cxx

273 lines
8.4 KiB
C++
Raw Normal View History

//============================================================================
//
// 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-2010 by Bradford W. Mott 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$
//
// Based on code from ScummVM - Scumm Interpreter
// Copyright (C) 2002-2004 The ScummVM project
//============================================================================
#include <sstream>
#include "Debugger.hxx"
#include "DebuggerParser.hxx"
#include "CartDebug.hxx"
#include "CpuDebug.hxx"
#include "GuiObject.hxx"
#include "InputTextDialog.hxx"
#include "EditTextWidget.hxx"
#include "PopUpWidget.hxx"
#include "StringList.hxx"
#include "ContextMenu.hxx"
#include "RomListWidget.hxx"
#include "RomWidget.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RomWidget::RomWidget(GuiObject* boss, const GUI::Font& font, int x, int y)
: Widget(boss, font, x, y, 16, 16),
CommandSender(boss),
myListIsDirty(true),
myCurrentBank(-1)
{
_type = kRomWidget;
int xpos, ypos;
StaticTextWidget* t;
WidgetArray wid;
// Show current bank
xpos = x; ypos = y + 7;
t = new StaticTextWidget(boss, font, xpos, ypos,
font.getStringWidth("Bank (current/total):"),
font.getFontHeight(),
"Bank (current/total):", kTextAlignLeft);
xpos += t->getWidth() + 10;
myBank = new EditTextWidget(boss, font, xpos, ypos-2,
4 * font.getMaxCharWidth(),
font.getLineHeight(), "");
myBank->setEditable(false);
// Show number of banks
xpos += myBank->getWidth() + 5;
myBankCount =
new EditTextWidget(boss, font, xpos, ypos-2, 4 * font.getMaxCharWidth(),
font.getLineHeight(), "");
myBankCount->setEditable(false);
// 'Autocode' setting for Distella
xpos += myBankCount->getWidth() + 20;
StringMap items;
items.push_back("Never", "0");
items.push_back("Always", "1");
items.push_back("Automatic", "2");
myAutocode =
new PopUpWidget(boss, font, xpos, ypos-2, font.getStringWidth("Automatic"),
font.getLineHeight(), items,
"Determine code: ", font.getStringWidth("Determine code: "),
kAutocodeChanged);
myAutocode->setTarget(this);
addFocusWidget(myAutocode);
// Create rom listing
xpos = x; ypos += myBank->getHeight() + 4;
const GUI::Rect& dialog = instance().debugger().getDialogBounds();
int w = dialog.width() - x - 5, h = dialog.height() - ypos - 3;
myRomList = new RomListWidget(boss, font, xpos, ypos, w, h);
myRomList->setTarget(this);
myRomList->myMenu->setTarget(this);
addFocusWidget(myRomList);
// Calculate real dimensions
_w = myRomList->getWidth();
_h = myRomList->getHeight();
// Create dialog box for save ROM (get name)
StringList label;
label.push_back("Filename: ");
mySaveRom = new InputTextDialog(boss, font, label);
mySaveRom->setTarget(this);
// By default, we try to automatically determine code vs. data sections
myAutocode->setSelected(instance().settings().getString("autocode"), "2");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RomWidget::~RomWidget()
{
delete mySaveRom;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomWidget::loadConfig()
{
Debugger& dbg = instance().debugger();
CartDebug& cart = dbg.cartDebug();
bool bankChanged = myCurrentBank != cart.getBank();
myCurrentBank = cart.getBank();
// Fill romlist the current bank of source or disassembly
myListIsDirty |= cart.disassemble(myAutocode->getSelectedTag(), myListIsDirty);
if(myListIsDirty)
{
myRomList->setList(cart.disassemblyList(), dbg.breakpoints());
myListIsDirty = false;
}
// Update romlist to point to current PC
int pcline = cart.addressToLine(dbg.cpuDebug().pc());
if(pcline >= 0)
myRomList->setHighlighted(pcline);
// Set current bank and number of banks
myBank->setEditString(instance().debugger().valueToString(myCurrentBank, kBASE_10), bankChanged);
myBankCount->setEditString(instance().debugger().valueToString(cart.bankCount(), kBASE_10));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomWidget::handleCommand(CommandSender* sender, int cmd, int data, int id)
{
switch(cmd)
{
case kRLBreakpointChangedCmd:
// 'id' is the line in the disassemblylist to be accessed
// 'data' is the state of the breakpoint at 'id'
setBreak(id, data);
// Refresh the romlist, since the breakpoint may not have
// actually changed
myRomList->setDirty();
myRomList->draw();
break;
case kRLRomChangedCmd:
// 'data' is the line in the disassemblylist to be accessed
patchROM(data, myRomList->getEditString());
break;
case kCMenuItemSelectedCmd:
{
const string& rmb = myRomList->myMenu->getSelectedTag();
if(rmb == "saverom")
{
mySaveRom->show(_x + 50, _y + 80);
mySaveRom->setEditString("");
mySaveRom->setTitle("");
mySaveRom->setEmitSignal(kRomNameEntered);
}
else if(rmb == "setpc")
setPC(myRomList->getSelected());
else if(rmb == "runtopc")
runtoPC(myRomList->getSelected());
break;
}
case kAutocodeChanged:
instance().settings().setString("autocode", myAutocode->getSelectedTag());
invalidate();
loadConfig();
break;
case kRomNameEntered:
{
const string& rom = mySaveRom->getResult();
if(rom == "")
mySaveRom->setTitle("Invalid name");
else
{
saveROM(rom);
parent().removeDialog();
}
break;
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomWidget::setBreak(int disasm_line, bool state)
{
const CartDebug::DisassemblyList& list =
instance().debugger().cartDebug().disassemblyList();
if(disasm_line >= (int)list.size()) return;
if(list[disasm_line].address != 0 && list[disasm_line].bytes != "")
instance().debugger().setBreakPoint(list[disasm_line].address, state);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomWidget::setPC(int disasm_line)
{
const CartDebug::DisassemblyList& list =
instance().debugger().cartDebug().disassemblyList();
if(disasm_line >= (int)list.size()) return;
if(list[disasm_line].address != 0)
{
ostringstream command;
command << "pc #" << list[disasm_line].address;
instance().debugger().run(command.str());
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomWidget::runtoPC(int disasm_line)
{
const CartDebug::DisassemblyList& list =
instance().debugger().cartDebug().disassemblyList();
if(disasm_line >= (int)list.size()) return;
if(list[disasm_line].address != 0)
{
ostringstream command;
command << "runtopc #" << list[disasm_line].address;
instance().debugger().run(command.str());
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomWidget::patchROM(int disasm_line, const string& bytes)
{
const CartDebug::DisassemblyList& list =
instance().debugger().cartDebug().disassemblyList();
if(disasm_line >= (int)list.size()) return;
if(list[disasm_line].address != 0)
{
ostringstream command;
// Temporarily set to base 16, since that's the format the disassembled
// byte string is in. This eliminates the need to prefix each byte with
// a '$' character
BaseFormat oldbase = instance().debugger().parser().base();
instance().debugger().parser().setBase(kBASE_16);
command << "rom #" << list[disasm_line].address << " " << bytes;
instance().debugger().run(command.str());
// Restore previous base
instance().debugger().parser().setBase(oldbase);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomWidget::saveROM(const string& rom)
{
ostringstream command;
command << "saverom " << rom;
instance().debugger().run(command.str());
}