1
0
mirror of https://github.com/cc65/cc65.git synced 2025-03-04 00:30:35 +00:00
cc65/src/ld65/config.c
uz 77bd3169f6 Added a new attribute IMPORT to the CONDES definition of the FEATURES section.
If this attribute is defined, an import for the given symbol is added to the
module that contains the condes declaraction. Using this feature, it is
possible to force linkage of a module that contains an export for the symbol.


git-svn-id: svn://svn.cc65.org/cc65/trunk@5900 b7a2c559-68d2-44c3-8de9-860c34a00d81
2012-11-03 21:37:19 +00:00

2082 lines
61 KiB
C

/*****************************************************************************/
/* */
/* config.c */
/* */
/* Target configuration file for the ld65 linker */
/* */
/* */
/* */
/* (C) 1998-2012, Ullrich von Bassewitz */
/* Roemerstrasse 52 */
/* D-70794 Filderstadt */
/* EMail: uz@cc65.org */
/* */
/* With contributions from: */
/* */
/* - "David M. Lloyd" <david.lloyd@redhat.com> */
/* */
/* */
/* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
/* common */
#include "addrsize.h"
#include "bitops.h"
#include "check.h"
#include "print.h"
#include "segdefs.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"
/*****************************************************************************/
/* 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;
/*****************************************************************************/
/* 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 [] = {
{ "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;
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 },
};
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 (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 (GetStrBufId (&CfgSVal));
CfgNextTok ();
break;
case CFGTOK_START:
FlagAttr (&S->Attr, SA_START, "START");
S->Addr = CfgCheckedConstExpr (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;
}
/* 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 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 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 ();
/* 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 anything went ok).
* In case of overflows, a short mapfile can be generated later, to ease the
* task of rearranging segments for the user.
*/
{
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 this.
*/
for (I = 0; I < CollCount (&MemoryAreas); ++I) {
unsigned J;
unsigned long Addr;
/* 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);
/* 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;
/* Some actions depend on wether 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.
*/
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
* this is somewhat suspicious.
*/
if (M->FillLevel == 0 && NewAddr > Addr) {
CfgWarning (GetSourcePos (S->LI),
"First segment in memory area `%s' does "
"already need fill bytes for alignment",
GetString (M->Name));
}
/* Use the aligned address */
Addr = NewAddr;
} else if (S->Flags & (SF_OFFSET | SF_START)) {
/* 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 (Addr > NewAddr) {
/* Offset already too large */
if (S->Flags & SF_OFFSET) {
CfgError (GetSourcePos (M->LI),
"Offset too small in `%s', segment `%s'",
GetString (M->Name),
GetString (S->Name));
} else {
CfgError (GetSourcePos (M->LI),
"Start address too low in `%s', segment `%s'",
GetString (M->Name),
GetString (S->Name));
}
}
Addr = NewAddr;
}
/* Set the start address of this segment, set the readonly flag
* in the segment and 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.
*/
M->FillLevel = Addr + S->Seg->Size - M->Start;
if (M->FillLevel > M->Size && (M->Flags & MF_OVERFLOW) == 0) {
++Overflows;
M->Flags |= MF_OVERFLOW;
CfgWarning (GetSourcePos (M->LI),
"Memory area overflow in `%s', segment `%s' (%lu bytes)",
GetString (M->Name), GetString (S->Name),
M->FillLevel - M->Size);
}
/* 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 goes out to the file, increase the file size */
if ((S->Flags & SF_BSS) == 0 && S->Load == M) {
M->F->Size += Addr - StartAddr;
}
}
/* 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, acount 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;
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;
}
}
}
}
}
}
}