mirror of https://github.com/stella-emu/stella.git
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:
parent
bc28a2408d
commit
d664beb45d
|
@ -27,7 +27,7 @@ feature that no other 2600 debugger has; it's <b>completely</b> cross-platform.<
|
|||
breakpoints as you want.</li>
|
||||
|
||||
<li>Conditional breakpoints - Break running program when some arbitrary
|
||||
condition is true (e.g. "breakif {a == $7f && c}" will break when the Accumulator value is $7f and the Carry flag is true, no matter where
|
||||
condition is true (e.g. "breakif 'a == $7f && c'" 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
|
||||
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>
|
||||
|
@ -387,9 +387,9 @@ we want to apply the ! to the result of the &, not just the first term
|
|||
<p>"breakif !(*SWCHB&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>
|
||||
|
||||
<p>"breakif { !(*SWCHB & 1 ) }" is a lot more readable, isn't it? If
|
||||
<p>"breakif ' !(*SWCHB & 1 ) '" is a lot more readable, isn't it? If
|
||||
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
|
||||
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>
|
||||
|
||||
<pre>
|
||||
function gameReset { !(*SWCHB & 1 ) }
|
||||
function gameReset ' !(*SWCHB & 1 ) '
|
||||
breakif gameReset
|
||||
</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>
|
||||
|
||||
<pre>
|
||||
breakif { gameReset && gameSelect }
|
||||
breakif ' gameReset && gameSelect '
|
||||
</pre>
|
||||
|
||||
<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>
|
||||
</table>
|
||||
<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
|
||||
actively using the debugger.</p>
|
||||
|
||||
|
@ -491,8 +491,8 @@ Stella debugger.</p>
|
|||
<tr><td> _joy1up</td><td> !(*SWCHA & $01)</td><td> Right joystick moved up</td></tr>
|
||||
<tr><td> _joy1down</td><td> !(*SWCHA & $02)</td><td> Right joystick moved down</td></tr>
|
||||
<tr><td> _joy1button</td><td> !(*INPT5 & $80)</td><td> Right joystick button pressed</td></tr>
|
||||
<tr><td> _select</td><td> !(*SWCHB & $01)</td><td> Game Select pressed</td></tr>
|
||||
<tr><td> _reset</td><td> !(*SWCHB & $02)</td><td> Game Reset pressed</td></tr>
|
||||
<tr><td> _select</td><td> !(*SWCHB & $02)</td><td> Game Select pressed</td></tr>
|
||||
<tr><td> _reset</td><td> !(*SWCHB & $01)</td><td> Game Reset pressed</td></tr>
|
||||
<tr><td> _color</td><td> *SWCHB & $08</td><td> Color/BW set to Color</td></tr>
|
||||
<tr><td> _bw</td><td> !(*SWCHB & $08)</td><td> Color/BW set to BW</td></tr>
|
||||
<tr><td> _diff0b</td><td> !(*SWCHB & $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>
|
||||
|
||||
<pre>
|
||||
breakif { pc==myLabel && _bank==1 }
|
||||
breakif ' pc==myLabel && _bank==1 '
|
||||
</pre>
|
||||
|
||||
<p>This is similar to setting a regular breakpoint, but it will only trigger
|
||||
|
|
|
@ -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_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; }
|
||||
|
||||
// Test whether two strings are equal (case insensitive)
|
||||
inline bool BSPF_equalsIgnoreCase(const string& s1, const string& s2)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
string::const_iterator pos = std::search(s1.begin(), s1.end(),
|
||||
|
|
|
@ -108,7 +108,7 @@ string DebuggerParser::run(const string& command)
|
|||
|
||||
for(int i = 0; i < kNumCommands; ++i)
|
||||
{
|
||||
if(verb == commands[i].cmdString)
|
||||
if(BSPF_equalsIgnoreCase(verb, commands[i].cmdString))
|
||||
{
|
||||
if(validateArgs(i))
|
||||
CALL_METHOD(commands[i].executor);
|
||||
|
@ -159,9 +159,8 @@ void DebuggerParser::getCompletions(const char* in, StringList& completions) con
|
|||
// cerr << "Attempting to complete \"" << in << "\"" << endl;
|
||||
for(int i = 0; i < kNumCommands; ++i)
|
||||
{
|
||||
const char* l = commands[i].cmdString.c_str();
|
||||
if(BSPF_strncasecmp(l, in, strlen(in)) == 0)
|
||||
completions.push_back(l);
|
||||
if(BSPF_startsWithIgnoreCase(commands[i].cmdString.c_str(), in))
|
||||
completions.push_back(commands[i].cmdString);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -341,7 +340,7 @@ bool DebuggerParser::getArgs(const string& command, string& verb)
|
|||
verb += c;
|
||||
break;
|
||||
case kIN_SPACE:
|
||||
if(c == '{')
|
||||
if(c == '\'')
|
||||
state = kIN_BRACE;
|
||||
else if(c != ' ') {
|
||||
state = kIN_ARG;
|
||||
|
@ -349,7 +348,7 @@ bool DebuggerParser::getArgs(const string& command, string& verb)
|
|||
}
|
||||
break;
|
||||
case kIN_BRACE:
|
||||
if(c == '}') {
|
||||
if(c == '\'') {
|
||||
state = kIN_SPACE;
|
||||
argStrings.push_back(curArg);
|
||||
// cerr << "{" << curArg << "}" << endl;
|
||||
|
|
|
@ -200,14 +200,14 @@ bool PromptWidget::handleKeyDown(int ascii, int keycode, int modifiers)
|
|||
int len = _promptEndPos - _promptStartPos;
|
||||
if(len > 256) len = 256;
|
||||
|
||||
int lastDelimPos = -1;
|
||||
int lastDelimPos = -1;
|
||||
char delimiter = '\0';
|
||||
|
||||
char str[256];
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
str[i] = buffer(_promptStartPos + i) & 0x7f;
|
||||
if(strchr("*@<> ", str[i]) != NULL )
|
||||
if(strchr("\'*@<> ", str[i]) != NULL )
|
||||
{
|
||||
lastDelimPos = i;
|
||||
delimiter = str[i];
|
||||
|
@ -885,7 +885,7 @@ string PromptWidget::getCompletionPrefix(const StringList& completions, string p
|
|||
const string& s = completions[i];
|
||||
if(s.length() < prefix.length())
|
||||
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);
|
||||
return prefix;
|
||||
|
|
|
@ -15,9 +15,6 @@
|
|||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Based on code from ScummVM - Scumm Interpreter
|
||||
// Copyright (C) 2002-2004 The ScummVM project
|
||||
//============================================================================
|
||||
|
||||
//#include "YaccParser.hxx"
|
||||
|
@ -81,23 +78,27 @@ int parse(const char *in)
|
|||
}
|
||||
|
||||
/* hand-rolled lexer. Hopefully faster than flex... */
|
||||
inline bool is_base_prefix(char x) { return ( (x=='\\' || 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_base_prefix(char x)
|
||||
{
|
||||
return ( (x=='\\' || 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=='*' ||
|
||||
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
|
||||
|
@ -171,29 +172,29 @@ int const_to_int(char *c) {
|
|||
// special methods that get e.g. CPU registers
|
||||
CPUDEBUG_INT_METHOD getCpuSpecial(char *c)
|
||||
{
|
||||
if(strcmp(c, "a") == 0)
|
||||
if(BSPF_equalsIgnoreCase(c, "a"))
|
||||
return &CpuDebug::a;
|
||||
else if(strcmp(c, "x") == 0)
|
||||
else if(BSPF_equalsIgnoreCase(c, "x"))
|
||||
return &CpuDebug::x;
|
||||
else if(strcmp(c, "y") == 0)
|
||||
else if(BSPF_equalsIgnoreCase(c, "y"))
|
||||
return &CpuDebug::y;
|
||||
else if(strcmp(c, "pc") == 0)
|
||||
else if(BSPF_equalsIgnoreCase(c, "pc"))
|
||||
return &CpuDebug::pc;
|
||||
else if(strcmp(c, "sp") == 0)
|
||||
else if(BSPF_equalsIgnoreCase(c, "sp"))
|
||||
return &CpuDebug::sp;
|
||||
else if(strcmp(c, "c") == 0)
|
||||
else if(BSPF_equalsIgnoreCase(c, "c"))
|
||||
return &CpuDebug::c;
|
||||
else if(strcmp(c, "z") == 0)
|
||||
else if(BSPF_equalsIgnoreCase(c, "z"))
|
||||
return &CpuDebug::z;
|
||||
else if(strcmp(c, "n") == 0)
|
||||
else if(BSPF_equalsIgnoreCase(c, "n"))
|
||||
return &CpuDebug::n;
|
||||
else if(strcmp(c, "v") == 0)
|
||||
else if(BSPF_equalsIgnoreCase(c, "v"))
|
||||
return &CpuDebug::v;
|
||||
else if(strcmp(c, "d") == 0)
|
||||
else if(BSPF_equalsIgnoreCase(c, "d"))
|
||||
return &CpuDebug::d;
|
||||
else if(strcmp(c, "i") == 0)
|
||||
else if(BSPF_equalsIgnoreCase(c, "i"))
|
||||
return &CpuDebug::i;
|
||||
else if(strcmp(c, "b") == 0)
|
||||
else if(BSPF_equalsIgnoreCase(c, "b"))
|
||||
return &CpuDebug::b;
|
||||
else
|
||||
return 0;
|
||||
|
@ -202,9 +203,9 @@ CPUDEBUG_INT_METHOD getCpuSpecial(char *c)
|
|||
// special methods that get Cart RAM/ROM internal state
|
||||
CARTDEBUG_INT_METHOD getCartSpecial(char *c)
|
||||
{
|
||||
if(strcmp(c, "_bank") == 0)
|
||||
if(BSPF_equalsIgnoreCase(c, "_bank"))
|
||||
return &CartDebug::getBank;
|
||||
else if(strcmp(c, "_rwport") == 0)
|
||||
else if(BSPF_equalsIgnoreCase(c, "_rwport"))
|
||||
return &CartDebug::readFromWritePort;
|
||||
else
|
||||
return 0;
|
||||
|
@ -213,15 +214,15 @@ CARTDEBUG_INT_METHOD getCartSpecial(char *c)
|
|||
// special methods that get TIA internal state
|
||||
TIADEBUG_INT_METHOD getTiaSpecial(char *c)
|
||||
{
|
||||
if(strcmp(c, "_scan") == 0)
|
||||
if(BSPF_equalsIgnoreCase(c, "_scan"))
|
||||
return &TIADebug::scanlines;
|
||||
else if(strcmp(c, "_fcount") == 0)
|
||||
else if(BSPF_equalsIgnoreCase(c, "_fcount"))
|
||||
return &TIADebug::frameCount;
|
||||
else if(strcmp(c, "_cclocks") == 0)
|
||||
else if(BSPF_equalsIgnoreCase(c, "_cclocks"))
|
||||
return &TIADebug::clocksThisLine;
|
||||
else if(strcmp(c, "_vsync") == 0)
|
||||
else if(BSPF_equalsIgnoreCase(c, "_vsync"))
|
||||
return &TIADebug::vsyncAsInt;
|
||||
else if(strcmp(c, "_vblank") == 0)
|
||||
else if(BSPF_equalsIgnoreCase(c, "_vblank"))
|
||||
return &TIADebug::vblankAsInt;
|
||||
else
|
||||
return 0;
|
||||
|
|
Loading…
Reference in New Issue