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