]> git.sur5r.net Git - cc65/commitdiff
Add support for Atari XEX file format to LD65
authorDaniel Serpell <daniel.serpell@gmail.com>
Tue, 25 Sep 2018 02:58:06 +0000 (23:58 -0300)
committerDaniel Serpell <daniel.serpell@gmail.com>
Sun, 3 Feb 2019 21:04:46 +0000 (18:04 -0300)
src/common/target.h
src/ld65.vcxproj
src/ld65/binfmt.c
src/ld65/config.c
src/ld65/scanner.h
src/ld65/xex.c [new file with mode: 0644]
src/ld65/xex.h [new file with mode: 0644]

index c8e2472d3062283ab9721435ef0207c4e668270d..5b086e40c9bd93effe80886dc9dda0e39220d166 100644 (file)
@@ -103,6 +103,7 @@ extern target_t         Target;
 #define BINFMT_DEFAULT          0       /* Default (binary) */
 #define BINFMT_BINARY           1       /* Straight binary format */
 #define BINFMT_O65              2       /* Andre Fachats o65 format */
+#define BINFMT_ATARIEXE         3       /* Standard Atari binary load */
 
 
 
index f98da6119031be76357c590e5f58c43e41ef1759..a78f3128bc0f874752a818d9fac0d07dc0954c0c 100644 (file)
     <ClInclude Include="ld65\span.h" />
     <ClInclude Include="ld65\spool.h" />
     <ClInclude Include="ld65\tpool.h" />
+    <ClInclude Include="ld65\xex.h" />
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="ld65\asserts.c" />
     <ClCompile Include="ld65\span.c" />
     <ClCompile Include="ld65\spool.c" />
     <ClCompile Include="ld65\tpool.c" />
+    <ClCompile Include="ld65\xex.c" />
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
-</Project>
\ No newline at end of file
+</Project>
index a510f94b7d12ef94d8061ae8395c0a795b950f5d..f4f2678feb8219c106c46efdcf909e473b9040b7 100644 (file)
@@ -73,6 +73,7 @@ int RelocatableBinFmt (unsigned Format)
     switch (Format) {
 
         case BINFMT_BINARY:
+        case BINFMT_ATARIEXE:
             Reloc = 0;
             break;
 
index 099617ba0b84a797e64701f1d20cc92c08bd0058..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;
 
 
 
@@ -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      },
@@ -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");
                     }
@@ -1023,6 +1030,7 @@ static void ParseFormats (void)
                 break;
 
             case CFGTOK_BIN:
+            case CFGTOK_ATARIEXE:
                 /* No attribibutes available */
                 break;
 
@@ -1559,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.
@@ -2098,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);
 
