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