Several changes to make entering commands at the debugger prompt

case-insensive.  Currently supported are all commands and
pseudo-registers.  Equates/labels and user-defined functions are
still a WIP, since they're stored in various hashtables accessible
by name, which is still a case-sensitive operation.  In general,
tab completion is mostly case-insensitive.

Changed the delimiter for surrounding lines with spaces from curly
braces ({}) to the apostrophe (').


git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@2115 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
stephena 2010-08-23 16:19:17 +00:00
parent bc28a2408d
commit d664beb45d
5 changed files with 67 additions and 53 deletions

View File

@ -27,7 +27,7 @@ feature that no other 2600 debugger has; it's <b>completely</b> cross-platform.<
breakpoints as you want.</li> breakpoints as you want.</li>
<li>Conditional breakpoints - Break running program when some arbitrary <li>Conditional breakpoints - Break running program when some arbitrary
condition is true (e.g. "breakif {a == $7f &amp;&amp; c}" will break when the Accumulator value is $7f and the Carry flag is true, no matter where condition is true (e.g. "breakif &apos;a == $7f &amp;&amp; c&apos;" will break when the Accumulator value is $7f and the Carry flag is true, no matter where
in the program this happens). Unlike the cond breaks in PCAE, Stella's in the program this happens). Unlike the cond breaks in PCAE, Stella's
are *fast*: the emulation will run at full speed unless you use lots are *fast*: the emulation will run at full speed unless you use lots
of breakif's at the same time, or have a slow CPU.</li> of breakif's at the same time, or have a slow CPU.</li>
@ -387,9 +387,9 @@ we want to apply the ! to the result of the &amp;, not just the first term
<p>"breakif !(*SWCHB&amp;1)" will do the job for us. However, it's an ugly mess <p>"breakif !(*SWCHB&amp;1)" will do the job for us. However, it's an ugly mess
of letters, numbers, and punctuation. We can do a little better:</p> of letters, numbers, and punctuation. We can do a little better:</p>
<p>"breakif { !(*SWCHB &amp; 1 ) }" is a lot more readable, isn't it? If <p>"breakif &apos; !(*SWCHB &amp; 1 ) &apos;" is a lot more readable, isn't it? If
you're going to use readable expressions with spaces in them, you're going to use readable expressions with spaces in them,
enclose the entire expression in curly braces {}.</p> enclose the entire expression in single quotes.</p>
<p>Remember that Stella only checks for input once per frame, so a break <p>Remember that Stella only checks for input once per frame, so a break
condition that depends on input (like SWCHB) will always happen at the condition that depends on input (like SWCHB) will always happen at the
@ -415,7 +415,7 @@ if we wanted to use it again.</p>
"breakif function_name":</p> "breakif function_name":</p>
<pre> <pre>
function gameReset { !(*SWCHB &amp; 1 ) } function gameReset &apos; !(*SWCHB &amp; 1 ) &apos;
breakif gameReset breakif gameReset
</pre> </pre>
@ -426,7 +426,7 @@ if the Game Select switch is pressed. We want to break when the user
presses both Select and Reset:</p> presses both Select and Reset:</p>
<pre> <pre>
breakif { gameReset &amp;&amp; gameSelect } breakif &apos; gameReset &amp;&amp; gameSelect &apos;
</pre> </pre>
<p>User-defined functions appear in "listfunctions", which shows the label <p>User-defined functions appear in "listfunctions", which shows the label
@ -464,7 +464,7 @@ of this file will depend on the version of Stella, as follows:</p>
</tr> </tr>
</table> </table>
<p>Note that these '.stella' script files are only accessed if you enter <p>Note that these '.stella' script files are only accessed if you enter
the debugger at least one during a program run. This means you can create the debugger at least once during a program run. This means you can create
these files, and not worry about slowing down emulation unless you're these files, and not worry about slowing down emulation unless you're
actively using the debugger.</p> actively using the debugger.</p>
@ -491,8 +491,8 @@ Stella debugger.</p>
<tr><td> _joy1up</td><td> !(*SWCHA &amp; $01)</td><td> Right joystick moved up</td></tr> <tr><td> _joy1up</td><td> !(*SWCHA &amp; $01)</td><td> Right joystick moved up</td></tr>
<tr><td> _joy1down</td><td> !(*SWCHA &amp; $02)</td><td> Right joystick moved down</td></tr> <tr><td> _joy1down</td><td> !(*SWCHA &amp; $02)</td><td> Right joystick moved down</td></tr>
<tr><td> _joy1button</td><td> !(*INPT5 &amp; $80)</td><td> Right joystick button pressed</td></tr> <tr><td> _joy1button</td><td> !(*INPT5 &amp; $80)</td><td> Right joystick button pressed</td></tr>
<tr><td> _select</td><td> !(*SWCHB &amp; $01)</td><td> Game Select pressed</td></tr> <tr><td> _select</td><td> !(*SWCHB &amp; $02)</td><td> Game Select pressed</td></tr>
<tr><td> _reset</td><td> !(*SWCHB &amp; $02)</td><td> Game Reset pressed</td></tr> <tr><td> _reset</td><td> !(*SWCHB &amp; $01)</td><td> Game Reset pressed</td></tr>
<tr><td> _color</td><td> *SWCHB &amp; $08</td><td> Color/BW set to Color</td></tr> <tr><td> _color</td><td> *SWCHB &amp; $08</td><td> Color/BW set to Color</td></tr>
<tr><td> _bw</td><td> !(*SWCHB &amp; $08)</td><td> Color/BW set to BW</td></tr> <tr><td> _bw</td><td> !(*SWCHB &amp; $08)</td><td> Color/BW set to BW</td></tr>
<tr><td> _diff0b</td><td> !(*SWCHB &amp; $40)</td><td> Left difficulty set to B (easy)</td></tr> <tr><td> _diff0b</td><td> !(*SWCHB &amp; $40)</td><td> Left difficulty set to B (easy)</td></tr>
@ -534,7 +534,7 @@ beginning of the 64th scanline.</p>
(non-bankswitched) ROMs, it will always contain 0. One useful use is:</p> (non-bankswitched) ROMs, it will always contain 0. One useful use is:</p>
<pre> <pre>
breakif { pc==myLabel &amp;&amp; _bank==1 } breakif &apos; pc==myLabel &amp;&amp; _bank==1 &apos;
</pre> </pre>
<p>This is similar to setting a regular breakpoint, but it will only trigger <p>This is similar to setting a regular breakpoint, but it will only trigger

View File

@ -114,6 +114,8 @@ template<typename T> inline void BSPF_swap(T& a, T& b) { T tmp = a; a = b; b = t
template<typename T> inline T BSPF_abs (T x) { return (x>=0) ? x : -x; } template<typename T> inline T BSPF_abs (T x) { return (x>=0) ? x : -x; }
template<typename T> inline T BSPF_min (T a, T b) { return (a<b) ? a : b; } template<typename T> inline T BSPF_min (T a, T b) { return (a<b) ? a : b; }
template<typename T> inline T BSPF_max (T a, T b) { return (a>b) ? a : b; } template<typename T> inline T BSPF_max (T a, T b) { return (a>b) ? a : b; }
// Test whether two strings are equal (case insensitive)
inline bool BSPF_equalsIgnoreCase(const string& s1, const string& s2) inline bool BSPF_equalsIgnoreCase(const string& s1, const string& s2)
{ {
return BSPF_strcasecmp(s1.c_str(), s2.c_str()) == 0; return BSPF_strcasecmp(s1.c_str(), s2.c_str()) == 0;
@ -123,10 +125,22 @@ inline bool BSPF_equalsIgnoreCase(const char* s1, const char* s2)
return BSPF_strcasecmp(s1, s2) == 0; return BSPF_strcasecmp(s1, s2) == 0;
} }
// Test whether the first string starts with the second one (case insensitive)
inline bool BSPF_startsWithIgnoreCase(const string& s1, const string& s2)
{
return BSPF_strncasecmp(s1.c_str(), s2.c_str(), s2.length()) == 0;
}
inline bool BSPF_startsWithIgnoreCase(const char* s1, const char* s2)
{
return BSPF_strncasecmp(s1, s2, strlen(s2)) == 0;
}
// Test whether two characters are equal (case insensitive)
static bool BSPF_equalsIgnoreCaseChar(char ch1, char ch2) static bool BSPF_equalsIgnoreCaseChar(char ch1, char ch2)
{ {
return toupper((unsigned char)ch1) == toupper((unsigned char)ch2); return toupper((unsigned char)ch1) == toupper((unsigned char)ch2);
} }
// Find location (if any) of the second string within the first
inline size_t BSPF_findIgnoreCase(const string& s1, const string& s2) inline size_t BSPF_findIgnoreCase(const string& s1, const string& s2)
{ {
string::const_iterator pos = std::search(s1.begin(), s1.end(), string::const_iterator pos = std::search(s1.begin(), s1.end(),

View File

@ -108,7 +108,7 @@ string DebuggerParser::run(const string& command)
for(int i = 0; i < kNumCommands; ++i) for(int i = 0; i < kNumCommands; ++i)
{ {
if(verb == commands[i].cmdString) if(BSPF_equalsIgnoreCase(verb, commands[i].cmdString))
{ {
if(validateArgs(i)) if(validateArgs(i))
CALL_METHOD(commands[i].executor); CALL_METHOD(commands[i].executor);
@ -159,9 +159,8 @@ void DebuggerParser::getCompletions(const char* in, StringList& completions) con
// cerr << "Attempting to complete \"" << in << "\"" << endl; // cerr << "Attempting to complete \"" << in << "\"" << endl;
for(int i = 0; i < kNumCommands; ++i) for(int i = 0; i < kNumCommands; ++i)
{ {
const char* l = commands[i].cmdString.c_str(); if(BSPF_startsWithIgnoreCase(commands[i].cmdString.c_str(), in))
if(BSPF_strncasecmp(l, in, strlen(in)) == 0) completions.push_back(commands[i].cmdString);
completions.push_back(l);
} }
} }
@ -341,7 +340,7 @@ bool DebuggerParser::getArgs(const string& command, string& verb)
verb += c; verb += c;
break; break;
case kIN_SPACE: case kIN_SPACE:
if(c == '{') if(c == '\'')
state = kIN_BRACE; state = kIN_BRACE;
else if(c != ' ') { else if(c != ' ') {
state = kIN_ARG; state = kIN_ARG;
@ -349,7 +348,7 @@ bool DebuggerParser::getArgs(const string& command, string& verb)
} }
break; break;
case kIN_BRACE: case kIN_BRACE:
if(c == '}') { if(c == '\'') {
state = kIN_SPACE; state = kIN_SPACE;
argStrings.push_back(curArg); argStrings.push_back(curArg);
// cerr << "{" << curArg << "}" << endl; // cerr << "{" << curArg << "}" << endl;

View File

@ -200,14 +200,14 @@ bool PromptWidget::handleKeyDown(int ascii, int keycode, int modifiers)
int len = _promptEndPos - _promptStartPos; int len = _promptEndPos - _promptStartPos;
if(len > 256) len = 256; if(len > 256) len = 256;
int lastDelimPos = -1; int lastDelimPos = -1;
char delimiter = '\0'; char delimiter = '\0';
char str[256]; char str[256];
for (i = 0; i < len; i++) for (i = 0; i < len; i++)
{ {
str[i] = buffer(_promptStartPos + i) & 0x7f; str[i] = buffer(_promptStartPos + i) & 0x7f;
if(strchr("*@<> ", str[i]) != NULL ) if(strchr("\'*@<> ", str[i]) != NULL )
{ {
lastDelimPos = i; lastDelimPos = i;
delimiter = str[i]; delimiter = str[i];
@ -885,7 +885,7 @@ string PromptWidget::getCompletionPrefix(const StringList& completions, string p
const string& s = completions[i]; const string& s = completions[i];
if(s.length() < prefix.length()) if(s.length() < prefix.length())
return prefix; // current prefix is the best we're going to get return prefix; // current prefix is the best we're going to get
else if(s.compare(0, prefix.length(), prefix) != 0) else if(!BSPF_startsWithIgnoreCase(s, prefix))
{ {
prefix.erase(prefix.length()-1); prefix.erase(prefix.length()-1);
return prefix; return prefix;

View File

@ -15,9 +15,6 @@
// this file, and for a DISCLAIMER OF ALL WARRANTIES. // this file, and for a DISCLAIMER OF ALL WARRANTIES.
// //
// $Id$ // $Id$
//
// Based on code from ScummVM - Scumm Interpreter
// Copyright (C) 2002-2004 The ScummVM project
//============================================================================ //============================================================================
//#include "YaccParser.hxx" //#include "YaccParser.hxx"
@ -81,23 +78,27 @@ int parse(const char *in)
} }
/* hand-rolled lexer. Hopefully faster than flex... */ /* hand-rolled lexer. Hopefully faster than flex... */
inline bool is_base_prefix(char x) { return ( (x=='\\' || x=='$' || x=='#') ); } inline bool is_base_prefix(char x)
{
inline bool is_identifier(char x) { return ( (x=='\\' || x=='$' || x=='#') );
return ( (x>='0' && x<='9') ||
(x>='a' && x<='z') ||
(x>='A' && x<='Z') ||
x=='.' || x=='_' );
} }
inline bool is_identifier(char x)
{
return ( (x>='0' && x<='9') ||
(x>='a' && x<='z') ||
(x>='A' && x<='Z') ||
x=='.' || x=='_' );
}
inline bool is_operator(char x) { inline bool is_operator(char x)
{
return ( (x=='+' || x=='-' || x=='*' || return ( (x=='+' || x=='-' || x=='*' ||
x=='/' || x=='<' || x=='>' || x=='/' || x=='<' || x=='>' ||
x=='|' || x=='&' || x=='^' || x=='|' || x=='&' || x=='^' ||
x=='!' || x=='~' || x=='(' || x=='!' || x=='~' || x=='(' ||
x==')' || x=='=' || x=='%' || x==')' || x=='=' || x=='%' ||
x=='[' || x==']' ) ); x=='[' || x==']' ) );
} }
// const_to_int converts a string into a number, in either the // const_to_int converts a string into a number, in either the
@ -171,29 +172,29 @@ int const_to_int(char *c) {
// special methods that get e.g. CPU registers // special methods that get e.g. CPU registers
CPUDEBUG_INT_METHOD getCpuSpecial(char *c) CPUDEBUG_INT_METHOD getCpuSpecial(char *c)
{ {
if(strcmp(c, "a") == 0) if(BSPF_equalsIgnoreCase(c, "a"))
return &CpuDebug::a; return &CpuDebug::a;
else if(strcmp(c, "x") == 0) else if(BSPF_equalsIgnoreCase(c, "x"))
return &CpuDebug::x; return &CpuDebug::x;
else if(strcmp(c, "y") == 0) else if(BSPF_equalsIgnoreCase(c, "y"))
return &CpuDebug::y; return &CpuDebug::y;
else if(strcmp(c, "pc") == 0) else if(BSPF_equalsIgnoreCase(c, "pc"))
return &CpuDebug::pc; return &CpuDebug::pc;
else if(strcmp(c, "sp") == 0) else if(BSPF_equalsIgnoreCase(c, "sp"))
return &CpuDebug::sp; return &CpuDebug::sp;
else if(strcmp(c, "c") == 0) else if(BSPF_equalsIgnoreCase(c, "c"))
return &CpuDebug::c; return &CpuDebug::c;
else if(strcmp(c, "z") == 0) else if(BSPF_equalsIgnoreCase(c, "z"))
return &CpuDebug::z; return &CpuDebug::z;
else if(strcmp(c, "n") == 0) else if(BSPF_equalsIgnoreCase(c, "n"))
return &CpuDebug::n; return &CpuDebug::n;
else if(strcmp(c, "v") == 0) else if(BSPF_equalsIgnoreCase(c, "v"))
return &CpuDebug::v; return &CpuDebug::v;
else if(strcmp(c, "d") == 0) else if(BSPF_equalsIgnoreCase(c, "d"))
return &CpuDebug::d; return &CpuDebug::d;
else if(strcmp(c, "i") == 0) else if(BSPF_equalsIgnoreCase(c, "i"))
return &CpuDebug::i; return &CpuDebug::i;
else if(strcmp(c, "b") == 0) else if(BSPF_equalsIgnoreCase(c, "b"))
return &CpuDebug::b; return &CpuDebug::b;
else else
return 0; return 0;
@ -202,9 +203,9 @@ CPUDEBUG_INT_METHOD getCpuSpecial(char *c)
// special methods that get Cart RAM/ROM internal state // special methods that get Cart RAM/ROM internal state
CARTDEBUG_INT_METHOD getCartSpecial(char *c) CARTDEBUG_INT_METHOD getCartSpecial(char *c)
{ {
if(strcmp(c, "_bank") == 0) if(BSPF_equalsIgnoreCase(c, "_bank"))
return &CartDebug::getBank; return &CartDebug::getBank;
else if(strcmp(c, "_rwport") == 0) else if(BSPF_equalsIgnoreCase(c, "_rwport"))
return &CartDebug::readFromWritePort; return &CartDebug::readFromWritePort;
else else
return 0; return 0;
@ -213,15 +214,15 @@ CARTDEBUG_INT_METHOD getCartSpecial(char *c)
// special methods that get TIA internal state // special methods that get TIA internal state
TIADEBUG_INT_METHOD getTiaSpecial(char *c) TIADEBUG_INT_METHOD getTiaSpecial(char *c)
{ {
if(strcmp(c, "_scan") == 0) if(BSPF_equalsIgnoreCase(c, "_scan"))
return &TIADebug::scanlines; return &TIADebug::scanlines;
else if(strcmp(c, "_fcount") == 0) else if(BSPF_equalsIgnoreCase(c, "_fcount"))
return &TIADebug::frameCount; return &TIADebug::frameCount;
else if(strcmp(c, "_cclocks") == 0) else if(BSPF_equalsIgnoreCase(c, "_cclocks"))
return &TIADebug::clocksThisLine; return &TIADebug::clocksThisLine;
else if(strcmp(c, "_vsync") == 0) else if(BSPF_equalsIgnoreCase(c, "_vsync"))
return &TIADebug::vsyncAsInt; return &TIADebug::vsyncAsInt;
else if(strcmp(c, "_vblank") == 0) else if(BSPF_equalsIgnoreCase(c, "_vblank"))
return &TIADebug::vblankAsInt; return &TIADebug::vblankAsInt;
else else
return 0; return 0;