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