Implemented quite a few debugger commands, plus machinery within

Debugger class to support them.


git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@487 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
urchlay 2005-06-13 02:47:44 +00:00
parent 58ef675a78
commit 536b65f4bc
7 changed files with 295 additions and 24 deletions

View File

@ -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.1 2005-06-12 18:18:00 stephena Exp $
// $Id: Debugger.cxx,v 1.2 2005-06-13 02:47:44 urchlay Exp $
//============================================================================
#include "bspf.hxx"
@ -108,6 +108,9 @@ const string Debugger::state()
result += to_hex_8(myDebugger->sp());
result += " P=";
result += to_hex_8(myDebugger->ps());
result += "/";
formatFlags(myDebugger->ps(), buf);
result += buf;
result += "\n ";
myDebugger->disassemble(myDebugger->pc(), buf);
result += buf;
@ -115,6 +118,56 @@ const string Debugger::state()
return result;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::reset() {
int pc = myDebugger->dPeek(0xfffc);
myDebugger->pc(pc);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::formatFlags(int f, char *out) {
// NV-BDIZC
if(f & 128)
out[0] = 'N';
else
out[0] = 'n';
if(f & 64)
out[1] = 'V';
else
out[1] = 'v';
out[2] = '-';
if(f & 16)
out[3] = 'B';
else
out[3] = 'b';
if(f & 8)
out[4] = 'D';
else
out[4] = 'd';
if(f & 4)
out[5] = 'I';
else
out[5] = 'i';
if(f & 2)
out[6] = 'Z';
else
out[6] = 'z';
if(f & 1)
out[7] = 'C';
else
out[7] = 'c';
out[8] = '\0';
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 Debugger::readRAM(uInt16 addr)
{
@ -127,6 +180,18 @@ void Debugger::writeRAM(uInt16 addr, uInt8 value)
mySystem->poke(addr + kRamStart, value);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const string Debugger::setRAM(int argCount, int *args) {
int address = args[0];
for(int i=1; i<argCount; i++)
mySystem->poke(address++, args[i]);
string ret = "changed ";
ret += args[0];
ret += " locations";
return ret;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const string Debugger::dumpRAM(uInt16 start)
{
@ -178,7 +243,81 @@ void Debugger::quit()
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::trace()
void Debugger::step()
{
mySystem->m6502().execute(1);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// trace is just like step, except it treats a subroutine call as one
// instruction.
// This implementation is not perfect: it just watches the program counter,
// instead of tracking (possibly) nested JSR/RTS pairs. In particular, it
// will fail for recursive subroutine calls. However, with 128 bytes of RAM
// to share between stack and variables, I doubt any 2600 games will ever
// use recursion...
void Debugger::trace()
{
// 32 is the 6502 JSR instruction:
if(mySystem->peek(myDebugger->pc()) == 32) {
int targetPC = myDebugger->pc() + 3; // return address
while(myDebugger->pc() != targetPC)
mySystem->m6502().execute(1);
} else {
step();
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::setA(int a) {
myDebugger->a(a);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::setX(int x) {
myDebugger->x(x);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::setY(int y) {
myDebugger->y(y);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::setS(int sp) {
myDebugger->sp(sp);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::setPC(int pc) {
myDebugger->pc(pc);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// NV-BDIZC
void Debugger::toggleC() {
myDebugger->ps( myDebugger->ps() ^ 0x01 );
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::toggleZ() {
myDebugger->ps( myDebugger->ps() ^ 0x02 );
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::toggleD() {
myDebugger->ps( myDebugger->ps() ^ 0x08 );
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::toggleV() {
myDebugger->ps( myDebugger->ps() ^ 0x40 );
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::toggleN() {
myDebugger->ps( myDebugger->ps() ^ 0x80 );
}

View File

@ -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.1 2005-06-12 18:18:00 stephena Exp $
// $Id: Debugger.hxx,v 1.2 2005-06-13 02:47:44 urchlay Exp $
//============================================================================
#ifndef DEBUGGER_HXX
@ -46,7 +46,7 @@ enum {
for all debugging operations in Stella (parser, 6502 debugger, etc).
@author Stephen Anthony
@version $Id: Debugger.hxx,v 1.1 2005-06-12 18:18:00 stephena Exp $
@version $Id: Debugger.hxx,v 1.2 2005-06-13 02:47:44 urchlay Exp $
*/
class Debugger : public DialogContainer
{
@ -116,8 +116,25 @@ class Debugger : public DialogContainer
uInt8 readRAM(uInt16 addr);
void writeRAM(uInt16 addr, uInt8 value);
// set a bunch of RAM locations at once
const string setRAM(int argCount, int *args);
void quit();
void trace();
void step();
void setA(int a);
void setX(int x);
void setY(int y);
void setS(int sp);
void setPC(int pc);
void toggleC();
void toggleZ();
void toggleN();
void toggleV();
void toggleD();
void reset();
void formatFlags(int f, char *out);
protected:
Console* myConsole;

View File

@ -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.1 2005-06-12 18:18:00 stephena Exp $
// $Id: DebuggerParser.cxx,v 1.2 2005-06-13 02:47:44 urchlay Exp $
//============================================================================
#include "bspf.hxx"
@ -61,15 +61,19 @@ void DebuggerParser::getArgs(const string& command) {
int deref = 0;
int curArg = 0;
argCount = 0;
verb = "";
const char *c = command.c_str();
cerr << "Parsing \"" << command << "\"" << endl;
// cerr << "Parsing \"" << command << "\"" << endl;
while(*c != '\0') {
// cerr << "State " << state << ", *c " << *c << endl;
switch(state) {
case kIN_COMMAND:
if(*c == ' ') state = kIN_SPACE;
if(*c == ' ')
state = kIN_SPACE;
else
verb += *c;
c++;
break;
@ -115,26 +119,130 @@ void DebuggerParser::getArgs(const string& command) {
// for(int i=0; i<argCount; i++)
// cerr << "args[" << i << "] == " << args[i] << endl;
cerr << endl;
// cerr << endl;
}
bool DebuggerParser::subStringMatch(const string& needle, const string& haystack) {
const char *hs = haystack.c_str();
const char *n = needle.c_str();
if(strncasecmp(n, hs, strlen(n)) == 0)
return true;
return false;
}
string DebuggerParser::run(const string& command) {
string result;
getArgs(command);
if(command == "quit") {
// TODO: use lookup table to determine which command to run
// "verb" is the command, stripped of any arguments.
// In the if/else below, put shorter command names first.
// In case of conflicts (e.g. user enters "t", possible
// commands are "tia" and "trace"), try to guess which
// will be used more often, and put it first. The user
// can always disambiguate: "ti" is short for "tia", or
// "tr" for "trace".
// TODO: de-uglify this somehow. (it may not be worth doing?)
if(subStringMatch(verb, "quit")) {
debugger->quit();
return "";
} else if(command == "trace") {
} else if(subStringMatch(verb, "a")) {
if(argCount == 1)
if(args[0] <= 0xff)
debugger->setA(args[0]);
else
return "value out of range (must be 00 - ff)";
else
return "one argument required";
} else if(subStringMatch(verb, "c")) {
debugger->toggleC();
} else if(subStringMatch(verb, "z")) {
debugger->toggleZ();
} else if(subStringMatch(verb, "n")) {
debugger->toggleN();
} else if(subStringMatch(verb, "v")) {
debugger->toggleV();
} else if(subStringMatch(verb, "d")) {
debugger->toggleD();
} else if(subStringMatch(verb, "pc")) {
if(argCount == 1)
debugger->setPC(args[0]);
else
return "one argument required";
} else if(subStringMatch(verb, "x")) {
if(argCount == 1)
if(args[0] <= 0xff)
debugger->setX(args[0]);
else
return "value out of range (must be 00 - ff)";
else
return "one argument required";
} else if(subStringMatch(verb, "y")) {
if(argCount == 1)
if(args[0] <= 0xff)
debugger->setY(args[0]);
else
return "value out of range (must be 00 - ff)";
else
return "one argument required";
} else if(subStringMatch(verb, "s")) {
if(argCount == 1)
if(args[0] <= 0xff)
debugger->setS(args[0]);
else
return "value out of range (must be 00 - ff)";
else
return "one argument required";
} else if(subStringMatch(verb, "step")) {
if(argCount > 0)
return "step takes no arguments";
debugger->step();
result = "OK";
} else if(subStringMatch(verb, "trace")) {
if(argCount > 0)
return "trace takes no arguments";
debugger->trace();
result = "OK";
} else if(command == "ram") {
result = debugger->dumpRAM(kRamStart);
} else if(command == "tia") {
} else if(subStringMatch(verb, "ram")) {
if(argCount == 0)
result = debugger->dumpRAM(kRamStart);
else if(argCount == 1)
return "missing data (need 2 or more args)";
else
result = debugger->setRAM(argCount, args);
} else if(subStringMatch(verb, "tia")) {
result = debugger->dumpTIA();
} else if(subStringMatch(verb, "reset")) {
debugger->reset();
} else if(subStringMatch(verb, "help") || verb == "?") {
// please leave each option on its own line so they're
// easy to sort - bkw
return
"a xx - Set Accumulator to xx\n"
// "break - Show all breakpoints\n"
// "break xx - Set/clear breakpoint at address xx\n"
"c - Toggle Carry Flag\n"
"d - Toggle Decimal Flag\n"
"n - Toggle Negative Flag\n"
"pc xx - Set Program Counter to xx\n"
"ram - Show RIOT RAM contents\n"
"ram xx yy - Set RAM location xx to value yy (multiple values may be given)\n"
"reset - Jump to 6502 init vector (does not reset TIA/RIOT)\n"
"s xx - Set Stack Pointer to xx\n"
"step - Single-step\n"
"tia - Show TIA register contents\n"
"trace - Single-step treating subroutine calls as one instruction\n"
"v - Toggle Overflow Flag\n"
"x xx - Set X register to xx\n"
"y xx - Set Y register to xx\n"
"z - Toggle Zero Flag\n"
;
} else {
result = "unimplemented command";
return "unimplemented command (try \"help\")";
}
result += debugger->state();

View File

@ -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.1 2005-06-12 18:18:00 stephena Exp $
// $Id: DebuggerParser.hxx,v 1.2 2005-06-13 02:47:44 urchlay Exp $
//============================================================================
#ifndef DEBUGGER_PARSER_HXX
@ -37,15 +37,13 @@ class DebuggerParser
private:
int DebuggerParser::conv_hex_digit(char d);
bool DebuggerParser::subStringMatch(const string& needle, const string& haystack);
Debugger* debugger;
DebuggerCommand *changeCmd;
DebuggerCommand *traceCmd;
DebuggerCommand *stepCmd;
DebuggerCommand *quitCmd;
bool done;
string verb;
int args[10]; // FIXME: should be dynamic
int argCount;
};

View File

@ -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: D6502.cxx,v 1.1.1.1 2001-12-27 19:54:29 bwmott Exp $
// $Id: D6502.cxx,v 1.2 2005-06-13 02:47:44 urchlay Exp $
//============================================================================
#include <stdio.h>
@ -39,6 +39,12 @@ static uInt16 dpeek(System* system, uInt16 address)
(((uInt16)system->peek(address + 1)) << 8);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt16 D6502::dPeek(uInt16 address)
{
return dpeek(mySystem, address);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt16 D6502::disassemble(uInt16 address, char* buffer)
{

View File

@ -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: D6502.hxx,v 1.1.1.1 2001-12-27 19:54:29 bwmott Exp $
// $Id: D6502.hxx,v 1.2 2005-06-13 02:47:44 urchlay Exp $
//============================================================================
#ifndef D6502_HXX
@ -30,7 +30,7 @@ class System;
basic functionality needed for interactive debuggers.
@author Bradford W. Mott
@version $Id: D6502.hxx,v 1.1.1.1 2001-12-27 19:54:29 bwmott Exp $
@version $Id: D6502.hxx,v 1.2 2005-06-13 02:47:44 urchlay Exp $
*/
class D6502
{
@ -144,6 +144,8 @@ class D6502
*/
void y(uInt8 value);
uInt16 D6502::dPeek(uInt16 address);
protected:
// Pointer to the system I'm debugging
System* mySystem;

View File

@ -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.1 2005-06-10 17:46:06 stephena Exp $
// $Id: PromptWidget.cxx,v 1.2 2005-06-13 02:47:44 urchlay Exp $
//
// Based on code from ScummVM - Scumm Interpreter
// Copyright (C) 2002-2004 The ScummVM project
@ -79,6 +79,7 @@ PromptWidget::PromptWidget(GuiObject* boss, int x, int y, int w, int h)
string version = string("Stella version ") + STELLA_VERSION + "\n";
print(version.c_str());
print("Debugger is ready\n");
print( instance()->debugger().state() + "\n"); // FIXME: this doesn't work yet
print(PROMPT);
_promptStartPos = _promptEndPos = _currentPos;
}