]> git.sur5r.net Git - cc65/blobdiff - src/ld65/config.c
Add support for Atari XEX file format to LD65
[cc65] / src / ld65 / config.c
index 8e7a049c7df5fa57abbd59fb6c8752480e683580..fdf7d13eb7da070124eae38ff8eb855352bb867f 100644 (file)
@@ -68,6 +68,7 @@
 #include "objdata.h"
 #include "scanner.h"
 #include "spool.h"
+#include "xex.h"
 
 
 
@@ -149,6 +150,7 @@ 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;
 
 
 
@@ -226,7 +228,7 @@ static MemoryArea* CfgGetMemory (unsigned Name)
 {
     MemoryArea* M = CfgFindMemory (Name);
     if (M == 0) {
-        CfgError (&CfgErrorPos, "Invalid memory area `%s'", GetString (Name));
+        CfgError (&CfgErrorPos, "Invalid memory area '%s'", GetString (Name));
     }
     return M;
 }
@@ -320,7 +322,7 @@ static MemoryArea* CreateMemoryArea (const FilePos* Pos, unsigned Name)
     MemoryArea* M = CfgFindMemory (Name);
     if (M) {
         CfgError (&CfgErrorPos,
-                  "Memory area `%s' defined twice",
+                  "Memory area '%s' defined twice",
                   GetString (Name));
     }
 
@@ -343,7 +345,7 @@ static SegDesc* NewSegDesc (unsigned Name)
     /* Check for duplicate names */
     SegDesc* S = CfgFindSegDesc (Name);
     if (S) {
-        CfgError (&CfgErrorPos, "Segment `%s' defined twice", GetString (Name));
+        CfgError (&CfgErrorPos, "Segment '%s' defined twice", GetString (Name));
     }
 
     /* Allocate memory */
@@ -543,6 +545,7 @@ static void ParseFiles (void)
         {   "FORMAT",   CFGTOK_FORMAT   },
     };
     static const IdentTok Formats [] = {
+        {   "ATARI",    CFGTOK_ATARIEXE },
         {   "O65",      CFGTOK_O65      },
         {   "BIN",      CFGTOK_BIN      },
         {   "BINARY",   CFGTOK_BIN      },
@@ -566,7 +569,7 @@ static void ParseFiles (void)
         F = FindFile (GetStrBufId (&CfgSVal));
         if (F == 0) {
             CfgError (&CfgErrorPos,
-                      "File `%s' not found in MEMORY section",
+                      "File '%s' not found in MEMORY section",
                       SB_GetConstBuf (&CfgSVal));
         }
 
@@ -607,6 +610,10 @@ static void ParseFiles (void)
                             F->Format = BINFMT_O65;
                             break;
 
+                        case CFGTOK_ATARIEXE:
+                            F->Format = BINFMT_ATARIEXE;
+                            break;
+
                         default:
                             Error ("Unexpected format token");
                     }
@@ -653,6 +660,7 @@ static void ParseSegments (void)
         {   "RW",               CFGTOK_RW               },
         {   "BSS",              CFGTOK_BSS              },
         {   "ZP",               CFGTOK_ZP               },
+        {   "OVERWRITE",        CFGTOK_OVERWRITE        },
     };
 
     unsigned Count;
@@ -753,11 +761,12 @@ static void ParseSegments (void)
                     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);
+                        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;
@@ -796,7 +805,7 @@ static void ParseSegments (void)
         */
         if ((S->Flags & SF_BSS) != 0 && (S->Load != S->Run)) {
             CfgWarning (&CfgErrorPos,
-                        "Segment with type `bss' has both LOAD and RUN "
+                        "Segment with type 'bss' has both LOAD and RUN "
                         "memory areas assigned");
         }
 
@@ -804,7 +813,7 @@ static void ParseSegments (void)
         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'",
+                          "Cannot put r/w segment '%s' in r/o memory area '%s'",
                           GetString (S->Name), GetString (S->Run->Name));
             }
         }
@@ -1021,6 +1030,7 @@ static void ParseFormats (void)
                 break;
 
             case CFGTOK_BIN:
+            case CFGTOK_ATARIEXE:
                 /* No attribibutes available */
                 break;
 
@@ -1509,7 +1519,7 @@ static void ParseConfig (void)
         CfgNextTok ();
 
         /* Expected a curly brace */
-        CfgConsume (CFGTOK_LCURLY, "`{' expected");
+        CfgConsume (CFGTOK_LCURLY, "'{' expected");
 
         /* Read the block */
         switch (BlockTok) {
@@ -1544,7 +1554,7 @@ static void ParseConfig (void)
         }
 
         /* Skip closing brace */
-        CfgConsume (CFGTOK_RCURLY, "`}' expected");
+        CfgConsume (CFGTOK_RCURLY, "'}' expected");
 
     } while (CfgTok != CFGTOK_EOF);
 }
@@ -1557,6 +1567,7 @@ void CfgRead (void)
     /* 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.
@@ -1601,7 +1612,7 @@ static void ProcessSegments (void)
         */
         if ((S->Flags & SF_BSS) != 0 && S->Seg != 0 && !IsBSSType (S->Seg)) {
             CfgWarning (GetSourcePos (S->LI),
-                        "Segment `%s' with type `bss' contains initialized data",
+                        "Segment '%s' with type 'bss' contains initialized data",
                         GetString (S->Name));
         }
 
@@ -1630,7 +1641,7 @@ static void ProcessSegments (void)
             /* Print a warning if the segment is not optional */
             if ((S->Flags & SF_OPTIONAL) == 0) {
                 CfgWarning (&CfgErrorPos,
-                            "Segment `%s' does not exist",
+                            "Segment '%s' does not exist",
                             GetString (S->Name));
             }
 
