OK, I've finally gotten back to Stella development and fixing the

disassembler.  Hopefully this will lead to a new release very soon.

Added 'autocode' commandline argument and associated UI item (in the
RomWidget debugger area), which controls how Distella will use the
'automatic code determination' option.  If set to 0/never, this is
completely disabled.  If set to 1/always, it is always enabled.
The default is 2/automatic, whereby it is first turned on, and then
turned off if the disassembly doesn't contain the current PC address.

RomListWidget has now been completely reworked, so that it informs
RomWidget of its intent to change breakpoints and patch ROM.  If
either of these fail, the action won't be performed, and more
importantly, it won't appear onscreen as if the action has succeeded.
This fixes an old bug whereby patching could fail, yet the onscreen
ROM data was actually changed.  Related to this, the list has been
made as efficient as possible, and its contents are never un-necessarily
copied.  Also, lines in the disassembly that cannot be modified no
longer show an edit area.

Due to the way the new disassembly works, you can no longer switch between
banks in the RomWidget (Distella would probably fail to disassemble in
such as case).

EditTextWidget can now indicate its contents have changed when adding
text to it.


git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@1966 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
stephena 2010-03-18 16:36:12 +00:00
parent 2cc0d60940
commit 0ed4a9fa79
20 changed files with 681 additions and 237 deletions

View File

@ -1025,6 +1025,12 @@
<th>Description</th>
</tr>
<tr>
<td><pre>-autocode &lt;0|1|2&gt;</pre></td>
<td>Try to differentiate between code vs. data sections in the
disassembler. See the Debugger section for more information.</td>
</tr>
<tr>
<td><pre>-debuggerres &lt;WxH&gt;</pre></td>
<td>Set the size of the debugger window.</td>

View File

