First pass at making the debugger be asynchronous from the debugger parser,

meaning that if a command is running, it can be interrupted, and Stella
doesn't lock up in the process.  It all feels like sort of a hack, but
for now it's the best we can do.


git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@1183 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
stephena 2006-12-05 22:05:35 +00:00
parent dcb74722ad
commit 3c93be914a
10 changed files with 161 additions and 154 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.105 2006-12-02 23:25:53 stephena Exp $
// $Id: Debugger.cxx,v 1.106 2006-12-05 22:05:33 stephena Exp $
//============================================================================
#include "bspf.hxx"
@ -334,12 +334,6 @@ const string Debugger::run(const string& command)
return myParser->run(command);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::cancel()
{
myParser->cancel();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const string Debugger::valueToString(int value, BaseFormat outputBase)
{
@ -841,10 +835,10 @@ void Debugger::disassemble(IntArray& addr, StringList& addrLabel,
void Debugger::nextScanline(int lines)
{
saveOldState();
// mySystem->unlockDataBus();
// mySystem->unlockDataBus();
unlockState();
myTiaOutput->advanceScanline(lines);
// mySystem->lockDataBus();
// mySystem->lockDataBus();
lockState();
}

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.83 2006-12-02 23:25:53 stephena Exp $
// $Id: Debugger.hxx,v 1.84 2006-12-05 22:05:33 stephena Exp $
//============================================================================
#ifndef DEBUGGER_HXX
@ -44,19 +44,9 @@ class Expression;
#include "Rect.hxx"
#include "bspf.hxx"
typedef multimap<string,string> ListFile;
typedef ListFile::const_iterator ListIter;
typedef map<string,Expression*> FunctionMap;
typedef map<string,string> FunctionDefMap;
enum {
kDebuggerWidth = 1023,
kDebuggerHeight = 700,
kDebuggerLineHeight = 15, // based on the height of the console font
kDebuggerLines = 27,
};
// Constants for RAM area
enum {
kRamStart = 0x80,
@ -79,7 +69,7 @@ typedef uInt16 (Debugger::*DEBUGGER_WORD_METHOD)();
for all debugging operations in Stella (parser, 6502 debugger, etc).
@author Stephen Anthony
@version $Id: Debugger.hxx,v 1.83 2006-12-02 23:25:53 stephena Exp $
@version $Id: Debugger.hxx,v 1.84 2006-12-05 22:05:33 stephena Exp $
*/
class Debugger : public DialogContainer
{
@ -170,9 +160,10 @@ class Debugger : public DialogContainer
const string run(const string& command);
/**
Cancel the currently running debugger command.
Indicate if the debugger is currently running a command
(it shouldn't be exited in this case)
*/
void cancel();
bool isBlocked() { return myParser->commandRunning(); }
/**
Give the contents of the CPU registers and disassembly of
@ -364,7 +355,17 @@ class Debugger : public DialogContainer
const string invIfChanged(int reg, int oldReg);
protected:
private:
enum {
kDebuggerWidth = 1023,
kDebuggerHeight = 700,
kDebuggerLineHeight = 15, // based on the height of the console font
kDebuggerLines = 27,
};
typedef multimap<string,string> ListFile;
typedef ListFile::const_iterator ListIter;
Console* myConsole;
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: DebuggerParser.cxx,v 1.90 2006-12-02 23:25:53 stephena Exp $
// $Id: DebuggerParser.cxx,v 1.91 2006-12-05 22:05:33 stephena Exp $
//============================================================================
#include <fstream>
@ -40,9 +40,10 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DebuggerParser::DebuggerParser(Debugger* d)
: debugger(d)
: debugger(d),
myRunningFlag(false),
myCancelFlag(false)
{
cancelCommand = false;
defaultBase = kBASE_16;
}
@ -100,14 +101,16 @@ string DebuggerParser::run(const string& command)
cerr << "Expression count: " << refCount << endl;
#endif
commandResult = "";
cancelCommand = false;
myCancelFlag = false;
for(int i = 0; i < kNumCommands; ++i)
{
if(verb == commands[i].cmdString)
{
myRunningFlag = true;
if(validateArgs(i))
CALL_METHOD(commands[i].executor);
myRunningFlag = false;
if(commands[i].refreshRequired)
debugger->myBaseDialog->loadConfig();
@ -123,10 +126,8 @@ string DebuggerParser::run(const string& command)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerParser::cancel()
{
cerr << "DebuggerParser::cancel()\n";
// Let the running command know it should stop
// Hopefully, it's actually checking this variable!
cancelCommand = true;
// Indicate to any blocking commands that it's time to quit
myCancelFlag = true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -364,62 +365,72 @@ string DebuggerParser::showWatches()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool DebuggerParser::getArgs(const string& command, string& verb)
{
int state = kIN_COMMAND;
int state = kIN_COMMAND, i = 0, length = command.length();
string curArg = "";
verb = "";
const char *c = command.c_str();
argStrings.clear();
args.clear();
// cerr << "Parsing \"" << command << "\"" << endl;
// cerr << "Parsing \"" << command << "\"" << ", length = " << command.length() << endl;
// 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) {
do
{
char c = command[i++];
switch(state)
{
case kIN_COMMAND:
if(*c == ' ')
if(c == ' ')
state = kIN_SPACE;
else
verb += *c;
verb += c;
break;
case kIN_SPACE:
if(*c == '{')
if(c == '{')
state = kIN_BRACE;
else if(*c != ' ') {
else if(c != ' ') {
state = kIN_ARG;
curArg += *c;
curArg += c;
}
break;
case kIN_BRACE:
if(*c == '}' || *c == '\0') {
if(c == '}') {
state = kIN_SPACE;
argStrings.push_back(curArg);
// cerr << "{" << curArg << "}" << endl;
curArg = "";
} else {
curArg += *c;
curArg += c;
}
break;
case kIN_ARG:
if(*c == ' ' || *c == '\0') {
if(c == ' ') {
state = kIN_SPACE;
argStrings.push_back(curArg);
curArg = "";
} else {
curArg += *c;
curArg += c;
}
break;
} // switch(state)
} while(*c++ != '\0');
} // switch(state)
}
while(i < length);
// Take care of the last argument, if there is one
if(curArg != "")
argStrings.push_back(curArg);
argCount = argStrings.size();
/*
cerr << "verb = " << verb << endl;
cerr << "arguments (" << argCount << "):\n";
for(int x = 0; x < argCount; x++)
cerr << "command " << x << ": " << argStrings[x] << endl;
*/
/*
// Now decipher each argument, in turn.
for(int i=0; i<argCount; i++) {
@ -429,12 +440,12 @@ bool DebuggerParser::getArgs(const string& command, string& verb)
}
*/
for(int i=0; i<argCount; i++) {
for(int i = 0; i < argCount; i++) {
int err = YaccParser::parse(argStrings[i].c_str());
if(err) {
args.push_back(-1);
} else {
Expression *e = YaccParser::getResult();
Expression* e = YaccParser::getResult();
args.push_back( e->evaluate() );
delete e;
}
@ -1167,19 +1178,20 @@ void DebuggerParser::executeRunTo()
int cycles = 0, count = 0;
do {
if(myCancelFlag) break;
cycles += debugger->step();
if(++count % 100 == 0)
// This command can potentially block forever
// We should yield to the system, and check for cancellation
if(++count % 10000 == 0)
{
debugger->prompt()->putchar('.');
// This command can potentially block forever
// We should check if the user cancelled it
cerr << "checking for break command ...\n";
if(cancelCommand) break;
debugger->getOSystem()->run();
}
string next = debugger->disassemble(debugger->cpuDebug().pc(), 1);
done = (next.find(argStrings[0]) != string::npos);
} while(!done);
commandResult = "executed ";

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.44 2006-12-02 23:25:53 stephena Exp $
// $Id: DebuggerParser.hxx,v 1.45 2006-12-05 22:05:34 stephena Exp $
//============================================================================
#ifndef DEBUGGER_PARSER_HXX
@ -42,6 +42,9 @@ class DebuggerParser
/** Run the given command, and return the result */
string run(const string& command);
/** Indicate if a command is currently running */
bool commandRunning() { return myRunningFlag; }
/** Cancel the currently running command, if any */
void cancel();
@ -84,7 +87,7 @@ class DebuggerParser
private:
enum {
kNumCommands = 59,
kNumCommands = 59,
kMAX_ARG_TYPES = 10 // TODO: put in separate header file Command.hxx
};
@ -123,12 +126,15 @@ class DebuggerParser
// Pointer to our debugger object
Debugger* debugger;
// Flag indicating whether a currently running command should be cancelled
bool cancelCommand;
// The results of the currently running command
string commandResult;
// Indicates whether a command is currently running, or a cancel
// event has been received
// Commands which expect to block for long periods of time should
// occasionally check the myCancelFlag
bool myRunningFlag, myCancelFlag;
// Arguments in 'int' and 'string' format for the currently running command
IntArray args;
StringList argStrings;

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.10 2006-12-02 23:25:53 stephena Exp $
// $Id: PromptWidget.cxx,v 1.11 2006-12-05 22:05:34 stephena Exp $
//
// Based on code from ScummVM - Scumm Interpreter
// Copyright (C) 2002-2004 The ScummVM project
@ -179,23 +179,16 @@ bool PromptWidget::handleKeyDown(int ascii, int keycode, int modifiers)
if (len > 0)
{
// We have to allocate the string buffer with new, since VC++ sadly does not
// comply to the C++ standard, so we can't use a dynamic sized stack array.
char *str = new char[len + 1];
// Copy the user input to str
// Copy the user input to command
string command;
for (i = 0; i < len; i++)
str[i] = buffer(_promptStartPos + i) & 0x7f;
str[len] = '\0';
command += buffer(_promptStartPos + i) & 0x7f;
// Add the input to the history
addToHistory(str);
addToHistory(command.c_str());
// Pass the command to the debugger, and print the result
print( instance()->debugger().run(str) + "\n");
// Get rid of the string buffer
delete [] str;
print( instance()->debugger().run(command) + "\n");
}
printPrompt();
@ -528,32 +521,26 @@ void PromptWidget::specialKeys(int keycode)
_currentPos = _promptStartPos;
handled = true;
break;
case 'c':
cancelLastCommand();
instance()->debugger().parser()->cancel();
handled = true;
break;
case 'd':
killChar(+1);
handled = true;
break;
case 'e':
_currentPos = _promptEndPos;
handled = true;
break;
case 'k':
killLine(+1);
handled = true;
break;
case 'u':
killLine(-1);
handled = true;
break;
case 'w':
killLastWord();
handled = true;
@ -643,12 +630,6 @@ void PromptWidget::killLastWord()
_promptEndPos -= cnt;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PromptWidget::cancelLastCommand()
{
instance()->debugger().cancel();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PromptWidget::addToHistory(const char *str)
{

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.hxx,v 1.6 2006-12-02 23:25:53 stephena Exp $
// $Id: PromptWidget.hxx,v 1.7 2006-12-05 22:05:34 stephena Exp $
//
// Based on code from ScummVM - Scumm Interpreter
// Copyright (C) 2002-2004 The ScummVM project
@ -70,7 +70,6 @@ class PromptWidget : public Widget, public CommandSender
void killChar(int direction);
void killLine(int direction);
void killLastWord();
void cancelLastCommand();
// History
void addToHistory(const char *str);

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: EventHandler.cxx,v 1.178 2006-12-02 23:25:53 stephena Exp $
// $Id: EventHandler.cxx,v 1.179 2006-12-05 22:05:34 stephena Exp $
//============================================================================
#include <sstream>
@ -2272,6 +2272,10 @@ void EventHandler::leaveDebugMode()
if(myState != S_DEBUGGER)
return;
// If for any reason a command is currently running, we can't exit the debugger
if(myOSystem->debugger().isBlocked())
return;
// Make sure debugger quits in a consistent state
myOSystem->debugger().setQuitState();
@ -2564,7 +2568,7 @@ void EventHandler::setSDLMappings()
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ActionList EventHandler::ourEmulActionList[kEmulActionListSize] = {
EventHandler::ActionList EventHandler::ourEmulActionList[kEmulActionListSize] = {
{ Event::ConsoleSelect, "Select", 0 },
{ Event::ConsoleReset, "Reset", 0 },
{ Event::ConsoleColor, "Color TV", 0 },
@ -2661,7 +2665,7 @@ ActionList EventHandler::ourEmulActionList[kEmulActionListSize] = {
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ActionList EventHandler::ourMenuActionList[kMenuActionListSize] = {
EventHandler::ActionList EventHandler::ourMenuActionList[kMenuActionListSize] = {
{ Event::UIUp, "Move Up", 0 },
{ Event::UIDown, "Move Down", 0 },
{ Event::UILeft, "Move Left", 0 },

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: EventHandler.hxx,v 1.92 2006-12-01 18:30:18 stephena Exp $
// $Id: EventHandler.hxx,v 1.93 2006-12-05 22:05:35 stephena Exp $
//============================================================================
#ifndef EVENTHANDLER_HXX
@ -34,16 +34,6 @@ class DialogContainer;
class EventMappingWidget;
class EventStreamer;
// A wrapper around SDL hat events, so we don't drag SDL
// through all the child classes
enum JoyHat {
kJHatUp,
kJHatDown,
kJHatLeft,
kJHatRight,
kJHatCentered
};
enum MouseButton {
EVENT_LBUTTONDOWN,
EVENT_LBUTTONUP,
@ -53,54 +43,12 @@ enum MouseButton {
EVENT_WHEELUP
};
// Structure used for action menu items
struct ActionList {
Event::Type event;
const char* action;
char* key;
};
enum {
kEmulActionListSize = 81,
kMenuActionListSize = 15
};
enum EventMode {
kEmulationMode = 0, // make sure these are set correctly,
kMenuMode = 1, // since they'll be used as array indices
kNumModes = 2
};
// Joystick related items
enum {
kNumJoysticks = 8,
kNumJoyButtons = 24,
kNumJoyAxis = 16,
kNumJoyHats = 16
};
enum JoyType {
JT_NONE,
JT_REGULAR,
JT_STELLADAPTOR_LEFT,
JT_STELLADAPTOR_RIGHT
};
struct Stella_Joystick {
SDL_Joystick* stick;
JoyType type;
string name;
};
// Used for joystick to mouse emulation
struct JoyMouse {
bool active;
int x, y, x_vel, y_vel, x_max, y_max, x_amt, y_amt, amt,
x_down_count, y_down_count;
unsigned int last_time, delay_time, x_down_time, y_down_time;
int joy_val, old_joy_val;
};
/**
This class takes care of event remapping and dispatching for the
Stella core, as well as keeping track of the current 'mode'.
@ -114,7 +62,7 @@ struct JoyMouse {
mapping can take place.
@author Stephen Anthony
@version $Id: EventHandler.hxx,v 1.92 2006-12-01 18:30:18 stephena Exp $
@version $Id: EventHandler.hxx,v 1.93 2006-12-05 22:05:35 stephena Exp $
*/
class EventHandler
{
@ -460,7 +408,7 @@ class EventHandler
@param value The value on the given hat
*/
void handleJoyHatEvent(int stick, int hat, int value);
/**
Detects and changes the eventhandler state
@ -523,6 +471,51 @@ class EventHandler
void setEventState(State state);
private:
enum {
kEmulActionListSize = 81,
kMenuActionListSize = 15
};
// Structure used for action menu items
struct ActionList {
Event::Type event;
const char* action;
char* key;
};
// Joystick related items
enum {
kNumJoysticks = 8,
kNumJoyButtons = 24,
kNumJoyAxis = 16,
kNumJoyHats = 16
};
enum JoyType {
JT_NONE,
JT_REGULAR,
JT_STELLADAPTOR_LEFT,
JT_STELLADAPTOR_RIGHT
};
struct Stella_Joystick {
SDL_Joystick* stick;
JoyType type;
string name;
};
enum JoyHat {
kJHatUp,
kJHatDown,
kJHatLeft,
kJHatRight,
kJHatCentered
};
struct JoyMouse { // Used for joystick to mouse emulation
bool active;
int x, y, x_vel, y_vel, x_max, y_max, x_amt, y_amt, amt,
x_down_count, y_down_count;
unsigned int last_time, delay_time, x_down_time, y_down_time;
int joy_val, old_joy_val;
};
// Global OSystem object
OSystem* myOSystem;

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: OSystem.cxx,v 1.76 2006-12-02 00:43:50 stephena Exp $
// $Id: OSystem.cxx,v 1.77 2006-12-05 22:05:35 stephena Exp $
//============================================================================
#include <cassert>
@ -524,6 +524,13 @@ bool OSystem::openROM(const string& rom, string& md5, uInt8** image, int* size)
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void OSystem::run() const
{
myEventHandler->poll(0);
myFrameBuffer->update();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void OSystem::setDefaultJoymap()
{

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: OSystem.hxx,v 1.43 2006-10-22 18:58:46 stephena Exp $
// $Id: OSystem.hxx,v 1.44 2006-12-05 22:05:35 stephena Exp $
//============================================================================
#ifndef OSYSTEM_HXX
@ -45,7 +45,7 @@ class VideoDialog;
other objects belong.
@author Stephen Anthony
@version $Id: OSystem.hxx,v 1.43 2006-10-22 18:58:46 stephena Exp $
@version $Id: OSystem.hxx,v 1.44 2006-12-05 22:05:35 stephena Exp $
*/
class OSystem
{
@ -271,6 +271,16 @@ class OSystem
*/
bool openROM(const string& rom, string& md5, uInt8** image, int* size);
/**
Runs through one iteration of the OSystem loop, which consists of
checking for events, rendering the framebuffer, etc.
This method isn't meant to be used from mainLoop(), but instead is
used as a sort of 'yield' function, whereby other parts of the code
may block for a time, and we need to check for cancellation (used
to emulate a poor man's threading system).
*/
void run() const;
public:
//////////////////////////////////////////////////////////////////////
// The following methods are system-specific and must be implemented