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