diff --git a/docs/index.html b/docs/index.html index 7c8126ba5..88c55e07c 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1025,6 +1025,12 @@ Description + +
-autocode <0|1|2>
+ Try to differentiate between code vs. data sections in the + disassembler. See the Debugger section for more information. + +
-debuggerres <WxH>
Set the size of the debugger window. diff --git a/src/debugger/CartDebug.cxx b/src/debugger/CartDebug.cxx index 314cfec13..764c100f6 100644 --- a/src/debugger/CartDebug.cxx +++ b/src/debugger/CartDebug.cxx @@ -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::const_iterator iter = myAddrToLineList.find(address); - return iter != myAddrToLineList.end() ? iter->second : 0; + return iter != myAddrToLineList.end() ? iter->second : -1; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/debugger/CartDebug.hxx b/src/debugger/CartDebug.hxx index 869a3e939..af83bc190 100644 --- a/src/debugger/CartDebug.hxx +++ b/src/debugger/CartDebug.hxx @@ -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; diff --git a/src/debugger/DiStella.cxx b/src/debugger/DiStella.cxx index 6c1db376c..503daa274 100644 --- a/src/debugger/DiStella.cxx +++ b/src/debugger/DiStella.cxx @@ -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(); diff --git a/src/debugger/DiStella.hxx b/src/debugger/DiStella.hxx index 20d898675..2e5a2a4bc 100644 --- a/src/debugger/DiStella.hxx +++ b/src/debugger/DiStella.hxx @@ -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(); diff --git a/src/debugger/PackedBitArray.cxx b/src/debugger/PackedBitArray.cxx index d316eba59..c88210287 100644 --- a/src/debugger/PackedBitArray.cxx +++ b/src/debugger/PackedBitArray.cxx @@ -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; idialog().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); } } } diff --git a/src/debugger/gui/RomListWidget.cxx b/src/debugger/gui/RomListWidget.cxx index 811d06c9e..2d83fcc62 100644 --- a/src/debugger/gui/RomListWidget.cxx +++ b/src/debugger/gui/RomListWidget.cxx @@ -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(); +} diff --git a/src/debugger/gui/RomListWidget.hxx b/src/debugger/gui/RomListWidget.hxx index 076164c59..186ab2ffd 100644 --- a/src/debugger/gui/RomListWidget.hxx +++ b/src/debugger/gui/RomListWidget.hxx @@ -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 myCheckList; }; #endif diff --git a/src/debugger/gui/RomWidget.cxx b/src/debugger/gui/RomWidget.cxx index 0f0ad6d27..ebbc87bf3 100644 --- a/src/debugger/gui/RomWidget.cxx +++ b/src/debugger/gui/RomWidget.cxx @@ -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 diff --git a/src/debugger/gui/RomWidget.hxx b/src/debugger/gui/RomWidget.hxx index 0ac17fc98..ded25c494 100644 --- a/src/debugger/gui/RomWidget.hxx +++ b/src/debugger/gui/RomWidget.hxx @@ -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; diff --git a/src/emucore/Cart.cxx b/src/emucore/Cart.cxx index 3db33607a..f40189800 100644 --- a/src/emucore/Cart.cxx +++ b/src/emucore/Cart.cxx @@ -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 diff --git a/src/emucore/Settings.cxx b/src/emucore/Settings.cxx index 55eadb3bd..0072e4b18 100644 --- a/src/emucore/Settings.cxx +++ b/src/emucore/Settings.cxx @@ -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 The resolution to use in debugger mode\n" << " -break
Set a breakpoint at 'address'\n" << " -debug Start in debugger mode\n" diff --git a/src/gui/Dialog.cxx b/src/gui/Dialog.cxx index 29023c2f4..8bfd3e2ad 100644 --- a/src/gui/Dialog.cxx +++ b/src/gui/Dialog.cxx @@ -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); diff --git a/src/gui/EditTextWidget.cxx b/src/gui/EditTextWidget.cxx index 4e4c5c7f5..b646ace1c 100644 --- a/src/gui/EditTextWidget.cxx +++ b/src/gui/EditTextWidget.cxx @@ -19,6 +19,8 @@ // Copyright (C) 2002-2004 The ScummVM project //============================================================================ +#include + #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(); diff --git a/src/gui/EditTextWidget.hxx b/src/gui/EditTextWidget.hxx index ca5a23de7..7d041fe15 100644 --- a/src/gui/EditTextWidget.hxx +++ b/src/gui/EditTextWidget.hxx @@ -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 diff --git a/src/gui/EditableWidget.hxx b/src/gui/EditableWidget.hxx index a265aea17..254516bb2 100644 --- a/src/gui/EditableWidget.hxx +++ b/src/gui/EditableWidget.hxx @@ -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; diff --git a/src/gui/ListWidget.cxx b/src/gui/ListWidget.cxx index fb4b902d2..fdadc64e2 100644 --- a/src/gui/ListWidget.cxx +++ b/src/gui/ListWidget.cxx @@ -41,7 +41,6 @@ ListWidget::ListWidget(GuiObject* boss, const GUI::Font& font, _highlightedItem(-1), _currentKeyDown(0), _editMode(false), - _caretInverse(true), _quickSelect(quickSelect), _quickSelectTime(0) { diff --git a/src/gui/ListWidget.hxx b/src/gui/ListWidget.hxx index 1a6f2b920..021e38dc0 100644 --- a/src/gui/ListWidget.hxx +++ b/src/gui/ListWidget.hxx @@ -104,9 +104,7 @@ class ListWidget : public EditableWidget int _selectedItem; int _highlightedItem; int _currentKeyDown; - bool _editMode; - bool _caretInverse; ScrollBarWidget* _scrollBar;