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