mirror of https://github.com/stella-emu/stella.git
310 lines
9.5 KiB
C++
310 lines
9.5 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-2011 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$
|
|
//
|
|
// 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 "DiStella.hxx"
|
|
#include "CpuDebug.hxx"
|
|
#include "GuiObject.hxx"
|
|
#include "InputTextDialog.hxx"
|
|
#include "DataGridWidget.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;
|
|
ostringstream buf;
|
|
buf << "Current bank (" << dec
|
|
<< instance().debugger().cartDebug().bankCount() << " total):";
|
|
t = new StaticTextWidget(boss, font, xpos, ypos,
|
|
font.getStringWidth(buf.str()),
|
|
font.getFontHeight(),
|
|
buf.str(), kTextAlignLeft);
|
|
|
|
xpos += t->getWidth() + 5;
|
|
myBank = new DataGridWidget(boss, font, xpos, ypos-2,
|
|
1, 1, 4, 8, kBASE_10);
|
|
myBank->setTarget(this);
|
|
myBank->setRange(0, instance().debugger().cartDebug().bankCount());
|
|
if(instance().debugger().cartDebug().bankCount() <= 1)
|
|
myBank->setEditable(false);
|
|
addFocusWidget(myBank);
|
|
|
|
// 'resolvedata' setting for Distella
|
|
xpos += myBank->getWidth() + 20;
|
|
StringMap items;
|
|
items.push_back("Never", "never");
|
|
items.push_back("Always", "always");
|
|
items.push_back("Automatic", "auto");
|
|
myResolveData =
|
|
new PopUpWidget(boss, font, xpos, ypos-2, font.getStringWidth("Automatic"),
|
|
font.getLineHeight(), items,
|
|
"Resolve data: ", font.getStringWidth("Resolve data: "),
|
|
kResolveDataChanged);
|
|
myResolveData->setTarget(this);
|
|
addFocusWidget(myResolveData);
|
|
|
|
// 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
|
|
myResolveData->setSelected(instance().settings().getString("resolvedata"), "auto");
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
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(myResolveData->getSelectedTag(), myListIsDirty);
|
|
if(myListIsDirty)
|
|
{
|
|
myRomList->setList(cart.disassembly(), dbg.breakpoints());
|
|
myListIsDirty = false;
|
|
}
|
|
|
|
// Update romlist to point to current PC (if it has changed)
|
|
int pcline = cart.addressToLine(dbg.cpuDebug().pc());
|
|
if(pcline >= 0 && pcline != myRomList->getHighlighted())
|
|
myRomList->setHighlighted(pcline);
|
|
|
|
// Set current bank and number of banks
|
|
myBank->setList(-1, myCurrentBank, bankChanged);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
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());
|
|
else if(rmb == "disasm")
|
|
invalidate();
|
|
else if(rmb == "pcaddr")
|
|
{
|
|
DiStella::settings.show_addresses = !DiStella::settings.show_addresses;
|
|
instance().settings().setString("showaddr",
|
|
DiStella::settings.show_addresses ? "true" : "false");
|
|
invalidate();
|
|
}
|
|
else if(rmb == "gfx")
|
|
{
|
|
if(DiStella::settings.gfx_format == kBASE_16)
|
|
{
|
|
DiStella::settings.gfx_format = kBASE_2;
|
|
instance().settings().setString("gfxformat", "2");
|
|
}
|
|
else
|
|
{
|
|
DiStella::settings.gfx_format = kBASE_16;
|
|
instance().settings().setString("gfxformat", "16");
|
|
}
|
|
invalidate();
|
|
}
|
|
break; // kCMenuItemSelectedCmd
|
|
}
|
|
|
|
case kDGItemDataChangedCmd:
|
|
setBank(myBank->getSelectedValue());
|
|
break;
|
|
|
|
case kResolveDataChanged:
|
|
instance().settings().setString("resolvedata", myResolveData->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::setBank(uInt16 bank)
|
|
{
|
|
ostringstream command;
|
|
command << "bank #" << bank;
|
|
instance().debugger().run(command.str());
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void RomWidget::setBreak(int disasm_line, bool state)
|
|
{
|
|
const CartDebug::DisassemblyList& list =
|
|
instance().debugger().cartDebug().disassembly().list;
|
|
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().disassembly().list;
|
|
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().disassembly().list;
|
|
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().disassembly().list;
|
|
if(disasm_line >= (int)list.size()) return;
|
|
|
|
if(list[disasm_line].address != 0)
|
|
{
|
|
ostringstream command;
|
|
|
|
// Temporarily set to correct base, so we don't have to prefix each byte
|
|
// with the type of data
|
|
BaseFormat oldbase = instance().debugger().parser().base();
|
|
if(list[disasm_line].type == CartDebug::GFX)
|
|
instance().debugger().parser().setBase(DiStella::settings.gfx_format);
|
|
else
|
|
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());
|
|
}
|