1 /*****************************************************************************/
5 /* Module to handle the o65 binary format */
9 /* (C) 1999-2000 Ullrich von Bassewitz */
11 /* D-70597 Stuttgart */
12 /* EMail: uz@musoftware.de */
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 /*****************************************************************************/
57 /*****************************************************************************/
59 /*****************************************************************************/
63 /* Header mode bits */
64 #define MF_SIZE_32BIT 0x2000 /* All size words are 32bit */
65 #define MF_CPU_816 0x8000 /* Executable is for 65816 */
67 /* The four o65 segment types. Note: These values are identical to the values
68 * needed for the segmentID in the o65 spec.
70 #define O65SEG_UNDEF 0x00
71 #define O65SEG_ABS 0x01
72 #define O65SEG_TEXT 0x02
73 #define O65SEG_DATA 0x03
74 #define O65SEG_BSS 0x04
75 #define O65SEG_ZP 0x05
77 /* Relocation type codes for the o65 format */
78 #define O65RELOC_WORD 0x80
79 #define O65RELOC_HIGH 0x40
80 #define O65RELOC_LOW 0x20
81 #define O65RELOC_SEGADR 0xc0
82 #define O65RELOC_SEG 0xa0
84 /* O65 executable file header */
85 typedef struct O65Header_ O65Header;
87 unsigned Version; /* Version number for o65 format */
88 unsigned Mode; /* Mode word */
89 unsigned long TextBase; /* Base address of text segment */
90 unsigned long TextSize; /* Size of text segment */
91 unsigned long DataBase; /* Base of data segment */
92 unsigned long DataSize; /* Size of data segment */
93 unsigned long BssBase; /* Base of bss segment */
94 unsigned long BssSize; /* Size of bss segment */
95 unsigned long ZPBase; /* Base of zeropage segment */
96 unsigned long ZPSize; /* Size of zeropage segment */
97 unsigned long StackSize; /* Requested stack size */
101 typedef struct O65Option_ O65Option;
103 O65Option* Next; /* Next in option list */
104 unsigned char Type; /* Type of option */
105 unsigned char Len; /* Data length */
106 unsigned char Data [1]; /* Data, dynamically allocated */
109 /* A o65 relocation table */
110 #define RELOC_BLOCKSIZE 4096
111 typedef struct O65RelocTab_ O65RelocTab;
112 struct O65RelocTab_ {
113 unsigned Size; /* Size of the table */
114 unsigned Fill; /* Amount used */
115 unsigned char* Buf; /* Buffer, dynamically allocated */
118 /* Structure describing the format */
120 O65Header Header; /* File header */
121 O65Option* Options; /* List of file options */
122 ExtSymTab* Exports; /* Table with exported symbols */
123 ExtSymTab* Imports; /* Table with imported symbols */
124 unsigned Undef; /* Count of undefined symbols */
125 FILE* F; /* The file we're writing to */
126 char* Filename; /* Name of the output file */
127 O65RelocTab* TextReloc; /* Relocation table for text segment */
128 O65RelocTab* DataReloc; /* Relocation table for data segment */
130 unsigned TextCount; /* Number of segments assigned to .text */
131 SegDesc** TextSeg; /* Array of text segments */
132 unsigned DataCount; /* Number of segments assigned to .data */
133 SegDesc** DataSeg; /* Array of data segments */
134 unsigned BssCount; /* Number of segments assigned to .bss */
135 SegDesc** BssSeg; /* Array of bss segments */
136 unsigned ZPCount; /* Number of segments assigned to .zp */
137 SegDesc** ZPSeg; /* Array of zp segments */
139 /* Temporary data for writing segments */
140 unsigned long SegSize;
141 O65RelocTab* CurReloc;
145 /* Structure for parsing expression trees */
146 typedef struct ExprDesc_ ExprDesc;
148 O65Desc* D; /* File format descriptor */
149 long Val; /* The offset value */
150 int TooComplex; /* Expression too complex */
151 Section* SegRef; /* Section referenced if any */
152 ExtSym* ExtRef; /* External reference if any */
157 /*****************************************************************************/
158 /* Helper functions */
159 /*****************************************************************************/
163 static void WriteSize (const O65Desc* D, unsigned long Val)
164 /* Write a "size" word to the file */
166 if (D->Header.Mode & MF_SIZE_32BIT) {
169 Write16 (D->F, (unsigned) Val);
175 static unsigned O65SegType (const SegDesc* S)
176 /* Map our own segment types into something o65 compatible */
178 /* Check the segment type. Readonly segments are assign to the o65
179 * text segment, writeable segments that contain data are assigned
180 * to data, bss and zp segments are handled respectively.
181 * Beware: Zeropage segments have the SF_BSS flag set, so be sure
182 * to check SF_ZP first.
184 if (S->Flags & SF_RO) {
186 } else if (S->Flags & SF_ZP) {
188 } else if (S->Flags & SF_BSS) {
197 /*****************************************************************************/
198 /* Expression handling */
199 /*****************************************************************************/
203 static void O65ParseExpr (ExprNode* Expr, ExprDesc* D, int Sign)
204 /* Extract and evaluate all constant factors in an subtree that has only
205 * additions and subtractions. If anything other than additions and
206 * subtractions are found, D->TooComplex is set to true.
215 D->Val -= Expr->V.Val;
217 D->Val += Expr->V.Val;
222 /* Get the referenced Export */
223 E = GetExprExport (Expr);
224 /* If this export has a mark set, we've already encountered it.
225 * This means that the export is used to define it's own value,
226 * which in turn means, that we have a circular reference.
228 if (ExportHasMark (E)) {
229 CircularRefError (E);
230 } else if (E->Expr == 0) {
231 /* Dummy export, must be an o65 imported symbol */
232 ExtSym* S = O65GetImport (D->D, E->Name);
235 /* We cannot have more than one external reference in o65 */
238 /* Remember the external reference */
243 O65ParseExpr (E->Expr, D, Sign);
250 /* We cannot handle more than one segment reference in o65 */
253 /* Remember the segment reference */
254 D->SegRef = GetExprSection (Expr);
259 O65ParseExpr (Expr->Left, D, Sign);
260 O65ParseExpr (Expr->Right, D, Sign);
264 O65ParseExpr (Expr->Left, D, Sign);
265 O65ParseExpr (Expr->Right, D, -Sign);
269 /* Expression contains illegal operators */
278 /*****************************************************************************/
279 /* Relocation tables */
280 /*****************************************************************************/
284 static O65RelocTab* NewO65RelocTab (void)
285 /* Create a new relocation table */
287 /* Allocate a new structure */
288 O65RelocTab* R = xmalloc (sizeof (O65RelocTab));
290 /* Initialize the data */
291 R->Size = RELOC_BLOCKSIZE;
293 R->Buf = xmalloc (RELOC_BLOCKSIZE);
295 /* Return the created struct */
301 static void FreeO65RelocTab (O65RelocTab* R)
302 /* Free a relocation table */
310 static void O65RelocPutByte (O65RelocTab* R, unsigned B)
311 /* Put the byte into the relocation table */
313 /* Do we have enough space in the buffer? */
314 if (R->Fill == R->Size) {
315 /* We need to grow the buffer */
316 unsigned char* NewBuf = xmalloc (R->Size + RELOC_BLOCKSIZE);
317 memcpy (NewBuf, R->Buf, R->Size);
322 /* Put the byte into the buffer */
323 R->Buf [R->Fill++] = (unsigned char) B;
328 static void O65RelocPutWord (O65RelocTab* R, unsigned W)
329 /* Put a word into the relocation table */
331 O65RelocPutByte (R, W);
332 O65RelocPutByte (R, W >> 8);
337 static void O65WriteReloc (O65RelocTab* R, FILE* F)
338 /* Write the relocation table to the given file */
340 WriteData (F, R->Buf, R->Fill);
345 /*****************************************************************************/
346 /* Option handling */
347 /*****************************************************************************/
351 static O65Option* NewO65Option (unsigned Type, const void* Data, unsigned DataLen)
352 /* Allocate and initialize a new option struct */
356 /* Check the length */
357 CHECK (DataLen <= 253);
359 /* Allocate memory */
360 O = xmalloc (sizeof (O65Option) - 1 + DataLen);
362 /* Initialize the structure */
366 memcpy (O->Data, Data, DataLen);
368 /* Return the created struct */
374 static void FreeO65Option (O65Option* O)
375 /* Free an O65Option struct */
382 /*****************************************************************************/
383 /* Subroutines to write o65 sections */
384 /*****************************************************************************/
388 static void O65WriteHeader (O65Desc* D)
389 /* Write the header of the executable to the given file */
391 static unsigned char Trailer [5] = {
392 0x01, 0x00, 0x6F, 0x36, 0x35
398 /* Write the fixed header */
399 WriteData (D->F, Trailer, sizeof (Trailer));
400 Write8 (D->F, D->Header.Version);
401 Write16 (D->F, D->Header.Mode);
402 WriteSize (D, D->Header.TextBase);
403 WriteSize (D, D->Header.TextSize);
404 WriteSize (D, D->Header.DataBase);
405 WriteSize (D, D->Header.DataSize);
406 WriteSize (D, D->Header.BssBase);
407 WriteSize (D, D->Header.BssSize);
408 WriteSize (D, D->Header.ZPBase);
409 WriteSize (D, D->Header.ZPSize);
410 WriteSize (D, D->Header.StackSize);
412 /* Write the options */
415 Write8 (D->F, O->Len + 2); /* Account for len and type bytes */
416 Write8 (D->F, O->Type);
418 WriteData (D->F, O->Data, O->Len);
423 /* Write the end-of-options byte */
429 static unsigned O65WriteExpr (ExprNode* E, int Signed, unsigned Size,
430 unsigned long Offs, void* Data)
431 /* Called from SegWrite for an expression. Evaluate the expression, check the
432 * range and write the expression value to the file, update the relocation
440 unsigned char RelocType;
442 /* Cast the Data pointer to its real type, an O65Desc */
443 O65Desc* D = (O65Desc*) Data;
445 /* Check for a constant expression */
446 if (IsConstExpr (E)) {
447 /* Write out the constant expression */
448 return SegWriteConstExpr (((O65Desc*)Data)->F, E, Signed, Size);
451 /* We have a relocatable expression that needs a relocation table entry.
452 * Calculate the number of bytes between this entry and the last one, and
453 * setup all necessary intermediate bytes in the relocation table.
455 Offs += D->SegSize; /* Calulate full offset */
456 Diff = ((long) Offs) - D->LastOffs;
457 while (Diff > 0xFE) {
458 O65RelocPutByte (D->CurReloc, 0xFF);
461 O65RelocPutByte (D->CurReloc, (unsigned char) Diff);
463 /* Remember this offset for the next time */
466 /* Determine the expression to relocate */
468 if (E->Op == EXPR_BYTE0 || E->Op == EXPR_BYTE1 ||
469 E->Op == EXPR_BYTE2 || E->Op == EXPR_BYTE3 ||
470 E->Op == EXPR_WORD0 || E->Op == EXPR_WORD1) {
471 /* Use the real expression */
475 /* Initialize the descriptor for expression parsing */
482 /* Recursively collect information about this expression */
483 O65ParseExpr (Expr, &ED, 1);
485 /* We cannot handle both, an imported symbol and a segment ref */
486 if (ED.SegRef != 0 && ED.ExtRef != 0) {
490 /* Bail out if we cannot handle the expression */
492 return SEG_EXPR_TOO_COMPLEX;
495 /* Safety: Check that we are really referencing a symbol or a segment */
496 CHECK (ED.SegRef != 0 || ED.ExtRef != 0);
498 /* Write out the offset that goes into the segment. */
501 case EXPR_BYTE0: BinVal &= 0xFF; break;
502 case EXPR_BYTE1: BinVal = (BinVal >> 8) & 0xFF; break;
503 case EXPR_BYTE2: BinVal = (BinVal >> 16) & 0xFF; break;
504 case EXPR_BYTE3: BinVal = (BinVal >> 24) & 0xFF; break;
505 case EXPR_WORD0: BinVal &= 0xFFFF; break;
506 case EXPR_WORD1: BinVal = (BinVal >> 16) & 0xFFFF; break;
508 WriteVal (D->F, BinVal, Size);
510 /* Determine the actual type of relocation entry needed from the
511 * information gathered about the expression.
513 if (E->Op == EXPR_BYTE0) {
514 RelocType = O65RELOC_LOW;
515 } else if (E->Op == EXPR_BYTE1) {
516 RelocType = O65RELOC_HIGH;
521 RelocType = O65RELOC_LOW;
525 RelocType = O65RELOC_WORD;
529 RelocType = O65RELOC_SEGADR;
533 /* 4 byte expression not supported by o65 */
534 return SEG_EXPR_TOO_COMPLEX;
537 Internal ("O65WriteExpr: Invalid expression size: %u", Size);
538 RelocType = 0; /* Avoid gcc warnings */
542 /* Determine which segment we're referencing */
544 /* Imported symbol */
545 RelocType |= O65SEG_UNDEF;
546 O65RelocPutByte (D->CurReloc, RelocType);
547 /* Put the number of the imported symbol into the table */
548 O65RelocPutWord (D->CurReloc, ExtSymNum (ED.ExtRef));
550 /* Segment reference */
562 static void O65WriteSeg (O65Desc* D, SegDesc** Seg, unsigned Count, int DoWrite)
563 /* Write one segment to the o65 output file */
568 /* Initialize variables */
572 /* Write out all segments */
573 for (I = 0; I < Count; ++I) {
575 /* Get the segment from the list node */
578 /* Keep the user happy */
579 Print (stdout, 1, " Writing `%s'\n", S->Name);
581 /* Write this segment */
583 SegWrite (D->F, S->Seg, O65WriteExpr, D);
586 /* Mark the segment as dumped */
589 /* Calculate the total size */
590 D->SegSize += S->Seg->Size;
593 /* Terminate the relocation table for the this segment */
595 O65RelocPutByte (D->CurReloc, 0);
598 /* Check the size of the segment for overflow */
599 if ((D->Header.Mode & MF_SIZE_32BIT) == 0 && D->SegSize > 0xFFFF) {
600 Error ("Segment overflow in file `%s'", D->Filename);
607 static void O65WriteTextSeg (O65Desc* D, Memory* M)
608 /* Write the code segment to the o65 output file */
610 /* Initialize variables */
611 D->CurReloc = D->TextReloc;
613 /* Dump all text segments */
614 O65WriteSeg (D, D->TextSeg, D->TextCount, 1);
616 /* Set the size of the segment */
617 D->Header.TextSize = D->SegSize;
622 static void O65WriteDataSeg (O65Desc* D, Memory* M)
623 /* Write the data segment to the o65 output file */
625 /* Initialize variables */
626 D->CurReloc = D->DataReloc;
628 /* Dump all data segments */
629 O65WriteSeg (D, D->DataSeg, D->DataCount, 1);
631 /* Set the size of the segment */
632 D->Header.DataSize = D->SegSize;
637 static void O65WriteBssSeg (O65Desc* D, Memory* M)
638 /* "Write" the bss segments to the o65 output file. This will only update
639 * the relevant header fields.
642 /* Initialize variables */
645 /* Dump all data segments */
646 O65WriteSeg (D, D->BssSeg, D->BssCount, 0);
648 /* Set the size of the segment */
649 D->Header.BssSize = D->SegSize;
654 static void O65WriteZPSeg (O65Desc* D, Memory* M)
655 /* "Write" the zeropage segments to the o65 output file. This will only update
656 * the relevant header fields.
659 /* Initialize variables */
662 /* Dump all data segments */
663 O65WriteSeg (D, D->ZPSeg, D->ZPCount, 0);
665 /* Set the size of the segment */
666 D->Header.ZPSize = D->SegSize;
671 static void O65WriteImports (O65Desc* D)
672 /* Write the list of imported symbols to the O65 file */
676 /* Write the number of external symbols */
677 WriteSize (D, ExtSymCount (D->Imports));
679 /* Write out the symbol names, zero terminated */
680 E = ExtSymList (D->Imports);
683 const char* Name = ExtSymName (E);
684 /* And write it to the output file */
685 WriteData (D->F, Name, strlen (Name) + 1);
693 static void O65WriteTextReloc (O65Desc* D)
694 /* Write the relocation for the text segment to the output file */
696 O65WriteReloc (D->TextReloc, D->F);
701 static void O65WriteDataReloc (O65Desc* D)
702 /* Write the relocation for the data segment to the output file */
704 O65WriteReloc (D->DataReloc, D->F);
709 static void O65WriteExports (O65Desc* D)
710 /* Write the list of exports */
718 /*****************************************************************************/
720 /*****************************************************************************/
724 O65Desc* NewO65Desc (void)
725 /* Create, initialize and return a new O65 descriptor struct */
727 /* Allocate a new structure */
728 O65Desc* D = xmalloc (sizeof (O65Desc));
730 /* Initialize the header */
731 D->Header.Version = 0;
733 D->Header.TextBase = 0;
734 D->Header.TextSize = 0;
735 D->Header.DataBase = 0;
736 D->Header.DataSize = 0;
737 D->Header.BssBase = 0;
738 D->Header.BssSize = 0;
739 D->Header.ZPBase = 0;
740 D->Header.ZPSize = 0;
741 D->Header.StackSize = 0; /* Let OS choose a good value */
743 /* Initialize other data */
745 D->Exports = NewExtSymTab ();
746 D->Imports = NewExtSymTab ();
750 D->TextReloc = NewO65RelocTab ();
751 D->DataReloc = NewO65RelocTab ();
761 /* Return the created struct */
767 void FreeO65Desc (O65Desc* D)
768 /* Delete the descriptor struct with cleanup */
770 /* Free the segment arrays */
776 /* Free the relocation tables */
777 FreeO65RelocTab (D->DataReloc);
778 FreeO65RelocTab (D->TextReloc);
780 /* Free the option list */
782 O65Option* O = D->Options;
783 D->Options = D->Options->Next;
787 /* Free the external symbol tables */
788 FreeExtSymTab (D->Exports);
789 FreeExtSymTab (D->Imports);
791 /* Free the struct itself */
797 void O65Set816 (O65Desc* D)
798 /* Enable 816 mode */
800 D->Header.Mode |= MF_CPU_816;
805 void O65SetLargeModel (O65Desc* D)
806 /* Enable a large memory model executable */
808 D->Header.Mode |= MF_SIZE_32BIT;
813 void O65SetAlignment (O65Desc* D, unsigned Align)
814 /* Set the executable alignment */
816 /* Remove all alignment bits from the mode word */
817 D->Header.Mode &= ~0x0003;
819 /* Set the alignment bits */
822 case 2: D->Header.Mode |= 0x01; break;
823 case 4: D->Header.Mode |= 0x02; break;
824 case 256: D->Header.Mode |= 0x03; break;
825 default: Error ("Invalid alignment for O65 format: %u", Align);
831 void O65SetOption (O65Desc* D, unsigned Type, const void* Data, unsigned DataLen)
832 /* Set an o65 header option */
834 /* Create a new option structure */
835 O65Option* O = NewO65Option (Type, Data, DataLen);
837 /* Insert it into the linked list */
838 O->Next = D->Options;
844 void O65SetOS (O65Desc* D, unsigned OS)
845 /* Set an option describing the target operating system */
847 static const unsigned char OSA65 [2] = { O65OS_OSA65, 0 };
848 static const unsigned char Lunix [2] = { O65OS_LUNIX, 0 };
850 /* Write the correct option */
854 O65SetOption (D, O65OPT_OS, OSA65, sizeof (OSA65));
858 O65SetOption (D, O65OPT_OS, Lunix, sizeof (Lunix));
862 Internal ("Trying to set invalid O65 operating system: %u", OS);
869 ExtSym* O65GetImport (O65Desc* D, const char* Ident)
870 /* Return the imported symbol or NULL if not found */
872 /* Retrieve the symbol from the table */
873 return GetExtSym (D->Imports, Ident);
878 void O65SetImport (O65Desc* D, const char* Ident)
879 /* Set an imported identifier */
881 /* Insert the entry into the table */
882 NewExtSym (D->Imports, Ident);
887 ExtSym* O65GetExport (O65Desc* D, const char* Ident)
888 /* Return the exported symbol or NULL if not found */
890 /* Retrieve the symbol from the table */
891 return GetExtSym (D->Exports, Ident);
896 void O65SetExport (O65Desc* D, const char* Ident)
897 /* Set an exported identifier */
899 /* Insert the entry into the table */
900 NewExtSym (D->Exports, Ident);
905 static void O65SetupSegments (O65Desc* D, Memory* M)
906 /* Setup segment assignments */
910 unsigned TextIdx, DataIdx, BssIdx, ZPIdx;
912 /* Initialize the counters */
918 /* Walk through the memory list and count the segment types */
922 /* Get the segment from the list node */
925 /* Check the segment type. */
926 switch (O65SegType (S)) {
927 case O65SEG_TEXT: D->TextCount++; break;
928 case O65SEG_DATA: D->DataCount++; break;
929 case O65SEG_BSS: D->BssCount++; break;
930 case O65SEG_ZP: D->ZPCount++; break;
931 default: Internal ("Invalid return from O65SegType");
934 /* Next segment node */
938 /* Allocate memory according to the numbers */
939 D->TextSeg = xmalloc (D->TextCount * sizeof (SegDesc*));
940 D->DataSeg = xmalloc (D->DataCount * sizeof (SegDesc*));
941 D->BssSeg = xmalloc (D->BssCount * sizeof (SegDesc*));
942 D->ZPSeg = xmalloc (D->ZPCount * sizeof (SegDesc*));
944 /* Walk again through the list and setup the segment arrays */
945 TextIdx = DataIdx = BssIdx = ZPIdx = 0;
949 /* Get the segment from the list node */
952 /* Check the segment type. */
953 switch (O65SegType (S)) {
954 case O65SEG_TEXT: D->TextSeg [TextIdx++] = S; break;
955 case O65SEG_DATA: D->DataSeg [DataIdx++] = S; break;
956 case O65SEG_BSS: D->BssSeg [BssIdx++] = S; break;
957 case O65SEG_ZP: D->ZPSeg [ZPIdx++] = S; break;
958 default: Internal ("Invalid return from O65SegType");
961 /* Next segment node */
968 static int O65Unresolved (const char* Name, void* D)
969 /* Called if an unresolved symbol is encountered */
971 /* Check if the symbol is an imported o65 symbol */
972 if (O65GetImport (D, Name) != 0) {
973 /* This is an external symbol, relax... */
976 /* This is actually an unresolved external. Bump the counter */
977 ((O65Desc*) D)->Undef++;
984 void O65WriteTarget (O65Desc* D, File* F)
985 /* Write an o65 output file */
988 char OptBuf [256]; /* Buffer for option strings */
991 /* Place the filename in the control structure */
992 D->Filename = F->Name;
994 /* The o65 format uses only one memory area per file. Check that. */
997 Warning ("Cannot handle more than one memory area for o65 format");
1000 /* Check for unresolved symbols. The function O65Unresolved is called
1001 * if we get an unresolved symbol.
1003 D->Undef = 0; /* Reset the counter */
1004 CheckExports (O65Unresolved, D);
1006 /* We had unresolved symbols, cannot create output file */
1007 Error ("%u unresolved external(s) found - cannot create output file", D->Undef);
1010 /* Setup the segment arrays */
1011 O65SetupSegments (D, M);
1014 D->F = fopen (F->Name, "wb");
1016 Error ("Cannot open `%s': %s", F->Name, strerror (errno));
1019 /* Keep the user happy */
1020 Print (stdout, 1, "Opened `%s'...\n", F->Name);
1022 /* Define some more options: A timestamp and the linker version */
1024 strcpy (OptBuf, ctime (&T));
1025 O65SetOption (D, O65OPT_TIMESTAMP, OptBuf, strlen (OptBuf) + 1);
1026 sprintf (OptBuf, "ld65 V%u.%u.%u", VER_MAJOR, VER_MINOR, VER_PATCH);
1027 O65SetOption (D, O65OPT_ASM, OptBuf, strlen (OptBuf) + 1);
1029 /* Write the header */
1032 /* Write the text segment */
1033 O65WriteTextSeg (D, M);
1035 /* Write the data segment */
1036 O65WriteDataSeg (D, M);
1038 /* "Write" the bss segments */
1039 O65WriteBssSeg (D, M);
1041 /* "Write" the zeropage segments */
1042 O65WriteZPSeg (D, M);
1044 /* Write the undefined references list */
1045 O65WriteImports (D);
1047 /* Write the text segment relocation table */
1048 O65WriteTextReloc (D);
1050 /* Write the data segment relocation table */
1051 O65WriteDataReloc (D);
1053 /* Write the list of exports */
1054 O65WriteExports (D);
1056 /* Seek back to the start and write the updated header */
1057 fseek (D->F, 0, SEEK_SET);
1060 /* Close the file */
1061 if (fclose (D->F) != 0) {
1062 Error ("Cannot write to `%s': %s", F->Name, strerror (errno));
1065 /* Reset the file and filename */