X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fca65%2Fpseudo.c;h=55383b935bac189a8db5d6392ee57df9b8612c2f;hb=2b15f40424dab742d756faf7845cd0ce8504c190;hp=ada1f4951389f7ecf56dbc77bc149be30918a2de;hpb=62abe293536dcdda0a7d001abf0bd7a7e1bdefc1;p=cc65 diff --git a/src/ca65/pseudo.c b/src/ca65/pseudo.c index ada1f4951..55383b935 100644 --- a/src/ca65/pseudo.c +++ b/src/ca65/pseudo.c @@ -6,10 +6,10 @@ /* */ /* */ /* */ -/* (C) 1998-2000 Ullrich von Bassewitz */ -/* Wacholderweg 14 */ -/* D-70597 Stuttgart */ -/* EMail: uz@musoftware.de */ +/* (C) 1998-2008, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ /* */ /* */ /* This software is provided 'as-is', without any expressed or implied */ @@ -39,12 +39,26 @@ #include #include -#include "../common/bitops.h" - +/* common */ +#include "assertdefs.h" +#include "bitops.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" @@ -52,8 +66,14 @@ #include "nexttok.h" #include "objcode.h" #include "options.h" -#include "symtab.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" @@ -64,7 +84,11 @@ /* Keyword we're about to handle */ -static char Keyword [sizeof (SVal)+1] = "."; +static StrBuf Keyword = STATIC_STRBUF_INITIALIZER; + +/* Segment stack */ +#define MAX_PUSHED_SEGMENTS 16 +static Collection SegStack = STATIC_COLLECTION_INITIALIZER; @@ -94,6 +118,27 @@ static void DoInvalid (void); +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 (); + if (!ValidAddrSizeForCPU (AddrSize)) { + /* Print an error and reset to default */ + Error ("Invalid address size specification for current CPU"); + AddrSize = ADDR_SIZE_DEFAULT; + } + NextTok (); + } + return AddrSize; +} + + + static void SetBoolOption (unsigned char* Flag) /* Read a on/off/+/- option and set flag accordingly */ { @@ -111,30 +156,78 @@ static void SetBoolOption (unsigned char* Flag) } 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 ExportWithAssign (SymEntry* Sym, unsigned char AddrSize, unsigned Flags) +/* Allow to assign the value of an export in an .export statement */ +{ + /* The name and optional address size spec may be followed by an assignment + * or equal token. + */ + if (Tok == TOK_ASSIGN || Tok == TOK_EQ) { + + /* Assignment means the symbol is a label */ + if (Tok == TOK_ASSIGN) { + Flags |= SF_LABEL; + } + + /* Skip the assignment token */ + NextTok (); + + /* Define the symbol with the expression following the '=' */ + SymDef (Sym, Expression(), ADDR_SIZE_DEFAULT, Flags); + + } + + /* Now export the symbol */ + SymExport (Sym, AddrSize, Flags); +} + + + +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 { @@ -150,13 +243,13 @@ static long IntArg (long Min, long Max) * and return -1 in this case. */ { - if (Tok == TOK_IDENT && strcmp (SVal, "unlimited") == 0) { + if (Tok == TOK_IDENT && SB_CompareStr (&SVal, "unlimited") == 0) { NextTok (); return -1; } else { long Val = ConstExpression (); if (Val < Min || Val > Max) { - Error (ERR_RANGE); + Error ("Range error"); Val = Min; } return Val; @@ -165,6 +258,36 @@ static long IntArg (long Min, long Max) +static void ConDes (const StrBuf* 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 */ /*****************************************************************************/ @@ -175,10 +298,10 @@ static void DoA16 (void) /* 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; + ExtBytes [AM65I_IMM_ACCU] = 2; } } @@ -188,10 +311,10 @@ static void DoA8 (void) /* 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; + ExtBytes [AM65I_IMM_ACCU] = 1; } } @@ -202,7 +325,7 @@ static void DoAddr (void) { while (1) { if (GetCPU() == CPU_65816) { - EmitWord (ForceWordExpr (Expression ())); + EmitWord (GenWordExpr (Expression ())); } else { /* Do a range check */ EmitWord (Expression ()); @@ -227,7 +350,7 @@ static void DoAlign (void) /* Read the alignment value */ Align = ConstExpression (); if (Align <= 0 || Align > 0x10000) { - ErrorSkip (ERR_RANGE); + ErrorSkip ("Range error"); return; } @@ -237,7 +360,7 @@ static void DoAlign (void) Val = ConstExpression (); /* We need a byte value here */ if (!IsByteRange (Val)) { - ErrorSkip (ERR_RANGE); + ErrorSkip ("Range error"); return; } } else { @@ -247,7 +370,7 @@ static void DoAlign (void) /* 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); } @@ -259,11 +382,15 @@ static void DoASCIIZ (void) /* Define text with a zero terminator */ { 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)); + + /* Translate into target charset and emit */ + TgtTranslateStrBuf (&SVal); + EmitStrBuf (&SVal); NextTok (); if (Tok == TOK_COMMA) { NextTok (); @@ -276,6 +403,78 @@ static void DoASCIIZ (void) +static void DoAssert (void) +/* Add an assertion */ +{ + static const char* ActionTab [] = { + "WARN", "WARNING", + "ERROR" + }; + + int Action; + unsigned Msg; + + /* 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 (); + + /* We can have an optional message. If no message is present, use + * "Assertion failed". + */ + if (Tok == TOK_COMMA) { + + /* Skip the comma */ + NextTok (); + + /* Read the message */ + if (Tok != TOK_STRCON) { + ErrorSkip ("String constant expected"); + return; + } + + /* Translate the message into a string id. We can then skip the input + * string. + */ + Msg = GetStrBufId (&SVal); + NextTok (); + + } else { + + /* Use "Assertion failed" */ + Msg = GetStringId ("Assertion failed"); + + } + + /* Remember the assertion */ + AddAssertion (Expr, Action, Msg); +} + + + static void DoAutoImport (void) /* Mark unresolved symbols as imported */ { @@ -283,11 +482,25 @@ static void DoAutoImport (void) } +static void DoBankBytes (void) +/* Define bytes, extracting the bank byte from each expression in the list */ +{ + while (1) { + EmitByte (FuncBankByte ()); + if (Tok != TOK_COMMA) { + break; + } else { + NextTok (); + } + } +} + + static void DoBss (void) /* Switch to the BSS segment */ { - UseBssSeg (); + UseSeg (&BssSegDef); } @@ -297,19 +510,20 @@ static void DoByte (void) { while (1) { if (Tok == TOK_STRCON) { - /* A string */ - EmitData ((unsigned char*) SVal, strlen (SVal)); + /* A string, translate into target charset and emit */ + TgtTranslateStrBuf (&SVal); + EmitStrBuf (&SVal); NextTok (); } else { EmitByte (Expression ()); } - if (Tok != TOK_COMMA) { + if (Tok != TOK_COMMA) { break; } else { NextTok (); /* Do smart handling of dangling comma */ if (Tok == TOK_SEP) { - Error (ERR_UNEXPECTED_EOL); + Error ("Unexpected end of line"); break; } } @@ -327,10 +541,118 @@ static void DoCase (void) +static void DoCharMap (void) +/* Allow custome character mappings */ +{ + long Index; + long Code; + + /* Read the index as numerical value */ + Index = ConstExpression (); + if (Index < 0 || Index > 255) { + /* Value out of range */ + ErrorSkip ("Range error"); + return; + } + + /* Comma follows */ + ConsumeComma (); + + /* Read the character code */ + Code = ConstExpression (); + if (Code < 0 || 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", + "INTERRUPTOR", + }; + StrBuf Name = STATIC_STRBUF_INITIALIZER; + long Type; + + /* Symbol name follows */ + if (Tok != TOK_IDENT) { + ErrorSkip ("Identifier expected"); + return; + } + SB_Copy (&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) { + ErrorSkip ("Syntax error"); + goto ExitPoint; + } + + } else { + + /* Read the type as numerical value */ + Type = ConstExpression (); + if (Type < CD_TYPE_MIN || Type > CD_TYPE_MAX) { + /* Value out of range */ + ErrorSkip ("Range error"); + goto ExitPoint; + } + + } + + /* Parse the remainder of the line and export the symbol */ + ConDes (&Name, (unsigned) Type); + +ExitPoint: + /* Free string memory */ + SB_Done (&Name); +} + + + +static void DoConstructor (void) +/* Export a symbol as constructor */ +{ + StrBuf Name = STATIC_STRBUF_INITIALIZER; + + /* Symbol name follows */ + if (Tok != TOK_IDENT) { + ErrorSkip ("Identifier expected"); + return; + } + SB_Copy (&Name, &SVal); + NextTok (); + + /* Parse the remainder of the line and export the symbol */ + ConDes (&Name, CD_TYPE_CON); + + /* Free string memory */ + SB_Done (&Name); } @@ -338,7 +660,41 @@ static void DoCode (void) 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; + } } @@ -347,7 +703,7 @@ static void DoDByt (void) /* Output double bytes */ { while (1) { - EmitWord (SwapExpr (Expression ())); + EmitWord (GenSwapExpr (Expression ())); if (Tok != TOK_COMMA) { break; } else { @@ -374,6 +730,28 @@ static void DoDefine (void) +static void DoDestructor (void) +/* Export a symbol as destructor */ +{ + StrBuf Name = STATIC_STRBUF_INITIALIZER; + + /* Symbol name follows */ + if (Tok != TOK_IDENT) { + ErrorSkip ("Identifier expected"); + return; + } + SB_Copy (&Name, &SVal); + NextTok (); + + /* Parse the remainder of the line and export the symbol */ + ConDes (&Name, CD_TYPE_DES); + + /* Free string memory */ + SB_Done (&Name); +} + + + static void DoDWord (void) /* Define dwords */ { @@ -393,6 +771,7 @@ static void DoEnd (void) /* End of assembly */ { ForcedEnd = 1; + NextTok (); } @@ -400,18 +779,36 @@ static void DoEnd (void) 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 (); + } } static void DoError (void) -/* Use error */ +/* User error */ { - if (Tok == TOK_STRCON) { - ErrorSkip (ERR_STRCON_EXPECTED); + if (Tok != TOK_STRCON) { + ErrorSkip ("String constant expected"); } else { - Error (ERR_USER, SVal); + Error ("User error: %m%p", &SVal); SkipUntilSep (); } } @@ -434,7 +831,7 @@ static void DoExitMacro (void) static void DoExport (void) /* Export a symbol */ { - ExportImport (SymExport, 0); + ExportImport (ExportWithAssign, ADDR_SIZE_DEFAULT, SF_NONE); } @@ -442,7 +839,7 @@ static void DoExport (void) static void DoExportZP (void) /* Export a zeropage symbol */ { - ExportImport (SymExport, 1); + ExportImport (ExportWithAssign, ADDR_SIZE_ZP, SF_NONE); } @@ -465,45 +862,27 @@ static void DoFarAddr (void) 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: `%m%p'", &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) { @@ -533,7 +912,7 @@ static void DoFileOpt (void) OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0])); if (OptNum < 0) { /* Not found */ - ErrorSkip (ERR_OPTION_KEY_EXPECTED); + ErrorSkip ("File option keyword expected"); return; } @@ -545,7 +924,7 @@ static void DoFileOpt (void) /* We accept only string options for now */ if (Tok != TOK_STRCON) { - ErrorSkip (ERR_STRCON_EXPECTED); + ErrorSkip ("String constant expected"); return; } @@ -554,21 +933,21 @@ static void DoFileOpt (void) case 0: /* Author */ - OptAuthor (SVal); + OptAuthor (&SVal); break; case 1: /* Comment */ - OptComment (SVal); + OptComment (&SVal); break; case 2: /* Compiler */ - OptCompiler (SVal); + OptCompiler (&SVal); break; default: - Internal ("Invalid OptNum: %l", OptNum); + Internal ("Invalid OptNum: %ld", OptNum); } @@ -580,7 +959,7 @@ static void DoFileOpt (void) /* Option given as number */ OptNum = ConstExpression (); if (!IsByteRange (OptNum)) { - ErrorSkip (ERR_RANGE); + ErrorSkip ("Range error"); return; } @@ -589,12 +968,12 @@ static void DoFileOpt (void) /* We accept only string options for now */ if (Tok != TOK_STRCON) { - ErrorSkip (ERR_STRCON_EXPECTED); + ErrorSkip ("String constant expected"); return; } /* Insert the option */ - OptStr ((unsigned char) OptNum, SVal); + OptStr ((unsigned char) OptNum, &SVal); /* Done */ NextTok (); @@ -603,10 +982,18 @@ static void DoFileOpt (void) +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); } @@ -614,7 +1001,21 @@ static void DoGlobal (void) static void DoGlobalZP (void) /* Declare a global zeropage symbol */ { - ExportImport (SymGlobal, 1); + ExportImport (SymGlobal, ADDR_SIZE_ZP, SF_NONE); +} + + +static void DoHiBytes (void) +/* Define bytes, extracting the hi byte from each expression in the list */ +{ + while (1) { + EmitByte (FuncHiByte ()); + if (Tok != TOK_COMMA) { + break; + } else { + NextTok (); + } + } } @@ -623,10 +1024,10 @@ static void DoI16 (void) /* 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; + ExtBytes [AM65I_IMM_INDEX] = 2; } } @@ -636,10 +1037,10 @@ static void DoI8 (void) /* 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; + ExtBytes [AM65I_IMM_INDEX] = 1; } } @@ -648,7 +1049,7 @@ static void DoI8 (void) static void DoImport (void) /* Import a symbol */ { - ExportImport (SymImport, 0); + ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_NONE); } @@ -656,7 +1057,7 @@ static void DoImport (void) static void DoImportZP (void) /* Import a zero page symbol */ { - ExportImport (SymImport, 1); + ExportImport (SymImport, ADDR_SIZE_ZP, SF_NONE); } @@ -664,27 +1065,108 @@ static void DoImportZP (void) static void DoIncBin (void) /* Include a binary file */ { + StrBuf Name = STATIC_STRBUF_INITIALIZER; + 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; + } + SB_Copy (&Name, &SVal); + SB_Terminate (&Name); + 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 (SB_GetConstBuf (&Name), "rb"); + if (F == 0) { + + /* Search for the file in the include directories. */ + char* PathName = FindInclude (SB_GetConstBuf (&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 `%m%p': %s", &Name, strerror (errno)); + } + + /* Free the allocated memory */ + xfree (PathName); + + /* If we had an error before, bail out now */ + if (F == 0) { + goto ExitPoint; + } + } + + /* 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 `%m%p': %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); + +ExitPoint: + /* Free string memory */ + SB_Done (&Name); } @@ -692,20 +1174,42 @@ static void DoIncBin (void) static void DoInclude (void) /* Include another file */ { - char Name [MAX_STR_LEN+1]; - /* Name must follow */ if (Tok != TOK_STRCON) { - ErrorSkip (ERR_STRCON_EXPECTED); + ErrorSkip ("String constant expected"); } else { - strcpy (Name, SVal); - NextTok (); - NewInputFile (Name); + SB_Terminate (&SVal); + if (NewInputFile (SB_GetConstBuf (&SVal)) == 0) { + /* Error opening the file, skip remainder of line */ + SkipUntilSep (); + } } } +static void DoInterruptor (void) +/* Export a symbol as interruptor */ +{ + StrBuf Name = STATIC_STRBUF_INITIALIZER; + + /* Symbol name follows */ + if (Tok != TOK_IDENT) { + ErrorSkip ("Identifier expected"); + return; + } + SB_Copy (&Name, &SVal); + NextTok (); + + /* Parse the remainder of the line and export the symbol */ + ConDes (&Name, CD_TYPE_INT); + + /* Free string memory */ + SB_Done (&Name); +} + + + static void DoInvalid (void) /* Handle a token that is invalid here, since it should have been handled on * a much lower level of the expression hierarchy. Getting this sort of token @@ -715,7 +1219,7 @@ static void DoInvalid (void) * an error in the assembler itself, while DoInvalid is. */ { - Internal ("Unexpected token: %s", Keyword); + Internal ("Unexpected token: %m%p", &Keyword); } @@ -745,6 +1249,20 @@ static void DoList (void) +static void DoLoBytes (void) +/* Define bytes, extracting the lo byte from each expression in the list */ +{ + while (1) { + EmitByte (FuncLoByte ()); + if (Tok != TOK_COMMA) { + break; + } else { + NextTok (); + } + } +} + + static void DoListBytes (void) /* Set maximum number of bytes to list for one line */ { @@ -757,10 +1275,10 @@ static void DoLocalChar (void) /* 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; } @@ -773,33 +1291,29 @@ static void DoLocalChar (void) static void DoMacPack (void) /* Insert a macro package */ { - /* Macro package names */ - static const char* Keys [] = { - "GENERIC", - "LONGBRANCH", - }; - int Package; /* We expect an identifier */ if (Tok != TOK_IDENT) { - ErrorSkip (ERR_IDENT_EXPECTED); + ErrorSkip ("Identifier expected"); return; } - /* Map the keyword to a number */ - Package = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0])); + /* Search for the macro package name */ + LocaseSVal (); + Package = MacPackFind (&SVal); if (Package < 0) { /* Not found */ - ErrorSkip (ERR_ILLEGAL_MACPACK); + ErrorSkip ("Invalid macro package"); return; } - /* Skip the package name */ - NextTok (); - - /* Insert the package */ - InsertMacPack (Package); + /* Insert the package. If this fails, skip the remainder of the line to + * avoid additional error messages. + */ + if (MacPackInsert (Package) == 0) { + SkipUntilSep (); + } } @@ -815,7 +1329,7 @@ static void DoMacro (void) static void DoNull (void) /* Switch to the NULL segment */ { - UseNullSeg (); + UseSeg (&NullSegDef); } @@ -824,11 +1338,11 @@ static void DoOrg (void) /* 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); + EnterAbsoluteMode (PC); } @@ -837,12 +1351,12 @@ static void DoOut (void) /* 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. */ - printf ("%s\n", SVal); + printf ("%*s\n", SB_GetLen (&SVal), SB_GetConstBuf (&SVal)); fflush (stdout); NextTok (); } @@ -882,15 +1396,91 @@ static void DoPageLength (void) +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 */ { + StrBuf Name = STATIC_STRBUF_INITIALIZER; + 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. */ + SB_Copy (&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, "PROC"); + AddrSize = ADDR_SIZE_DEFAULT; + + } + + /* Enter a new scope */ + SymEnterLevel (&Name, ST_PROC, AddrSize); + + /* Free memory for Name */ + SB_Done (&Name); +} + + + +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; } - SymEnterLevel (); + + /* Get the current segment and push it */ + CollAppend (&SegStack, DupSegDef (GetCurrentSegDef ())); } @@ -898,7 +1488,7 @@ static void DoProc (void) static void DoReloc (void) /* Enter relocatable mode */ { - RelocMode = 1; + EnterRelocMode (); } @@ -906,7 +1496,7 @@ static void DoReloc (void) static void DoRepeat (void) /* Repeat some instruction block */ { - ErrorSkip (ERR_NOT_IMPLEMENTED); + ParseRepeat (); } @@ -919,7 +1509,7 @@ static void DoRes (void) Count = ConstExpression (); if (Count > 0xFFFF || Count < 0) { - ErrorSkip (ERR_RANGE); + ErrorSkip ("Range error"); return; } if (Tok == TOK_COMMA) { @@ -927,7 +1517,7 @@ static void DoRes (void) Val = ConstExpression (); /* We need a byte value here */ if (!IsByteRange (Val)) { - ErrorSkip (ERR_RANGE); + ErrorSkip ("Range error"); return; } @@ -947,7 +1537,39 @@ static void DoRes (void) static void DoROData (void) /* Switch to the r/o data segment */ { - UseRODataSeg (); + UseSeg (&RODataSegDef); +} + + + +static void DoScope (void) +/* Start a local scope */ +{ + StrBuf Name = STATIC_STRBUF_INITIALIZER; + unsigned char AddrSize; + + + if (Tok == TOK_IDENT) { + + /* The new scope has a name. Remember and skip it. */ + SB_Copy (&Name, &SVal); + NextTok (); + + } else { + + /* An unnamed scope */ + AnonName (&Name, "SCOPE"); + + } + + /* Read an optional address size specifier */ + AddrSize = OptionalAddrSize (); + + /* Enter the new scope */ + SymEnterLevel (&Name, ST_SCOPE, AddrSize); + + /* Free memory for Name */ + SB_Done (&Name); } @@ -955,58 +1577,54 @@ static void DoROData (void) static void DoSegment (void) /* Switch to another segment */ { - static const char* AttrTab [] = { - "ZEROPAGE", "DIRECT", - "ABSOLUTE", - "FAR", "LONG" - }; - char Name [sizeof (SVal)]; - int SegType; + StrBuf Name = STATIC_STRBUF_INITIALIZER; + SegDef Def; 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); + SB_Copy (&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 (); - } - } + /* Use the name for the segment definition */ + SB_Terminate (&Name); + Def.Name = SB_GetBuf (&Name); + + /* Check for an optional address size modifier */ + Def.AddrSize = OptionalAddrSize (); /* Set the segment */ - UseSeg (Name, SegType); + UseSeg (&Def); + } + + /* Free memory for Name */ + SB_Done (&Name); +} + + + +static void DoSetCPU (void) +/* Switch the CPU instruction set */ +{ + /* We expect an identifier */ + if (Tok != TOK_STRCON) { + ErrorSkip ("String constant expected"); + } else { + cpu_t CPU; + + /* Try to find the CPU */ + SB_Terminate (&SVal); + CPU = FindCPU (SB_GetConstBuf (&SVal)); + + /* Switch to the new CPU */ + SetCPU (CPU); + + /* Skip the identifier. If the CPU switch was successful, the scanner + * will treat the input now correctly for the new CPU. + */ + NextTok (); } } @@ -1028,15 +1646,75 @@ static void DoSunPlus (void) +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 `%m%p'", &Keyword); SkipUntilSep (); } +static void DoWarning (void) +/* User warning */ +{ + if (Tok != TOK_STRCON) { + ErrorSkip ("String constant expected"); + } else { + Warning (0, "User warning: %m%p", &SVal); + SkipUntilSep (); + } +} + + + static void DoWord (void) /* Define words */ { @@ -1055,7 +1733,7 @@ static void DoWord (void) static void DoZeropage (void) /* Switch to the zeropage segment */ { - UseZeropageSeg (); + UseSeg (&ZeropageSegDef); } @@ -1073,11 +1751,11 @@ enum { }; /* 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 [] = { @@ -1086,28 +1764,41 @@ static CtrlDesc CtrlCmdTab [] = { { ccNone, DoAddr }, /* .ADDR */ { ccNone, DoAlign }, { ccNone, DoASCIIZ }, + { ccNone, DoAssert }, { ccNone, DoAutoImport }, + { ccNone, DoUnexpected }, /* .BANKBYTE */ + { ccNone, DoBankBytes }, { 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 }, @@ -1115,10 +1806,16 @@ static CtrlDesc CtrlCmdTab [] = { { ccNone, DoFarAddr }, { ccNone, DoFeature }, { ccNone, DoFileOpt }, + { ccNone, DoForceImport }, + { ccNone, DoUnexpected }, /* .FORCEWORD */ { ccNone, DoGlobal }, { ccNone, DoGlobalZP }, + { ccNone, DoUnexpected }, /* .HIBYTE */ + { ccNone, DoHiBytes }, + { ccNone, DoUnexpected }, /* .HIWORD */ { ccNone, DoI16 }, { ccNone, DoI8 }, + { ccNone, DoUnexpected }, /* .IDENT */ { ccKeepToken, DoConditionals }, /* .IF */ { ccKeepToken, DoConditionals }, /* .IFBLANK */ { ccKeepToken, DoConditionals }, /* .IFCONST */ @@ -1130,17 +1827,22 @@ static CtrlDesc CtrlCmdTab [] = { { ccKeepToken, DoConditionals }, /* .IFP02 */ { ccKeepToken, DoConditionals }, /* .IFP816 */ { ccKeepToken, DoConditionals }, /* .IFPC02 */ + { ccKeepToken, DoConditionals }, /* .IFPSC02 */ { ccKeepToken, DoConditionals }, /* .IFREF */ { ccNone, DoImport }, { ccNone, DoImportZP }, { ccNone, DoIncBin }, { ccNone, DoInclude }, + { ccNone, DoInterruptor }, { ccNone, DoInvalid }, /* .LEFT */ { ccNone, DoLineCont }, { ccNone, DoList }, { ccNone, DoListBytes }, + { ccNone, DoUnexpected }, /* .LOBYTE */ + { ccNone, DoLoBytes }, { ccNone, DoUnexpected }, /* .LOCAL */ { ccNone, DoLocalChar }, + { ccNone, DoUnexpected }, /* .LOWORD */ { ccNone, DoMacPack }, { ccNone, DoMacro }, { ccNone, DoUnexpected }, /* .MATCH */ @@ -1151,43 +1853,49 @@ static CtrlDesc CtrlCmdTab [] = { { 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, DoUnexpected }, /* .SET */ + { ccNone, DoSetCPU }, + { ccNone, DoUnexpected }, /* .SIZEOF */ { ccNone, DoSmart }, - { ccNone, DoUnexpected }, /* .STRAT */ - { ccNone, DoUnexpected }, /* .STRING */ - { ccNone, DoUnexpected }, /* .STRLEN */ + { ccNone, DoUnexpected }, /* .SPRINTF */ + { 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 */ { @@ -1199,7 +1907,7 @@ void HandlePseudo (void) /* Safety check */ if (PSEUDO_COUNT != (TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1)) { Internal ("Pseudo mismatch: PSEUDO_COUNT = %u, actual count = %u\n", - PSEUDO_COUNT, TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1); + (unsigned) PSEUDO_COUNT, TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1); } CHECK (Index < PSEUDO_COUNT); @@ -1208,7 +1916,7 @@ void HandlePseudo (void) /* Remember the instruction, then skip it if needed */ if ((D->Flags & ccKeepToken) == 0) { - strcpy (Keyword+1, SVal); + SB_Copy (&Keyword, &SVal); NextTok (); } @@ -1218,3 +1926,13 @@ void HandlePseudo (void) +void SegStackCheck (void) +/* Check if the segment stack is empty at end of assembly */ +{ + if (CollCount (&SegStack) != 0) { + Error ("Segment stack is not empty"); + } +} + + +