1 /*****************************************************************************/
5 /* Module to handle the o65 binary format */
9 /* (C) 1999-2012, Ullrich von Bassewitz */
10 /* Roemerstrasse 52 */
11 /* D-70794 Filderstadt */
12 /* EMail: uz@cc65.org */
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. */
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: */
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 */
32 /*****************************************************************************/
63 /*****************************************************************************/
65 /*****************************************************************************/
69 /* Header mode bits */
70 #define MF_CPU_65816 0x8000 /* Executable is for 65816 */
71 #define MF_CPU_6502 0x0000 /* Executable is for the 6502 */
72 #define MF_CPU_MASK 0x8000 /* Mask to extract CPU type */
74 #define MF_RELOC_PAGE 0x4000 /* Page wise relocation */
75 #define MF_RELOC_BYTE 0x0000 /* Byte wise relocation */
76 #define MF_RELOC_MASK 0x4000 /* Mask to extract relocation type */
78 #define MF_SIZE_32BIT 0x2000 /* All size words are 32bit */
79 #define MF_SIZE_16BIT 0x0000 /* All size words are 16bit */
80 #define MF_SIZE_MASK 0x2000 /* Mask to extract size */
82 #define MF_FTYPE_OBJ 0x1000 /* Object file */
83 #define MF_FTYPE_EXE 0x0000 /* Executable file */
84 #define MF_FTYPE_MASK 0x1000 /* Mask to extract type */
86 #define MF_ADDR_SIMPLE 0x0800 /* Simple addressing */
87 #define MF_ADDR_DEFAULT 0x0000 /* Default addressing */
88 #define MF_ADDR_MASK 0x0800 /* Mask to extract addressing */
90 #define MF_ALIGN_1 0x0000 /* Bytewise alignment */
91 #define MF_ALIGN_2 0x0001 /* Align words */
92 #define MF_ALIGN_4 0x0002 /* Align longwords */
93 #define MF_ALIGN_256 0x0003 /* Align pages (256 bytes) */
94 #define MF_ALIGN_MASK 0x0003 /* Mask to extract alignment */
96 /* The four o65 segment types. Note: These values are identical to the values
97 ** needed for the segmentID in the o65 spec.
99 #define O65SEG_UNDEF 0x00
100 #define O65SEG_ABS 0x01
101 #define O65SEG_TEXT 0x02
102 #define O65SEG_DATA 0x03
103 #define O65SEG_BSS 0x04
104 #define O65SEG_ZP 0x05
106 /* Relocation type codes for the o65 format */
107 #define O65RELOC_WORD 0x80
108 #define O65RELOC_HIGH 0x40
109 #define O65RELOC_LOW 0x20
110 #define O65RELOC_SEGADR 0xC0
111 #define O65RELOC_SEG 0xA0
112 #define O65RELOC_MASK 0xE0
114 /* O65 executable file header */
115 typedef struct O65Header O65Header;
117 unsigned Version; /* Version number for o65 format */
118 unsigned Mode; /* Mode word */
119 unsigned long TextBase; /* Base address of text segment */
120 unsigned long TextSize; /* Size of text segment */
121 unsigned long DataBase; /* Base of data segment */
122 unsigned long DataSize; /* Size of data segment */
123 unsigned long BssBase; /* Base of bss segment */
124 unsigned long BssSize; /* Size of bss segment */
125 unsigned long ZPBase; /* Base of zeropage segment */
126 unsigned long ZPSize; /* Size of zeropage segment */
127 unsigned long StackSize; /* Requested stack size */
131 typedef struct O65Option O65Option;
133 O65Option* Next; /* Next in option list */
134 unsigned char Type; /* Type of option */
135 unsigned char Len; /* Data length */
136 unsigned char Data [1]; /* Data, dynamically allocated */
139 /* A o65 relocation table */
140 typedef struct O65RelocTab O65RelocTab;
142 unsigned Size; /* Size of the table */
143 unsigned Fill; /* Amount used */
144 unsigned char* Buf; /* Buffer, dynamically allocated */
147 /* Structure describing the format */
149 O65Header Header; /* File header */
150 O65Option* Options; /* List of file options */
151 ExtSymTab* Exports; /* Table with exported symbols */
152 ExtSymTab* Imports; /* Table with imported symbols */
153 unsigned Undef; /* Count of undefined symbols */
154 FILE* F; /* The file we're writing to */
155 const char* Filename; /* Name of the output file */
156 O65RelocTab* TextReloc; /* Relocation table for text segment */
157 O65RelocTab* DataReloc; /* Relocation table for data segment */
159 unsigned TextCount; /* Number of segments assigned to .text */
160 SegDesc** TextSeg; /* Array of text segments */
161 unsigned DataCount; /* Number of segments assigned to .data */
162 SegDesc** DataSeg; /* Array of data segments */
163 unsigned BssCount; /* Number of segments assigned to .bss */
164 SegDesc** BssSeg; /* Array of bss segments */
165 unsigned ZPCount; /* Number of segments assigned to .zp */
166 SegDesc** ZPSeg; /* Array of zp segments */
168 /* Temporary data for writing segments */
169 unsigned long SegSize;
170 O65RelocTab* CurReloc;
174 /* Structure for parsing expression trees */
175 typedef struct ExprDesc ExprDesc;
177 O65Desc* D; /* File format descriptor */
178 long Val; /* The offset value */
179 int TooComplex; /* Expression too complex */
180 MemoryArea* MemRef; /* Memory reference if any */
181 Segment* SegRef; /* Segment reference if any */
182 Section* SecRef; /* Section reference if any */
183 ExtSym* ExtRef; /* External reference if any */
188 /*****************************************************************************/
189 /* Helper functions */
190 /*****************************************************************************/
194 static ExprDesc* InitExprDesc (ExprDesc* ED, O65Desc* D)
195 /* Initialize an ExprDesc structure for use with O65ParseExpr */
209 static void WriteSize (const O65Desc* D, unsigned long Val)
210 /* Write a "size" word to the file */
212 switch (D->Header.Mode & MF_SIZE_MASK) {
213 case MF_SIZE_16BIT: Write16 (D->F, (unsigned) Val); break;
214 case MF_SIZE_32BIT: Write32 (D->F, Val); break;
215 default: Internal ("Invalid size in header: %04X", D->Header.Mode);
221 static unsigned O65SegType (const SegDesc* S)
222 /* Map our own segment types into something o65 compatible */
224 /* Check the segment type. Readonly segments are assign to the o65
225 ** text segment, writeable segments that contain data are assigned
226 ** to data, bss and zp segments are handled respectively.
227 ** Beware: Zeropage segments have the SF_BSS flag set, so be sure
228 ** to check SF_ZP first.
230 if (S->Flags & SF_RO) {
232 } else if (S->Flags & SF_ZP) {
234 } else if (S->Flags & SF_BSS) {
243 static void CvtMemoryToSegment (ExprDesc* ED)
244 /* Convert a memory area into a segment by searching the list of run segments
245 ** in this memory area and assigning the nearest one.
248 /* Get the memory area from the expression */
249 MemoryArea* M = ED->MemRef;
251 /* Remember the "nearest" segment and its offset */
252 Segment* Nearest = 0;
253 unsigned long Offs = ULONG_MAX;
255 /* Walk over all segments */
257 for (I = 0; I < CollCount (&M->SegList); ++I) {
259 /* Get the segment and check if it's a run segment */
260 SegDesc* S = CollAtUnchecked (&M->SegList, I);
265 /* Get the segment from the segment descriptor */
266 Segment* Seg = S->Seg;
269 if ((long) Seg->PC <= ED->Val && (O = (ED->Val - Seg->PC)) < Offs) {
270 /* This is the nearest segment for now */
274 /* If we found an exact match, don't look further */
282 /* If we found a segment, use it and adjust the offset */
284 ED->SegRef = Nearest;
286 ED->Val -= Nearest->PC;
292 static const SegDesc* FindSeg (SegDesc** const List, unsigned Count, const Segment* S)
293 /* Search for a segment in the given list of segment descriptors and return
294 ** the descriptor for a segment if we found it, and NULL if not.
299 for (I = 0; I < Count; ++I) {
300 if (List[I]->Seg == S) {
312 static const SegDesc* O65FindSeg (const O65Desc* D, const Segment* S)
313 /* Search for a segment in the segment lists and return it's segment descriptor */
317 if ((SD = FindSeg (D->TextSeg, D->TextCount, S)) != 0) {
320 if ((SD = FindSeg (D->DataSeg, D->DataCount, S)) != 0) {
323 if ((SD = FindSeg (D->BssSeg, D->BssCount, S)) != 0) {
326 if ((SD = FindSeg (D->ZPSeg, D->ZPCount, S)) != 0) {
336 /*****************************************************************************/
337 /* Expression handling */
338 /*****************************************************************************/
342 static void O65ParseExpr (ExprNode* Expr, ExprDesc* D, int Sign)
343 /* Extract and evaluate all constant factors in an subtree that has only
344 ** additions and subtractions. If anything other than additions and
345 ** subtractions are found, D->TooComplex is set to true.
353 D->Val += (Sign * Expr->V.IVal);
357 /* Get the referenced Export */
358 E = GetExprExport (Expr);
359 /* If this export has a mark set, we've already encountered it.
360 ** This means that the export is used to define it's own value,
361 ** which in turn means, that we have a circular reference.
363 if (ExportHasMark (E)) {
364 CircularRefError (E);
365 } else if (E->Expr == 0) {
366 /* Dummy export, must be an o65 imported symbol */
367 ExtSym* S = O65GetImport (D->D, E->Name);
370 /* We cannot have more than one external reference in o65 */
373 /* Remember the external reference */
378 O65ParseExpr (E->Expr, D, Sign);
385 /* We cannot handle more than one segment reference in o65 */
388 /* Remember the segment reference */
389 D->SecRef = GetExprSection (Expr);
390 /* Add the offset of the section to the constant value */
391 D->Val += Sign * (D->SecRef->Offs + D->SecRef->Seg->PC);
397 /* We cannot handle more than one segment reference in o65 */
400 /* Remember the segment reference */
401 D->SegRef = Expr->V.Seg;
402 /* Add the offset of the segment to the constant value */
403 D->Val += (Sign * D->SegRef->PC);
409 /* We cannot handle more than one memory reference in o65 */
412 /* Remember the memory area reference */
413 D->MemRef = Expr->V.Mem;
414 /* Add the start address of the memory area to the constant
417 D->Val += (Sign * D->MemRef->Start);
422 O65ParseExpr (Expr->Left, D, Sign);
423 O65ParseExpr (Expr->Right, D, Sign);
427 O65ParseExpr (Expr->Left, D, Sign);
428 O65ParseExpr (Expr->Right, D, -Sign);
432 /* Expression contains illegal operators */
441 /*****************************************************************************/
442 /* Relocation tables */
443 /*****************************************************************************/
447 static O65RelocTab* NewO65RelocTab (void)
448 /* Create a new relocation table */
450 /* Allocate a new structure */
451 O65RelocTab* R = xmalloc (sizeof (O65RelocTab));
453 /* Initialize the data */
458 /* Return the created struct */
464 static void FreeO65RelocTab (O65RelocTab* R)
465 /* Free a relocation table */
473 static void O65RelocPutByte (O65RelocTab* R, unsigned B)
474 /* Put the byte into the relocation table */
476 /* Do we have enough space in the buffer? */
477 if (R->Fill == R->Size) {
478 /* We need to grow the buffer */
482 R->Size = 1024; /* Initial size */
484 R->Buf = xrealloc (R->Buf, R->Size);
487 /* Put the byte into the buffer */
488 R->Buf [R->Fill++] = (unsigned char) B;
493 static void O65RelocPutWord (O65RelocTab* R, unsigned W)
494 /* Put a word into the relocation table */
496 O65RelocPutByte (R, W);
497 O65RelocPutByte (R, W >> 8);
502 static void O65WriteReloc (O65RelocTab* R, FILE* F)
503 /* Write the relocation table to the given file */
505 WriteData (F, R->Buf, R->Fill);
510 /*****************************************************************************/
511 /* Option handling */
512 /*****************************************************************************/
516 static O65Option* NewO65Option (unsigned Type, const void* Data, unsigned DataLen)
517 /* Allocate and initialize a new option struct */
521 /* Check the length */
522 CHECK (DataLen <= 253);
524 /* Allocate memory */
525 O = xmalloc (sizeof (O65Option) - 1 + DataLen);
527 /* Initialize the structure */
531 memcpy (O->Data, Data, DataLen);
533 /* Return the created struct */
539 static void FreeO65Option (O65Option* O)
540 /* Free an O65Option struct */
547 /*****************************************************************************/
548 /* Subroutines to write o65 sections */
549 /*****************************************************************************/
553 static void O65WriteHeader (O65Desc* D)
554 /* Write the header of the executable to the given file */
556 static unsigned char Trailer [5] = {
557 0x01, 0x00, 0x6F, 0x36, 0x35
562 /* Write the fixed header */
563 WriteData (D->F, Trailer, sizeof (Trailer));
564 Write8 (D->F, D->Header.Version);
565 Write16 (D->F, D->Header.Mode);
566 WriteSize (D, D->Header.TextBase);
567 WriteSize (D, D->Header.TextSize);
568 WriteSize (D, D->Header.DataBase);
569 WriteSize (D, D->Header.DataSize);
570 WriteSize (D, D->Header.BssBase);
571 WriteSize (D, D->Header.BssSize);
572 WriteSize (D, D->Header.ZPBase);
573 WriteSize (D, D->Header.ZPSize);
574 WriteSize (D, D->Header.StackSize);
576 /* Write the options */
579 Write8 (D->F, O->Len + 2); /* Account for len and type bytes */
580 Write8 (D->F, O->Type);
582 WriteData (D->F, O->Data, O->Len);
587 /* Write the end-of-options byte */
593 static unsigned O65WriteExpr (ExprNode* E, int Signed, unsigned Size,
594 unsigned long Offs, void* Data)
595 /* Called from SegWrite for an expression. Evaluate the expression, check the
596 ** range and write the expression value to the file, update the relocation
605 unsigned char RelocType;
607 /* Cast the Data pointer to its real type, an O65Desc */
608 O65Desc* D = (O65Desc*) Data;
610 /* Check for a constant expression */
611 if (IsConstExpr (E)) {
612 /* Write out the constant expression */
613 return SegWriteConstExpr (((O65Desc*)Data)->F, E, Signed, Size);
616 /* We have a relocatable expression that needs a relocation table entry.
617 ** Calculate the number of bytes between this entry and the last one, and
618 ** setup all necessary intermediate bytes in the relocation table.
620 Offs += D->SegSize; /* Calulate full offset */
621 Diff = ((long) Offs) - D->LastOffs;
622 while (Diff > 0xFE) {
623 O65RelocPutByte (D->CurReloc, 0xFF);
626 O65RelocPutByte (D->CurReloc, (unsigned char) Diff);
628 /* Remember this offset for the next time */
631 /* Determine the expression to relocate */
633 if (E->Op == EXPR_BYTE0 || E->Op == EXPR_BYTE1 ||
634 E->Op == EXPR_BYTE2 || E->Op == EXPR_BYTE3 ||
635 E->Op == EXPR_WORD0 || E->Op == EXPR_WORD1 ||
636 E->Op == EXPR_FARADDR || E->Op == EXPR_DWORD) {
637 /* Use the real expression */
641 /* Recursively collect information about this expression */
642 O65ParseExpr (Expr, InitExprDesc (&ED, D), 1);
644 /* We cannot handle more than one external reference */
645 RefCount = (ED.MemRef != 0) + (ED.SegRef != 0) +
646 (ED.SecRef != 0) + (ED.ExtRef != 0);
651 /* If we have a memory area reference, we need to convert it into a
652 ** segment reference. If we cannot do that, we cannot handle the
656 CvtMemoryToSegment (&ED);
657 if (ED.SegRef == 0) {
658 return SEG_EXPR_TOO_COMPLEX;
662 /* Bail out if we cannot handle the expression */
664 return SEG_EXPR_TOO_COMPLEX;
667 /* Safety: Check that we have exactly one reference */
668 CHECK (RefCount == 1);
670 /* Write out the offset that goes into the segment. */
673 case EXPR_BYTE0: BinVal &= 0xFF; break;
674 case EXPR_BYTE1: BinVal = (BinVal >> 8) & 0xFF; break;
675 case EXPR_BYTE2: BinVal = (BinVal >> 16) & 0xFF; break;
676 case EXPR_BYTE3: BinVal = (BinVal >> 24) & 0xFF; break;
677 case EXPR_WORD0: BinVal &= 0xFFFF; break;
678 case EXPR_WORD1: BinVal = (BinVal >> 16) & 0xFFFF; break;
679 case EXPR_FARADDR: BinVal &= 0xFFFFFFUL; break;
680 case EXPR_DWORD: BinVal &= 0xFFFFFFFFUL; break;
682 WriteVal (D->F, BinVal, Size);
684 /* Determine the actual type of relocation entry needed from the
685 ** information gathered about the expression.
687 if (E->Op == EXPR_BYTE0) {
688 RelocType = O65RELOC_LOW;
689 } else if (E->Op == EXPR_BYTE1) {
690 RelocType = O65RELOC_HIGH;
691 } else if (E->Op == EXPR_BYTE2) {
692 RelocType = O65RELOC_SEG;
697 RelocType = O65RELOC_LOW;
701 RelocType = O65RELOC_WORD;
705 RelocType = O65RELOC_SEGADR;
709 /* 4 byte expression not supported by o65 */
710 return SEG_EXPR_TOO_COMPLEX;
713 Internal ("O65WriteExpr: Invalid expression size: %u", Size);
714 RelocType = 0; /* Avoid gcc warnings */
718 /* Determine which segment we're referencing */
719 if (ED.SegRef || ED.SecRef) {
723 /* Segment or section reference. */
725 /* Get segment from section */
726 ED.SegRef = ED.SecRef->Seg;
729 /* Search for the segment and map it to it's o65 segmentID */
730 Seg = O65FindSeg (D, ED.SegRef);
732 /* For some reason, we didn't find this segment in the list of
733 ** segments written to the o65 file.
735 return SEG_EXPR_INVALID;
737 RelocType |= O65SegType (Seg);
738 O65RelocPutByte (D->CurReloc, RelocType);
740 /* Output additional data if needed */
741 switch (RelocType & O65RELOC_MASK) {
743 O65RelocPutByte (D->CurReloc, ED.Val & 0xFF);
746 O65RelocPutWord (D->CurReloc, ED.Val & 0xFFFF);
750 } else if (ED.ExtRef) {
751 /* Imported symbol */
752 RelocType |= O65SEG_UNDEF;
753 O65RelocPutByte (D->CurReloc, RelocType);
754 /* Put the number of the imported symbol into the table */
755 O65RelocPutWord (D->CurReloc, ExtSymNum (ED.ExtRef));
759 /* OOPS - something bad happened */
760 Internal ("External reference not handled");
770 static void O65WriteSeg (O65Desc* D, SegDesc** Seg, unsigned Count, int DoWrite)
771 /* Write one segment to the o65 output file */
776 /* Initialize variables */
780 /* Write out all segments */
781 for (I = 0; I < Count; ++I) {
783 /* Get the segment from the list node */
786 /* Keep the user happy */
787 Print (stdout, 1, " Writing `%s'\n", GetString (S->Name));
789 /* Write this segment */
791 SegWrite (D->Filename, D->F, S->Seg, O65WriteExpr, D);
794 /* Mark the segment as dumped */
797 /* Calculate the total size */
798 D->SegSize += S->Seg->Size;
801 /* Terminate the relocation table for this segment */
803 O65RelocPutByte (D->CurReloc, 0);
806 /* Check the size of the segment for overflow */
807 if ((D->Header.Mode & MF_SIZE_MASK) == MF_SIZE_16BIT && D->SegSize > 0xFFFF) {
808 Error ("Segment overflow in file `%s'", D->Filename);
815 static void O65WriteTextSeg (O65Desc* D)
816 /* Write the code segment to the o65 output file */
818 /* Initialize variables */
819 D->CurReloc = D->TextReloc;
821 /* Dump all text segments */
822 O65WriteSeg (D, D->TextSeg, D->TextCount, 1);
824 /* Set the size of the segment */
825 D->Header.TextSize = D->SegSize;
830 static void O65WriteDataSeg (O65Desc* D)
831 /* Write the data segment to the o65 output file */
833 /* Initialize variables */
834 D->CurReloc = D->DataReloc;
836 /* Dump all data segments */
837 O65WriteSeg (D, D->DataSeg, D->DataCount, 1);
839 /* Set the size of the segment */
840 D->Header.DataSize = D->SegSize;
845 static void O65WriteBssSeg (O65Desc* D)
846 /* "Write" the bss segments to the o65 output file. This will only update
847 ** the relevant header fields.
850 /* Initialize variables */
853 /* Dump all bss segments */
854 O65WriteSeg (D, D->BssSeg, D->BssCount, 0);
856 /* Set the size of the segment */
857 D->Header.BssSize = D->SegSize;
862 static void O65WriteZPSeg (O65Desc* D)
863 /* "Write" the zeropage segments to the o65 output file. This will only update
864 ** the relevant header fields.
867 /* Initialize variables */
870 /* Dump all zp segments */
871 O65WriteSeg (D, D->ZPSeg, D->ZPCount, 0);
873 /* Set the size of the segment */
874 D->Header.ZPSize = D->SegSize;
879 static void O65WriteImports (O65Desc* D)
880 /* Write the list of imported symbols to the O65 file */
884 /* Write the number of imports */
885 WriteSize (D, ExtSymCount (D->Imports));
887 /* Write out the symbol names, zero terminated */
888 S = ExtSymList (D->Imports);
891 const char* Name = GetString (ExtSymName (S));
892 /* And write it to the output file */
893 WriteData (D->F, Name, strlen (Name) + 1);
901 static void O65WriteTextReloc (O65Desc* D)
902 /* Write the relocation for the text segment to the output file */
904 O65WriteReloc (D->TextReloc, D->F);
909 static void O65WriteDataReloc (O65Desc* D)
910 /* Write the relocation for the data segment to the output file */
912 O65WriteReloc (D->DataReloc, D->F);
917 static void O65WriteExports (O65Desc* D)
918 /* Write the list of exports */
922 /* Write the number of exports */
923 WriteSize (D, ExtSymCount (D->Exports));
925 /* Write out the symbol information */
926 S = ExtSymList (D->Exports);
930 unsigned char SegmentID;
934 unsigned NameIdx = ExtSymName (S);
935 const char* Name = GetString (NameIdx);
937 /* Get the export for this symbol. We've checked before that this
938 ** export does really exist, so if it is unresolved, or if we don't
939 ** find it, there is an error in the linker code.
941 Export* E = FindExport (NameIdx);
942 if (E == 0 || IsUnresolvedExport (E)) {
943 Internal ("Unresolved export `%s' found in O65WriteExports", Name);
946 /* Get the expression for the symbol */
949 /* Recursively collect information about this expression */
950 O65ParseExpr (Expr, InitExprDesc (&ED, D), 1);
952 /* We cannot handle expressions with imported symbols, or expressions
953 ** with more than one segment reference here
955 if (ED.ExtRef != 0 || (ED.SegRef != 0 && ED.SecRef != 0)) {
959 /* Bail out if we cannot handle the expression */
961 Error ("Expression for symbol `%s' is too complex", Name);
964 /* Determine the segment id for the expression */
965 if (ED.SegRef != 0 || ED.SecRef != 0) {
969 /* Segment or section reference */
970 if (ED.SecRef != 0) {
971 ED.SegRef = ED.SecRef->Seg; /* Get segment from section */
974 /* Search for the segment and map it to it's o65 segmentID */
975 Seg = O65FindSeg (D, ED.SegRef);
977 /* For some reason, we didn't find this segment in the list of
978 ** segments written to the o65 file.
980 Error ("Segment for symbol `%s' is undefined", Name);
982 SegmentID = O65SegType (Seg);
987 SegmentID = O65SEG_ABS;
991 /* Write the name to the output file */
992 WriteData (D->F, Name, strlen (Name) + 1);
994 /* Output the segment id followed by the literal value */
995 Write8 (D->F, SegmentID);
996 WriteSize (D, ED.Val);
1005 /*****************************************************************************/
1007 /*****************************************************************************/
1011 O65Desc* NewO65Desc (void)
1012 /* Create, initialize and return a new O65 descriptor struct */
1014 /* Allocate a new structure */
1015 O65Desc* D = xmalloc (sizeof (O65Desc));
1017 /* Initialize the header */
1018 D->Header.Version = 0;
1020 D->Header.TextBase = 0;
1021 D->Header.TextSize = 0;
1022 D->Header.DataBase = 0;
1023 D->Header.DataSize = 0;
1024 D->Header.BssBase = 0;
1025 D->Header.BssSize = 0;
1026 D->Header.ZPBase = 0;
1027 D->Header.ZPSize = 0;
1028 D->Header.StackSize = 0; /* Let OS choose a good value */
1030 /* Initialize other data */
1032 D->Exports = NewExtSymTab ();
1033 D->Imports = NewExtSymTab ();
1037 D->TextReloc = NewO65RelocTab ();
1038 D->DataReloc = NewO65RelocTab ();
1048 /* Return the created struct */
1054 void FreeO65Desc (O65Desc* D)
1055 /* Delete the descriptor struct with cleanup */
1057 /* Free the segment arrays */
1063 /* Free the relocation tables */
1064 FreeO65RelocTab (D->DataReloc);
1065 FreeO65RelocTab (D->TextReloc);
1067 /* Free the option list */
1068 while (D->Options) {
1069 O65Option* O = D->Options;
1070 D->Options = D->Options->Next;
1074 /* Free the external symbol tables */
1075 FreeExtSymTab (D->Exports);
1076 FreeExtSymTab (D->Imports);
1078 /* Free the struct itself */
1084 void O65Set6502 (O65Desc* D)
1085 /* Enable 6502 mode */
1087 D->Header.Mode = (D->Header.Mode & ~MF_CPU_MASK) | MF_CPU_6502;
1092 void O65Set65816 (O65Desc* D)
1093 /* Enable 816 mode */
1095 D->Header.Mode = (D->Header.Mode & ~MF_CPU_MASK) | MF_CPU_65816;
1100 void O65SetSmallModel (O65Desc* D)
1101 /* Enable a small memory model executable */
1103 D->Header.Mode = (D->Header.Mode & ~MF_SIZE_MASK) | MF_SIZE_16BIT;
1108 void O65SetLargeModel (O65Desc* D)
1109 /* Enable a large memory model executable */
1111 D->Header.Mode = (D->Header.Mode & ~MF_SIZE_MASK) | MF_SIZE_32BIT;
1116 void O65SetAlignment (O65Desc* D, unsigned Alignment)
1117 /* Set the executable alignment */
1119 /* Remove all alignment bits from the mode word */
1120 D->Header.Mode &= ~MF_ALIGN_MASK;
1122 /* Set the alignment bits */
1123 switch (Alignment) {
1124 case 1: D->Header.Mode |= MF_ALIGN_1; break;
1125 case 2: D->Header.Mode |= MF_ALIGN_2; break;
1126 case 4: D->Header.Mode |= MF_ALIGN_4; break;
1127 case 256: D->Header.Mode |= MF_ALIGN_256; break;
1128 default: Error ("Invalid alignment for O65 format: %u", Alignment);
1134 void O65SetOption (O65Desc* D, unsigned Type, const void* Data, unsigned DataLen)
1135 /* Set an o65 header option */
1137 /* Create a new option structure */
1138 O65Option* O = NewO65Option (Type, Data, DataLen);
1140 /* Insert it into the linked list */
1141 O->Next = D->Options;
1147 void O65SetOS (O65Desc* D, unsigned OS, unsigned Version, unsigned Id)
1148 /* Set an option describing the target operating system */
1150 /* Setup the option data */
1151 unsigned char Opt[4];
1155 /* Write the correct option length */
1159 /* Set the 16 bit id */
1160 Opt[2] = (unsigned char) Id;
1161 Opt[3] = (unsigned char) (Id >> 8);
1162 O65SetOption (D, O65OPT_OS, Opt, 4);
1166 /* No id for OS/A65, Lunix, and unknown OSes */
1167 O65SetOption (D, O65OPT_OS, Opt, 2);
1175 ExtSym* O65GetImport (O65Desc* D, unsigned Ident)
1176 /* Return the imported symbol or NULL if not found */
1178 /* Retrieve the symbol from the table */
1179 return GetExtSym (D->Imports, Ident);
1184 void O65SetImport (O65Desc* D, unsigned Ident)
1185 /* Set an imported identifier */
1187 /* Insert the entry into the table */
1188 NewExtSym (D->Imports, Ident);
1193 ExtSym* O65GetExport (O65Desc* D, unsigned Ident)
1194 /* Return the exported symbol or NULL if not found */
1196 /* Retrieve the symbol from the table */
1197 return GetExtSym (D->Exports, Ident);
1202 void O65SetExport (O65Desc* D, unsigned Ident)
1203 /* Set an exported identifier */
1205 /* Get the export for this symbol and check if it does exist and is
1206 ** a resolved symbol.
1208 Export* E = FindExport (Ident);
1209 if (E == 0 || IsUnresolvedExport (E)) {
1210 Error ("Unresolved export: `%s'", GetString (Ident));
1213 /* Insert the entry into the table */
1214 NewExtSym (D->Exports, Ident);
1219 static void O65SetupSegments (O65Desc* D, File* F)
1220 /* Setup segment assignments */
1223 unsigned TextIdx, DataIdx, BssIdx, ZPIdx;
1225 /* Initialize the counters */
1231 /* Walk over the memory list */
1232 for (I = 0; I < CollCount (&F->MemoryAreas); ++I) {
1233 /* Get this entry */
1234 MemoryArea* M = CollAtUnchecked (&F->MemoryAreas, I);
1236 /* Walk through the segment list and count the segment types */
1238 for (J = 0; J < CollCount (&M->SegList); ++J) {
1240 /* Get the segment */
1241 SegDesc* S = CollAtUnchecked (&M->SegList, J);
1243 /* Check the segment type. */
1244 switch (O65SegType (S)) {
1245 case O65SEG_TEXT: D->TextCount++; break;
1246 case O65SEG_DATA: D->DataCount++; break;
1247 case O65SEG_BSS: D->BssCount++; break;
1248 case O65SEG_ZP: D->ZPCount++; break;
1249 default: Internal ("Invalid return from O65SegType");
1254 /* Allocate memory according to the numbers */
1255 D->TextSeg = xmalloc (D->TextCount * sizeof (SegDesc*));
1256 D->DataSeg = xmalloc (D->DataCount * sizeof (SegDesc*));
1257 D->BssSeg = xmalloc (D->BssCount * sizeof (SegDesc*));
1258 D->ZPSeg = xmalloc (D->ZPCount * sizeof (SegDesc*));
1260 /* Walk again through the list and setup the segment arrays */
1261 TextIdx = DataIdx = BssIdx = ZPIdx = 0;
1262 for (I = 0; I < CollCount (&F->MemoryAreas); ++I) {
1263 /* Get this entry */
1264 MemoryArea* M = CollAtUnchecked (&F->MemoryAreas, I);
1266 /* Walk over the segment list and check the segment types */
1268 for (J = 0; J < CollCount (&M->SegList); ++J) {
1270 /* Get the segment */
1271 SegDesc* S = CollAtUnchecked (&M->SegList, J);
1273 /* Check the segment type. */
1274 switch (O65SegType (S)) {
1275 case O65SEG_TEXT: D->TextSeg [TextIdx++] = S; break;
1276 case O65SEG_DATA: D->DataSeg [DataIdx++] = S; break;
1277 case O65SEG_BSS: D->BssSeg [BssIdx++] = S; break;
1278 case O65SEG_ZP: D->ZPSeg [ZPIdx++] = S; break;
1279 default: Internal ("Invalid return from O65SegType");
1287 static int O65Unresolved (unsigned Name, void* D)
1288 /* Called if an unresolved symbol is encountered */
1290 /* Check if the symbol is an imported o65 symbol */
1291 if (O65GetImport (D, Name) != 0) {
1292 /* This is an external symbol, relax... */
1295 /* This is actually an unresolved external. Bump the counter */
1296 ((O65Desc*) D)->Undef++;
1303 static void O65SetupHeader (O65Desc* D)
1304 /* Set additional stuff in the header */
1306 /* Set the base addresses of the segments */
1307 if (D->TextCount > 0) {
1308 SegDesc* FirstSeg = D->TextSeg [0];
1309 D->Header.TextBase = FirstSeg->Seg->PC;
1311 if (D->DataCount > 0) {
1312 SegDesc* FirstSeg = D->DataSeg [0];
1313 D->Header.DataBase = FirstSeg->Seg->PC;
1315 if (D->BssCount > 0) {
1316 SegDesc* FirstSeg = D->BssSeg [0];
1317 D->Header.BssBase = FirstSeg->Seg->PC;
1319 if (D->ZPCount > 0) {
1320 SegDesc* FirstSeg = D->ZPSeg [0];
1321 D->Header.ZPBase = FirstSeg->Seg->PC;
1327 static void O65UpdateHeader (O65Desc* D)
1328 /* Update mode word, currently only the "simple" bit */
1330 /* If we have byte wise relocation and an alignment of 1, and text
1331 ** and data are adjacent, we can set the "simple addressing" bit
1334 if ((D->Header.Mode & MF_RELOC_MASK) == MF_RELOC_BYTE &&
1335 (D->Header.Mode & MF_ALIGN_MASK) == MF_ALIGN_1 &&
1336 D->Header.TextBase + D->Header.TextSize == D->Header.DataBase &&
1337 D->Header.DataBase + D->Header.DataSize == D->Header.BssBase) {
1338 D->Header.Mode = (D->Header.Mode & ~MF_ADDR_MASK) | MF_ADDR_SIMPLE;
1343 void O65WriteTarget (O65Desc* D, File* F)
1344 /* Write an o65 output file */
1346 char OptBuf [256]; /* Buffer for option strings */
1351 /* Place the filename in the control structure */
1352 D->Filename = GetString (F->Name);
1354 /* Check for unresolved symbols. The function O65Unresolved is called
1355 ** if we get an unresolved symbol.
1357 D->Undef = 0; /* Reset the counter */
1358 CheckUnresolvedImports (O65Unresolved, D);
1360 /* We had unresolved symbols, cannot create output file */
1361 Error ("%u unresolved external(s) found - cannot create output file", D->Undef);
1364 /* Setup the segment arrays */
1365 O65SetupSegments (D, F);
1367 /* Setup additional stuff in the header */
1371 D->F = fopen (D->Filename, "wb");
1373 Error ("Cannot open `%s': %s", D->Filename, strerror (errno));
1376 /* Keep the user happy */
1377 Print (stdout, 1, "Opened `%s'...\n", D->Filename);
1379 /* Define some more options: A timestamp, the linker version and the
1383 strcpy (OptBuf, ctime (&T));
1384 OptLen = strlen (OptBuf);
1385 while (OptLen > 0 && IsControl (OptBuf[OptLen-1])) {
1388 OptBuf[OptLen] = '\0';
1389 O65SetOption (D, O65OPT_TIMESTAMP, OptBuf, OptLen + 1);
1390 sprintf (OptBuf, "ld65 V%s", GetVersionAsString ());
1391 O65SetOption (D, O65OPT_ASM, OptBuf, strlen (OptBuf) + 1);
1392 Name = FindName (D->Filename);
1393 O65SetOption (D, O65OPT_FILENAME, Name, strlen (Name) + 1);
1395 /* Write the header */
1398 /* Write the text segment */
1399 O65WriteTextSeg (D);
1401 /* Write the data segment */
1402 O65WriteDataSeg (D);
1404 /* "Write" the bss segments */
1407 /* "Write" the zeropage segments */
1410 /* Write the undefined references list */
1411 O65WriteImports (D);
1413 /* Write the text segment relocation table */
1414 O65WriteTextReloc (D);
1416 /* Write the data segment relocation table */
1417 O65WriteDataReloc (D);
1419 /* Write the list of exports */
1420 O65WriteExports (D);
1422 /* Update header flags */
1423 O65UpdateHeader (D);
1425 /* Seek back to the start and write the updated header */
1426 fseek (D->F, 0, SEEK_SET);
1429 /* Close the file */
1430 if (fclose (D->F) != 0) {
1431 Error ("Cannot write to `%s': %s", D->Filename, strerror (errno));
1434 /* Reset the file and filename */