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