]> git.sur5r.net Git - cc65/blobdiff - src/ld65/config.c
Finish support for .BANK.
[cc65] / src / ld65 / config.c
index 623264358f09abc2a092b4655aa45b5d0cc4fa32..54d88e6b8a0e8401e30e58fae97225fc9d166618 100644 (file)
@@ -6,11 +6,15 @@
 /*                                                                           */
 /*                                                                           */
 /*                                                                           */
-/* (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"
@@ -97,6 +103,7 @@ static Collection       MemoryAreas = STATIC_COLLECTION_INITIALIZER;
 #define MA_DEFINE              0x0010
 #define MA_FILL                0x0020
 #define MA_FILLVAL             0x0040
+#define MA_BANK         0x0080
 
 /* Segment list */
 static Collection       SegDescList = STATIC_COLLECTION_INITIALIZER;
@@ -128,7 +135,7 @@ typedef enum {
 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 */
@@ -268,7 +275,7 @@ static CfgSymbol* NewCfgSymbol (CfgSymType Type, unsigned Name)
 
     /* Initialize the fields */
     Sym->Type     = Type;
-    Sym->Pos      = CfgErrorPos;
+    Sym->LI       = GenLineInfo (&CfgErrorPos);
     Sym->Name     = Name;
     Sym->Value    = 0;
     Sym->AddrSize = ADDR_SIZE_INVALID;
@@ -292,6 +299,7 @@ static File* NewFile (unsigned Name)
     F->Name    = Name;
     F->Flags   = 0;
     F->Format  = BINFMT_DEFAULT;
+    F->Size    = 0;
     InitCollection (&F->MemoryAreas);
 
     /* Insert the struct into the list */
@@ -340,12 +348,13 @@ static SegDesc* NewSegDesc (unsigned Name)
     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);
@@ -359,6 +368,7 @@ static SegDesc* NewSegDesc (unsigned Name)
 static void FreeSegDesc (SegDesc* S)
 /* Free a segment descriptor */
 {
+    FreeLineInfo (S->LI);
     xfree (S);
 }
 
@@ -397,13 +407,14 @@ static void ParseMemory (void)
 /* 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       },
@@ -434,21 +445,17 @@ static void ParseMemory (void)
            /* 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;
@@ -461,16 +468,6 @@ static void ParseMemory (void)
                     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 */
@@ -486,6 +483,25 @@ static void ParseMemory (void)
                    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");
 
@@ -636,7 +652,6 @@ static void ParseSegments (void)
     };
 
     unsigned Count;
-    long     Val;
 
     /* The MEMORY section must preceed the SEGMENTS section */
     if ((SectionsEncountered & SE_MEMORY) == 0) {
@@ -671,21 +686,13 @@ static void ParseSegments (void)
 
                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;
 
@@ -767,9 +774,9 @@ static void ParseSegments (void)
          * 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;
         }
@@ -778,8 +785,9 @@ static void ParseSegments (void)
          * 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 */
@@ -1413,10 +1421,10 @@ static void ParseSymbols (void)
 
             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:
@@ -1427,12 +1435,12 @@ static void ParseSymbols (void)
                 /* 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;
@@ -1565,8 +1573,9 @@ static void ProcessSegments (void)
          * 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
@@ -1623,7 +1632,7 @@ static void ProcessSymbols (void)
                 /* 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)
                     );
@@ -1635,7 +1644,7 @@ static void ProcessSymbols (void)
                  */
                 if (O65GetExport (O65FmtDesc, Sym->Name) != 0) {
                     CfgError (
-                        &Sym->Pos,
+                        GetSourcePos (Sym->LI),
                         "Duplicate exported o65 symbol: `%s'",
                         GetString (Sym->Name)
                     );
@@ -1649,7 +1658,7 @@ static void ProcessSymbols (void)
                 /* 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)
                     );
@@ -1661,7 +1670,7 @@ static void ProcessSymbols (void)
                  */
                 if (O65GetImport (O65FmtDesc, Sym->Name) != 0) {
                     CfgError (
-                        &Sym->Pos,
+                        GetSourcePos (Sym->LI),
                         "Duplicate imported o65 symbol: `%s'",
                         GetString (Sym->Name)
                     );
@@ -1676,7 +1685,7 @@ static void ProcessSymbols (void)
                 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;
 
@@ -1699,12 +1708,12 @@ static void CreateRunDefines (SegDesc* S, unsigned long SegAddr)
     /* 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);
@@ -1721,7 +1730,7 @@ static void CreateLoadDefines (SegDesc* S, unsigned long SegAddr)
     /* 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);
@@ -1761,37 +1770,56 @@ unsigned CfgProcess (void)
         /* 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.
              */
@@ -1802,8 +1830,22 @@ unsigned CfgProcess (void)
                  */
                 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;
@@ -1814,12 +1856,12 @@ unsigned CfgProcess (void)
                     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));
@@ -1828,16 +1870,30 @@ unsigned CfgProcess (void)
                     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) {
 
@@ -1846,8 +1902,7 @@ unsigned CfgProcess (void)
                  */
                 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);
                 }
 
             }
@@ -1859,9 +1914,10 @@ unsigned CfgProcess (void)
                    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
@@ -1879,31 +1935,49 @@ unsigned CfgProcess (void)
            /* 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 */