static void ParseConfig (void) /* Parse the config file */ { static const IdentTok BlockNames [] = { { "MEMORY", CFGTOK_MEMORY }, { "FILES", CFGTOK_FILES }, { "SEGMENTS", CFGTOK_SEGMENTS }, { "FORMATS", CFGTOK_FORMATS }, { "FEATURES", CFGTOK_FEATURES }, { "SYMBOLS", CFGTOK_SYMBOLS }, }; cfgtok_t BlockTok; do { /* Read the block ident */ CfgSpecialToken (BlockNames, ENTRY_COUNT (BlockNames), "Block identifier"); BlockTok = CfgTok; CfgNextTok (); /* Expected a curly brace */ CfgConsume (CFGTOK_LCURLY, "`{' expected"); /* Read the block */ switch (BlockTok) { case CFGTOK_MEMORY: ParseMemory (); break; case CFGTOK_FILES: ParseFiles (); break; case CFGTOK_SEGMENTS: ParseSegments (); break; case CFGTOK_FORMATS: ParseFormats (); break; case CFGTOK_FEATURES: ParseFeatures (); break; case CFGTOK_SYMBOLS: ParseSymbols (); break; default: FAIL ("Unexpected block token"); } /* Skip closing brace */ CfgConsume (CFGTOK_RCURLY, "`}' expected"); } while (CfgTok != CFGTOK_EOF); }
static void InfoParse (void) /* Parse the config file */ { static const IdentTok Globals[] = { { "ASMINC", INFOTOK_ASMINC }, { "GLOBAL", INFOTOK_GLOBAL }, { "LABEL", INFOTOK_LABEL }, { "RANGE", INFOTOK_RANGE }, { "SEGMENT", INFOTOK_SEGMENT }, }; while (InfoTok != INFOTOK_EOF) { /* Convert an identifier into a token */ InfoSpecialToken (Globals, ENTRY_COUNT (Globals), "Config directive"); /* Check the token */ switch (InfoTok) { case INFOTOK_ASMINC: AsmIncSection (); break; case INFOTOK_GLOBAL: GlobalSection (); break; case INFOTOK_LABEL: LabelSection (); break; case INFOTOK_RANGE: RangeSection (); break; case INFOTOK_SEGMENT: SegmentSection (); break; default: Internal ("Unexpected token: %u", InfoTok); } /* Semicolon expected */ InfoConsumeSemi (); } }
static void ParseFormats (void) /* Parse a target format section */ { static const IdentTok Formats [] = { { "O65", CFGTOK_O65 }, { "BIN", CFGTOK_BIN }, { "BINARY", CFGTOK_BIN }, }; while (CfgTok == CFGTOK_IDENT) { /* Map the identifier to a token */ cfgtok_t FormatTok; CfgSpecialToken (Formats, ENTRY_COUNT (Formats), "Format"); FormatTok = CfgTok; /* Skip the name and the following colon */ CfgNextTok (); CfgConsumeColon (); /* Parse the format options */ switch (FormatTok) { case CFGTOK_O65: ParseO65 (); break; case CFGTOK_BIN: /* No attribibutes available */ break; default: Error ("Unexpected format token"); } /* Skip the semicolon */ CfgConsumeSemi (); } /* Remember we had this section */ SectionsEncountered |= SE_FORMATS; }
static void ParseFeatures (void) /* Parse a features section */ { static const IdentTok Features [] = { { "CONDES", CFGTOK_CONDES }, { "STARTADDRESS", CFGTOK_STARTADDRESS }, }; while (CfgTok == CFGTOK_IDENT) { /* Map the identifier to a token */ cfgtok_t FeatureTok; CfgSpecialToken (Features, ENTRY_COUNT (Features), "Feature"); FeatureTok = CfgTok; /* Skip the name and the following colon */ CfgNextTok (); CfgConsumeColon (); /* Parse the format options */ switch (FeatureTok) { case CFGTOK_CONDES: ParseConDes (); break; case CFGTOK_STARTADDRESS: ParseStartAddress (); break; default: FAIL ("Unexpected feature token"); } /* Skip the semicolon */ CfgConsumeSemi (); } /* Remember we had this section */ SectionsEncountered |= SE_FEATURES; }
void CfgBoolToken (void) /* Map an identifier or integer to a boolean token */ { static const IdentTok Booleans [] = { { "YES", CFGTOK_TRUE }, { "NO", CFGTOK_FALSE }, { "TRUE", CFGTOK_TRUE }, { "FALSE", CFGTOK_FALSE }, }; /* If we have an identifier, map it to a boolean token */ if (CfgTok == CFGTOK_IDENT) { CfgSpecialToken (Booleans, ENTRY_COUNT (Booleans), "Boolean"); } else { /* We expected an integer here */ if (CfgTok != CFGTOK_INTCON) { CfgError ("Boolean value expected"); } CfgTok = (CfgIVal == 0)? CFGTOK_FALSE : CFGTOK_TRUE; } }
void InfoBoolToken (void) /* Map an identifier or integer to a boolean token */ { static const IdentTok Booleans [] = { { "YES", INFOTOK_TRUE }, { "NO", INFOTOK_FALSE }, { "TRUE", INFOTOK_TRUE }, { "FALSE", INFOTOK_FALSE }, { "ON", INFOTOK_TRUE }, { "OFF", INFOTOK_FALSE }, }; /* If we have an identifier, map it to a boolean token */ if (InfoTok == INFOTOK_IDENT) { InfoSpecialToken (Booleans, ENTRY_COUNT (Booleans), "Boolean"); } else { /* We expected an integer here */ if (InfoTok != INFOTOK_INTCON) { InfoError ("Boolean value expected"); } InfoTok = (InfoIVal == 0)? INFOTOK_FALSE : INFOTOK_TRUE; } }
static void ParseO65 (void) /* Parse the o65 format section */ { static const IdentTok Attributes [] = { { "EXPORT", CFGTOK_EXPORT }, { "IMPORT", CFGTOK_IMPORT }, { "TYPE", CFGTOK_TYPE }, { "OS", CFGTOK_OS }, { "ID", CFGTOK_ID }, { "VERSION", CFGTOK_VERSION }, }; static const IdentTok Types [] = { { "SMALL", CFGTOK_SMALL }, { "LARGE", CFGTOK_LARGE }, }; static const IdentTok OperatingSystems [] = { { "LUNIX", CFGTOK_LUNIX }, { "OSA65", CFGTOK_OSA65 }, { "CC65", CFGTOK_CC65 }, { "OPENCBM", CFGTOK_OPENCBM }, }; /* Bitmask to remember the attributes we got already */ enum { atNone = 0x0000, atOS = 0x0001, atOSVersion = 0x0002, atType = 0x0004, atImport = 0x0008, atExport = 0x0010, atID = 0x0020, atVersion = 0x0040 }; unsigned AttrFlags = atNone; /* Remember the attributes read */ unsigned CfgSValId; unsigned OS = 0; /* Initialize to keep gcc happy */ unsigned Version = 0; /* Read the attributes */ while (CfgTok == CFGTOK_IDENT) { /* Map the identifier to a token */ cfgtok_t AttrTok; CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute"); AttrTok = CfgTok; /* An optional assignment follows */ CfgNextTok (); CfgOptionalAssign (); /* Check which attribute was given */ switch (AttrTok) { case CFGTOK_EXPORT: /* Remember we had this token (maybe more than once) */ AttrFlags |= atExport; /* We expect an identifier */ CfgAssureIdent (); /* Convert the string into a string index */ CfgSValId = GetStringId (CfgSVal); /* Check if the export symbol is also defined as an import. */ if (O65GetImport (O65FmtDesc, CfgSValId) != 0) { CfgError ("Exported symbol `%s' cannot be an import", CfgSVal); } /* Check if we have this symbol defined already. The entry * routine will check this also, but we get a more verbose * error message when checking it here. */ if (O65GetExport (O65FmtDesc, CfgSValId) != 0) { CfgError ("Duplicate exported symbol: `%s'", CfgSVal); } /* Insert the symbol into the table */ O65SetExport (O65FmtDesc, CfgSValId); /* Eat the identifier token */ CfgNextTok (); break; case CFGTOK_IMPORT: /* Remember we had this token (maybe more than once) */ AttrFlags |= atImport; /* We expect an identifier */ CfgAssureIdent (); /* Convert the string into a string index */ CfgSValId = GetStringId (CfgSVal); /* Check if the imported symbol is also defined as an export. */ if (O65GetExport (O65FmtDesc, CfgSValId) != 0) { CfgError ("Imported symbol `%s' cannot be an export", CfgSVal); } /* Check if we have this symbol defined already. The entry * routine will check this also, but we get a more verbose * error message when checking it here. */ if (O65GetImport (O65FmtDesc, CfgSValId) != 0) { CfgError ("Duplicate imported symbol: `%s'", CfgSVal); } /* Insert the symbol into the table */ O65SetImport (O65FmtDesc, CfgSValId); /* Eat the identifier token */ CfgNextTok (); break; case CFGTOK_TYPE: /* Cannot have this attribute twice */ FlagAttr (&AttrFlags, atType, "TYPE"); /* Get the type of the executable */ CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type"); switch (CfgTok) { case CFGTOK_SMALL: O65SetSmallModel (O65FmtDesc); break; case CFGTOK_LARGE: O65SetLargeModel (O65FmtDesc); break; default: CfgError ("Unexpected type token"); } /* Eat the attribute token */ CfgNextTok (); break; case CFGTOK_OS: /* Cannot use this attribute twice */ FlagAttr (&AttrFlags, atOS, "OS"); /* Get the operating system. It may be specified as name or * as a number in the range 1..255. */ if (CfgTok == CFGTOK_INTCON) { CfgRangeCheck (O65OS_MIN, O65OS_MAX); OS = (unsigned) CfgIVal; } else { CfgSpecialToken (OperatingSystems, ENTRY_COUNT (OperatingSystems), "OS type"); switch (CfgTok) { case CFGTOK_LUNIX: OS = O65OS_LUNIX; break; case CFGTOK_OSA65: OS = O65OS_OSA65; break; case CFGTOK_CC65: OS = O65OS_CC65; break; case CFGTOK_OPENCBM: OS = O65OS_OPENCBM; break; default: CfgError ("Unexpected OS token"); } } CfgNextTok (); break; case CFGTOK_ID: /* Cannot have this attribute twice */ FlagAttr (&AttrFlags, atID, "ID"); /* We're expecting a number in the 0..$FFFF range*/ ModuleId = (unsigned) CfgCheckedIntExpr (0, 0xFFFF); break; case CFGTOK_VERSION: /* Cannot have this attribute twice */ FlagAttr (&AttrFlags, atVersion, "VERSION"); /* We're expecting a number in byte range */ Version = (unsigned) CfgCheckedIntExpr (0, 0xFF); break; default: FAIL ("Unexpected attribute token"); } /* Skip an optional comma */ CfgOptionalComma (); } /* Check if we have all mandatory attributes */ AttrCheck (AttrFlags, atOS, "OS"); /* Check for attributes that may not be combined */ if (OS == O65OS_CC65) { if ((AttrFlags & (atImport | atExport)) != 0 && ModuleId < 0x8000) { CfgError ("OS type CC65 may not have imports or exports for ids < $8000"); } } else { if (AttrFlags & atID) { CfgError ("Operating system does not support the ID attribute"); } } /* Set the O65 operating system to use */ O65SetOS (O65FmtDesc, OS, Version, ModuleId); }
static void AsmIncSection (void) /* Parse a asminc section */ { static const IdentTok LabelDefs[] = { { "COMMENTSTART", INFOTOK_COMMENTSTART }, { "FILE", INFOTOK_FILE }, { "IGNOREUNKNOWN", INFOTOK_IGNOREUNKNOWN }, }; /* Locals - initialize to avoid gcc warnings */ char* Name = 0; int CommentStart = EOF; int IgnoreUnknown = -1; /* Skip the token */ InfoNextTok (); /* Expect the opening curly brace */ InfoConsumeLCurly (); /* Look for section tokens */ while (InfoTok != INFOTOK_RCURLY) { /* Convert to special token */ InfoSpecialToken (LabelDefs, ENTRY_COUNT (LabelDefs), "Asminc directive"); /* Look at the token */ switch (InfoTok) { case INFOTOK_COMMENTSTART: InfoNextTok (); if (CommentStart != EOF) { InfoError ("Commentstart already given"); } InfoAssureChar (); CommentStart = (char) InfoIVal; InfoNextTok (); break; case INFOTOK_FILE: InfoNextTok (); if (Name) { InfoError ("File name already given"); } InfoAssureStr (); if (InfoSVal[0] == '\0') { InfoError ("File name may not be empty"); } Name = xstrdup (InfoSVal); InfoNextTok (); break; case INFOTOK_IGNOREUNKNOWN: InfoNextTok (); if (IgnoreUnknown != -1) { InfoError ("Ignoreunknown already specified"); } InfoBoolToken (); IgnoreUnknown = (InfoTok != INFOTOK_FALSE); InfoNextTok (); break; default: Internal ("Unexpected token: %u", InfoTok); } /* Directive is followed by a semicolon */ InfoConsumeSemi (); } /* Check for the necessary data and assume defaults */ if (Name == 0) { InfoError ("File name is missing"); } if (CommentStart == EOF) { CommentStart = ';'; } if (IgnoreUnknown == -1) { IgnoreUnknown = 0; } /* Open the file and read the symbol definitions */ AsmInc (Name, CommentStart, IgnoreUnknown); /* Delete the dynamically allocated memory for Name */ xfree (Name); /* Consume the closing brace */ InfoConsumeRCurly (); }
static void ParseStartAddress (void) /* Parse the STARTADDRESS feature */ { static const IdentTok Attributes [] = { { "DEFAULT", CFGTOK_DEFAULT }, }; /* Attribute values. */ unsigned long DefStartAddr = 0; /* Bitmask to remember the attributes we got already */ enum { atNone = 0x0000, atDefault = 0x0001 }; unsigned AttrFlags = atNone; /* Parse the attributes */ while (1) { /* Map the identifier to a token */ cfgtok_t AttrTok; CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute"); AttrTok = CfgTok; /* An optional assignment follows */ CfgNextTok (); CfgOptionalAssign (); /* Check which attribute was given */ switch (AttrTok) { case CFGTOK_DEFAULT: /* Don't allow this twice */ FlagAttr (&AttrFlags, atDefault, "DEFAULT"); /* We expect a numeric expression */ DefStartAddr = CfgCheckedIntExpr (0, 0xFFFFFF); break; default: FAIL ("Unexpected attribute token"); } /* Semicolon ends the ConDes decl, otherwise accept an optional comma */ if (CfgTok == CFGTOK_SEMI) { break; } else if (CfgTok == CFGTOK_COMMA) { CfgNextTok (); } } /* Check if we have all mandatory attributes */ AttrCheck (AttrFlags, atDefault, "DEFAULT"); /* If no start address was given on the command line, use the one given * here */ if (!HaveStartAddr) { StartAddr = DefStartAddr; } }
static void SegmentSection (void) /* Parse a segment section */ { static const IdentTok LabelDefs[] = { { "END", INFOTOK_END }, { "NAME", INFOTOK_NAME }, { "START", INFOTOK_START }, }; /* Locals - initialize to avoid gcc warnings */ long End = -1; long Start = -1; char* Name = 0; /* Skip the token */ InfoNextTok (); /* Expect the opening curly brace */ InfoConsumeLCurly (); /* Look for section tokens */ while (InfoTok != INFOTOK_RCURLY) { /* Convert to special token */ InfoSpecialToken (LabelDefs, ENTRY_COUNT (LabelDefs), "Segment attribute"); /* Look at the token */ switch (InfoTok) { case INFOTOK_END: InfoNextTok (); if (End >= 0) { InfoError ("Value already given"); } InfoAssureInt (); InfoRangeCheck (0, 0xFFFF); End = InfoIVal; InfoNextTok (); break; case INFOTOK_NAME: InfoNextTok (); if (Name) { InfoError ("Name already given"); } InfoAssureStr (); Name = xstrdup (InfoSVal); InfoNextTok (); break; case INFOTOK_START: InfoNextTok (); if (Start >= 0) { InfoError ("Value already given"); } InfoAssureInt (); InfoRangeCheck (0, 0xFFFF); Start = InfoIVal; InfoNextTok (); break; default: Internal ("Unexpected token: %u", InfoTok); } /* Directive is followed by a semicolon */ InfoConsumeSemi (); } /* Did we get the necessary data, and is it correct? */ if (Name == 0 || Name[0] == '\0') { InfoError ("Segment name is missing"); } if (End < 0) { InfoError ("End address is missing"); } if (Start < 0) { InfoError ("Start address is missing"); } if (Start > End) { InfoError ("Start address of segment is greater than end address"); } /* Check that segments do not overlap */ if (SegmentDefined ((unsigned) Start, (unsigned) End)) { InfoError ("Segments must not overlap"); } /* Remember the segment data */ AddAbsSegment ((unsigned) Start, (unsigned) End, Name); /* Delete the dynamically allocated memory for Name */ xfree (Name); /* Consume the closing brace */ InfoConsumeRCurly (); }
static void RangeSection (void) /* Parse a range section */ { static const IdentTok RangeDefs[] = { { "COMMENT", INFOTOK_COMMENT }, { "END", INFOTOK_END }, { "NAME", INFOTOK_NAME }, { "START", INFOTOK_START }, { "TYPE", INFOTOK_TYPE }, }; static const IdentTok TypeDefs[] = { { "ADDRTABLE", INFOTOK_ADDRTAB }, { "BYTETABLE", INFOTOK_BYTETAB }, { "CODE", INFOTOK_CODE }, { "DBYTETABLE", INFOTOK_DBYTETAB }, { "DWORDTABLE", INFOTOK_DWORDTAB }, { "RTSTABLE", INFOTOK_RTSTAB }, { "SKIP", INFOTOK_SKIP }, { "TEXTTABLE", INFOTOK_TEXTTAB }, { "WORDTABLE", INFOTOK_WORDTAB }, }; /* Which values did we get? */ enum { tNone = 0x00, tStart = 0x01, tEnd = 0x02, tType = 0x04, tName = 0x08, tComment= 0x10, tNeeded = (tStart | tEnd | tType) }; unsigned Attributes = tNone; /* Locals - initialize to avoid gcc warnings */ unsigned Start = 0; unsigned End = 0; unsigned char Type = 0; char* Name = 0; char* Comment = 0; unsigned MemberSize = 0; /* Skip the token */ InfoNextTok (); /* Expect the opening curly brace */ InfoConsumeLCurly (); /* Look for section tokens */ while (InfoTok != INFOTOK_RCURLY) { /* Convert to special token */ InfoSpecialToken (RangeDefs, ENTRY_COUNT (RangeDefs), "Range attribute"); /* Look at the token */ switch (InfoTok) { case INFOTOK_COMMENT: AddAttr ("COMMENT", &Attributes, tComment); InfoNextTok (); InfoAssureStr (); if (InfoSVal[0] == '\0') { InfoError ("Comment may not be empty"); } Comment = xstrdup (InfoSVal); Attributes |= tComment; InfoNextTok (); break; case INFOTOK_END: AddAttr ("END", &Attributes, tEnd); InfoNextTok (); InfoAssureInt (); InfoRangeCheck (0x0000, 0xFFFF); End = InfoIVal; InfoNextTok (); break; case INFOTOK_NAME: AddAttr ("NAME", &Attributes, tName); InfoNextTok (); InfoAssureStr (); if (InfoSVal[0] == '\0') { InfoError ("Name may not be empty"); } Name = xstrdup (InfoSVal); Attributes |= tName; InfoNextTok (); break; case INFOTOK_START: AddAttr ("START", &Attributes, tStart); InfoNextTok (); InfoAssureInt (); InfoRangeCheck (0x0000, 0xFFFF); Start = InfoIVal; InfoNextTok (); break; case INFOTOK_TYPE: AddAttr ("TYPE", &Attributes, tType); InfoNextTok (); InfoSpecialToken (TypeDefs, ENTRY_COUNT (TypeDefs), "TYPE"); switch (InfoTok) { case INFOTOK_ADDRTAB: Type = atAddrTab; MemberSize = 2; break; case INFOTOK_BYTETAB: Type = atByteTab; MemberSize = 1; break; case INFOTOK_CODE: Type = atCode; MemberSize = 1; break; case INFOTOK_DBYTETAB: Type = atDByteTab; MemberSize = 2; break; case INFOTOK_DWORDTAB: Type = atDWordTab; MemberSize = 4; break; case INFOTOK_RTSTAB: Type = atRtsTab; MemberSize = 2; break; case INFOTOK_SKIP: Type = atSkip; MemberSize = 1; break; case INFOTOK_TEXTTAB: Type = atTextTab; MemberSize = 1; break; case INFOTOK_WORDTAB: Type = atWordTab; MemberSize = 2; break; } InfoNextTok (); break; default: Internal ("Unexpected token: %u", InfoTok); } /* Directive is followed by a semicolon */ InfoConsumeSemi (); } /* Did we get all required values? */ if ((Attributes & tNeeded) != tNeeded) { InfoError ("Required values missing from this section"); } /* Start must be less than end */ if (Start > End) { InfoError ("Start value must not be greater than end value"); } /* Check the granularity */ if (((End - Start + 1) % MemberSize) != 0) { InfoError ("Type of range needs a granularity of %u", MemberSize); } /* Set the range */ MarkRange (Start, End, Type); /* Do we have a label? */ if (Attributes & tName) { /* Define a label for the table */ AddExtLabel (Start, Name); /* Set the comment if we have one */ if (Comment) { SetComment (Start, Comment); } /* Delete name and comment */ xfree (Name); xfree (Comment); } /* Consume the closing brace */ InfoConsumeRCurly (); }
static void LabelSection (void) /* Parse a label section */ { static const IdentTok LabelDefs[] = { { "COMMENT", INFOTOK_COMMENT }, { "ADDR", INFOTOK_ADDR }, { "NAME", INFOTOK_NAME }, { "SIZE", INFOTOK_SIZE }, }; /* Locals - initialize to avoid gcc warnings */ char* Name = 0; char* Comment = 0; long Value = -1; long Size = -1; /* Skip the token */ InfoNextTok (); /* Expect the opening curly brace */ InfoConsumeLCurly (); /* Look for section tokens */ while (InfoTok != INFOTOK_RCURLY) { /* Convert to special token */ InfoSpecialToken (LabelDefs, ENTRY_COUNT (LabelDefs), "Label attribute"); /* Look at the token */ switch (InfoTok) { case INFOTOK_ADDR: InfoNextTok (); if (Value >= 0) { InfoError ("Value already given"); } InfoAssureInt (); InfoRangeCheck (0, 0xFFFF); Value = InfoIVal; InfoNextTok (); break; case INFOTOK_COMMENT: InfoNextTok (); if (Comment) { InfoError ("Comment already given"); } InfoAssureStr (); if (InfoSVal[0] == '\0') { InfoError ("Comment may not be empty"); } Comment = xstrdup (InfoSVal); InfoNextTok (); break; case INFOTOK_NAME: InfoNextTok (); if (Name) { InfoError ("Name already given"); } InfoAssureStr (); Name = xstrdup (InfoSVal); InfoNextTok (); break; case INFOTOK_SIZE: InfoNextTok (); if (Size >= 0) { InfoError ("Size already given"); } InfoAssureInt (); InfoRangeCheck (1, 0x10000); Size = InfoIVal; InfoNextTok (); break; default: Internal ("Unexpected token: %u", InfoTok); } /* Directive is followed by a semicolon */ InfoConsumeSemi (); } /* Did we get the necessary data */ if (Name == 0) { InfoError ("Label name is missing"); } if (Name[0] == '\0' && Size > 1) { InfoError ("Unnamed labels must not have a size > 1"); } if (Value < 0) { InfoError ("Label value is missing"); } if (Size < 0) { /* Use default */ Size = 1; } if (Value + Size > 0x10000) { InfoError ("Invalid size (address out of range)"); } if (HaveLabel ((unsigned) Value)) { InfoError ("Label for address $%04lX already defined", Value); } /* Define the label(s) */ if (Name[0] == '\0') { /* Size has already beed checked */ AddUnnamedLabel (Value); } else { AddExtLabelRange ((unsigned) Value, Name, Size); } /* Define the comment */ if (Comment) { SetComment (Value, Comment); } /* Delete the dynamically allocated memory for Name and Comment */ xfree (Name); xfree (Comment); /* Consume the closing brace */ InfoConsumeRCurly (); }
static void GlobalSection (void) /* Parse a global section */ { static const IdentTok GlobalDefs[] = { { "ARGUMENTCOL", INFOTOK_ARGUMENT_COLUMN }, { "ARGUMENTCOLUMN", INFOTOK_ARGUMENT_COLUMN }, { "COMMENTCOL", INFOTOK_COMMENT_COLUMN }, { "COMMENTCOLUMN", INFOTOK_COMMENT_COLUMN }, { "COMMENTS", INFOTOK_COMMENTS }, { "CPU", INFOTOK_CPU }, { "HEXOFFS", INFOTOK_HEXOFFS }, { "INPUTNAME", INFOTOK_INPUTNAME }, { "INPUTOFFS", INFOTOK_INPUTOFFS }, { "INPUTSIZE", INFOTOK_INPUTSIZE }, { "LABELBREAK", INFOTOK_LABELBREAK }, { "MNEMONICCOL", INFOTOK_MNEMONIC_COLUMN }, { "MNEMONICCOLUMN", INFOTOK_MNEMONIC_COLUMN }, { "NEWLINEAFTERJMP", INFOTOK_NL_AFTER_JMP }, { "NEWLINEAFTERRTS", INFOTOK_NL_AFTER_RTS }, { "OUTPUTNAME", INFOTOK_OUTPUTNAME }, { "PAGELENGTH", INFOTOK_PAGELENGTH }, { "STARTADDR", INFOTOK_STARTADDR }, { "TEXTCOL", INFOTOK_TEXT_COLUMN }, { "TEXTCOLUMN", INFOTOK_TEXT_COLUMN }, }; /* Skip the token */ InfoNextTok (); /* Expect the opening curly brace */ InfoConsumeLCurly (); /* Look for section tokens */ while (InfoTok != INFOTOK_RCURLY) { /* Convert to special token */ InfoSpecialToken (GlobalDefs, ENTRY_COUNT (GlobalDefs), "Global directive"); /* Look at the token */ switch (InfoTok) { case INFOTOK_ARGUMENT_COLUMN: InfoNextTok (); InfoAssureInt (); InfoRangeCheck (MIN_ACOL, MAX_ACOL); ACol = InfoIVal; InfoNextTok (); break; case INFOTOK_COMMENT_COLUMN: InfoNextTok (); InfoAssureInt (); InfoRangeCheck (MIN_CCOL, MAX_CCOL); CCol = InfoIVal; InfoNextTok (); break; case INFOTOK_COMMENTS: InfoNextTok (); InfoAssureInt (); InfoRangeCheck (MIN_COMMENTS, MAX_COMMENTS); Comments = InfoIVal; InfoNextTok (); break; case INFOTOK_CPU: InfoNextTok (); InfoAssureStr (); if (CPU != CPU_UNKNOWN) { InfoError ("CPU already specified"); } CPU = FindCPU (InfoSVal); SetOpcTable (CPU); InfoNextTok (); break; case INFOTOK_HEXOFFS: InfoNextTok (); InfoBoolToken (); switch (InfoTok) { case INFOTOK_FALSE: UseHexOffs = 0; break; case INFOTOK_TRUE: UseHexOffs = 1; break; } InfoNextTok (); break; case INFOTOK_INPUTNAME: InfoNextTok (); InfoAssureStr (); if (InFile) { InfoError ("Input file name already given"); } InFile = xstrdup (InfoSVal); InfoNextTok (); break; case INFOTOK_INPUTOFFS: InfoNextTok (); InfoAssureInt (); InputOffs = InfoIVal; InfoNextTok (); break; case INFOTOK_INPUTSIZE: InfoNextTok (); InfoAssureInt (); InfoRangeCheck (1, 0x10000); InputSize = InfoIVal; InfoNextTok (); break; case INFOTOK_LABELBREAK: InfoNextTok (); InfoAssureInt (); InfoRangeCheck (0, UCHAR_MAX); LBreak = (unsigned char) InfoIVal; InfoNextTok (); break; case INFOTOK_MNEMONIC_COLUMN: InfoNextTok (); InfoAssureInt (); InfoRangeCheck (MIN_MCOL, MAX_MCOL); MCol = InfoIVal; InfoNextTok (); break; case INFOTOK_NL_AFTER_JMP: InfoNextTok (); if (NewlineAfterJMP != -1) { InfoError ("NLAfterJMP already specified"); } InfoBoolToken (); NewlineAfterJMP = (InfoTok != INFOTOK_FALSE); InfoNextTok (); break; case INFOTOK_NL_AFTER_RTS: InfoNextTok (); InfoBoolToken (); if (NewlineAfterRTS != -1) { InfoError ("NLAfterRTS already specified"); } NewlineAfterRTS = (InfoTok != INFOTOK_FALSE); InfoNextTok (); break; case INFOTOK_OUTPUTNAME: InfoNextTok (); InfoAssureStr (); if (OutFile) { InfoError ("Output file name already given"); } OutFile = xstrdup (InfoSVal); InfoNextTok (); break; case INFOTOK_PAGELENGTH: InfoNextTok (); InfoAssureInt (); if (InfoIVal != 0) { InfoRangeCheck (MIN_PAGE_LEN, MAX_PAGE_LEN); } PageLength = InfoIVal; InfoNextTok (); break; case INFOTOK_STARTADDR: InfoNextTok (); InfoAssureInt (); InfoRangeCheck (0x0000, 0xFFFF); StartAddr = InfoIVal; InfoNextTok (); break; case INFOTOK_TEXT_COLUMN: InfoNextTok (); InfoAssureInt (); InfoRangeCheck (MIN_TCOL, MAX_TCOL); TCol = InfoIVal; InfoNextTok (); break; default: Internal ("Unexpected token: %u", InfoTok); } /* Directive is followed by a semicolon */ InfoConsumeSemi (); } /* Consume the closing brace */ InfoConsumeRCurly (); }
static void ParseConDes (void) /* Parse the CONDES feature */ { static const IdentTok Attributes [] = { { "SEGMENT", CFGTOK_SEGMENT }, { "LABEL", CFGTOK_LABEL }, { "COUNT", CFGTOK_COUNT }, { "TYPE", CFGTOK_TYPE }, { "ORDER", CFGTOK_ORDER }, }; static const IdentTok Types [] = { { "CONSTRUCTOR", CFGTOK_CONSTRUCTOR }, { "DESTRUCTOR", CFGTOK_DESTRUCTOR }, { "INTERRUPTOR", CFGTOK_INTERRUPTOR }, }; static const IdentTok Orders [] = { { "DECREASING", CFGTOK_DECREASING }, { "INCREASING", CFGTOK_INCREASING }, }; /* Attribute values. */ unsigned SegName = INVALID_STRING_ID; unsigned Label = INVALID_STRING_ID; unsigned Count = INVALID_STRING_ID; /* Initialize to avoid gcc warnings: */ int Type = -1; ConDesOrder Order = cdIncreasing; /* Bitmask to remember the attributes we got already */ enum { atNone = 0x0000, atSegName = 0x0001, atLabel = 0x0002, atCount = 0x0004, atType = 0x0008, atOrder = 0x0010 }; unsigned AttrFlags = atNone; /* Parse the attributes */ while (1) { /* Map the identifier to a token */ cfgtok_t AttrTok; CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute"); AttrTok = CfgTok; /* An optional assignment follows */ CfgNextTok (); CfgOptionalAssign (); /* Check which attribute was given */ switch (AttrTok) { case CFGTOK_SEGMENT: /* Don't allow this twice */ FlagAttr (&AttrFlags, atSegName, "SEGMENT"); /* We expect an identifier */ CfgAssureIdent (); /* Remember the value for later */ SegName = GetStringId (CfgSVal); break; case CFGTOK_LABEL: /* Don't allow this twice */ FlagAttr (&AttrFlags, atLabel, "LABEL"); /* We expect an identifier */ CfgAssureIdent (); /* Remember the value for later */ Label = GetStringId (CfgSVal); break; case CFGTOK_COUNT: /* Don't allow this twice */ FlagAttr (&AttrFlags, atCount, "COUNT"); /* We expect an identifier */ CfgAssureIdent (); /* Remember the value for later */ Count = GetStringId (CfgSVal); break; case CFGTOK_TYPE: /* Don't allow this twice */ FlagAttr (&AttrFlags, atType, "TYPE"); /* The type may be given as id or numerical */ if (CfgTok == CFGTOK_INTCON) { CfgRangeCheck (CD_TYPE_MIN, CD_TYPE_MAX); Type = (int) CfgIVal; } else { CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type"); switch (CfgTok) { case CFGTOK_CONSTRUCTOR: Type = CD_TYPE_CON; break; case CFGTOK_DESTRUCTOR: Type = CD_TYPE_DES; break; case CFGTOK_INTERRUPTOR: Type = CD_TYPE_INT; break; default: FAIL ("Unexpected type token"); } } break; case CFGTOK_ORDER: /* Don't allow this twice */ FlagAttr (&AttrFlags, atOrder, "ORDER"); CfgSpecialToken (Orders, ENTRY_COUNT (Orders), "Order"); switch (CfgTok) { case CFGTOK_DECREASING: Order = cdDecreasing; break; case CFGTOK_INCREASING: Order = cdIncreasing; break; default: FAIL ("Unexpected order token"); } break; default: FAIL ("Unexpected attribute token"); } /* Skip the attribute value */ CfgNextTok (); /* Semicolon ends the ConDes decl, otherwise accept an optional comma */ if (CfgTok == CFGTOK_SEMI) { break; } else if (CfgTok == CFGTOK_COMMA) { CfgNextTok (); } } /* Check if we have all mandatory attributes */ AttrCheck (AttrFlags, atSegName, "SEGMENT"); AttrCheck (AttrFlags, atLabel, "LABEL"); AttrCheck (AttrFlags, atType, "TYPE"); /* Check if the condes has already attributes defined */ if (ConDesHasSegName(Type) || ConDesHasLabel(Type)) { CfgError ("CONDES attributes for type %d are already defined", Type); } /* Define the attributes */ ConDesSetSegName (Type, SegName); ConDesSetLabel (Type, Label); if (AttrFlags & atCount) { ConDesSetCountSym (Type, Count); } if (AttrFlags & atOrder) { ConDesSetOrder (Type, Order); } }
static void ParseSegments (void) /* Parse a SEGMENTS section */ { static const IdentTok Attributes [] = { { "ALIGN", CFGTOK_ALIGN }, { "ALIGN_LOAD", CFGTOK_ALIGN_LOAD }, { "DEFINE", CFGTOK_DEFINE }, { "LOAD", CFGTOK_LOAD }, { "OFFSET", CFGTOK_OFFSET }, { "OPTIONAL", CFGTOK_OPTIONAL }, { "RUN", CFGTOK_RUN }, { "START", CFGTOK_START }, { "TYPE", CFGTOK_TYPE }, }; static const IdentTok Types [] = { { "RO", CFGTOK_RO }, { "RW", CFGTOK_RW }, { "BSS", CFGTOK_BSS }, { "ZP", CFGTOK_ZP }, }; unsigned Count; long Val; /* The MEMORY section must preceed the SEGMENTS section */ if ((SectionsEncountered & SE_MEMORY) == 0) { CfgError ("MEMORY must precede SEGMENTS"); } while (CfgTok == CFGTOK_IDENT) { SegDesc* S; /* Create a new entry on the heap */ S = NewSegDesc (GetStringId (CfgSVal)); /* Skip the name and the following colon */ CfgNextTok (); CfgConsumeColon (); /* Read the attributes */ while (CfgTok == CFGTOK_IDENT) { /* Map the identifier to a token */ cfgtok_t AttrTok; CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute"); AttrTok = CfgTok; /* An optional assignment follows */ CfgNextTok (); CfgOptionalAssign (); /* Check which attribute was given */ switch (AttrTok) { case CFGTOK_ALIGN: FlagAttr (&S->Attr, SA_ALIGN, "ALIGN"); Val = CfgCheckedIntExpr (1, 0x10000); S->Align = BitFind (Val); if ((0x01L << S->Align) != Val) { CfgError ("Alignment must be a power of 2"); } S->Flags |= SF_ALIGN; break; case CFGTOK_ALIGN_LOAD: FlagAttr (&S->Attr, SA_ALIGN_LOAD, "ALIGN_LOAD"); Val = CfgCheckedIntExpr (1, 0x10000); S->AlignLoad = BitFind (Val); if ((0x01L << S->AlignLoad) != Val) { CfgError ("Alignment must be a power of 2"); } S->Flags |= SF_ALIGN_LOAD; break; case CFGTOK_DEFINE: FlagAttr (&S->Attr, SA_DEFINE, "DEFINE"); /* Map the token to a boolean */ CfgBoolToken (); if (CfgTok == CFGTOK_TRUE) { S->Flags |= SF_DEFINE; } CfgNextTok (); break; case CFGTOK_LOAD: FlagAttr (&S->Attr, SA_LOAD, "LOAD"); S->Load = CfgGetMemory (GetStringId (CfgSVal)); CfgNextTok (); break; case CFGTOK_OFFSET: FlagAttr (&S->Attr, SA_OFFSET, "OFFSET"); S->Addr = CfgCheckedIntExpr (1, 0x1000000); S->Flags |= SF_OFFSET; break; case CFGTOK_OPTIONAL: FlagAttr (&S->Attr, SA_OPTIONAL, "OPTIONAL"); CfgBoolToken (); if (CfgTok == CFGTOK_TRUE) { S->Flags |= SF_OPTIONAL; } CfgNextTok (); break; case CFGTOK_RUN: FlagAttr (&S->Attr, SA_RUN, "RUN"); S->Run = CfgGetMemory (GetStringId (CfgSVal)); CfgNextTok (); break; case CFGTOK_START: FlagAttr (&S->Attr, SA_START, "START"); S->Addr = CfgCheckedIntExpr (1, 0x1000000); S->Flags |= SF_START; break; case CFGTOK_TYPE: FlagAttr (&S->Attr, SA_TYPE, "TYPE"); CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type"); switch (CfgTok) { case CFGTOK_RO: S->Flags |= SF_RO; break; case CFGTOK_RW: /* Default */ break; case CFGTOK_BSS: S->Flags |= SF_BSS; break; case CFGTOK_ZP: S->Flags |= (SF_BSS | SF_ZP); break; default: Internal ("Unexpected token: %d", CfgTok); } CfgNextTok (); break; default: FAIL ("Unexpected attribute token"); } /* Skip an optional comma */ CfgOptionalComma (); } /* Check for mandatory parameters */ AttrCheck (S->Attr, SA_LOAD, "LOAD"); /* Set defaults for stuff not given */ if ((S->Attr & SA_RUN) == 0) { S->Attr |= SA_RUN; S->Run = S->Load; } /* If the segment is marked as BSS style, and if the segment exists * in any of the object file, check that there's no initialized data * in the segment. */ if ((S->Flags & SF_BSS) != 0 && S->Seg != 0 && !IsBSSType (S->Seg)) { Warning ("%s(%u): Segment with type `bss' contains initialized data", CfgGetName (), CfgErrorLine); } /* An attribute of ALIGN_LOAD doesn't make sense if there are no * separate run and load memory areas. */ if ((S->Flags & SF_ALIGN_LOAD) != 0 && (S->Load == S->Run)) { Warning ("%s(%u): ALIGN_LOAD attribute specified, but no separate " "LOAD and RUN memory areas assigned", CfgGetName (), CfgErrorLine); /* Remove the flag */ S->Flags &= ~SF_ALIGN_LOAD; } /* If the segment is marked as BSS style, it may not have separate * load and run memory areas, because it's is never written to disk. */ if ((S->Flags & SF_BSS) != 0 && (S->Load != S->Run)) { Warning ("%s(%u): Segment with type `bss' has both LOAD and RUN " "memory areas assigned", CfgGetName (), CfgErrorLine); } /* Don't allow read/write data to be put into a readonly area */ if ((S->Flags & SF_RO) == 0) { if (S->Run->Flags & MF_RO) { CfgError ("Cannot put r/w segment `%s' in r/o memory area `%s'", GetString (S->Name), GetString (S->Run->Name)); } } /* Only one of ALIGN, START and OFFSET may be used */ Count = ((S->Flags & SF_ALIGN) != 0) + ((S->Flags & SF_OFFSET) != 0) + ((S->Flags & SF_START) != 0); if (Count > 1) { CfgError ("Only one of ALIGN, START, OFFSET may be used"); } /* If this segment does exist in any of the object files, insert the * descriptor into the list of segment descriptors. Otherwise print a * warning and discard it, because the segment pointer in the * descriptor is invalid. */ if (S->Seg != 0) { /* Insert the descriptor into the list of all descriptors */ SegDescInsert (S); /* Insert the segment into the memory area list */ MemoryInsert (S->Run, S); if (S->Load != S->Run) { /* We have separate RUN and LOAD areas */ MemoryInsert (S->Load, S); } } else { /* Print a warning if the segment is not optional */ if ((S->Flags & SF_OPTIONAL) == 0) { CfgWarning ("Segment `%s' does not exist", GetString (S->Name)); } /* Discard the descriptor */ FreeSegDesc (S); } /* Skip the semicolon */ CfgConsumeSemi (); } /* Remember we had this section */ SectionsEncountered |= SE_SEGMENTS; }
static void ParseFiles (void) /* Parse a FILES section */ { static const IdentTok Attributes [] = { { "FORMAT", CFGTOK_FORMAT }, }; static const IdentTok Formats [] = { { "O65", CFGTOK_O65 }, { "BIN", CFGTOK_BIN }, { "BINARY", CFGTOK_BIN }, }; /* The MEMORY section must preceed the FILES section */ if ((SectionsEncountered & SE_MEMORY) == 0) { CfgError ("MEMORY must precede FILES"); } /* Parse all files */ while (CfgTok != CFGTOK_RCURLY) { File* F; /* We expect a string value here */ CfgAssureStr (); /* Search for the file, it must exist */ F = FindFile (GetStringId (CfgSVal)); if (F == 0) { CfgError ("File `%s' not found in MEMORY section", CfgSVal); } /* Skip the token and the following colon */ CfgNextTok (); CfgConsumeColon (); /* Read the attributes */ while (CfgTok == CFGTOK_IDENT) { /* Map the identifier to a token */ cfgtok_t AttrTok; CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute"); AttrTok = CfgTok; /* An optional assignment follows */ CfgNextTok (); CfgOptionalAssign (); /* Check which attribute was given */ switch (AttrTok) { case CFGTOK_FORMAT: if (F->Format != BINFMT_DEFAULT) { /* We've set the format already! */ Error ("Cannot set a file format twice"); } /* Read the format token */ CfgSpecialToken (Formats, ENTRY_COUNT (Formats), "Format"); switch (CfgTok) { case CFGTOK_BIN: F->Format = BINFMT_BINARY; break; case CFGTOK_O65: F->Format = BINFMT_O65; break; default: Error ("Unexpected format token"); } break; default: FAIL ("Unexpected attribute token"); } /* Skip the attribute value and an optional comma */ CfgNextTok (); CfgOptionalComma (); } /* Skip the semicolon */ CfgConsumeSemi (); } /* Remember we had this section */ SectionsEncountered |= SE_FILES; }
static void ParseMemory (void) /* Parse a MEMORY section */ { static const IdentTok Attributes [] = { { "START", CFGTOK_START }, { "SIZE", CFGTOK_SIZE }, { "TYPE", CFGTOK_TYPE }, { "FILE", CFGTOK_FILE }, { "DEFINE", CFGTOK_DEFINE }, { "FILL", CFGTOK_FILL }, { "FILLVAL", CFGTOK_FILLVAL }, }; static const IdentTok Types [] = { { "RO", CFGTOK_RO }, { "RW", CFGTOK_RW }, }; while (CfgTok == CFGTOK_IDENT) { /* Create a new entry on the heap */ Memory* M = NewMemory (GetStringId (CfgSVal)); /* Skip the name and the following colon */ CfgNextTok (); CfgConsumeColon (); /* Read the attributes */ while (CfgTok == CFGTOK_IDENT) { /* Map the identifier to a token */ cfgtok_t AttrTok; CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute"); AttrTok = CfgTok; /* An optional assignment follows */ CfgNextTok (); CfgOptionalAssign (); /* Check which attribute was given */ switch (AttrTok) { case CFGTOK_START: FlagAttr (&M->Attr, MA_START, "START"); M->Start = CfgIntExpr (); break; case CFGTOK_SIZE: FlagAttr (&M->Attr, MA_SIZE, "SIZE"); M->Size = CfgIntExpr (); break; case CFGTOK_TYPE: FlagAttr (&M->Attr, MA_TYPE, "TYPE"); CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type"); if (CfgTok == CFGTOK_RO) { M->Flags |= MF_RO; } CfgNextTok (); break; case CFGTOK_FILE: FlagAttr (&M->Attr, MA_FILE, "FILE"); CfgAssureStr (); /* Get the file entry and insert the memory area */ FileInsert (GetFile (GetStringId (CfgSVal)), M); CfgNextTok (); break; case CFGTOK_DEFINE: FlagAttr (&M->Attr, MA_DEFINE, "DEFINE"); /* Map the token to a boolean */ CfgBoolToken (); if (CfgTok == CFGTOK_TRUE) { M->Flags |= MF_DEFINE; } CfgNextTok (); break; case CFGTOK_FILL: FlagAttr (&M->Attr, MA_FILL, "FILL"); /* Map the token to a boolean */ CfgBoolToken (); if (CfgTok == CFGTOK_TRUE) { M->Flags |= MF_FILL; } CfgNextTok (); break; case CFGTOK_FILLVAL: FlagAttr (&M->Attr, MA_FILLVAL, "FILLVAL"); M->FillVal = (unsigned char) CfgCheckedIntExpr (0, 0xFF); break; default: FAIL ("Unexpected attribute token"); } /* Skip an optional comma */ CfgOptionalComma (); } /* Skip the semicolon */ CfgConsumeSemi (); /* Check for mandatory parameters */ AttrCheck (M->Attr, MA_START, "START"); AttrCheck (M->Attr, MA_SIZE, "SIZE"); /* If we don't have a file name for output given, use the default * file name. */ if ((M->Attr & MA_FILE) == 0) { FileInsert (GetFile (GetStringId (OutputName)), M); } } /* Remember we had this section */ SectionsEncountered |= SE_MEMORY; }
static void ParseSymbols (void) /* Parse a symbols section */ { static const IdentTok Attributes[] = { { "VALUE", CFGTOK_VALUE }, { "WEAK", CFGTOK_WEAK }, }; while (CfgTok == CFGTOK_IDENT) { long Val = 0L; int Weak = 0; Export* E; /* Remember the name */ unsigned Name = GetStringId (CfgSVal); CfgNextTok (); /* Support both, old and new syntax here. New syntax is a colon * followed by an attribute list, old syntax is an optional equal * sign plus a value. */ if (CfgTok != CFGTOK_COLON) { /* Old syntax */ /* Allow an optional assignment */ CfgOptionalAssign (); /* Make sure the next token is an integer expression, read and * skip it. */ Val = CfgIntExpr (); } else { /* Bitmask to remember the attributes we got already */ enum { atNone = 0x0000, atValue = 0x0001, atWeak = 0x0002 }; unsigned AttrFlags = atNone; /* New syntax - skip the colon */ CfgNextTok (); /* Parse the attributes */ while (1) { /* Map the identifier to a token */ cfgtok_t AttrTok; CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute"); AttrTok = CfgTok; /* Skip the attribute name */ CfgNextTok (); /* An optional assignment follows */ CfgOptionalAssign (); /* Check which attribute was given */ switch (AttrTok) { case CFGTOK_VALUE: /* Don't allow this twice */ FlagAttr (&AttrFlags, atValue, "VALUE"); /* We expect a numeric expression */ Val = CfgIntExpr (); break; case CFGTOK_WEAK: /* Don't allow this twice */ FlagAttr (&AttrFlags, atWeak, "WEAK"); CfgBoolToken (); Weak = (CfgTok == CFGTOK_TRUE); CfgNextTok (); break; default: FAIL ("Unexpected attribute token"); } /* Semicolon ends the decl, otherwise accept an optional comma */ if (CfgTok == CFGTOK_SEMI) { break; } else if (CfgTok == CFGTOK_COMMA) { CfgNextTok (); } } /* Check if we have all mandatory attributes */ AttrCheck (AttrFlags, atValue, "VALUE"); /* Weak is optional, the default are non weak symbols */ if ((AttrFlags & atWeak) == 0) { Weak = 0; } } /* Check if the symbol is already defined */ if ((E = FindExport (Name)) != 0 && !IsUnresolvedExport (E)) { /* If the symbol is not marked as weak, this is an error. * Otherwise ignore the symbol from the config. */ if (!Weak) { CfgError ("Symbol `%s' is already defined", GetString (Name)); } } else { /* The symbol is undefined, generate an export */ CreateConstExport (Name, Val); } /* Skip the semicolon */ CfgConsumeSemi (); } /* Remember we had this section */ SectionsEncountered |= SE_SYMBOLS; }