index 2df952ebbb6aa8be7813437152e08e955cbb8601..783685951ddbd77ee34f97a1da9d93316a44becd 100644 (file)
@@ -107,6 +107,7 @@ typedef enum {
     CFGTOK_ZP,
     CFGTOK_OVERWRITE,
 
+    CFGTOK_ATARIEXE,
     CFGTOK_O65,
     CFGTOK_BIN,
 
diff --git a/src/ld65/xex.c b/src/ld65/xex.c
new file mode 100644 (file)
index 0000000..a57fdad
--- /dev/null
@@ -0,0 +1,344 @@
+/*****************************************************************************/
+/*                                                                           */
+/*                                   xex.c                                   */
+/*                                                                           */
+/*               Module to handle the Atari XEX binary format                */
+/*                                                                           */
+/*                                                                           */
+/*                                                                           */
+/* (C) 2018 Daniel Serpell                                                   */
+/*                                                                           */
+/*                                                                           */
+/* 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 <string.h>
+#include <errno.h>
+
+/* common */
+#include "alignment.h"
+#include "print.h"
+#include "xmalloc.h"
+
+/* ld65 */
+#include "xex.h"
+#include "config.h"
+#include "exports.h"
+#include "expr.h"
+#include "error.h"
+#include "global.h"
+#include "fileio.h"
+#include "lineinfo.h"
+#include "memarea.h"
+#include "segments.h"
+#include "spool.h"
+
+
+
+/*****************************************************************************/
+/*                                   Data                                    */
+/*****************************************************************************/
+
+
+
+struct XexDesc {
+    unsigned    Undef;          /* Count of undefined externals */
+    FILE*       F;              /* Output file */
+    const char* Filename;       /* Name of output file */
+};
+
+
+
+/*****************************************************************************/
+/*                                   Code                                    */
+/*****************************************************************************/
+
+
+
+XexDesc* NewXexDesc (void)
+/* Create a new XEX format descriptor */
+{
+    /* Allocate memory for a new XexDesc struct */
+    XexDesc* D = xmalloc (sizeof (XexDesc));
+
+    /* Initialize the fields */
+    D->Undef    = 0;
+    D->F        = 0;
+    D->Filename = 0;
+
+    /* Return the created struct */
+    return D;
+}
+
+
+
+void FreeXexDesc (XexDesc* D)
+/* Free a XEX format descriptor */
+{
+    xfree (D);
+}
+
+
+
+static unsigned XexWriteExpr (ExprNode* E, int Signed, unsigned Size,
+                              unsigned long Offs attribute ((unused)),
+                              void* Data)
+/* Called from SegWrite for an expression. Evaluate the expression, check the
+** range and write the expression value to the file.
+*/
+{
+    /* There's a predefined function to handle constant expressions */
+    return SegWriteConstExpr (((XexDesc*)Data)->F, E, Signed, Size);
+}
+
+
+
+static void PrintBoolVal (const char* Name, int B)
+/* Print a boolean value for debugging */
+{
+    Print (stdout, 2, "      %s = %s\n", Name, B? "true" : "false");
+}
+
+
+
+static void PrintNumVal (const char* Name, unsigned long V)
+/* Print a numerical value for debugging */
+{
+    Print (stdout, 2, "      %s = 0x%lx\n", Name, V);
+}
+
+
+
+static void XexWriteMem (XexDesc* D, MemoryArea* M)
+/* Write the segments of one memory area to a file */
+{
+    unsigned I;
+
+    /* Get the start address and size of this memory area */
+    unsigned long Addr = M->Start;
+
+    /* Walk over segments twice: first to get real area size, then to write
+     * all segments. */
+    for (I = 0; I < CollCount (&M->SegList); ++I) {
+
+        /* Get the segment */
+        SegDesc* S = CollAtUnchecked (&M->SegList, I);
+
+        /* Keep the user happy */
+        Print (stdout, 1, "    Allocating `%s'\n", GetString (S->Name));
+
+        /* If this is the run memory area, we must apply run alignment. If
+        ** this is not the run memory area but the load memory area (which
+        ** means that both are different), we must apply load alignment.
+        ** Beware: DoWrite may be true even if this is the run memory area,
+        ** because it may be also the load memory area.
+        */
+        if (S->Run == M) {
+
+            /* Handle ALIGN and OFFSET/START */
+            if (S->Flags & SF_ALIGN) {
+                /* Align the address */
+                Addr = AlignAddr (Addr, S->RunAlignment);
+            } else if (S->Flags & (SF_OFFSET | SF_START)) {
+                Addr = S->Addr;
+                if (S->Flags & SF_OFFSET) {
+                    /* It's an offset, not a fixed address, make an address */
+                    Addr += M->Start;
+                }
+            }
+
+        } else if (S->Load == M) {
+
+            /* Handle ALIGN_LOAD */
+            if (S->Flags & SF_ALIGN_LOAD) {
+                /* Align the address */
+                Addr = AlignAddr (Addr, S->LoadAlignment);
+            }
+
+        }
+
+        /* Calculate the new address */
+        Addr += S->Seg->Size;
+    }
+
+    /* Write header */
+    Write16(D->F, 0xFFFF);
+    Write16(D->F, M->Start);
+    Write16(D->F, Addr-1);
+
+    /* Redo */
+    Addr = M->Start;
+    for (I = 0; I < CollCount (&M->SegList); ++I) {
+
+        int DoWrite;
+
+        /* Get the segment */
+        SegDesc* S = CollAtUnchecked (&M->SegList, I);
+
+        /* Keep the user happy */
+        Print (stdout, 1, "    Allocating `%s'\n", GetString (S->Name));
+
+        /* Writes do only occur in the load area and not for BSS segments */
+        DoWrite = (S->Flags & SF_BSS) == 0      &&      /* No BSS segment */
+                   S->Load == M                 &&      /* LOAD segment */
+                   S->Seg->Dumped == 0;                 /* Not already written */
+
+        /* If this is the run memory area, we must apply run alignment. If
+        ** this is not the run memory area but the load memory area (which
+        ** means that both are different), we must apply load alignment.
+        ** Beware: DoWrite may be true even if this is the run memory area,
+        ** because it may be also the load memory area.
+        */
+        if (S->Run == M) {
+
+            /* Handle ALIGN and OFFSET/START */
+            if (S->Flags & SF_ALIGN) {
+                /* Align the address */
+                unsigned long NewAddr = AlignAddr (Addr, S->RunAlignment);
+                if (DoWrite || (M->Flags & MF_FILL) != 0) {
+                    WriteMult (D->F, M->FillVal, NewAddr - Addr);
+                    PrintNumVal ("SF_ALIGN", NewAddr - Addr);
+                }
+                Addr = NewAddr;
+            } else if (S->Flags & (SF_OFFSET | SF_START)) {
+                unsigned long NewAddr = S->Addr;
+                if (S->Flags & SF_OFFSET) {
+                    /* It's an offset, not a fixed address, make an address */
+                    NewAddr += M->Start;
+                }
+                if (DoWrite || (M->Flags & MF_FILL) != 0) {
+                    /* Seek in "overwrite" segments */
+                    if (S->Flags & SF_OVERWRITE) {
+                        fseek (D->F, NewAddr - M->Start, SEEK_SET);
+                    } else {
+                        WriteMult (D->F, M->FillVal, NewAddr-Addr);
+                        PrintNumVal ("SF_OFFSET", NewAddr - Addr);
+                    }
+                }
+                Addr = NewAddr;
+            }
+
+        } else if (S->Load == M) {
+
+            /* Handle ALIGN_LOAD */
+            if (S->Flags & SF_ALIGN_LOAD) {
+                /* Align the address */
+                unsigned long NewAddr = AlignAddr (Addr, S->LoadAlignment);
+                if (DoWrite || (M->Flags & MF_FILL) != 0) {
+                    WriteMult (D->F, M->FillVal, NewAddr - Addr);
+                    PrintNumVal ("SF_ALIGN_LOAD", NewAddr - Addr);
+                }
+                Addr = NewAddr;
+            }
+
+        }
+
+        /* Now write the segment to disk if it is not a BSS type segment and
+        ** if the memory area is the load area.
+        */
+        if (DoWrite) {
+            unsigned long P = ftell (D->F);
+            SegWrite (D->Filename, D->F, S->Seg, XexWriteExpr, D);
+            PrintNumVal ("Wrote", (unsigned long) (ftell (D->F) - P));
+        } else if (M->Flags & MF_FILL) {
+            WriteMult (D->F, S->Seg->FillVal, S->Seg->Size);
+            PrintNumVal ("Filled", (unsigned long) S->Seg->Size);
+        }
+
+        /* If this was the load memory area, mark the segment as dumped */
+        if (S->Load == M) {
+            S->Seg->Dumped = 1;
+        }
+
+        /* Calculate the new address */
+        Addr += S->Seg->Size;
+    }
+
+    /* If a fill was requested, fill the remaining space */
+    if ((M->Flags & MF_FILL) != 0 && M->FillLevel < M->Size) {
+        unsigned long ToFill = M->Size - M->FillLevel;
+        Print (stdout, 2, "    Filling 0x%lx bytes with 0x%02x\n",
+               ToFill, M->FillVal);
+        WriteMult (D->F, M->FillVal, ToFill);
+        M->FillLevel = M->Size;
+    }
+}
+
+
+
+static int XexUnresolved (unsigned Name attribute ((unused)), void* D)
+/* Called if an unresolved symbol is encountered */
+{
+    /* Unresolved symbols are an error in XEX format. Bump the counter
+    ** and return zero telling the caller that the symbol is indeed
+    ** unresolved.
+    */
+    ((XexDesc*) D)->Undef++;
+    return 0;
+}
+
+
+
+void XexWriteTarget (XexDesc* D, struct File* F)
+/* Write a XEX output file */
+{
+    unsigned I;
+
+    /* Place the filename in the control structure */
+    D->Filename = GetString (F->Name);
+
+    /* Check for unresolved symbols. The function XexUnresolved is called
+    ** if we get an unresolved symbol.
+    */
+    D->Undef = 0;               /* Reset the counter */
+    CheckUnresolvedImports (XexUnresolved, D);
+    if (D->Undef > 0) {
+        /* We had unresolved symbols, cannot create output file */
+        Error ("%u unresolved external(s) found - cannot create output file", D->Undef);
+    }
+
+    /* Open the file */
+    D->F = fopen (D->Filename, "wb");
+    if (D->F == 0) {
+        Error ("Cannot open `%s': %s", D->Filename, strerror (errno));
+    }
+
+    /* Keep the user happy */
+    Print (stdout, 1, "Opened `%s'...\n", D->Filename);
+
+    /* Dump all memory areas */
+    for (I = 0; I < CollCount (&F->MemoryAreas); ++I) {
+        /* Get this entry */
+        MemoryArea* M = CollAtUnchecked (&F->MemoryAreas, I);
+        Print (stdout, 1, "  XEX Dumping `%s'\n", GetString (M->Name));
+        XexWriteMem (D, M);
+    }
+
+    /* Close the file */
+    if (fclose (D->F) != 0) {
+        Error ("Cannot write to `%s': %s", D->Filename, strerror (errno));
+    }
+
+    /* Reset the file and filename */
+    D->F        = 0;
+    D->Filename = 0;
+}
diff --git a/src/ld65/xex.h b/src/ld65/xex.h
new file mode 100644 (file)
index 0000000..fd6ba72
--- /dev/null
@@ -0,0 +1,72 @@
+/*****************************************************************************/
+/*                                                                           */
+/*                                   xex.h                                   */
+/*                                                                           */
+/*               Module to handle the Atari EXE binary format                */
+/*                                                                           */
+/*                                                                           */
+/*                                                                           */
+/* (C) 2018 Daniel Serpell                                                   */
+/*                                                                           */
+/*                                                                           */
+/* 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.                                                          */
+/*                                                                           */
+/*****************************************************************************/
+
+
+
+#ifndef XEX_H
+#define XEX_H
+
+
+
+#include "config.h"
+
+
+
+/*****************************************************************************/
+/*                                   Data                                    */
+/*****************************************************************************/
+
+
+
+/* Structure describing the format */
+typedef struct XexDesc XexDesc;
+
+
+
+/*****************************************************************************/
+/*                                   Code                                    */
+/*****************************************************************************/
+
+
+
+XexDesc* NewXexDesc (void);
+/* Create a new XEX format descriptor */
+
+void FreeXexDesc (XexDesc* D);
+/* Free a XEX format descriptor */
+
+void XexWriteTarget (XexDesc* D, File* F);
+/* Write a XEX output file */
+
+
+
+/* End of xex.h */
+
+#endif