mirror of
https://github.com/AppleWin/AppleWin.git
synced 2024-08-20 17:29:09 +00:00
958 lines
23 KiB
C++
958 lines
23 KiB
C++
/*
|
|
AppleWin : An Apple //e emulator for Windows
|
|
|
|
Copyright (C) 1994-1996, Michael O'Brien
|
|
Copyright (C) 1999-2001, Oliver Schmidt
|
|
Copyright (C) 2002-2005, Tom Charlesworth
|
|
Copyright (C) 2006-2010, Tom Charlesworth, Michael Pohoreski
|
|
|
|
AppleWin is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
AppleWin is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with AppleWin; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
/* Description: Debugger
|
|
*
|
|
* Author: Copyright (C) 2006, Michael Pohoreski
|
|
*/
|
|
|
|
#include "StdAfx.h"
|
|
|
|
#include "Debug.h"
|
|
|
|
#include "../CPU.h"
|
|
#include "../Memory.h"
|
|
|
|
// Args ___________________________________________________________________________________________
|
|
|
|
int g_nArgRaw;
|
|
Arg_t g_aArgRaw[ MAX_ARGS ]; // pre-processing
|
|
Arg_t g_aArgs [ MAX_ARGS ]; // post-processing (cooked)
|
|
|
|
const TCHAR TCHAR_LF = TEXT('\x0D');
|
|
const TCHAR TCHAR_CR = TEXT('\x0A');
|
|
const TCHAR TCHAR_SPACE = TEXT(' ');
|
|
const TCHAR TCHAR_TAB = TEXT('\t');
|
|
// const TCHAR TCHAR_QUOTED = TEXT('"');
|
|
const TCHAR TCHAR_QUOTE_DOUBLE = TEXT('"');
|
|
const TCHAR TCHAR_QUOTE_SINGLE = TEXT('\'');
|
|
const TCHAR TCHAR_ESCAPE = TEXT('\x1B');
|
|
|
|
|
|
// NOTE: ArgToken_e and g_aTokens must match!
|
|
const TokenTable_t g_aTokens[ NUM_TOKENS ] =
|
|
{ // Input
|
|
{ TOKEN_ALPHANUMERIC, TYPE_STRING , 0 }, // Default, if doen't match anything else
|
|
{ TOKEN_AMPERSAND , TYPE_OPERATOR, "&" }, // bit-and
|
|
{ TOKEN_AT , TYPE_OPERATOR, "@" }, // reference results
|
|
{ TOKEN_BRACE_L , TYPE_STRING , "{" },
|
|
{ TOKEN_BRACE_R , TYPE_STRING , "}" },
|
|
{ TOKEN_BRACKET_L , TYPE_STRING , "[" },
|
|
{ TOKEN_BRACKET_R , TYPE_STRING , "]" },
|
|
{ TOKEN_BSLASH , TYPE_OPERATOR, "\\" },
|
|
{ TOKEN_CARET , TYPE_OPERATOR, "^" }, // bit-eor, C/C++: xor, Math: POWER
|
|
{ TOKEN_COLON , TYPE_OPERATOR, ":" },
|
|
{ TOKEN_COMMA , TYPE_OPERATOR, "," },
|
|
{ TOKEN_DOLLAR , TYPE_STRING , "$" },
|
|
{ TOKEN_EQUAL , TYPE_OPERATOR, "=" },
|
|
{ TOKEN_EXCLAMATION , TYPE_OPERATOR, "!" }, // NOT
|
|
{ TOKEN_FSLASH , TYPE_OPERATOR, "/" }, // div
|
|
{ TOKEN_GREATER_THAN, TYPE_OPERATOR, ">" }, // TODO/FIXME: Parser will break up '>=' (needed for uber breakpoints)
|
|
{ TOKEN_HASH , TYPE_OPERATOR, "#" },
|
|
{ TOKEN_LESS_THAN , TYPE_OPERATOR, "<" },
|
|
{ TOKEN_MINUS , TYPE_OPERATOR, "-" }, // sub
|
|
{ TOKEN_PAREN_L , TYPE_OPERATOR, "(" },
|
|
{ TOKEN_PAREN_R , TYPE_OPERATOR, ")" },
|
|
{ TOKEN_PERCENT , TYPE_OPERATOR, "%" }, // mod
|
|
{ TOKEN_PIPE , TYPE_OPERATOR, "|" }, // bit-or
|
|
{ TOKEN_PLUS , TYPE_OPERATOR, "+" }, // add
|
|
// { TOKEN_QUESTION , TYPE_OPERATOR, TEXT('?') }, // Not a token 1) wildcard needs to stay together with other chars
|
|
{ TOKEN_QUOTE_SINGLE, TYPE_QUOTED_1, "\'" },
|
|
{ TOKEN_QUOTE_DOUBLE, TYPE_QUOTED_2, "\"" }, // for strings
|
|
{ TOKEN_SEMI , TYPE_STRING , ";" },
|
|
{ TOKEN_SPACE , TYPE_STRING , " " }, // space is also a delimiter between tokens/args
|
|
{ TOKEN_STAR , TYPE_OPERATOR, "*" }, // Not a token 1) wildcard needs to stay together with other chars
|
|
// { TOKEN_TAB , TYPE_STRING , TEXT('\t') }
|
|
{ TOKEN_TILDE , TYPE_OPERATOR, "~" }, // C/C++: Not. Used for console.
|
|
|
|
{ TOKEN_COMMENT_EOL , TYPE_STRING , "//" },
|
|
{ TOKEN_GREATER_EQUAL,TYPE_OPERATOR, ">=" },
|
|
{ TOKEN_LESS_EQUAL , TYPE_OPERATOR, "<=" },
|
|
{ TOKEN_NOT_EQUAL , TYPE_OPERATOR , "!=" }
|
|
};
|
|
|
|
// Arg ____________________________________________________________________________________________
|
|
|
|
|
|
//===========================================================================
|
|
int _Arg_1( int nValue )
|
|
{
|
|
g_aArgs[1].nValue = nValue;
|
|
return 1;
|
|
}
|
|
|
|
//===========================================================================
|
|
int _Arg_1( LPTSTR pName )
|
|
{
|
|
int nLen = _tcslen( g_aArgs[1].sArg );
|
|
if (nLen < MAX_ARG_LEN)
|
|
{
|
|
_tcscpy( g_aArgs[1].sArg, pName );
|
|
}
|
|
else
|
|
{
|
|
_tcsncpy( g_aArgs[1].sArg, pName, MAX_ARG_LEN );
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
@description Copies Args[iSrc .. iEnd] to Args[0]
|
|
@param iSrc First argument to copy
|
|
@param iEnd Last argument to end
|
|
@return nArgs Number of new args
|
|
Usually called as: nArgs = _Arg_Shift( iArg, nArgs );
|
|
//=========================================================================== */
|
|
int _Arg_Shift( int iSrc, int iEnd, int iDst )
|
|
{
|
|
if (iDst < 0)
|
|
return ARG_SYNTAX_ERROR;
|
|
if (iDst >= MAX_ARGS)
|
|
return ARG_SYNTAX_ERROR;
|
|
|
|
int nArgs = (iEnd - iSrc);
|
|
int nLen = nArgs + 1;
|
|
|
|
if ((iDst + nLen) > MAX_ARGS)
|
|
return ARG_SYNTAX_ERROR;
|
|
|
|
while (nLen--)
|
|
{
|
|
g_aArgs[iDst] = g_aArgs[iSrc];
|
|
iSrc++;
|
|
iDst++;
|
|
}
|
|
return nArgs;
|
|
}
|
|
|
|
//===========================================================================
|
|
int _Args_Insert( int iSrc, int iEnd, int nLen )
|
|
{
|
|
iSrc += nLen;
|
|
int iDst = iEnd + nLen;
|
|
|
|
if (iDst >= MAX_ARGS)
|
|
return ARG_SYNTAX_ERROR;
|
|
|
|
if (iSrc >= MAX_ARGS)
|
|
return ARG_SYNTAX_ERROR;
|
|
|
|
while (nLen--)
|
|
{
|
|
g_aArgs[iDst] = g_aArgs[iSrc];
|
|
iSrc--;
|
|
iDst--;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void ClearArg( Arg_t *pArg )
|
|
{
|
|
pArg->sArg[0] = 0;
|
|
pArg->nArgLen = 0;
|
|
pArg->bSymbol = false;
|
|
pArg->eDevice = NUM_DEVICES; // none
|
|
pArg->eToken = NO_TOKEN ; // none
|
|
pArg->bType = TYPE_STRING;
|
|
pArg->nValue = 0;
|
|
#if DEBUG_VAL_2
|
|
pArg->nVal2 = 0;
|
|
#endif
|
|
}
|
|
|
|
//===========================================================================
|
|
void ArgsClear ()
|
|
{
|
|
Arg_t *pArg = &g_aArgs[0];
|
|
Arg_t *pRaw = &g_aArgRaw[0];
|
|
|
|
for (int iArg = 0; iArg < MAX_ARGS; iArg++ )
|
|
{
|
|
ClearArg( pArg );
|
|
ClearArg( pRaw );
|
|
|
|
pArg++;
|
|
pRaw++;
|
|
}
|
|
}
|
|
|
|
//===========================================================================
|
|
bool ArgsGetValue ( Arg_t *pArg, WORD * pAddressValue_, const int nBase )
|
|
{
|
|
_ASSERT(pArg);
|
|
if (pArg == NULL)
|
|
return false;
|
|
|
|
TCHAR *pSrc = & (pArg->sArg[ 0 ]);
|
|
TCHAR *pEnd = NULL;
|
|
|
|
if (pAddressValue_)
|
|
{
|
|
*pAddressValue_ = (WORD)(_tcstoul( pSrc, &pEnd, nBase) & _6502_MEM_END);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//===========================================================================
|
|
bool ArgsGetImmediateValue ( Arg_t *pArg, WORD * pAddressValue_ )
|
|
{
|
|
if (pArg && pAddressValue_)
|
|
{
|
|
if (pArg->eToken == TOKEN_HASH)
|
|
{
|
|
pArg++;
|
|
return ArgsGetValue( pArg, pAddressValue_ );
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Read console input, process the raw args, turning them into tokens and types.
|
|
//===========================================================================
|
|
int ArgsGet ( TCHAR * pInput )
|
|
{
|
|
LPCTSTR pSrc = pInput;
|
|
LPCTSTR pEnd = NULL;
|
|
int nBuf = 0;
|
|
|
|
ArgToken_e iTokenSrc = NO_TOKEN;
|
|
ArgToken_e iTokenEnd = NO_TOKEN;
|
|
ArgType_e iType = TYPE_STRING;
|
|
int nLen;
|
|
|
|
int iArg = 0;
|
|
int nArg = 0;
|
|
Arg_t *pArg = &g_aArgRaw[0]; // &g_aArgs[0];
|
|
|
|
g_pConsoleFirstArg = NULL;
|
|
|
|
// BP FAC8:FACA // Range=3
|
|
// BP FAC8,2 // Length=2
|
|
// ^ ^^ ^^
|
|
// | || |pSrc
|
|
// | || pSrc
|
|
// | |pSrc
|
|
// | pEnd
|
|
// pSrc
|
|
while ((*pSrc) && (iArg < MAX_ARGS))
|
|
{
|
|
// Technically, there shouldn't be any leading spaces,
|
|
// since pressing the spacebar is an alias for TRACE.
|
|
// However, there is spaces between arguments
|
|
pSrc = const_cast<char*>( SkipWhiteSpace( pSrc ));
|
|
|
|
if (pSrc)
|
|
{
|
|
pEnd = FindTokenOrAlphaNumeric( pSrc, g_aTokens, NUM_TOKENS, &iTokenSrc );
|
|
if ((iTokenSrc == NO_TOKEN) || (iTokenSrc == TOKEN_ALPHANUMERIC))
|
|
{
|
|
pEnd = SkipUntilToken( pSrc+1, g_aTokens, NUM_TOKENS, &iTokenEnd );
|
|
}
|
|
|
|
if (iTokenSrc == TOKEN_COMMENT_EOL)
|
|
break; //pArg->eToken = iTokenSrc;
|
|
|
|
if (iTokenSrc == NO_TOKEN)
|
|
{
|
|
iTokenSrc = TOKEN_ALPHANUMERIC;
|
|
}
|
|
|
|
iType = g_aTokens[ iTokenSrc ].eType;
|
|
|
|
if (iTokenSrc == TOKEN_SEMI)
|
|
{
|
|
// TODO - command seperator, must handle non-quoted though!
|
|
}
|
|
|
|
if (iTokenSrc == TOKEN_QUOTE_DOUBLE)
|
|
{
|
|
pSrc++; // Don't store start of quote
|
|
pEnd = SkipUntilChar( pSrc, CHAR_QUOTE_DOUBLE );
|
|
}
|
|
else
|
|
if (iTokenSrc == TOKEN_QUOTE_SINGLE)
|
|
{
|
|
pSrc++; // Don't store start of quote
|
|
pEnd = SkipUntilChar( pSrc, CHAR_QUOTE_SINGLE );
|
|
}
|
|
|
|
if (pEnd)
|
|
{
|
|
nBuf = pEnd - pSrc;
|
|
}
|
|
|
|
if (nBuf > 0)
|
|
{
|
|
// Does anyone actually "need" > 132 character output???
|
|
// Technically, we are capped via ParseInput(), g_aArgs[ iArg ] = g_aArgRaw[ iArg ];
|
|
//if (iTokenSrc == TOKEN_QUOTE_DOUBLE)
|
|
// nLen = nBuf;
|
|
nLen = MIN( nBuf, MAX_ARG_LEN ); // NOTE: see Arg_t.sArg[] // GH#481
|
|
_tcsncpy( pArg->sArg, pSrc, nLen );
|
|
pArg->sArg[ nLen ] = 0;
|
|
pArg->nArgLen = nLen;
|
|
pArg->eToken = iTokenSrc;
|
|
pArg->bType = iType;
|
|
|
|
if (iTokenSrc == TOKEN_QUOTE_DOUBLE)
|
|
{
|
|
pEnd++;
|
|
}
|
|
else
|
|
if (iTokenSrc == TOKEN_QUOTE_SINGLE)
|
|
{
|
|
if (nLen > 1)
|
|
{
|
|
// Technically, chars aren't allowed to be multi-char
|
|
// But we've extended the syntax to allow the user
|
|
// to input High-Bit Apple Text
|
|
}
|
|
pEnd++;
|
|
}
|
|
|
|
pSrc = pEnd;
|
|
iArg++;
|
|
pArg++;
|
|
|
|
if (iArg == 1)
|
|
{
|
|
g_pConsoleFirstArg = pSrc;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (iArg)
|
|
{
|
|
nArg = iArg - 1; // first arg is command
|
|
}
|
|
|
|
g_nArgRaw = iArg;
|
|
|
|
return nArg;
|
|
}
|
|
|
|
|
|
//===========================================================================
|
|
bool ArgsGetRegisterValue ( Arg_t *pArg, WORD * pAddressValue_ )
|
|
{
|
|
bool bStatus = false;
|
|
|
|
if (pArg && pAddressValue_)
|
|
{
|
|
// Check if we refer to reg A X Y P S
|
|
for( int iReg = 0; iReg < (NUM_BREAKPOINT_SOURCES-1); iReg++ )
|
|
{
|
|
// Skip Opcode/Instruction/Mnemonic
|
|
if (iReg == BP_SRC_OPCODE)
|
|
continue;
|
|
|
|
// Skip individual flag names
|
|
if ((iReg >= BP_SRC_FLAG_C) && (iReg <= BP_SRC_FLAG_N))
|
|
continue;
|
|
|
|
// Handle one char names
|
|
if ((pArg->nArgLen == 1) && (pArg->sArg[0] == g_aBreakpointSource[ iReg ][0]))
|
|
{
|
|
switch( iReg )
|
|
{
|
|
case BP_SRC_REG_A : *pAddressValue_ = regs.a & 0xFF; bStatus = true; break;
|
|
case BP_SRC_REG_P : *pAddressValue_ = regs.ps & 0xFF; bStatus = true; break;
|
|
case BP_SRC_REG_X : *pAddressValue_ = regs.x & 0xFF; bStatus = true; break;
|
|
case BP_SRC_REG_Y : *pAddressValue_ = regs.y & 0xFF; bStatus = true; break;
|
|
case BP_SRC_REG_S : *pAddressValue_ = regs.sp ; bStatus = true; break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
if (iReg == BP_SRC_REG_PC)
|
|
{
|
|
if ((pArg->nArgLen == 2) && (_tcscmp( pArg->sArg, g_aBreakpointSource[ iReg ] ) == 0))
|
|
{
|
|
*pAddressValue_ = regs.pc ; bStatus = true; break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return bStatus;
|
|
}
|
|
|
|
|
|
//===========================================================================
|
|
void ArgsRawParse ( void )
|
|
{
|
|
const int BASE = 16; // hex
|
|
TCHAR *pSrc = NULL;
|
|
TCHAR *pEnd = NULL;
|
|
|
|
int iArg = 1;
|
|
Arg_t *pArg = & g_aArgRaw[ iArg ];
|
|
int nArg = g_nArgRaw;
|
|
|
|
WORD nAddressArg;
|
|
WORD nAddressSymbol;
|
|
WORD nAddressValue;
|
|
|
|
while (iArg <= nArg)
|
|
{
|
|
pSrc = & (pArg->sArg[ 0 ]);
|
|
|
|
nAddressArg = (WORD)(_tcstoul( pSrc, &pEnd, BASE) & _6502_MEM_END);
|
|
nAddressValue = nAddressArg;
|
|
|
|
bool bFound = false;
|
|
if (! (pArg->bType & TYPE_NO_SYM))
|
|
{
|
|
bFound = FindAddressFromSymbol( pSrc, & nAddressSymbol );
|
|
if (bFound)
|
|
{
|
|
nAddressValue = nAddressSymbol;
|
|
pArg->bSymbol = true;
|
|
}
|
|
}
|
|
|
|
if (! (pArg->bType & TYPE_VALUE)) // already up to date?
|
|
pArg->nValue = nAddressValue;
|
|
|
|
pArg->bType |= TYPE_ADDRESS;
|
|
|
|
iArg++;
|
|
pArg++;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
@param nArgs Number of raw args.
|
|
|
|
Note: The number of args can be changed via:
|
|
|
|
address1,length Length
|
|
address1:address2 Range
|
|
address1+delta Delta
|
|
address1-delta Delta
|
|
//=========================================================================== */
|
|
int ArgsCook ( const int nArgs )
|
|
{
|
|
const int BASE = 16; // hex
|
|
TCHAR *pSrc = NULL;
|
|
TCHAR *pEnd2 = NULL;
|
|
|
|
int nArg = nArgs;
|
|
int iArg = 1;
|
|
Arg_t *pArg = NULL;
|
|
Arg_t *pPrev = NULL;
|
|
Arg_t *pNext = NULL;
|
|
|
|
WORD nAddressArg;
|
|
WORD nAddressRHS;
|
|
WORD nAddressSym;
|
|
WORD nAddressVal;
|
|
int nParamLen = 0;
|
|
int nArgsLeft = 0;
|
|
|
|
int nParenL = 0;
|
|
int nParenR = 0;
|
|
|
|
while (iArg <= nArg)
|
|
{
|
|
pArg = & (g_aArgs[ iArg ]);
|
|
pSrc = & (pArg->sArg[ 0 ]);
|
|
|
|
if (pArg->eToken == TOKEN_DOLLAR) // address
|
|
{
|
|
// TODO: Need to flag was a DOLLAR token for assembler
|
|
pNext = NULL;
|
|
|
|
nArgsLeft = (nArg - iArg);
|
|
if (nArgsLeft > 0)
|
|
{
|
|
pNext = pArg + 1;
|
|
|
|
_Arg_Shift( iArg + 1, nArgs, iArg );
|
|
nArg--;
|
|
iArg--; // inc for start of next loop
|
|
|
|
// Don't do register lookup
|
|
pArg->bType |= TYPE_NO_REG;
|
|
}
|
|
else
|
|
return ARG_SYNTAX_ERROR;
|
|
}
|
|
|
|
if (pArg->bType & TYPE_OPERATOR) // prev op type == address?
|
|
{
|
|
pPrev = NULL; // pLHS
|
|
pNext = NULL; // pRHS
|
|
nParamLen = 0;
|
|
|
|
if (pArg->eToken == TOKEN_HASH) // HASH # immediate
|
|
nParamLen = 1;
|
|
|
|
nArgsLeft = (nArg - iArg);
|
|
if (nArgsLeft < nParamLen)
|
|
{
|
|
return ARG_SYNTAX_ERROR;
|
|
}
|
|
|
|
pPrev = pArg - 1;
|
|
|
|
// Pass wildstar '*' to commands if only arg
|
|
if ((pArg->eToken == TOKEN_STAR) && (nArg == 1))
|
|
;
|
|
else
|
|
if (nArgsLeft > 0) // These ops take at least 1 argument
|
|
{
|
|
pNext = pArg + 1;
|
|
pSrc = &pNext->sArg[0];
|
|
|
|
nAddressVal = 0;
|
|
if (ArgsGetValue( pNext, & nAddressRHS ))
|
|
nAddressVal = nAddressRHS;
|
|
|
|
bool bFound = FindAddressFromSymbol( pSrc, & nAddressSym );
|
|
if (bFound)
|
|
{
|
|
nAddressVal = nAddressSym;
|
|
pArg->bSymbol = true;
|
|
}
|
|
|
|
// Comma and Colon are range operators, but they are not parsed here,
|
|
// since args no longer have a 1st and 2nd value
|
|
/*
|
|
pPrev->eToken = TOKEN_COLON;
|
|
pPrev->bType |= TYPE_ADDRESS;
|
|
pPrev->bType |= TYPE_RANGE;
|
|
*/
|
|
|
|
if (pArg->eToken == TOKEN_AMPERSAND) // AND & delta
|
|
{
|
|
if (! ArgsGetImmediateValue( pNext, & nAddressRHS ))
|
|
{
|
|
ArgsGetRegisterValue( pNext, & nAddressRHS );
|
|
}
|
|
pPrev->nValue &= nAddressRHS;
|
|
pPrev->bType |= TYPE_VALUE; // signal already up to date
|
|
nParamLen = 2;
|
|
}
|
|
|
|
if (pArg->eToken == TOKEN_PIPE) // OR | delta
|
|
{
|
|
if (! ArgsGetImmediateValue( pNext, & nAddressRHS ))
|
|
{
|
|
ArgsGetRegisterValue( pNext, & nAddressRHS );
|
|
}
|
|
pPrev->nValue |= nAddressRHS;
|
|
pPrev->bType |= TYPE_VALUE; // signal already up to date
|
|
nParamLen = 2;
|
|
}
|
|
|
|
if (pArg->eToken == TOKEN_CARET) // XOR ^ delta
|
|
{
|
|
if (! ArgsGetImmediateValue( pNext, & nAddressRHS ))
|
|
{
|
|
ArgsGetRegisterValue( pNext, & nAddressRHS );
|
|
}
|
|
pPrev->nValue ^= nAddressRHS;
|
|
pPrev->bType |= TYPE_VALUE; // signal already up to date
|
|
nParamLen = 2;
|
|
}
|
|
|
|
if (pArg->eToken == TOKEN_PLUS) // PLUS + delta
|
|
{
|
|
if (! ArgsGetImmediateValue( pNext, & nAddressRHS ))
|
|
{
|
|
ArgsGetRegisterValue( pNext, & nAddressRHS );
|
|
}
|
|
pPrev->nValue += nAddressRHS;
|
|
pPrev->bType |= TYPE_VALUE; // signal already up to date
|
|
nParamLen = 2;
|
|
}
|
|
|
|
if (pArg->eToken == TOKEN_MINUS) // MINUS - delta
|
|
{
|
|
if (! ArgsGetImmediateValue( pNext, & nAddressRHS ))
|
|
{
|
|
ArgsGetRegisterValue( pNext, & nAddressRHS );
|
|
}
|
|
pPrev->nValue -= nAddressRHS;
|
|
pPrev->bType |= TYPE_VALUE; // signal already up to date
|
|
nParamLen = 2;
|
|
}
|
|
|
|
if (pArg->eToken == TOKEN_PERCENT) // PERCENT % delta
|
|
{
|
|
if (! ArgsGetImmediateValue( pNext, & nAddressRHS ))
|
|
{
|
|
ArgsGetRegisterValue( pNext, & nAddressRHS );
|
|
}
|
|
pPrev->nValue %= nAddressRHS;
|
|
pPrev->bType |= TYPE_VALUE; // signal already up to date
|
|
nParamLen = 2;
|
|
}
|
|
|
|
if (pArg->eToken == TOKEN_STAR) // STAR * delta
|
|
{
|
|
if (! ArgsGetImmediateValue( pNext, & nAddressRHS ))
|
|
{
|
|
ArgsGetRegisterValue( pNext, & nAddressRHS );
|
|
}
|
|
pPrev->nValue *= nAddressRHS;
|
|
pPrev->bType |= TYPE_VALUE; // signal already up to date
|
|
nParamLen = 2;
|
|
}
|
|
|
|
if (pArg->eToken == TOKEN_FSLASH) // FORWARD SLASH / delta
|
|
{
|
|
if (pNext->eToken == TOKEN_FSLASH) // Comment
|
|
{
|
|
nArg = iArg - 1;
|
|
return nArg;
|
|
}
|
|
if (! ArgsGetImmediateValue( pNext, & nAddressRHS ))
|
|
{
|
|
ArgsGetRegisterValue( pNext, & nAddressRHS );
|
|
}
|
|
if (! nAddressRHS)
|
|
nAddressRHS = 1; // divide by zero bug
|
|
pPrev->nValue /= nAddressRHS;
|
|
pPrev->bType |= TYPE_VALUE; // signal already up to date
|
|
nParamLen = 2;
|
|
}
|
|
|
|
if (pArg->eToken == TOKEN_EQUAL) // EQUAL = assign
|
|
{
|
|
pPrev->nValue = nAddressRHS;
|
|
pPrev->bType |= TYPE_VALUE; // signal already up to date
|
|
nParamLen = 0; // need token for Smart BreakPoints
|
|
}
|
|
|
|
if (pArg->eToken == TOKEN_AT) // AT @ pointer de-reference
|
|
{
|
|
nParamLen = 1;
|
|
_Arg_Shift( iArg + nParamLen, nArgs, iArg );
|
|
nArg--;
|
|
|
|
pArg->nValue = 0; // nAddressRHS;
|
|
pArg->bSymbol = false;
|
|
|
|
int nPointers = g_vMemorySearchResults.size();
|
|
if ((nPointers) &&
|
|
(nAddressRHS < nPointers))
|
|
{
|
|
pArg->nValue = g_vMemorySearchResults.at( nAddressRHS );
|
|
pArg->bType = TYPE_VALUE | TYPE_ADDRESS | TYPE_NO_REG | TYPE_NO_SYM;
|
|
}
|
|
nParamLen = 0;
|
|
}
|
|
|
|
if (pArg->eToken == TOKEN_HASH) // HASH # immediate
|
|
{
|
|
pArg->nValue = nAddressRHS;
|
|
pArg->bSymbol = false;
|
|
pArg->bType = TYPE_VALUE | TYPE_ADDRESS | TYPE_NO_REG | TYPE_NO_SYM;
|
|
nParamLen = 0;
|
|
}
|
|
|
|
if (pArg->eToken == TOKEN_LESS_THAN) // <
|
|
{
|
|
nParamLen = 0;
|
|
}
|
|
|
|
if (pArg->eToken == TOKEN_GREATER_THAN) // >
|
|
{
|
|
nParamLen = 0;
|
|
}
|
|
|
|
if (pArg->eToken == TOKEN_EXCLAMATION) // NOT !
|
|
{
|
|
if (! ArgsGetImmediateValue( pNext, & nAddressRHS ))
|
|
{
|
|
if (! ArgsGetRegisterValue( pNext, & nAddressRHS ))
|
|
{
|
|
nAddressRHS = nAddressVal;
|
|
}
|
|
}
|
|
pArg->nValue = ~nAddressRHS;
|
|
pArg->bType |= TYPE_VALUE; // signal already up to date
|
|
// Don't remove, since "SYM ! symbol" needs token to remove symbol
|
|
}
|
|
|
|
if (pArg->eToken == TOKEN_PAREN_L)
|
|
{
|
|
nParenL++;
|
|
|
|
if (nArgsLeft >= 2)
|
|
{
|
|
nParamLen = 1; // eat '('
|
|
_Arg_Shift( iArg + nParamLen, nArgs, iArg );
|
|
|
|
pNext = & (g_aArgs[ iArg + 1 ]);
|
|
if (pNext->eToken == TOKEN_PAREN_R)
|
|
{
|
|
nParenR++;
|
|
pArg->bSymbol = false;
|
|
|
|
// This is static binding, instead of dynamic binding
|
|
// i.e. memdump (BRKV) -> memdump (3F0), but if 3F0 changes later
|
|
// the debugger won't know that it has
|
|
// TODO: TYPE_INDIRECT
|
|
// pArg->bType |= TYPE_INDIRECT;
|
|
// pArg->nValue = nAddressVal;
|
|
//nAddressVal = pNext->nValue;
|
|
pArg->nValue = * (WORD*) (mem + nAddressVal);
|
|
pArg->bType = TYPE_VALUE | TYPE_ADDRESS | TYPE_NO_REG;
|
|
|
|
iArg++; // eat ')'
|
|
nArg -= 2;
|
|
nParamLen = 0;
|
|
}
|
|
else
|
|
return ARG_SYNTAX_ERROR; // ERROR: unbalanced/unmatched ( )
|
|
}
|
|
}
|
|
|
|
if (pArg->eToken == TOKEN_PAREN_R)
|
|
{
|
|
nParenR++;
|
|
if (nParenL == nParenR)
|
|
{
|
|
nParamLen = 1;
|
|
}
|
|
else
|
|
return ARG_SYNTAX_ERROR;
|
|
}
|
|
|
|
if (nParamLen)
|
|
{
|
|
_Arg_Shift( iArg + nParamLen, nArgs, iArg );
|
|
nArg -= nParamLen;
|
|
iArg = 0; // reset args, to handle multiple operators
|
|
}
|
|
}
|
|
else
|
|
return ARG_SYNTAX_ERROR;
|
|
}
|
|
else // not an operator, try (1) address, (2) symbol lookup
|
|
{
|
|
nAddressArg = (WORD)(_tcstoul( pSrc, &pEnd2, BASE) & _6502_MEM_END);
|
|
|
|
if (! (pArg->bType & TYPE_NO_REG))
|
|
{
|
|
ArgsGetRegisterValue( pArg, & nAddressArg );
|
|
}
|
|
|
|
nAddressVal = nAddressArg;
|
|
|
|
bool bFound = false;
|
|
if (! (pArg->bType & TYPE_NO_SYM))
|
|
{
|
|
bFound = FindAddressFromSymbol( pSrc, & nAddressSym );
|
|
if (bFound)
|
|
{
|
|
nAddressVal = nAddressSym;
|
|
pArg->bSymbol = true;
|
|
}
|
|
}
|
|
|
|
if (! (pArg->bType & TYPE_VALUE)) // already up to date?
|
|
pArg->nValue = nAddressVal;
|
|
|
|
pArg->bType |= TYPE_ADDRESS;
|
|
}
|
|
|
|
iArg++;
|
|
}
|
|
|
|
return nArg;
|
|
}
|
|
|
|
|
|
// Text Util ______________________________________________________________________________________
|
|
|
|
|
|
|
|
//===========================================================================
|
|
const char * ParserFindToken( const char *pSrc, const TokenTable_t *aTokens, const int nTokens, ArgToken_e * pToken_ )
|
|
{
|
|
if (! pSrc)
|
|
return NULL;
|
|
|
|
const TCHAR *pName = NULL;
|
|
int iToken;
|
|
|
|
// Look-ahead for <=
|
|
// Look-ahead for >=
|
|
for (iToken = _TOKEN_FLAG_MULTI; iToken < NUM_TOKENS; iToken++ )
|
|
{
|
|
pName = & (g_aTokens[ iToken ].sToken[0]);
|
|
if ((pSrc[0] == pName[ 0 ]) &&
|
|
(pSrc[1] == pName[ 1 ]))
|
|
{
|
|
*pToken_ = g_aTokens[ iToken ].eToken;
|
|
return pSrc + 2;
|
|
}
|
|
}
|
|
|
|
const TokenTable_t *pToken = aTokens;
|
|
|
|
for (iToken = 0; iToken < _TOKEN_FLAG_MULTI; iToken++ )
|
|
{
|
|
pName = & (pToken->sToken[0]);
|
|
if (*pSrc == *pName)
|
|
{
|
|
if ( pToken_ )
|
|
{
|
|
*pToken_ = (ArgToken_e) iToken;
|
|
}
|
|
return pSrc + 1;
|
|
}
|
|
pToken++;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//===========================================================================
|
|
const TCHAR * FindTokenOrAlphaNumeric ( const TCHAR *pSrc, const TokenTable_t *aTokens, const int nTokens, ArgToken_e * pToken_ )
|
|
{
|
|
if ( pToken_ )
|
|
*pToken_ = NO_TOKEN;
|
|
|
|
const TCHAR *pEnd = pSrc;
|
|
|
|
if (pSrc && (*pSrc))
|
|
{
|
|
if (isalnum( *pSrc ))
|
|
{
|
|
if (pToken_)
|
|
*pToken_ = TOKEN_ALPHANUMERIC;
|
|
}
|
|
else
|
|
{
|
|
pEnd = ParserFindToken( pSrc, aTokens, nTokens, pToken_ );
|
|
if (! pEnd)
|
|
pEnd = pSrc;
|
|
}
|
|
}
|
|
return pEnd;
|
|
}
|
|
|
|
|
|
//===========================================================================
|
|
void TextConvertTabsToSpaces( TCHAR *pDeTabified_, LPCTSTR pText, const int nDstSize, int nTabStop )
|
|
{
|
|
int TAB_SPACING = 8;
|
|
int TAB_SPACING_1 = 16;
|
|
int TAB_SPACING_2 = 21;
|
|
|
|
if (nTabStop)
|
|
TAB_SPACING = nTabStop;
|
|
|
|
LPCTSTR pSrc = pText;
|
|
LPTSTR pDst = pDeTabified_;
|
|
|
|
int nTab = 0; // gap left to next tab
|
|
int nGap = 0; // actual gap
|
|
int nCur = 0; // current cursor position
|
|
while (pSrc && *pSrc && (nCur < nDstSize))
|
|
{
|
|
if (*pSrc == CHAR_TAB)
|
|
{
|
|
if (nTabStop)
|
|
{
|
|
nTab = nCur % TAB_SPACING;
|
|
nGap = (TAB_SPACING - nTab);
|
|
}
|
|
else
|
|
{
|
|
if (nCur <= TAB_SPACING_1)
|
|
{
|
|
nGap = (TAB_SPACING_1 - nCur);
|
|
}
|
|
else
|
|
if (nCur <= TAB_SPACING_2)
|
|
{
|
|
nGap = (TAB_SPACING_2 - nCur);
|
|
}
|
|
else
|
|
{
|
|
nTab = nCur % TAB_SPACING;
|
|
nGap = (TAB_SPACING - nTab);
|
|
}
|
|
}
|
|
|
|
|
|
if ((nCur + nGap) >= nDstSize)
|
|
break;
|
|
|
|
for( int iSpc = 0; iSpc < nGap; iSpc++ )
|
|
{
|
|
*pDst++ = CHAR_SPACE;
|
|
}
|
|
nCur += nGap;
|
|
}
|
|
else
|
|
if ((*pSrc == CHAR_LF) || (*pSrc == CHAR_CR))
|
|
{
|
|
*pDst++ = 0; // *pSrc;
|
|
nCur++;
|
|
}
|
|
else
|
|
{
|
|
*pDst++ = *pSrc;
|
|
nCur++;
|
|
}
|
|
pSrc++;
|
|
}
|
|
*pDst = 0;
|
|
}
|
|
|
|
|
|
// @return Length of new string
|
|
//===========================================================================
|
|
int RemoveWhiteSpaceReverse ( TCHAR *pSrc )
|
|
{
|
|
int nLen = _tcslen( pSrc );
|
|
char *pDst = pSrc + nLen;
|
|
while (nLen--)
|
|
{
|
|
pDst--;
|
|
if (*pDst == CHAR_SPACE)
|
|
{
|
|
*pDst = 0;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
return nLen;
|
|
}
|