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