]> git.sur5r.net Git - cc65/blob - src/ld65/o65.c
Fixed copy & paste errors in comments.
[cc65] / src / ld65 / o65.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                   o65.c                                   */
4 /*                                                                           */
5 /*                  Module to handle the o65 binary format                   */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1999-2011, Ullrich von Bassewitz                                      */
10 /*                Roemerstrasse 52                                           */
11 /*                D-70794 Filderstadt                                        */
12 /* EMail:         uz@cc65.org                                                */
13 /*                                                                           */
14 /*                                                                           */
15 /* This software is provided 'as-is', without any expressed or implied       */
16 /* warranty.  In no event will the authors be held liable for any damages    */
17 /* arising from the use of this software.                                    */
18 /*                                                                           */
19 /* Permission is granted to anyone to use this software for any purpose,     */
20 /* including commercial applications, and to alter it and redistribute it    */
21 /* freely, subject to the following restrictions:                            */
22 /*                                                                           */
23 /* 1. The origin of this software must not be misrepresented; you must not   */
24 /*    claim that you wrote the original software. If you use this software   */
25 /*    in a product, an acknowledgment in the product documentation would be  */
26 /*    appreciated but is not required.                                       */
27 /* 2. Altered source versions must be plainly marked as such, and must not   */
28 /*    be misrepresented as being the original software.                      */
29 /* 3. This notice may not be removed or altered from any source              */
30 /*    distribution.                                                          */
31 /*                                                                           */
32 /*****************************************************************************/
33
34
35
36 #include <stdio.h>
37 #include <string.h>
38 #include <limits.h>
39 #include <errno.h>
40 #include <time.h>
41
42 /* common */
43 #include "chartype.h"
44 #include "check.h"
45 #include "print.h"
46 #include "version.h"
47 #include "xmalloc.h"
48
49 /* ld65 */
50 #include "error.h"
51 #include "exports.h"
52 #include "expr.h"
53 #include "fileio.h"
54 #include "global.h"
55 #include "lineinfo.h"
56 #include "memarea.h"
57 #include "o65.h"
58 #include "spool.h"
59
60
61
62 /*****************************************************************************/
63 /*                                   Data                                    */
64 /*****************************************************************************/
65
66
67
68 /* Header mode bits */
69 #define MF_CPU_65816    0x8000          /* Executable is for 65816 */
70 #define MF_CPU_6502     0x0000          /* Executable is for the 6502 */
71 #define MF_CPU_MASK     0x8000          /* Mask to extract CPU type */
72
73 #define MF_RELOC_PAGE   0x4000          /* Page wise relocation */
74 #define MF_RELOC_BYTE   0x0000          /* Byte wise relocation */
75 #define MF_RELOC_MASK   0x4000          /* Mask to extract relocation type */
76
77 #define MF_SIZE_32BIT   0x2000          /* All size words are 32bit */
78 #define MF_SIZE_16BIT   0x0000          /* All size words are 16bit */
79 #define MF_SIZE_MASK    0x2000          /* Mask to extract size */
80
81 #define MF_FTYPE_OBJ    0x1000          /* Object file */
82 #define MF_FTYPE_EXE    0x0000          /* Executable file */
83 #define MF_FTYPE_MASK   0x1000          /* Mask to extract type */
84
85 #define MF_ADDR_SIMPLE  0x0800          /* Simple addressing */
86 #define MF_ADDR_DEFAULT 0x0000          /* Default addressing */
87 #define MF_ADDR_MASK    0x0800          /* Mask to extract addressing */
88
89 #define MF_ALIGN_1      0x0000          /* Bytewise alignment */
90 #define MF_ALIGN_2      0x0001          /* Align words */
91 #define MF_ALIGN_4      0x0002          /* Align longwords */
92 #define MF_ALIGN_256    0x0003          /* Align pages (256 bytes) */
93 #define MF_ALIGN_MASK   0x0003          /* Mask to extract alignment */
94
95 /* The four o65 segment types. Note: These values are identical to the values
96  * needed for the segmentID in the o65 spec.
97  */
98 #define O65SEG_UNDEF    0x00
99 #define O65SEG_ABS      0x01
100 #define O65SEG_TEXT     0x02
101 #define O65SEG_DATA     0x03
102 #define O65SEG_BSS      0x04
103 #define O65SEG_ZP       0x05
104
105 /* Relocation type codes for the o65 format */
106 #define O65RELOC_WORD   0x80
107 #define O65RELOC_HIGH   0x40
108 #define O65RELOC_LOW    0x20
109 #define O65RELOC_SEGADR 0xC0
110 #define O65RELOC_SEG    0xA0
111 #define O65RELOC_MASK   0xE0
112
113 /* O65 executable file header */
114 typedef struct O65Header O65Header;
115 struct O65Header {
116     unsigned        Version;            /* Version number for o65 format */
117     unsigned        Mode;               /* Mode word */
118     unsigned long   TextBase;           /* Base address of text segment */
119     unsigned long   TextSize;           /* Size of text segment */
120     unsigned long   DataBase;           /* Base of data segment */
121     unsigned long   DataSize;           /* Size of data segment */
122     unsigned long   BssBase;            /* Base of bss segment */
123     unsigned long   BssSize;            /* Size of bss segment */
124     unsigned long   ZPBase;             /* Base of zeropage segment */
125     unsigned long   ZPSize;             /* Size of zeropage segment */
126     unsigned long   StackSize;          /* Requested stack size */
127 };
128
129 /* An o65 option */
130 typedef struct O65Option O65Option;
131 struct O65Option {
132     O65Option*      Next;               /* Next in option list */
133     unsigned char   Type;               /* Type of option */
134     unsigned char   Len;                /* Data length */
135     unsigned char   Data [1];           /* Data, dynamically allocated */
136 };
137
138 /* A o65 relocation table */
139 typedef struct O65RelocTab O65RelocTab;
140 struct O65RelocTab {
141     unsigned        Size;               /* Size of the table */
142     unsigned        Fill;               /* Amount used */
143     unsigned char*  Buf;                /* Buffer, dynamically allocated */
144 };
145
146 /* Structure describing the format */
147 struct O65Desc {
148     O65Header       Header;             /* File header */
149     O65Option*      Options;            /* List of file options */
150     ExtSymTab*      Exports;            /* Table with exported symbols */
151     ExtSymTab*      Imports;            /* Table with imported symbols */
152     unsigned        Undef;              /* Count of undefined symbols */
153     FILE*           F;                  /* The file we're writing to */
154     const char*     Filename;           /* Name of the output file */
155     O65RelocTab*    TextReloc;          /* Relocation table for text segment */
156     O65RelocTab*    DataReloc;          /* Relocation table for data segment */
157
158     unsigned        TextCount;          /* Number of segments assigned to .text */
159     SegDesc**       TextSeg;            /* Array of text segments */
160     unsigned        DataCount;          /* Number of segments assigned to .data */
161     SegDesc**       DataSeg;            /* Array of data segments */
162     unsigned        BssCount;           /* Number of segments assigned to .bss */
163     SegDesc**       BssSeg;             /* Array of bss segments */
164     unsigned        ZPCount;            /* Number of segments assigned to .zp */
165     SegDesc**       ZPSeg;              /* Array of zp segments */
166
167     /* Temporary data for writing segments */
168     unsigned long   SegSize;
169     O65RelocTab*    CurReloc;
170     long            LastOffs;
171 };
172
173 /* Structure for parsing expression trees */
174 typedef struct ExprDesc ExprDesc;
175 struct ExprDesc {
176     O65Desc*        D;                  /* File format descriptor */
177     long            Val;                /* The offset value */
178     int             TooComplex;         /* Expression too complex */
179     MemoryArea*     MemRef;             /* Memory reference if any */
180     Segment*        SegRef;             /* Segment reference if any */
181     Section*        SecRef;             /* Section reference if any */
182     ExtSym*         ExtRef;             /* External reference if any */
183 };
184
185
186
187 /*****************************************************************************/
188 /*                             Helper functions                              */
189 /*****************************************************************************/
190
191
192
193 static ExprDesc* InitExprDesc (ExprDesc* ED, O65Desc* D)
194 /* Initialize an ExprDesc structure for use with O65ParseExpr */
195 {
196     ED->D          = D;
197     ED->Val        = 0;
198     ED->TooComplex = 0;
199     ED->MemRef     = 0;
200     ED->SegRef     = 0;
201     ED->SecRef     = 0;
202     ED->ExtRef     = 0;
203     return ED;
204 }
205
206
207
208 static void WriteSize (const O65Desc* D, unsigned long Val)
209 /* Write a "size" word to the file */
210 {
211     switch (D->Header.Mode & MF_SIZE_MASK) {
212         case MF_SIZE_16BIT:     Write16 (D->F, (unsigned) Val); break;
213         case MF_SIZE_32BIT:     Write32 (D->F, Val);            break;
214         default:                Internal ("Invalid size in header: %04X", D->Header.Mode);
215     }
216 }
217
218
219
220 static unsigned O65SegType (const SegDesc* S)
221 /* Map our own segment types into something o65 compatible */
222 {
223     /* Check the segment type. Readonly segments are assign to the o65
224      * text segment, writeable segments that contain data are assigned
225      * to data, bss and zp segments are handled respectively.
226      * Beware: Zeropage segments have the SF_BSS flag set, so be sure
227      * to check SF_ZP first.
228      */
229     if (S->Flags & SF_RO) {
230         return O65SEG_TEXT;
231     } else if (S->Flags & SF_ZP) {
232         return O65SEG_ZP;
233     } else if (S->Flags & SF_BSS) {
234         return O65SEG_BSS;
235     } else {
236         return O65SEG_DATA;
237     }
238 }
239
240
241
242 static void CvtMemoryToSegment (ExprDesc* ED)
243 /* Convert a memory area into a segment by searching the list of run segments
244  * in this memory area and assigning the nearest one.
245  */
246 {
247     /* Get the memory area from the expression */
248     MemoryArea* M = ED->MemRef;
249
250     /* Remember the "nearest" segment and its offset */
251     Segment* Nearest   = 0;
252     unsigned long Offs = ULONG_MAX;
253
254     /* Walk over all segments */
255     unsigned I;
256     for (I = 0; I < CollCount (&M->SegList); ++I) {
257
258         /* Get the segment and check if it's a run segment */
259         SegDesc* S = CollAtUnchecked (&M->SegList, I);
260         if (S->Run == M) {
261
262             unsigned long O;
263
264             /* Get the segment from the segment descriptor */
265             Segment* Seg = S->Seg;
266
267             /* Check the PC. */
268             if ((long) Seg->PC <= ED->Val && (O = (ED->Val - Seg->PC)) < Offs) {
269                 /* This is the nearest segment for now */
270                 Offs = O;
271                 Nearest = Seg;
272
273                 /* If we found an exact match, don't look further */
274                 if (Offs == 0) {
275                     break;
276                 }
277             }
278         }
279     }
280
281     /* If we found a segment, use it and adjust the offset */
282     if (Nearest) {
283         ED->SegRef = Nearest;
284         ED->MemRef = 0;
285         ED->Val    -= Nearest->PC;
286     }
287 }
288
289
290
291 static const SegDesc* FindSeg (SegDesc** const List, unsigned Count, const Segment* S)
292 /* Search for a segment in the given list of segment descriptors and return
293  * the descriptor for a segment if we found it, and NULL if not.
294  */
295 {
296     unsigned I;
297
298     for (I = 0; I < Count; ++I) {
299         if (List[I]->Seg == S) {
300             /* Found */
301             return List[I];
302         }
303     }
304
305     /* Not found */
306     return 0;
307 }
308
309
310
311 static const SegDesc* O65FindSeg (const O65Desc* D, const Segment* S)
312 /* Search for a segment in the segment lists and return it's segment descriptor */
313 {
314     const SegDesc* SD;
315
316     if ((SD = FindSeg (D->TextSeg, D->TextCount, S)) != 0) {
317         return SD;
318     }
319     if ((SD = FindSeg (D->DataSeg, D->DataCount, S)) != 0) {
320         return SD;
321     }
322     if ((SD = FindSeg (D->BssSeg, D->BssCount, S)) != 0) {
323         return SD;
324     }
325     if ((SD = FindSeg (D->ZPSeg, D->ZPCount, S)) != 0) {
326         return SD;
327     }
328
329     /* Not found */
330     return 0;
331 }
332
333
334
335 /*****************************************************************************/
336 /*                            Expression handling                            */
337 /*****************************************************************************/
338
339
340
341 static void O65ParseExpr (ExprNode* Expr, ExprDesc* D, int Sign)
342 /* Extract and evaluate all constant factors in an subtree that has only
343  * additions and subtractions. If anything other than additions and
344  * subtractions are found, D->TooComplex is set to true.
345  */
346 {
347     Export* E;
348
349     switch (Expr->Op) {
350
351         case EXPR_LITERAL:
352             D->Val += (Sign * Expr->V.IVal);
353             break;
354
355         case EXPR_SYMBOL:
356             /* Get the referenced Export */
357             E = GetExprExport (Expr);
358             /* If this export has a mark set, we've already encountered it.
359              * This means that the export is used to define it's own value,
360              * which in turn means, that we have a circular reference.
361              */
362             if (ExportHasMark (E)) {
363                 CircularRefError (E);
364             } else if (E->Expr == 0) {
365                 /* Dummy export, must be an o65 imported symbol */
366                 ExtSym* S = O65GetImport (D->D, E->Name);
367                 CHECK (S != 0);
368                 if (D->ExtRef) {
369                     /* We cannot have more than one external reference in o65 */
370                     D->TooComplex = 1;
371                 } else {
372                     /* Remember the external reference */
373                     D->ExtRef = S;
374                 }
375             } else {
376                 MarkExport (E);
377                 O65ParseExpr (E->Expr, D, Sign);
378                 UnmarkExport (E);
379             }
380             break;
381
382         case EXPR_SECTION:
383             if (D->SecRef) {
384                 /* We cannot handle more than one segment reference in o65 */
385                 D->TooComplex = 1;
386             } else {
387                 /* Remember the segment reference */
388                 D->SecRef = GetExprSection (Expr);
389                 /* Add the offset of the section to the constant value */
390                 D->Val += Sign * (D->SecRef->Offs + D->SecRef->Seg->PC);
391             }
392             break;
393
394         case EXPR_SEGMENT:
395             if (D->SegRef) {
396                 /* We cannot handle more than one segment reference in o65 */
397                 D->TooComplex = 1;
398             } else {
399                 /* Remember the segment reference */
400                 D->SegRef = Expr->V.Seg;
401                 /* Add the offset of the segment to the constant value */
402                 D->Val += (Sign * D->SegRef->PC);
403             }
404             break;
405
406         case EXPR_MEMAREA:
407             if (D->MemRef) {
408                 /* We cannot handle more than one memory reference in o65 */
409                 D->TooComplex = 1;
410             } else {
411                 /* Remember the memory area reference */
412                 D->MemRef = Expr->V.Mem;
413                 /* Add the start address of the memory area to the constant
414                  * value
415                  */
416                 D->Val += (Sign * D->MemRef->Start);
417             }
418             break;
419
420         case EXPR_PLUS:
421             O65ParseExpr (Expr->Left, D, Sign);
422             O65ParseExpr (Expr->Right, D, Sign);
423             break;
424
425         case EXPR_MINUS:
426             O65ParseExpr (Expr->Left, D, Sign);
427             O65ParseExpr (Expr->Right, D, -Sign);
428             break;
429
430         default:
431             /* Expression contains illegal operators */
432             D->TooComplex = 1;
433             break;
434
435     }
436 }
437
438
439
440 /*****************************************************************************/
441 /*                             Relocation tables                             */
442 /*****************************************************************************/
443
444
445
446 static O65RelocTab* NewO65RelocTab (void)
447 /* Create a new relocation table */
448 {
449     /* Allocate a new structure */
450     O65RelocTab* R = xmalloc (sizeof (O65RelocTab));
451
452     /* Initialize the data */
453     R->Size = 0;
454     R->Fill = 0;
455     R->Buf  = 0;
456
457     /* Return the created struct */
458     return R;
459 }
460
461
462
463 static void FreeO65RelocTab (O65RelocTab* R)
464 /* Free a relocation table */
465 {
466     xfree (R->Buf);
467     xfree (R);
468 }
469
470
471
472 static void O65RelocPutByte (O65RelocTab* R, unsigned B)
473 /* Put the byte into the relocation table */
474 {
475     /* Do we have enough space in the buffer? */
476     if (R->Fill == R->Size) {
477         /* We need to grow the buffer */
478         if (R->Size) {
479             R->Size *= 2;
480         } else {
481             R->Size = 1024;     /* Initial size */
482         }
483         R->Buf = xrealloc (R->Buf, R->Size);
484     }
485
486     /* Put the byte into the buffer */
487     R->Buf [R->Fill++] = (unsigned char) B;
488 }
489
490
491
492 static void O65RelocPutWord (O65RelocTab* R, unsigned W)
493 /* Put a word into the relocation table */
494 {
495     O65RelocPutByte (R, W);
496     O65RelocPutByte (R, W >> 8);
497 }
498
499
500
501 static void O65WriteReloc (O65RelocTab* R, FILE* F)
502 /* Write the relocation table to the given file */
503 {
504     WriteData (F, R->Buf, R->Fill);
505 }
506
507
508
509 /*****************************************************************************/
510 /*                              Option handling                              */
511 /*****************************************************************************/
512
513
514
515 static O65Option* NewO65Option (unsigned Type, const void* Data, unsigned DataLen)
516 /* Allocate and initialize a new option struct */
517 {
518     O65Option* O;
519
520     /* Check the length */
521     CHECK (DataLen <= 253);
522
523     /* Allocate memory */
524     O = xmalloc (sizeof (O65Option) - 1 + DataLen);
525
526     /* Initialize the structure */
527     O->Next     = 0;
528     O->Type     = Type;
529     O->Len      = DataLen;
530     memcpy (O->Data, Data, DataLen);
531
532     /* Return the created struct */
533     return O;
534 }
535
536
537
538 static void FreeO65Option (O65Option* O)
539 /* Free an O65Option struct */
540 {
541     xfree (O);
542 }
543
544
545
546 /*****************************************************************************/
547 /*                     Subroutines to write o65 sections                     */
548 /*****************************************************************************/
549
550
551
552 static void O65WriteHeader (O65Desc* D)
553 /* Write the header of the executable to the given file */
554 {
555     static unsigned char Trailer [5] = {
556         0x01, 0x00, 0x6F, 0x36, 0x35
557     };
558
559     O65Option* O;
560
561     /* Write the fixed header */
562     WriteData (D->F, Trailer, sizeof (Trailer));
563     Write8    (D->F, D->Header.Version);
564     Write16   (D->F, D->Header.Mode);
565     WriteSize (D, D->Header.TextBase);
566     WriteSize (D, D->Header.TextSize);
567     WriteSize (D, D->Header.DataBase);
568     WriteSize (D, D->Header.DataSize);
569     WriteSize (D, D->Header.BssBase);
570     WriteSize (D, D->Header.BssSize);
571     WriteSize (D, D->Header.ZPBase);
572     WriteSize (D, D->Header.ZPSize);
573     WriteSize (D, D->Header.StackSize);
574
575     /* Write the options */
576     O = D->Options;
577     while (O) {
578         Write8 (D->F, O->Len + 2);              /* Account for len and type bytes */
579         Write8 (D->F, O->Type);
580         if (O->Len) {
581             WriteData (D->F, O->Data, O->Len);
582         }
583         O = O->Next;
584     }
585
586     /* Write the end-of-options byte */
587     Write8 (D->F, 0);
588 }
589
590
591
592 static unsigned O65WriteExpr (ExprNode* E, int Signed, unsigned Size,
593                               unsigned long Offs, void* Data)
594 /* Called from SegWrite for an expression. Evaluate the expression, check the
595  * range and write the expression value to the file, update the relocation
596  * table.
597  */
598 {
599     long          Diff;
600     unsigned      RefCount;
601     long          BinVal;
602     ExprNode*     Expr;
603     ExprDesc      ED;
604     unsigned char RelocType;
605
606     /* Cast the Data pointer to its real type, an O65Desc */
607     O65Desc* D = (O65Desc*) Data;
608
609     /* Check for a constant expression */
610     if (IsConstExpr (E)) {
611         /* Write out the constant expression */
612         return SegWriteConstExpr (((O65Desc*)Data)->F, E, Signed, Size);
613     }
614
615     /* We have a relocatable expression that needs a relocation table entry.
616      * Calculate the number of bytes between this entry and the last one, and
617      * setup all necessary intermediate bytes in the relocation table.
618      */
619     Offs += D->SegSize;         /* Calulate full offset */
620     Diff = ((long) Offs) - D->LastOffs;
621     while (Diff > 0xFE) {
622         O65RelocPutByte (D->CurReloc, 0xFF);
623         Diff -= 0xFE;
624     }
625     O65RelocPutByte (D->CurReloc, (unsigned char) Diff);
626
627     /* Remember this offset for the next time */
628     D->LastOffs = Offs;
629
630     /* Determine the expression to relocate */
631     Expr = E;
632     if (E->Op == EXPR_BYTE0 || E->Op == EXPR_BYTE1 ||
633         E->Op == EXPR_BYTE2 || E->Op == EXPR_BYTE3 ||
634         E->Op == EXPR_WORD0 || E->Op == EXPR_WORD1) {
635         /* Use the real expression */
636         Expr = E->Left;
637     }
638
639     /* Recursively collect information about this expression */
640     O65ParseExpr (Expr, InitExprDesc (&ED, D), 1);
641
642     /* We cannot handle more than one external reference */
643     RefCount = (ED.MemRef != 0) + (ED.SegRef != 0) +
644                (ED.SecRef != 0) + (ED.ExtRef != 0);
645     if (RefCount > 1) {
646         ED.TooComplex = 1;
647     }
648
649     /* If we have a memory area reference, we need to convert it into a
650      * segment reference. If we cannot do that, we cannot handle the
651      * expression.
652      */
653     if (ED.MemRef) {
654         CvtMemoryToSegment (&ED);
655         if (ED.SegRef == 0) {
656             return SEG_EXPR_TOO_COMPLEX;
657         }
658     }
659
660     /* Bail out if we cannot handle the expression */
661     if (ED.TooComplex) {
662         return SEG_EXPR_TOO_COMPLEX;
663     }
664
665     /* Safety: Check that we have exactly one reference */
666     CHECK (RefCount == 1);
667
668     /* Write out the offset that goes into the segment. */
669     BinVal = ED.Val;
670     switch (E->Op) {
671         case EXPR_BYTE0:    BinVal &= 0xFF;                     break;
672         case EXPR_BYTE1:    BinVal = (BinVal >>  8) & 0xFF;     break;
673         case EXPR_BYTE2:    BinVal = (BinVal >> 16) & 0xFF;     break;
674         case EXPR_BYTE3:    BinVal = (BinVal >> 24) & 0xFF;     break;
675         case EXPR_WORD0:    BinVal &= 0xFFFF;                   break;
676         case EXPR_WORD1:    BinVal = (BinVal >> 16) & 0xFFFF;   break;
677     }
678     WriteVal (D->F, BinVal, Size);
679
680     /* Determine the actual type of relocation entry needed from the
681      * information gathered about the expression.
682      */
683     if (E->Op == EXPR_BYTE0) {
684         RelocType = O65RELOC_LOW;
685     } else if (E->Op == EXPR_BYTE1) {
686         RelocType = O65RELOC_HIGH;
687     } else if (E->Op == EXPR_BYTE2) {
688         RelocType = O65RELOC_SEG;
689     } else {
690         switch (Size) {
691
692             case 1:
693                 RelocType = O65RELOC_LOW;
694                 break;
695
696             case 2:
697                 RelocType = O65RELOC_WORD;
698                 break;
699
700             case 3:
701                 RelocType = O65RELOC_SEGADR;
702                 break;
703
704             case 4:
705                 /* 4 byte expression not supported by o65 */
706                 return SEG_EXPR_TOO_COMPLEX;
707
708             default:
709                 Internal ("O65WriteExpr: Invalid expression size: %u", Size);
710                 RelocType = 0;          /* Avoid gcc warnings */
711         }
712     }
713
714     /* Determine which segment we're referencing */
715     if (ED.SegRef || ED.SecRef) {
716
717         const SegDesc* Seg;
718
719         /* Segment or section reference. */
720         if (ED.SecRef) {
721             /* Get segment from section */
722             ED.SegRef = ED.SecRef->Seg;
723         }
724
725         /* Search for the segment and map it to it's o65 segmentID */
726         Seg = O65FindSeg (D, ED.SegRef);
727         if (Seg == 0) {
728             /* For some reason, we didn't find this segment in the list of
729              * segments written to the o65 file.
730              */
731             return SEG_EXPR_INVALID;
732         }
733         RelocType |= O65SegType (Seg);
734         O65RelocPutByte (D->CurReloc, RelocType);
735
736         /* Output additional data if needed */
737         switch (RelocType & O65RELOC_MASK) {
738             case O65RELOC_HIGH:
739                 O65RelocPutByte (D->CurReloc, ED.Val & 0xFF);
740                 break;
741             case O65RELOC_SEG:
742                 O65RelocPutWord (D->CurReloc, ED.Val & 0xFFFF);
743                 break;
744         }
745
746     } else if (ED.ExtRef) {
747         /* Imported symbol */
748         RelocType |= O65SEG_UNDEF;
749         O65RelocPutByte (D->CurReloc, RelocType);
750         /* Put the number of the imported symbol into the table */
751         O65RelocPutWord (D->CurReloc, ExtSymNum (ED.ExtRef));
752
753     } else {
754
755         /* OOPS - something bad happened */
756         Internal ("External reference not handled");
757
758     }
759
760     /* Success */
761     return SEG_EXPR_OK;
762 }
763
764
765
766 static void O65WriteSeg (O65Desc* D, SegDesc** Seg, unsigned Count, int DoWrite)
767 /* Write one segment to the o65 output file */
768 {
769     SegDesc* S;
770     unsigned I;
771
772     /* Initialize variables */
773     D->SegSize  = 0;
774     D->LastOffs = -1;
775
776     /* Write out all segments */
777     for (I = 0; I < Count; ++I) {
778
779         /* Get the segment from the list node */
780         S = Seg [I];
781
782         /* Keep the user happy */
783         Print (stdout, 1, "    Writing `%s'\n", GetString (S->Name));
784
785         /* Write this segment */
786         if (DoWrite) {
787             SegWrite (D->Filename, D->F, S->Seg, O65WriteExpr, D);
788         }
789
790         /* Mark the segment as dumped */
791         S->Seg->Dumped = 1;
792
793         /* Calculate the total size */
794         D->SegSize += S->Seg->Size;
795     }
796
797     /* Terminate the relocation table for this segment */
798     if (D->CurReloc) {
799         O65RelocPutByte (D->CurReloc, 0);
800     }
801
802     /* Check the size of the segment for overflow */
803     if ((D->Header.Mode & MF_SIZE_MASK) == MF_SIZE_16BIT && D->SegSize > 0xFFFF) {
804         Error ("Segment overflow in file `%s'", D->Filename);
805     }
806
807 }
808
809
810
811 static void O65WriteTextSeg (O65Desc* D)
812 /* Write the code segment to the o65 output file */
813 {
814     /* Initialize variables */
815     D->CurReloc = D->TextReloc;
816
817     /* Dump all text segments */
818     O65WriteSeg (D, D->TextSeg, D->TextCount, 1);
819
820     /* Set the size of the segment */
821     D->Header.TextSize = D->SegSize;
822 }
823
824
825
826 static void O65WriteDataSeg (O65Desc* D)
827 /* Write the data segment to the o65 output file */
828 {
829     /* Initialize variables */
830     D->CurReloc = D->DataReloc;
831
832     /* Dump all data segments */
833     O65WriteSeg (D, D->DataSeg, D->DataCount, 1);
834
835     /* Set the size of the segment */
836     D->Header.DataSize = D->SegSize;
837 }
838
839
840
841 static void O65WriteBssSeg (O65Desc* D)
842 /* "Write" the bss segments to the o65 output file. This will only update
843  * the relevant header fields.
844  */
845 {
846     /* Initialize variables */
847     D->CurReloc = 0;
848
849     /* Dump all bss segments */               
850     O65WriteSeg (D, D->BssSeg, D->BssCount, 0);
851
852     /* Set the size of the segment */
853     D->Header.BssSize = D->SegSize;
854 }
855
856
857
858 static void O65WriteZPSeg (O65Desc* D)
859 /* "Write" the zeropage segments to the o65 output file. This will only update
860  * the relevant header fields.
861  */
862 {
863     /* Initialize variables */
864     D->CurReloc = 0;
865
866     /* Dump all zp segments */
867     O65WriteSeg (D, D->ZPSeg, D->ZPCount, 0);
868
869     /* Set the size of the segment */
870     D->Header.ZPSize = D->SegSize;
871 }
872
873
874
875 static void O65WriteImports (O65Desc* D)
876 /* Write the list of imported symbols to the O65 file */
877 {
878     const ExtSym* S;
879
880     /* Write the number of imports */
881     WriteSize (D, ExtSymCount (D->Imports));
882
883     /* Write out the symbol names, zero terminated */
884     S = ExtSymList (D->Imports);
885     while (S) {
886         /* Get the name */
887         const char* Name = GetString (ExtSymName (S));
888         /* And write it to the output file */
889         WriteData (D->F, Name, strlen (Name) + 1);
890         /* Next symbol */
891         S = ExtSymNext (S);
892     }
893 }
894
895
896
897 static void O65WriteTextReloc (O65Desc* D)
898 /* Write the relocation for the text segment to the output file */
899 {
900     O65WriteReloc (D->TextReloc, D->F);
901 }
902
903
904
905 static void O65WriteDataReloc (O65Desc* D)
906 /* Write the relocation for the data segment to the output file */
907 {
908     O65WriteReloc (D->DataReloc, D->F);
909 }
910
911
912
913 static void O65WriteExports (O65Desc* D)
914 /* Write the list of exports */
915 {
916     const ExtSym* S;
917
918     /* Write the number of exports */
919     WriteSize (D, ExtSymCount (D->Exports));
920
921     /* Write out the symbol information */
922     S = ExtSymList (D->Exports);
923     while (S) {
924
925         ExprNode* Expr;
926         unsigned char SegmentID;
927         ExprDesc ED;
928
929         /* Get the name */
930         unsigned NameIdx = ExtSymName (S);
931         const char* Name = GetString (NameIdx);
932
933         /* Get the export for this symbol. We've checked before that this
934          * export does really exist, so if it is unresolved, or if we don't
935          * find it, there is an error in the linker code.
936          */
937         Export* E = FindExport (NameIdx);
938         if (E == 0 || IsUnresolvedExport (E)) {
939             Internal ("Unresolved export `%s' found in O65WriteExports", Name);
940         }
941
942         /* Get the expression for the symbol */
943         Expr = E->Expr;
944
945         /* Recursively collect information about this expression */
946         O65ParseExpr (Expr, InitExprDesc (&ED, D), 1);
947
948         /* We cannot handle expressions with imported symbols, or expressions
949          * with more than one segment reference here
950          */
951         if (ED.ExtRef != 0 || (ED.SegRef != 0 && ED.SecRef != 0)) {
952             ED.TooComplex = 1;
953         }
954
955         /* Bail out if we cannot handle the expression */
956         if (ED.TooComplex) {
957             Error ("Expression for symbol `%s' is too complex", Name);
958         }
959
960         /* Determine the segment id for the expression */
961         if (ED.SegRef != 0 || ED.SecRef != 0) {
962
963             const SegDesc* Seg;
964
965             /* Segment or section reference */
966             if (ED.SecRef != 0) {
967                 ED.SegRef = ED.SecRef->Seg;     /* Get segment from section */
968             }
969
970             /* Search for the segment and map it to it's o65 segmentID */
971             Seg = O65FindSeg (D, ED.SegRef);
972             if (Seg == 0) {
973                 /* For some reason, we didn't find this segment in the list of
974                  * segments written to the o65 file.
975                  */
976                 Error ("Segment for symbol `%s' is undefined", Name);
977             }
978             SegmentID = O65SegType (Seg);
979
980         } else {
981
982             /* Absolute value */
983             SegmentID = O65SEG_ABS;
984
985         }
986
987         /* Write the name to the output file */
988         WriteData (D->F, Name, strlen (Name) + 1);
989
990         /* Output the segment id followed by the literal value */
991         Write8 (D->F, SegmentID);
992         WriteSize (D, ED.Val);
993
994         /* Next symbol */
995         S = ExtSymNext (S);
996     }
997 }
998
999
1000
1001 /*****************************************************************************/
1002 /*                                Public code                                */
1003 /*****************************************************************************/
1004
1005
1006
1007 O65Desc* NewO65Desc (void)
1008 /* Create, initialize and return a new O65 descriptor struct */
1009 {
1010     /* Allocate a new structure */
1011     O65Desc* D = xmalloc (sizeof (O65Desc));
1012
1013     /* Initialize the header */
1014     D->Header.Version   = 0;
1015     D->Header.Mode      = 0;
1016     D->Header.TextBase  = 0;
1017     D->Header.TextSize  = 0;
1018     D->Header.DataBase  = 0;
1019     D->Header.DataSize  = 0;
1020     D->Header.BssBase   = 0;
1021     D->Header.BssSize   = 0;
1022     D->Header.ZPBase    = 0;
1023     D->Header.ZPSize    = 0;
1024     D->Header.StackSize = 0;            /* Let OS choose a good value */
1025
1026     /* Initialize other data */
1027     D->Options          = 0;
1028     D->Exports          = NewExtSymTab ();
1029     D->Imports          = NewExtSymTab ();
1030     D->Undef            = 0;
1031     D->F                = 0;
1032     D->Filename         = 0;
1033     D->TextReloc        = NewO65RelocTab ();
1034     D->DataReloc        = NewO65RelocTab ();
1035     D->TextCount        = 0;
1036     D->TextSeg          = 0;
1037     D->DataCount        = 0;
1038     D->DataSeg          = 0;
1039     D->BssCount         = 0;
1040     D->BssSeg           = 0;
1041     D->ZPCount          = 0;
1042     D->ZPSeg            = 0;
1043
1044     /* Return the created struct */
1045     return D;
1046 }
1047
1048
1049
1050 void FreeO65Desc (O65Desc* D)
1051 /* Delete the descriptor struct with cleanup */
1052 {
1053     /* Free the segment arrays */
1054     xfree (D->ZPSeg);
1055     xfree (D->BssSeg);
1056     xfree (D->DataSeg);
1057     xfree (D->TextSeg);
1058
1059     /* Free the relocation tables */
1060     FreeO65RelocTab (D->DataReloc);
1061     FreeO65RelocTab (D->TextReloc);
1062
1063     /* Free the option list */
1064     while (D->Options) {
1065         O65Option* O = D->Options;
1066         D->Options = D->Options->Next;
1067         FreeO65Option (O);
1068     }
1069
1070     /* Free the external symbol tables */
1071     FreeExtSymTab (D->Exports);
1072     FreeExtSymTab (D->Imports);
1073
1074     /* Free the struct itself */
1075     xfree (D);
1076 }
1077
1078
1079
1080 void O65Set6502 (O65Desc* D)
1081 /* Enable 6502 mode */
1082 {
1083     D->Header.Mode = (D->Header.Mode & ~MF_CPU_MASK) | MF_CPU_6502;
1084 }
1085
1086
1087
1088 void O65Set65816 (O65Desc* D)
1089 /* Enable 816 mode */
1090 {
1091     D->Header.Mode = (D->Header.Mode & ~MF_CPU_MASK) | MF_CPU_65816;
1092 }
1093
1094
1095
1096 void O65SetSmallModel (O65Desc* D)
1097 /* Enable a small memory model executable */
1098 {
1099     D->Header.Mode = (D->Header.Mode & ~MF_SIZE_MASK) | MF_SIZE_16BIT;
1100 }
1101
1102
1103
1104 void O65SetLargeModel (O65Desc* D)
1105 /* Enable a large memory model executable */
1106 {
1107     D->Header.Mode = (D->Header.Mode & ~MF_SIZE_MASK) | MF_SIZE_32BIT;
1108 }
1109
1110
1111
1112 void O65SetAlignment (O65Desc* D, unsigned Alignment)
1113 /* Set the executable alignment */
1114 {
1115     /* Remove all alignment bits from the mode word */
1116     D->Header.Mode &= ~MF_ALIGN_MASK;
1117
1118     /* Set the alignment bits */
1119     switch (Alignment) {
1120         case 1:   D->Header.Mode |= MF_ALIGN_1;   break;
1121         case 2:   D->Header.Mode |= MF_ALIGN_2;   break;
1122         case 4:   D->Header.Mode |= MF_ALIGN_4;   break;
1123         case 256: D->Header.Mode |= MF_ALIGN_256; break;
1124         default:  Error ("Invalid alignment for O65 format: %u", Alignment);
1125     }
1126 }
1127
1128
1129
1130 void O65SetOption (O65Desc* D, unsigned Type, const void* Data, unsigned DataLen)
1131 /* Set an o65 header option */
1132 {
1133     /* Create a new option structure */
1134     O65Option* O = NewO65Option (Type, Data, DataLen);
1135
1136     /* Insert it into the linked list */
1137     O->Next = D->Options;
1138     D->Options = O;
1139 }
1140
1141
1142
1143 void O65SetOS (O65Desc* D, unsigned OS, unsigned Version, unsigned Id)
1144 /* Set an option describing the target operating system */
1145 {
1146     /* Setup the option data */
1147     unsigned char Opt[4];
1148     Opt[0] = OS;
1149     Opt[1] = Version;
1150
1151     /* Write the correct option length */
1152     switch (OS) {
1153
1154         case O65OS_CC65:
1155             /* Set the 16 bit id */
1156             Opt[2] = (unsigned char) Id;
1157             Opt[3] = (unsigned char) (Id >> 8);
1158             O65SetOption (D, O65OPT_OS, Opt, 4);
1159             break;
1160
1161         default:
1162             /* No id for OS/A65, Lunix, and unknown OSes */
1163             O65SetOption (D, O65OPT_OS, Opt, 2);
1164             break;
1165
1166     }
1167 }
1168
1169
1170
1171 ExtSym* O65GetImport (O65Desc* D, unsigned Ident)
1172 /* Return the imported symbol or NULL if not found */
1173 {
1174     /* Retrieve the symbol from the table */
1175     return GetExtSym (D->Imports, Ident);
1176 }
1177
1178
1179
1180 void O65SetImport (O65Desc* D, unsigned Ident)
1181 /* Set an imported identifier */
1182 {
1183     /* Insert the entry into the table */
1184     NewExtSym (D->Imports, Ident);
1185 }
1186
1187
1188
1189 ExtSym* O65GetExport (O65Desc* D, unsigned Ident)
1190 /* Return the exported symbol or NULL if not found */
1191 {
1192     /* Retrieve the symbol from the table */
1193     return GetExtSym (D->Exports, Ident);
1194 }
1195
1196
1197
1198 void O65SetExport (O65Desc* D, unsigned Ident)
1199 /* Set an exported identifier */
1200 {
1201     /* Get the export for this symbol and check if it does exist and is
1202      * a resolved symbol.
1203      */
1204     Export* E = FindExport (Ident);
1205     if (E == 0 || IsUnresolvedExport (E)) {
1206         Error ("Unresolved export: `%s'", GetString (Ident));
1207     }
1208
1209     /* Insert the entry into the table */
1210     NewExtSym (D->Exports, Ident);
1211 }
1212
1213
1214
1215 static void O65SetupSegments (O65Desc* D, File* F)
1216 /* Setup segment assignments */
1217 {
1218     unsigned I;
1219     unsigned TextIdx, DataIdx, BssIdx, ZPIdx;
1220
1221     /* Initialize the counters */
1222     D->TextCount = 0;
1223     D->DataCount = 0;
1224     D->BssCount  = 0;
1225     D->ZPCount   = 0;
1226
1227     /* Walk over the memory list */
1228     for (I = 0; I < CollCount (&F->MemoryAreas); ++I) {
1229         /* Get this entry */
1230         MemoryArea* M = CollAtUnchecked (&F->MemoryAreas, I);
1231
1232         /* Walk through the segment list and count the segment types */
1233         unsigned J;
1234         for (J = 0; J < CollCount (&M->SegList); ++J) {
1235
1236             /* Get the segment */
1237             SegDesc* S = CollAtUnchecked (&M->SegList, J);
1238
1239             /* Check the segment type. */
1240             switch (O65SegType (S)) {
1241                 case O65SEG_TEXT:   D->TextCount++; break;
1242                 case O65SEG_DATA:   D->DataCount++; break;
1243                 case O65SEG_BSS:    D->BssCount++;  break;
1244                 case O65SEG_ZP:     D->ZPCount++;   break;
1245                 default:            Internal ("Invalid return from O65SegType");
1246             }
1247         }
1248     }
1249
1250     /* Allocate memory according to the numbers */
1251     D->TextSeg = xmalloc (D->TextCount * sizeof (SegDesc*));
1252     D->DataSeg = xmalloc (D->DataCount * sizeof (SegDesc*));
1253     D->BssSeg  = xmalloc (D->BssCount  * sizeof (SegDesc*));
1254     D->ZPSeg   = xmalloc (D->ZPCount   * sizeof (SegDesc*));
1255
1256     /* Walk again through the list and setup the segment arrays */
1257     TextIdx = DataIdx = BssIdx = ZPIdx = 0;
1258     for (I = 0; I < CollCount (&F->MemoryAreas); ++I) {
1259         /* Get this entry */
1260         MemoryArea* M = CollAtUnchecked (&F->MemoryAreas, I);
1261
1262         /* Walk over the segment list and check the segment types */
1263         unsigned J;
1264         for (J = 0; J < CollCount (&M->SegList); ++J) {
1265
1266             /* Get the segment */
1267             SegDesc* S = CollAtUnchecked (&M->SegList, J);
1268
1269             /* Check the segment type. */
1270             switch (O65SegType (S)) {
1271                 case O65SEG_TEXT:   D->TextSeg [TextIdx++] = S; break;
1272                 case O65SEG_DATA:   D->DataSeg [DataIdx++] = S; break;
1273                 case O65SEG_BSS:    D->BssSeg [BssIdx++]   = S; break;
1274                 case O65SEG_ZP:     D->ZPSeg [ZPIdx++]     = S; break;
1275                 default:            Internal ("Invalid return from O65SegType");
1276             }
1277         }
1278     }
1279 }
1280
1281
1282
1283 static int O65Unresolved (unsigned Name, void* D)
1284 /* Called if an unresolved symbol is encountered */
1285 {
1286     /* Check if the symbol is an imported o65 symbol */
1287     if (O65GetImport (D, Name) != 0) {
1288         /* This is an external symbol, relax... */
1289         return 1;
1290     } else {
1291         /* This is actually an unresolved external. Bump the counter */
1292         ((O65Desc*) D)->Undef++;
1293         return 0;
1294     }
1295 }
1296
1297
1298
1299 static void O65SetupHeader (O65Desc* D)
1300 /* Set additional stuff in the header */
1301 {
1302     /* Set the base addresses of the segments */
1303     if (D->TextCount > 0) {
1304         SegDesc* FirstSeg  = D->TextSeg [0];
1305         D->Header.TextBase = FirstSeg->Seg->PC;
1306     }
1307     if (D->DataCount > 0) {
1308         SegDesc* FirstSeg  = D->DataSeg [0];
1309         D->Header.DataBase = FirstSeg->Seg->PC;
1310     }
1311     if (D->BssCount > 0) {
1312         SegDesc* FirstSeg  = D->BssSeg [0];
1313         D->Header.BssBase = FirstSeg->Seg->PC;
1314     }
1315     if (D->ZPCount > 0) {
1316         SegDesc* FirstSeg = D->ZPSeg [0];
1317         D->Header.ZPBase  = FirstSeg->Seg->PC;
1318     }
1319
1320     /* If we have byte wise relocation and an alignment of 1, we can set
1321      * the "simple addressing" bit in the header.
1322      */
1323     if ((D->Header.Mode & MF_RELOC_MASK) == MF_RELOC_BYTE &&
1324         (D->Header.Mode & MF_ALIGN_MASK) == MF_ALIGN_1) {
1325         D->Header.Mode = (D->Header.Mode & ~MF_ADDR_MASK) | MF_ADDR_SIMPLE;
1326     }
1327 }
1328
1329
1330
1331 void O65WriteTarget (O65Desc* D, File* F)
1332 /* Write an o65 output file */
1333 {
1334     char        OptBuf [256];   /* Buffer for option strings */
1335     unsigned    OptLen;
1336     time_t      T;
1337
1338     /* Place the filename in the control structure */
1339     D->Filename = GetString (F->Name);
1340
1341     /* Check for unresolved symbols. The function O65Unresolved is called
1342      * if we get an unresolved symbol.
1343      */
1344     D->Undef = 0;               /* Reset the counter */
1345     CheckUnresolvedImports (O65Unresolved, D);
1346     if (D->Undef > 0) {
1347         /* We had unresolved symbols, cannot create output file */
1348         Error ("%u unresolved external(s) found - cannot create output file", D->Undef);
1349     }
1350
1351     /* Setup the segment arrays */
1352     O65SetupSegments (D, F);
1353
1354     /* Setup additional stuff in the header */
1355     O65SetupHeader (D);
1356
1357     /* Open the file */
1358     D->F = fopen (D->Filename, "wb");
1359     if (D->F == 0) {
1360         Error ("Cannot open `%s': %s", D->Filename, strerror (errno));
1361     }
1362
1363     /* Keep the user happy */
1364     Print (stdout, 1, "Opened `%s'...\n", D->Filename);
1365
1366     /* Define some more options: A timestamp and the linker version */
1367     T = time (0);
1368     strcpy (OptBuf, ctime (&T));
1369     OptLen = strlen (OptBuf);
1370     while (OptLen > 0 && IsControl (OptBuf[OptLen-1])) {
1371         --OptLen;
1372     }
1373     OptBuf[OptLen] = '\0';
1374     O65SetOption (D, O65OPT_TIMESTAMP, OptBuf, OptLen + 1);
1375     sprintf (OptBuf, "ld65 V%s", GetVersionAsString ());
1376     O65SetOption (D, O65OPT_ASM, OptBuf, strlen (OptBuf) + 1);
1377
1378     /* Write the header */
1379     O65WriteHeader (D);
1380
1381     /* Write the text segment */
1382     O65WriteTextSeg (D);
1383
1384     /* Write the data segment */
1385     O65WriteDataSeg (D);
1386
1387     /* "Write" the bss segments */
1388     O65WriteBssSeg (D);
1389
1390     /* "Write" the zeropage segments */
1391     O65WriteZPSeg (D);
1392
1393     /* Write the undefined references list */
1394     O65WriteImports (D);
1395
1396     /* Write the text segment relocation table */
1397     O65WriteTextReloc (D);
1398
1399     /* Write the data segment relocation table */
1400     O65WriteDataReloc (D);
1401
1402     /* Write the list of exports */
1403     O65WriteExports (D);
1404
1405     /* Seek back to the start and write the updated header */
1406     fseek (D->F, 0, SEEK_SET);
1407     O65WriteHeader (D);
1408
1409     /* Close the file */
1410     if (fclose (D->F) != 0) {
1411         Error ("Cannot write to `%s': %s", D->Filename, strerror (errno));
1412     }
1413
1414     /* Reset the file and filename */
1415     D->F        = 0;
1416     D->Filename = 0;
1417 }
1418
1419
1420
1421