/* */
/* */
/* */
-/* (C) 1998-2004 Ullrich von Bassewitz */
-/* Römerstraße 52 */
-/* D-70794 Filderstadt */
-/* EMail: uz@cc65.org */
+/* (C) 1998-2011, Ullrich von Bassewitz */
+/* Roemerstrasse 52 */
+/* D-70794 Filderstadt */
+/* EMail: uz@cc65.org */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
/* common */
#include "addrsize.h"
+#include "attrib.h"
#include "chartype.h"
#include "check.h"
#include "fname.h"
-enum Token Tok = TOK_NONE; /* Current token */
-int WS; /* Flag: Whitespace before token */
-long IVal; /* Integer token attribute */
-char SVal [MAX_STR_LEN+1]; /* String token attribute */
+/* Current input token incl. attributes */
+Token CurTok = STATIC_TOKEN_INITIALIZER;
-FilePos CurPos = { 0, 0, 0 }; /* Name and position in current file */
-
-
-
-/* Struct to handle include files. Note: The length of the input line may
- * not exceed 255+1, since the column is stored in the file position struct
- * as a character. Increasing this value means changing the FilePos struct,
- * and the read and write routines in the assembler and linker.
- */
-typedef struct InputFile_ InputFile;
-struct InputFile_ {
+/* Struct to handle include files. */
+typedef struct InputFile InputFile;
+struct InputFile {
FILE* F; /* Input file descriptor */
FilePos Pos; /* Position in file */
- enum Token Tok; /* Last token */
+ token_t Tok; /* Last token */
int C; /* Last character */
char Line[256]; /* The current input line */
+ int IncSearchPath; /* True if we've added a search path */
+ int BinSearchPath; /* True if we've added a search path */
InputFile* Next; /* Linked list of input files */
};
/* Struct to handle textual input data */
-typedef struct InputData_ InputData;
-struct InputData_ {
- char* Data; /* Pointer to the data */
+typedef struct InputData InputData;
+struct InputData {
+ char* Text; /* Pointer to the text data */
const char* Pos; /* Pointer to current position */
int Malloced; /* Memory was malloced */
- enum Token Tok; /* Last token */
+ token_t Tok; /* Last token */
int C; /* Last character */
InputData* Next; /* Linked list of input data */
};
+/* Input source: Either file or data */
+typedef struct CharSource CharSource;
+
+/* Set of input functions */
+typedef struct CharSourceFunctions CharSourceFunctions;
+struct CharSourceFunctions {
+ void (*MarkStart) (CharSource*); /* Mark the start pos of a token */
+ void (*NextChar) (CharSource*); /* Read next char from input */
+ void (*Done) (CharSource*); /* Close input source */
+};
+
+/* Input source: Either file or data */
+struct CharSource {
+ CharSource* Next; /* Linked list of char sources */
+ token_t Tok; /* Last token */
+ int C; /* Last character */
+ const CharSourceFunctions* Func; /* Pointer to function table */
+ union {
+ InputFile File; /* File data */
+ InputData Data; /* Textual data */
+ } V;
+};
+
/* Current input variables */
-static InputFile* IFile = 0; /* Current input file */
-static InputData* IData = 0; /* Current input memory data */
-static unsigned ICount = 0; /* Count of input files */
-static int C = 0; /* Current input character */
+static CharSource* Source = 0; /* Current char source */
+static unsigned FCount = 0; /* Count of input files */
+static int C = 0; /* Current input character */
/* Force end of assembly */
-int ForcedEnd = 0;
+int ForcedEnd = 0;
/* List of dot keywords with the corresponding tokens */
struct DotKeyword {
const char* Key; /* MUST be first field */
- enum Token Tok;
+ token_t Tok;
} DotKeywords [] = {
{ ".A16", TOK_A16 },
{ ".A8", TOK_A8 },
{ ".ASSERT", TOK_ASSERT },
{ ".AUTOIMPORT", TOK_AUTOIMPORT },
{ ".BANKBYTE", TOK_BANKBYTE },
+ { ".BANKBYTES", TOK_BANKBYTES },
{ ".BITAND", TOK_AND },
{ ".BITNOT", TOK_NOT },
{ ".BITOR", TOK_OR },
{ ".EXPORT", TOK_EXPORT },
{ ".EXPORTZP", TOK_EXPORTZP },
{ ".FARADDR", TOK_FARADDR },
+ { ".FATAL", TOK_FATAL },
{ ".FEATURE", TOK_FEATURE },
{ ".FILEOPT", TOK_FILEOPT },
{ ".FOPT", TOK_FILEOPT },
{ ".GLOBAL", TOK_GLOBAL },
{ ".GLOBALZP", TOK_GLOBALZP },
{ ".HIBYTE", TOK_HIBYTE },
+ { ".HIBYTES", TOK_HIBYTES },
{ ".HIWORD", TOK_HIWORD },
{ ".I16", TOK_I16 },
{ ".I8", TOK_I8 },
+ { ".IDENT", TOK_MAKEIDENT },
{ ".IF", TOK_IF },
{ ".IFBLANK", TOK_IFBLANK },
{ ".IFCONST", TOK_IFCONST },
{ ".LIST", TOK_LIST },
{ ".LISTBYTES", TOK_LISTBYTES },
{ ".LOBYTE", TOK_LOBYTE },
+ { ".LOBYTES", TOK_LOBYTES },
{ ".LOCAL", TOK_LOCAL },
{ ".LOCALCHAR", TOK_LOCALCHAR },
{ ".LOWORD", TOK_LOWORD },
{ ".MACPACK", TOK_MACPACK },
{ ".MACRO", TOK_MACRO },
{ ".MATCH", TOK_MATCH },
+ { ".MAX", TOK_MAX },
{ ".MID", TOK_MID },
+ { ".MIN", TOK_MIN },
{ ".MOD", TOK_MOD },
{ ".NOT", TOK_BOOLNOT },
- { ".NULL", TOK_NULL },
+ { ".NULL", TOK_NULL },
{ ".OR", TOK_BOOLOR },
{ ".ORG", TOK_ORG },
{ ".OUT", TOK_OUT },
{ ".PAGELENGTH", TOK_PAGELENGTH },
{ ".PARAMCOUNT", TOK_PARAMCOUNT },
{ ".PC02", TOK_PC02 },
+ { ".POPCPU", TOK_POPCPU },
{ ".POPSEG", TOK_POPSEG },
{ ".PROC", TOK_PROC },
{ ".PSC02", TOK_PSC02 },
+ { ".PUSHCPU", TOK_PUSHCPU },
{ ".PUSHSEG", TOK_PUSHSEG },
{ ".REF", TOK_REFERENCED },
{ ".REFERENCED", TOK_REFERENCED },
{ ".RODATA", TOK_RODATA },
{ ".SCOPE", TOK_SCOPE },
{ ".SEGMENT", TOK_SEGMENT },
+ { ".SET", TOK_SET },
{ ".SETCPU", TOK_SETCPU },
{ ".SHL", TOK_SHL },
{ ".SHR", TOK_SHR },
{ ".SIZEOF", TOK_SIZEOF },
{ ".SMART", TOK_SMART },
+ { ".SPRINTF", TOK_SPRINTF },
{ ".STRAT", TOK_STRAT },
{ ".STRING", TOK_STRING },
{ ".STRLEN", TOK_STRLEN },
/*****************************************************************************/
-/* Forwards */
+/* CharSource functions */
/*****************************************************************************/
-static void NextChar (void);
-/* Read the next character from the input file */
+static void UseCharSource (CharSource* S)
+/* Initialize a new input source and start to use it. */
+{
+ /* Remember the current input char and token */
+ S->Tok = CurTok.Tok;
+ S->C = C;
+
+ /* Use the new input source */
+ S->Next = Source;
+ Source = S;
+
+ /* Read the first character from the new file */
+ S->Func->NextChar (S);
+
+ /* Setup the next token so it will be skipped on the next call to
+ * NextRawTok().
+ */
+ CurTok.Tok = TOK_SEP;
+}
+
+
+
+static void DoneCharSource (void)
+/* Close the top level character source */
+{
+ CharSource* S;
+
+ /* First, call the type specific function */
+ Source->Func->Done (Source);
+
+ /* Restore the old token */
+ CurTok.Tok = Source->Tok;
+ C = Source->C;
+
+ /* Remember the last stacked input source */
+ S = Source->Next;
+
+ /* Delete the top level one ... */
+ xfree (Source);
+
+ /* ... and use the one before */
+ Source = S;
+}
/*****************************************************************************/
-/* Character classification functions */
+/* InputFile functions */
/*****************************************************************************/
-static int IsIdChar (int C)
-/* Return true if the character is a valid character for an identifier */
+static void IFMarkStart (CharSource* S)
+/* Mark the start of the next token */
{
- return IsAlNum (C) ||
- (C == '_') ||
- (C == '@' && AtInIdents) ||
- (C == '$' && DollarInIdents);
+ CurTok.Pos = S->V.File.Pos;
}
-static int IsIdStart (int C)
-/* Return true if the character may start an identifier */
+static void IFNextChar (CharSource* S)
+/* Read the next character from the input file */
{
- return IsAlpha (C) || C == '_';
-}
+ /* Check for end of line, read the next line if needed */
+ while (S->V.File.Line [S->V.File.Pos.Col] == '\0') {
+ unsigned Len, Removed;
+ /* End of current line reached, read next line */
+ if (fgets (S->V.File.Line, sizeof (S->V.File.Line), S->V.File.F) == 0) {
+ /* End of file. Add an empty line to the listing. This is a
+ * small hack needed to keep the PC output in sync.
+ */
+ NewListingLine ("", S->V.File.Pos.Name, FCount);
+ C = EOF;
+ return;
+ }
-/*****************************************************************************/
-/* Code */
-/*****************************************************************************/
+ /* For better handling of files with unusual line endings (DOS
+ * files that are accidently translated on Unix for example),
+ * first remove all whitespace at the end, then add a single
+ * newline.
+ */
+ Len = strlen (S->V.File.Line);
+ Removed = 0;
+ while (Len > 0 && IsSpace (S->V.File.Line[Len-1])) {
+ ++Removed;
+ --Len;
+ }
+ if (Removed) {
+ S->V.File.Line[Len+0] = '\n';
+ S->V.File.Line[Len+1] = '\0';
+ }
+
+ /* One more line */
+ S->V.File.Pos.Line++;
+ S->V.File.Pos.Col = 0;
+
+ /* Remember the new line for the listing */
+ NewListingLine (S->V.File.Line, S->V.File.Pos.Name, FCount);
+
+ }
+
+ /* Return the next character from the file */
+ C = S->V.File.Line [S->V.File.Pos.Col++];
+}
-void NewInputFile (const char* Name)
-/* Open a new input file */
+void IFDone (CharSource* S)
+/* Close the current input file */
{
- InputFile* I;
- FILE* F;
+ /* We're at the end of an include file. Check if we have any
+ * open .IFs, or any open token lists in this file. This
+ * enforcement is artificial, using conditionals that start
+ * in one file and end in another are uncommon, and don't
+ * allowing these things will help finding errors.
+ */
+ CheckOpenIfs ();
- /* First try to open the file */
- F = fopen (Name, "r");
- if (F == 0) {
+ /* If we've added search paths for this file, remove them */
+ if (S->V.File.IncSearchPath) {
+ PopSearchPath (IncSearchPath);
+ }
+ if (S->V.File.BinSearchPath) {
+ PopSearchPath (BinSearchPath);
+ }
+
+ /* Close the input file and decrement the file count. We will ignore
+ * errors here, since we were just reading from the file.
+ */
+ (void) fclose (S->V.File.F);
+ --FCount;
+}
- char* PathName;
- /* Error (fatal error if this is the main file) */
- if (ICount == 0) {
- Fatal ("Cannot open input file `%s': %s", Name, strerror (errno));
- }
+/* Set of input file handling functions */
+static const CharSourceFunctions IFFunc = {
+ IFMarkStart,
+ IFNextChar,
+ IFDone
+};
+
+
+
+int NewInputFile (const char* Name)
+/* Open a new input file. Returns true if the file could be successfully opened
+ * and false otherwise.
+ */
+{
+ int RetCode = 0; /* Return code. Assume an error. */
+ char* PathName = 0;
+ FILE* F;
+ struct stat Buf;
+ StrBuf NameBuf; /* No need to initialize */
+ StrBuf Path = AUTO_STRBUF_INITIALIZER;
+ unsigned FileIdx;
+ CharSource* S;
+
+
+ /* If this is the main file, just try to open it. If it's an include file,
+ * search for it using the include path list.
+ */
+ if (FCount == 0) {
+ /* Main file */
+ F = fopen (Name, "r");
+ if (F == 0) {
+ Fatal ("Cannot open input file `%s': %s", Name, strerror (errno));
+ }
+ } else {
/* We are on include level. Search for the file in the include
- * directories.
- */
- PathName = FindInclude (Name);
+ * directories.
+ */
+ PathName = SearchFile (IncSearchPath, Name);
if (PathName == 0 || (F = fopen (PathName, "r")) == 0) {
- /* Not found or cannot open, print an error and bail out */
- Error ("Cannot open include file `%s': %s", Name, strerror (errno));
- }
+ /* Not found or cannot open, print an error and bail out */
+ Error ("Cannot open include file `%s': %s", Name, strerror (errno));
+ goto ExitPoint;
+ }
- /* Free the allocated memory */
- xfree (PathName);
+ /* Use the path name from now on */
+ Name = PathName;
+ }
+ /* Stat the file and remember the values. There a race condition here,
+ * since we cannot use fileno() (non standard identifier in standard
+ * header file), and therefore not fstat. When using stat with the
+ * file name, there's a risk that the file was deleted and recreated
+ * while it was open. Since mtime and size are only used to check
+ * if a file has changed in the debugger, we will ignore this problem
+ * here.
+ */
+ if (stat (Name, &Buf) != 0) {
+ Fatal ("Cannot stat input file `%s': %s", Name, strerror (errno));
}
- /* check again if we do now have an open file */
- if (F != 0) {
+ /* Add the file to the input file table and remember the index */
+ FileIdx = AddFile (SB_InitFromString (&NameBuf, Name),
+ (FCount == 0)? FT_MAIN : FT_INCLUDE,
+ Buf.st_size, Buf.st_mtime);
+
+ /* Create a new input source variable and initialize it */
+ S = xmalloc (sizeof (*S));
+ S->Func = &IFFunc;
+ S->V.File.F = F;
+ S->V.File.Pos.Line = 0;
+ S->V.File.Pos.Col = 0;
+ S->V.File.Pos.Name = FileIdx;
+ S->V.File.Line[0] = '\0';
+
+ /* Push the path for this file onto the include search lists */
+ SB_CopyBuf (&Path, Name, FindName (Name) - Name);
+ SB_Terminate (&Path);
+ S->V.File.IncSearchPath = PushSearchPath (IncSearchPath, SB_GetConstBuf (&Path));
+ S->V.File.BinSearchPath = PushSearchPath (BinSearchPath, SB_GetConstBuf (&Path));
+ SB_Done (&Path);
+
+ /* Count active input files */
+ ++FCount;
+
+ /* Use this input source */
+ UseCharSource (S);
+
+ /* File successfully opened */
+ RetCode = 1;
+
+ExitPoint:
+ /* Free an allocated name buffer */
+ xfree (PathName);
+
+ /* Return the success code */
+ return RetCode;
+}
- unsigned FileIdx;
- /* Stat the file and remember the values */
- struct stat Buf;
- if (fstat (fileno (F), &Buf) != 0) {
- Fatal ("Cannot stat input file `%s': %s", Name, strerror (errno));
- }
- /* Add the file to the input file table and remember the index */
- FileIdx = AddFile (Name, Buf.st_size, Buf.st_mtime);
-
- /* Create a new state variable and initialize it */
- I = xmalloc (sizeof (*I));
- I->F = F;
- I->Pos.Line = 0;
- I->Pos.Col = 0;
- I->Pos.Name = FileIdx;
- I->Tok = Tok;
- I->C = C;
- I->Line[0] = '\0';
-
- /* Use the new file */
- I->Next = IFile;
- IFile = I;
- ++ICount;
-
- /* Prime the pump */
- NextChar ();
- }
+/*****************************************************************************/
+/* InputData functions */
+/*****************************************************************************/
+
+
+
+static void IDMarkStart (CharSource* S attribute ((unused)))
+/* Mark the start of the next token */
+{
+ /* Nothing to do here */
}
-void DoneInputFile (void)
-/* Close the current input file */
+static void IDNextChar (CharSource* S)
+/* Read the next character from the input text */
{
- InputFile* I;
+ C = *S->V.Data.Pos++;
+ if (C == '\0') {
+ /* End of input data */
+ --S->V.Data.Pos;
+ C = EOF;
+ }
+}
- /* Restore the old token */
- Tok = IFile->Tok;
- C = IFile->C;
- /* Save a pointer to the current struct, then set it back */
- I = IFile;
- IFile = I->Next;
+void IDDone (CharSource* S)
+/* Close the current input data */
+{
/* Cleanup the current stuff */
- fclose (I->F);
- xfree (I);
- --ICount;
+ if (S->V.Data.Malloced) {
+ xfree (S->V.Data.Text);
+ }
}
-void NewInputData (char* Data, int Malloced)
+/* Set of input data handling functions */
+static const CharSourceFunctions IDFunc = {
+ IDMarkStart,
+ IDNextChar,
+ IDDone
+};
+
+
+
+void NewInputData (char* Text, int Malloced)
/* Add a chunk of input data to the input stream */
{
- InputData* I;
-
- /* Create a new state variable and initialize it */
- I = xmalloc (sizeof (*I));
- I->Data = Data;
- I->Pos = Data;
- I->Malloced = Malloced;
- I->Tok = Tok;
- I->C = C;
+ CharSource* S;
- /* Use the new data */
- I->Next = IData;
- IData = I;
+ /* Create a new input source variable and initialize it */
+ S = xmalloc (sizeof (*S));
+ S->Func = &IDFunc;
+ S->V.Data.Text = Text;
+ S->V.Data.Pos = Text;
+ S->V.Data.Malloced = Malloced;
- /* Prime the pump */
- NextChar ();
+ /* Use this input source */
+ UseCharSource (S);
}
-static void DoneInputData (void)
-/* End the current input data stream */
+/*****************************************************************************/
+/* Character classification functions */
+/*****************************************************************************/
+
+
+
+int IsIdChar (int C)
+/* Return true if the character is a valid character for an identifier */
{
- InputData* I;
+ return IsAlNum (C) ||
+ (C == '_') ||
+ (C == '@' && AtInIdents) ||
+ (C == '$' && DollarInIdents);
+}
- /* Restore the old token */
- Tok = IData->Tok;
- C = IData->C;
- /* Save a pointer to the current struct, then set it back */
- I = IData;
- IData = I->Next;
- /* Cleanup the current stuff */
- if (I->Malloced) {
- xfree (I->Data);
- }
- xfree (I);
+int IsIdStart (int C)
+/* Return true if the character may start an identifier */
+{
+ return IsAlpha (C) || C == '_';
}
+/*****************************************************************************/
+/* Code */
+/*****************************************************************************/
+
+
+
static unsigned DigitVal (unsigned char C)
/* Convert a digit into it's numerical representation */
{
static void NextChar (void)
/* Read the next character from the input file */
{
- /* If we have an input data structure, read from there */
- if (IData) {
-
- C = *IData->Pos++;
- if (C == '\0') {
- /* End of input data, will set to last file char */
- DoneInputData ();
- }
-
- } else {
-
- /* Check for end of line, read the next line if needed */
- while (IFile->Line [IFile->Pos.Col] == '\0') {
-
- unsigned Len, Removed;
-
- /* End of current line reached, read next line */
- if (fgets (IFile->Line, sizeof (IFile->Line), IFile->F) == 0) {
- /* End of file. Add an empty line to the listing. This is a
- * small hack needed to keep the PC output in sync.
- */
- NewListingLine ("", IFile->Pos.Name, ICount);
- C = EOF;
- return;
- }
-
- /* For better handling of files with unusual line endings (DOS
- * files that are accidently translated on Unix for example),
- * first remove all whitespace at the end, then add a single
- * newline.
- */
- Len = strlen (IFile->Line);
- Removed = 0;
- while (Len > 0 && IsSpace (IFile->Line[Len-1])) {
- ++Removed;
- --Len;
- }
- if (Removed) {
- IFile->Line[Len+0] = '\n';
- IFile->Line[Len+1] = '\0';
- }
-
- /* One more line */
- IFile->Pos.Line++;
- IFile->Pos.Col = 0;
-
- /* Remember the new line for the listing */
- NewListingLine (IFile->Line, IFile->Pos.Name, ICount);
-
- }
-
- /* Return the next character from the file */
- C = IFile->Line [IFile->Pos.Col++];
-
- }
+ Source->Func->NextChar (Source);
}
void LocaseSVal (void)
/* Make SVal lower case */
{
- unsigned I = 0;
- while (SVal [I]) {
- SVal [I] = tolower (SVal [I]);
- ++I;
- }
+ SB_ToLower (&CurTok.SVal);
}
void UpcaseSVal (void)
/* Make SVal upper case */
{
- unsigned I = 0;
- while (SVal [I]) {
- SVal [I] = toupper (SVal [I]);
- ++I;
- }
+ SB_ToUpper (&CurTok.SVal);
}
-static unsigned char FindDotKeyword (void)
+static token_t FindDotKeyword (void)
/* Find the dot keyword in SVal. Return the corresponding token if found,
* return TOK_NONE if not found.
*/
{
- static const struct DotKeyword K = { SVal, 0 };
+ struct DotKeyword K;
struct DotKeyword* R;
+ /* Initialize K */
+ K.Key = SB_GetConstBuf (&CurTok.SVal);
+ K.Tok = 0;
+
/* If we aren't in ignore case mode, we have to uppercase the keyword */
if (!IgnoreCase) {
UpcaseSVal ();
/* Search for the keyword */
R = bsearch (&K, DotKeywords, sizeof (DotKeywords) / sizeof (DotKeywords [0]),
- sizeof (DotKeywords [0]), CmpDotKeyword);
+ sizeof (DotKeywords [0]), CmpDotKeyword);
if (R != 0) {
return R->Tok;
} else {
-static void ReadIdent (unsigned Index)
+static void ReadIdent (void)
/* Read an identifier from the current input position into Ident. Filling SVal
- * starts at Index with the current character in C. It is assumed that any
- * characters already filled in are ok, and the character in C is checked.
+ * starts at the current position with the next character in C. It is assumed
+ * that any characters already filled in are ok, and the character in C is
+ * checked.
*/
{
/* Read the identifier */
do {
- if (Index < MAX_STR_LEN) {
- SVal [Index++] = C;
- }
- NextChar ();
+ SB_AppendChar (&CurTok.SVal, C);
+ NextChar ();
} while (IsIdChar (C));
- SVal [Index] = '\0';
+ SB_Terminate (&CurTok.SVal);
/* If we should ignore case, convert the identifier to upper case */
if (IgnoreCase) {
-static unsigned ReadStringConst (int StringTerm)
-/* Read a string constant into SVal. Check for maximum string length and all
- * other stuff. The length of the string is returned.
- */
+static void ReadStringConst (int StringTerm)
+/* Read a string constant into SVal. */
{
- unsigned I;
-
/* Skip the leading string terminator */
NextChar ();
/* Read the string */
- I = 0;
while (1) {
if (C == StringTerm) {
break;
break;
}
- /* Check for string length, print an error message once */
- if (I == MAX_STR_LEN) {
- Error ("Maximum string size exceeded");
- } else if (I < MAX_STR_LEN) {
- SVal [I] = C;
- }
- ++I;
+ /* Append the char to the string */
+ SB_AppendChar (&CurTok.SVal, C);
- /* Skip the character */
+ /* Skip the character */
NextChar ();
}
NextChar ();
/* Terminate the string */
- if (I >= MAX_STR_LEN) {
- I = MAX_STR_LEN;
- }
- SVal [I] = '\0';
-
- /* Return the length of the string */
- return I;
+ SB_Terminate (&CurTok.SVal);
}
-static int Sweet16Reg (const char* Ident)
+static int Sweet16Reg (const StrBuf* Id)
/* Check if the given identifier is a sweet16 register. Return -1 if this is
* not the case, return the register number otherwise.
*/
unsigned RegNum;
char Check;
- if (Ident[0] != 'r' && Ident[0] != 'R') {
+ if (SB_GetLen (Id) < 2) {
+ return -1;
+ }
+ if (toupper (SB_AtUnchecked (Id, 0)) != 'R') {
return -1;
}
- if (!IsDigit (Ident[1])) {
+ if (!IsDigit (SB_AtUnchecked (Id, 1))) {
return -1;
}
- if (sscanf (Ident+1, "%u%c", &RegNum, &Check) != 1 || RegNum > 15) {
+ if (sscanf (SB_GetConstBuf (Id)+1, "%u%c", &RegNum, &Check) != 1 || RegNum > 15) {
/* Invalid register */
return -1;
}
{
/* If we've a forced end of assembly, don't read further */
if (ForcedEnd) {
- Tok = TOK_EOF;
+ CurTok.Tok = TOK_EOF;
return;
}
Again:
/* Skip whitespace, remember if we had some */
- if ((WS = IsBlank (C)) != 0) {
+ if ((CurTok.WS = IsBlank (C)) != 0) {
do {
NextChar ();
} while (IsBlank (C));
}
- /* If we're reading from the file, update the location from where the
- * next token will be read. If we're reading from input data, keep the
- * current position.
- */
- if (IData == 0) {
- CurPos = IFile->Pos;
- }
+ /* Mark the file position of the next token */
+ Source->Func->MarkStart (Source);
+
+ /* Clear the string attribute */
+ SB_Clear (&CurTok.SVal);
+
+ /* Generate line info for the current token */
+ GenLineInfo (LI_SLOT_ASM, &CurTok.Pos);
/* Hex number or PC symbol? */
if (C == '$') {
/* Hex digit must follow or DollarIsPC must be enabled */
if (!IsXDigit (C)) {
if (DollarIsPC) {
- Tok = TOK_PC;
+ CurTok.Tok = TOK_PC;
return;
} else {
Error ("Hexadecimal digit expected");
}
}
- /* Read the number */
- IVal = 0;
+ /* Read the number */
+ CurTok.IVal = 0;
while (IsXDigit (C)) {
- if (IVal & 0xF0000000) {
+ if (CurTok.IVal & 0xF0000000) {
Error ("Overflow in hexadecimal number");
- IVal = 0;
+ CurTok.IVal = 0;
}
- IVal = (IVal << 4) + DigitVal (C);
+ CurTok.IVal = (CurTok.IVal << 4) + DigitVal (C);
NextChar ();
}
/* This is an integer constant */
- Tok = TOK_INTCON;
+ CurTok.Tok = TOK_INTCON;
return;
}
Error ("Binary digit expected");
}
- /* Read the number */
- IVal = 0;
+ /* Read the number */
+ CurTok.IVal = 0;
while (IsBDigit (C)) {
- if (IVal & 0x80000000) {
- Error ("Overflow in binary number");
- IVal = 0;
+ if (CurTok.IVal & 0x80000000) {
+ Error ("Overflow in binary number");
+ CurTok.IVal = 0;
}
- IVal = (IVal << 1) + DigitVal (C);
+ CurTok.IVal = (CurTok.IVal << 1) + DigitVal (C);
NextChar ();
}
/* This is an integer constant */
- Tok = TOK_INTCON;
+ CurTok.Tok = TOK_INTCON;
return;
}
- /* Decimal number? */
+ /* Number? */
if (IsDigit (C)) {
- /* Read the number */
- IVal = 0;
- while (IsDigit (C)) {
- if (IVal > (long) (0xFFFFFFFFUL / 10)) {
- Error ("Overflow in decimal number");
- IVal = 0;
+ char Buf[16];
+ unsigned Digits;
+ unsigned Base;
+ unsigned I;
+ long Max;
+ unsigned DVal;
+
+ /* Ignore leading zeros */
+ while (C == '0') {
+ NextChar ();
+ }
+
+ /* Read the number into Buf counting the digits */
+ Digits = 0;
+ while (IsXDigit (C)) {
+
+ /* Buf is big enough to allow any decimal and hex number to
+ * overflow, so ignore excess digits here, they will be detected
+ * when we convert the value.
+ */
+ if (Digits < sizeof (Buf)) {
+ Buf[Digits++] = C;
+ }
+
+ NextChar ();
+ }
+
+ /* Allow zilog/intel style hex numbers with a 'h' suffix */
+ if (C == 'h' || C == 'H') {
+ NextChar ();
+ Base = 16;
+ Max = 0xFFFFFFFFUL / 16;
+ } else {
+ Base = 10;
+ Max = 0xFFFFFFFFUL / 10;
+ }
+
+ /* Convert the number using the given base */
+ CurTok.IVal = 0;
+ for (I = 0; I < Digits; ++I) {
+ if (CurTok.IVal > Max) {
+ Error ("Number out of range");
+ CurTok.IVal = 0;
+ break;
}
- IVal = (IVal * 10) + DigitVal (C);
- NextChar ();
- }
+ DVal = DigitVal (Buf[I]);
+ if (DVal > Base) {
+ Error ("Invalid digits in number");
+ CurTok.IVal = 0;
+ break;
+ }
+ CurTok.IVal = (CurTok.IVal * Base) + DVal;
+ }
/* This is an integer constant */
- Tok = TOK_INTCON;
- return;
+ CurTok.Tok = TOK_INTCON;
+ return;
}
/* Control command? */
if (!IsIdStart (C)) {
/* Just a dot */
- Tok = TOK_DOT;
+ CurTok.Tok = TOK_DOT;
} else {
/* Read the remainder of the identifier */
- SVal[0] = '.';
- ReadIdent (1);
+ SB_AppendChar (&CurTok.SVal, '.');
+ ReadIdent ();
/* Dot keyword, search for it */
- Tok = FindDotKeyword ();
- if (Tok == TOK_NONE) {
+ CurTok.Tok = FindDotKeyword ();
+ if (CurTok.Tok == TOK_NONE) {
- /* Not found */
+ /* Not found */
if (!LeadingDotInIdents) {
/* Invalid pseudo instruction */
- Error ("`%s' is not a recognized control command", SVal);
+ Error ("`%m%p' is not a recognized control command", &CurTok.SVal);
goto Again;
}
/* An identifier with a dot. Check if it's a define style
* macro.
*/
- if (IsDefine (SVal)) {
+ if (IsDefine (&CurTok.SVal)) {
/* This is a define style macro - expand it */
MacExpandStart ();
goto Restart;
}
/* Just an identifier with a dot */
- Tok = TOK_IDENT;
+ CurTok.Tok = TOK_IDENT;
}
}
*/
if (CPU == CPU_SWEET16 && C == '@') {
NextChar ();
- Tok = TOK_AT;
+ CurTok.Tok = TOK_AT;
return;
}
/* Local symbol? */
if (C == LocalStart) {
- /* Read the identifier */
- ReadIdent (0);
+ /* Read the identifier. */
+ ReadIdent ();
/* Start character alone is not enough */
- if (SVal [1] == '\0') {
+ if (SB_GetLen (&CurTok.SVal) == 1) {
Error ("Invalid cheap local symbol");
goto Again;
}
/* A local identifier */
- Tok = TOK_LOCAL_IDENT;
+ CurTok.Tok = TOK_LOCAL_IDENT;
return;
}
if (IsIdStart (C)) {
/* Read the identifier */
- ReadIdent (0);
+ ReadIdent ();
/* Check for special names. Bail out if we have identified the type of
* the token. Go on if the token is an identifier.
*/
- if (SVal[1] == '\0') {
- switch (toupper (SVal [0])) {
+ if (SB_GetLen (&CurTok.SVal) == 1) {
+ switch (toupper (SB_AtUnchecked (&CurTok.SVal, 0))) {
- case 'A':
+ case 'A':
if (C == ':') {
NextChar ();
- Tok = TOK_OVERRIDE_ABS;
+ CurTok.Tok = TOK_OVERRIDE_ABS;
} else {
- Tok = TOK_A;
+ CurTok.Tok = TOK_A;
}
return;
case 'F':
if (C == ':') {
NextChar ();
- Tok = TOK_OVERRIDE_FAR;
+ CurTok.Tok = TOK_OVERRIDE_FAR;
return;
}
break;
case 'S':
- Tok = TOK_S;
- return;
+ if (CPU == CPU_65816) {
+ CurTok.Tok = TOK_S;
+ return;
+ }
+ break;
case 'X':
- Tok = TOK_X;
+ CurTok.Tok = TOK_X;
return;
case 'Y':
- Tok = TOK_Y;
+ CurTok.Tok = TOK_Y;
return;
case 'Z':
if (C == ':') {
NextChar ();
- Tok = TOK_OVERRIDE_ZP;
+ CurTok.Tok = TOK_OVERRIDE_ZP;
return;
}
break;
- default:
+ default:
break;
}
- } else if (CPU == CPU_SWEET16 && (IVal = Sweet16Reg (SVal)) >= 0) {
+ } else if (CPU == CPU_SWEET16 &&
+ (CurTok.IVal = Sweet16Reg (&CurTok.SVal)) >= 0) {
/* A sweet16 register number in sweet16 mode */
- Tok = TOK_REG;
+ CurTok.Tok = TOK_REG;
return;
}
/* Check for define style macro */
- if (IsDefine (SVal)) {
+ if (IsDefine (&CurTok.SVal)) {
/* Macro - expand it */
MacExpandStart ();
goto Restart;
} else {
/* An identifier */
- Tok = TOK_IDENT;
+ CurTok.Tok = TOK_IDENT;
}
return;
}
case '+':
NextChar ();
- Tok = TOK_PLUS;
+ CurTok.Tok = TOK_PLUS;
return;
case '-':
NextChar ();
- Tok = TOK_MINUS;
+ CurTok.Tok = TOK_MINUS;
return;
case '/':
NextChar ();
- Tok = TOK_DIV;
+ if (C != '*') {
+ CurTok.Tok = TOK_DIV;
+ } else if (CComments) {
+ /* Remember the position, then skip the '*' */
+ FilePos Pos = CurTok.Pos;
+ NextChar ();
+ do {
+ while (C != '*') {
+ if (C == EOF) {
+ PError (&Pos, "Unterminated comment");
+ goto CharAgain;
+ }
+ NextChar ();
+ }
+ NextChar ();
+ } while (C != '/');
+ NextChar ();
+ goto Again;
+ }
return;
case '*':
NextChar ();
- Tok = TOK_MUL;
+ CurTok.Tok = TOK_MUL;
return;
case '^':
NextChar ();
- Tok = TOK_XOR;
+ CurTok.Tok = TOK_XOR;
return;
case '&':
NextChar ();
if (C == '&') {
NextChar ();
- Tok = TOK_BOOLAND;
+ CurTok.Tok = TOK_BOOLAND;
} else {
- Tok = TOK_AND;
+ CurTok.Tok = TOK_AND;
}
return;
NextChar ();
if (C == '|') {
NextChar ();
- Tok = TOK_BOOLOR;
+ CurTok.Tok = TOK_BOOLOR;
} else {
- Tok = TOK_OR;
+ CurTok.Tok = TOK_OR;
}
return;
case ':':
NextChar ();
- switch (C) {
+ switch (C) {
case ':':
NextChar ();
- Tok = TOK_NAMESPACE;
+ CurTok.Tok = TOK_NAMESPACE;
break;
case '-':
- IVal = 0;
+ CurTok.IVal = 0;
do {
- --IVal;
- NextChar ();
+ --CurTok.IVal;
+ NextChar ();
} while (C == '-');
- Tok = TOK_ULABEL;
+ CurTok.Tok = TOK_ULABEL;
break;
case '+':
- IVal = 0;
+ CurTok.IVal = 0;
do {
- ++IVal;
+ ++CurTok.IVal;
NextChar ();
} while (C == '+');
- Tok = TOK_ULABEL;
+ CurTok.Tok = TOK_ULABEL;
break;
case '=':
NextChar ();
- Tok = TOK_ASSIGN;
+ CurTok.Tok = TOK_ASSIGN;
break;
default:
- Tok = TOK_COLON;
+ CurTok.Tok = TOK_COLON;
break;
}
return;
case ',':
NextChar ();
- Tok = TOK_COMMA;
+ CurTok.Tok = TOK_COMMA;
return;
case ';':
case '#':
NextChar ();
- Tok = TOK_HASH;
+ CurTok.Tok = TOK_HASH;
return;
case '(':
NextChar ();
- Tok = TOK_LPAREN;
+ CurTok.Tok = TOK_LPAREN;
return;
case ')':
NextChar ();
- Tok = TOK_RPAREN;
+ CurTok.Tok = TOK_RPAREN;
return;
case '[':
NextChar ();
- Tok = TOK_LBRACK;
+ CurTok.Tok = TOK_LBRACK;
return;
case ']':
NextChar ();
- Tok = TOK_RBRACK;
+ CurTok.Tok = TOK_RBRACK;
return;
case '{':
NextChar ();
- Tok = TOK_LCURLY;
+ CurTok.Tok = TOK_LCURLY;
return;
case '}':
NextChar ();
- Tok = TOK_RCURLY;
+ CurTok.Tok = TOK_RCURLY;
return;
case '<':
NextChar ();
if (C == '=') {
NextChar ();
- Tok = TOK_LE;
+ CurTok.Tok = TOK_LE;
} else if (C == '<') {
NextChar ();
- Tok = TOK_SHL;
+ CurTok.Tok = TOK_SHL;
} else if (C == '>') {
NextChar ();
- Tok = TOK_NE;
+ CurTok.Tok = TOK_NE;
} else {
- Tok = TOK_LT;
+ CurTok.Tok = TOK_LT;
}
return;
case '=':
- NextChar ();
- Tok = TOK_EQ;
+ NextChar ();
+ CurTok.Tok = TOK_EQ;
return;
case '!':
NextChar ();
- Tok = TOK_BOOLNOT;
+ CurTok.Tok = TOK_BOOLNOT;
return;
case '>':
- NextChar ();
+ NextChar ();
if (C == '=') {
NextChar ();
- Tok = TOK_GE;
+ CurTok.Tok = TOK_GE;
} else if (C == '>') {
NextChar ();
- Tok = TOK_SHR;
+ CurTok.Tok = TOK_SHR;
} else {
- Tok = TOK_GT;
+ CurTok.Tok = TOK_GT;
}
return;
case '~':
NextChar ();
- Tok = TOK_NOT;
- return;
+ CurTok.Tok = TOK_NOT;
+ return;
case '\'':
/* Hack: If we allow ' as terminating character for strings, read
* string later.
*/
if (LooseStringTerm) {
- if (ReadStringConst ('\'') == 1) {
- IVal = SVal[0];
- Tok = TOK_CHARCON;
+ ReadStringConst ('\'');
+ if (SB_GetLen (&CurTok.SVal) == 1) {
+ CurTok.IVal = SB_AtUnchecked (&CurTok.SVal, 0);
+ CurTok.Tok = TOK_CHARCON;
} else {
- Tok = TOK_STRCON;
+ CurTok.Tok = TOK_STRCON;
}
} else {
/* Always a character constant */
Error ("Illegal character constant");
goto CharAgain;
}
- IVal = C;
- Tok = TOK_CHARCON;
+ CurTok.IVal = C;
+ CurTok.Tok = TOK_CHARCON;
NextChar ();
if (C != '\'') {
if (!MissingCharTerm) {
return;
case '\"':
- ReadStringConst ('\"');
- Tok = TOK_STRCON;
+ ReadStringConst ('\"');
+ CurTok.Tok = TOK_STRCON;
return;
case '\\':
/* Line continuation? */
if (LineCont) {
- NextChar ();
- if (C == '\n') {
- /* Handle as white space */
- NextChar ();
- C = ' ';
- goto Again;
- }
- }
- break;
+ NextChar ();
+ if (C == '\n') {
+ /* Handle as white space */
+ NextChar ();
+ C = ' ';
+ goto Again;
+ }
+ }
+ break;
case '\n':
- NextChar ();
- Tok = TOK_SEP;
- return;
+ NextChar ();
+ CurTok.Tok = TOK_SEP;
+ return;
case EOF:
- /* Check if we have any open .IFs in this file */
- CheckOpenIfs ();
- /* Check if we have any open token lists in this file */
- CheckInputStack ();
-
- /* If this was an include file, then close it and handle like a
- * separator. Do not close the main file, but return EOF.
- */
- if (ICount > 1) {
- DoneInputFile ();
- } else {
- Tok = TOK_EOF;
- }
- return;
-
+ CheckInputStack ();
+ /* In case of the main file, do not close it, but return EOF. */
+ if (Source && Source->Next) {
+ DoneCharSource ();
+ goto Again;
+ } else {
+ CurTok.Tok = TOK_EOF;
+ }
+ return;
}
/* If we go here, we could not identify the current character. Skip it
-int TokHasSVal (enum Token Tok)
-/* Return true if the given token has an attached SVal */
-{
- return (Tok == TOK_IDENT || TOK_LOCAL_IDENT || Tok == TOK_STRCON);
-}
-
-
-
-int TokHasIVal (enum Token Tok)
-/* Return true if the given token has an attached IVal */
-{
- return (Tok == TOK_INTCON || Tok == TOK_CHARCON || Tok == TOK_REG);
-}
-
-
-
int GetSubKey (const char** Keys, unsigned Count)
/* Search for a subkey in a table of keywords. The current token must be an
* identifier and all keys must be in upper case. The identifier will be
unsigned I;
/* Must have an identifier */
- PRECONDITION (Tok == TOK_IDENT);
+ PRECONDITION (CurTok.Tok == TOK_IDENT);
/* If we aren't in ignore case mode, we have to uppercase the identifier */
if (!IgnoreCase) {
- UpcaseSVal ();
+ UpcaseSVal ();
}
/* Do a linear search (a binary search is not worth the effort) */
for (I = 0; I < Count; ++I) {
- if (strcmp (SVal, Keys [I]) == 0) {
- /* Found it */
- return I;
- }
+ if (SB_CompareStr (&CurTok.SVal, Keys [I]) == 0) {
+ /* Found it */
+ return I;
+ }
}
/* Not found */
* error message and return ADDR_SIZE_DEFAULT.
*/
{
- static const char* Keys[] = {
- "DIRECT", "ZEROPAGE", "ZP",
- "ABSOLUTE", "ABS", "NEAR",
- "FAR",
- "LONG", "DWORD",
- };
+ unsigned char AddrSize;
/* Check for an identifier */
- if (Tok != TOK_IDENT) {
+ if (CurTok.Tok != TOK_IDENT) {
Error ("Address size specifier expected");
return ADDR_SIZE_DEFAULT;
}
- /* Search for the attribute */
- switch (GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]))) {
- case 0:
- case 1:
- case 2: return ADDR_SIZE_ZP;
- case 3:
- case 4:
- case 5: return ADDR_SIZE_ABS;
- case 6: return ADDR_SIZE_FAR;
- case 7:
- case 8: return ADDR_SIZE_LONG;
- default:
- Error ("Address size specifier expected");
- return ADDR_SIZE_DEFAULT;
+ /* Convert the attribute */
+ AddrSize = AddrSizeFromStr (SB_GetConstBuf (&CurTok.SVal));
+ if (AddrSize == ADDR_SIZE_INVALID) {
+ Error ("Address size specifier expected");
+ AddrSize = ADDR_SIZE_DEFAULT;
}
+
+ /* Done */
+ return AddrSize;
}
void DoneScanner (void)
/* Release scanner resources */
{
- DoneInputFile ();
+ DoneCharSource ();
}
+