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