/* */
/* */
/* */
-/* (C) 1998-2000 Ullrich von Bassewitz */
-/* Wacholderweg 14 */
-/* D-70597 Stuttgart */
-/* EMail: uz@musoftware.de */
+/* (C) 1998-2003 Ullrich von Bassewitz */
+/* Römerstraße 52 */
+/* D-70794 Filderstadt */
+/* EMail: uz@cc65.org */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
#include <errno.h>
/* common */
+#include "assertdefs.h"
#include "bitops.h"
-#include "check.h"
+#include "cddefs.h"
+#include "coll.h"
+#include "symdefs.h"
+#include "tgttrans.h"
+#include "xmalloc.h"
/* ca65 */
+#include "anonname.h"
+#include "asserts.h"
#include "condasm.h"
+#include "dbginfo.h"
+#include "enum.h"
#include "error.h"
#include "expr.h"
+#include "feature.h"
#include "global.h"
+#include "incpath.h"
#include "instr.h"
#include "listing.h"
#include "macpack.h"
#include "nexttok.h"
#include "objcode.h"
#include "options.h"
+#include "pseudo.h"
#include "repeat.h"
+#include "segment.h"
+#include "sizeof.h"
+#include "spool.h"
+#include "struct.h"
+#include "symbol.h"
#include "symtab.h"
-#include "pseudo.h"
/* Keyword we're about to handle */
-static char Keyword [sizeof (SVal)+1] = ".";
+static char Keyword [sizeof (SVal)+1];
+
+/* Segment stack */
+#define MAX_PUSHED_SEGMENTS 16
+static Collection SegStack = STATIC_COLLECTION_INITIALIZER;
+static unsigned char OptionalAddrSize (void)
+/* If a colon follows, parse an optional address size spec and return it.
+ * Otherwise return ADDR_SIZE_DEFAULT.
+ */
+{
+ unsigned AddrSize = ADDR_SIZE_DEFAULT;
+ if (Tok == TOK_COLON) {
+ NextTok ();
+ AddrSize = ParseAddrSize ();
+ NextTok ();
+ }
+ return AddrSize;
+}
+
+
+
static void SetBoolOption (unsigned char* Flag)
/* Read a on/off/+/- option and set flag accordingly */
{
} else if (Tok == TOK_IDENT) {
/* Map the keyword to a number */
switch (GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]))) {
- case 0: *Flag = 0; NextTok (); break;
- case 1: *Flag = 1; NextTok (); break;
- default: ErrorSkip (ERR_ONOFF_EXPECTED); break;
+ case 0: *Flag = 0; NextTok (); break;
+ case 1: *Flag = 1; NextTok (); break;
+ default: ErrorSkip ("`on' or `off' expected"); break;
}
- } else if (Tok == TOK_SEP || Tok == TOK_EOF) {
+ } else if (TokIsSep (Tok)) {
/* Without anything assume switch on */
*Flag = 1;
} else {
- ErrorSkip (ERR_ONOFF_EXPECTED);
+ ErrorSkip ("`on' or `off' expected");
}
}
-static void ExportImport (void (*SymFunc) (const char*, int), int ZP)
+static void ExportImport (void (*Func) (SymEntry*, unsigned char, unsigned),
+ unsigned char DefAddrSize, unsigned Flags)
/* Export or import symbols */
{
+ SymEntry* Sym;
+ unsigned char AddrSize;
+
while (1) {
+
+ /* We need an identifier here */
if (Tok != TOK_IDENT) {
- ErrorSkip (ERR_IDENT_EXPECTED);
- break;
+ ErrorSkip ("Identifier expected");
+ return;
}
- SymFunc (SVal, ZP);
- NextTok ();
+
+ /* Find the symbol table entry, allocate a new one if necessary */
+ Sym = SymFind (CurrentScope, SVal, SYM_ALLOC_NEW);
+
+ /* Skip the name */
+ NextTok ();
+
+ /* Get an optional address size */
+ AddrSize = OptionalAddrSize ();
+ if (AddrSize == ADDR_SIZE_DEFAULT) {
+ AddrSize = DefAddrSize;
+ }
+
+ /* Call the actual import/export function */
+ Func (Sym, AddrSize, Flags);
+
+ /* More symbols? */
if (Tok == TOK_COMMA) {
NextTok ();
} else {
} else {
long Val = ConstExpression ();
if (Val < Min || Val > Max) {
- Error (ERR_RANGE);
+ Error ("Range error");
Val = Min;
}
return Val;
+static void ConDes (const char* Name, unsigned Type)
+/* Parse remaining line for constructor/destructor of the remaining type */
+{
+ long Prio;
+
+
+ /* Find the symbol table entry, allocate a new one if necessary */
+ SymEntry* Sym = SymFind (CurrentScope, Name, SYM_ALLOC_NEW);
+
+ /* Optional constructor priority */
+ if (Tok == TOK_COMMA) {
+ /* Priority value follows */
+ NextTok ();
+ Prio = ConstExpression ();
+ if (Prio < CD_PRIO_MIN || Prio > CD_PRIO_MAX) {
+ /* Value out of range */
+ Error ("Range error");
+ return;
+ }
+ } else {
+ /* Use the default priority value */
+ Prio = CD_PRIO_DEF;
+ }
+
+ /* Define the symbol */
+ SymConDes (Sym, ADDR_SIZE_DEFAULT, Type, (unsigned) Prio);
+}
+
+
+
/*****************************************************************************/
/* Handler functions */
/*****************************************************************************/
/* Switch the accu to 16 bit mode (assembler only) */
{
if (GetCPU() != CPU_65816) {
- Error (ERR_816_MODE_ONLY);
+ Error ("Command is only valid in 65816 mode");
} else {
/* Immidiate mode has two extension bytes */
ExtBytes [AMI_IMM_ACCU] = 2;
/* Switch the accu to 8 bit mode (assembler only) */
{
if (GetCPU() != CPU_65816) {
- Error (ERR_816_MODE_ONLY);
+ Error ("Command is only valid in 65816 mode");
} else {
/* Immidiate mode has one extension byte */
ExtBytes [AMI_IMM_ACCU] = 1;
{
while (1) {
if (GetCPU() == CPU_65816) {
- EmitWord (ForceWordExpr (Expression ()));
+ EmitWord (GenWordExpr (Expression ()));
} else {
/* Do a range check */
EmitWord (Expression ());
/* Read the alignment value */
Align = ConstExpression ();
if (Align <= 0 || Align > 0x10000) {
- ErrorSkip (ERR_RANGE);
+ ErrorSkip ("Range error");
return;
}
Val = ConstExpression ();
/* We need a byte value here */
if (!IsByteRange (Val)) {
- ErrorSkip (ERR_RANGE);
+ ErrorSkip ("Range error");
return;
}
} else {
/* Check if the alignment is a power of two */
Bit = BitFind (Align);
if (Align != (0x01L << Bit)) {
- Error (ERR_ALIGN);
+ Error ("Alignment value must be a power of 2");
} else {
SegAlign (Bit, (int) Val);
}
static void DoASCIIZ (void)
/* Define text with a zero terminator */
{
+ unsigned Len;
+
while (1) {
+ /* Must have a string constant */
if (Tok != TOK_STRCON) {
- ErrorSkip (ERR_STRCON_EXPECTED);
+ ErrorSkip ("String constant expected");
return;
}
- EmitData ((unsigned char*) SVal, strlen (SVal));
+
+ /* Get the length of the string constant */
+ Len = strlen (SVal);
+
+ /* Translate into target charset and emit */
+ TgtTranslateBuf (SVal, Len);
+ EmitData ((unsigned char*) SVal, Len);
NextTok ();
if (Tok == TOK_COMMA) {
NextTok ();
+static void DoAssert (void)
+/* Add an assertion */
+{
+ static const char* ActionTab [] = {
+ "WARN", "WARNING",
+ "ERROR"
+ };
+
+ int Action;
+
+
+ /* First we have the expression that has to evaluated */
+ ExprNode* Expr = Expression ();
+ ConsumeComma ();
+
+ /* Action follows */
+ if (Tok != TOK_IDENT) {
+ ErrorSkip ("Identifier expected");
+ return;
+ }
+ Action = GetSubKey (ActionTab, sizeof (ActionTab) / sizeof (ActionTab[0]));
+ switch (Action) {
+
+ case 0:
+ case 1:
+ /* Warning */
+ Action = ASSERT_ACT_WARN;
+ break;
+
+ case 2:
+ /* Error */
+ Action = ASSERT_ACT_ERROR;
+ break;
+
+ default:
+ Error ("Illegal assert action specifier");
+ }
+ NextTok ();
+ ConsumeComma ();
+
+ /* Read the message */
+ if (Tok != TOK_STRCON) {
+ ErrorSkip ("String constant expected");
+ } else {
+ AddAssertion (Expr, Action, GetStringId (SVal));
+ NextTok ();
+ }
+}
+
+
+
static void DoAutoImport (void)
/* Mark unresolved symbols as imported */
{
static void DoBss (void)
/* Switch to the BSS segment */
{
- UseBssSeg ();
+ UseSeg (&BssSegDef);
}
{
while (1) {
if (Tok == TOK_STRCON) {
- /* A string */
- EmitData ((unsigned char*) SVal, strlen (SVal));
+ /* A string, translate into target charset and emit */
+ unsigned Len = strlen (SVal);
+ TgtTranslateBuf (SVal, Len);
+ EmitData ((unsigned char*) SVal, Len);
NextTok ();
} else {
EmitByte (Expression ());
NextTok ();
/* Do smart handling of dangling comma */
if (Tok == TOK_SEP) {
- Error (ERR_UNEXPECTED_EOL);
+ Error ("Unexpected end of line");
break;
}
}
+static void DoCharMap (void)
+/* Allow custome character mappings */
+{
+ long Index;
+ long Code;
+
+ /* Read the index as numerical value */
+ Index = ConstExpression ();
+ if (Index < 1 || Index > 255) {
+ /* Value out of range */
+ ErrorSkip ("Range error");
+ return;
+ }
+
+ /* Comma follows */
+ ConsumeComma ();
+
+ /* Read the character code */
+ Code = ConstExpression ();
+ if (Code < 1 || Code > 255) {
+ /* Value out of range */
+ ErrorSkip ("Range error");
+ return;
+ }
+
+ /* Set the character translation */
+ TgtTranslateSet ((unsigned) Index, (unsigned char) Code);
+}
+
+
+
static void DoCode (void)
/* Switch to the code segment */
{
- UseCodeSeg ();
+ UseSeg (&CodeSegDef);
+}
+
+
+
+static void DoConDes (void)
+/* Export a symbol as constructor/destructor */
+{
+ static const char* Keys[] = {
+ "CONSTRUCTOR",
+ "DESTRUCTOR",
+ };
+ char Name [sizeof (SVal)];
+ long Type;
+
+ /* Symbol name follows */
+ if (Tok != TOK_IDENT) {
+ ErrorSkip ("Identifier expected");
+ return;
+ }
+ strcpy (Name, SVal);
+ NextTok ();
+
+ /* Type follows. May be encoded as identifier or numerical */
+ ConsumeComma ();
+ if (Tok == TOK_IDENT) {
+
+ /* Map the following keyword to a number, then skip it */
+ Type = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
+ NextTok ();
+
+ /* Check if we got a valid keyword */
+ if (Type < 0) {
+ Error ("Syntax error");
+ SkipUntilSep ();
+ return;
+ }
+
+ } else {
+
+ /* Read the type as numerical value */
+ Type = ConstExpression ();
+ if (Type < CD_TYPE_MIN || Type > CD_TYPE_MAX) {
+ /* Value out of range */
+ Error ("Range error");
+ return;
+ }
+
+ }
+
+ /* Parse the remainder of the line and export the symbol */
+ ConDes (Name, (unsigned) Type);
+}
+
+
+
+static void DoConstructor (void)
+/* Export a symbol as constructor */
+{
+ char Name [sizeof (SVal)];
+
+ /* Symbol name follows */
+ if (Tok != TOK_IDENT) {
+ ErrorSkip ("Identifier expected");
+ return;
+ }
+ strcpy (Name, SVal);
+ NextTok ();
+
+ /* Parse the remainder of the line and export the symbol */
+ ConDes (Name, CD_TYPE_CON);
}
static void DoData (void)
/* Switch to the data segment */
{
- UseDataSeg ();
+ UseSeg (&DataSegDef);
+}
+
+
+
+static void DoDbg (void)
+/* Add debug information from high level code */
+{
+ static const char* Keys[] = {
+ "FILE",
+ "LINE",
+ "SYM",
+ };
+ int Key;
+
+
+ /* We expect a subkey */
+ if (Tok != TOK_IDENT) {
+ ErrorSkip ("Identifier expected");
+ return;
+ }
+
+ /* Map the following keyword to a number */
+ Key = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
+
+ /* Skip the subkey */
+ NextTok ();
+
+ /* Check the key and dispatch to a handler */
+ switch (Key) {
+ case 0: DbgInfoFile (); break;
+ case 1: DbgInfoLine (); break;
+ case 2: DbgInfoSym (); break;
+ default: ErrorSkip ("Syntax error"); break;
+ }
}
/* Output double bytes */
{
while (1) {
- EmitWord (SwapExpr (Expression ()));
+ EmitWord (GenSwapExpr (Expression ()));
if (Tok != TOK_COMMA) {
break;
} else {
+static void DoDestructor (void)
+/* Export a symbol as destructor */
+{
+ char Name [sizeof (SVal)];
+
+ /* Symbol name follows */
+ if (Tok != TOK_IDENT) {
+ ErrorSkip ("Identifier expected");
+ return;
+ }
+ strcpy (Name, SVal);
+ NextTok ();
+
+ /* Parse the remainder of the line and export the symbol */
+ ConDes (Name, CD_TYPE_DES);
+}
+
+
+
static void DoDWord (void)
/* Define dwords */
{
/* End of assembly */
{
ForcedEnd = 1;
+ NextTok ();
}
static void DoEndProc (void)
/* Leave a lexical level */
{
- SymLeaveLevel ();
+ if (GetCurrentSymTabType () != ST_PROC) {
+ /* No local scope */
+ ErrorSkip ("No open .PROC");
+ } else {
+ SymLeaveLevel ();
+ }
+}
+
+
+
+static void DoEndScope (void)
+/* Leave a lexical level */
+{
+ if ( GetCurrentSymTabType () != ST_SCOPE) {
+ /* No local scope */
+ ErrorSkip ("No open .SCOPE");
+ } else {
+ SymLeaveLevel ();
+ }
}
/* User error */
{
if (Tok != TOK_STRCON) {
- ErrorSkip (ERR_STRCON_EXPECTED);
+ ErrorSkip ("String constant expected");
} else {
- Error (ERR_USER, SVal);
+ Error ("User error: %s", SVal);
SkipUntilSep ();
}
}
static void DoExport (void)
/* Export a symbol */
{
- ExportImport (SymExport, 0);
+ ExportImport (SymExport, ADDR_SIZE_DEFAULT, SF_NONE);
}
static void DoExportZP (void)
/* Export a zeropage symbol */
{
- ExportImport (SymExport, 1);
+ ExportImport (SymExport, ADDR_SIZE_ZP, SF_NONE);
}
static void DoFeature (void)
/* Switch the Feature option */
{
- int Feature;
-
- static const char* Keys[] = {
- "DOLLAR_IS_PC",
- "LABELS_WITHOUT_COLONS",
- "LOOSE_STRING_TERM",
- "AT_IN_IDENTIFIERS",
- "DOLLAR_IN_IDENTIFIERS",
- };
-
/* Allow a list of comma separated keywords */
while (1) {
/* We expect an identifier */
if (Tok != TOK_IDENT) {
- ErrorSkip (ERR_IDENT_EXPECTED);
+ ErrorSkip ("Identifier expected");
return;
}
- /* Map the keyword to a number */
- Feature = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
- if (Feature < 0) {
+ /* Make the string attribute lower case */
+ LocaseSVal ();
+
+ /* Set the feature and check for errors */
+ if (SetFeature (SVal) == FEAT_UNKNOWN) {
/* Not found */
- ErrorSkip (ERR_ILLEGAL_FEATURE);
+ ErrorSkip ("Invalid feature: `%s'", SVal);
return;
- }
-
- /* Skip the keyword */
- NextTok ();
-
- /* Switch the feature on */
- switch (Feature) {
- case 0: DollarIsPC = 1; break;
- case 1: NoColonLabels = 1; break;
- case 2: LooseStringTerm = 1; break;
- case 3: AtInIdents = 1; break;
- case 4: DollarInIdents = 1; break;
- default: Internal ("Invalid feature: %d", Feature);
- }
+ } else {
+ /* Skip the keyword */
+ NextTok ();
+ }
/* Allow more than one keyword */
if (Tok == TOK_COMMA) {
OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
if (OptNum < 0) {
/* Not found */
- ErrorSkip (ERR_OPTION_KEY_EXPECTED);
+ ErrorSkip ("File option keyword expected");
return;
}
/* We accept only string options for now */
if (Tok != TOK_STRCON) {
- ErrorSkip (ERR_STRCON_EXPECTED);
+ ErrorSkip ("String constant expected");
return;
}
break;
default:
- Internal ("Invalid OptNum: %l", OptNum);
+ Internal ("Invalid OptNum: %ld", OptNum);
}
/* Option given as number */
OptNum = ConstExpression ();
if (!IsByteRange (OptNum)) {
- ErrorSkip (ERR_RANGE);
+ ErrorSkip ("Range error");
return;
}
/* We accept only string options for now */
if (Tok != TOK_STRCON) {
- ErrorSkip (ERR_STRCON_EXPECTED);
+ ErrorSkip ("String constant expected");
return;
}
+static void DoForceImport (void)
+/* Do a forced import on a symbol */
+{
+ ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_FORCED);
+}
+
+
+
static void DoGlobal (void)
/* Declare a global symbol */
{
- ExportImport (SymGlobal, 0);
+ ExportImport (SymGlobal, ADDR_SIZE_DEFAULT, SF_NONE);
}
static void DoGlobalZP (void)
/* Declare a global zeropage symbol */
{
- ExportImport (SymGlobal, 1);
+ ExportImport (SymGlobal, ADDR_SIZE_ZP, SF_NONE);
}
/* Switch the index registers to 16 bit mode (assembler only) */
{
if (GetCPU() != CPU_65816) {
- Error (ERR_816_MODE_ONLY);
+ Error ("Command is only valid in 65816 mode");
} else {
/* Immidiate mode has two extension bytes */
ExtBytes [AMI_IMM_INDEX] = 2;
/* Switch the index registers to 16 bit mode (assembler only) */
{
if (GetCPU() != CPU_65816) {
- Error (ERR_816_MODE_ONLY);
+ Error ("Command is only valid in 65816 mode");
} else {
/* Immidiate mode has one extension byte */
ExtBytes [AMI_IMM_INDEX] = 1;
static void DoImport (void)
/* Import a symbol */
{
- ExportImport (SymImport, 0);
+ ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_NONE);
}
static void DoImportZP (void)
/* Import a zero page symbol */
{
- ExportImport (SymImport, 1);
+ ExportImport (SymImport, ADDR_SIZE_ZP, SF_NONE);
}
static void DoIncBin (void)
/* Include a binary file */
{
+ char Name [sizeof (SVal)];
+ long Start = 0L;
+ long Count = -1L;
+ long Size;
+ FILE* F;
+
/* Name must follow */
if (Tok != TOK_STRCON) {
- ErrorSkip (ERR_STRCON_EXPECTED);
+ ErrorSkip ("String constant expected");
+ return;
+ }
+ strcpy (Name, SVal);
+ NextTok ();
+
+ /* A starting offset may follow */
+ if (Tok == TOK_COMMA) {
+ NextTok ();
+ Start = ConstExpression ();
+
+ /* And a length may follow */
+ if (Tok == TOK_COMMA) {
+ NextTok ();
+ Count = ConstExpression ();
+ }
+
+ }
+
+ /* Try to open the file */
+ F = fopen (Name, "rb");
+ if (F == 0) {
+
+ /* Search for the file in the include directories. */
+ char* PathName = FindInclude (Name);
+ if (PathName == 0 || (F = fopen (PathName, "r")) == 0) {
+ /* Not found or cannot open, print an error and bail out */
+ ErrorSkip ("Cannot open include file `%s': %s", Name, strerror (errno));
+ }
+
+ /* Free the allocated memory */
+ xfree (PathName);
+
+ /* If we had an error before, bail out now */
+ if (F == 0) {
+ return;
+ }
+ }
+
+ /* Get the size of the file */
+ fseek (F, 0, SEEK_END);
+ Size = ftell (F);
+
+ /* If a count was not given, calculate it now */
+ if (Count < 0) {
+ Count = Size - Start;
+ if (Count < 0) {
+ /* Nothing to read - flag this as a range error */
+ ErrorSkip ("Range error");
+ goto Done;
+ }
} else {
- /* Try to open the file */
- FILE* F = fopen (SVal, "rb");
- if (F == 0) {
- Error (ERR_CANNOT_OPEN_INCLUDE, SVal, strerror (errno));
- } else {
- unsigned char Buf [1024];
- size_t Count;
- /* Read chunks and insert them into the output */
- while ((Count = fread (Buf, 1, sizeof (Buf), F)) > 0) {
- EmitData (Buf, Count);
- }
- /* Close the file, ignore errors since it's r/o */
- (void) fclose (F);
+ /* Count was given, check if it is valid */
+ if (Start + Count > Size) {
+ ErrorSkip ("Range error");
+ goto Done;
}
- /* Skip the name */
- NextTok ();
}
+
+ /* Seek to the start position */
+ fseek (F, Start, SEEK_SET);
+
+ /* Read chunks and insert them into the output */
+ while (Count > 0) {
+
+ unsigned char Buf [1024];
+
+ /* Calculate the number of bytes to read */
+ size_t BytesToRead = (Count > (long)sizeof(Buf))? sizeof(Buf) : (size_t) Count;
+
+ /* Read chunk */
+ size_t BytesRead = fread (Buf, 1, BytesToRead, F);
+ if (BytesToRead != BytesRead) {
+ /* Some sort of error */
+ ErrorSkip ("Cannot read from include file `%s': %s",
+ Name, strerror (errno));
+ break;
+ }
+
+ /* Insert it into the output */
+ EmitData (Buf, BytesRead);
+
+ /* Keep the counters current */
+ Count -= BytesRead;
+ }
+
+Done:
+ /* Close the file, ignore errors since it's r/o */
+ (void) fclose (F);
}
/* Name must follow */
if (Tok != TOK_STRCON) {
- ErrorSkip (ERR_STRCON_EXPECTED);
+ ErrorSkip ("String constant expected");
} else {
- strcpy (Name, SVal);
- NextTok ();
- NewInputFile (Name);
+ strcpy (Name, SVal);
+ NextTok ();
+ NewInputFile (Name);
}
}
/* Define the character that starts local labels */
{
if (Tok != TOK_CHARCON) {
- ErrorSkip (ERR_CHARCON_EXPECTED);
+ ErrorSkip ("Character constant expected");
} else {
if (IVal != '@' && IVal != '?') {
- Error (ERR_ILLEGAL_LOCALSTART);
+ Error ("Invalid start character for locals");
} else {
LocalStart = (char) IVal;
}
{
/* Macro package names */
static const char* Keys [] = {
- "GENERIC",
+ "GENERIC",
"LONGBRANCH",
+ "CBM",
+ "CPU"
};
int Package;
/* We expect an identifier */
if (Tok != TOK_IDENT) {
- ErrorSkip (ERR_IDENT_EXPECTED);
+ ErrorSkip ("Identifier expected");
return;
}
Package = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
if (Package < 0) {
/* Not found */
- ErrorSkip (ERR_ILLEGAL_MACPACK);
+ ErrorSkip ("Invalid macro package");
return;
}
static void DoNull (void)
/* Switch to the NULL segment */
{
- UseNullSeg ();
+ UseSeg (&NullSegDef);
}
/* Start absolute code */
{
long PC = ConstExpression ();
- if (PC < 0 || PC > 0xFFFF) {
- Error (ERR_RANGE);
+ if (PC < 0 || PC > 0xFFFFFF) {
+ Error ("Range error");
return;
}
SetAbsPC (PC);
/* Output a string */
{
if (Tok != TOK_STRCON) {
- ErrorSkip (ERR_STRCON_EXPECTED);
+ ErrorSkip ("String constant expected");
} else {
/* Output the string and be sure to flush the output to keep it in
* sync with any error messages if the output is redirected to a file.
+static void DoPopSeg (void)
+/* Pop an old segment from the segment stack */
+{
+ SegDef* Def;
+
+ /* Must have a segment on the stack */
+ if (CollCount (&SegStack) == 0) {
+ ErrorSkip ("Segment stack is empty");
+ return;
+ }
+
+ /* Pop the last element */
+ Def = CollPop (&SegStack);
+
+ /* Restore this segment */
+ UseSeg (Def);
+
+ /* Delete the segment definition */
+ FreeSegDef (Def);
+}
+
+
+
static void DoProc (void)
/* Start a new lexical scope */
{
+ char Name[sizeof(SVal)];
+ unsigned char AddrSize;
+
if (Tok == TOK_IDENT) {
- /* The new scope has a name */
- SymDef (SVal, CurrentPC (), IsZPSeg ());
- NextTok ();
+
+ SymEntry* Sym;
+
+ /* The new scope has a name. Remember it. */
+ strcpy (Name, SVal);
+
+ /* Search for the symbol, generate a new one if needed */
+ Sym = SymFind (CurrentScope, Name, SYM_ALLOC_NEW);
+
+ /* Skip the scope name */
+ NextTok ();
+
+ /* Read an optional address size specifier */
+ AddrSize = OptionalAddrSize ();
+
+ /* Mark the symbol as defined */
+ SymDef (Sym, GenCurrentPC (), AddrSize, SF_LABEL);
+
+ } else {
+
+ /* A .PROC statement without a name */
+ Warning (1, "Unnamed .PROCs are deprecated, please use .SCOPE");
+ AnonName (Name, sizeof (Name), "PROC");
+ AddrSize = ADDR_SIZE_DEFAULT;
+
}
- SymEnterLevel ();
+
+ /* Enter a new scope */
+ SymEnterLevel (Name, ST_PROC, AddrSize);
+}
+
+
+
+static void DoPSC02 (void)
+/* Switch to 65SC02 CPU */
+{
+ SetCPU (CPU_65SC02);
+}
+
+
+
+static void DoPushSeg (void)
+/* Push the current segment onto the segment stack */
+{
+ /* Can only push a limited size of segments */
+ if (CollCount (&SegStack) >= MAX_PUSHED_SEGMENTS) {
+ ErrorSkip ("Segment stack overflow");
+ return;
+ }
+
+ /* Get the current segment and push it */
+ CollAppend (&SegStack, DupSegDef (GetCurrentSegDef ()));
}
Count = ConstExpression ();
if (Count > 0xFFFF || Count < 0) {
- ErrorSkip (ERR_RANGE);
+ ErrorSkip ("Range error");
return;
}
if (Tok == TOK_COMMA) {
Val = ConstExpression ();
/* We need a byte value here */
if (!IsByteRange (Val)) {
- ErrorSkip (ERR_RANGE);
+ ErrorSkip ("Range error");
return;
}
static void DoROData (void)
/* Switch to the r/o data segment */
{
- UseRODataSeg ();
+ UseSeg (&RODataSegDef);
+}
+
+
+
+static void DoScope (void)
+/* Start a local scope */
+{
+ char Name[sizeof (SVal)];
+ unsigned char AddrSize;
+
+
+ if (Tok == TOK_IDENT) {
+
+ /* The new scope has a name. Remember and skip it. */
+ strcpy (Name, SVal);
+ NextTok ();
+
+ } else {
+
+ /* An unnamed scope */
+ AnonName (Name, sizeof (Name), "SCOPE");
+
+ }
+
+ /* Read an optional address size specifier */
+ AddrSize = OptionalAddrSize ();
+
+ /* Enter the new scope */
+ SymEnterLevel (Name, ST_SCOPE, AddrSize);
+
}
static void DoSegment (void)
/* Switch to another segment */
{
- static const char* AttrTab [] = {
- "ZEROPAGE", "DIRECT",
- "ABSOLUTE",
- "FAR", "LONG"
- };
char Name [sizeof (SVal)];
- int SegType;
+ SegDef Def;
+ Def.Name = Name;
if (Tok != TOK_STRCON) {
- ErrorSkip (ERR_STRCON_EXPECTED);
+ ErrorSkip ("String constant expected");
} else {
/* Save the name of the segment and skip it */
strcpy (Name, SVal);
NextTok ();
- /* Check for an optional segment attribute */
- SegType = SEGTYPE_DEFAULT;
- if (Tok == TOK_COMMA) {
- NextTok ();
- if (Tok != TOK_IDENT) {
- ErrorSkip (ERR_IDENT_EXPECTED);
- } else {
- int Attr = GetSubKey (AttrTab, sizeof (AttrTab) / sizeof (AttrTab [0]));
- switch (Attr) {
-
- case 0:
- case 1:
- /* Zeropage */
- SegType = SEGTYPE_ZP;
- break;
-
- case 2:
- /* Absolute */
- SegType = SEGTYPE_ABS;
- break;
-
- case 3:
- case 4:
- /* Far */
- SegType = SEGTYPE_FAR;
- break;
-
- default:
- Error (ERR_ILLEGAL_SEG_ATTR);
- }
- NextTok ();
- }
- }
+ /* Check for an optional address size modifier */
+ Def.AddrSize = OptionalAddrSize ();
/* Set the segment */
- UseSeg (Name, SegType);
+ UseSeg (&Def);
+ }
+}
+
+
+
+static void DoSetCPU (void)
+/* Switch the CPU instruction set */
+{
+ /* We expect an identifier */
+ if (Tok != TOK_STRCON) {
+ ErrorSkip ("String constant expected");
+ } else {
+ /* Try to find the CPU, then skip the identifier */
+ cpu_t CPU = FindCPU (SVal);
+ NextTok ();
+
+ /* Switch to the new CPU */
+ SetCPU (CPU);
}
}
+static void DoTag (void)
+/* Allocate space for a struct */
+{
+ SymEntry* SizeSym;
+ long Size;
+
+ /* Read the struct name */
+ SymTable* Struct = ParseScopedSymTable ();
+
+ /* Check the supposed struct */
+ if (Struct == 0) {
+ ErrorSkip ("Unknown struct");
+ return;
+ }
+ if (GetSymTabType (Struct) != ST_STRUCT) {
+ ErrorSkip ("Not a struct");
+ return;
+ }
+
+ /* Get the symbol that defines the size of the struct */
+ SizeSym = GetSizeOfScope (Struct);
+
+ /* Check if it does exist and if its value is known */
+ if (SizeSym == 0 || !SymIsConst (SizeSym, &Size)) {
+ ErrorSkip ("Size of struct/union is unknown");
+ return;
+ }
+
+ /* Optional multiplicator may follow */
+ if (Tok == TOK_COMMA) {
+ long Multiplicator;
+ NextTok ();
+ Multiplicator = ConstExpression ();
+ /* Multiplicator must make sense */
+ if (Multiplicator <= 0) {
+ ErrorSkip ("Range error");
+ return;
+ }
+ Size *= Multiplicator;
+ }
+
+ /* Emit fill fragments */
+ EmitFill (Size);
+}
+
+
+
static void DoUnexpected (void)
/* Got an unexpected keyword */
{
- Error (ERR_UNEXPECTED, Keyword);
+ Error ("Unexpected `%s'", Keyword);
SkipUntilSep ();
}
/* User warning */
{
if (Tok != TOK_STRCON) {
- ErrorSkip (ERR_STRCON_EXPECTED);
+ ErrorSkip ("String constant expected");
} else {
- Warning (WARN_USER, SVal);
+ Warning (0, "User warning: %s", SVal);
SkipUntilSep ();
}
}
static void DoZeropage (void)
/* Switch to the zeropage segment */
{
- UseZeropageSeg ();
+ UseSeg (&ZeropageSegDef);
}
};
/* Control command table */
-struct CtrlDesc_ {
+typedef struct CtrlDesc CtrlDesc;
+struct CtrlDesc {
unsigned Flags; /* Flags for this directive */
void (*Handler) (void); /* Command handler */
};
-typedef struct CtrlDesc_ CtrlDesc;
#define PSEUDO_COUNT (sizeof (CtrlCmdTab) / sizeof (CtrlCmdTab [0]))
static CtrlDesc CtrlCmdTab [] = {
{ ccNone, DoAddr }, /* .ADDR */
{ ccNone, DoAlign },
{ ccNone, DoASCIIZ },
+ { ccNone, DoAssert },
{ ccNone, DoAutoImport },
+ { ccNone, DoUnexpected }, /* .BANKBYTE */
{ ccNone, DoUnexpected }, /* .BLANK */
{ ccNone, DoBss },
{ ccNone, DoByte },
{ ccNone, DoCase },
+ { ccNone, DoCharMap },
{ ccNone, DoCode },
{ ccNone, DoUnexpected, }, /* .CONCAT */
+ { ccNone, DoConDes },
{ ccNone, DoUnexpected }, /* .CONST */
+ { ccNone, DoConstructor },
{ ccNone, DoUnexpected }, /* .CPU */
{ ccNone, DoData },
+ { ccNone, DoDbg, },
{ ccNone, DoDByt },
{ ccNone, DoDebugInfo },
{ ccNone, DoDefine },
{ ccNone, DoUnexpected }, /* .DEFINED */
+ { ccNone, DoDestructor },
{ ccNone, DoDWord },
{ ccKeepToken, DoConditionals }, /* .ELSE */
{ ccKeepToken, DoConditionals }, /* .ELSEIF */
- { ccNone, DoEnd },
+ { ccKeepToken, DoEnd },
+ { ccNone, DoUnexpected }, /* .ENDENUM */
{ ccKeepToken, DoConditionals }, /* .ENDIF */
{ ccNone, DoUnexpected }, /* .ENDMACRO */
{ ccNone, DoEndProc },
{ ccNone, DoUnexpected }, /* .ENDREPEAT */
+ { ccNone, DoEndScope },
+ { ccNone, DoUnexpected }, /* .ENDSTRUCT */
+ { ccNone, DoUnexpected }, /* .ENDUNION */
+ { ccNone, DoEnum },
{ ccNone, DoError },
{ ccNone, DoExitMacro },
{ ccNone, DoExport },
{ ccNone, DoFarAddr },
{ ccNone, DoFeature },
{ ccNone, DoFileOpt },
+ { ccNone, DoForceImport },
{ ccNone, DoUnexpected }, /* .FORCEWORD */
{ ccNone, DoGlobal },
{ ccNone, DoGlobalZP },
+ { ccNone, DoUnexpected }, /* .HIBYTE */
+ { ccNone, DoUnexpected }, /* .HIWORD */
{ ccNone, DoI16 },
{ ccNone, DoI8 },
{ ccKeepToken, DoConditionals }, /* .IF */
{ ccKeepToken, DoConditionals }, /* .IFP02 */
{ ccKeepToken, DoConditionals }, /* .IFP816 */
{ ccKeepToken, DoConditionals }, /* .IFPC02 */
+ { ccKeepToken, DoConditionals }, /* .IFPSC02 */
{ ccKeepToken, DoConditionals }, /* .IFREF */
{ ccNone, DoImport },
{ ccNone, DoImportZP },
{ ccNone, DoLineCont },
{ ccNone, DoList },
{ ccNone, DoListBytes },
+ { ccNone, DoUnexpected }, /* .LOBYTE */
{ ccNone, DoUnexpected }, /* .LOCAL */
{ ccNone, DoLocalChar },
+ { ccNone, DoUnexpected }, /* .LOWORD */
{ ccNone, DoMacPack },
{ ccNone, DoMacro },
{ ccNone, DoUnexpected }, /* .MATCH */
{ ccNone, DoP02 },
{ ccNone, DoP816 },
{ ccNone, DoPageLength },
- { ccNone, DoUnexpected }, /* .PARAMCOUNT */
+ { ccNone, DoUnexpected }, /* .PARAMCOUNT */
{ ccNone, DoPC02 },
+ { ccNone, DoPopSeg },
{ ccNone, DoProc },
- { ccNone, DoUnexpected }, /* .REFERENCED */
+ { ccNone, DoPSC02 },
+ { ccNone, DoPushSeg },
+ { ccNone, DoUnexpected }, /* .REFERENCED */
{ ccNone, DoReloc },
{ ccNone, DoRepeat },
{ ccNone, DoRes },
{ ccNone, DoInvalid }, /* .RIGHT */
{ ccNone, DoROData },
+ { ccNone, DoScope },
{ ccNone, DoSegment },
+ { ccNone, DoSetCPU },
+ { ccNone, DoUnexpected }, /* .SIZEOF */
{ ccNone, DoSmart },
- { ccNone, DoUnexpected }, /* .STRAT */
- { ccNone, DoUnexpected }, /* .STRING */
- { ccNone, DoUnexpected }, /* .STRLEN */
+ { ccNone, DoUnexpected }, /* .STRAT */
+ { ccNone, DoUnexpected }, /* .STRING */
+ { ccNone, DoUnexpected }, /* .STRLEN */
+ { ccNone, DoStruct },
{ ccNone, DoSunPlus },
- { ccNone, DoUnexpected }, /* .TCOUNT */
+ { ccNone, DoTag },
+ { ccNone, DoUnexpected }, /* .TCOUNT */
+ { ccNone, DoUnexpected }, /* .TIME */
+ { ccNone, DoUnion },
+ { ccNone, DoUnexpected }, /* .VERSION */
{ ccNone, DoWarning },
{ ccNone, DoWord },
- { ccNone, DoUnexpected }, /* .XMATCH */
+ { ccNone, DoUnexpected }, /* .XMATCH */
{ ccNone, DoZeropage },
};
/*****************************************************************************/
-/* Code */
+/* Code */
/*****************************************************************************/
-int TokIsPseudo (unsigned Tok)
-/* Return true if the given token is a pseudo instruction token */
-{
- return (Tok >= TOK_FIRSTPSEUDO && Tok <= TOK_LASTPSEUDO);
-}
-
-
-
void HandlePseudo (void)
/* Handle a pseudo instruction */
{
/* Remember the instruction, then skip it if needed */
if ((D->Flags & ccKeepToken) == 0) {
- strcpy (Keyword+1, SVal);
+ strcpy (Keyword, SVal);
NextTok ();
}
+void SegStackCheck (void)
+/* Check if the segment stack is empty at end of assembly */
+{
+ if (CollCount (&SegStack) != 0) {
+ Error ("Segment stack is not empty");
+ }
+}
+
+
+