//============================================================================ // // 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-2013 by Bradford W. Mott, Stephen Anthony // and the Stella Team // // See the file "License.txt" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // // $Id$ // // Based on code from ScummVM - Scumm Interpreter // Copyright (C) 2002-2004 The ScummVM project //============================================================================ #include #include "DataGridWidget.hxx" #include "EditTextWidget.hxx" #include "FrameBuffer.hxx" #include "GuiObject.hxx" #include "InputTextDialog.hxx" #include "OSystem.hxx" #include "CartDebug.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), myCurrentRamBank(0) { _type = kRamWidget; const int fontWidth = font.getMaxCharWidth(), fontHeight = font.getFontHeight(), lineHeight = font.getLineHeight(), bwidth = font.getStringWidth("Compare "), bheight = lineHeight + 2; int xpos, ypos, lwidth; // Create a 16x8 grid holding byte values (16 x 8 = 128 RAM bytes) with labels // Add a scrollbar, since there may be more than 128 bytes of RAM available xpos = x; ypos = y + lineHeight; lwidth = 4 * fontWidth; myRamGrid = new DataGridWidget(boss, font, xpos + lwidth, ypos, 16, 8, 2, 8, kBASE_16, true); 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, "Revert", kRevertCmd); myRevertButton->setTarget(this); ypos += 2 * bheight + 2; mySearchButton = new ButtonWidget(boss, font, xpos, ypos, bwidth, bheight, "Search", kSearchCmd); mySearchButton->setTarget(this); ypos += bheight + 4; myCompareButton = new ButtonWidget(boss, font, xpos, ypos, bwidth, bheight, "Compare", kCmpCmd); myCompareButton->setTarget(this); ypos += bheight + 4; myRestartButton = new ButtonWidget(boss, font, xpos, ypos, bwidth, bheight, "Reset", kRestartCmd); myRestartButton->setTarget(this); // Labels for RAM grid xpos = x; ypos = y + lineHeight; myRamStart = new StaticTextWidget(boss, font, xpos, ypos - lineHeight, font.getStringWidth("xxxx"), fontHeight, "00xx", kTextAlignLeft); for(int col = 0; col < 16; ++col) { new StaticTextWidget(boss, font, xpos + col*myRamGrid->colWidth() + lwidth + 8, ypos - lineHeight, fontWidth, fontHeight, instance().debugger().valueToString(col, kBASE_16_1), kTextAlignLeft); } for(int row = 0; row < 8; ++row) { myRamLabels[row] = new StaticTextWidget(boss, font, xpos + 8, ypos + row*lineHeight + 2, 3*fontWidth, fontHeight, "", 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 labels; labels.push_back("Search: "); myInputBox = new InputTextDialog(boss, font, labels); myInputBox->setTarget(this); // Start with these buttons disabled myCompareButton->clearFlags(WIDGET_ENABLED); myRestartButton->clearFlags(WIDGET_ENABLED); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - RamWidget::~RamWidget() { delete myInputBox; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void RamWidget::handleCommand(CommandSender* sender, int cmd, int data, int id) { // We simply change the values in the DataGridWidget // It will then send the 'kDGItemDataChangedCmd' signal to change the actual // memory location int addr, value; CartDebug& dbg = instance().debugger().cartDebug(); const CartState& state = (CartState&) dbg.getState(); switch(cmd) { case DataGridWidget::kItemDataChangedCmd: { addr = myRamGrid->getSelectedAddr(); value = myRamGrid->getSelectedValue(); // Attempt the write, and revert if it didn't succeed uInt8 oldval = dbg.peek(state.rport[addr]); dbg.poke(state.wport[addr], value); uInt8 newval = dbg.peek(state.rport[addr]); if(value != newval) { myRamGrid->setValue(addr - myCurrentRamBank*128, newval, false); break; } myUndoAddress = addr; myUndoValue = oldval; myDecValue->setEditString(instance().debugger().valueToString(value, kBASE_10)); myBinValue->setEditString(instance().debugger().valueToString(value, kBASE_2)); myRevertButton->setEnabled(true); myUndoButton->setEnabled(true); break; } case DataGridWidget::kSelectionChangedCmd: { addr = myRamGrid->getSelectedAddr(); value = myRamGrid->getSelectedValue(); myLabel->setEditString(dbg.getLabel(state.rport[addr], true)); myDecValue->setEditString(instance().debugger().valueToString(value, kBASE_10)); myBinValue->setEditString(instance().debugger().valueToString(value, kBASE_2)); break; } case kRevertCmd: for(uInt32 i = 0; i < myOldValueList.size(); ++i) dbg.poke(state.wport[i], myOldValueList[i]); fillGrid(true); break; case kUndoCmd: dbg.poke(state.wport[myUndoAddress], myUndoValue); myUndoButton->setEnabled(false); fillGrid(false); break; case kSearchCmd: showInputBox(kSValEntered); break; case kCmpCmd: showInputBox(kCValEntered); break; case kRestartCmd: doRestart(); break; case kSValEntered: { const string& result = doSearch(myInputBox->getResult()); if(result != "") myInputBox->setTitle(result); else dialog().close(); break; } case kCValEntered: { const string& result = doCompare(myInputBox->getResult()); if(result != "") myInputBox->setTitle(result); else dialog().close(); break; } case kSetPositionCmd: myCurrentRamBank = data; showSearchResults(); fillGrid(false); break; } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void RamWidget::setOpsWidget(DataGridOpsWidget* w) { myRamGrid->setOpsWidget(w); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void RamWidget::loadConfig() { fillGrid(true); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void RamWidget::fillGrid(bool updateOld) { IntArray alist; IntArray vlist; BoolArray changed; if(updateOld) myOldValueList.clear(); CartDebug& dbg = instance().debugger().cartDebug(); const CartState& state = (CartState&) dbg.getState(); const CartState& oldstate = (CartState&) dbg.getOldState(); // Jump to the correct 128 byte 'window' in the RAM area // This assumes that the RAM areas are aligned on 128 byte boundaries // TODO - the boundary restriction may not always apply ... uInt32 start = myCurrentRamBank * 128; assert(start+128 <= state.ram.size()); if(updateOld) myOldValueList = state.ram; for(uInt32 i = start; i < start + 16*8; ++i) { alist.push_back(i); vlist.push_back(state.ram[i]); changed.push_back(state.ram[i] != oldstate.ram[i]); } myRamGrid->setNumRows(state.ram.size() / 128); myRamGrid->setList(alist, vlist, changed); if(updateOld) { myRevertButton->setEnabled(false); myUndoButton->setEnabled(false); } // Update RAM labels char buf[5]; BSPF_snprintf(buf, 5, "%04X", state.rport[start] & 0xff00); buf[2] = buf[3] = 'x'; myRamStart->setLabel(buf); for(uInt32 i = start, row = 0; i < start + 16*8; i += 16, ++row) { BSPF_snprintf(buf, 3, "%02X", state.rport[i] & 0x00ff); myRamLabels[row]->setLabel(buf); } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void RamWidget::showInputBox(int cmd) { // Add inputbox in the middle of the RAM widget uInt32 x = getAbsX() + ((getWidth() - myInputBox->getWidth()) >> 1); uInt32 y = getAbsY() + ((getHeight() - myInputBox->getHeight()) >> 1); myInputBox->show(x, y); myInputBox->setEditString(""); myInputBox->setTitle(""); myInputBox->setFocus(0); myInputBox->setEmitSignal(cmd); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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(); mySearchState.clear(); // Now, search all memory locations for this value, and add it to the // search array bool hitfound = false; CartDebug& dbg = instance().debugger().cartDebug(); const CartState& state = (CartState&) dbg.getState(); for(uInt32 addr = 0; addr < state.ram.size(); ++addr) { int value = state.ram[addr]; if(comparisonSearch && searchVal != value) { mySearchState.push_back(false); } else { mySearchAddr.push_back(addr); mySearchValue.push_back(value); mySearchState.push_back(true); hitfound = true; } } // If we have some hits, enable the comparison methods if(hitfound) { mySearchButton->setEnabled(false); myCompareButton->setEnabled(true); myRestartButton->setEnabled(true); } // Finally, show the search results in the list showSearchResults(); return ""; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 previously 'found' for this value bool hitfound = false; CartDebug& dbg = instance().debugger().cartDebug(); const CartState& state = (CartState&) dbg.getState(); IntArray tempAddrList, tempValueList; mySearchState.clear(); for(uInt32 i = 0; i < state.rport.size(); ++i) mySearchState.push_back(false); 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.peek(state.rport[addr]) == searchVal) { tempAddrList.push_back(addr); tempValueList.push_back(searchVal); mySearchState[addr] = hitfound = true; } } // Update the searchArray for the new addresses and data mySearchAddr = tempAddrList; mySearchValue = tempValueList; // If we have some hits, enable the comparison methods if(hitfound) { myCompareButton->setEnabled(true); myRestartButton->setEnabled(true); } // Finally, show the search results in the list showSearchResults(); return ""; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void RamWidget::doRestart() { // Erase all search buffers, reset to start mode mySearchAddr.clear(); mySearchValue.clear(); mySearchState.clear(); showSearchResults(); mySearchButton->setEnabled(true); myCompareButton->setEnabled(false); myRestartButton->setEnabled(false); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void RamWidget::showSearchResults() { // Only update the search results for the bank currently being shown BoolArray temp; uInt32 start = myCurrentRamBank * 128; if(mySearchState.size() == 0 || start > mySearchState.size()) { for(uInt32 i = 0; i < 128; ++i) temp.push_back(false); } else { for(uInt32 i = start; i < start + 128; ++i) temp.push_back(mySearchState[i]); } myRamGrid->setHiliteList(temp); }