@@ -1663,7 +1674,7 @@ static void ProcessSymbols (void)
                 if (O65GetImport (O65FmtDesc, Sym->Name) != 0) {
                     CfgError (
                         GetSourcePos (Sym->LI),
-                        "Exported o65 symbol `%s' cannot also be an o65 import",
+                        "Exported o65 symbol '%s' cannot also be an o65 import",
                         GetString (Sym->Name)
                     );
                 }
@@ -1675,7 +1686,7 @@ static void ProcessSymbols (void)
                 if (O65GetExport (O65FmtDesc, Sym->Name) != 0) {
                     CfgError (
                         GetSourcePos (Sym->LI),
-                        "Duplicate exported o65 symbol: `%s'",
+                        "Duplicate exported o65 symbol: '%s'",
                         GetString (Sym->Name)
                     );
                 }
@@ -1689,7 +1700,7 @@ static void ProcessSymbols (void)
                 if (O65GetExport (O65FmtDesc, Sym->Name) != 0) {
                     CfgError (
                         GetSourcePos (Sym->LI),
-                        "Imported o65 symbol `%s' cannot also be an o65 export",
+                        "Imported o65 symbol '%s' cannot also be an o65 export",
                         GetString (Sym->Name)
                     );
                 }
@@ -1701,7 +1712,7 @@ static void ProcessSymbols (void)
                 if (O65GetImport (O65FmtDesc, Sym->Name) != 0) {
                     CfgError (
                         GetSourcePos (Sym->LI),
-                        "Duplicate imported o65 symbol: `%s'",
+                        "Duplicate imported o65 symbol: '%s'",
                         GetString (Sym->Name)
                     );
                 }
@@ -1795,6 +1806,7 @@ unsigned CfgProcess (void)
     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);
@@ -1810,7 +1822,7 @@ unsigned CfgProcess (void)
         */
         if (!IsConstExpr (M->StartExpr)) {
             CfgError (GetSourcePos (M->LI),
-                      "Start address of memory area `%s' is not constant",
+                      "Start address of memory area '%s' is not constant",
                       GetString (M->Name));
         }
         Addr = M->Start = GetExprVal (M->StartExpr);
@@ -1835,7 +1847,7 @@ unsigned CfgProcess (void)
         /* Resolve the size expression */
         if (!IsConstExpr (M->SizeExpr)) {
             CfgError (GetSourcePos (M->LI),
-                      "Size of memory area `%s' is not constant",
+                      "Size of memory area '%s' is not constant",
                       GetString (M->Name));
         }
         M->Size = GetExprVal (M->SizeExpr);
@@ -1848,6 +1860,27 @@ unsigned CfgProcess (void)
             /* Remember the start address before handling this segment */
             unsigned long StartAddr = Addr;
 
+            /* 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.
             */
@@ -1855,6 +1888,25 @@ unsigned CfgProcess (void)
                 /* 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);
@@ -1865,8 +1917,8 @@ unsigned CfgProcess (void)
                     */
                     if (M->FillLevel == 0 && NewAddr > Addr) {
                         CfgWarning (GetSourcePos (S->LI),
-                                    "First segment in memory area `%s' does "
-                                    "already need fill bytes for alignment",
+                                    "The first segment in memory area '%s' "
+                                    "needs fill bytes for alignment.",
                                     GetString (M->Name));
                     }
 
@@ -1882,22 +1934,33 @@ unsigned CfgProcess (void)
                         /* An offset was given, no address, make an address */
                         NewAddr += M->Start;
                     }
-                    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%c",
-                                        GetString (S->Name), GetString (M->Name),
-                                        Addr - NewAddr, (Addr - NewAddr == 1) ? ' ' : 's');
+
+                    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 {
-                            CfgWarning (GetSourcePos (S->LI),
-                                        "Segment `%s' start address is too low in `%s' by %lu byte%c",
-                                        GetString (S->Name), GetString (M->Name),
-                                        Addr - NewAddr, (Addr - NewAddr == 1) ? ' ' : 's');
+                            Addr = NewAddr;
                         }
                     } else {
-                        Addr = NewAddr;
+                        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%c",
+                                            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%c",
+                                            GetString (S->Name), GetString (M->Name),
+                                            Addr - NewAddr, (Addr - NewAddr == 1) ? ' ' : 's');
+                            }
+                        } else {
+                            Addr = NewAddr;
+                        }
                     }
                 }
 
@@ -1938,7 +2001,7 @@ unsigned CfgProcess (void)
                 ++Overflows;
                 M->Flags |= MF_OVERFLOW;
                 CfgWarning (GetSourcePos (M->LI),
-                            "Segment `%s' overflows memory area `%s' by %lu byte%c",
+                            "Segment '%s' overflows memory area '%s' by %lu byte%c",
                             GetString (S->Name), GetString (M->Name),
                             M->FillLevel - M->Size, (M->FillLevel - M->Size == 1) ? ' ' : 's');
             }
@@ -2044,6 +2107,10 @@ void CfgWriteTarget (void)
                         O65WriteTarget (O65FmtDesc, F);
                         break;
 
+                    case BINFMT_ATARIEXE:
+                        XexWriteTarget (XexFmtDesc, F);
+                        break;
+
                     default:
                         Internal ("Invalid binary format: %u", F->Format);
 
@@ -2063,7 +2130,7 @@ void CfgWriteTarget (void)
                     MemoryArea* M = CollAtUnchecked (&F->MemoryAreas, J);
 
                     /* Debugging */
-                    Print (stdout, 2, "Skipping `%s'...\n", GetString (M->Name));
+                    Print (stdout, 2, "Skipping '%s'...\n", GetString (M->Name));
 
                     /* Walk throught the segments */
                     for (K = 0; K < CollCount (&M->SegList); ++K) {