Reworked argument processor. Now it can be called as a public method from

another class. Also added decimal support, plus default base support. As
a side benefit, there's much better syntax checking.

Also you can now do a word-pointer dereference with "@address". This
works just like "*address" except that it returns a 16-bit value.


git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@529 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
urchlay 2005-06-19 08:29:40 +00:00
parent cb6232cc25
commit b5b07d41ed
4 changed files with 193 additions and 121 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.17 2005-06-18 17:28:18 urchlay Exp $
// $Id: Debugger.cxx,v 1.18 2005-06-19 08:29:39 urchlay Exp $
//============================================================================
#include "bspf.hxx"
@ -468,3 +468,8 @@ void Debugger::clearAllBreakPoints() {
int Debugger::peek(int addr) {
return mySystem->peek(addr);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int Debugger::dpeek(int addr) {
return mySystem->peek(addr) | (mySystem->peek(addr+1) << 8);
}

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.15 2005-06-18 17:28:18 urchlay Exp $
// $Id: Debugger.hxx,v 1.16 2005-06-19 08:29:39 urchlay Exp $
//============================================================================
#ifndef DEBUGGER_HXX
@ -49,7 +49,7 @@ enum {
for all debugging operations in Stella (parser, 6502 debugger, etc).
@author Stephen Anthony
@version $Id: Debugger.hxx,v 1.15 2005-06-18 17:28:18 urchlay Exp $
@version $Id: Debugger.hxx,v 1.16 2005-06-19 08:29:39 urchlay Exp $
*/
class Debugger : public DialogContainer
{
@ -173,6 +173,7 @@ class Debugger : public DialogContainer
int getP();
int getS();
int peek(int addr);
int dpeek(int addr);
void toggleC();
void toggleZ();
void toggleN();

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.14 2005-06-18 19:00:44 urchlay Exp $
// $Id: DebuggerParser.cxx,v 1.15 2005-06-19 08:29:40 urchlay Exp $
//============================================================================
#include "bspf.hxx"
@ -26,8 +26,7 @@
enum {
kIN_COMMAND,
kIN_SPACE,
kIN_ARG_START,
kIN_ARG_CONT
kIN_ARG
};
@ -35,19 +34,12 @@ DebuggerParser::DebuggerParser(Debugger* d)
: debugger(d)
{
done = false;
defaultBase = kBASE_16;
}
DebuggerParser::~DebuggerParser() {
}
string DebuggerParser::currentAddress() {
return "currentAddress()";
}
void DebuggerParser::setDone() {
done = true;
}
int DebuggerParser::conv_hex_digit(char d) {
if(d >= '0' && d <= '9')
return d - '0';
@ -58,34 +50,75 @@ int DebuggerParser::conv_hex_digit(char d) {
else return -1;
}
// Given a string argument that's either a label,
// hex value, or a register, either dereference the label or convert
// the hex to an int. Returns -1 on error.
int DebuggerParser::decipher_arg(string &arg, bool deref, bool lobyte, bool hibyte, bool bin)
{
const char *a = arg.c_str();
int address;
void DebuggerParser::setBase(int base) {
defaultBase = base;
}
// cerr << "decipher_arg: arg==" << arg << ", deref==" << deref << ", lobyte==" << lobyte << ", hibyte==" << hibyte << ", bin==" << bin << endl;
// Evaluate expression.
int DebuggerParser::decipher_arg(string &arg) {
bool derefByte=false, derefWord=false, lobyte=false, hibyte=false, bin=false, dec=false;
int result;
if(defaultBase == kBASE_2) {
bin=true; dec=false;
} else if(defaultBase == kBASE_10) {
bin=false; dec=true;
} else {
bin=false; dec=false;
}
if(arg.substr(0, 1) == "*") {
derefByte = true;
arg.erase(0, 1);
} else if(arg.substr(0, 1) == "@") {
derefWord = true;
arg.erase(0, 1);
}
if(arg.substr(0, 1) == "<") {
lobyte = true;
arg.erase(0, 1);
} else if(arg.substr(0, 1) == ">") {
hibyte = true;
arg.erase(0, 1);
}
if(arg.substr(0, 1) == "%") {
bin = true;
arg.erase(0, 1);
} else if(arg.substr(0, 1) == "#") {
dec = true;
arg.erase(0, 1);
} else if(arg.substr(0, 1) == "$") {
dec = false;
bin = false;
arg.erase(0, 1);
}
// sanity check mutually exclusive options:
if(derefByte && derefWord) return -1;
if(lobyte && hibyte) return -1;
if(bin && dec) return -1;
// Special cases (registers):
if(arg == "a") address = debugger->getA();
else if(arg == "x") address = debugger->getX();
else if(arg == "y") address = debugger->getY();
else if(arg == "p") address = debugger->getP();
else if(arg == "s") address = debugger->getS();
else if(arg == "pc") address = debugger->getPC();
else { // normal addresses: check for label first
address = debugger->equateList->getAddress(a);
if(arg == "a") result = debugger->getA();
else if(arg == "x") result = debugger->getX();
else if(arg == "y") result = debugger->getY();
else if(arg == "p") result = debugger->getP();
else if(arg == "s") result = debugger->getS();
else if(arg == "pc" || arg == ".") result = debugger->getPC();
else { // Not a special, must be a regular arg: check for label first
const char *a = arg.c_str();
result = debugger->equateList->getAddress(a);
if(address < 0) { // if not label, must be a number
if(result < 0) { // if not label, must be a number
if(bin) { // treat as binary
address = 0;
result = 0;
while(*a != '\0') {
address <<= 1;
result <<= 1;
switch(*a++) {
case '1':
address++;
result++;
break;
case '0':
@ -95,32 +128,59 @@ int DebuggerParser::decipher_arg(string &arg, bool deref, bool lobyte, bool hiby
return -1;
}
}
} else { // not binary, must be hex.
address = 0;
} else if(dec) {
result = 0;
while(*a != '\0') {
int digit = (*a++) - '0';
if(digit < 0 || digit > 9)
return -1;
result = (result * 10) + digit;
}
} else { // must be hex.
result = 0;
while(*a != '\0') {
int hex = conv_hex_digit(*a++);
if(hex < 0)
return -1;
address = (address << 4) + hex;
result = (result << 4) + hex;
}
}
}
}
if(lobyte && hibyte) return -1;
else if(lobyte) address &= 0xff;
else if(hibyte) address = (address >> 8) & 0xff;
if(lobyte) result &= 0xff;
else if(hibyte) result = (result >> 8) & 0xff;
// dereference if we're supposed to:
if(deref) address = debugger->peek(address);
if(derefByte) result = debugger->peek(result);
if(derefWord) result = debugger->dpeek(result);
return address;
return result;
}
// The GUI uses this:
bool DebuggerParser::parseArgument(
string& arg, int *value, char *rendered)
{
*value = decipher_arg(arg);
if(*value == -1) {
sprintf(rendered, "error");
return false;
}
if(*value < 0x100)
sprintf(rendered, "%02x", *value);
else
sprintf(rendered, "%04x", *value);
return true;
}
bool DebuggerParser::getArgs(const string& command) {
int state = kIN_COMMAND;
bool deref = false, lobyte = false, hibyte = false, bin = false;
string curArg = "";
argCount = 0;
verb = "";
@ -128,7 +188,9 @@ bool DebuggerParser::getArgs(const string& command) {
// cerr << "Parsing \"" << command << "\"" << endl;
while(*c != '\0') {
// First, pick apart string into space-separated tokens.
// The first token is the command verb, the rest go in an array
do {
// cerr << "State " << state << ", *c '" << *c << "'" << endl;
switch(state) {
case kIN_COMMAND:
@ -136,75 +198,43 @@ bool DebuggerParser::getArgs(const string& command) {
state = kIN_SPACE;
else
verb += *c;
c++;
break;
case kIN_SPACE:
if(*c == ' ')
c++;
else
state = kIN_ARG_START;
if(*c != ' ')
state = kIN_ARG;
curArg += *c;
break;
case kIN_ARG_START:
deref = lobyte = hibyte = false;
curArg = "";
state = kIN_ARG_CONT;
if(*c == '*') {
deref = true;
c++;
}
if(*c == '<') {
lobyte = true;
c++;
}
if(*c == '>') {
hibyte = true;
c++;
}
if(*c == '%') {
bin = true;
c++;
}
// FALL THROUGH!
case kIN_ARG_CONT:
if(isalpha(*c) || isdigit(*c) || *c == '_') {
// cerr << (*c) << ", " << "curArg: " << curArg << endl;
curArg += *c++;
} else {
// cerr << "end-of-arg: " << (*c) << ", " << "curArg: " << curArg << endl;
int a = decipher_arg(curArg, deref, lobyte, hibyte, bin);
if(a < 0)
return false;
args[argCount++] = a;
curArg = "";
case kIN_ARG:
if(*c == ' ' || *c == '\0') {
state = kIN_SPACE;
argStrings[argCount++] = curArg;
curArg = "";
} else {
curArg += *c;
}
}
break;
} // switch(state)
if(argCount == kMAX_ARGS) {
cerr << "reached max " << kMAX_ARGS << " args" << endl;
return true;
}
} while(*c++ != '\0');
for(int i=0; i<argCount; i++)
//cerr << "argStrings[" << i << "] == \"" << argStrings[i] << "\"" << endl;
// Now decipher each argument, in turn.
for(int i=0; i<argCount; i++) {
curArg = argStrings[i];
if( (args[i] = decipher_arg(curArg)) < 0) {
return false;
}
//cerr << "args[" << i << "] == " << args[i] << endl;
}
// pick up last arg, if any:
if(state == kIN_ARG_CONT || state == kIN_ARG_START)
if( (args[argCount++] = decipher_arg(curArg, deref, lobyte, hibyte, bin)) < 0)
return false;
// for(int i=0; i<argCount; i++)
// cerr << "args[" << i << "] == " << args[i] << endl;
// cerr << endl;
return true;
}
@ -254,7 +284,7 @@ string DebuggerParser::disasm() {
}
string DebuggerParser::eval() {
char buf[10];
char buf[50];
string ret;
for(int i=0; i<argCount; i++) {
char *label = debugger->equates()->getLabel(args[i]);
@ -262,16 +292,17 @@ string DebuggerParser::eval() {
ret += label;
ret += ": ";
}
ret += "$";
if(args[i] < 0x100) {
ret += Debugger::to_hex_8(args[i]);
ret += " ";
ret += " %";
ret += Debugger::to_bin_8(args[i]);
} else {
ret += Debugger::to_hex_16(args[i]);
ret += " ";
ret += " %";
ret += Debugger::to_bin_16(args[i]);
}
sprintf(buf, " %d", args[i]);
sprintf(buf, " (dec %d)", args[i]);
ret += buf;
if(i != argCount - 1) ret += "\n";
}
@ -281,16 +312,15 @@ string DebuggerParser::eval() {
string DebuggerParser::run(const string& command) {
string result;
// special case command, takes a filename instead of an address:
if(subStringMatch("loadsym ", command)) {
result = command;
result.erase(0, 8);
result = debugger->equateList->loadFile(result);
if(!getArgs(command)) {
// commands that take filenames or other arbitrary strings go here.
if(subStringMatch(verb, "loadsym")) {
result = debugger->equateList->loadFile(argStrings[0]);
return result;
}
if(!getArgs(command))
} else {
return "invalid label or address";
}
}
// "verb" is the command, stripped of any arguments.
// In the if/else below, put shorter command names first.
@ -404,11 +434,34 @@ string DebuggerParser::run(const string& command) {
} else if(subStringMatch(verb, "clearbreaks")) {
debugger->clearAllBreakPoints();
return "cleared all breakpoints";
} else if(subStringMatch(verb, "eval")) {
} else if(subStringMatch(verb, "print")) {
if(argCount < 1)
return "one or more arguments required";
else
return eval();
} else if(subStringMatch(verb, "base")) {
switch(args[0]) {
case 2:
setBase(kBASE_2);
break;
case 10:
setBase(kBASE_10);
break;
case 16:
setBase(kBASE_16);
break;
default:
return "invalid base (must be #2, #10, or #16)";
break;
}
char buf[5];
sprintf(buf, "#%2d", args[0]);
result += "Set base ";
result += buf;
return result;
} else if(subStringMatch(verb, "quit") || subStringMatch(verb, "run")) {
debugger->quit();
return "";
@ -420,6 +473,7 @@ string DebuggerParser::run(const string& command) {
"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"
@ -427,21 +481,25 @@ string DebuggerParser::run(const string& command) {
"d - Toggle Decimal Flag\n"
"disasm - Disassemble (from current PC)\n"
"disasm xx - Disassemble (from address xx)\n"
"eval xx - Evaluate expression xx\n"
"frame - Advance to next TIA frame, then break\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"

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.8 2005-06-18 19:00:44 urchlay Exp $
// $Id: DebuggerParser.hxx,v 1.9 2005-06-19 08:29:40 urchlay Exp $
//============================================================================
#ifndef DEBUGGER_PARSER_HXX
@ -26,21 +26,27 @@ class Debugger;
#define kMAX_ARGS 100
enum {
kBASE_16,
kBASE_10,
kBASE_2
};
class DebuggerParser
{
public:
DebuggerParser(Debugger* debugger);
~DebuggerParser();
string currentAddress();
void setDone();
string run(const string& command);
bool getArgs(const string& command);
bool parseArgument(string& arg, int *value, char *rendered);
void setBase(int base);
private:
int DebuggerParser::conv_hex_digit(char d);
bool DebuggerParser::subStringMatch(const string& needle, const string& haystack);
int decipher_arg(string &arg, bool deref, bool lobyte, bool hibyte, bool bin);
bool getArgs(const string& command);
int conv_hex_digit(char d);
bool subStringMatch(const string& needle, const string& haystack);
int decipher_arg(string &arg);
string disasm();
string listBreaks();
string eval();
@ -51,7 +57,9 @@ class DebuggerParser
string verb;
int args[kMAX_ARGS+1]; // FIXME: should be dynamic
string argStrings[kMAX_ARGS+1];
int argCount;
int defaultBase;
};
#endif