From 0e947d594f43bfba4165b80ddef1a47e22d1afa3 Mon Sep 17 00:00:00 2001 From: Daniel Serpell Date: Mon, 24 Sep 2018 23:58:06 -0300 Subject: [PATCH 1/1] Add support for Atari XEX file format to LD65 --- src/common/target.h | 1 + src/ld65.vcxproj | 4 +- src/ld65/binfmt.c | 1 + src/ld65/config.c | 13 ++ src/ld65/scanner.h | 1 + src/ld65/xex.c | 344 ++++++++++++++++++++++++++++++++++++++++++++ src/ld65/xex.h | 72 ++++++++++ 7 files changed, 435 insertions(+), 1 deletion(-) create mode 100644 src/ld65/xex.c create mode 100644 src/ld65/xex.h diff --git a/src/common/target.h b/src/common/target.h index c8e2472d3..5b086e40c 100644 --- a/src/common/target.h +++ b/src/common/target.h @@ -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 */ diff --git a/src/ld65.vcxproj b/src/ld65.vcxproj index f98da6119..a78f3128b 100644 --- a/src/ld65.vcxproj +++ b/src/ld65.vcxproj @@ -106,6 +106,7 @@ + @@ -139,8 +140,9 @@ + - \ No newline at end of file + diff --git a/src/ld65/binfmt.c b/src/ld65/binfmt.c index a510f94b7..f4f2678fe 100644 --- a/src/ld65/binfmt.c +++ b/src/ld65/binfmt.c @@ -73,6 +73,7 @@ int RelocatableBinFmt (unsigned Format) switch (Format) { case BINFMT_BINARY: + case BINFMT_ATARIEXE: Reloc = 0; break; diff --git a/src/ld65/config.c b/src/ld65/config.c index 099617ba0..fdf7d13eb 100644 --- a/src/ld65/config.c +++ b/src/ld65/config.c @@ -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); diff --git a/src/ld65/scanner.h b/src/ld65/scanner.h index 2df952ebb..783685951 100644 --- a/src/ld65/scanner.h +++ b/src/ld65/scanner.h @@ -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 index 000000000..a57fdad06 --- /dev/null +++ b/src/ld65/xex.c @@ -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 +#include +#include + +/* 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 index 000000000..fd6ba7201 --- /dev/null +++ b/src/ld65/xex.h @@ -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 -- 2.39.2