]> git.sur5r.net Git - cc65/blob - src/ld65/xex.c
71065ea5755f26c742b63b2ac4f560f83cc7b521
[cc65] / src / ld65 / xex.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                   xex.c                                   */
4 /*                                                                           */
5 /*               Module to handle the Atari XEX binary format                */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 2018 Daniel Serpell                                                   */
10 /*                                                                           */
11 /*                                                                           */
12 /* This software is provided 'as-is', without any expressed or implied       */
13 /* warranty.  In no event will the authors be held liable for any damages    */
14 /* arising from the use of this software.                                    */
15 /*                                                                           */
16 /* Permission is granted to anyone to use this software for any purpose,     */
17 /* including commercial applications, and to alter it and redistribute it    */
18 /* freely, subject to the following restrictions:                            */
19 /*                                                                           */
20 /* 1. The origin of this software must not be misrepresented; you must not   */
21 /*    claim that you wrote the original software. If you use this software   */
22 /*    in a product, an acknowledgment in the product documentation would be  */
23 /*    appreciated but is not required.                                       */
24 /* 2. Altered source versions must be plainly marked as such, and must not   */
25 /*    be misrepresented as being the original software.                      */
26 /* 3. This notice may not be removed or altered from any source              */
27 /*    distribution.                                                          */
28 /*                                                                           */
29 /*****************************************************************************/
30
31
32
33 #include <stdio.h>
34 #include <string.h>
35 #include <errno.h>
36
37 /* common */
38 #include "alignment.h"
39 #include "print.h"
40 #include "xmalloc.h"
41
42 /* ld65 */
43 #include "xex.h"
44 #include "config.h"
45 #include "exports.h"
46 #include "expr.h"
47 #include "error.h"
48 #include "global.h"
49 #include "fileio.h"
50 #include "lineinfo.h"
51 #include "memarea.h"
52 #include "segments.h"
53 #include "spool.h"
54
55
56
57 /*****************************************************************************/
58 /*                                   Data                                    */
59 /*****************************************************************************/
60
61
62
63 struct XexDesc {
64     unsigned    Undef;          /* Count of undefined externals */
65     FILE*       F;              /* Output file */
66     const char* Filename;       /* Name of output file */
67 };
68
69
70
71 /*****************************************************************************/
72 /*                                   Code                                    */
73 /*****************************************************************************/
74
75
76
77 XexDesc* NewXexDesc (void)
78 /* Create a new XEX format descriptor */
79 {
80     /* Allocate memory for a new XexDesc struct */
81     XexDesc* D = xmalloc (sizeof (XexDesc));
82
83     /* Initialize the fields */
84     D->Undef    = 0;
85     D->F        = 0;
86     D->Filename = 0;
87
88     /* Return the created struct */
89     return D;
90 }
91
92
93
94 void FreeXexDesc (XexDesc* D)
95 /* Free a XEX format descriptor */
96 {
97     xfree (D);
98 }
99
100
101
102 static unsigned XexWriteExpr (ExprNode* E, int Signed, unsigned Size,
103                               unsigned long Offs attribute ((unused)),
104                               void* Data)
105 /* Called from SegWrite for an expression. Evaluate the expression, check the
106 ** range and write the expression value to the file.
107 */
108 {
109     /* There's a predefined function to handle constant expressions */
110     return SegWriteConstExpr (((XexDesc*)Data)->F, E, Signed, Size);
111 }
112
113
114
115 static void PrintNumVal (const char* Name, unsigned long V)
116 /* Print a numerical value for debugging */
117 {
118     Print (stdout, 2, "      %s = 0x%lx\n", Name, V);
119 }
120
121
122
123 static void XexWriteMem (XexDesc* D, MemoryArea* M)
124 /* Write the segments of one memory area to a file */
125 {
126     unsigned I;
127
128     /* Get the start address and size of this memory area */
129     unsigned long Addr = M->Start;
130
131     /* Walk over segments twice: first to get real area size, then to write
132      * all segments. */
133     for (I = 0; I < CollCount (&M->SegList); ++I) {
134
135         /* Get the segment */
136         SegDesc* S = CollAtUnchecked (&M->SegList, I);
137
138         /* Keep the user happy */
139         Print (stdout, 1, "    Allocating `%s'\n", GetString (S->Name));
140
141         /* If this is the run memory area, we must apply run alignment. If
142         ** this is not the run memory area but the load memory area (which
143         ** means that both are different), we must apply load alignment.
144         ** Beware: DoWrite may be true even if this is the run memory area,
145         ** because it may be also the load memory area.
146         */
147         if (S->Run == M) {
148
149             /* Handle ALIGN and OFFSET/START */
150             if (S->Flags & SF_ALIGN) {
151                 /* Align the address */
152                 Addr = AlignAddr (Addr, S->RunAlignment);
153             } else if (S->Flags & (SF_OFFSET | SF_START)) {
154                 Addr = S->Addr;
155                 if (S->Flags & SF_OFFSET) {
156                     /* It's an offset, not a fixed address, make an address */
157                     Addr += M->Start;
158                 }
159             }
160
161         } else if (S->Load == M) {
162
163             /* Handle ALIGN_LOAD */
164             if (S->Flags & SF_ALIGN_LOAD) {
165                 /* Align the address */
166                 Addr = AlignAddr (Addr, S->LoadAlignment);
167             }
168
169         }
170
171         /* Calculate the new address */
172         Addr += S->Seg->Size;
173     }
174
175     /* Write header */
176     Write16(D->F, 0xFFFF);
177     Write16(D->F, M->Start);
178     Write16(D->F, Addr-1);
179
180     /* Redo */
181     Addr = M->Start;
182     for (I = 0; I < CollCount (&M->SegList); ++I) {
183
184         int DoWrite;
185
186         /* Get the segment */
187         SegDesc* S = CollAtUnchecked (&M->SegList, I);
188
189         /* Keep the user happy */
190         Print (stdout, 1, "    Allocating `%s'\n", GetString (S->Name));
191
192         /* Writes do only occur in the load area and not for BSS segments */
193         DoWrite = (S->Flags & SF_BSS) == 0      &&      /* No BSS segment */
194                    S->Load == M                 &&      /* LOAD segment */
195                    S->Seg->Dumped == 0;                 /* Not already written */
196
197         /* If this is the run memory area, we must apply run alignment. If
198         ** this is not the run memory area but the load memory area (which
199         ** means that both are different), we must apply load alignment.
200         ** Beware: DoWrite may be true even if this is the run memory area,
201         ** because it may be also the load memory area.
202         */
203         if (S->Run == M) {
204
205             /* Handle ALIGN and OFFSET/START */
206             if (S->Flags & SF_ALIGN) {
207                 /* Align the address */
208                 unsigned long NewAddr = AlignAddr (Addr, S->RunAlignment);
209                 if (DoWrite || (M->Flags & MF_FILL) != 0) {
210                     WriteMult (D->F, M->FillVal, NewAddr - Addr);
211                     PrintNumVal ("SF_ALIGN", NewAddr - Addr);
212                 }
213                 Addr = NewAddr;
214             } else if (S->Flags & (SF_OFFSET | SF_START)) {
215                 unsigned long NewAddr = S->Addr;
216                 if (S->Flags & SF_OFFSET) {
217                     /* It's an offset, not a fixed address, make an address */
218                     NewAddr += M->Start;
219                 }
220                 if (DoWrite || (M->Flags & MF_FILL) != 0) {
221                     /* Seek in "overwrite" segments */
222                     if (S->Flags & SF_OVERWRITE) {
223                         fseek (D->F, NewAddr - M->Start, SEEK_SET);
224                     } else {
225                         WriteMult (D->F, M->FillVal, NewAddr-Addr);
226                         PrintNumVal ("SF_OFFSET", NewAddr - Addr);
227                     }
228                 }
229                 Addr = NewAddr;
230             }
231
232         } else if (S->Load == M) {
233
234             /* Handle ALIGN_LOAD */
235             if (S->Flags & SF_ALIGN_LOAD) {
236                 /* Align the address */
237                 unsigned long NewAddr = AlignAddr (Addr, S->LoadAlignment);
238                 if (DoWrite || (M->Flags & MF_FILL) != 0) {
239                     WriteMult (D->F, M->FillVal, NewAddr - Addr);
240                     PrintNumVal ("SF_ALIGN_LOAD", NewAddr - Addr);
241                 }
242                 Addr = NewAddr;
243             }
244
245         }
246
247         /* Now write the segment to disk if it is not a BSS type segment and
248         ** if the memory area is the load area.
249         */
250         if (DoWrite) {
251             unsigned long P = ftell (D->F);
252             SegWrite (D->Filename, D->F, S->Seg, XexWriteExpr, D);
253             PrintNumVal ("Wrote", (unsigned long) (ftell (D->F) - P));
254         } else if (M->Flags & MF_FILL) {
255             WriteMult (D->F, S->Seg->FillVal, S->Seg->Size);
256             PrintNumVal ("Filled", (unsigned long) S->Seg->Size);
257         }
258
259         /* If this was the load memory area, mark the segment as dumped */
260         if (S->Load == M) {
261             S->Seg->Dumped = 1;
262         }
263
264         /* Calculate the new address */
265         Addr += S->Seg->Size;
266     }
267
268     /* If a fill was requested, fill the remaining space */
269     if ((M->Flags & MF_FILL) != 0 && M->FillLevel < M->Size) {
270         unsigned long ToFill = M->Size - M->FillLevel;
271         Print (stdout, 2, "    Filling 0x%lx bytes with 0x%02x\n",
272                ToFill, M->FillVal);
273         WriteMult (D->F, M->FillVal, ToFill);
274         M->FillLevel = M->Size;
275     }
276 }
277
278
279
280 static int XexUnresolved (unsigned Name attribute ((unused)), void* D)
281 /* Called if an unresolved symbol is encountered */
282 {
283     /* Unresolved symbols are an error in XEX format. Bump the counter
284     ** and return zero telling the caller that the symbol is indeed
285     ** unresolved.
286     */
287     ((XexDesc*) D)->Undef++;
288     return 0;
289 }
290
291
292
293 void XexWriteTarget (XexDesc* D, struct File* F)
294 /* Write a XEX output file */
295 {
296     unsigned I;
297
298     /* Place the filename in the control structure */
299     D->Filename = GetString (F->Name);
300
301     /* Check for unresolved symbols. The function XexUnresolved is called
302     ** if we get an unresolved symbol.
303     */
304     D->Undef = 0;               /* Reset the counter */
305     CheckUnresolvedImports (XexUnresolved, D);
306     if (D->Undef > 0) {
307         /* We had unresolved symbols, cannot create output file */
308         Error ("%u unresolved external(s) found - cannot create output file", D->Undef);
309     }
310
311     /* Open the file */
312     D->F = fopen (D->Filename, "wb");
313     if (D->F == 0) {
314         Error ("Cannot open `%s': %s", D->Filename, strerror (errno));
315     }
316
317     /* Keep the user happy */
318     Print (stdout, 1, "Opened `%s'...\n", D->Filename);
319
320     /* Dump all memory areas */
321     for (I = 0; I < CollCount (&F->MemoryAreas); ++I) {
322         /* Get this entry */
323         MemoryArea* M = CollAtUnchecked (&F->MemoryAreas, I);
324         Print (stdout, 1, "  XEX Dumping `%s'\n", GetString (M->Name));
325         XexWriteMem (D, M);
326     }
327
328     /* Close the file */
329     if (fclose (D->F) != 0) {
330         Error ("Cannot write to `%s': %s", D->Filename, strerror (errno));
331     }
332
333     /* Reset the file and filename */
334     D->F        = 0;
335     D->Filename = 0;
336 }