diff --git a/src/ca65/expr.c b/src/ca65/expr.c index b39f0655f..daed0e69b 100644 --- a/src/ca65/expr.c +++ b/src/ca65/expr.c @@ -305,8 +305,15 @@ static ExprNode* Symbol (SymEntry* S) } else { /* Mark the symbol as referenced */ SymRef (S); - /* Create symbol node */ - return GenSymExpr (S); + /* If the symbol is a variable, return just its value, otherwise + * return a reference to the symbol. + */ + if (SymIsVar (S)) { + return CloneExpr (GetSymExpr (S)); + } else { + /* Create symbol node */ + return GenSymExpr (S); + } } } diff --git a/src/ca65/main.c b/src/ca65/main.c index 157470314..42b2891b0 100644 --- a/src/ca65/main.c +++ b/src/ca65/main.c @@ -562,16 +562,41 @@ static void OneLine (void) * is no colon, it's an assignment. */ if (Tok == TOK_EQ || Tok == TOK_ASSIGN) { - /* If it's an assign token, we have a label */ + + /* Determine the symbol flags from the assignment token */ unsigned Flags = (Tok == TOK_ASSIGN)? SF_LABEL : SF_NONE; + /* Skip the '=' */ NextTok (); + /* Define the symbol with the expression following the '=' */ SymDef (Sym, Expression(), ADDR_SIZE_DEFAULT, Flags); + /* Don't allow anything after a symbol definition */ ConsumeSep (); return; + + } else if (Tok == TOK_SET) { + + ExprNode* Expr; + + /* .SET defines variables (= redefinable symbols) */ + NextTok (); + + /* Read the assignment expression, which must be constant */ + Expr = GenLiteralExpr (ConstExpression ()); + + /* Define the symbol with the constant expression following + * the '=' + */ + SymDef (Sym, Expr, ADDR_SIZE_DEFAULT, SF_VAR); + + /* Don't allow anything after a symbol definition */ + ConsumeSep (); + return; + } else { + /* A label. Remember the current segment, so we can later * determine the size of the data stored under the label. */ diff --git a/src/ca65/nexttok.c b/src/ca65/nexttok.c index 139017494..350aaf38d 100644 --- a/src/ca65/nexttok.c +++ b/src/ca65/nexttok.c @@ -206,47 +206,6 @@ static void FuncConcat (void) -static void FuncLeft (void) -/* Handle the .LEFT function */ -{ - long Count; - TokList* List; - - /* Skip it */ - NextTok (); - - /* Left paren expected */ - ConsumeLParen (); - - /* Count argument */ - Count = ConstExpression (); - if (Count < 0 || Count > 100) { - Error ("Range error"); - Count = 1; - } - ConsumeComma (); - - /* Read the token list */ - List = CollectTokens (0, (unsigned) Count); - - /* Since we want to insert the list before the now current token, we have - * to save the current token in some way and then skip it. To do this, we - * will add the current token at the end of the token list (so the list - * will never be empty), push the token list, and then skip the current - * token. This will replace the current token by the first token from the - * list (which will be the old current token in case the list was empty). - */ - AddCurTok (List); - - /* Insert it into the scanner feed */ - PushTokList (List, ".LEFT"); - - /* Skip the current token */ - NextTok (); -} - - - static void NoIdent (void) /* Print an error message and skip the remainder of the line */ { @@ -325,6 +284,46 @@ static void FuncIdent (void) +static void FuncLeft (void) +/* Handle the .LEFT function */ +{ + long Count; + TokList* List; + + /* Skip it */ + NextTok (); + + /* Left paren expected */ + ConsumeLParen (); + + /* Count argument. Correct negative counts to zero. */ + Count = ConstExpression (); + if (Count < 0) { + Count = 1; + } + ConsumeComma (); + + /* Read the token list */ + List = CollectTokens (0, (unsigned) Count); + + /* Since we want to insert the list before the now current token, we have + * to save the current token in some way and then skip it. To do this, we + * will add the current token at the end of the token list (so the list + * will never be empty), push the token list, and then skip the current + * token. This will replace the current token by the first token from the + * list (which will be the old current token in case the list was empty). + */ + AddCurTok (List); + + /* Insert it into the scanner feed */ + PushTokList (List, ".LEFT"); + + /* Skip the current token */ + NextTok (); +} + + + static void FuncMid (void) /* Handle the .MID function */ { @@ -338,19 +337,21 @@ static void FuncMid (void) /* Left paren expected */ ConsumeLParen (); - /* Start argument */ + /* Start argument. Since the start argument can get negative with + * expressions like ".tcount(arg)-2", we correct it to zero silently. + */ Start = ConstExpression (); if (Start < 0 || Start > 100) { - Error ("Range error"); Start = 0; } ConsumeComma (); - /* Count argument */ + /* Count argument. Similar as above, we will accept negative counts and + * correct them to zero silently. + */ Count = ConstExpression (); - if (Count < 0 || Count > 100) { - Error ("Range error"); - Count = 1; + if (Count < 0) { + Count = 0; } ConsumeComma (); @@ -387,11 +388,10 @@ static void FuncRight (void) /* Left paren expected */ ConsumeLParen (); - /* Count argument */ + /* Count argument. Correct negative counts to zero. */ Count = ConstExpression (); - if (Count < 0 || Count > 100) { - Error ("Range error"); - Count = 1; + if (Count < 0) { + Count = 0; } ConsumeComma (); diff --git a/src/ca65/pseudo.c b/src/ca65/pseudo.c index 85aef7d8b..1eed86f94 100644 --- a/src/ca65/pseudo.c +++ b/src/ca65/pseudo.c @@ -1771,9 +1771,10 @@ static CtrlDesc CtrlCmdTab [] = { { ccNone, DoROData }, { ccNone, DoScope }, { ccNone, DoSegment }, + { ccNone, DoUnexpected }, /* .SET */ { ccNone, DoSetCPU }, { ccNone, DoUnexpected }, /* .SIZEOF */ - { ccNone, DoSmart }, + { ccNone, DoSmart }, { ccNone, DoUnexpected }, /* .SPRINTF */ { ccNone, DoUnexpected }, /* .STRAT */ { ccNone, DoUnexpected }, /* .STRING */ diff --git a/src/ca65/scanner.c b/src/ca65/scanner.c index 69846d9c5..5f64c2ade 100644 --- a/src/ca65/scanner.c +++ b/src/ca65/scanner.c @@ -236,6 +236,7 @@ struct DotKeyword { { ".RODATA", TOK_RODATA }, { ".SCOPE", TOK_SCOPE }, { ".SEGMENT", TOK_SEGMENT }, + { ".SET", TOK_SET }, { ".SETCPU", TOK_SETCPU }, { ".SHL", TOK_SHL }, { ".SHR", TOK_SHR }, diff --git a/src/ca65/scanner.h b/src/ca65/scanner.h index dedbeb047..d347e7a20 100644 --- a/src/ca65/scanner.h +++ b/src/ca65/scanner.h @@ -225,6 +225,7 @@ enum Token { TOK_RODATA, TOK_SCOPE, TOK_SEGMENT, + TOK_SET, TOK_SETCPU, TOK_SIZEOF, TOK_SMART, diff --git a/src/ca65/symentry.c b/src/ca65/symentry.c index a1c86d802..8fcd5c0a5 100644 --- a/src/ca65/symentry.c +++ b/src/ca65/symentry.c @@ -174,6 +174,38 @@ void SymTransferExprRefs (SymEntry* From, SymEntry* To) +static void SymReplaceExprRefs (SymEntry* S) +/* Replace the references to this symbol by a copy of the symbol expression */ +{ + unsigned I; + long Val; + + /* Check if the expression is const and get its value */ + int IsConst = IsConstExpr (S->Expr, &Val); + CHECK (IsConst); + + /* Loop over all references */ + for (I = 0; I < CollCount (&S->ExprRefs); ++I) { + + /* Get the expression node */ + ExprNode* E = CollAtUnchecked (&S->ExprRefs, I); + + /* Safety */ + CHECK (E->Op == EXPR_SYMBOL && E->V.Sym == S); + + /* We cannot touch the root node, since there are pointers to it. + * Replace it by a literal node. + */ + E->Op = EXPR_LITERAL; + E->V.Val = Val; + } + + /* Remove all symbol references from the symbol */ + CollDeleteAll (&S->ExprRefs); +} + + + void SymDef (SymEntry* S, ExprNode* Expr, unsigned char AddrSize, unsigned Flags) /* Define a new symbol */ { @@ -182,11 +214,29 @@ void SymDef (SymEntry* S, ExprNode* Expr, unsigned char AddrSize, unsigned Flags Error ("Symbol `%s' is already an import", GetSymName (S)); return; } + if ((Flags & SF_VAR) != 0 && (S->Flags & (SF_EXPORT | SF_GLOBAL))) { + /* Variable symbols cannot be exports or globals */ + Error ("Var symbol `%s' cannot be an export or global symbol", GetSymName (S)); + return; + } if (S->Flags & SF_DEFINED) { - /* Multiple definition */ - Error ("Symbol `%s' is already defined", GetSymName (S)); - S->Flags |= SF_MULTDEF; - return; + /* Multiple definition. In case of a variable, this is legal. */ + if ((S->Flags & SF_VAR) == 0) { + Error ("Symbol `%s' is already defined", GetSymName (S)); + S->Flags |= SF_MULTDEF; + return; + } else { + /* Redefinition must also be a variable symbol */ + if ((Flags & SF_VAR) == 0) { + Error ("Symbol `%s' is already different kind", GetSymName (S)); + return; + } + /* Delete the current symbol expression, since it will get + * replaced + */ + FreeExpr (S->Expr); + S->Expr = 0; + } } /* Map a default address size to a real value */ @@ -202,6 +252,15 @@ void SymDef (SymEntry* S, ExprNode* Expr, unsigned char AddrSize, unsigned Flags /* Set the symbol value */ S->Expr = Expr; + /* In case of a variable symbol, walk over all expressions containing + * this symbol and replace the (sub-)expression by the literal value of + * the tree. Be sure to replace the expression node in place, since there + * may be pointers to it. + */ + if (Flags & SF_VAR) { + SymReplaceExprRefs (S); + } + /* If the symbol is marked as global, export it. Address size is checked * below. */ @@ -289,6 +348,11 @@ void SymExport (SymEntry* S, unsigned char AddrSize, unsigned Flags) Error ("Symbol `%s' is already an import", GetSymName (S)); return; } + if (S->Flags & SF_VAR) { + /* Variable symbols cannot be exported */ + Error ("Var symbol `%s' cannot be exported", GetSymName (S)); + return; + } /* If the symbol was marked as global before, remove the global flag and * proceed, but check the address size. @@ -336,6 +400,12 @@ void SymGlobal (SymEntry* S, unsigned char AddrSize, unsigned Flags) * either imported or exported. */ { + if (S->Flags & SF_VAR) { + /* Variable symbols cannot be exported or imported */ + Error ("Var symbol `%s' cannot be made global", GetSymName (S)); + return; + } + /* If the symbol is already marked as import, the address size must match. * Apart from that, ignore the global declaration. */ @@ -433,6 +503,11 @@ void SymConDes (SymEntry* S, unsigned char AddrSize, unsigned Type, unsigned Pri Error ("Symbol `%s' is already an import", GetSymName (S)); return; } + if (S->Flags & SF_VAR) { + /* Variable symbols cannot be exported or imported */ + Error ("Var symbol `%s' cannot be exported", GetSymName (S)); + return; + } /* If the symbol was already marked as an export or global, check if * this was done specifiying the same address size. In case of a global diff --git a/src/ca65/symentry.h b/src/ca65/symentry.h index c9772a99e..a5d745dd8 100644 --- a/src/ca65/symentry.h +++ b/src/ca65/symentry.h @@ -64,7 +64,8 @@ #define SF_GLOBAL 0x0010 /* Global symbol */ #define SF_LOCAL 0x0020 /* Cheap local symbol */ #define SF_LABEL 0x0080 /* Used as a label */ -#define SF_FORCED 0x0100 /* Forced import, SF_IMPORT also set */ +#define SF_VAR 0x0100 /* Variable symbol */ +#define SF_FORCED 0x0400 /* Forced import, SF_IMPORT also set */ #define SF_INDEXED 0x0800 /* Index is valid */ #define SF_MULTDEF 0x2000 /* Multiply defined symbol */ #define SF_DEFINED 0x4000 /* Defined */ @@ -217,6 +218,17 @@ INLINE int SymIsExport (const SymEntry* S) # define SymIsExport(S) (((S)->Flags & SF_EXPORT) != 0) #endif +#if defined(HAVE_INLINE) +INLINE int SymIsVar (const SymEntry* S) +/* Return true if the given symbol is marked as variable */ +{ + /* Check the variable flag */ + return (S->Flags & SF_VAR) != 0; +} +#else +# define SymIsVar(S) (((S)->Flags & SF_VAR) != 0) +#endif + int SymIsConst (SymEntry* Sym, long* Val); /* Return true if the given symbol has a constant value. If Val is not NULL * and the symbol has a constant value, store it's value there.