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