Watch support: you can set up to 10 arbitrary expressions which will get

evaluated and printed before each prompt. Since they're evaluated at
print-time, you can say e.g. "watch a" or "watch *myCounter" and it'll
do what you want.

Added "dump" command. This dumps any address you give it, not just RAM,
and it respects the current base setting (bin, dec, hex). Actually I may
rename the "ram" command to "mem" and allow it to work on non-RAM, and
merge the "dump" command with that.

The debugger state() now respects the default base (the registers are
printed in whatever base the user has selected with the "base" command).


git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@535 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
urchlay 2005-06-21 00:13:49 +00:00
parent 5433918ba7
commit 9a2d410a4d
5 changed files with 183 additions and 58 deletions

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of // See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES. // this file, and for a DISCLAIMER OF ALL WARRANTIES.
// //
// $Id: Debugger.cxx,v 1.21 2005-06-20 21:01:37 stephena Exp $ // $Id: Debugger.cxx,v 1.22 2005-06-21 00:13:49 urchlay Exp $
//============================================================================ //============================================================================
#include "bspf.hxx" #include "bspf.hxx"
@ -130,6 +130,11 @@ const string Debugger::run(const string& command)
return myParser->run(command); return myParser->run(command);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const string Debugger::valueToString(int value) {
return valueToString(value, kBASE_DEFAULT);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const string Debugger::valueToString(int value, BaseFormat outputBase) const string Debugger::valueToString(int value, BaseFormat outputBase)
{ {
@ -171,17 +176,17 @@ const string Debugger::state()
//cerr << "state(): pc is " << myDebugger->pc() << endl; //cerr << "state(): pc is " << myDebugger->pc() << endl;
result += "\nPC="; result += "\nPC=";
result += to_hex_16(myDebugger->pc()); result += valueToString(myDebugger->pc());
result += " A="; result += " A=";
result += to_hex_8(myDebugger->a()); result += valueToString(myDebugger->a());
result += " X="; result += " X=";
result += to_hex_8(myDebugger->x()); result += valueToString(myDebugger->x());
result += " Y="; result += " Y=";
result += to_hex_8(myDebugger->y()); result += valueToString(myDebugger->y());
result += " S="; result += " S=";
result += to_hex_8(myDebugger->sp()); result += valueToString(myDebugger->sp());
result += " P="; result += " P=";
result += to_hex_8(myDebugger->ps()); result += valueToString(myDebugger->ps());
result += "/"; result += "/";
formatFlags(myDebugger->ps(), buf); formatFlags(myDebugger->ps(), buf);
result += buf; result += buf;
@ -343,9 +348,11 @@ void Debugger::quit()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::step() int Debugger::step()
{ {
int cyc = mySystem->cycles();
mySystem->m6502().execute(1); mySystem->m6502().execute(1);
return mySystem->cycles() - cyc;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -361,15 +368,17 @@ void Debugger::step()
// FIXME: TIA framebuffer should be updated during tracing! // FIXME: TIA framebuffer should be updated during tracing!
void Debugger::trace() int Debugger::trace()
{ {
// 32 is the 6502 JSR instruction: // 32 is the 6502 JSR instruction:
if(mySystem->peek(myDebugger->pc()) == 32) { if(mySystem->peek(myDebugger->pc()) == 32) {
int cyc = mySystem->cycles();
int targetPC = myDebugger->pc() + 3; // return address int targetPC = myDebugger->pc() + 3; // return address
while(myDebugger->pc() != targetPC) while(myDebugger->pc() != targetPC)
mySystem->m6502().execute(1); mySystem->m6502().execute(1);
return mySystem->cycles() - cyc;
} else { } else {
step(); return step();
} }
} }
@ -542,3 +551,8 @@ bool Debugger::setHeight(int height)
return true; return true;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string Debugger::showWatches() {
return myParser->showWatches();
}

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of // See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES. // this file, and for a DISCLAIMER OF ALL WARRANTIES.
// //
// $Id: Debugger.hxx,v 1.19 2005-06-20 21:01:37 stephena Exp $ // $Id: Debugger.hxx,v 1.20 2005-06-21 00:13:49 urchlay Exp $
//============================================================================ //============================================================================
#ifndef DEBUGGER_HXX #ifndef DEBUGGER_HXX
@ -50,7 +50,7 @@ enum {
for all debugging operations in Stella (parser, 6502 debugger, etc). for all debugging operations in Stella (parser, 6502 debugger, etc).
@author Stephen Anthony @author Stephen Anthony
@version $Id: Debugger.hxx,v 1.19 2005-06-20 21:01:37 stephena Exp $ @version $Id: Debugger.hxx,v 1.20 2005-06-21 00:13:49 urchlay Exp $
*/ */
class Debugger : public DialogContainer class Debugger : public DialogContainer
{ {
@ -127,6 +127,7 @@ class Debugger : public DialogContainer
int stringToValue(const string& stringval) int stringToValue(const string& stringval)
{ return myParser->decipher_arg(stringval); } { return myParser->decipher_arg(stringval); }
const string valueToString(int value, BaseFormat outputBase); const string valueToString(int value, BaseFormat outputBase);
const string valueToString(int value);
void toggleBreakPoint(int bp); void toggleBreakPoint(int bp);
bool breakPoint(int bp); bool breakPoint(int bp);
@ -161,8 +162,8 @@ class Debugger : public DialogContainer
bool start(); bool start();
void quit(); void quit();
void trace(); int trace();
void step(); int step();
void setA(int a); void setA(int a);
void setX(int x); void setX(int x);
void setY(int y); void setY(int y);
@ -189,6 +190,7 @@ class Debugger : public DialogContainer
void formatFlags(int f, char *out); void formatFlags(int f, char *out);
EquateList *equates(); EquateList *equates();
PromptWidget *prompt(); PromptWidget *prompt();
string showWatches();
protected: protected:
Console* myConsole; Console* myConsole;

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of // See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES. // this file, and for a DISCLAIMER OF ALL WARRANTIES.
// //
// $Id: DebuggerParser.cxx,v 1.20 2005-06-20 21:01:37 stephena Exp $ // $Id: DebuggerParser.cxx,v 1.21 2005-06-21 00:13:49 urchlay Exp $
//============================================================================ //============================================================================
#include "bspf.hxx" #include "bspf.hxx"
@ -35,6 +35,8 @@ DebuggerParser::DebuggerParser(Debugger* d)
{ {
done = false; done = false;
defaultBase = kBASE_16; defaultBase = kBASE_16;
for(int i=0; i<kMAX_WATCHES; i++)
watches[i] = new string;
} }
DebuggerParser::~DebuggerParser() { DebuggerParser::~DebuggerParser() {
@ -201,7 +203,7 @@ bool DebuggerParser::getArgs(const string& command) {
} }
} while(*c++ != '\0'); } while(*c++ != '\0');
for(int i=0; i<argCount; i++) //for(int i=0; i<argCount; i++)
//cerr << "argStrings[" << i << "] == \"" << argStrings[i] << "\"" << endl; //cerr << "argStrings[" << i << "] == \"" << argStrings[i] << "\"" << endl;
// Now decipher each argument, in turn. // Now decipher each argument, in turn.
@ -261,6 +263,22 @@ string DebuggerParser::disasm() {
return debugger->disassemble(start, lines); return debugger->disassemble(start, lines);
} }
string DebuggerParser::dump() {
string ret;
for(int i=0; i<8; i++) {
int start = args[0] + i*16;
ret += debugger->valueToString(start);
ret += ": ";
for(int j=0; j<16; j++) {
ret += debugger->valueToString( debugger->peek(start+j) );
ret += " ";
if(j == 7) ret += "- ";
}
if(i != 7) ret += "\n";
}
return ret;
}
string DebuggerParser::eval() { string DebuggerParser::eval() {
char buf[50]; char buf[50];
string ret; string ret;
@ -280,13 +298,66 @@ string DebuggerParser::eval() {
ret += " %"; ret += " %";
ret += Debugger::to_bin_16(args[i]); ret += Debugger::to_bin_16(args[i]);
} }
sprintf(buf, " (dec %d)", args[i]); sprintf(buf, " #%d", args[i]);
ret += buf; ret += buf;
if(i != argCount - 1) ret += "\n"; if(i != argCount - 1) ret += "\n";
} }
return ret; return ret;
} }
string DebuggerParser::showWatches() {
string ret = "\n";
char buf[10];
for(int i=0; i<kMAX_WATCHES; i++) {
if(*(watches[i]->c_str()) != '\0') {
//cerr << "here1 " << i << endl;
sprintf(buf, "%d", i+1);
argCount = 1;
argStrings[0] = *watches[i];
args[0] = decipher_arg(argStrings[0]);
if(args[0] < 0) {
ret += "BAD WATCH ";
ret += buf;
ret += ": " + argStrings[0] + "\n";
} else {
ret += " watch #";
ret += buf;
ret += " (" + argStrings[0] + ") -> " + eval() + "\n";
}
}
}
// get rid of trailing \n
ret.erase(ret.length()-1, 1);
return ret;
}
string DebuggerParser::addWatch(string watch) {
for(int i=0; i<kMAX_WATCHES; i++) {
if(*(watches[i]->c_str()) == '\0') {
string *w = new string(watch);
watches[i] = w;
return "Added watch";
}
}
return "Can't add watch: Too many watches";
}
void DebuggerParser::delAllWatches() {
for(int i=0; i<kMAX_WATCHES; i++)
watches[i] = new string;
}
string DebuggerParser::delWatch(int which) {
which--;
if(which < 0 || which >= kMAX_WATCHES)
return "no such watch";
else
watches[which] = new string;
return "removed watch";
}
string DebuggerParser::run(const string& command) { string DebuggerParser::run(const string& command) {
string result; string result;
@ -358,15 +429,21 @@ string DebuggerParser::run(const string& command) {
else else
return "one argument required"; return "one argument required";
} else if(subStringMatch(verb, "step")) { } else if(subStringMatch(verb, "step")) {
char buf[12];
if(argCount > 0) if(argCount > 0)
return "step takes no arguments"; return "step takes no arguments";
debugger->step(); sprintf(buf, "#%d", debugger->step());
result = "OK"; result = "executed ";
result += buf;
result += " cycles";
} else if(subStringMatch(verb, "trace")) { } else if(subStringMatch(verb, "trace")) {
char buf[12];
if(argCount > 0) if(argCount > 0)
return "trace takes no arguments"; return "trace takes no arguments";
debugger->trace(); sprintf(buf, "#%d", debugger->trace());
result = "OK"; result = "executed ";
result += buf;
result += " cycles";
} else if(subStringMatch(verb, "ram")) { } else if(subStringMatch(verb, "ram")) {
if(argCount == 0) if(argCount == 0)
result = debugger->dumpRAM(kRamStart); result = debugger->dumpRAM(kRamStart);
@ -408,9 +485,19 @@ string DebuggerParser::run(const string& command) {
debugger->nextFrame(); debugger->nextFrame();
*/ */
debugger->nextFrame(); debugger->nextFrame();
return "OK"; return "advanced frame";
} else if(subStringMatch(verb, "clearbreaks")) { } else if(subStringMatch(verb, "clearbreaks")) {
debugger->clearAllBreakPoints(); debugger->clearAllBreakPoints();
} else if(subStringMatch(verb, "clearwatches")) {
delAllWatches();
return "cleared all watches";
} else if(subStringMatch(verb, "watch")) {
addWatch(argStrings[0]);
} else if(subStringMatch(verb, "delwatch")) {
if(argCount == 1)
return delWatch(args[0]);
else
return "one argument required";
} else if(subStringMatch(verb, "height")) { } else if(subStringMatch(verb, "height")) {
if(argCount != 1) if(argCount != 1)
return "one argument required"; return "one argument required";
@ -423,6 +510,11 @@ string DebuggerParser::run(const string& command) {
else else
return "bad height (use 0 for default, min height #383)"; return "bad height (use 0 for default, min height #383)";
*/ */
} else if(subStringMatch(verb, "dump")) {
if(argCount != 1)
return "one argument required";
else
return dump();
} else if(subStringMatch(verb, "print")) { } else if(subStringMatch(verb, "print")) {
if(argCount < 1) if(argCount < 1)
return "one or more arguments required"; return "one or more arguments required";
@ -458,41 +550,49 @@ string DebuggerParser::run(const string& command) {
// please leave each option on its own line so they're // please leave each option on its own line so they're
// easy to sort - bkw // easy to sort - bkw
return return
"Commands are case-insensitive and may be abbreviated.\n" "Commands are case-insensitive and may be abbreviated (e.g. \"tr\" for \"trace\").\n"
"Arguments are either labels or hex constants, and may be\n" "Arguments are either labels or hex constants, and may be\n"
"prefixed with a * to dereference.\n" "prefixed with a * to dereference, < or > for low/high byte,\n"
"a xx - Set Accumulator to xx\n" "and/or $/#/% for hex/dec/binary.\n\n"
"base xx - Set default input base (#2=binary, #10=decimal, #16=hex)\n" "a xx - Set Accumulator to xx\n"
"break - Set/clear breakpoint at current PC\n" "base xx - Set default input base (#2=binary, #10=decimal, #16=hex)\n"
"break xx - Set/clear breakpoint at address xx\n" "break - Set/clear breakpoint at current PC\n"
"c - Toggle Carry Flag\n" "break xx - Set/clear breakpoint at address xx\n"
"clearbreaks - Clear all breakpoints\n" "c - Toggle Carry Flag\n"
"d - Toggle Decimal Flag\n" "clearbreaks - Clear all breakpoints\n"
"disasm - Disassemble (from current PC)\n" "cleartraps - Clear all traps\n"
"disasm xx - Disassemble (from address xx)\n" "clearwatches - Clear all watches\n"
"frame - Advance to next TIA frame, then break\n" "d - Toggle Decimal Flag\n"
"height xx - Set height of debugger window in pixels\n" "dump xx - Dump 128 bytes of memory starting at xx (may be ROM, TIA, RAM)\n"
"listbreaks - List all breakpoints\n" "delwatch xx - Delete watch xx\n"
"loadsym f - Load DASM symbols from file f\n" "disasm - Disassemble (from current PC)\n"
"n - Toggle Negative Flag\n" "disasm xx - Disassemble (from address xx)\n"
"pc xx - Set Program Counter to xx\n" "frame - Advance to next TIA frame, then break\n"
"print xx - Evaluate and print expression xx\n" "height xx - Set height of debugger window in pixels\n"
"poke xx yy - Write data yy to address xx (may be ROM, TIA, etc)\n" "listbreaks - List all breakpoints\n"
"ram - Show RIOT RAM contents\n" "*listtraps - List all traps\n"
"ram xx yy - Set RAM location xx to value yy (multiple values allowed)\n" "loadsym f - Load DASM symbols from file f\n"
"reset - Jump to 6502 init vector (does not reset TIA/RIOT)\n" "n - Toggle Negative Flag\n"
"run - Exit debugger (back to emulator)\n" "pc xx - Set Program Counter to xx\n"
"s xx - Set Stack Pointer to xx\n" "print xx - Evaluate and print expression xx\n"
// "save f - Save console session to file f\n" //"poke xx yy - Write data yy to address xx (may be ROM, TIA, etc)\n"
"step - Single-step\n" "ram - Show RIOT RAM contents\n"
"tia - Show TIA register contents\n" "ram xx yy - Set RAM location xx to value yy (multiple values allowed)\n"
"trace - Single-step treating subroutine calls as 1 instruction\n" "reset - Jump to 6502 init vector (does not reset TIA/RIOT)\n"
"v - Toggle Overflow Flag\n" "run - Exit debugger (back to emulator)\n"
// "watch - Clear watch list\n" "s xx - Set Stack Pointer to xx\n"
// "watch xx - Print contents of location xx before every prompt\n" "*save f - Save console session to file f\n"
"x xx - Set X register to xx\n" "step - Single-step\n"
"y xx - Set Y register to xx\n" "tia - Show TIA register contents\n"
"z - Toggle Zero Flag\n" "trace - Single-step treating subroutine calls as 1 instruction\n"
"*trap xx - Trap any access to location xx (enter debugger on access)\n"
"*trapread xx - Trap any read access from location xx\n"
"*trapwrite xx - Trap any write access to location xx\n"
"v - Toggle Overflow Flag\n"
"watch xx - Print contents of location xx before every prompt\n"
"x xx - Set X register to xx\n"
"y xx - Set Y register to xx\n"
"z - Toggle Zero Flag\n"
; ;
} else { } else {
return "unimplemented command (try \"help\")"; return "unimplemented command (try \"help\")";

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of // See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES. // this file, and for a DISCLAIMER OF ALL WARRANTIES.
// //
// $Id: DebuggerParser.hxx,v 1.12 2005-06-20 21:01:37 stephena Exp $ // $Id: DebuggerParser.hxx,v 1.13 2005-06-21 00:13:49 urchlay Exp $
//============================================================================ //============================================================================
#ifndef DEBUGGER_PARSER_HXX #ifndef DEBUGGER_PARSER_HXX
@ -25,6 +25,7 @@ class Debugger;
#include "EquateList.hxx" #include "EquateList.hxx"
#define kMAX_ARGS 100 #define kMAX_ARGS 100
#define kMAX_WATCHES 10
typedef enum { typedef enum {
kBASE_16, kBASE_16,
@ -44,6 +45,11 @@ class DebuggerParser
void setBase(BaseFormat base) { defaultBase = base; } void setBase(BaseFormat base) { defaultBase = base; }
BaseFormat base() { return defaultBase; } BaseFormat base() { return defaultBase; }
string showWatches();
string addWatch(string watch);
string delWatch(int which);
void delAllWatches();
private: private:
bool getArgs(const string& command); bool getArgs(const string& command);
@ -52,6 +58,7 @@ class DebuggerParser
string disasm(); string disasm();
string listBreaks(); string listBreaks();
string eval(); string eval();
string dump();
Debugger* debugger; Debugger* debugger;
@ -62,6 +69,7 @@ class DebuggerParser
string argStrings[kMAX_ARGS+1]; string argStrings[kMAX_ARGS+1];
int argCount; int argCount;
BaseFormat defaultBase; BaseFormat defaultBase;
string *watches[kMAX_WATCHES]; // FIXME: remove the limit sometime
}; };
#endif #endif

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of // See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES. // this file, and for a DISCLAIMER OF ALL WARRANTIES.
// //
// $Id: PromptWidget.cxx,v 1.10 2005-06-18 15:45:05 urchlay Exp $ // $Id: PromptWidget.cxx,v 1.11 2005-06-21 00:13:49 urchlay Exp $
// //
// Based on code from ScummVM - Scumm Interpreter // Based on code from ScummVM - Scumm Interpreter
// Copyright (C) 2002-2004 The ScummVM project // Copyright (C) 2002-2004 The ScummVM project
@ -138,6 +138,7 @@ void PromptWidget::handleMouseWheel(int x, int y, int direction)
} }
void PromptWidget::printPrompt() { void PromptWidget::printPrompt() {
print( instance()->debugger().showWatches() );
print( instance()->debugger().state() ); print( instance()->debugger().state() );
print(PROMPT); print(PROMPT);
_promptStartPos = _promptEndPos = _currentPos; _promptStartPos = _promptEndPos = _currentPos;