1 /*****************************************************************************/
5 /* Module to handle the o65 binary format */
9 /* (C) 1999-2001 Ullrich von Bassewitz */
11 /* D-70597 Stuttgart */
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 /*****************************************************************************/
58 /*****************************************************************************/
60 /*****************************************************************************/
64 /* Header mode bits */
65 #define MF_CPU_65816 0x8000 /* Executable is for 65816 */
66 #define MF_CPU_6502 0x0000 /* Executable is for the 6502 */
67 #define MF_CPU_MASK 0x8000 /* Mask to extract CPU type */
69 #define MF_RELOC_PAGE 0x4000 /* Page wise relocation */
70 #define MF_RELOC_BYTE 0x0000 /* Byte wise relocation */
71 #define MF_RELOC_MASK 0x4000 /* Mask to extract relocation type */
73 #define MF_SIZE_32BIT 0x2000 /* All size words are 32bit */
74 #define MF_SIZE_16BIT 0x0000 /* All size words are 16bit */
75 #define MF_SIZE_MASK 0x2000 /* Mask to extract size */
77 #define MF_FTYPE_OBJ 0x1000 /* Object file */
78 #define MF_FTYPE_EXE 0x0000 /* Executable file */
79 #define MF_FTYPE_MASK 0x1000 /* Mask to extract type */
81 #define MF_ADDR_SIMPLE 0x0800 /* Simple addressing */
82 #define MF_ADDR_DEFAULT 0x0000 /* Default addressing */
83 #define MF_ADDR_MASK 0x0800 /* Mask to extract addressing */
85 #define MF_ALIGN_1 0x0000 /* Bytewise alignment */
86 #define MF_ALIGN_2 0x0001 /* Align words */
87 #define MF_ALIGN_4 0x0002 /* Align longwords */
88 #define MF_ALIGN_256 0x0003 /* Align pages (256 bytes) */
89 #define MF_ALIGN_MASK 0x0003 /* Mask to extract alignment */
91 /* The four o65 segment types. Note: These values are identical to the values
92 * needed for the segmentID in the o65 spec.
94 #define O65SEG_UNDEF 0x00
95 #define O65SEG_ABS 0x01
96 #define O65SEG_TEXT 0x02
97 #define O65SEG_DATA 0x03
98 #define O65SEG_BSS 0x04
99 #define O65SEG_ZP 0x05
101 /* Relocation type codes for the o65 format */
102 #define O65RELOC_WORD 0x80
103 #define O65RELOC_HIGH 0x40
104 #define O65RELOC_LOW 0x20
105 #define O65RELOC_SEGADR 0xC0
106 #define O65RELOC_SEG 0xA0
107 #define O65RELOC_MASK 0xE0
109 /* O65 executable file header */
110 typedef struct O65Header O65Header;
112 unsigned Version; /* Version number for o65 format */
113 unsigned Mode; /* Mode word */
114 unsigned long TextBase; /* Base address of text segment */
115 unsigned long TextSize; /* Size of text segment */
116 unsigned long DataBase; /* Base of data segment */
117 unsigned long DataSize; /* Size of data segment */
118 unsigned long BssBase; /* Base of bss segment */
119 unsigned long BssSize; /* Size of bss segment */
120 unsigned long ZPBase; /* Base of zeropage segment */
121 unsigned long ZPSize; /* Size of zeropage segment */
122 unsigned long StackSize; /* Requested stack size */
126 typedef struct O65Option O65Option;
128 O65Option* Next; /* Next in option list */
129 unsigned char Type; /* Type of option */
130 unsigned char Len; /* Data length */
131 unsigned char Data [1]; /* Data, dynamically allocated */
134 /* A o65 relocation table */
135 #define RELOC_BLOCKSIZE 4096
136 typedef struct O65RelocTab O65RelocTab;
138 unsigned Size; /* Size of the table */
139 unsigned Fill; /* Amount used */
140 unsigned char* Buf; /* Buffer, dynamically allocated */
143 /* Structure describing the format */
145 O65Header Header; /* File header */
146 O65Option* Options; /* List of file options */
147 ExtSymTab* Exports; /* Table with exported symbols */
148 ExtSymTab* Imports; /* Table with imported symbols */
149 unsigned Undef; /* Count of undefined symbols */
150 FILE* F; /* The file we're writing to */
151 char* Filename; /* Name of the output file */
152 O65RelocTab* TextReloc; /* Relocation table for text segment */
153 O65RelocTab* DataReloc; /* Relocation table for data segment */
155 unsigned TextCount; /* Number of segments assigned to .text */
156 SegDesc** TextSeg; /* Array of text segments */
157 unsigned DataCount; /* Number of segments assigned to .data */
158 SegDesc** DataSeg; /* Array of data segments */
159 unsigned BssCount; /* Number of segments assigned to .bss */
160 SegDesc** BssSeg; /* Array of bss segments */
161 unsigned ZPCount; /* Number of segments assigned to .zp */
162 SegDesc** ZPSeg; /* Array of zp segments */
164 /* Temporary data for writing segments */
165 unsigned long SegSize;
166 O65RelocTab* CurReloc;
170 /* Structure for parsing expression trees */
171 typedef struct ExprDesc ExprDesc;
173 O65Desc* D; /* File format descriptor */
174 long Val; /* The offset value */
175 int TooComplex; /* Expression too complex */
176 Section* SegRef; /* Section referenced if any */
177 ExtSym* ExtRef; /* External reference if any */
182 /*****************************************************************************/
183 /* Helper functions */
184 /*****************************************************************************/
188 static ExprDesc* InitExprDesc (ExprDesc* ED, O65Desc* D)
189 /* Initialize an ExprDesc structure for use with O65ParseExpr */
201 static void WriteSize (const O65Desc* D, unsigned long Val)
202 /* Write a "size" word to the file */
204 switch (D->Header.Mode & MF_SIZE_MASK) {
205 case MF_SIZE_16BIT: Write16 (D->F, (unsigned) Val); break;
206 case MF_SIZE_32BIT: Write32 (D->F, Val); break;
207 default: Internal ("Invalid size in header: %04X", D->Header.Mode);
213 static unsigned O65SegType (const SegDesc* S)
214 /* Map our own segment types into something o65 compatible */
216 /* Check the segment type. Readonly segments are assign to the o65
217 * text segment, writeable segments that contain data are assigned
218 * to data, bss and zp segments are handled respectively.
219 * Beware: Zeropage segments have the SF_BSS flag set, so be sure
220 * to check SF_ZP first.
222 if (S->Flags & SF_RO) {
224 } else if (S->Flags & SF_ZP) {
226 } else if (S->Flags & SF_BSS) {
235 static const SegDesc* FindSeg (SegDesc** const List, unsigned Count, const Segment* S)
236 /* Search for a segment in the given list of segment descriptors and return
237 * the descriptor for a segment if we found it, and NULL if not.
242 for (I = 0; I < Count; ++I) {
243 if (List[I]->Seg == S) {
255 static const SegDesc* O65FindSeg (const O65Desc* D, const Segment* S)
256 /* Search for a segment in the segment lists and return it's segment descriptor */
260 if ((SD = FindSeg (D->TextSeg, D->TextCount, S)) != 0) {
263 if ((SD = FindSeg (D->DataSeg, D->DataCount, S)) != 0) {
266 if ((SD = FindSeg (D->BssSeg, D->BssCount, S)) != 0) {
269 if ((SD = FindSeg (D->ZPSeg, D->ZPCount, S)) != 0) {
279 /*****************************************************************************/
280 /* Expression handling */
281 /*****************************************************************************/
285 static void O65ParseExpr (ExprNode* Expr, ExprDesc* D, int Sign)
286 /* Extract and evaluate all constant factors in an subtree that has only
287 * additions and subtractions. If anything other than additions and
288 * subtractions are found, D->TooComplex is set to true.
298 D->Val -= Expr->V.Val;
300 D->Val += Expr->V.Val;
305 /* Get the referenced Export */
306 E = GetExprExport (Expr);
307 /* If this export has a mark set, we've already encountered it.
308 * This means that the export is used to define it's own value,
309 * which in turn means, that we have a circular reference.
311 if (ExportHasMark (E)) {
312 CircularRefError (E);
313 } else if (E->Expr == 0) {
314 /* Dummy export, must be an o65 imported symbol */
315 ExtSym* S = O65GetImport (D->D, E->Name);
318 /* We cannot have more than one external reference in o65 */
321 /* Remember the external reference */
326 O65ParseExpr (E->Expr, D, Sign);
333 /* We cannot handle more than one segment reference in o65 */
336 /* Remember the segment reference */
337 D->SegRef = GetExprSection (Expr);
338 /* Add the offset of the section to the constant value */
339 Val = D->SegRef->Offs + D->SegRef->Seg->PC;
349 O65ParseExpr (Expr->Left, D, Sign);
350 O65ParseExpr (Expr->Right, D, Sign);
354 O65ParseExpr (Expr->Left, D, Sign);
355 O65ParseExpr (Expr->Right, D, -Sign);
359 /* Expression contains illegal operators */
368 /*****************************************************************************/
369 /* Relocation tables */
370 /*****************************************************************************/
374 static O65RelocTab* NewO65RelocTab (void)
375 /* Create a new relocation table */
377 /* Allocate a new structure */
378 O65RelocTab* R = xmalloc (sizeof (O65RelocTab));
380 /* Initialize the data */
381 R->Size = RELOC_BLOCKSIZE;
383 R->Buf = xmalloc (RELOC_BLOCKSIZE);
385 /* Return the created struct */
391 static void FreeO65RelocTab (O65RelocTab* R)
392 /* Free a relocation table */
400 static void O65RelocPutByte (O65RelocTab* R, unsigned B)
401 /* Put the byte into the relocation table */
403 /* Do we have enough space in the buffer? */
404 if (R->Fill == R->Size) {
405 /* We need to grow the buffer */
406 unsigned char* NewBuf = xmalloc (R->Size + RELOC_BLOCKSIZE);
407 memcpy (NewBuf, R->Buf, R->Size);
412 /* Put the byte into the buffer */
413 R->Buf [R->Fill++] = (unsigned char) B;
418 static void O65RelocPutWord (O65RelocTab* R, unsigned W)
419 /* Put a word into the relocation table */
421 O65RelocPutByte (R, W);
422 O65RelocPutByte (R, W >> 8);
427 static void O65WriteReloc (O65RelocTab* R, FILE* F)
428 /* Write the relocation table to the given file */
430 WriteData (F, R->Buf, R->Fill);
435 /*****************************************************************************/
436 /* Option handling */
437 /*****************************************************************************/
441 static O65Option* NewO65Option (unsigned Type, const void* Data, unsigned DataLen)
442 /* Allocate and initialize a new option struct */
446 /* Check the length */
447 CHECK (DataLen <= 253);
449 /* Allocate memory */
450 O = xmalloc (sizeof (O65Option) - 1 + DataLen);
452 /* Initialize the structure */
456 memcpy (O->Data, Data, DataLen);
458 /* Return the created struct */
464 static void FreeO65Option (O65Option* O)
465 /* Free an O65Option struct */
472 /*****************************************************************************/
473 /* Subroutines to write o65 sections */
474 /*****************************************************************************/
478 static void O65WriteHeader (O65Desc* D)
479 /* Write the header of the executable to the given file */
481 static unsigned char Trailer [5] = {
482 0x01, 0x00, 0x6F, 0x36, 0x35
487 /* Write the fixed header */
488 WriteData (D->F, Trailer, sizeof (Trailer));
489 Write8 (D->F, D->Header.Version);
490 Write16 (D->F, D->Header.Mode);
491 WriteSize (D, D->Header.TextBase);
492 WriteSize (D, D->Header.TextSize);
493 WriteSize (D, D->Header.DataBase);
494 WriteSize (D, D->Header.DataSize);
495 WriteSize (D, D->Header.BssBase);
496 WriteSize (D, D->Header.BssSize);
497 WriteSize (D, D->Header.ZPBase);
498 WriteSize (D, D->Header.ZPSize);
499 WriteSize (D, D->Header.StackSize);
501 /* Write the options */
504 Write8 (D->F, O->Len + 2); /* Account for len and type bytes */
505 Write8 (D->F, O->Type);
507 WriteData (D->F, O->Data, O->Len);
512 /* Write the end-of-options byte */
518 static unsigned O65WriteExpr (ExprNode* E, int Signed, unsigned Size,
519 unsigned long Offs, void* Data)
520 /* Called from SegWrite for an expression. Evaluate the expression, check the
521 * range and write the expression value to the file, update the relocation
529 unsigned char RelocType;
531 /* Cast the Data pointer to its real type, an O65Desc */
532 O65Desc* D = (O65Desc*) Data;
534 /* Check for a constant expression */
535 if (IsConstExpr (E)) {
536 /* Write out the constant expression */
537 return SegWriteConstExpr (((O65Desc*)Data)->F, E, Signed, Size);
540 /* We have a relocatable expression that needs a relocation table entry.
541 * Calculate the number of bytes between this entry and the last one, and
542 * setup all necessary intermediate bytes in the relocation table.
544 Offs += D->SegSize; /* Calulate full offset */
545 Diff = ((long) Offs) - D->LastOffs;
546 while (Diff > 0xFE) {
547 O65RelocPutByte (D->CurReloc, 0xFF);
550 O65RelocPutByte (D->CurReloc, (unsigned char) Diff);
552 /* Remember this offset for the next time */
555 /* Determine the expression to relocate */
557 if (E->Op == EXPR_BYTE0 || E->Op == EXPR_BYTE1 ||
558 E->Op == EXPR_BYTE2 || E->Op == EXPR_BYTE3 ||
559 E->Op == EXPR_WORD0 || E->Op == EXPR_WORD1) {
560 /* Use the real expression */
564 /* Recursively collect information about this expression */
565 O65ParseExpr (Expr, InitExprDesc (&ED, D), 1);
567 /* We cannot handle both, an imported symbol and a segment ref */
568 if (ED.SegRef != 0 && ED.ExtRef != 0) {
572 /* Bail out if we cannot handle the expression */
574 return SEG_EXPR_TOO_COMPLEX;
577 /* Safety: Check that we are really referencing a symbol or a segment */
578 CHECK (ED.SegRef != 0 || ED.ExtRef != 0);
580 /* Write out the offset that goes into the segment. */
583 case EXPR_BYTE0: BinVal &= 0xFF; break;
584 case EXPR_BYTE1: BinVal = (BinVal >> 8) & 0xFF; break;
585 case EXPR_BYTE2: BinVal = (BinVal >> 16) & 0xFF; break;
586 case EXPR_BYTE3: BinVal = (BinVal >> 24) & 0xFF; break;
587 case EXPR_WORD0: BinVal &= 0xFFFF; break;
588 case EXPR_WORD1: BinVal = (BinVal >> 16) & 0xFFFF; break;
590 WriteVal (D->F, BinVal, Size);
592 /* Determine the actual type of relocation entry needed from the
593 * information gathered about the expression.
595 if (E->Op == EXPR_BYTE0) {
596 RelocType = O65RELOC_LOW;
597 } else if (E->Op == EXPR_BYTE1) {
598 RelocType = O65RELOC_HIGH;
599 } else if (E->Op == EXPR_BYTE2) {
600 RelocType = O65RELOC_SEG;
605 RelocType = O65RELOC_LOW;
609 RelocType = O65RELOC_WORD;
613 RelocType = O65RELOC_SEGADR;
617 /* 4 byte expression not supported by o65 */
618 return SEG_EXPR_TOO_COMPLEX;
621 Internal ("O65WriteExpr: Invalid expression size: %u", Size);
622 RelocType = 0; /* Avoid gcc warnings */
626 /* Determine which segment we're referencing */
628 /* Imported symbol */
629 RelocType |= O65SEG_UNDEF;
630 O65RelocPutByte (D->CurReloc, RelocType);
631 /* Put the number of the imported symbol into the table */
632 O65RelocPutWord (D->CurReloc, ExtSymNum (ED.ExtRef));
634 /* Segment reference. Search for the segment and map it to it's
637 const SegDesc* Seg = O65FindSeg (D, ED.SegRef->Seg);
639 /* For some reason, we didn't find this segment in the list of
640 * segments written to the o65 file.
642 return SEG_EXPR_INVALID;
644 RelocType |= O65SegType (Seg);
645 O65RelocPutByte (D->CurReloc, RelocType);
647 /* Output additional data if needed */
648 switch (RelocType & O65RELOC_MASK) {
650 O65RelocPutByte (D->CurReloc, ED.Val & 0xFF);
653 O65RelocPutWord (D->CurReloc, ED.Val & 0xFFFF);
664 static void O65WriteSeg (O65Desc* D, SegDesc** Seg, unsigned Count, int DoWrite)
665 /* Write one segment to the o65 output file */
670 /* Initialize variables */
674 /* Write out all segments */
675 for (I = 0; I < Count; ++I) {
677 /* Get the segment from the list node */
680 /* Keep the user happy */
681 Print (stdout, 1, " Writing `%s'\n", S->Name);
683 /* Write this segment */
685 RelocLineInfo (S->Seg);
686 SegWrite (D->F, S->Seg, O65WriteExpr, D);
689 /* Mark the segment as dumped */
692 /* Calculate the total size */
693 D->SegSize += S->Seg->Size;
696 /* Terminate the relocation table for this segment */
698 O65RelocPutByte (D->CurReloc, 0);
701 /* Check the size of the segment for overflow */
702 if ((D->Header.Mode & MF_SIZE_MASK) == MF_SIZE_16BIT && D->SegSize > 0xFFFF) {
703 Error ("Segment overflow in file `%s'", D->Filename);
710 static void O65WriteTextSeg (O65Desc* D)
711 /* Write the code segment to the o65 output file */
713 /* Initialize variables */
714 D->CurReloc = D->TextReloc;
716 /* Dump all text segments */
717 O65WriteSeg (D, D->TextSeg, D->TextCount, 1);
719 /* Set the size of the segment */
720 D->Header.TextSize = D->SegSize;
725 static void O65WriteDataSeg (O65Desc* D)
726 /* Write the data segment to the o65 output file */
728 /* Initialize variables */
729 D->CurReloc = D->DataReloc;
731 /* Dump all data segments */
732 O65WriteSeg (D, D->DataSeg, D->DataCount, 1);
734 /* Set the size of the segment */
735 D->Header.DataSize = D->SegSize;
740 static void O65WriteBssSeg (O65Desc* D)
741 /* "Write" the bss segments to the o65 output file. This will only update
742 * the relevant header fields.
745 /* Initialize variables */
748 /* Dump all data segments */
749 O65WriteSeg (D, D->BssSeg, D->BssCount, 0);
751 /* Set the size of the segment */
752 D->Header.BssSize = D->SegSize;
757 static void O65WriteZPSeg (O65Desc* D)
758 /* "Write" the zeropage segments to the o65 output file. This will only update
759 * the relevant header fields.
762 /* Initialize variables */
765 /* Dump all data segments */
766 O65WriteSeg (D, D->ZPSeg, D->ZPCount, 0);
768 /* Set the size of the segment */
769 D->Header.ZPSize = D->SegSize;
774 static void O65WriteImports (O65Desc* D)
775 /* Write the list of imported symbols to the O65 file */
779 /* Write the number of imports */
780 WriteSize (D, ExtSymCount (D->Imports));
782 /* Write out the symbol names, zero terminated */
783 S = ExtSymList (D->Imports);
786 const char* Name = ExtSymName (S);
787 /* And write it to the output file */
788 WriteData (D->F, Name, strlen (Name) + 1);
796 static void O65WriteTextReloc (O65Desc* D)
797 /* Write the relocation for the text segment to the output file */
799 O65WriteReloc (D->TextReloc, D->F);
804 static void O65WriteDataReloc (O65Desc* D)
805 /* Write the relocation for the data segment to the output file */
807 O65WriteReloc (D->DataReloc, D->F);
812 static void O65WriteExports (O65Desc* D)
813 /* Write the list of exports */
817 /* Write the number of exports */
818 WriteSize (D, ExtSymCount (D->Exports));
820 /* Write out the symbol information */
821 S = ExtSymList (D->Exports);
825 unsigned char SegmentID;
829 const char* Name = ExtSymName (S);
831 /* Get the export for this symbol. We've checked before that this
832 * export does really exist, so if it is unresolved, or if we don't
833 * find it, there is an error in the linker code.
835 Export* E = FindExport (Name);
836 if (E == 0 || IsUnresolvedExport (E)) {
837 Internal ("Unresolved export `%s' found in O65WriteExports", Name);
840 /* Get the expression for the symbol */
843 /* Recursively collect information about this expression */
844 O65ParseExpr (Expr, InitExprDesc (&ED, D), 1);
846 /* We cannot handle expressions with imported symbols here */
847 if (ED.ExtRef != 0) {
851 /* Bail out if we cannot handle the expression */
853 Error ("Expression for symbol `%s' is too complex", Name);
856 /* Determine the segment id for the expression */
857 if (ED.SegRef == 0) {
859 SegmentID = O65SEG_ABS;
861 /* Segment reference. Search for the segment and map it to it's
864 const SegDesc* Seg = O65FindSeg (D, ED.SegRef->Seg);
866 /* For some reason, we didn't find this segment in the list of
867 * segments written to the o65 file.
869 Error ("Segment for symbol `%s' is undefined", Name);
871 SegmentID = O65SegType (Seg);
874 /* Write the name to the output file */
875 WriteData (D->F, Name, strlen (Name) + 1);
877 /* Output the segment id followed by the literal value */
878 Write8 (D->F, SegmentID);
879 WriteSize (D, ED.Val);
888 /*****************************************************************************/
890 /*****************************************************************************/
894 O65Desc* NewO65Desc (void)
895 /* Create, initialize and return a new O65 descriptor struct */
897 /* Allocate a new structure */
898 O65Desc* D = xmalloc (sizeof (O65Desc));
900 /* Initialize the header */
901 D->Header.Version = 0;
903 D->Header.TextBase = 0;
904 D->Header.TextSize = 0;
905 D->Header.DataBase = 0;
906 D->Header.DataSize = 0;
907 D->Header.BssBase = 0;
908 D->Header.BssSize = 0;
909 D->Header.ZPBase = 0;
910 D->Header.ZPSize = 0;
911 D->Header.StackSize = 0; /* Let OS choose a good value */
913 /* Initialize other data */
915 D->Exports = NewExtSymTab ();
916 D->Imports = NewExtSymTab ();
920 D->TextReloc = NewO65RelocTab ();
921 D->DataReloc = NewO65RelocTab ();
931 /* Return the created struct */
937 void FreeO65Desc (O65Desc* D)
938 /* Delete the descriptor struct with cleanup */
940 /* Free the segment arrays */
946 /* Free the relocation tables */
947 FreeO65RelocTab (D->DataReloc);
948 FreeO65RelocTab (D->TextReloc);
950 /* Free the option list */
952 O65Option* O = D->Options;
953 D->Options = D->Options->Next;
957 /* Free the external symbol tables */
958 FreeExtSymTab (D->Exports);
959 FreeExtSymTab (D->Imports);
961 /* Free the struct itself */
967 void O65Set6502 (O65Desc* D)
968 /* Enable 6502 mode */
970 D->Header.Mode = (D->Header.Mode & ~MF_CPU_MASK) | MF_CPU_6502;
975 void O65Set65816 (O65Desc* D)
976 /* Enable 816 mode */
978 D->Header.Mode = (D->Header.Mode & ~MF_CPU_MASK) | MF_CPU_65816;
983 void O65SetSmallModel (O65Desc* D)
984 /* Enable a small memory model executable */
986 D->Header.Mode = (D->Header.Mode & ~MF_SIZE_MASK) | MF_SIZE_16BIT;
991 void O65SetLargeModel (O65Desc* D)
992 /* Enable a large memory model executable */
994 D->Header.Mode = (D->Header.Mode & ~MF_SIZE_MASK) | MF_SIZE_32BIT;
999 void O65SetAlignment (O65Desc* D, unsigned Align)
1000 /* Set the executable alignment */
1002 /* Remove all alignment bits from the mode word */
1003 D->Header.Mode &= ~MF_ALIGN_MASK;
1005 /* Set the alignment bits */
1007 case 1: D->Header.Mode |= MF_ALIGN_1; break;
1008 case 2: D->Header.Mode |= MF_ALIGN_2; break;
1009 case 4: D->Header.Mode |= MF_ALIGN_4; break;
1010 case 256: D->Header.Mode |= MF_ALIGN_256; break;
1011 default: Error ("Invalid alignment for O65 format: %u", Align);
1017 void O65SetOption (O65Desc* D, unsigned Type, const void* Data, unsigned DataLen)
1018 /* Set an o65 header option */
1020 /* Create a new option structure */
1021 O65Option* O = NewO65Option (Type, Data, DataLen);
1023 /* Insert it into the linked list */
1024 O->Next = D->Options;
1030 void O65SetOS (O65Desc* D, unsigned OS, unsigned Version, unsigned Id)
1031 /* Set an option describing the target operating system */
1033 /* Setup the option data */
1034 unsigned char Opt[4];
1038 /* Write the correct option length */
1042 /* No id for these two */
1043 O65SetOption (D, O65OPT_OS, Opt, 2);
1047 /* Set the 16 bit id */
1048 Opt[2] = (unsigned char) Id;
1049 Opt[3] = (unsigned char) (Id >> 8);
1050 O65SetOption (D, O65OPT_OS, Opt, 4);
1054 Internal ("Trying to set invalid O65 operating system: %u", OS);
1060 ExtSym* O65GetImport (O65Desc* D, const char* Ident)
1061 /* Return the imported symbol or NULL if not found */
1063 /* Retrieve the symbol from the table */
1064 return GetExtSym (D->Imports, Ident);
1069 void O65SetImport (O65Desc* D, const char* Ident)
1070 /* Set an imported identifier */
1072 /* Insert the entry into the table */
1073 NewExtSym (D->Imports, Ident);
1078 ExtSym* O65GetExport (O65Desc* D, const char* Ident)
1079 /* Return the exported symbol or NULL if not found */
1081 /* Retrieve the symbol from the table */
1082 return GetExtSym (D->Exports, Ident);
1087 void O65SetExport (O65Desc* D, const char* Ident)
1088 /* Set an exported identifier */
1090 /* Get the export for this symbol and check if it does exist and is
1091 * a resolved symbol.
1093 Export* E = FindExport (Ident);
1094 if (E == 0 || IsUnresolvedExport (E)) {
1095 Error ("Unresolved export: `%s'", Ident);
1098 /* Insert the entry into the table */
1099 NewExtSym (D->Exports, Ident);
1104 static void O65SetupSegments (O65Desc* D, File* F)
1105 /* Setup segment assignments */
1110 unsigned TextIdx, DataIdx, BssIdx, ZPIdx;
1112 /* Initialize the counters */
1118 /* Walk over the memory list */
1121 /* Walk through the segment list and count the segment types */
1125 /* Get the segment from the list node */
1128 /* Check the segment type. */
1129 switch (O65SegType (S)) {
1130 case O65SEG_TEXT: D->TextCount++; break;
1131 case O65SEG_DATA: D->DataCount++; break;
1132 case O65SEG_BSS: D->BssCount++; break;
1133 case O65SEG_ZP: D->ZPCount++; break;
1134 default: Internal ("Invalid return from O65SegType");
1137 /* Next segment node */
1140 /* Next memory area */
1144 /* Allocate memory according to the numbers */
1145 D->TextSeg = xmalloc (D->TextCount * sizeof (SegDesc*));
1146 D->DataSeg = xmalloc (D->DataCount * sizeof (SegDesc*));
1147 D->BssSeg = xmalloc (D->BssCount * sizeof (SegDesc*));
1148 D->ZPSeg = xmalloc (D->ZPCount * sizeof (SegDesc*));
1150 /* Walk again through the list and setup the segment arrays */
1151 TextIdx = DataIdx = BssIdx = ZPIdx = 0;
1158 /* Get the segment from the list node */
1161 /* Check the segment type. */
1162 switch (O65SegType (S)) {
1163 case O65SEG_TEXT: D->TextSeg [TextIdx++] = S; break;
1164 case O65SEG_DATA: D->DataSeg [DataIdx++] = S; break;
1165 case O65SEG_BSS: D->BssSeg [BssIdx++] = S; break;
1166 case O65SEG_ZP: D->ZPSeg [ZPIdx++] = S; break;
1167 default: Internal ("Invalid return from O65SegType");
1170 /* Next segment node */
1173 /* Next memory area */
1180 static int O65Unresolved (const char* Name, void* D)
1181 /* Called if an unresolved symbol is encountered */
1183 /* Check if the symbol is an imported o65 symbol */
1184 if (O65GetImport (D, Name) != 0) {
1185 /* This is an external symbol, relax... */
1188 /* This is actually an unresolved external. Bump the counter */
1189 ((O65Desc*) D)->Undef++;
1196 static void O65SetupHeader (O65Desc* D)
1197 /* Set additional stuff in the header */
1199 /* Set the base addresses of the segments */
1200 if (D->TextCount > 0) {
1201 SegDesc* FirstSeg = D->TextSeg [0];
1202 D->Header.TextBase = FirstSeg->Seg->PC;
1204 if (D->DataCount > 0) {
1205 SegDesc* FirstSeg = D->DataSeg [0];
1206 D->Header.DataBase = FirstSeg->Seg->PC;
1208 if (D->BssCount > 0) {
1209 SegDesc* FirstSeg = D->BssSeg [0];
1210 D->Header.BssBase = FirstSeg->Seg->PC;
1212 if (D->ZPCount > 0) {
1213 SegDesc* FirstSeg = D->ZPSeg [0];
1214 D->Header.ZPBase = FirstSeg->Seg->PC;
1217 /* If we have byte wise relocation and an alignment of 1, we can set
1218 * the "simple addressing" bit in the header.
1220 if ((D->Header.Mode & MF_RELOC_MASK) == MF_RELOC_BYTE &&
1221 (D->Header.Mode & MF_ALIGN_MASK) == MF_ALIGN_1) {
1222 D->Header.Mode = (D->Header.Mode & ~MF_ADDR_MASK) | MF_ADDR_SIMPLE;
1228 void O65WriteTarget (O65Desc* D, File* F)
1229 /* Write an o65 output file */
1231 char OptBuf [256]; /* Buffer for option strings */
1234 /* Place the filename in the control structure */
1235 D->Filename = F->Name;
1237 /* Check for unresolved symbols. The function O65Unresolved is called
1238 * if we get an unresolved symbol.
1240 D->Undef = 0; /* Reset the counter */
1241 CheckExports (O65Unresolved, D);
1243 /* We had unresolved symbols, cannot create output file */
1244 Error ("%u unresolved external(s) found - cannot create output file", D->Undef);
1247 /* Setup the segment arrays */
1248 O65SetupSegments (D, F);
1250 /* Setup additional stuff in the header */
1254 D->F = fopen (F->Name, "wb");
1256 Error ("Cannot open `%s': %s", F->Name, strerror (errno));
1259 /* Keep the user happy */
1260 Print (stdout, 1, "Opened `%s'...\n", F->Name);
1262 /* Define some more options: A timestamp and the linker version */
1264 strcpy (OptBuf, ctime (&T));
1265 O65SetOption (D, O65OPT_TIMESTAMP, OptBuf, strlen (OptBuf) + 1);
1266 sprintf (OptBuf, "ld65 V%u.%u.%u", VER_MAJOR, VER_MINOR, VER_PATCH);
1267 O65SetOption (D, O65OPT_ASM, OptBuf, strlen (OptBuf) + 1);
1269 /* Write the header */
1272 /* Write the text segment */
1273 O65WriteTextSeg (D);
1275 /* Write the data segment */
1276 O65WriteDataSeg (D);
1278 /* "Write" the bss segments */
1281 /* "Write" the zeropage segments */
1284 /* Write the undefined references list */
1285 O65WriteImports (D);
1287 /* Write the text segment relocation table */
1288 O65WriteTextReloc (D);
1290 /* Write the data segment relocation table */
1291 O65WriteDataReloc (D);
1293 /* Write the list of exports */
1294 O65WriteExports (D);
1296 /* Seek back to the start and write the updated header */
1297 fseek (D->F, 0, SEEK_SET);
1300 /* Close the file */
1301 if (fclose (D->F) != 0) {
1302 Error ("Cannot write to `%s': %s", F->Name, strerror (errno));
1305 /* Reset the file and filename */