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_SIZE_32BIT 0x2000 /* All size words are 32bit */
70 #define MF_SIZE_16BIT 0x0000 /* All size words are 16bit */
71 #define MF_SIZE_MASK 0x2000 /* Mask to extract size */
73 #define MF_ALIGN_1 0x0000 /* Bytewise alignment */
74 #define MF_ALIGN_2 0x0001 /* Align words */
75 #define MF_ALIGN_4 0x0002 /* Align longwords */
76 #define MF_ALIGN_256 0x0003 /* Align pages (256 bytes) */
77 #define MF_ALIGN_MASK 0x0003 /* Mask to extract alignment */
79 /* The four o65 segment types. Note: These values are identical to the values
80 * needed for the segmentID in the o65 spec.
82 #define O65SEG_UNDEF 0x00
83 #define O65SEG_ABS 0x01
84 #define O65SEG_TEXT 0x02
85 #define O65SEG_DATA 0x03
86 #define O65SEG_BSS 0x04
87 #define O65SEG_ZP 0x05
89 /* Relocation type codes for the o65 format */
90 #define O65RELOC_WORD 0x80
91 #define O65RELOC_HIGH 0x40
92 #define O65RELOC_LOW 0x20
93 #define O65RELOC_SEGADR 0xc0
94 #define O65RELOC_SEG 0xa0
95 #define O65RELOC_MASK 0xc0
97 /* O65 executable file header */
98 typedef struct O65Header O65Header;
100 unsigned Version; /* Version number for o65 format */
101 unsigned Mode; /* Mode word */
102 unsigned long TextBase; /* Base address of text segment */
103 unsigned long TextSize; /* Size of text segment */
104 unsigned long DataBase; /* Base of data segment */
105 unsigned long DataSize; /* Size of data segment */
106 unsigned long BssBase; /* Base of bss segment */
107 unsigned long BssSize; /* Size of bss segment */
108 unsigned long ZPBase; /* Base of zeropage segment */
109 unsigned long ZPSize; /* Size of zeropage segment */
110 unsigned long StackSize; /* Requested stack size */
114 typedef struct O65Option O65Option;
116 O65Option* Next; /* Next in option list */
117 unsigned char Type; /* Type of option */
118 unsigned char Len; /* Data length */
119 unsigned char Data [1]; /* Data, dynamically allocated */
122 /* A o65 relocation table */
123 #define RELOC_BLOCKSIZE 4096
124 typedef struct O65RelocTab O65RelocTab;
126 unsigned Size; /* Size of the table */
127 unsigned Fill; /* Amount used */
128 unsigned char* Buf; /* Buffer, dynamically allocated */
131 /* Structure describing the format */
133 O65Header Header; /* File header */
134 O65Option* Options; /* List of file options */
135 ExtSymTab* Exports; /* Table with exported symbols */
136 ExtSymTab* Imports; /* Table with imported symbols */
137 unsigned Undef; /* Count of undefined symbols */
138 FILE* F; /* The file we're writing to */
139 char* Filename; /* Name of the output file */
140 O65RelocTab* TextReloc; /* Relocation table for text segment */
141 O65RelocTab* DataReloc; /* Relocation table for data segment */
143 unsigned TextCount; /* Number of segments assigned to .text */
144 SegDesc** TextSeg; /* Array of text segments */
145 unsigned DataCount; /* Number of segments assigned to .data */
146 SegDesc** DataSeg; /* Array of data segments */
147 unsigned BssCount; /* Number of segments assigned to .bss */
148 SegDesc** BssSeg; /* Array of bss segments */
149 unsigned ZPCount; /* Number of segments assigned to .zp */
150 SegDesc** ZPSeg; /* Array of zp segments */
152 /* Temporary data for writing segments */
153 unsigned long SegSize;
154 O65RelocTab* CurReloc;
158 /* Structure for parsing expression trees */
159 typedef struct ExprDesc ExprDesc;
161 O65Desc* D; /* File format descriptor */
162 long Val; /* The offset value */
163 int TooComplex; /* Expression too complex */
164 Section* SegRef; /* Section referenced if any */
165 ExtSym* ExtRef; /* External reference if any */
170 /*****************************************************************************/
171 /* Helper functions */
172 /*****************************************************************************/
176 static void WriteSize (const O65Desc* D, unsigned long Val)
177 /* Write a "size" word to the file */
179 if (D->Header.Mode & MF_SIZE_32BIT) {
182 Write16 (D->F, (unsigned) Val);
188 static unsigned O65SegType (const SegDesc* S)
189 /* Map our own segment types into something o65 compatible */
191 /* Check the segment type. Readonly segments are assign to the o65
192 * text segment, writeable segments that contain data are assigned
193 * to data, bss and zp segments are handled respectively.
194 * Beware: Zeropage segments have the SF_BSS flag set, so be sure
195 * to check SF_ZP first.
197 if (S->Flags & SF_RO) {
199 } else if (S->Flags & SF_ZP) {
201 } else if (S->Flags & SF_BSS) {
210 static const SegDesc* FindSeg (SegDesc** const List, unsigned Count, const Segment* S)
211 /* Search for a segment in the given list of segment descriptors and return
212 * the descriptor for a segment if we found it, and NULL if not.
217 for (I = 0; I < Count; ++I) {
218 if (List[I]->Seg == S) {
230 static const SegDesc* O65FindSeg (const O65Desc* D, const Segment* S)
231 /* Search for a segment in the segment lists and return it's segment descriptor */
235 if ((SD = FindSeg (D->TextSeg, D->TextCount, S)) != 0) {
238 if ((SD = FindSeg (D->DataSeg, D->DataCount, S)) != 0) {
241 if ((SD = FindSeg (D->BssSeg, D->BssCount, S)) != 0) {
244 if ((SD = FindSeg (D->ZPSeg, D->ZPCount, S)) != 0) {
254 /*****************************************************************************/
255 /* Expression handling */
256 /*****************************************************************************/
260 static void O65ParseExpr (ExprNode* Expr, ExprDesc* D, int Sign)
261 /* Extract and evaluate all constant factors in an subtree that has only
262 * additions and subtractions. If anything other than additions and
263 * subtractions are found, D->TooComplex is set to true.
272 D->Val -= Expr->V.Val;
274 D->Val += Expr->V.Val;
279 /* Get the referenced Export */
280 E = GetExprExport (Expr);
281 /* If this export has a mark set, we've already encountered it.
282 * This means that the export is used to define it's own value,
283 * which in turn means, that we have a circular reference.
285 if (ExportHasMark (E)) {
286 CircularRefError (E);
287 } else if (E->Expr == 0) {
288 /* Dummy export, must be an o65 imported symbol */
289 ExtSym* S = O65GetImport (D->D, E->Name);
292 /* We cannot have more than one external reference in o65 */
295 /* Remember the external reference */
300 O65ParseExpr (E->Expr, D, Sign);
307 /* We cannot handle more than one segment reference in o65 */
310 /* Remember the segment reference */
311 D->SegRef = GetExprSection (Expr);
316 O65ParseExpr (Expr->Left, D, Sign);
317 O65ParseExpr (Expr->Right, D, Sign);
321 O65ParseExpr (Expr->Left, D, Sign);
322 O65ParseExpr (Expr->Right, D, -Sign);
326 /* Expression contains illegal operators */
335 /*****************************************************************************/
336 /* Relocation tables */
337 /*****************************************************************************/
341 static O65RelocTab* NewO65RelocTab (void)
342 /* Create a new relocation table */
344 /* Allocate a new structure */
345 O65RelocTab* R = xmalloc (sizeof (O65RelocTab));
347 /* Initialize the data */
348 R->Size = RELOC_BLOCKSIZE;
350 R->Buf = xmalloc (RELOC_BLOCKSIZE);
352 /* Return the created struct */
358 static void FreeO65RelocTab (O65RelocTab* R)
359 /* Free a relocation table */
367 static void O65RelocPutByte (O65RelocTab* R, unsigned B)
368 /* Put the byte into the relocation table */
370 /* Do we have enough space in the buffer? */
371 if (R->Fill == R->Size) {
372 /* We need to grow the buffer */
373 unsigned char* NewBuf = xmalloc (R->Size + RELOC_BLOCKSIZE);
374 memcpy (NewBuf, R->Buf, R->Size);
379 /* Put the byte into the buffer */
380 R->Buf [R->Fill++] = (unsigned char) B;
385 static void O65RelocPutWord (O65RelocTab* R, unsigned W)
386 /* Put a word into the relocation table */
388 O65RelocPutByte (R, W);
389 O65RelocPutByte (R, W >> 8);
394 static void O65WriteReloc (O65RelocTab* R, FILE* F)
395 /* Write the relocation table to the given file */
397 WriteData (F, R->Buf, R->Fill);
402 /*****************************************************************************/
403 /* Option handling */
404 /*****************************************************************************/
408 static O65Option* NewO65Option (unsigned Type, const void* Data, unsigned DataLen)
409 /* Allocate and initialize a new option struct */
413 /* Check the length */
414 CHECK (DataLen <= 253);
416 /* Allocate memory */
417 O = xmalloc (sizeof (O65Option) - 1 + DataLen);
419 /* Initialize the structure */
423 memcpy (O->Data, Data, DataLen);
425 /* Return the created struct */
431 static void FreeO65Option (O65Option* O)
432 /* Free an O65Option struct */
439 /*****************************************************************************/
440 /* Subroutines to write o65 sections */
441 /*****************************************************************************/
445 static void O65WriteHeader (O65Desc* D)
446 /* Write the header of the executable to the given file */
448 static unsigned char Trailer [5] = {
449 0x01, 0x00, 0x6F, 0x36, 0x35
455 /* Write the fixed header */
456 WriteData (D->F, Trailer, sizeof (Trailer));
457 Write8 (D->F, D->Header.Version);
458 Write16 (D->F, D->Header.Mode);
459 WriteSize (D, D->Header.TextBase);
460 WriteSize (D, D->Header.TextSize);
461 WriteSize (D, D->Header.DataBase);
462 WriteSize (D, D->Header.DataSize);
463 WriteSize (D, D->Header.BssBase);
464 WriteSize (D, D->Header.BssSize);
465 WriteSize (D, D->Header.ZPBase);
466 WriteSize (D, D->Header.ZPSize);
467 WriteSize (D, D->Header.StackSize);
469 /* Write the options */
472 Write8 (D->F, O->Len + 2); /* Account for len and type bytes */
473 Write8 (D->F, O->Type);
475 WriteData (D->F, O->Data, O->Len);
480 /* Write the end-of-options byte */
486 static unsigned O65WriteExpr (ExprNode* E, int Signed, unsigned Size,
487 unsigned long Offs, void* Data)
488 /* Called from SegWrite for an expression. Evaluate the expression, check the
489 * range and write the expression value to the file, update the relocation
497 unsigned char RelocType;
499 /* Cast the Data pointer to its real type, an O65Desc */
500 O65Desc* D = (O65Desc*) Data;
502 /* Check for a constant expression */
503 if (IsConstExpr (E)) {
504 /* Write out the constant expression */
505 return SegWriteConstExpr (((O65Desc*)Data)->F, E, Signed, Size);
508 /* We have a relocatable expression that needs a relocation table entry.
509 * Calculate the number of bytes between this entry and the last one, and
510 * setup all necessary intermediate bytes in the relocation table.
512 Offs += D->SegSize; /* Calulate full offset */
513 Diff = ((long) Offs) - D->LastOffs;
514 while (Diff > 0xFE) {
515 O65RelocPutByte (D->CurReloc, 0xFF);
518 O65RelocPutByte (D->CurReloc, (unsigned char) Diff);
520 /* Remember this offset for the next time */
523 /* Determine the expression to relocate */
525 if (E->Op == EXPR_BYTE0 || E->Op == EXPR_BYTE1 ||
526 E->Op == EXPR_BYTE2 || E->Op == EXPR_BYTE3 ||
527 E->Op == EXPR_WORD0 || E->Op == EXPR_WORD1) {
528 /* Use the real expression */
532 /* Initialize the descriptor for expression parsing */
539 /* Recursively collect information about this expression */
540 O65ParseExpr (Expr, &ED, 1);
542 /* We cannot handle both, an imported symbol and a segment ref */
543 if (ED.SegRef != 0 && ED.ExtRef != 0) {
547 /* Bail out if we cannot handle the expression */
549 return SEG_EXPR_TOO_COMPLEX;
552 /* Safety: Check that we are really referencing a symbol or a segment */
553 CHECK (ED.SegRef != 0 || ED.ExtRef != 0);
555 /* Write out the offset that goes into the segment. */
558 case EXPR_BYTE0: BinVal &= 0xFF; break;
559 case EXPR_BYTE1: BinVal = (BinVal >> 8) & 0xFF; break;
560 case EXPR_BYTE2: BinVal = (BinVal >> 16) & 0xFF; break;
561 case EXPR_BYTE3: BinVal = (BinVal >> 24) & 0xFF; break;
562 case EXPR_WORD0: BinVal &= 0xFFFF; break;
563 case EXPR_WORD1: BinVal = (BinVal >> 16) & 0xFFFF; break;
565 WriteVal (D->F, BinVal, Size);
567 /* Determine the actual type of relocation entry needed from the
568 * information gathered about the expression.
570 if (E->Op == EXPR_BYTE0) {
571 RelocType = O65RELOC_LOW;
572 } else if (E->Op == EXPR_BYTE1) {
573 RelocType = O65RELOC_HIGH;
574 } else if (E->Op == EXPR_BYTE2) {
575 RelocType = O65RELOC_SEG;
580 RelocType = O65RELOC_LOW;
584 RelocType = O65RELOC_WORD;
588 RelocType = O65RELOC_SEGADR;
592 /* 4 byte expression not supported by o65 */
593 return SEG_EXPR_TOO_COMPLEX;
596 Internal ("O65WriteExpr: Invalid expression size: %u", Size);
597 RelocType = 0; /* Avoid gcc warnings */
601 /* Determine which segment we're referencing */
603 /* Imported symbol */
604 RelocType |= O65SEG_UNDEF;
605 O65RelocPutByte (D->CurReloc, RelocType);
606 /* Put the number of the imported symbol into the table */
607 O65RelocPutWord (D->CurReloc, ExtSymNum (ED.ExtRef));
609 /* Segment reference. Search for the segment and map it to it's
612 const SegDesc* Seg = O65FindSeg (D, ED.SegRef->Seg);
614 /* For some reason, we didn't find this segment in the list of
615 * segments written to the o65 file.
617 return SEG_EXPR_INVALID;
619 RelocType |= O65SegType (Seg);
620 O65RelocPutByte (D->CurReloc, RelocType);
622 /* Output additional data if needed */
623 switch (RelocType & O65RELOC_MASK) {
625 O65RelocPutByte (D->CurReloc, ED.Val & 0xFF);
628 O65RelocPutWord (D->CurReloc, ED.Val & 0xFFFF);
639 static void O65WriteSeg (O65Desc* D, SegDesc** Seg, unsigned Count, int DoWrite)
640 /* Write one segment to the o65 output file */
645 /* Initialize variables */
649 /* Write out all segments */
650 for (I = 0; I < Count; ++I) {
652 /* Get the segment from the list node */
655 /* Keep the user happy */
656 Print (stdout, 1, " Writing `%s'\n", S->Name);
658 /* Write this segment */
660 RelocLineInfo (S->Seg);
661 SegWrite (D->F, S->Seg, O65WriteExpr, D);
664 /* Mark the segment as dumped */
667 /* Calculate the total size */
668 D->SegSize += S->Seg->Size;
671 /* Terminate the relocation table for this segment */
673 O65RelocPutByte (D->CurReloc, 0);
676 /* Check the size of the segment for overflow */
677 if ((D->Header.Mode & MF_SIZE_MASK) == MF_SIZE_16BIT && D->SegSize > 0xFFFF) {
678 Error ("Segment overflow in file `%s'", D->Filename);
685 static void O65WriteTextSeg (O65Desc* D, Memory* M attribute ((unused)))
686 /* Write the code segment to the o65 output file */
688 /* Initialize variables */
689 D->CurReloc = D->TextReloc;
691 /* Dump all text segments */
692 O65WriteSeg (D, D->TextSeg, D->TextCount, 1);
694 /* Set the size of the segment */
695 D->Header.TextSize = D->SegSize;
700 static void O65WriteDataSeg (O65Desc* D, Memory* M attribute ((unused)))
701 /* Write the data segment to the o65 output file */
703 /* Initialize variables */
704 D->CurReloc = D->DataReloc;
706 /* Dump all data segments */
707 O65WriteSeg (D, D->DataSeg, D->DataCount, 1);
709 /* Set the size of the segment */
710 D->Header.DataSize = D->SegSize;
715 static void O65WriteBssSeg (O65Desc* D, Memory* M attribute ((unused)))
716 /* "Write" the bss segments to the o65 output file. This will only update
717 * the relevant header fields.
720 /* Initialize variables */
723 /* Dump all data segments */
724 O65WriteSeg (D, D->BssSeg, D->BssCount, 0);
726 /* Set the size of the segment */
727 D->Header.BssSize = D->SegSize;
732 static void O65WriteZPSeg (O65Desc* D, Memory* M attribute ((unused)))
733 /* "Write" the zeropage segments to the o65 output file. This will only update
734 * the relevant header fields.
737 /* Initialize variables */
740 /* Dump all data segments */
741 O65WriteSeg (D, D->ZPSeg, D->ZPCount, 0);
743 /* Set the size of the segment */
744 D->Header.ZPSize = D->SegSize;
749 static void O65WriteImports (O65Desc* D)
750 /* Write the list of imported symbols to the O65 file */
754 /* Write the number of external symbols */
755 WriteSize (D, ExtSymCount (D->Imports));
757 /* Write out the symbol names, zero terminated */
758 E = ExtSymList (D->Imports);
761 const char* Name = ExtSymName (E);
762 /* And write it to the output file */
763 WriteData (D->F, Name, strlen (Name) + 1);
771 static void O65WriteTextReloc (O65Desc* D)
772 /* Write the relocation for the text segment to the output file */
774 O65WriteReloc (D->TextReloc, D->F);
779 static void O65WriteDataReloc (O65Desc* D)
780 /* Write the relocation for the data segment to the output file */
782 O65WriteReloc (D->DataReloc, D->F);
787 static void O65WriteExports (O65Desc* D)
788 /* Write the list of exports */
790 /* Since ld65 creates exectutables, not object files, we do not have
791 * exports. This may change if we support writing shared libraries...
798 /*****************************************************************************/
800 /*****************************************************************************/
804 O65Desc* NewO65Desc (void)
805 /* Create, initialize and return a new O65 descriptor struct */
807 /* Allocate a new structure */
808 O65Desc* D = xmalloc (sizeof (O65Desc));
810 /* Initialize the header */
811 D->Header.Version = 0;
813 D->Header.TextBase = 0;
814 D->Header.TextSize = 0;
815 D->Header.DataBase = 0;
816 D->Header.DataSize = 0;
817 D->Header.BssBase = 0;
818 D->Header.BssSize = 0;
819 D->Header.ZPBase = 0;
820 D->Header.ZPSize = 0;
821 D->Header.StackSize = 0; /* Let OS choose a good value */
823 /* Initialize other data */
825 D->Exports = NewExtSymTab ();
826 D->Imports = NewExtSymTab ();
830 D->TextReloc = NewO65RelocTab ();
831 D->DataReloc = NewO65RelocTab ();
841 /* Return the created struct */
847 void FreeO65Desc (O65Desc* D)
848 /* Delete the descriptor struct with cleanup */
850 /* Free the segment arrays */
856 /* Free the relocation tables */
857 FreeO65RelocTab (D->DataReloc);
858 FreeO65RelocTab (D->TextReloc);
860 /* Free the option list */
862 O65Option* O = D->Options;
863 D->Options = D->Options->Next;
867 /* Free the external symbol tables */
868 FreeExtSymTab (D->Exports);
869 FreeExtSymTab (D->Imports);
871 /* Free the struct itself */
877 void O65Set6502 (O65Desc* D)
878 /* Enable 6502 mode */
880 D->Header.Mode = (D->Header.Mode & ~MF_CPU_MASK) | MF_CPU_6502;
885 void O65Set65816 (O65Desc* D)
886 /* Enable 816 mode */
888 D->Header.Mode = (D->Header.Mode & ~MF_CPU_MASK) | MF_CPU_65816;
893 void O65SetSmallModel (O65Desc* D)
894 /* Enable a small memory model executable */
896 D->Header.Mode = (D->Header.Mode & ~MF_SIZE_MASK) | MF_SIZE_16BIT;
901 void O65SetLargeModel (O65Desc* D)
902 /* Enable a large memory model executable */
904 D->Header.Mode = (D->Header.Mode & ~MF_SIZE_MASK) | MF_SIZE_32BIT;
909 void O65SetAlignment (O65Desc* D, unsigned Align)
910 /* Set the executable alignment */
912 /* Remove all alignment bits from the mode word */
913 D->Header.Mode &= ~MF_ALIGN_MASK;
915 /* Set the alignment bits */
917 case 1: D->Header.Mode |= MF_ALIGN_1; break;
918 case 2: D->Header.Mode |= MF_ALIGN_2; break;
919 case 4: D->Header.Mode |= MF_ALIGN_4; break;
920 case 256: D->Header.Mode |= MF_ALIGN_256; break;
921 default: Error ("Invalid alignment for O65 format: %u", Align);
927 void O65SetOption (O65Desc* D, unsigned Type, const void* Data, unsigned DataLen)
928 /* Set an o65 header option */
930 /* Create a new option structure */
931 O65Option* O = NewO65Option (Type, Data, DataLen);
933 /* Insert it into the linked list */
934 O->Next = D->Options;
940 void O65SetOS (O65Desc* D, unsigned OS)
941 /* Set an option describing the target operating system */
943 static const unsigned char OSA65 [2] = { O65OS_OSA65, 0 };
944 static const unsigned char Lunix [2] = { O65OS_LUNIX, 0 };
946 /* Write the correct option */
950 O65SetOption (D, O65OPT_OS, OSA65, sizeof (OSA65));
954 O65SetOption (D, O65OPT_OS, Lunix, sizeof (Lunix));
958 Internal ("Trying to set invalid O65 operating system: %u", OS);
965 ExtSym* O65GetImport (O65Desc* D, const char* Ident)
966 /* Return the imported symbol or NULL if not found */
968 /* Retrieve the symbol from the table */
969 return GetExtSym (D->Imports, Ident);
974 void O65SetImport (O65Desc* D, const char* Ident)
975 /* Set an imported identifier */
977 /* Insert the entry into the table */
978 NewExtSym (D->Imports, Ident);
983 ExtSym* O65GetExport (O65Desc* D, const char* Ident)
984 /* Return the exported symbol or NULL if not found */
986 /* Retrieve the symbol from the table */
987 return GetExtSym (D->Exports, Ident);
992 void O65SetExport (O65Desc* D, const char* Ident)
993 /* Set an exported identifier */
995 /* Insert the entry into the table */
996 NewExtSym (D->Exports, Ident);
1001 static void O65SetupSegments (O65Desc* D, Memory* M)
1002 /* Setup segment assignments */
1006 unsigned TextIdx, DataIdx, BssIdx, ZPIdx;
1008 /* Initialize the counters */
1014 /* Walk through the segment list and count the segment types */
1018 /* Get the segment from the list node */
1021 /* Check the segment type. */
1022 switch (O65SegType (S)) {
1023 case O65SEG_TEXT: D->TextCount++; break;
1024 case O65SEG_DATA: D->DataCount++; break;
1025 case O65SEG_BSS: D->BssCount++; break;
1026 case O65SEG_ZP: D->ZPCount++; break;
1027 default: Internal ("Invalid return from O65SegType");
1030 /* Next segment node */
1034 /* Allocate memory according to the numbers */
1035 D->TextSeg = xmalloc (D->TextCount * sizeof (SegDesc*));
1036 D->DataSeg = xmalloc (D->DataCount * sizeof (SegDesc*));
1037 D->BssSeg = xmalloc (D->BssCount * sizeof (SegDesc*));
1038 D->ZPSeg = xmalloc (D->ZPCount * sizeof (SegDesc*));
1040 /* Walk again through the list and setup the segment arrays */
1041 TextIdx = DataIdx = BssIdx = ZPIdx = 0;
1045 /* Get the segment from the list node */
1048 /* Check the segment type. */
1049 switch (O65SegType (S)) {
1050 case O65SEG_TEXT: D->TextSeg [TextIdx++] = S; break;
1051 case O65SEG_DATA: D->DataSeg [DataIdx++] = S; break;
1052 case O65SEG_BSS: D->BssSeg [BssIdx++] = S; break;
1053 case O65SEG_ZP: D->ZPSeg [ZPIdx++] = S; break;
1054 default: Internal ("Invalid return from O65SegType");
1057 /* Next segment node */
1064 static int O65Unresolved (const char* Name, void* D)
1065 /* Called if an unresolved symbol is encountered */
1067 /* Check if the symbol is an imported o65 symbol */
1068 if (O65GetImport (D, Name) != 0) {
1069 /* This is an external symbol, relax... */
1072 /* This is actually an unresolved external. Bump the counter */
1073 ((O65Desc*) D)->Undef++;
1080 void O65WriteTarget (O65Desc* D, File* F)
1081 /* Write an o65 output file */
1084 char OptBuf [256]; /* Buffer for option strings */
1087 /* Place the filename in the control structure */
1088 D->Filename = F->Name;
1090 /* The o65 format uses only one memory area per file. Check that. */
1093 Warning ("Cannot handle more than one memory area for o65 format");
1096 /* Check for unresolved symbols. The function O65Unresolved is called
1097 * if we get an unresolved symbol.
1099 D->Undef = 0; /* Reset the counter */
1100 CheckExports (O65Unresolved, D);
1102 /* We had unresolved symbols, cannot create output file */
1103 Error ("%u unresolved external(s) found - cannot create output file", D->Undef);
1106 /* Setup the segment arrays */
1107 O65SetupSegments (D, M);
1110 D->F = fopen (F->Name, "wb");
1112 Error ("Cannot open `%s': %s", F->Name, strerror (errno));
1115 /* Keep the user happy */
1116 Print (stdout, 1, "Opened `%s'...\n", F->Name);
1118 /* Define some more options: A timestamp and the linker version */
1120 strcpy (OptBuf, ctime (&T));
1121 O65SetOption (D, O65OPT_TIMESTAMP, OptBuf, strlen (OptBuf) + 1);
1122 sprintf (OptBuf, "ld65 V%u.%u.%u", VER_MAJOR, VER_MINOR, VER_PATCH);
1123 O65SetOption (D, O65OPT_ASM, OptBuf, strlen (OptBuf) + 1);
1125 /* Write the header */
1128 /* Write the text segment */
1129 O65WriteTextSeg (D, M);
1131 /* Write the data segment */
1132 O65WriteDataSeg (D, M);
1134 /* "Write" the bss segments */
1135 O65WriteBssSeg (D, M);
1137 /* "Write" the zeropage segments */
1138 O65WriteZPSeg (D, M);
1140 /* Write the undefined references list */
1141 O65WriteImports (D);
1143 /* Write the text segment relocation table */
1144 O65WriteTextReloc (D);
1146 /* Write the data segment relocation table */
1147 O65WriteDataReloc (D);
1149 /* Write the list of exports */
1150 O65WriteExports (D);
1152 /* Seek back to the start and write the updated header */
1153 fseek (D->F, 0, SEEK_SET);
1156 /* Close the file */
1157 if (fclose (D->F) != 0) {
1158 Error ("Cannot write to `%s': %s", F->Name, strerror (errno));
1161 /* Reset the file and filename */