1/*****************************************************************************/
2/* */
3/* o65.c */
4/* */
5/* Module to handle the o65 binary format */
6/* */
7/* */
8/* */
9/* (C) 1999-2012, Ullrich von Bassewitz */
10/* Roemerstrasse 52 */
11/* D-70794 Filderstadt */
12/* EMail: uz@cc65.org */
13/* */
14/* */
15/* This software is provided 'as-is', without any expressed or implied */
16/* warranty. In no event will the authors be held liable for any damages */
17/* arising from the use of this software. */
18/* */
19/* Permission is granted to anyone to use this software for any purpose, */
20/* including commercial applications, and to alter it and redistribute it */
21/* freely, subject to the following restrictions: */
22/* */
23/* 1. The origin of this software must not be misrepresented; you must not */
24/* claim that you wrote the original software. If you use this software */
25/* in a product, an acknowledgment in the product documentation would be */
26/* appreciated but is not required. */
27/* 2. Altered source versions must be plainly marked as such, and must not */
28/* be misrepresented as being the original software. */
29/* 3. This notice may not be removed or altered from any source */
30/* distribution. */
31/* */
32/*****************************************************************************/
33
34
35
36#include <stdio.h>
37#include <string.h>
38#include <limits.h>
39#include <errno.h>
40#include <time.h>
41
42/* common */
43#include "chartype.h"
44#include "check.h"
45#include "fname.h"
46#include "print.h"
47#include "version.h"
48#include "xmalloc.h"
49
50/* ld65 */
51#include "error.h"
52#include "exports.h"
53#include "expr.h"
54#include "fileio.h"
55#include "global.h"
56#include "lineinfo.h"
57#include "memarea.h"
58#include "o65.h"
59#include "spool.h"
60
61
62
63/*****************************************************************************/
64/* Data */
65/*****************************************************************************/
66
67
68
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 */
73
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 */
77
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 */
81
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 */
85
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 */
89
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 */
95
96/* The four o65 segment types. Note: These values are identical to the values
97** needed for the segmentID in the o65 spec.
98*/
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
105
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
113
114/* O65 executable file header */
115typedef struct O65Header O65Header;
116struct 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 */
128};
129
130/* An o65 option */
131typedef struct O65Option O65Option;
132struct 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 */
137};
138
139/* A o65 relocation table */
140typedef struct O65RelocTab O65RelocTab;
141struct O65RelocTab {
142 unsigned Size; /* Size of the table */
143 unsigned Fill; /* Amount used */
144 unsigned char* Buf; /* Buffer, dynamically allocated */
145};
146
147/* Structure describing the format */
148struct O65Desc {
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 */
158
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 */
167
168 /* Temporary data for writing segments */
169 unsigned long SegSize;
170 O65RelocTab* CurReloc;
171 long LastOffs;
172};
173
174/* Structure for parsing expression trees */
175typedef struct ExprDesc ExprDesc;
176struct 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 */
184};
185
186
187
188/*****************************************************************************/
189/* Helper functions */
190/*****************************************************************************/
191
192
193
194static ExprDesc* InitExprDesc (ExprDesc* ED, O65Desc* D)
195/* Initialize an ExprDesc structure for use with O65ParseExpr */
196{
197 ED->D = D;
198 ED->Val = 0;
199 ED->TooComplex = 0;
200 ED->MemRef = 0;
201 ED->SegRef = 0;
202 ED->SecRef = 0;
203 ED->ExtRef = 0;
204 return ED;
205}
206
207
208
209static void WriteSize (const O65Desc* D, unsigned long Val)
210/* Write a "size" word to the file */
211{
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);
216 }
217}
218
219
220
221static unsigned O65SegType (const SegDesc* S)
222/* Map our own segment types into something o65 compatible */
223{
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.
229 */
230 if (S->Flags & SF_RO) {
231 return O65SEG_TEXT;
232 } else if (S->Flags & SF_ZP) {
233 return O65SEG_ZP;
234 } else if (S->Flags & SF_BSS) {
235 return O65SEG_BSS;
236 } else {
237 return O65SEG_DATA;
238 }
239}
240
241
242
243static 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.
246*/
247{
248 /* Get the memory area from the expression */
249 MemoryArea* M = ED->MemRef;
250
251 /* Remember the "nearest" segment and its offset */
252 Segment* Nearest = 0;
253 unsigned long Offs = ULONG_MAX;
254
255 /* Walk over all segments */
256 unsigned I;
257 for (I = 0; I < CollCount (&M->SegList); ++I) {
258
259 /* Get the segment and check if it's a run segment */
260 SegDesc* S = CollAtUnchecked (&M->SegList, I);
261 if (S->Run == M) {
262
263 unsigned long O;
264
265 /* Get the segment from the segment descriptor */
266 Segment* Seg = S->Seg;
267
268 /* Check the PC. */
269 if ((long) Seg->PC <= ED->Val && (O = (ED->Val - Seg->PC)) < Offs) {
270 /* This is the nearest segment for now */
271 Offs = O;
272 Nearest = Seg;
273
274 /* If we found an exact match, don't look further */
275 if (Offs == 0) {
276 break;
277 }
278 }
279 }
280 }
281
282 /* If we found a segment, use it and adjust the offset */
283 if (Nearest) {
284 ED->SegRef = Nearest;
285 ED->MemRef = 0;
286 ED->Val -= Nearest->PC;
287 }
288}
289
290
291
292static 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.
295*/
296{
297 unsigned I;
298
299 for (I = 0; I < Count; ++I) {
300 if (List[I]->Seg == S) {
301 /* Found */
302 return List[I];
303 }
304 }
305
306 /* Not found */
307 return 0;
308}
309
310
311
312static const SegDesc* O65FindSeg (const O65Desc* D, const Segment* S)
313/* Search for a segment in the segment lists and return it's segment descriptor */
314{
315 const SegDesc* SD;
316
317 if ((SD = FindSeg (D->TextSeg, D->TextCount, S)) != 0) {
318 return SD;
319 }
320 if ((SD = FindSeg (D->DataSeg, D->DataCount, S)) != 0) {
321 return SD;
322 }
323 if ((SD = FindSeg (D->BssSeg, D->BssCount, S)) != 0) {
324 return SD;
325 }
326 if ((SD = FindSeg (D->ZPSeg, D->ZPCount, S)) != 0) {
327 return SD;
328 }
329
330 /* Not found */
331 return 0;
332}
333
334
335
336/*****************************************************************************/
337/* Expression handling */
338/*****************************************************************************/
339
340
341
342static 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.
346*/
347{
348 Export* E;
349
350 switch (Expr->Op) {
351
352 case EXPR_LITERAL:
353 D->Val += (Sign * Expr->V.IVal);
354 break;
355
356 case EXPR_SYMBOL:
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.
362 */
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);
368 CHECK (S != 0);
369 if (D->ExtRef) {
370 /* We cannot have more than one external reference in o65 */
371 D->TooComplex = 1;
372 } else {
373 /* Remember the external reference */
374 D->ExtRef = S;
375 }
376 } else {
377 MarkExport (E);
378 O65ParseExpr (E->Expr, D, Sign);
379 UnmarkExport (E);
380 }
381 break;
382
383 case EXPR_SECTION:
384 if (D->SecRef) {
385 /* We cannot handle more than one segment reference in o65 */
386 D->TooComplex = 1;
387 } else {
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);
392 }
393 break;
394
395 case EXPR_SEGMENT:
396 if (D->SegRef) {
397 /* We cannot handle more than one segment reference in o65 */
398 D->TooComplex = 1;
399 } else {
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);
404 }
405 break;
406
407 case EXPR_MEMAREA:
408 if (D->MemRef) {
409 /* We cannot handle more than one memory reference in o65 */
410 D->TooComplex = 1;
411 } else {
412 /* Remember the memory area reference */
413 D->MemRef = Expr->V.Mem;
414 /* Add the start address of the memory area to the constant
415 ** value
416 */
417 D->Val += (Sign * D->MemRef->Start);
418 }
419 break;
420
421 case EXPR_PLUS:
422 O65ParseExpr (Expr->Left, D, Sign);
423 O65ParseExpr (Expr->Right, D, Sign);
424 break;
425
426 case EXPR_MINUS:
427 O65ParseExpr (Expr->Left, D, Sign);
428 O65ParseExpr (Expr->Right, D, -Sign);
429 break;
430
431 default:
432 /* Expression contains illegal operators */
433 D->TooComplex = 1;
434 break;
435
436 }
437}
438
439
440
441/*****************************************************************************/
442/* Relocation tables */
443/*****************************************************************************/
444
445
446
447static O65RelocTab* NewO65RelocTab (void)
448/* Create a new relocation table */
449{
450 /* Allocate a new structure */
451 O65RelocTab* R = xmalloc (sizeof (O65RelocTab));
452
453 /* Initialize the data */
454 R->Size = 0;
455 R->Fill = 0;
456 R->Buf = 0;
457
458 /* Return the created struct */
459 return R;
460}
461
462
463
464static void FreeO65RelocTab (O65RelocTab* R)
465/* Free a relocation table */
466{
467 xfree (R->Buf);
468 xfree (R);
469}
470
471
472
473static void O65RelocPutByte (O65RelocTab* R, unsigned B)
474/* Put the byte into the relocation table */
475{
476 /* Do we have enough space in the buffer? */
477 if (R->Fill == R->Size) {
478 /* We need to grow the buffer */
479 if (R->Size) {
480 R->Size *= 2;
481 } else {
482 R->Size = 1024; /* Initial size */
483 }
484 R->Buf = xrealloc (R->Buf, R->Size);
485 }
486
487 /* Put the byte into the buffer */
488 R->Buf [R->Fill++] = (unsigned char) B;
489}
490
491
492
493static void O65RelocPutWord (O65RelocTab* R, unsigned W)
494/* Put a word into the relocation table */
495{
496 O65RelocPutByte (R, W);
497 O65RelocPutByte (R, W >> 8);
498}
499
500
501
502static void O65WriteReloc (O65RelocTab* R, FILE* F)
503/* Write the relocation table to the given file */
504{
505 WriteData (F, R->Buf, R->Fill);
506}
507
508
509
510/*****************************************************************************/
511/* Option handling */
512/*****************************************************************************/
513
514
515
516static O65Option* NewO65Option (unsigned Type, const void* Data, unsigned DataLen)
517/* Allocate and initialize a new option struct */
518{
519 O65Option* O;
520
521 /* Check the length */
522 CHECK (DataLen <= 253);
523
524 /* Allocate memory */
525 O = xmalloc (sizeof (O65Option) - 1 + DataLen);
526
527 /* Initialize the structure */
528 O->Next = 0;
529 O->Type = Type;
530 O->Len = DataLen;
531 memcpy (O->Data, Data, DataLen);
532
533 /* Return the created struct */
534 return O;
535}
536
537
538
539static void FreeO65Option (O65Option* O)
540/* Free an O65Option struct */
541{
542 xfree (O);
543}
544
545
546
547/*****************************************************************************/
548/* Subroutines to write o65 sections */
549/*****************************************************************************/
550
551
552
553static void O65WriteHeader (O65Desc* D)
554/* Write the header of the executable to the given file */
555{
556 static unsigned char Trailer [5] = {
557 0x01, 0x00, 0x6F, 0x36, 0x35
558 };
559
560 O65Option* O;
561
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);
575
576 /* Write the options */
577 O = D->Options;
578 while (O) {
579 Write8 (D->F, O->Len + 2); /* Account for len and type bytes */
580 Write8 (D->F, O->Type);
581 if (O->Len) {
582 WriteData (D->F, O->Data, O->Len);
583 }
584 O = O->Next;
585 }
586
587 /* Write the end-of-options byte */
588 Write8 (D->F, 0);
589}
590
591
592
593static 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
597** table.
598*/
599{
600 long Diff;
601 unsigned RefCount;
602 long BinVal;
603 ExprNode* Expr;
604 ExprDesc ED;
605 unsigned char RelocType;
606
607 /* Cast the Data pointer to its real type, an O65Desc */
608 O65Desc* D = (O65Desc*) Data;
609
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);
614 }
615
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.
619 */
620 Offs += D->SegSize; /* Calulate full offset */
621 Diff = ((long) Offs) - D->LastOffs;
622 while (Diff > 0xFE) {
623 O65RelocPutByte (D->CurReloc, 0xFF);
624 Diff -= 0xFE;
625 }
626 O65RelocPutByte (D->CurReloc, (unsigned char) Diff);
627
628 /* Remember this offset for the next time */
629 D->LastOffs = Offs;
630
631 /* Determine the expression to relocate */
632 Expr = E;
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 E->Op == EXPR_NEARADDR) {
638 /* Use the real expression */
639 Expr = E->Left;
640 }
641
642 /* Recursively collect information about this expression */
643 O65ParseExpr (Expr, InitExprDesc (&ED, D), 1);
644
645 /* We cannot handle more than one external reference */
646 RefCount = (ED.MemRef != 0) + (ED.SegRef != 0) +
647 (ED.SecRef != 0) + (ED.ExtRef != 0);
648 if (RefCount > 1) {
649 ED.TooComplex = 1;
650 }
651
652 /* If we have a memory area reference, we need to convert it into a
653 ** segment reference. If we cannot do that, we cannot handle the
654 ** expression.
655 */
656 if (ED.MemRef) {
657 CvtMemoryToSegment (&ED);
658 if (ED.SegRef == 0) {
659 return SEG_EXPR_TOO_COMPLEX;
660 }
661 }
662
663 /* Bail out if we cannot handle the expression */
664 if (ED.TooComplex) {
665 return SEG_EXPR_TOO_COMPLEX;
666 }
667
668 /* Safety: Check that we have exactly one reference */
669 CHECK (RefCount == 1);
670
671 /* Write out the offset that goes into the segment. */
672 BinVal = ED.Val;
673 switch (E->Op) {
674 case EXPR_BYTE0: BinVal &= 0xFF; break;
675 case EXPR_BYTE1: BinVal = (BinVal >> 8) & 0xFF; break;
676 case EXPR_BYTE2: BinVal = (BinVal >> 16) & 0xFF; break;
677 case EXPR_BYTE3: BinVal = (BinVal >> 24) & 0xFF; break;
678 case EXPR_WORD0: BinVal &= 0xFFFF; break;
679 case EXPR_WORD1: BinVal = (BinVal >> 16) & 0xFFFF; break;
680 case EXPR_FARADDR: BinVal &= 0xFFFFFFUL; break;
681 case EXPR_DWORD: BinVal &= 0xFFFFFFFFUL; break;
682 case EXPR_NEARADDR: BinVal &= 0xFFFF; break;
683 }
684 WriteVal (D->F, BinVal, Size);
685
686 /* Determine the actual type of relocation entry needed from the
687 ** information gathered about the expression.
688 */
689 if (E->Op == EXPR_BYTE0) {
690 RelocType = O65RELOC_LOW;
691 } else if (E->Op == EXPR_BYTE1) {
692 RelocType = O65RELOC_HIGH;
693 } else if (E->Op == EXPR_BYTE2) {
694 RelocType = O65RELOC_SEG;
695 } else {
696 switch (Size) {
697
698 case 1:
699 RelocType = O65RELOC_LOW;
700 break;
701
702 case 2:
703 RelocType = O65RELOC_WORD;
704 break;
705
706 case 3:
707 RelocType = O65RELOC_SEGADR;
708 break;
709
710 case 4:
711 /* 4 byte expression not supported by o65 */
712 return SEG_EXPR_TOO_COMPLEX;
713
714 default:
715 Internal ("O65WriteExpr: Invalid expression size: %u", Size);
716 RelocType = 0; /* Avoid gcc warnings */
717 }
718 }
719
720 /* Determine which segment we're referencing */
721 if (ED.SegRef || ED.SecRef) {
722
723 const SegDesc* Seg;
724
725 /* Segment or section reference. */
726 if (ED.SecRef) {
727 /* Get segment from section */
728 ED.SegRef = ED.SecRef->Seg;
729 }
730
731 /* Search for the segment and map it to it's o65 segmentID */
732 Seg = O65FindSeg (D, ED.SegRef);
733 if (Seg == 0) {
734 /* For some reason, we didn't find this segment in the list of
735 ** segments written to the o65 file.
736 */
737 return SEG_EXPR_INVALID;
738 }
739 RelocType |= O65SegType (Seg);
740 O65RelocPutByte (D->CurReloc, RelocType);
741
742 /* Output additional data if needed */
743 switch (RelocType & O65RELOC_MASK) {
744 case O65RELOC_HIGH:
745 O65RelocPutByte (D->CurReloc, ED.Val & 0xFF);
746 break;
747 case O65RELOC_SEG:
748 O65RelocPutWord (D->CurReloc, ED.Val & 0xFFFF);
749 break;
750 }
751
752 } else if (ED.ExtRef) {
753 /* Imported symbol */
754 RelocType |= O65SEG_UNDEF;
755 O65RelocPutByte (D->CurReloc, RelocType);
756 /* Put the number of the imported symbol into the table */
757 O65RelocPutWord (D->CurReloc, ExtSymNum (ED.ExtRef));
758
759 } else {
760
761 /* OOPS - something bad happened */
762 Internal ("External reference not handled");
763
764 }
765
766 /* Success */
767 return SEG_EXPR_OK;
768}
769
770
771
772static void O65WriteSeg (O65Desc* D, SegDesc** Seg, unsigned Count, int DoWrite)
773/* Write one segment to the o65 output file */
774{
775 SegDesc* S;
776 unsigned I;
777
778 /* Initialize variables */
779 D->SegSize = 0;
780 D->LastOffs = -1;
781
782 /* Write out all segments */
783 for (I = 0; I < Count; ++I) {
784
785 /* Get the segment from the list node */
786 S = Seg [I];
787
788 /* Keep the user happy */
789 Print (stdout, 1, " Writing '%s'\n", GetString (S->Name));
790
791 /* Write this segment */
792 if (DoWrite) {
793 SegWrite (D->Filename, D->F, S->Seg, O65WriteExpr, D);
794 }
795
796 /* Mark the segment as dumped */
797 S->Seg->Dumped = 1;
798
799 /* Calculate the total size */
800 D->SegSize += S->Seg->Size;
801 }
802
803 /* Terminate the relocation table for this segment */
804 if (D->CurReloc) {
805 O65RelocPutByte (D->CurReloc, 0);
806 }
807
808 /* Check the size of the segment for overflow */
809 if ((D->Header.Mode & MF_SIZE_MASK) == MF_SIZE_16BIT && D->SegSize > 0xFFFF) {
810 Error ("Segment overflow in file '%s'", D->Filename);
811 }
812
813}
814
815
816
817static void O65WriteTextSeg (O65Desc* D)
818/* Write the code segment to the o65 output file */
819{
820 /* Initialize variables */
821 D->CurReloc = D->TextReloc;
822
823 /* Dump all text segments */
824 O65WriteSeg (D, D->TextSeg, D->TextCount, 1);
825
826 /* Set the size of the segment */
827 D->Header.TextSize = D->SegSize;
828}
829
830
831
832static void O65WriteDataSeg (O65Desc* D)
833/* Write the data segment to the o65 output file */
834{
835 /* Initialize variables */
836 D->CurReloc = D->DataReloc;
837
838 /* Dump all data segments */
839 O65WriteSeg (D, D->DataSeg, D->DataCount, 1);
840
841 /* Set the size of the segment */
842 D->Header.DataSize = D->SegSize;
843}
844
845
846
847static void O65WriteBssSeg (O65Desc* D)
848/* "Write" the bss segments to the o65 output file. This will only update
849** the relevant header fields.
850*/
851{
852 /* Initialize variables */
853 D->CurReloc = 0;
854
855 /* Dump all bss segments */
856 O65WriteSeg (D, D->BssSeg, D->BssCount, 0);
857
858 /* Set the size of the segment */
859 D->Header.BssSize = D->SegSize;
860}
861
862
863
864static void O65WriteZPSeg (O65Desc* D)
865/* "Write" the zeropage segments to the o65 output file. This will only update
866** the relevant header fields.
867*/
868{
869 /* Initialize variables */
870 D->CurReloc = 0;
871
872 /* Dump all zp segments */
873 O65WriteSeg (D, D->ZPSeg, D->ZPCount, 0);
874
875 /* Set the size of the segment */
876 D->Header.ZPSize = D->SegSize;
877}
878
879
880
881static void O65WriteImports (O65Desc* D)
882/* Write the list of imported symbols to the O65 file */
883{
884 const ExtSym* S;
885
886 /* Write the number of imports */
887 WriteSize (D, ExtSymCount (D->Imports));
888
889 /* Write out the symbol names, zero terminated */
890 S = ExtSymList (D->Imports);
891 while (S) {
892 /* Get the name */
893 const char* Name = GetString (ExtSymName (S));
894 /* And write it to the output file */
895 WriteData (D->F, Name, strlen (Name) + 1);
896 /* Next symbol */
897 S = ExtSymNext (S);
898 }
899}
900
901
902
903static void O65WriteTextReloc (O65Desc* D)
904/* Write the relocation for the text segment to the output file */
905{
906 O65WriteReloc (D->TextReloc, D->F);
907}
908
909
910
911static void O65WriteDataReloc (O65Desc* D)
912/* Write the relocation for the data segment to the output file */
913{
914 O65WriteReloc (D->DataReloc, D->F);
915}
916
917
918
919static void O65WriteExports (O65Desc* D)
920/* Write the list of exports */
921{
922 const ExtSym* S;
923
924 /* Write the number of exports */
925 WriteSize (D, ExtSymCount (D->Exports));
926
927 /* Write out the symbol information */
928 S = ExtSymList (D->Exports);
929 while (S) {
930
931 ExprNode* Expr;
932 unsigned char SegmentID;
933 ExprDesc ED;
934
935 /* Get the name */
936 unsigned NameIdx = ExtSymName (S);
937 const char* Name = GetString (NameIdx);
938
939 /* Get the export for this symbol. We've checked before that this
940 ** export does really exist, so if it is unresolved, or if we don't
941 ** find it, there is an error in the linker code.
942 */
943 Export* E = FindExport (NameIdx);
944 if (E == 0 || IsUnresolvedExport (E)) {
945 Internal ("Unresolved export '%s' found in O65WriteExports", Name);
946 }
947
948 /* Get the expression for the symbol */
949 Expr = E->Expr;
950
951 /* Recursively collect information about this expression */
952 O65ParseExpr (Expr, InitExprDesc (&ED, D), 1);
953
954 /* We cannot handle expressions with imported symbols, or expressions
955 ** with more than one segment reference here
956 */
957 if (ED.ExtRef != 0 || (ED.SegRef != 0 && ED.SecRef != 0)) {
958 ED.TooComplex = 1;
959 }
960
961 /* Bail out if we cannot handle the expression */
962 if (ED.TooComplex) {
963 Error ("Expression for symbol '%s' is too complex", Name);
964 }
965
966 /* Determine the segment id for the expression */
967 if (ED.SegRef != 0 || ED.SecRef != 0) {
968
969 const SegDesc* Seg;
970
971 /* Segment or section reference */
972 if (ED.SecRef != 0) {
973 ED.SegRef = ED.SecRef->Seg; /* Get segment from section */
974 }
975
976 /* Search for the segment and map it to it's o65 segmentID */
977 Seg = O65FindSeg (D, ED.SegRef);
978 if (Seg == 0) {
979 /* For some reason, we didn't find this segment in the list of
980 ** segments written to the o65 file.
981 */
982 Error ("Segment for symbol '%s' is undefined", Name);
983 }
984 SegmentID = O65SegType (Seg);
985
986 } else {
987
988 /* Absolute value */
989 SegmentID = O65SEG_ABS;
990
991 }
992
993 /* Write the name to the output file */
994 WriteData (D->F, Name, strlen (Name) + 1);
995
996 /* Output the segment id followed by the literal value */
997 Write8 (D->F, SegmentID);
998 WriteSize (D, ED.Val);
999
1000 /* Next symbol */
1001 S = ExtSymNext (S);
1002 }
1003}
1004
1005
1006
1007/*****************************************************************************/
1008/* Public code */
1009/*****************************************************************************/
1010
1011
1012
1013O65Desc* NewO65Desc (void)
1014/* Create, initialize and return a new O65 descriptor struct */
1015{
1016 /* Allocate a new structure */
1017 O65Desc* D = xmalloc (sizeof (O65Desc));
1018
1019 /* Initialize the header */
1020 D->Header.Version = 0;
1021 D->Header.Mode = 0;
1022 D->Header.TextBase = 0;
1023 D->Header.TextSize = 0;
1024 D->Header.DataBase = 0;
1025 D->Header.DataSize = 0;
1026 D->Header.BssBase = 0;
1027 D->Header.BssSize = 0;
1028 D->Header.ZPBase = 0;
1029 D->Header.ZPSize = 0;
1030 D->Header.StackSize = 0; /* Let OS choose a good value */
1031
1032 /* Initialize other data */
1033 D->Options = 0;
1034 D->Exports = NewExtSymTab ();
1035 D->Imports = NewExtSymTab ();
1036 D->Undef = 0;
1037 D->F = 0;
1038 D->Filename = 0;
1039 D->TextReloc = NewO65RelocTab ();
1040 D->DataReloc = NewO65RelocTab ();
1041 D->TextCount = 0;
1042 D->TextSeg = 0;
1043 D->DataCount = 0;
1044 D->DataSeg = 0;
1045 D->BssCount = 0;
1046 D->BssSeg = 0;
1047 D->ZPCount = 0;
1048 D->ZPSeg = 0;
1049
1050 /* Return the created struct */
1051 return D;
1052}
1053
1054
1055
1056void FreeO65Desc (O65Desc* D)
1057/* Delete the descriptor struct with cleanup */
1058{
1059 /* Free the segment arrays */
1060 xfree (D->ZPSeg);
1061 xfree (D->BssSeg);
1062 xfree (D->DataSeg);
1063 xfree (D->TextSeg);
1064
1065 /* Free the relocation tables */
1066 FreeO65RelocTab (D->DataReloc);
1067 FreeO65RelocTab (D->TextReloc);
1068
1069 /* Free the option list */
1070 while (D->Options) {
1071 O65Option* O = D->Options;
1072 D->Options = D->Options->Next;
1073 FreeO65Option (O);
1074 }
1075
1076 /* Free the external symbol tables */
1077 FreeExtSymTab (D->Exports);
1078 FreeExtSymTab (D->Imports);
1079
1080 /* Free the struct itself */
1081 xfree (D);
1082}
1083
1084
1085
1086void O65Set6502 (O65Desc* D)
1087/* Enable 6502 mode */
1088{
1089 D->Header.Mode = (D->Header.Mode & ~MF_CPU_MASK) | MF_CPU_6502;
1090}
1091
1092
1093
1094void O65Set65816 (O65Desc* D)
1095/* Enable 816 mode */
1096{
1097 D->Header.Mode = (D->Header.Mode & ~MF_CPU_MASK) | MF_CPU_65816;
1098}
1099
1100
1101
1102void O65SetSmallModel (O65Desc* D)
1103/* Enable a small memory model executable */
1104{
1105 D->Header.Mode = (D->Header.Mode & ~MF_SIZE_MASK) | MF_SIZE_16BIT;
1106}
1107
1108
1109
1110void O65SetLargeModel (O65Desc* D)
1111/* Enable a large memory model executable */
1112{
1113 D->Header.Mode = (D->Header.Mode & ~MF_SIZE_MASK) | MF_SIZE_32BIT;
1114}
1115
1116
1117
1118void O65SetAlignment (O65Desc* D, unsigned Alignment)
1119/* Set the executable alignment */
1120{
1121 /* Remove all alignment bits from the mode word */
1122 D->Header.Mode &= ~MF_ALIGN_MASK;
1123
1124 /* Set the alignment bits */
1125 switch (Alignment) {
1126 case 1: D->Header.Mode |= MF_ALIGN_1; break;
1127 case 2: D->Header.Mode |= MF_ALIGN_2; break;
1128 case 4: D->Header.Mode |= MF_ALIGN_4; break;
1129 case 256: D->Header.Mode |= MF_ALIGN_256; break;
1130 default: Error ("Invalid alignment for O65 format: %u", Alignment);
1131 }
1132}
1133
1134
1135
1136void O65SetOption (O65Desc* D, unsigned Type, const void* Data, unsigned DataLen)
1137/* Set an o65 header option */
1138{
1139 /* Create a new option structure */
1140 O65Option* O = NewO65Option (Type, Data, DataLen);
1141
1142 /* Insert it into the linked list */
1143 O->Next = D->Options;
1144 D->Options = O;
1145}
1146
1147
1148
1149void O65SetOS (O65Desc* D, unsigned OS, unsigned Version, unsigned Id)
1150/* Set an option describing the target operating system */
1151{
1152 /* Setup the option data */
1153 unsigned char Opt[4];
1154 Opt[0] = OS;
1155 Opt[1] = Version;
1156
1157 /* Write the correct option length */
1158 switch (OS) {
1159
1160 case O65OS_CC65:
1161 /* Set the 16 bit id */
1162 Opt[2] = (unsigned char) Id;
1163 Opt[3] = (unsigned char) (Id >> 8);
1164 O65SetOption (D, O65OPT_OS, Opt, 4);
1165 break;
1166
1167 default:
1168 /* No id for OS/A65, Lunix, and unknown OSes */
1169 O65SetOption (D, O65OPT_OS, Opt, 2);
1170 break;
1171
1172 }
1173}
1174
1175
1176
1177ExtSym* O65GetImport (O65Desc* D, unsigned Ident)
1178/* Return the imported symbol or NULL if not found */
1179{
1180 /* Retrieve the symbol from the table */
1181 return GetExtSym (D->Imports, Ident);
1182}
1183
1184
1185
1186void O65SetImport (O65Desc* D, unsigned Ident)
1187/* Set an imported identifier */
1188{
1189 /* Insert the entry into the table */
1190 NewExtSym (D->Imports, Ident);
1191}
1192
1193
1194
1195ExtSym* O65GetExport (O65Desc* D, unsigned Ident)
1196/* Return the exported symbol or NULL if not found */
1197{
1198 /* Retrieve the symbol from the table */
1199 return GetExtSym (D->Exports, Ident);
1200}
1201
1202
1203
1204void O65SetExport (O65Desc* D, unsigned Ident)
1205/* Set an exported identifier */
1206{
1207 /* Get the export for this symbol and check if it does exist and is
1208 ** a resolved symbol.
1209 */
1210 Export* E = FindExport (Ident);
1211 if (E == 0 || IsUnresolvedExport (E)) {
1212 Error ("Unresolved export: '%s'", GetString (Ident));
1213 }
1214
1215 /* Insert the entry into the table */
1216 NewExtSym (D->Exports, Ident);
1217}
1218
1219
1220
1221static void O65SetupSegments (O65Desc* D, File* F)
1222/* Setup segment assignments */
1223{
1224 unsigned I;
1225 unsigned TextIdx, DataIdx, BssIdx, ZPIdx;
1226
1227 /* Initialize the counters */
1228 D->TextCount = 0;
1229 D->DataCount = 0;
1230 D->BssCount = 0;
1231 D->ZPCount = 0;
1232
1233 /* Walk over the memory list */
1234 for (I = 0; I < CollCount (&F->MemoryAreas); ++I) {
1235 /* Get this entry */
1236 MemoryArea* M = CollAtUnchecked (&F->MemoryAreas, I);
1237
1238 /* Walk through the segment list and count the segment types */
1239 unsigned J;
1240 for (J = 0; J < CollCount (&M->SegList); ++J) {
1241
1242 /* Get the segment */
1243 SegDesc* S = CollAtUnchecked (&M->SegList, J);
1244
1245 /* Check the segment type. */
1246 switch (O65SegType (S)) {
1247 case O65SEG_TEXT: D->TextCount++; break;
1248 case O65SEG_DATA: D->DataCount++; break;
1249 case O65SEG_BSS: D->BssCount++; break;
1250 case O65SEG_ZP: D->ZPCount++; break;
1251 default: Internal ("Invalid return from O65SegType");
1252 }
1253 }
1254 }
1255
1256 /* Allocate memory according to the numbers */
1257 D->TextSeg = xmalloc (D->TextCount * sizeof (SegDesc*));
1258 D->DataSeg = xmalloc (D->DataCount * sizeof (SegDesc*));
1259 D->BssSeg = xmalloc (D->BssCount * sizeof (SegDesc*));
1260 D->ZPSeg = xmalloc (D->ZPCount * sizeof (SegDesc*));
1261
1262 /* Walk again through the list and setup the segment arrays */
1263 TextIdx = DataIdx = BssIdx = ZPIdx = 0;
1264 for (I = 0; I < CollCount (&F->MemoryAreas); ++I) {
1265 /* Get this entry */
1266 MemoryArea* M = CollAtUnchecked (&F->MemoryAreas, I);
1267
1268 /* Walk over the segment list and check the segment types */
1269 unsigned J;
1270 for (J = 0; J < CollCount (&M->SegList); ++J) {
1271
1272 /* Get the segment */
1273 SegDesc* S = CollAtUnchecked (&M->SegList, J);
1274
1275 /* Check the segment type. */
1276 switch (O65SegType (S)) {
1277 case O65SEG_TEXT: D->TextSeg [TextIdx++] = S; break;
1278 case O65SEG_DATA: D->DataSeg [DataIdx++] = S; break;
1279 case O65SEG_BSS: D->BssSeg [BssIdx++] = S; break;
1280 case O65SEG_ZP: D->ZPSeg [ZPIdx++] = S; break;
1281 default: Internal ("Invalid return from O65SegType");
1282 }
1283 }
1284 }
1285}
1286
1287
1288
1289static int O65Unresolved (unsigned Name, void* D)
1290/* Called if an unresolved symbol is encountered */
1291{
1292 /* Check if the symbol is an imported o65 symbol */
1293 if (O65GetImport (D, Name) != 0) {
1294 /* This is an external symbol, relax... */
1295 return 1;
1296 } else {
1297 /* This is actually an unresolved external. Bump the counter */
1298 ((O65Desc*) D)->Undef++;
1299 return 0;
1300 }
1301}
1302
1303
1304
1305static void O65SetupHeader (O65Desc* D)
1306/* Set additional stuff in the header */
1307{
1308 /* Set the base addresses of the segments */
1309 if (D->TextCount > 0) {
1310 SegDesc* FirstSeg = D->TextSeg [0];
1311 D->Header.TextBase = FirstSeg->Seg->PC;
1312 }
1313 if (D->DataCount > 0) {
1314 SegDesc* FirstSeg = D->DataSeg [0];
1315 D->Header.DataBase = FirstSeg->Seg->PC;
1316 }
1317 if (D->BssCount > 0) {
1318 SegDesc* FirstSeg = D->BssSeg [0];
1319 D->Header.BssBase = FirstSeg->Seg->PC;
1320 }
1321 if (D->ZPCount > 0) {
1322 SegDesc* FirstSeg = D->ZPSeg [0];
1323 D->Header.ZPBase = FirstSeg->Seg->PC;
1324 }
1325}
1326
1327
1328
1329static void O65UpdateHeader (O65Desc* D)
1330/* Update mode word, currently only the "simple" bit */
1331{
1332 /* If we have byte wise relocation and an alignment of 1, and text
1333 ** and data are adjacent, we can set the "simple addressing" bit
1334 ** in the header.
1335 */
1336 if ((D->Header.Mode & MF_RELOC_MASK) == MF_RELOC_BYTE &&
1337 (D->Header.Mode & MF_ALIGN_MASK) == MF_ALIGN_1 &&
1338 D->Header.TextBase + D->Header.TextSize == D->Header.DataBase &&
1339 D->Header.DataBase + D->Header.DataSize == D->Header.BssBase) {
1340 D->Header.Mode = (D->Header.Mode & ~MF_ADDR_MASK) | MF_ADDR_SIMPLE;
1341 }
1342}
1343
1344
1345void O65WriteTarget (O65Desc* D, File* F)
1346/* Write an o65 output file */
1347{
1348 char OptBuf [256]; /* Buffer for option strings */
1349 unsigned OptLen;
1350 time_t T;
1351 const char* Name;
1352
1353 /* Place the filename in the control structure */
1354 D->Filename = GetString (F->Name);
1355
1356 /* Check for unresolved symbols. The function O65Unresolved is called
1357 ** if we get an unresolved symbol.
1358 */
1359 D->Undef = 0; /* Reset the counter */
1360 CheckUnresolvedImports (O65Unresolved, D);
1361 if (D->Undef > 0) {
1362 /* We had unresolved symbols, cannot create output file */
1363 Error ("%u unresolved external(s) found - cannot create output file", D->Undef);
1364 }
1365
1366 /* Setup the segment arrays */
1367 O65SetupSegments (D, F);
1368
1369 /* Setup additional stuff in the header */
1370 O65SetupHeader (D);
1371
1372 /* Open the file */
1373 D->F = fopen (D->Filename, "wb");
1374 if (D->F == 0) {
1375 Error ("Cannot open '%s': %s", D->Filename, strerror (errno));
1376 }
1377
1378 /* Keep the user happy */
1379 Print (stdout, 1, "Opened '%s'...\n", D->Filename);
1380
1381 /* Define some more options: A timestamp, the linker version and the
1382 ** filename
1383 */
1384 T = time (0);
1385 strcpy (OptBuf, ctime (&T));
1386 OptLen = strlen (OptBuf);
1387 while (OptLen > 0 && IsControl (OptBuf[OptLen-1])) {
1388 --OptLen;
1389 }
1390 OptBuf[OptLen] = '\0';
1391 O65SetOption (D, O65OPT_TIMESTAMP, OptBuf, OptLen + 1);
1392 sprintf (OptBuf, "ld65 V%s", GetVersionAsString ());
1393 O65SetOption (D, O65OPT_ASM, OptBuf, strlen (OptBuf) + 1);
1394 Name = FindName (D->Filename);
1395 O65SetOption (D, O65OPT_FILENAME, Name, strlen (Name) + 1);
1396
1397 /* Write the header */
1398 O65WriteHeader (D);
1399
1400 /* Write the text segment */
1401 O65WriteTextSeg (D);
1402
1403 /* Write the data segment */
1404 O65WriteDataSeg (D);
1405
1406 /* "Write" the bss segments */
1407 O65WriteBssSeg (D);
1408
1409 /* "Write" the zeropage segments */
1410 O65WriteZPSeg (D);
1411
1412 /* Write the undefined references list */
1413 O65WriteImports (D);
1414
1415 /* Write the text segment relocation table */
1416 O65WriteTextReloc (D);
1417
1418 /* Write the data segment relocation table */
1419 O65WriteDataReloc (D);
1420
1421 /* Write the list of exports */
1422 O65WriteExports (D);
1423
1424 /* Update header flags */
1425 O65UpdateHeader (D);
1426
1427 /* Seek back to the start and write the updated header */
1428 fseek (D->F, 0, SEEK_SET);
1429 O65WriteHeader (D);
1430
1431 /* Close the file */
1432 if (fclose (D->F) != 0) {
1433 Error ("Cannot write to '%s': %s", D->Filename, strerror (errno));
1434 }
1435
1436 /* Reset the file and filename */
1437 D->F = 0;
1438 D->Filename = 0;
1439}
1440