@ -184,61 +184,88 @@ string CartDebug::toString()
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartDebug::disassemble(bool autocode)
bool CartDebug::disassemble(const string& autocode, bool force)
{
// Test current disassembly; don't re-disassemble if it hasn't changed
// ...
bool changed = myConsole.cartridge().bankChanged();
// Also check if the current PC is in the current list
uInt16 PC = myDebugger.cpuDebug().pc();
bool changed = force || myConsole.cartridge().bankChanged() ||
(addressToLine(PC) == -1);
if(changed)
{
myDisassembly.clear();
myAddrToLineList.clear();
// We only use the reset vector when we're actually in the startup bank
// Otherwise, we look at previous accesses to this bank to begin
// If no previous address exists, use the current program counter
uInt16 start = myStartAddresses[getBank()];
if(start == 0)
start = myStartAddresses[getBank()] = PC;
// For now, DiStella can't handle address space below 0x1000
if(!(start & 0x1000))
return false;
#if 0
cerr << "start addresses: ";
for(int i = 0; i < myConsole.cartridge().bankCount(); ++i) cerr << " " << setw(4) << hex << myStartAddresses[i];
cerr << endl;
cerr << "current bank = " << getBank() << ", start bank = " << myConsole.cartridge().startBank() << endl
<< "reset = " << hex << 0xfffc << ", pc = " << hex << myDebugger.cpuDebug().pc() << endl
<< "start = " << hex << start << endl;
if(start == 0)
start = myStartAddresses[getBank()] = myDebugger.cpuDebug().pc();
cerr << "actual start = " << hex << start << endl << endl;
<< "reset = " << hex << 0xfffc << ", pc = " << hex << PC << endl
<< "start = " << hex << start << endl << endl;
#endif
// For now, DiStella can't handle address space below 0x1000
if(!(start & 0x1000))
return true;
autocode = false;
DiStella distella(myDisassembly, start, autocode);
// Parts of the disassembly will be accessed later in different ways
// We place those parts in separate maps, to speed up access
for(uInt32 i = 0; i < myDisassembly.size(); ++i)
// Check whether to use the 'autocode' functionality from Distella
if(autocode == "0") // 'never'
fillDisassemblyList(start, false, PC);
else if(autocode == "1") // always
fillDisassemblyList(start, true, PC);
else // automatic
{
const DisassemblyTag& tag = myDisassembly[i];
// Create a mapping from addresses to line numbers
if(tag.address != 0)
myAddrToLineList.insert(make_pair(tag.address, i));
// TODO - look at list, extract address to label mappings
// we need these for label support in the UI and promptwidget
// First try with autocode on, then turn off if PC isn't found
if(!fillDisassemblyList(start, true, PC))
fillDisassemblyList(start, false, PC);
}
}
return changed;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartDebug::fillDisassemblyList(uInt16 start, bool autocode, uInt16 search)
{
bool found = false;
myDisassembly.clear();
DiStella distella(myDisassembly, start, autocode);
// Parts of the disassembly will be accessed later in different ways
// We place those parts in separate maps, to speed up access
myAddrToLineList.clear();
for(uInt32 i = 0; i < myDisassembly.size(); ++i)
{
const DisassemblyTag& tag = myDisassembly[i];
// Only non-zero addresses are valid
if(tag.address != 0)
{
// Create a mapping from addresses to line numbers
myAddrToLineList.insert(make_pair(tag.address, i));
// Did we find the search value?
if(tag.address == search)
found = true;
}
// TODO - look at list, extract address to label mappings
// we need these for label support in the UI and promptwidget
}
return found;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int CartDebug::addressToLine(uInt16 address) const
{
map<uInt16, int>::const_iterator iter = myAddrToLineList.find(address);
return iter != myAddrToLineList.end() ? iter->second : 0;
return iter != myAddrToLineList.end() ? iter->second : -1;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -100,9 +100,12 @@ class CartDebug : public DebuggerSystem
Disassemble from the given address using the Distella disassembler
Address-to-label mappings (and vice-versa) are also determined here
@param autocode Whether to determine code vs data sections
@param force Force a re-disassembly, even if the state hasn't changed
@return True if disassembly changed from previous call, else false
*/
bool disassemble(bool autocode);
bool disassemble(const string& autocode, bool force = false);
/**
Get the results from the most recent call to disassemble()
@ -178,6 +181,10 @@ class CartDebug : public DebuggerSystem
};
AddrType addressType(uInt16 addr) const;
// Actually call DiStella to fill the DisassemblyList structure
// Return whether the search address was actually in the list
bool fillDisassemblyList(uInt16 start, bool autocode, uInt16 search);
// Extract labels and values from the given character stream
string extractLabel(char *c) const;
int extractValue(char *c) const;

View File

@ -24,7 +24,7 @@
DiStella::DiStella(CartDebug::DisassemblyList& list, uInt16 start,
bool autocode)
: myList(list),
labels(NULL) /* array of information about addresses-- can be from 2K-48K bytes in size */
labels(NULL) /* array of information about addresses */
{
while(!myAddressQueue.empty())
myAddressQueue.pop();

View File

@ -49,8 +49,7 @@ class DiStella
@param start The address at which to start disassembly
@param autocode If enabled, try to determine code vs. data sections
*/
DiStella(CartDebug::DisassemblyList& list, uInt16 start,
bool autocode = true);
DiStella(CartDebug::DisassemblyList& list, uInt16 start, bool autocode = true);
~DiStella();

View File

@ -19,50 +19,64 @@
#include "bspf.hxx"
#include "PackedBitArray.hxx"
PackedBitArray::PackedBitArray(int length) {
size = length;
words = length / wordSize + 1;
bits = new unsigned int[ words ];
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PackedBitArray::PackedBitArray(uInt32 length)
: size(length),
words(length / wordSize + 1)
{
bits = new uInt32[ words ];
for(int i=0; i<words; i++)
bits[i] = 0;
for(uInt32 i = 0; i < words; ++i)
bits[i] = 0;
}
PackedBitArray::~PackedBitArray() {
delete[] bits;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PackedBitArray::~PackedBitArray()
{
delete[] bits;
}
int PackedBitArray::isSet(unsigned int bit) {
unsigned int word = bit / wordSize;
bit %= wordSize;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 PackedBitArray::isSet(uInt32 bit) const
{
uInt32 word = bit / wordSize;
bit %= wordSize;
return (bits[word] & (1 << bit));
return (bits[word] & (1 << bit));
}
int PackedBitArray::isClear(unsigned int bit) {
unsigned int word = bit / wordSize;
bit %= wordSize;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 PackedBitArray::isClear(uInt32 bit) const
{
uInt32 word = bit / wordSize;
bit %= wordSize;
return !(bits[word] & (1 << bit));
return !(bits[word] & (1 << bit));
}
void PackedBitArray::toggle(unsigned int bit) {
unsigned int word = bit / wordSize;
bit %= wordSize;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PackedBitArray::toggle(uInt32 bit)
{
uInt32 word = bit / wordSize;
bit %= wordSize;
bits[word] ^= (1 << bit);
bits[word] ^= (1 << bit);
}
void PackedBitArray::set(unsigned int bit) {
unsigned int word = bit / wordSize;
bit %= wordSize;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PackedBitArray::set(uInt32 bit)
{
uInt32 word = bit / wordSize;
bit %= wordSize;
bits[word] |= (1 << bit);
bits[word] |= (1 << bit);
}
void PackedBitArray::clear(unsigned int bit) {
unsigned int word = bit / wordSize;
bit %= wordSize;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PackedBitArray::clear(uInt32 bit)
{
uInt32 word = bit / wordSize;
bit %= wordSize;
bits[word] &= (~(1 << bit));
bits[word] &= (~(1 << bit));
}

View File

@ -23,27 +23,28 @@
#define wordSize ( (sizeof(unsigned int)) * 8)
class PackedBitArray {
public:
PackedBitArray(int length);
~PackedBitArray();
class PackedBitArray
{
public:
PackedBitArray(uInt32 length);
~PackedBitArray();
int isSet(unsigned int bit);
int isClear(unsigned int bit);
uInt32 isSet(uInt32 bit) const;
uInt32 isClear(uInt32 bit) const;
void set(unsigned int bit);
void clear(unsigned int bit);
void toggle(unsigned int bit);
void set(uInt32 bit);
void clear(uInt32 bit);
void toggle(uInt32 bit);
private:
// number of bits in the array:
int size;
private:
// number of bits in the array:
uInt32 size;
// number of unsigned ints (size/wordSize):
int words;
// number of unsigned ints (size/wordSize):
uInt32 words;
// the array itself:
unsigned int *bits;
// the array itself:
uInt32* bits;
};
#endif

View File

@ -516,7 +516,6 @@ void DataGridWidget::drawWidget(bool hilite)
//cerr << "DataGridWidget::drawWidget\n";
FBSurface& s = _boss->dialog().surface();
int row, col, deltax;
string buffer;
// Draw the internal grid and labels
int linewidth = _cols * _colWidth;
@ -542,16 +541,14 @@ void DataGridWidget::drawWidget(bool hilite)
if (_selectedItem == pos && _editMode)
{
buffer = _editString;
adjustOffset();
deltax = -_editScrollOffset;
s.drawString(_font, buffer, x, y, _colWidth, kTextColor,
s.drawString(_font, _editString, x, y, _colWidth, kTextColor,
kTextAlignLeft, deltax, false);
}
else
{
buffer = _valueStringList[pos];
deltax = 0;
uInt32 color = kTextColor;
@ -567,7 +564,7 @@ void DataGridWidget::drawWidget(bool hilite)
else if(_hiliteList[pos])
color = kDbgColorHi;
s.drawString(_font, buffer, x, y, _colWidth, color);
s.drawString(_font, _valueStringList[pos], x, y, _colWidth, color);
}
}
}

View File

@ -20,17 +20,44 @@
//============================================================================
#include "bspf.hxx"
#include "Debugger.hxx"
#include "ContextMenu.hxx"
#include "Widget.hxx"
#include "ScrollBarWidget.hxx"
#include "RomListWidget.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RomListWidget::RomListWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, int w, int h)
: CheckListWidget(boss, font, x, y, w, h),
myMenu(NULL)
: EditableWidget(boss, font, x, y, 16, 16),
myMenu(NULL),
_rows(0),
_cols(0),
_currentPos(0),
_selectedItem(-1),
_highlightedItem(-1),
_currentKeyDown(0),
_editMode(false)
{
_flags = WIDGET_ENABLED | WIDGET_CLEARBG | WIDGET_RETAIN_FOCUS;
_type = kRomListWidget;
_bgcolor = kWidColor;
_bgcolorhi = kWidColor;
_textcolor = kTextColor;
_textcolorhi = kTextColor;
_cols = w / _fontWidth;
_rows = h / _fontHeight;
// Set real dimensions
_w = w - kScrollBarWidth;
_h = h + 2;
// Create scrollbar and attach to the list
myScrollBar = new ScrollBarWidget(boss, font, _x + _w, _y, kScrollBarWidth, _h);
myScrollBar->setTarget(this);
// Add context menu
StringMap l;
// l.push_back("Add bookmark");
l.push_back("Save ROM", "saverom");
@ -43,6 +70,30 @@ RomListWidget::RomListWidget(GuiObject* boss, const GUI::Font& font,
myLabelWidth = BSPF_max(16, int(0.35 * (numchars - 12))) * fontWidth;
myBytesWidth = 12 * fontWidth;
//////////////////////////////////////////////////////
// Add checkboxes
int ypos = _y + 2;
// rowheight is determined by largest item on a line,
// possibly meaning that number of rows will change
_fontHeight = BSPF_max(_fontHeight, CheckboxWidget::boxSize());
_rows = h / _fontHeight;
// Create a CheckboxWidget for each row in the list
CheckboxWidget* t;
for(int i = 0; i < _rows; ++i)
{
t = new CheckboxWidget(boss, font, _x + 2, ypos, "", kCheckActionCmd);
t->setTarget(this);
t->setID(i);
t->drawBox(false);
t->setFill(true);
t->setTextColor(kTextColorEm);
ypos += _fontHeight;
myCheckList.push_back(t);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -53,20 +104,115 @@ RomListWidget::~RomListWidget()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomListWidget::setList(const CartDebug::DisassemblyList& list,
const BoolArray& state)
const PackedBitArray& state)
{
myList = &list;
myBPState = &state;
// TODO - maybe there's a better way than copying all the bytes again??
StringList bytes;
for(uInt32 i = 0; i < list.size(); ++i)
bytes.push_back(list[i].bytes);
CheckListWidget::setList(bytes, state);
// Enable all checkboxes
for(int i = 0; i < _rows; ++i)
myCheckList[i]->setFlags(WIDGET_ENABLED);
// Then turn off any extras
if((int)myList->size() < _rows)
for(int i = myList->size(); i < _rows; ++i)
myCheckList[i]->clearFlags(WIDGET_ENABLED);
recalc();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomListWidget::setHighlighted(int item)
{
if(item < -1 || item >= (int)myList->size())
return;
if(isEnabled())
{
if(_editMode)
abortEditMode();
_highlightedItem = item;
// Only scroll the list if we're about to pass the page boundary
if(_currentPos == 0)
_currentPos = _highlightedItem;
else if(_highlightedItem == _currentPos + _rows)
_currentPos += _rows;
scrollToHighlighted();
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const string& RomListWidget::getEditString() const
{
if(_selectedItem < -1 || _selectedItem >= (int)myList->size())
return EmptyString;
else
return _editString;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int RomListWidget::findItem(int x, int y) const
{
return (y - 1) / _fontHeight + _currentPos;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomListWidget::recalc()
{
int size = myList->size();
if (_currentPos >= size)
_currentPos = size - 1;
if (_currentPos < 0)
_currentPos = 0;
if(_selectedItem < 0 || _selectedItem >= size)
_selectedItem = 0;
_editMode = false;
myScrollBar->_numEntries = myList->size();
myScrollBar->_entriesPerPage = _rows;
// Reset to normal data entry
abortEditMode();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomListWidget::scrollToCurrent(int item)
{
// Only do something if the current item is not in our view port
if (item < _currentPos)
{
// it's above our view
_currentPos = item;
}
else if (item >= _currentPos + _rows )
{
// it's below our view
_currentPos = item - _rows + 1;
}
if (_currentPos < 0 || _rows > (int)myList->size())
_currentPos = 0;
else if (_currentPos + _rows > (int)myList->size())
_currentPos = myList->size() - _rows;
myScrollBar->_currentPos = _currentPos;
myScrollBar->recalc();
setDirty(); draw();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomListWidget::handleMouseDown(int x, int y, int button, int clickCount)
{
if (!isEnabled())
return;
// Grab right mouse button for context menu, send left to base class
if(button == 2)
{
@ -74,13 +220,184 @@ void RomListWidget::handleMouseDown(int x, int y, int button, int clickCount)
myMenu->show(x + getAbsX(), y + getAbsY());
}
else
ListWidget::handleMouseDown(x, y, button, clickCount);
{
// First check whether the selection changed
int newSelectedItem;
newSelectedItem = findItem(x, y);
if (newSelectedItem > (int)myList->size() - 1)
newSelectedItem = -1;
if (_selectedItem != newSelectedItem)
{
if (_editMode)
abortEditMode();
_selectedItem = newSelectedItem;
setDirty(); draw();
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomListWidget::handleMouseUp(int x, int y, int button, int clickCount)
{
// If this was a double click and the mouse is still over the selected item,
// send the double click command
if (clickCount == 2 && (_selectedItem == findItem(x, y)))
{
// Start edit mode
if(_editable && !_editMode)
startEditMode();
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomListWidget::handleMouseWheel(int x, int y, int direction)
{
myScrollBar->handleMouseWheel(x, y, direction);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool RomListWidget::handleKeyDown(int ascii, int keycode, int modifiers)
{
// Ignore all Alt-mod keys
if(instance().eventHandler().kbdAlt(modifiers))
return true;
bool handled = true;
int oldSelectedItem = _selectedItem;
if (_editMode)
{
// Class EditableWidget handles all text editing related key presses for us
handled = EditableWidget::handleKeyDown(ascii, keycode, modifiers);
}
else
{
// not editmode
switch (keycode)
{
case ' ': // space
// Snap list back to currently highlighted line
if(_highlightedItem >= 0)
{
_currentPos = _highlightedItem;
scrollToHighlighted();
}
break;
default:
handled = false;
}
}
if (_selectedItem != oldSelectedItem)
{
myScrollBar->draw();
scrollToSelected();
}
_currentKeyDown = keycode;
return handled;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool RomListWidget::handleKeyUp(int ascii, int keycode, int modifiers)
{
if (keycode == _currentKeyDown)
_currentKeyDown = 0;
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool RomListWidget::handleEvent(Event::Type e)
{
return ListWidget::handleEvent(e); // override CheckListWidget::handleEvent()
if(!isEnabled() || _editMode)
return false;
bool handled = true;
int oldSelectedItem = _selectedItem;
switch(e)
{
case Event::UISelect:
if (_selectedItem >= 0)
{
if (_editable)
startEditMode();
}
break;
case Event::UIUp:
if (_selectedItem > 0)
_selectedItem--;
break;
case Event::UIDown:
if (_selectedItem < (int)myList->size() - 1)
_selectedItem++;
break;
case Event::UIPgUp:
_selectedItem -= _rows - 1;
if (_selectedItem < 0)
_selectedItem = 0;
break;
case Event::UIPgDown:
_selectedItem += _rows - 1;
if (_selectedItem >= (int)myList->size() )
_selectedItem = myList->size() - 1;
break;
case Event::UIHome:
_selectedItem = 0;
break;
case Event::UIEnd:
_selectedItem = myList->size() - 1;
break;
default:
handled = false;
}
if (_selectedItem != oldSelectedItem)
{
myScrollBar->draw();
scrollToSelected();
}
return handled;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomListWidget::handleCommand(CommandSender* sender, int cmd, int data, int id)
{
switch (cmd)
{
case kCheckActionCmd:
// We let the parent class handle this
// Pass it as a kRLBreakpointChangedCmd command, since that's the intent
sendCommand(kRLBreakpointChangedCmd, myCheckList[id]->getState(), _currentPos+id);
break;
case kSetPositionCmd:
if (_currentPos != (int)data)
{
_currentPos = data;
setDirty(); draw();
}
break;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomListWidget::lostFocusWidget()
{
_editMode = false;
// Reset to normal data entry
abortEditMode();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -90,7 +407,6 @@ void RomListWidget::drawWidget(bool hilite)
FBSurface& s = _boss->dialog().surface();
const CartDebug::DisassemblyList& dlist = *myList;
int i, pos, xpos, ypos, len = dlist.size();
string buffer;
int deltax;
// Draw a thin frame around the list and to separate columns
@ -101,15 +417,15 @@ void RomListWidget::drawWidget(bool hilite)
s.vLine(_x + CheckboxWidget::boxSize() + 5, _y, _y + _h - 1, kColor);
// Draw the list items
GUI::Rect r = getEditRect();
GUI::Rect l = getLineRect();
const GUI::Rect& r = getEditRect();
const GUI::Rect& l = getLineRect();
xpos = _x + CheckboxWidget::boxSize() + 10; ypos = _y + 2;
for (i = 0, pos = _currentPos; i < _rows && pos < len; i++, pos++, ypos += _fontHeight)
{
// Draw checkboxes for correct lines (takes scrolling into account)
_checkList[i]->setState(_stateList[pos]);
_checkList[i]->setDirty();
_checkList[i]->draw();
myCheckList[i]->setState(myBPState->isSet(dlist[pos].address));
myCheckList[i]->setDirty();
myCheckList[i]->draw();
// Draw highlighted item in a frame
if (_highlightedItem == pos)
@ -140,24 +456,22 @@ void RomListWidget::drawWidget(bool hilite)
// Draw editable bytes
if (_selectedItem == pos && _editMode)
{
buffer = _editString;
adjustOffset();
deltax = -_editScrollOffset;
s.drawString(_font, buffer, _x + r.left, ypos, r.width(), kTextColor,
s.drawString(_font, _editString, _x + r.left, ypos, r.width(), kTextColor,
kTextAlignLeft, deltax, false);
}
else
{
buffer = _list[pos];
deltax = 0;
s.drawString(_font, buffer, _x + r.left, ypos, r.width(), kTextColor);
s.drawString(_font, dlist[pos].bytes, _x + r.left, ypos, r.width(), kTextColor);
}
}
// Only draw the caret while editing, and if it's in the current viewport
if(_editMode && (_selectedItem >= _scrollBar->_currentPos) &&
(_selectedItem < _scrollBar->_currentPos + _rows))
if(_editMode && (_selectedItem >= myScrollBar->_currentPos) &&
(_selectedItem < myScrollBar->_currentPos + _rows))
drawCaret();
}
@ -202,3 +516,45 @@ bool RomListWidget::tryInsertChar(char c, int pos)
else
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomListWidget::startEditMode()
{
if (_editable && !_editMode && _selectedItem >= 0)
{
// Does this line represent an editable area?
if((*myList)[_selectedItem].bytes == "")
return;
_editMode = true;
// Widget gets raw data while editing
EditableWidget::startEditMode();
setEditString((*myList)[_selectedItem].bytes);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomListWidget::endEditMode()
{
if (!_editMode)
return;
// Send a message that editing finished with a return/enter key press
// The parent then calls getEditString() to get the newly entered data
_editMode = false;
sendCommand(kRLRomChangedCmd, _selectedItem, _id);
// Reset to normal data entry
EditableWidget::endEditMode();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomListWidget::abortEditMode()
{
// Undo any changes made
_editMode = false;
// Reset to normal data entry
EditableWidget::abortEditMode();
}

View File

@ -22,14 +22,24 @@
#ifndef ROM_LIST_WIDGET_HXX
#define ROM_LIST_WIDGET_HXX
class CheckboxWidget;
class ContextMenu;
class ScrollBarWidget;
class PackedBitArray;
#include "Array.hxx"
#include "StringList.hxx"
#include "CheckListWidget.hxx"
#include "CartDebug.hxx"
#include "EditableWidget.hxx"
// Some special commands for this widget
enum {
kRLBreakpointChangedCmd = 'RLbp', // click on the checkbox for a breakpoint
kRLRomChangedCmd = 'RLpr' // ROM item data changed - 'data' will be item index
};
/** RomListWidget */
class RomListWidget : public CheckListWidget
class RomListWidget : public EditableWidget
{
friend class RomWidget;
@ -38,24 +48,60 @@ class RomListWidget : public CheckListWidget
int x, int y, int w, int h);
virtual ~RomListWidget();
void setList(const CartDebug::DisassemblyList& list, const BoolArray& state);
void setList(const CartDebug::DisassemblyList& list, const PackedBitArray& state);
int getSelected() const { return _selectedItem; }
int getHighlighted() const { return _highlightedItem; }
void setHighlighted(int item);
const string& getEditString() const;
void startEditMode();
void endEditMode();
protected:
void handleMouseDown(int x, int y, int button, int clickCount);
void handleMouseUp(int x, int y, int button, int clickCount);
void handleMouseWheel(int x, int y, int direction);
bool handleKeyDown(int ascii, int keycode, int modifiers);
bool handleKeyUp(int ascii, int keycode, int modifiers);
bool handleEvent(Event::Type e);
void handleCommand(CommandSender* sender, int cmd, int data, int id);
void drawWidget(bool hilite);
GUI::Rect getLineRect() const;
GUI::Rect getEditRect() const;
int findItem(int x, int y) const;
void recalc();
bool tryInsertChar(char c, int pos);
void abortEditMode();
void lostFocusWidget();
void scrollToSelected() { scrollToCurrent(_selectedItem); }
void scrollToHighlighted() { scrollToCurrent(_highlightedItem); }
private:
ContextMenu* myMenu;
void scrollToCurrent(int item);
private:
ContextMenu* myMenu;
ScrollBarWidget* myScrollBar;
int myLabelWidth;
int myBytesWidth;
int _rows;
int _cols;
int _currentPos;
int _selectedItem;
int _highlightedItem;
int _currentKeyDown;
bool _editMode;
const CartDebug::DisassemblyList* myList;
const PackedBitArray* myBPState;
Common::Array<CheckboxWidget*> myCheckList;
};
#endif

View File

@ -25,11 +25,11 @@
#include "DebuggerParser.hxx"
#include "CartDebug.hxx"
#include "CpuDebug.hxx"
#include "DataGridWidget.hxx"
#include "PackedBitArray.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"
@ -45,45 +45,50 @@ RomWidget::RomWidget(GuiObject* boss, const GUI::Font& font, int x, int y)
int xpos, ypos;
StaticTextWidget* t;
WidgetArray wid;
// Create bank editable area
xpos = x + 40; ypos = y + 7;
// Show current bank
xpos = x; ypos = y + 7;
t = new StaticTextWidget(boss, font, xpos, ypos,
font.getStringWidth("Current bank: "),
font.getStringWidth("Bank (current/total):"),
font.getFontHeight(),
"Current bank:", kTextAlignLeft);
"Bank (current/total):", kTextAlignLeft);
xpos += t->getWidth() + 10;
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);
myBank = new EditTextWidget(boss, font, xpos, ypos-2,
4 * font.getMaxCharWidth(),
font.getLineHeight(), "");
myBank->setEditable(false);
// Show number of banks
xpos += myBank->getWidth() + 45;
t = new StaticTextWidget(boss, font, xpos, ypos,
font.getStringWidth("Total banks: "),
font.getFontHeight(),
"Total banks:", kTextAlignLeft);
xpos += t->getWidth() + 10;
myBankCount = new EditTextWidget(boss, font, xpos, ypos-2,
font.getStringWidth("XXXX"),
font.getLineHeight(), "");
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;
GUI::Rect dialog = instance().debugger().getDialogBounds();
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);
myRomList->setStyle(kSolidFill);
addFocusWidget(myRomList);
// Calculate real dimensions
@ -95,6 +100,9 @@ RomWidget::RomWidget(GuiObject* boss, const GUI::Font& font, int x, int y)
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");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -103,17 +111,46 @@ 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 kListItemChecked:
setBreak(data);
case kRLBreakpointChangedCmd:
// 'id' is the line in the disassemblylist to be accessed
// 'data' is the state of the breakpoint at 'id'
setBreak(id, data);
break;
case kListItemDataChangedCmd:
patchROM(data, myRomList->getSelectedString());
case kRLRomChangedCmd:
// 'data' is the line in the disassemblylist to be accessed
patchROM(data, myRomList->getEditString());
break;
case kCMenuItemSelectedCmd:
@ -132,6 +169,12 @@ void RomWidget::handleCommand(CommandSender* sender, int cmd, int data, int id)
break;
}
case kAutocodeChanged:
instance().settings().setString("autocode", myAutocode->getSelectedTag());
invalidate();
loadConfig();
break;
case kRomNameEntered:
{
const string& rom = mySaveRom->getResult();
@ -144,108 +187,43 @@ void RomWidget::handleCommand(CommandSender* sender, int cmd, int data, int id)
}
break;
}
case kDGItemDataChangedCmd:
{
int bank = myBank->getSelectedValue();
instance().debugger().setBank(bank);
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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
// Only reload full bank when necessary
// TODO - bank changes aren't the only time that the disassembly needs to
// be redone; ROMs which dynamically change cart address space and
// have self-modifying code need to be taken into account
// As well, we don't always start from the 0xfffc; this only
// happens in the startup bank
myListIsDirty |= cart.disassemble(true);
if(myListIsDirty)
{
const CartDebug::DisassemblyList& list = cart.disassemblyList();
BoolArray state;
PackedBitArray& bp = dbg.breakpoints();
for(uInt32 i = 0; i < list.size(); ++i)
{
const CartDebug::DisassemblyTag& tag = list[i];
if(tag.address != 0 && bp.isSet(tag.address))
state.push_back(true);
else
state.push_back(false);
}
myRomList->setList(list, state);
// Restore the old bank, in case we inadvertently switched while reading.
// dbg.setBank(myCurrentBank); // TODO - why is this here?
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
IntArray alist;
IntArray vlist;
BoolArray changed;
alist.push_back(-1);
vlist.push_back(cart.getBank());
changed.push_back(bankChanged);
myBank->setList(alist, vlist, changed);
// Indicate total number of banks
myBankCount->setEditString(dbg.valueToString(cart.bankCount(), kBASE_10));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomWidget::setBreak(int data)
void RomWidget::setBreak(int disasm_line, bool state)
{
const CartDebug::DisassemblyList& list =
instance().debugger().cartDebug().disassemblyList();
if(data >= (int)list.size()) return;
if(disasm_line >= (int)list.size()) return;
bool state = myRomList->getState(data);
if(list[data].address != 0)
instance().debugger().setBreakPoint(list[data].address, state);
if(list[disasm_line].address != 0)
instance().debugger().setBreakPoint(list[disasm_line].address, state);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomWidget::setPC(int data)
void RomWidget::setPC(int disasm_line)
{
const CartDebug::DisassemblyList& list =
instance().debugger().cartDebug().disassemblyList();
if(data >= (int)list.size()) return;
if(disasm_line >= (int)list.size()) return;
if(list[data].address != 0)
if(list[disasm_line].address != 0)
{
ostringstream command;
command << "pc #" << list[data].address;
command << "pc #" << list[disasm_line].address;
instance().debugger().run(command.str());
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomWidget::patchROM(int data, const string& bytes)
void RomWidget::patchROM(int disasm_line, const string& bytes)
{
const CartDebug::DisassemblyList& list =
instance().debugger().cartDebug().disassemblyList();
if(data >= (int)list.size()) return;
if(disasm_line >= (int)list.size()) return;
if(list[data].address != 0)
if(list[disasm_line].address != 0)
{
ostringstream command;
@ -255,7 +233,7 @@ void RomWidget::patchROM(int data, const string& bytes)
BaseFormat oldbase = instance().debugger().parser().base();
instance().debugger().parser().setBase(kBASE_16);
command << "rom #" << list[data].address << " " << bytes;
command << "rom #" << list[disasm_line].address << " " << bytes;
instance().debugger().run(command.str());
// Restore previous base

View File

@ -23,9 +23,9 @@
#define ROM_WIDGET_HXX
class GuiObject;
class DataGridWidget;
class EditTextWidget;
class InputTextDialog;
class PopUpWidget;
class RomListWidget;
class StringList;
@ -46,19 +46,21 @@ class RomWidget : public Widget, public CommandSender
void loadConfig();
private:
void setBreak(int data);
void setPC(int data);
void patchROM(int data, const string& bytes);
void setBreak(int disasm_line, bool state);
void setPC(int disasm_line);
void patchROM(int disasm_line, const string& bytes);
void saveROM(const string& rom);
private:
enum {
kRomNameEntered = 'RWrn'
kAutocodeChanged = 'ACch',
kRomNameEntered = 'RWrn'
};
RomListWidget* myRomList;
DataGridWidget* myBank;
EditTextWidget* myBank;
EditTextWidget* myBankCount;
PopUpWidget* myAutocode;
InputTextDialog* mySaveRom;
bool myListIsDirty;

View File

@ -206,7 +206,7 @@ string Cartridge::createFromMultiCart(const uInt8*& image, uInt32& size,
image += i*size;
// We need a new md5 and name
md5 = MD5(image, size);
md5 = MD5(image, size);
ostringstream buf;
buf << " [G" << (i+1) << "]";
id = buf.str();
@ -347,7 +347,7 @@ string Cartridge::autodetectType(const uInt8* image, uInt32 size)
// TODO - this should really be in a method that checks the first
// 512 bytes of ROM and finds if either the lower 256 bytes or
// higher 256 bytes are all the same. For now, we assume that
// all carts of 12K are CBS RAM Plus/FASC.
// all carts of 12K are CBS RAM Plus/FA.
type = "FA";
}
else if(size == 16384) // 16K

View File

@ -125,6 +125,9 @@ Settings::Settings(OSystem* osystem)
setInternal("fastscbios", "false");
setExternal("romloadcount", "0");
setExternal("maxres", "0x0");
// Debugger options
setInternal("autocode", "2");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -396,6 +399,7 @@ void Settings::usage()
<< " The following options are meant for developers\n"
<< " Arguments are more fully explained in the manual\n"
<< endl
<< " -autocode <0|1|2> Set automatic code vs. data determination in the disassembler\n"
<< " -debuggerres <WxH> The resolution to use in debugger mode\n"
<< " -break <address> Set a breakpoint at 'address'\n"
<< " -debug Start in debugger mode\n"

View File

@ -158,9 +158,9 @@ void Dialog::addToFocusList(WidgetArray& list, int id)
// Make sure the array is large enough
while((int)_ourFocusList.size() <= id)
{
Focus f;
f.focusedWidget = NULL;
_ourFocusList.push_back(f);
Focus f;
f.focusedWidget = NULL;
_ourFocusList.push_back(f);
}
_ourFocusList[id].focusList.push_back(list);

View File

@ -19,6 +19,8 @@
// Copyright (C) 2002-2004 The ScummVM project
//============================================================================
#include <sstream>
#include "OSystem.hxx"
#include "FrameBuffer.hxx"
#include "Dialog.hxx"
@ -28,20 +30,22 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EditTextWidget::EditTextWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, int w, int h, const string& text)
: EditableWidget(boss, font, x, y - 1, w, h + 2)
: EditableWidget(boss, font, x, y - 1, w, h + 2),
_editable(true),
_changed(false)
{
_flags = WIDGET_ENABLED | WIDGET_CLEARBG | WIDGET_RETAIN_FOCUS;
_type = kEditTextWidget;
_editable = true;
startEditMode(); // We're always in edit mode
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EditTextWidget::setEditString(const string& str)
void EditTextWidget::setEditString(const string& str, bool changed)
{
EditableWidget::setEditString(str);
_backupString = str;
_changed = changed;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -74,6 +78,10 @@ void EditTextWidget::drawWidget(bool hilite)
//cerr << "EditTextWidget::drawWidget\n";
FBSurface& s = _boss->dialog().surface();
// Highlight changes
if(_changed)
s.fillRect(_x, _y, _w, _h, kDbgChangedColor);
// Draw a thin frame around us.
s.hLine(_x, _y, _x + _w - 1, kColor);
s.hLine(_x, _y + _h - 1, _x +_w - 1, kShadowColor);
@ -83,7 +91,8 @@ void EditTextWidget::drawWidget(bool hilite)
// Draw the text
adjustOffset();
s.drawString(_font, _editString, _x + 2, _y + 2, getEditRect().width(),
_textcolor, kTextAlignLeft, -_editScrollOffset, false);
!_changed ? _textcolor : kDbgChangedTextColor,
kTextAlignLeft, -_editScrollOffset, false);
// Draw the caret
drawCaret();

View File

@ -33,7 +33,7 @@ class EditTextWidget : public EditableWidget
EditTextWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, int w, int h, const string& text);
void setEditString(const string& str);
void setEditString(const string& str, bool changed = false);
virtual void handleMouseDown(int x, int y, int button, int clickCount);
@ -50,6 +50,7 @@ class EditTextWidget : public EditableWidget
protected:
string _backupString;
int _editable;
bool _changed;
};
#endif

View File

@ -54,9 +54,9 @@ class EditableWidget : public Widget, public CommandSender
virtual bool wantsFocus() { return _editable; }
protected:
virtual void startEditMode() { setFlags(WIDGET_WANTS_RAWDATA); }
virtual void endEditMode() { clearFlags(WIDGET_WANTS_RAWDATA); }
virtual void abortEditMode() { clearFlags(WIDGET_WANTS_RAWDATA); }
virtual void startEditMode() { setFlags(WIDGET_WANTS_RAWDATA); _editString = ""; }
virtual void endEditMode() { clearFlags(WIDGET_WANTS_RAWDATA); _editString = ""; }
virtual void abortEditMode() { clearFlags(WIDGET_WANTS_RAWDATA); _editString = ""; }
virtual GUI::Rect getEditRect() const = 0;
virtual int getCaretOffset() const;

View File

@ -41,7 +41,6 @@ ListWidget::ListWidget(GuiObject* boss, const GUI::Font& font,
_highlightedItem(-1),
_currentKeyDown(0),
_editMode(false),
_caretInverse(true),
_quickSelect(quickSelect),
_quickSelectTime(0)
{

View File

@ -104,9 +104,7 @@ class ListWidget : public EditableWidget
int _selectedItem;
int _highlightedItem;
int _currentKeyDown;
bool _editMode;
bool _caretInverse;
ScrollBarWidget* _scrollBar;