mirror of https://github.com/stella-emu/stella.git
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:
parent
5433918ba7
commit
9a2d410a4d
|
@ -13,7 +13,7 @@
|
|||
// See the file "license" for information on usage and redistribution of
|
||||
// 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"
|
||||
|
@ -130,6 +130,11 @@ const string Debugger::run(const string& command)
|
|||
return myParser->run(command);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
const string Debugger::valueToString(int value) {
|
||||
return valueToString(value, kBASE_DEFAULT);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
const string Debugger::valueToString(int value, BaseFormat outputBase)
|
||||
{
|
||||
|
@ -171,17 +176,17 @@ const string Debugger::state()
|
|||
|
||||
//cerr << "state(): pc is " << myDebugger->pc() << endl;
|
||||
result += "\nPC=";
|
||||
result += to_hex_16(myDebugger->pc());
|
||||
result += valueToString(myDebugger->pc());
|
||||
result += " A=";
|
||||
result += to_hex_8(myDebugger->a());
|
||||
result += valueToString(myDebugger->a());
|
||||
result += " X=";
|
||||
result += to_hex_8(myDebugger->x());
|
||||
result += valueToString(myDebugger->x());
|
||||
result += " Y=";
|
||||
result += to_hex_8(myDebugger->y());
|
||||
result += valueToString(myDebugger->y());
|
||||
result += " S=";
|
||||
result += to_hex_8(myDebugger->sp());
|
||||
result += valueToString(myDebugger->sp());
|
||||
result += " P=";
|
||||
result += to_hex_8(myDebugger->ps());
|
||||
result += valueToString(myDebugger->ps());
|
||||
result += "/";
|
||||
formatFlags(myDebugger->ps(), buf);
|
||||
result += buf;
|
||||
|
@ -343,9 +348,11 @@ void Debugger::quit()
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Debugger::step()
|
||||
int Debugger::step()
|
||||
{
|
||||
int cyc = mySystem->cycles();
|
||||
mySystem->m6502().execute(1);
|
||||
return mySystem->cycles() - cyc;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -361,15 +368,17 @@ void Debugger::step()
|
|||
|
||||
// FIXME: TIA framebuffer should be updated during tracing!
|
||||
|
||||
void Debugger::trace()
|
||||
int Debugger::trace()
|
||||
{
|
||||
// 32 is the 6502 JSR instruction:
|
||||
if(mySystem->peek(myDebugger->pc()) == 32) {
|
||||
int cyc = mySystem->cycles();
|
||||
int targetPC = myDebugger->pc() + 3; // return address
|
||||
while(myDebugger->pc() != targetPC)
|
||||
mySystem->m6502().execute(1);
|
||||
return mySystem->cycles() - cyc;
|
||||
} else {
|
||||
step();
|
||||
return step();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -542,3 +551,8 @@ bool Debugger::setHeight(int height)
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
string Debugger::showWatches() {
|
||||
return myParser->showWatches();
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
// See the file "license" for information on usage and redistribution of
|
||||
// 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
|
||||
|
@ -50,7 +50,7 @@ enum {
|
|||
for all debugging operations in Stella (parser, 6502 debugger, etc).
|
||||
|
||||
@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
|
||||
{
|
||||
|
@ -127,6 +127,7 @@ class Debugger : public DialogContainer
|
|||
int stringToValue(const string& stringval)
|
||||
{ return myParser->decipher_arg(stringval); }
|
||||
const string valueToString(int value, BaseFormat outputBase);
|
||||
const string valueToString(int value);
|
||||
|
||||
void toggleBreakPoint(int bp);
|
||||
bool breakPoint(int bp);
|
||||
|
@ -161,8 +162,8 @@ class Debugger : public DialogContainer
|
|||
|
||||
bool start();
|
||||
void quit();
|
||||
void trace();
|
||||
void step();
|
||||
int trace();
|
||||
int step();
|
||||
void setA(int a);
|
||||
void setX(int x);
|
||||
void setY(int y);
|
||||
|
@ -189,6 +190,7 @@ class Debugger : public DialogContainer
|
|||
void formatFlags(int f, char *out);
|
||||
EquateList *equates();
|
||||
PromptWidget *prompt();
|
||||
string showWatches();
|
||||
|
||||
protected:
|
||||
Console* myConsole;
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
// See the file "license" for information on usage and redistribution of
|
||||
// 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"
|
||||
|
@ -35,6 +35,8 @@ DebuggerParser::DebuggerParser(Debugger* d)
|
|||
{
|
||||
done = false;
|
||||
defaultBase = kBASE_16;
|
||||
for(int i=0; i<kMAX_WATCHES; i++)
|
||||
watches[i] = new string;
|
||||
}
|
||||
|
||||
DebuggerParser::~DebuggerParser() {
|
||||
|
@ -201,7 +203,7 @@ bool DebuggerParser::getArgs(const string& command) {
|
|||
}
|
||||
} while(*c++ != '\0');
|
||||
|
||||
for(int i=0; i<argCount; i++)
|
||||
//for(int i=0; i<argCount; i++)
|
||||
//cerr << "argStrings[" << i << "] == \"" << argStrings[i] << "\"" << endl;
|
||||
|
||||
// Now decipher each argument, in turn.
|
||||
|
@ -261,6 +263,22 @@ string DebuggerParser::disasm() {
|
|||
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() {
|
||||
char buf[50];
|
||||
string ret;
|
||||
|
@ -280,13 +298,66 @@ string DebuggerParser::eval() {
|
|||
ret += " %";
|
||||
ret += Debugger::to_bin_16(args[i]);
|
||||
}
|
||||
sprintf(buf, " (dec %d)", args[i]);
|
||||
sprintf(buf, " #%d", args[i]);
|
||||
ret += buf;
|
||||
if(i != argCount - 1) ret += "\n";
|
||||
}
|
||||
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 result;
|
||||
|
||||
|
@ -358,15 +429,21 @@ string DebuggerParser::run(const string& command) {
|
|||
else
|
||||
return "one argument required";
|
||||
} else if(subStringMatch(verb, "step")) {
|
||||
char buf[12];
|
||||
if(argCount > 0)
|
||||
return "step takes no arguments";
|
||||
debugger->step();
|
||||
result = "OK";
|
||||
sprintf(buf, "#%d", debugger->step());
|
||||
result = "executed ";
|
||||
result += buf;
|
||||
result += " cycles";
|
||||
} else if(subStringMatch(verb, "trace")) {
|
||||
char buf[12];
|
||||
if(argCount > 0)
|
||||
return "trace takes no arguments";
|
||||
debugger->trace();
|
||||
result = "OK";
|
||||
sprintf(buf, "#%d", debugger->trace());
|
||||
result = "executed ";
|
||||
result += buf;
|
||||
result += " cycles";
|
||||
} else if(subStringMatch(verb, "ram")) {
|
||||
if(argCount == 0)
|
||||
result = debugger->dumpRAM(kRamStart);
|
||||
|
@ -408,9 +485,19 @@ string DebuggerParser::run(const string& command) {
|
|||
debugger->nextFrame();
|
||||
*/
|
||||
debugger->nextFrame();
|
||||
return "OK";
|
||||
return "advanced frame";
|
||||
} else if(subStringMatch(verb, "clearbreaks")) {
|
||||
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")) {
|
||||
if(argCount != 1)
|
||||
return "one argument required";
|
||||
|
@ -423,6 +510,11 @@ string DebuggerParser::run(const string& command) {
|
|||
else
|
||||
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")) {
|
||||
if(argCount < 1)
|
||||
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
|
||||
// easy to sort - bkw
|
||||
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"
|
||||
"prefixed with a * to dereference.\n"
|
||||
"a xx - Set Accumulator to xx\n"
|
||||
"base xx - Set default input base (#2=binary, #10=decimal, #16=hex)\n"
|
||||
"break - Set/clear breakpoint at current PC\n"
|
||||
"break xx - Set/clear breakpoint at address xx\n"
|
||||
"c - Toggle Carry Flag\n"
|
||||
"clearbreaks - Clear all breakpoints\n"
|
||||
"d - Toggle Decimal Flag\n"
|
||||
"disasm - Disassemble (from current PC)\n"
|
||||
"disasm xx - Disassemble (from address xx)\n"
|
||||
"frame - Advance to next TIA frame, then break\n"
|
||||
"height xx - Set height of debugger window in pixels\n"
|
||||
"listbreaks - List all breakpoints\n"
|
||||
"loadsym f - Load DASM symbols from file f\n"
|
||||
"n - Toggle Negative Flag\n"
|
||||
"pc xx - Set Program Counter to xx\n"
|
||||
"print xx - Evaluate and print expression xx\n"
|
||||
"poke xx yy - Write data yy to address xx (may be ROM, TIA, etc)\n"
|
||||
"ram - Show RIOT RAM contents\n"
|
||||
"ram xx yy - Set RAM location xx to value yy (multiple values allowed)\n"
|
||||
"reset - Jump to 6502 init vector (does not reset TIA/RIOT)\n"
|
||||
"run - Exit debugger (back to emulator)\n"
|
||||
"s xx - Set Stack Pointer to xx\n"
|
||||
// "save f - Save console session to file f\n"
|
||||
"step - Single-step\n"
|
||||
"tia - Show TIA register contents\n"
|
||||
"trace - Single-step treating subroutine calls as 1 instruction\n"
|
||||
"v - Toggle Overflow Flag\n"
|
||||
// "watch - Clear watch list\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"
|
||||
"prefixed with a * to dereference, < or > for low/high byte,\n"
|
||||
"and/or $/#/% for hex/dec/binary.\n\n"
|
||||
"a xx - Set Accumulator to xx\n"
|
||||
"base xx - Set default input base (#2=binary, #10=decimal, #16=hex)\n"
|
||||
"break - Set/clear breakpoint at current PC\n"
|
||||
"break xx - Set/clear breakpoint at address xx\n"
|
||||
"c - Toggle Carry Flag\n"
|
||||
"clearbreaks - Clear all breakpoints\n"
|
||||
"cleartraps - Clear all traps\n"
|
||||
"clearwatches - Clear all watches\n"
|
||||
"d - Toggle Decimal Flag\n"
|
||||
"dump xx - Dump 128 bytes of memory starting at xx (may be ROM, TIA, RAM)\n"
|
||||
"delwatch xx - Delete watch xx\n"
|
||||
"disasm - Disassemble (from current PC)\n"
|
||||
"disasm xx - Disassemble (from address xx)\n"
|
||||
"frame - Advance to next TIA frame, then break\n"
|
||||
"height xx - Set height of debugger window in pixels\n"
|
||||
"listbreaks - List all breakpoints\n"
|
||||
"*listtraps - List all traps\n"
|
||||
"loadsym f - Load DASM symbols from file f\n"
|
||||
"n - Toggle Negative Flag\n"
|
||||
"pc xx - Set Program Counter to xx\n"
|
||||
"print xx - Evaluate and print expression xx\n"
|
||||
//"poke xx yy - Write data yy to address xx (may be ROM, TIA, etc)\n"
|
||||
"ram - Show RIOT RAM contents\n"
|
||||
"ram xx yy - Set RAM location xx to value yy (multiple values allowed)\n"
|
||||
"reset - Jump to 6502 init vector (does not reset TIA/RIOT)\n"
|
||||
"run - Exit debugger (back to emulator)\n"
|
||||
"s xx - Set Stack Pointer to xx\n"
|
||||
"*save f - Save console session to file f\n"
|
||||
"step - Single-step\n"
|
||||
"tia - Show TIA register contents\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 {
|
||||
return "unimplemented command (try \"help\")";
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
// See the file "license" for information on usage and redistribution of
|
||||
// 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
|
||||
|
@ -25,6 +25,7 @@ class Debugger;
|
|||
#include "EquateList.hxx"
|
||||
|
||||
#define kMAX_ARGS 100
|
||||
#define kMAX_WATCHES 10
|
||||
|
||||
typedef enum {
|
||||
kBASE_16,
|
||||
|
@ -44,6 +45,11 @@ class DebuggerParser
|
|||
|
||||
void setBase(BaseFormat base) { defaultBase = base; }
|
||||
BaseFormat base() { return defaultBase; }
|
||||
string showWatches();
|
||||
string addWatch(string watch);
|
||||
string delWatch(int which);
|
||||
void delAllWatches();
|
||||
|
||||
|
||||
private:
|
||||
bool getArgs(const string& command);
|
||||
|
@ -52,6 +58,7 @@ class DebuggerParser
|
|||
string disasm();
|
||||
string listBreaks();
|
||||
string eval();
|
||||
string dump();
|
||||
|
||||
Debugger* debugger;
|
||||
|
||||
|
@ -62,6 +69,7 @@ class DebuggerParser
|
|||
string argStrings[kMAX_ARGS+1];
|
||||
int argCount;
|
||||
BaseFormat defaultBase;
|
||||
string *watches[kMAX_WATCHES]; // FIXME: remove the limit sometime
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
// See the file "license" for information on usage and redistribution of
|
||||
// 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
|
||||
// Copyright (C) 2002-2004 The ScummVM project
|
||||
|
@ -138,6 +138,7 @@ void PromptWidget::handleMouseWheel(int x, int y, int direction)
|
|||
}
|
||||
|
||||
void PromptWidget::printPrompt() {
|
||||
print( instance()->debugger().showWatches() );
|
||||
print( instance()->debugger().state() );
|
||||
print(PROMPT);
|
||||
_promptStartPos = _promptEndPos = _currentPos;
|
||||
|
|
Loading…
Reference in New Issue