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