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