mirror of https://github.com/stella-emu/stella.git
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:
parent
58ef675a78
commit
536b65f4bc
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue