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