/* */
/* */
/* */
-/* (C) 1998-2010, Ullrich von Bassewitz */
+/* (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 */
#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"
#define MA_DEFINE 0x0010
#define MA_FILL 0x0020
#define MA_FILLVAL 0x0040
+#define MA_BANK 0x0080
/* Segment list */
static Collection SegDescList = STATIC_COLLECTION_INITIALIZER;
typedef struct CfgSymbol CfgSymbol;
struct CfgSymbol {
CfgSymType Type; /* Type of symbol */
- FilePos Pos; /* Config file position */
+ LineInfo* LI; /* Config file position */
unsigned Name; /* Symbol name */
ExprNode* Value; /* Symbol value if any */
unsigned AddrSize; /* Address size of symbol */
/* Initialize the fields */
Sym->Type = Type;
- Sym->Pos = CfgErrorPos;
+ Sym->LI = GenLineInfo (&CfgErrorPos);
Sym->Name = Name;
Sym->Value = 0;
Sym->AddrSize = ADDR_SIZE_INVALID;
F->Name = Name;
F->Flags = 0;
F->Format = BINFMT_DEFAULT;
+ F->Size = 0;
InitCollection (&F->MemoryAreas);
/* Insert the struct into the list */
S = xmalloc (sizeof (SegDesc));
/* Initialize the fields */
- S->Name = Name;
- S->Pos = CfgErrorPos;
- S->Seg = 0;
- S->Attr = 0;
- S->Flags = 0;
- S->Align = 0;
+ S->Name = Name;
+ S->LI = GenLineInfo (&CfgErrorPos);
+ S->Seg = 0;
+ S->Attr = 0;
+ S->Flags = 0;
+ S->RunAlignment = 1;
+ S->LoadAlignment = 1;
/* Insert the struct into the list ... */
CollAppend (&SegDescList, S);
static void FreeSegDesc (SegDesc* S)
/* Free a segment descriptor */
{
+ FreeLineInfo (S->LI);
xfree (S);
}
/* Parse a MEMORY section */
{
static const IdentTok Attributes [] = {
- { "START", CFGTOK_START },
- { "SIZE", CFGTOK_SIZE },
- { "TYPE", CFGTOK_TYPE },
- { "FILE", CFGTOK_FILE },
+ { "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 },
/* Check which attribute was given */
switch (AttrTok) {
- case CFGTOK_START:
- FlagAttr (&M->Attr, MA_START, "START");
- M->StartExpr = CfgExpr ();
- break;
-
- case CFGTOK_SIZE:
- FlagAttr (&M->Attr, MA_SIZE, "SIZE");
- M->SizeExpr = CfgExpr ();
+ case CFGTOK_BANK:
+ FlagAttr (&M->Attr, MA_BANK, "BANK");
+ M->BankExpr = 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;
+ 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;
CfgNextTok ();
break;
- case CFGTOK_DEFINE:
- FlagAttr (&M->Attr, MA_DEFINE, "DEFINE");
- /* Map the token to a boolean */
- CfgBoolToken ();
- if (CfgTok == CFGTOK_TRUE) {
- M->Flags |= MF_DEFINE;
- }
- CfgNextTok ();
- break;
-
case CFGTOK_FILL:
FlagAttr (&M->Attr, MA_FILL, "FILL");
/* Map the token to a boolean */
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");
};
unsigned Count;
- long Val;
/* The MEMORY section must preceed the SEGMENTS section */
if ((SectionsEncountered & SE_MEMORY) == 0) {
case CFGTOK_ALIGN:
FlagAttr (&S->Attr, SA_ALIGN, "ALIGN");
- Val = CfgCheckedConstExpr (1, 0x10000);
- S->Align = BitFind (Val);
- if ((0x01L << S->Align) != Val) {
- CfgError (&CfgErrorPos, "Alignment must be a power of 2");
- }
+ S->RunAlignment = (unsigned) CfgCheckedConstExpr (1, MAX_ALIGNMENT);
S->Flags |= SF_ALIGN;
break;
case CFGTOK_ALIGN_LOAD:
FlagAttr (&S->Attr, SA_ALIGN_LOAD, "ALIGN_LOAD");
- Val = CfgCheckedConstExpr (1, 0x10000);
- S->AlignLoad = BitFind (Val);
- if ((0x01L << S->AlignLoad) != Val) {
- CfgError (&CfgErrorPos, "Alignment must be a power of 2");
- }
+ S->LoadAlignment = (unsigned) CfgCheckedConstExpr (1, MAX_ALIGNMENT);
S->Flags |= SF_ALIGN_LOAD;
break;
* separate run and load memory areas.
*/
if ((S->Flags & SF_ALIGN_LOAD) != 0 && (S->Load == S->Run)) {
- Warning ("%s(%lu): ALIGN_LOAD attribute specified, but no separate "
- "LOAD and RUN memory areas assigned",
- CfgGetName (), CfgErrorPos.Line);
+ CfgWarning (&CfgErrorPos,
+ "ALIGN_LOAD attribute specified, but no separate "
+ "LOAD and RUN memory areas assigned");
/* Remove the flag */
S->Flags &= ~SF_ALIGN_LOAD;
}
* load and run memory areas, because it's is never written to disk.
*/
if ((S->Flags & SF_BSS) != 0 && (S->Load != S->Run)) {
- Warning ("%s(%lu): Segment with type `bss' has both LOAD and RUN "
- "memory areas assigned", CfgGetName (), CfgErrorPos.Line);
+ 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 */
case CfgSymExport:
/* We must have a value */
- AttrCheck (AttrFlags, atType, "TYPE");
+ AttrCheck (AttrFlags, atValue, "VALUE");
/* Create the export */
Exp = CreateExprExport (Name, Value, AddrSize);
- Exp->Pos = CfgErrorPos;
+ CollAppend (&Exp->DefLines, GenLineInfo (&CfgErrorPos));
break;
case CfgSymImport:
/* Generate the import */
Imp = InsertImport (GenImport (Name, AddrSize));
/* Remember the file position */
- Imp->Pos = CfgErrorPos;
+ CollAppend (&Imp->RefLines, GenLineInfo (&CfgErrorPos));
break;
case CfgSymWeak:
/* We must have a value */
- AttrCheck (AttrFlags, atType, "TYPE");
+ AttrCheck (AttrFlags, atValue, "VALUE");
/* Remember the symbol for later */
Sym = NewCfgSymbol (CfgSymWeak, Name);
Sym->Value = Value;
* in the segment.
*/
if ((S->Flags & SF_BSS) != 0 && S->Seg != 0 && !IsBSSType (S->Seg)) {
- Warning ("Segment `%s' with type `bss' contains initialized data",
- GetString (S->Name));
+ 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
/* Check if the export symbol is also defined as an import. */
if (O65GetImport (O65FmtDesc, Sym->Name) != 0) {
CfgError (
- &Sym->Pos,
+ GetSourcePos (Sym->LI),
"Exported o65 symbol `%s' cannot also be an o65 import",
GetString (Sym->Name)
);
*/
if (O65GetExport (O65FmtDesc, Sym->Name) != 0) {
CfgError (
- &Sym->Pos,
+ GetSourcePos (Sym->LI),
"Duplicate exported o65 symbol: `%s'",
GetString (Sym->Name)
);
/* Check if the import symbol is also defined as an export. */
if (O65GetExport (O65FmtDesc, Sym->Name) != 0) {
CfgError (
- &Sym->Pos,
+ GetSourcePos (Sym->LI),
"Imported o65 symbol `%s' cannot also be an o65 export",
GetString (Sym->Name)
);
*/
if (O65GetImport (O65FmtDesc, Sym->Name) != 0) {
CfgError (
- &Sym->Pos,
+ GetSourcePos (Sym->LI),
"Duplicate imported o65 symbol: `%s'",
GetString (Sym->Name)
);
if ((E = FindExport (Sym->Name)) == 0 || IsUnresolvedExport (E)) {
/* The symbol is undefined, generate an export */
E = CreateExprExport (Sym->Name, Sym->Value, Sym->AddrSize);
- E->Pos = Sym->Pos;
+ CollAppend (&E->DefLines, Sym->LI);
}
break;
/* 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);
- E->Pos = S->Pos;
+ 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);
- E->Pos = S->Pos;
+ CollAppend (&E->DefLines, S->LI);
S->Flags |= SF_RUN_DEF;
SB_Done (&Buf);
/* 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);
- E->Pos = S->Pos;
+ CollAppend (&E->DefLines, S->LI);
S->Flags |= SF_LOAD_DEF;
SB_Done (&Buf);
/* 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 */
+ /* Resolve the start address expression, remember the start address
+ * and mark the memory area as placed.
+ */
if (!IsConstExpr (M->StartExpr)) {
- CfgError (&M->Pos,
+ CfgError (GetSourcePos (M->LI),
"Start address of memory area `%s' is not constant",
GetString (M->Name));
}
- M->Start = GetExprVal (M->StartExpr);
+ 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 (&M->Pos,
+ CfgError (GetSourcePos (M->LI),
"Size of memory area `%s' is not constant",
GetString (M->Name));
}
M->Size = GetExprVal (M->SizeExpr);
- /* Mark the memory area as placed */
- M->Flags |= MF_PLACED;
-
- /* Get the start address of this memory area */
- Addr = M->Start;
-
/* 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->Flags & SF_ALIGN) {
/* Align the address */
- unsigned long Val = (0x01UL << S->Align) - 1;
- Addr = (Addr + Val) & ~Val;
+ 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 (Addr > NewAddr) {
/* Offset already too large */
if (S->Flags & SF_OFFSET) {
- CfgError (&M->Pos,
+ CfgError (GetSourcePos (M->LI),
"Offset too small in `%s', segment `%s'",
GetString (M->Name),
GetString (S->Name));
} else {
- CfgError (&M->Pos,
+ CfgError (GetSourcePos (M->LI),
"Start address too low in `%s', segment `%s'",
GetString (M->Name),
GetString (S->Name));
Addr = NewAddr;
}
+ /* If the segment has .BANK expressions referring to it, it
+ * must be placed into a memory area that has the bank
+ * attribute.
+ */
+ if ((S->Seg->Flags & SEG_FLAG_BANKREF) != 0 && M->BankExpr == 0) {
+ CfgError (GetSourcePos (S->LI),
+ "Segment `%s' is refered to by .BANK, but the "
+ "memory area `%s' it is placed into has no BANK "
+ "attribute",
+ GetString (S->Name),
+ GetString (M->Name));
+ }
+
/* 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;
- S->Seg->Relocatable = M->Relocatable;
- /* Remember that this segment is placed */
- S->Seg->Placed = 1;
+ /* 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) {
*/
if (S->Flags & SF_ALIGN_LOAD) {
/* Align the address */
- unsigned long Val = (0x01UL << S->AlignLoad) - 1;
- Addr = (Addr + Val) & ~Val;
+ Addr = AlignAddr (Addr, S->LoadAlignment);
}
}
if (M->FillLevel > M->Size && (M->Flags & MF_OVERFLOW) == 0) {
++Overflows;
M->Flags |= MF_OVERFLOW;
- Warning ("Memory area overflow in `%s', segment `%s' (%lu bytes)",
- GetString (M->Name), GetString (S->Name),
- M->FillLevel - M->Size);
+ 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
/* 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 and size of the memory area */
+ /* 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 start of the memory area */
- SB_Printf (&Buf, "__%s_START__", GetString (M->Name));
- E = CreateMemoryExport (GetStrBufId (&Buf), M, 0);
- E->Pos = M->Pos;
-
/* Define the size of the memory area */
SB_Printf (&Buf, "__%s_SIZE__", GetString (M->Name));
E = CreateConstExport (GetStrBufId (&Buf), M->Size);
- E->Pos = M->Pos;
+ 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);
- E->Pos = M->Pos;
+ 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 */