stella/stella/src/debugger/gui/RamWidget.cxx

410 lines
12 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-2008 by Bradford W. Mott and the Stella team
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: RamWidget.cxx,v 1.17 2008-05-04 17:16:39 stephena Exp $
//
// Based on code from ScummVM - Scumm Interpreter
// Copyright (C) 2002-2004 The ScummVM project
//============================================================================
#include <sstream>
#include "DataGridWidget.hxx"
#include "EditTextWidget.hxx"
#include "FrameBuffer.hxx"
#include "GuiObject.hxx"
#include "InputTextDialog.hxx"
#include "OSystem.hxx"
#include "RamDebug.hxx"
#include "Widget.hxx"
#include "RamWidget.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RamWidget::RamWidget(GuiObject* boss, const GUI::Font& font, int x, int y)
: Widget(boss, font, x, y, 16, 16),
CommandSender(boss),
myUndoAddress(-1),
myUndoValue(-1)
{
_type = kRamWidget;
const int fontWidth = font.getMaxCharWidth(),
fontHeight = font.getFontHeight(),
lineHeight = font.getLineHeight(),
bwidth = 44,//font.getStringWidth("Undo "),
bheight = lineHeight + 2;
int xpos, ypos, lwidth;
// Create a 16x8 grid holding byte values (16 x 8 = 128 RAM bytes) with labels
xpos = x; ypos = y + lineHeight; lwidth = 4 * fontWidth;
myRamGrid = new DataGridWidget(boss, font, xpos + lwidth, ypos,
16, 8, 2, 8, kBASE_16);
myRamGrid->setTarget(this);
addFocusWidget(myRamGrid);
// Create actions buttons to the left of the RAM grid
xpos += lwidth + myRamGrid->getWidth() + 4;
myUndoButton = new ButtonWidget(boss, font, xpos, ypos, bwidth, bheight,
"Undo", kUndoCmd);
myUndoButton->setTarget(this);
ypos += bheight + 4;
myRevertButton = new ButtonWidget(boss, font, xpos, ypos, bwidth, bheight,
"Rev", kRevertCmd);
myRevertButton->setTarget(this);
ypos += 2 * bheight + 2;
mySearchButton = new ButtonWidget(boss, font, xpos, ypos, bwidth, bheight,
"Srch", kSearchCmd);
mySearchButton->setTarget(this);
ypos += bheight + 4;
myCompareButton = new ButtonWidget(boss, font, xpos, ypos, bwidth, bheight,
"Cmp", kCmpCmd);
myCompareButton->setTarget(this);
ypos += bheight + 4;
myRestartButton = new ButtonWidget(boss, font, xpos, ypos, bwidth, bheight,
"Rset", kRestartCmd);
myRestartButton->setTarget(this);
// Labels for RAM grid
xpos = x; ypos = y + lineHeight;
for(int row = 0; row < 8; ++row)
{
new StaticTextWidget(boss, font, xpos-2, ypos + row*lineHeight + 2,
lwidth-2, fontHeight,
Debugger::to_hex_8(row*16 + kRamStart) + string(":"),
kTextAlignLeft);
}
for(int col = 0; col < 16; ++col)
{
new StaticTextWidget(boss, font, xpos + col*myRamGrid->colWidth() + lwidth + 8,
ypos - lineHeight,
fontWidth, fontHeight,
Debugger::to_hex_4(col),
kTextAlignLeft);
}
xpos = x + 10; ypos += 9 * lineHeight;
new StaticTextWidget(boss, font, xpos, ypos,
6*fontWidth, fontHeight,
"Label:", kTextAlignLeft);
xpos += 6*fontWidth + 5;
myLabel = new EditTextWidget(boss, font, xpos, ypos-2, 17*fontWidth,
lineHeight, "");
myLabel->setEditable(false);
xpos += 17*fontWidth + 20;
new StaticTextWidget(boss, font, xpos, ypos, 4*fontWidth, fontHeight,
"Dec:", kTextAlignLeft);
xpos += 4*fontWidth + 5;
myDecValue = new EditTextWidget(boss, font, xpos, ypos-2, 4*fontWidth,
lineHeight, "");
myDecValue->setEditable(false);
xpos += 4*fontWidth + 20;
new StaticTextWidget(boss, font, xpos, ypos, 4*fontWidth, fontHeight,
"Bin:", kTextAlignLeft);
xpos += 4*fontWidth + 5;
myBinValue = new EditTextWidget(boss, font, xpos, ypos-2, 9*fontWidth,
lineHeight, "");
myBinValue->setEditable(false);
// Calculate real dimensions
_w = lwidth + myRamGrid->getWidth();
_h = ypos + lineHeight - y;
// Inputbox which will pop up when searching RAM
StringList label;
label.push_back("Search: ");
myInputBox = new InputTextDialog(boss, font, label,
x + lwidth + 20, y + 2*lineHeight - 5);
myInputBox->setTarget(this);
OK, some huge changes across the board, so lets see if I get it all: After much request, added ability to access the settings menu from the ROM browser dialog. This menu now contains almost all items that can be selected in Stella, and can be accessed in-game as before. Completely removed pause functionality from the core code. It made sense back when Stella was a single-mode program: there were two modes; emulation and pause. Now that there are other event modes, the EventHandler state machine is getting too complicated. If you want to pause, you can simply enter one of the in-game menus. Related to this, when the app is minimized, Stella enters the menu dialog state. Previously, minimizing the app caused a pause, but since there was no onscreen feedback, many people assumed the app locked up. Added centering to all Dialog boxes, which is done dynamically, as they're placed on the dialog stack to be drawn to the screen. Cleaned up the API of Console/FrameBuffer/OSystem classes wrt to palettes and timing. Parts of each were being done in different classes; now it should be more consistent. Started infrastructure for user-selectable UI palettes. For now, there's no way to change it in the GUI, and it defaults to the normal palette. Eventually, there will be several choices selectable from an in-game menu. Removed '-channels' commandline argument, since that feature can be set from the ROM properties. Added '128' to the choices for fragment size in AudioDialog. Tweaked the OpenGL dynamic loading code to test both the given GL lib, and if that fails to use auto-detection. It seems in the OSX port, the first approach works for some people, and not the other (and vice-versa), git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@1255 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
2006-12-30 22:26:29 +00:00
myInputBox->setCenter(false);
// Start with these buttons disabled
myCompareButton->clearFlags(WIDGET_ENABLED);
myRestartButton->clearFlags(WIDGET_ENABLED);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RamWidget::~RamWidget()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RamWidget::handleCommand(CommandSender* sender, int cmd, int data, int id)
{
// We simply change the values in the ByteGridWidget
// It will then send the 'kDGItemDataChangedCmd' signal to change the actual
// memory location
int addr, value;
RamDebug& dbg = instance()->debugger().ramDebug();
switch(cmd)
{
case kDGItemDataChangedCmd:
addr = myRamGrid->getSelectedAddr();
value = myRamGrid->getSelectedValue();
myUndoAddress = addr;
myUndoValue = dbg.read(addr);
dbg.write(addr, value);
myDecValue->setEditString(instance()->debugger().valueToString(value, kBASE_10));
myBinValue->setEditString(instance()->debugger().valueToString(value, kBASE_2));
myRevertButton->setEnabled(true);
myUndoButton->setEnabled(true);
break;
case kDGSelectionChangedCmd:
{
addr = myRamGrid->getSelectedAddr();
value = myRamGrid->getSelectedValue();
myLabel->setEditString(
instance()->debugger().equates().getLabel(addr+kRamStart, true));
myDecValue->setEditString(instance()->debugger().valueToString(value, kBASE_10));
myBinValue->setEditString(instance()->debugger().valueToString(value, kBASE_2));
break;
}
case kRevertCmd:
for(unsigned int i = 0; i < kRamSize; i++)
dbg.write(i, myOldValueList[i]);
fillGrid(true);
break;
case kUndoCmd:
dbg.write(myUndoAddress, myUndoValue);
myUndoButton->setEnabled(false);
fillGrid(false);
break;
case kSearchCmd:
OK, some huge changes across the board, so lets see if I get it all: After much request, added ability to access the settings menu from the ROM browser dialog. This menu now contains almost all items that can be selected in Stella, and can be accessed in-game as before. Completely removed pause functionality from the core code. It made sense back when Stella was a single-mode program: there were two modes; emulation and pause. Now that there are other event modes, the EventHandler state machine is getting too complicated. If you want to pause, you can simply enter one of the in-game menus. Related to this, when the app is minimized, Stella enters the menu dialog state. Previously, minimizing the app caused a pause, but since there was no onscreen feedback, many people assumed the app locked up. Added centering to all Dialog boxes, which is done dynamically, as they're placed on the dialog stack to be drawn to the screen. Cleaned up the API of Console/FrameBuffer/OSystem classes wrt to palettes and timing. Parts of each were being done in different classes; now it should be more consistent. Started infrastructure for user-selectable UI palettes. For now, there's no way to change it in the GUI, and it defaults to the normal palette. Eventually, there will be several choices selectable from an in-game menu. Removed '-channels' commandline argument, since that feature can be set from the ROM properties. Added '128' to the choices for fragment size in AudioDialog. Tweaked the OpenGL dynamic loading code to test both the given GL lib, and if that fails to use auto-detection. It seems in the OSX port, the first approach works for some people, and not the other (and vice-versa), git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@1255 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
2006-12-30 22:26:29 +00:00
parent()->addDialog(myInputBox);
myInputBox->setEditString("");
myInputBox->setTitle("");
myInputBox->setEmitSignal(kSValEntered);
break;
case kCmpCmd:
OK, some huge changes across the board, so lets see if I get it all: After much request, added ability to access the settings menu from the ROM browser dialog. This menu now contains almost all items that can be selected in Stella, and can be accessed in-game as before. Completely removed pause functionality from the core code. It made sense back when Stella was a single-mode program: there were two modes; emulation and pause. Now that there are other event modes, the EventHandler state machine is getting too complicated. If you want to pause, you can simply enter one of the in-game menus. Related to this, when the app is minimized, Stella enters the menu dialog state. Previously, minimizing the app caused a pause, but since there was no onscreen feedback, many people assumed the app locked up. Added centering to all Dialog boxes, which is done dynamically, as they're placed on the dialog stack to be drawn to the screen. Cleaned up the API of Console/FrameBuffer/OSystem classes wrt to palettes and timing. Parts of each were being done in different classes; now it should be more consistent. Started infrastructure for user-selectable UI palettes. For now, there's no way to change it in the GUI, and it defaults to the normal palette. Eventually, there will be several choices selectable from an in-game menu. Removed '-channels' commandline argument, since that feature can be set from the ROM properties. Added '128' to the choices for fragment size in AudioDialog. Tweaked the OpenGL dynamic loading code to test both the given GL lib, and if that fails to use auto-detection. It seems in the OSX port, the first approach works for some people, and not the other (and vice-versa), git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@1255 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
2006-12-30 22:26:29 +00:00
parent()->addDialog(myInputBox);
myInputBox->setEditString("");
myInputBox->setTitle("");
myInputBox->setEmitSignal(kCValEntered);
break;
case kRestartCmd:
doRestart();
break;
case kSValEntered:
{
const string& result = doSearch(myInputBox->getResult());
if(result != "")
myInputBox->setTitle(result);
else
parent()->removeDialog();
break;
}
case kCValEntered:
{
const string& result = doCompare(myInputBox->getResult());
if(result != "")
myInputBox->setTitle(result);
else
parent()->removeDialog();
break;
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RamWidget::loadConfig()
{
//cerr << "RamWidget::loadConfig()\n";
fillGrid(true);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RamWidget::fillGrid(bool updateOld)
{
IntArray alist;
IntArray vlist;
BoolArray changed;
if(updateOld) myOldValueList.clear();
RamDebug& dbg = instance()->debugger().ramDebug();
RamState state = (RamState&) dbg.getState();
RamState oldstate = (RamState&) dbg.getOldState();
vlist = state.ram;
if(updateOld) myOldValueList = state.ram;
for(unsigned int i = 0; i < 16*8; i++)
{
alist.push_back(i);
changed.push_back(state.ram[i] != oldstate.ram[i]);
}
myRamGrid->setList(alist, vlist, changed);
if(updateOld)
{
myRevertButton->setEnabled(false);
myUndoButton->setEnabled(false);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const string RamWidget::doSearch(const string& str)
{
bool comparisonSearch = true;
if(str.length() == 0)
{
// An empty field means return all memory locations
comparisonSearch = false;
}
else if(str.find_first_of("+-", 0) != string::npos)
{
// Don't accept these characters here, only in compare
return "Invalid input +|-";
}
int searchVal = instance()->debugger().stringToValue(str);
// Clear the search array of previous items
mySearchAddr.clear();
mySearchValue.clear();
// Now, search all memory locations for this value, and add it to the
// search array
RamDebug& dbg = instance()->debugger().ramDebug();
for(int addr = 0; addr < kRamSize; ++addr)
{
int value = dbg.read(addr);
if(comparisonSearch && searchVal != value)
continue;
mySearchAddr.push_back(addr);
mySearchValue.push_back(value);
}
// If we have some hits, enable the comparison methods
if(mySearchAddr.size() > 0)
{
mySearchButton->setEnabled(false);
myCompareButton->setEnabled(true);
myRestartButton->setEnabled(true);
}
// Finally, show the search results in the list
myRamGrid->setHiliteList(mySearchAddr);
return "";
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const string RamWidget::doCompare(const string& str)
{
bool comparitiveSearch = false;
int searchVal = 0, offset = 0;
if(str.length() == 0)
return "Enter an absolute or comparitive value";
// Do some pre-processing on the string
string::size_type pos = str.find_first_of("+-", 0);
if(pos > 0 && pos != string::npos)
{
// Only accept '+' or '-' at the start of the string
return "Input must be [+|-]NUM";
}
// A comparitive search searches memory for locations that have changed by
// the specified amount, vs. for exact values
if(str[0] == '+' || str[0] == '-')
{
comparitiveSearch = true;
bool negative = false;
if(str[0] == '-')
negative = true;
string tmp = str;
tmp.erase(0, 1); // remove the operator
offset = instance()->debugger().stringToValue(tmp);
if(negative)
offset = -offset;
}
else
searchVal = instance()->debugger().stringToValue(str);
// Now, search all memory locations specified in mySearchArray for this value
RamDebug& dbg = instance()->debugger().ramDebug();
IntArray tempAddrList, tempValueList;
for(unsigned int i = 0; i < mySearchAddr.size(); ++i)
{
if(comparitiveSearch)
{
searchVal = mySearchValue[i] + offset;
if(searchVal < 0 || searchVal > 255)
continue;
}
int addr = mySearchAddr[i];
if(dbg.read(addr) == searchVal)
{
tempAddrList.push_back(addr);
tempValueList.push_back(searchVal);
}
}
// Update the searchArray for the new addresses and data
mySearchAddr = tempAddrList;
mySearchValue = tempValueList;
// If we have some hits, enable the comparison methods
if(mySearchAddr.size() > 0)
{
myCompareButton->setEnabled(true);
myRestartButton->setEnabled(true);
}
// Finally, show the search results in the list
myRamGrid->setHiliteList(mySearchAddr);
return "";
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RamWidget::doRestart()
{
// Erase all search buffers, reset to start mode
mySearchAddr.clear();
mySearchValue.clear();
myRamGrid->setHiliteList(mySearchAddr);
mySearchButton->setEnabled(true);
myCompareButton->setEnabled(false);
myRestartButton->setEnabled(false);
}