/*****************************************************************************/ /* */ /* config.c */ /* */ /* Target configuration file for the ld65 linker */ /* */ /* */ /* */ /* (c) 1998-2013, Ullrich von Bassewitz */ /* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ /* With contributions from: */ /* */ /* - "David M. Lloyd" */ /* */ /* */ /* This software is provided 'as-is', without any expressed or implied */ /* warranty. In no event will the authors be held liable for any damages */ /* arising from the use of this software. */ /* */ /* Permission is granted to anyone to use this software for any purpose, */ /* including commercial applications, and to alter it and redistribute it */ /* freely, subject to the following restrictions: */ /* */ /* 1. The origin of this software must not be misrepresented; you must not */ /* claim that you wrote the original software. If you use this software */ /* in a product, an acknowledgment in the product documentation would be */ /* appreciated but is not required. */ /* 2. Altered source versions must be plainly marked as such, and must not */ /* be misrepresented as being the original software. */ /* 3. This notice may not be removed or altered from any source */ /* distribution. */ /* */ /*****************************************************************************/ #include #include #include #include /* common */ #include "addrsize.h" #include "bitops.h" #include "check.h" #include "print.h" #include "segdefs.h" #include "target.h" #include "xmalloc.h" #include "xsprintf.h" /* ld65 */ #include "alignment.h" #include "bin.h" #include "binfmt.h" #include "cfgexpr.h" #include "condes.h" #include "config.h" #include "error.h" #include "exports.h" #include "expr.h" #include "global.h" #include "memarea.h" #include "o65.h" #include "objdata.h" #include "scanner.h" #include "spool.h" #include "xex.h" /*****************************************************************************/ /* Data */ /*****************************************************************************/ /* Remember which sections we had encountered */ static enum { SE_NONE = 0x0000, SE_MEMORY = 0x0001, SE_SEGMENTS = 0x0002, SE_FEATURES = 0x0004, SE_FILES = 0x0008, SE_FORMATS = 0x0010, SE_SYMBOLS = 0x0020 } SectionsEncountered = SE_NONE; /* File list */ static Collection FileList = STATIC_COLLECTION_INITIALIZER; /* Memory list */ static Collection MemoryAreas = STATIC_COLLECTION_INITIALIZER; /* Memory attributes */ #define MA_START 0x0001 #define MA_SIZE 0x0002 #define MA_TYPE 0x0004 #define MA_FILE 0x0008 #define MA_DEFINE 0x0010 #define MA_FILL 0x0020 #define MA_FILLVAL 0x0040 #define MA_BANK 0x0080 /* Segment list */ static Collection SegDescList = STATIC_COLLECTION_INITIALIZER; /* Segment attributes */ #define SA_TYPE 0x0001 #define SA_LOAD 0x0002 #define SA_RUN 0x0004 #define SA_ALIGN 0x0008 #define SA_ALIGN_LOAD 0x0010 #define SA_DEFINE 0x0020 #define SA_OFFSET 0x0040 #define SA_START 0x0080 #define SA_OPTIONAL 0x0100 #define SA_FILLVAL 0x0200 /* Symbol types used in the CfgSymbol structure */ typedef enum { CfgSymExport, /* Not really used in struct CfgSymbol */ CfgSymImport, /* Dito */ CfgSymWeak, /* Like export but weak */ CfgSymO65Export, /* An o65 export */ CfgSymO65Import, /* An o65 import */ } CfgSymType; /* Symbol structure. It is used for o65 imports and exports, but also for ** symbols from the SYMBOLS sections (symbols defined in the config file or ** forced imports). */ typedef struct CfgSymbol CfgSymbol; struct CfgSymbol { CfgSymType Type; /* Type of symbol */ LineInfo* LI; /* Config file position */ unsigned Name; /* Symbol name */ ExprNode* Value; /* Symbol value if any */ unsigned AddrSize; /* Address size of symbol */ }; /* Collections with symbols */ static Collection CfgSymbols = STATIC_COLLECTION_INITIALIZER; /* Descriptor holding information about the binary formats */ static BinDesc* BinFmtDesc = 0; static O65Desc* O65FmtDesc = 0; static XexDesc* XexFmtDesc = 0; /*****************************************************************************/ /* Forwards */ /*****************************************************************************/ static File* NewFile (unsigned Name); /* Create a new file descriptor and insert it into the list */ /*****************************************************************************/ /* List management */ /*****************************************************************************/ static File* FindFile (unsigned Name) /* Find a file with a given name. */ { unsigned I; for (I = 0; I < CollCount (&FileList); ++I) { File* F = CollAtUnchecked (&FileList, I); if (F->Name == Name) { return F; } } return 0; } static File* GetFile (unsigned Name) /* Get a file entry with the given name. Create a new one if needed. */ { File* F = FindFile (Name); if (F == 0) { /* Create a new one */ F = NewFile (Name); } return F; } static void FileInsert (File* F, MemoryArea* M) /* Insert the memory area into the files list */ { M->F = F; CollAppend (&F->MemoryAreas, M); } static MemoryArea* CfgFindMemory (unsigned Name) /* Find the memory are with the given name. Return NULL if not found */ { unsigned I; for (I = 0; I < CollCount (&MemoryAreas); ++I) { MemoryArea* M = CollAtUnchecked (&MemoryAreas, I); if (M->Name == Name) { return M; } } return 0; } static MemoryArea* CfgGetMemory (unsigned Name) /* Find the memory are with the given name. Print an error on an invalid name */ { MemoryArea* M = CfgFindMemory (Name); if (M == 0) { CfgError (&CfgErrorPos, "Invalid memory area '%s'", GetString (Name)); } return M; } static SegDesc* CfgFindSegDesc (unsigned Name) /* Find the segment descriptor with the given name, return NULL if not found. */ { unsigned I; for (I = 0; I < CollCount (&SegDescList); ++I) { SegDesc* S = CollAtUnchecked (&SegDescList, I); if (S->Name == Name) { /* Found */ return S; } } /* Not found */ return 0; } static void MemoryInsert (MemoryArea* M, SegDesc* S) /* Insert the segment descriptor into the memory area list */ { /* Insert the segment into the segment list of the memory area */ CollAppend (&M->SegList, S); } /*****************************************************************************/ /* Constructors/Destructors */ /*****************************************************************************/ static CfgSymbol* NewCfgSymbol (CfgSymType Type, unsigned Name) /* Create a new CfgSymbol structure with the given type and name. The ** current config file position is recorded in the returned struct. The ** created struct is inserted into the CfgSymbols collection and returned. */ { /* Allocate memory */ CfgSymbol* Sym = xmalloc (sizeof (CfgSymbol)); /* Initialize the fields */ Sym->Type = Type; Sym->LI = GenLineInfo (&CfgErrorPos); Sym->Name = Name; Sym->Value = 0; Sym->AddrSize = ADDR_SIZE_INVALID; /* Insert the symbol into the collection */ CollAppend (&CfgSymbols, Sym); /* Return the initialized struct */ return Sym; } static File* NewFile (unsigned Name) /* Create a new file descriptor and insert it into the list */ { /* Allocate memory */ File* F = xmalloc (sizeof (File)); /* Initialize the fields */ F->Name = Name; F->Flags = 0; F->Format = BINFMT_DEFAULT; F->Size = 0; InitCollection (&F->MemoryAreas); /* Insert the struct into the list */ CollAppend (&FileList, F); /* ...and return it */ return F; } static MemoryArea* CreateMemoryArea (const FilePos* Pos, unsigned Name) /* Create a new memory area and insert it into the list */ { /* Check for duplicate names */ MemoryArea* M = CfgFindMemory (Name); if (M) { CfgError (&CfgErrorPos, "Memory area '%s' defined twice", GetString (Name)); } /* Create a new memory area */ M = NewMemoryArea (Pos, Name); /* Insert the struct into the list ... */ CollAppend (&MemoryAreas, M); /* ...and return it */ return M; } static SegDesc* NewSegDesc (unsigned Name) /* Create a segment descriptor and insert it into the list */ { /* Check for duplicate names */ SegDesc* S = CfgFindSegDesc (Name); if (S) { CfgError (&CfgErrorPos, "Segment '%s' defined twice", GetString (Name)); } /* Allocate memory */ S = xmalloc (sizeof (SegDesc)); /* Initialize the fields */ S->Name = Name; S->LI = GenLineInfo (&CfgErrorPos); S->Seg = 0; S->Attr = 0; S->Flags = 0; S->FillVal = 0; S->RunAlignment = 1; S->LoadAlignment = 1; /* Insert the struct into the list ... */ CollAppend (&SegDescList, S); /* ...and return it */ return S; } static void FreeSegDesc (SegDesc* S) /* Free a segment descriptor */ { FreeLineInfo (S->LI); xfree (S); } /*****************************************************************************/ /* Config file parsing */ /*****************************************************************************/ static void FlagAttr (unsigned* Flags, unsigned Mask, const char* Name) /* Check if the item is already defined. Print an error if so. If not, set ** the marker that we have a definition now. */ { if (*Flags & Mask) { CfgError (&CfgErrorPos, "%s is already defined", Name); } *Flags |= Mask; } static void AttrCheck (unsigned Attr, unsigned Mask, const char* Name) /* Check that a mandatory attribute was given */ { if ((Attr & Mask) == 0) { CfgError (&CfgErrorPos, "%s attribute is missing", Name); } } static void ParseMemory (void) /* Parse a MEMORY section */ { static const IdentTok Attributes [] = { { "BANK", CFGTOK_BANK }, { "DEFINE", CFGTOK_DEFINE }, { "FILE", CFGTOK_FILE }, { "FILL", CFGTOK_FILL }, { "FILLVAL", CFGTOK_FILLVAL }, { "SIZE", CFGTOK_SIZE }, { "START", CFGTOK_START }, { "TYPE", CFGTOK_TYPE }, }; static const IdentTok Types [] = { { "RO", CFGTOK_RO }, { "RW", CFGTOK_RW }, }; while (CfgTok == CFGTOK_IDENT) { /* Create a new entry on the heap */ MemoryArea* M = CreateMemoryArea (&CfgErrorPos, GetStrBufId (&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_BANK: FlagAttr (&M->Attr, MA_BANK, "BANK"); M->BankExpr = CfgExpr (); 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_FILE: FlagAttr (&M->Attr, MA_FILE, "FILE"); CfgAssureStr (); /* Get the file entry and insert the memory area */ FileInsert (GetFile (GetStrBufId (&CfgSVal)), M); 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) CfgCheckedConstExpr (0, 0xFF); break; case CFGTOK_SIZE: FlagAttr (&M->Attr, MA_SIZE, "SIZE"); M->SizeExpr = CfgExpr (); break; case CFGTOK_START: FlagAttr (&M->Attr, MA_START, "START"); M->StartExpr = CfgExpr (); 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; 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); OutputNameUsed = 1; } } /* Remember we had this section */ SectionsEncountered |= SE_MEMORY; } static void ParseFiles (void) /* Parse a FILES section */ { static const IdentTok Attributes [] = { { "FORMAT", CFGTOK_FORMAT }, }; static const IdentTok Formats [] = { { "ATARI", CFGTOK_ATARIEXE }, { "O65", CFGTOK_O65 }, { "BIN", CFGTOK_BIN }, { "BINARY", CFGTOK_BIN }, }; /* The MEMORY section must preceed the FILES section */ if ((SectionsEncountered & SE_MEMORY) == 0) { CfgError (&CfgErrorPos, "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 (GetStrBufId (&CfgSVal)); if (F == 0) { CfgError (&CfgErrorPos, "File '%s' not found in MEMORY section", SB_GetConstBuf (&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! */ CfgError (&CfgErrorPos, "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; case CFGTOK_ATARIEXE: F->Format = BINFMT_ATARIEXE; 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 ParseSegments (void) /* Parse a SEGMENTS section */ { static const IdentTok Attributes [] = { { "ALIGN", CFGTOK_ALIGN }, { "ALIGN_LOAD", CFGTOK_ALIGN_LOAD }, { "DEFINE", CFGTOK_DEFINE }, { "FILLVAL", CFGTOK_FILLVAL }, { "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 }, { "OVERWRITE", CFGTOK_OVERWRITE }, }; unsigned Count; /* The MEMORY section must preceed the SEGMENTS section */ if ((SectionsEncountered & SE_MEMORY) == 0) { CfgError (&CfgErrorPos, "MEMORY must precede SEGMENTS"); } while (CfgTok == CFGTOK_IDENT) { SegDesc* S; /* Create a new entry on the heap */ S = NewSegDesc (GetStrBufId (&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"); S->RunAlignment = (unsigned) CfgCheckedConstExpr (1, MAX_ALIGNMENT); S->Flags |= SF_ALIGN; break; case CFGTOK_ALIGN_LOAD: FlagAttr (&S->Attr, SA_ALIGN_LOAD, "ALIGN_LOAD"); S->LoadAlignment = (unsigned) CfgCheckedConstExpr (1, MAX_ALIGNMENT); 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_FILLVAL: FlagAttr (&S->Attr, SA_FILLVAL, "FILLVAL"); S->FillVal = (unsigned char) CfgCheckedConstExpr (0, 0xFF); S->Flags |= SF_FILLVAL; break; case CFGTOK_LOAD: FlagAttr (&S->Attr, SA_LOAD, "LOAD"); S->Load = CfgGetMemory (GetStrBufId (&CfgSVal)); CfgNextTok (); break; case CFGTOK_OFFSET: FlagAttr (&S->Attr, SA_OFFSET, "OFFSET"); S->Addr = CfgCheckedConstExpr (0, 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 (GetStrBufId (&CfgSVal)); CfgNextTok (); break; case CFGTOK_START: FlagAttr (&S->Attr, SA_START, "START"); S->Addr = CfgCheckedConstExpr (0, 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; case CFGTOK_OVERWRITE: S->Flags |= (SF_OVERWRITE | SF_RO); 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; } /* 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)) { CfgWarning (&CfgErrorPos, "ALIGN_LOAD attribute specified, but no separate " "LOAD and RUN memory areas assigned"); /* 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)) { CfgWarning (&CfgErrorPos, "Segment with type 'bss' has both LOAD and RUN " "memory areas assigned"); } /* 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 (&CfgErrorPos, "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 (&CfgErrorPos, "Only one of ALIGN, START, OFFSET may be used"); } /* Skip the semicolon */ CfgConsumeSemi (); } /* Remember we had this section */ SectionsEncountered |= SE_SEGMENTS; } 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 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 (); /* Remember it as an export for later */ NewCfgSymbol (CfgSymO65Export, GetStrBufId (&CfgSVal)); /* 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 (); /* Remember it as an import for later */ NewCfgSymbol (CfgSymO65Import, GetStrBufId (&CfgSVal)); /* 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 (&CfgErrorPos, "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 (&CfgErrorPos, "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) CfgCheckedConstExpr (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) CfgCheckedConstExpr (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 (&CfgErrorPos, "OS type CC65 may not have imports or exports for ids < $8000"); } } else { if (AttrFlags & atID) { CfgError (&CfgErrorPos, "Operating system does not support the ID attribute"); } } /* Set the O65 operating system to use */ O65SetOS (O65FmtDesc, OS, Version, ModuleId); } static void ParseXex (void) /* Parse the o65 format section */ { static const IdentTok Attributes [] = { { "RUNAD", CFGTOK_RUNAD }, { "INITAD", CFGTOK_INITAD }, }; /* Remember the attributes read */ /* Bitmask to remember the attributes we got already */ enum { atNone = 0x0000, atRunAd = 0x0001, }; unsigned AttrFlags = atNone; Import *RunAd = 0; Import *InitAd; MemoryArea *InitMem; /* 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_RUNAD: /* Cannot have this attribute twice */ FlagAttr (&AttrFlags, atRunAd, "RUNAD"); /* We expect an identifier */ CfgAssureIdent (); /* Generate an import for the symbol */ RunAd = InsertImport (GenImport (GetStrBufId (&CfgSVal), ADDR_SIZE_ABS)); /* Remember the file position */ CollAppend (&RunAd->RefLines, GenLineInfo (&CfgErrorPos)); /* Eat the identifier token */ CfgNextTok (); break; case CFGTOK_INITAD: /* We expect a memory area followed by a colon and an identifier */ CfgAssureIdent (); InitMem = CfgGetMemory (GetStrBufId (&CfgSVal)); CfgNextTok (); CfgConsumeColon (); CfgAssureIdent (); /* Generate an import for the symbol */ InitAd = InsertImport (GenImport (GetStrBufId (&CfgSVal), ADDR_SIZE_ABS)); /* Remember the file position */ CollAppend (&InitAd->RefLines, GenLineInfo (&CfgErrorPos)); /* Eat the identifier token */ CfgNextTok (); /* Add to XEX */ if (XexAddInitAd (XexFmtDesc, InitMem, InitAd)) CfgError (&CfgErrorPos, "INITAD already given for memory area"); break; default: FAIL ("Unexpected attribute token"); } /* Skip an optional comma */ CfgOptionalComma (); } /* Set the RUNAD import if we have one */ if ( RunAd ) XexSetRunAd (XexFmtDesc, RunAd); } static void ParseFormats (void) /* Parse a target format section */ { static const IdentTok Formats [] = { { "O65", CFGTOK_O65 }, { "BIN", CFGTOK_BIN }, { "BINARY", CFGTOK_BIN }, { "ATARI", CFGTOK_ATARIEXE }, }; 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_ATARIEXE: ParseXex (); 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 ParseConDes (void) /* Parse the CONDES feature */ { static const IdentTok Attributes [] = { { "COUNT", CFGTOK_COUNT }, { "IMPORT", CFGTOK_IMPORT }, { "LABEL", CFGTOK_LABEL }, { "ORDER", CFGTOK_ORDER }, { "SEGMENT", CFGTOK_SEGMENT }, { "TYPE", CFGTOK_TYPE }, }; 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 Count = INVALID_STRING_ID; unsigned Label = INVALID_STRING_ID; unsigned SegName = INVALID_STRING_ID; ConDesImport Import; /* Initialize to avoid gcc warnings: */ int Type = -1; ConDesOrder Order = cdIncreasing; /* Bitmask to remember the attributes we got already */ enum { atNone = 0x0000, atCount = 0x0001, atImport = 0x0002, atLabel = 0x0004, atOrder = 0x0008, atSegName = 0x0010, atType = 0x0020, }; 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_COUNT: /* Don't allow this twice */ FlagAttr (&AttrFlags, atCount, "COUNT"); /* We expect an identifier */ CfgAssureIdent (); /* Remember the value for later */ Count = GetStrBufId (&CfgSVal); break; case CFGTOK_IMPORT: /* Don't allow this twice */ FlagAttr (&AttrFlags, atImport, "IMPORT"); /* We expect an identifier */ CfgAssureIdent (); /* Remember value and position for later */ Import.Name = GetStrBufId (&CfgSVal); Import.Pos = CfgErrorPos; Import.AddrSize = ADDR_SIZE_ABS; break; case CFGTOK_LABEL: /* Don't allow this twice */ FlagAttr (&AttrFlags, atLabel, "LABEL"); /* We expect an identifier */ CfgAssureIdent (); /* Remember the value for later */ Label = GetStrBufId (&CfgSVal); 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; case CFGTOK_SEGMENT: /* Don't allow this twice */ FlagAttr (&AttrFlags, atSegName, "SEGMENT"); /* We expect an identifier */ CfgAssureIdent (); /* Remember the value for later */ SegName = GetStrBufId (&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; 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 (&CfgErrorPos, "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 & atImport) { ConDesSetImport (Type, &Import); } if (AttrFlags & atOrder) { ConDesSetOrder (Type, Order); } } 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 = CfgCheckedConstExpr (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 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; } static void ParseSymbols (void) /* Parse a symbols section */ { static const IdentTok Attributes[] = { { "ADDRSIZE", CFGTOK_ADDRSIZE }, { "TYPE", CFGTOK_TYPE }, { "VALUE", CFGTOK_VALUE }, }; static const IdentTok AddrSizes [] = { { "ABS", CFGTOK_ABS }, { "ABSOLUTE", CFGTOK_ABS }, { "DIRECT", CFGTOK_ZP }, { "DWORD", CFGTOK_LONG }, { "FAR", CFGTOK_FAR }, { "LONG", CFGTOK_LONG }, { "NEAR", CFGTOK_ABS }, { "ZEROPAGE", CFGTOK_ZP }, { "ZP", CFGTOK_ZP }, }; static const IdentTok Types [] = { { "EXPORT", CFGTOK_EXPORT }, { "IMPORT", CFGTOK_IMPORT }, { "WEAK", CFGTOK_WEAK }, }; while (CfgTok == CFGTOK_IDENT) { /* Bitmask to remember the attributes we got already */ enum { atNone = 0x0000, atAddrSize = 0x0001, atType = 0x0002, atValue = 0x0004, }; unsigned AttrFlags = atNone; ExprNode* Value = 0; CfgSymType Type = CfgSymExport; unsigned char AddrSize = ADDR_SIZE_ABS; Import* Imp; Export* Exp; CfgSymbol* Sym; /* Remember the name */ unsigned Name = GetStrBufId (&CfgSVal); CfgNextTok (); /* 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_ADDRSIZE: /* Don't allow this twice */ FlagAttr (&AttrFlags, atAddrSize, "ADDRSIZE"); /* Map the type to a token */ CfgSpecialToken (AddrSizes, ENTRY_COUNT (AddrSizes), "AddrSize"); switch (CfgTok) { case CFGTOK_ABS: AddrSize = ADDR_SIZE_ABS; break; case CFGTOK_FAR: AddrSize = ADDR_SIZE_FAR; break; case CFGTOK_LONG: AddrSize = ADDR_SIZE_LONG; break; case CFGTOK_ZP: AddrSize = ADDR_SIZE_ZP; break; default: Internal ("Unexpected token: %d", CfgTok); } CfgNextTok (); break; case CFGTOK_TYPE: /* Don't allow this twice */ FlagAttr (&AttrFlags, atType, "TYPE"); /* Map the type to a token */ CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type"); switch (CfgTok) { case CFGTOK_EXPORT: Type = CfgSymExport; break; case CFGTOK_IMPORT: Type = CfgSymImport; break; case CFGTOK_WEAK: Type = CfgSymWeak; break; default: Internal ("Unexpected token: %d", CfgTok); } CfgNextTok (); break; case CFGTOK_VALUE: /* Don't allow this twice */ FlagAttr (&AttrFlags, atValue, "VALUE"); /* Value is an expression */ Value = CfgExpr (); 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 (); } } /* We must have a type */ AttrCheck (AttrFlags, atType, "TYPE"); /* Further actions depend on the type */ switch (Type) { case CfgSymExport: /* We must have a value */ AttrCheck (AttrFlags, atValue, "VALUE"); /* Create the export */ Exp = CreateExprExport (Name, Value, AddrSize); CollAppend (&Exp->DefLines, GenLineInfo (&CfgErrorPos)); break; case CfgSymImport: /* An import must not have a value */ if (AttrFlags & atValue) { CfgError (&CfgErrorPos, "Imports must not have a value"); } /* Generate the import */ Imp = InsertImport (GenImport (Name, AddrSize)); /* Remember the file position */ CollAppend (&Imp->RefLines, GenLineInfo (&CfgErrorPos)); break; case CfgSymWeak: /* We must have a value */ AttrCheck (AttrFlags, atValue, "VALUE"); /* Remember the symbol for later */ Sym = NewCfgSymbol (CfgSymWeak, Name); Sym->Value = Value; Sym->AddrSize = AddrSize; break; default: Internal ("Unexpected symbol type %d", Type); } /* Skip the semicolon */ CfgConsumeSemi (); } /* Remember we had this section */ SectionsEncountered |= SE_SYMBOLS; } 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); } void CfgRead (void) /* Read the configuration */ { /* Create the descriptors for the binary formats */ BinFmtDesc = NewBinDesc (); O65FmtDesc = NewO65Desc (); XexFmtDesc = NewXexDesc (); /* If we have a config name given, open the file, otherwise we will read ** from a buffer. */ CfgOpenInput (); /* Parse the file */ ParseConfig (); /* Close the input file */ CfgCloseInput (); } /*****************************************************************************/ /* Config file processing */ /*****************************************************************************/ static void ProcessSegments (void) /* Process the SEGMENTS section */ { unsigned I; /* Walk over the list of segment descriptors */ I = 0; while (I < CollCount (&SegDescList)) { /* Get the next segment descriptor */ SegDesc* S = CollAtUnchecked (&SegDescList, I); /* Search for the actual segment in the input files. The function may ** return NULL (no such segment), this is checked later. */ S->Seg = SegFind (S->Name); /* 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)) { CfgWarning (GetSourcePos (S->LI), "Segment '%s' with type 'bss' contains initialized data", GetString (S->Name)); } /* If this segment does exist in any of the object files, insert the ** segment into the load/run memory areas. Otherwise print a warning ** and discard it, because the segment pointer in the descriptor is ** invalid. */ if (S->Seg != 0) { /* 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); } /* Use the fill value from the config */ S->Seg->FillVal = S->FillVal; /* Process the next segment descriptor in the next run */ ++I; } else { /* Print a warning if the segment is not optional */ if ((S->Flags & SF_OPTIONAL) == 0) { CfgWarning (&CfgErrorPos, "Segment '%s' does not exist", GetString (S->Name)); } /* Discard the descriptor and remove it from the collection */ FreeSegDesc (S); CollDelete (&SegDescList, I); } } } static void ProcessSymbols (void) /* Process the SYMBOLS section */ { Export* E; /* Walk over all symbols */ unsigned I; for (I = 0; I < CollCount (&CfgSymbols); ++I) { /* Get the next symbol */ CfgSymbol* Sym = CollAtUnchecked (&CfgSymbols, I); /* Check what it is. */ switch (Sym->Type) { case CfgSymO65Export: /* Check if the export symbol is also defined as an import. */ if (O65GetImport (O65FmtDesc, Sym->Name) != 0) { CfgError ( GetSourcePos (Sym->LI), "Exported o65 symbol '%s' cannot also be an o65 import", GetString (Sym->Name) ); } /* 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, Sym->Name) != 0) { CfgError ( GetSourcePos (Sym->LI), "Duplicate exported o65 symbol: '%s'", GetString (Sym->Name) ); } /* Insert the symbol into the table */ O65SetExport (O65FmtDesc, Sym->Name); break; case CfgSymO65Import: /* Check if the import symbol is also defined as an export. */ if (O65GetExport (O65FmtDesc, Sym->Name) != 0) { CfgError ( GetSourcePos (Sym->LI), "Imported o65 symbol '%s' cannot also be an o65 export", GetString (Sym->Name) ); } /* 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, Sym->Name) != 0) { CfgError ( GetSourcePos (Sym->LI), "Duplicate imported o65 symbol: '%s'", GetString (Sym->Name) ); } /* Insert the symbol into the table */ O65SetImport (O65FmtDesc, Sym->Name); break; case CfgSymWeak: /* If the symbol is not defined until now, define it */ if ((E = FindExport (Sym->Name)) == 0 || IsUnresolvedExport (E)) { /* The symbol is undefined, generate an export */ E = CreateExprExport (Sym->Name, Sym->Value, Sym->AddrSize); CollAppend (&E->DefLines, Sym->LI); } break; default: Internal ("Unexpected symbol type %d", Sym->Type); break; } } } static void CreateRunDefines (SegDesc* S, unsigned long SegAddr) /* Create the defines for a RUN segment */ { Export* E; StrBuf Buf = STATIC_STRBUF_INITIALIZER; /* Define the run address of the segment */ SB_Printf (&Buf, "__%s_RUN__", GetString (S->Name)); E = CreateMemoryExport (GetStrBufId (&Buf), S->Run, SegAddr - S->Run->Start); CollAppend (&E->DefLines, S->LI); /* Define the size of the segment */ SB_Printf (&Buf, "__%s_SIZE__", GetString (S->Name)); E = CreateConstExport (GetStrBufId (&Buf), S->Seg->Size); CollAppend (&E->DefLines, S->LI); S->Flags |= SF_RUN_DEF; SB_Done (&Buf); } static void CreateLoadDefines (SegDesc* S, unsigned long SegAddr) /* Create the defines for a LOAD segment */ { Export* E; StrBuf Buf = STATIC_STRBUF_INITIALIZER; /* Define the load address of the segment */ SB_Printf (&Buf, "__%s_LOAD__", GetString (S->Name)); E = CreateMemoryExport (GetStrBufId (&Buf), S->Load, SegAddr - S->Load->Start); CollAppend (&E->DefLines, S->LI); S->Flags |= SF_LOAD_DEF; SB_Done (&Buf); } unsigned CfgProcess (void) /* Process the config file, after reading in object files and libraries. This ** includes postprocessing of the config file data; but also assigning segments, ** and defining segment/memory-area related symbols. The function will return ** the number of memory area overflows (so, zero means everything went OK). ** In case of overflows, a short mapfile can be generated later, to ease the ** user's task of re-arranging segments. */ { unsigned Overflows = 0; unsigned I; /* Postprocess symbols. We must do that first, since weak symbols are ** defined here, which may be needed later. */ ProcessSymbols (); /* Postprocess segments */ ProcessSegments (); /* Walk through each of the memory sections. Add up the sizes; and, check ** for an overflow of the section. Assign the start addresses of the ** segments while doing that. */ for (I = 0; I < CollCount (&MemoryAreas); ++I) { unsigned J; unsigned long Addr; unsigned Overwrites = 0; /* Get the next memory area */ MemoryArea* M = CollAtUnchecked (&MemoryAreas, I); /* Remember the offset in the output file */ M->FileOffs = M->F->Size; /* Remember if this is a relocatable memory area */ M->Relocatable = RelocatableBinFmt (M->F->Format); /* Resolve the start address expression, remember the start address, ** and mark the memory area as placed. */ if (!IsConstExpr (M->StartExpr)) { CfgError (GetSourcePos (M->LI), "Start address of memory area '%s' is not constant", GetString (M->Name)); } Addr = M->Start = GetExprVal (M->StartExpr); M->Flags |= MF_PLACED; /* If requested, define the symbol for the start of the memory area. ** Doing it here means that the expression for the size of the area ** may reference this symbol. */ if (M->Flags & MF_DEFINE) { Export* E; StrBuf Buf = STATIC_STRBUF_INITIALIZER; /* Define the start of the memory area */ SB_Printf (&Buf, "__%s_START__", GetString (M->Name)); E = CreateMemoryExport (GetStrBufId (&Buf), M, 0); CollAppend (&E->DefLines, M->LI); SB_Done (&Buf); } /* Resolve the size expression */ if (!IsConstExpr (M->SizeExpr)) { CfgError (GetSourcePos (M->LI), "Size of memory area '%s' is not constant", GetString (M->Name)); } M->Size = GetExprVal (M->SizeExpr); if (M->Size >= 0x80000000) { CfgError (GetSourcePos (M->LI), "Size of memory area '%s' is negative: %ld", GetString (M->Name), (long)M->Size); } /* Walk through the segments in this memory area */ for (J = 0; J < CollCount (&M->SegList); ++J) { /* Get the segment */ SegDesc* S = CollAtUnchecked (&M->SegList, J); /* Remember the start address before handling this segment */ unsigned long StartAddr = Addr; /* For computing FillLevel */ unsigned long FillLevel; unsigned long FillAdded = 0; /* Take note of "overwrite" segments and make sure there are no ** other segment types following them in current memory region. */ if (S->Flags & SF_OVERWRITE) { if (S->Flags & (SF_OFFSET | SF_START)) { ++Overwrites; } else { CfgError (GetSourcePos (M->LI), "Segment '%s' of type 'overwrite' requires either" " 'Start' or 'Offset' attribute to be specified", GetString (S->Name)); } } else { if (Overwrites > 0) { CfgError (GetSourcePos (M->LI), "Segment '%s' is preceded by at least one segment" " of type 'overwrite'", GetString (S->Name)); } } /* Some actions depend on whether this is the load or run memory ** area. */ if (S->Run == M) { /* This is the run (and maybe load) memory area. Handle ** alignment and explict start address and offset. */ /* Check if the alignment for the segment from the linker ** config is a multiple for that of the segment. ** If START or OFFSET is provided instead of ALIGN, check ** if its address fits alignment requirements. */ unsigned long AlignedBy = (S->Flags & SF_START) ? S->Addr : (S->Flags & SF_OFFSET) ? (S->Addr + M->Start) : S->RunAlignment; if ((AlignedBy % S->Seg->Alignment) != 0) { /* Segment requires another alignment than configured ** in the linker. */ CfgWarning (GetSourcePos (S->LI), "Segment '%s' isn't aligned properly; the" " resulting executable might not be functional.", GetString (S->Name)); } if (S->Flags & SF_ALIGN) { /* Align the address */ unsigned long NewAddr = AlignAddr (Addr, S->RunAlignment); /* If the first segment placed in the memory area needs ** fill bytes for the alignment, emit a warning, since ** that is somewhat suspicious. */ if (M->FillLevel == 0 && NewAddr > Addr) { CfgWarning (GetSourcePos (S->LI), "The first segment in memory area '%s' " "needs fill bytes for alignment.", GetString (M->Name)); } /* Use the aligned address */ Addr = NewAddr; } else if ((S->Flags & (SF_OFFSET | SF_START)) != 0 && (M->Flags & MF_OVERFLOW) == 0) { /* Give the segment a fixed starting address */ unsigned long NewAddr = S->Addr; if (S->Flags & SF_OFFSET) { /* An offset was given, no address, make an address */ NewAddr += M->Start; } if (S->Flags & SF_OVERWRITE) { if (NewAddr < M->Start) { CfgError (GetSourcePos (S->LI), "Segment '%s' begins before memory area '%s'", GetString (S->Name), GetString (M->Name)); } else { Addr = NewAddr; } } else { if (NewAddr < Addr) { /* Offset already too large */ ++Overflows; if (S->Flags & SF_OFFSET) { CfgWarning (GetSourcePos (S->LI), "Segment '%s' offset is too small in '%s' by %lu byte%s", GetString (S->Name), GetString (M->Name), Addr - NewAddr, (Addr - NewAddr == 1) ? "" : "s"); } else { CfgWarning (GetSourcePos (S->LI), "Segment '%s' start address is too low in '%s' by %lu byte%s", GetString (S->Name), GetString (M->Name), Addr - NewAddr, (Addr - NewAddr == 1) ? "" : "s"); } } else { Addr = NewAddr; } } } /* Set the start address of this segment, set the readonly flag ** in the segment, and remember if the segment is in a ** relocatable file or not. */ S->Seg->PC = Addr; S->Seg->ReadOnly = (S->Flags & SF_RO) != 0; /* Remember the run memory for this segment, which is also a ** flag that the segment has been placed. */ S->Seg->MemArea = M; } else if (S->Load == M) { /* This is the load memory area; *and*, run and load are ** different (because of the "else" above). Handle alignment. */ if (S->Flags & SF_ALIGN_LOAD) { /* Align the address */ Addr = AlignAddr (Addr, S->LoadAlignment); } } /* If this is the load memory area, and the segment doesn't have a ** fill value defined, use the one from the memory area. */ if (S->Load == M && (S->Flags & SF_FILLVAL) == 0) { S->Seg->FillVal = M->FillVal; } /* Increment the fill level of the memory area; and, check for an ** overflow. */ FillLevel = Addr + S->Seg->Size - M->Start; if (FillLevel > M->Size && (M->Flags & MF_OVERFLOW) == 0) { ++Overflows; M->Flags |= MF_OVERFLOW; CfgWarning (GetSourcePos (M->LI), "Segment '%s' overflows memory area '%s' by %lu byte%s", GetString (S->Name), GetString (M->Name), FillLevel - M->Size, (FillLevel - M->Size == 1) ? "" : "s"); } if (FillLevel > M->FillLevel) { /* Regular segments increase FillLevel. Overwrite segments may increase but not decrease FillLevel. */ FillAdded = FillLevel - M->FillLevel; M->FillLevel = FillLevel; } /* If requested, define symbols for the start and size of the ** segment. */ if (S->Flags & SF_DEFINE) { if (S->Run == M && (S->Flags & SF_RUN_DEF) == 0) { CreateRunDefines (S, Addr); } if (S->Load == M && (S->Flags & SF_LOAD_DEF) == 0) { CreateLoadDefines (S, Addr); } } /* Calculate the new address */ Addr += S->Seg->Size; /* If this segment will go out to the file, or its place ** in the file will be filled, then increase the file size. ** An OVERWRITE segment will only increase the size if it overlapped some of the fill area. */ if (S->Load == M && ((S->Flags & SF_BSS) == 0 || (M->Flags & MF_FILL) != 0)) { M->F->Size += (!(S->Flags & SF_OVERWRITE)) ? (Addr - StartAddr) : FillAdded; } } /* If requested, define symbols for start, size, and offset of the ** memory area */ if (M->Flags & MF_DEFINE) { Export* E; StrBuf Buf = STATIC_STRBUF_INITIALIZER; /* Define the size of the memory area */ SB_Printf (&Buf, "__%s_SIZE__", GetString (M->Name)); E = CreateConstExport (GetStrBufId (&Buf), M->Size); CollAppend (&E->DefLines, M->LI); /* Define the fill level of the memory area */ SB_Printf (&Buf, "__%s_LAST__", GetString (M->Name)); E = CreateMemoryExport (GetStrBufId (&Buf), M, M->FillLevel); CollAppend (&E->DefLines, M->LI); /* Define the file offset of the memory area. This isn't of much ** use for relocatable output files. */ if (!M->Relocatable) { SB_Printf (&Buf, "__%s_FILEOFFS__", GetString (M->Name)); E = CreateConstExport (GetStrBufId (&Buf), M->FileOffs); CollAppend (&E->DefLines, M->LI); } /* Throw away the string buffer */ SB_Done (&Buf); } /* If we didn't have an overflow, and are requested to fill the memory ** area, account for that in the file size. */ if ((M->Flags & MF_OVERFLOW) == 0 && (M->Flags & MF_FILL) != 0) { M->F->Size += (M->Size - M->FillLevel); } } /* Return the number of memory area overflows */ return Overflows; } void CfgWriteTarget (void) /* Write the target file(s) */ { unsigned I; /* Walk through the files list */ for (I = 0; I < CollCount (&FileList); ++I) { /* Get this entry */ File* F = CollAtUnchecked (&FileList, I); /* We don't need to look at files with no memory areas */ if (CollCount (&F->MemoryAreas) > 0) { /* Is there an output file? */ if (SB_GetLen (GetStrBuf (F->Name)) > 0) { /* Assign a proper binary format */ if (F->Format == BINFMT_DEFAULT) { F->Format = DefaultBinFmt; } /* Call the apropriate routine for the binary format */ switch (F->Format) { case BINFMT_BINARY: BinWriteTarget (BinFmtDesc, F); break; case BINFMT_O65: O65WriteTarget (O65FmtDesc, F); break; case BINFMT_ATARIEXE: XexWriteTarget (XexFmtDesc, F); break; default: Internal ("Invalid binary format: %u", F->Format); } } else { /* No output file. Walk through the list and mark all segments ** loading into these memory areas in this file as dumped. */ unsigned J; for (J = 0; J < CollCount (&F->MemoryAreas); ++J) { unsigned K; /* Get this entry */ MemoryArea* M = CollAtUnchecked (&F->MemoryAreas, J); /* Debugging */ Print (stdout, 2, "Skipping '%s'...\n", GetString (M->Name)); /* Walk throught the segments */ for (K = 0; K < CollCount (&M->SegList); ++K) { SegDesc* S = CollAtUnchecked (&M->SegList, K); if (S->Load == M) { /* Load area - mark the segment as dumped */ S->Seg->Dumped = 1; } } } } } } }