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 */ |
115 | typedef struct O65Header ; |
116 | struct { |
117 | unsigned ; /* Version number for o65 format */ |
118 | unsigned ; /* Mode word */ |
119 | unsigned long ; /* Base address of text segment */ |
120 | unsigned long ; /* Size of text segment */ |
121 | unsigned long ; /* Base of data segment */ |
122 | unsigned long ; /* Size of data segment */ |
123 | unsigned long ; /* Base of bss segment */ |
124 | unsigned long ; /* Size of bss segment */ |
125 | unsigned long ; /* Base of zeropage segment */ |
126 | unsigned long ; /* Size of zeropage segment */ |
127 | unsigned long ; /* Requested stack size */ |
128 | }; |
129 | |
130 | /* An o65 option */ |
131 | typedef struct O65Option O65Option; |
132 | struct 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 */ |
140 | typedef struct O65RelocTab O65RelocTab; |
141 | struct 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 */ |
148 | struct O65Desc { |
149 | O65Header ; /* 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 */ |
175 | typedef struct ExprDesc ExprDesc; |
176 | struct 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 | |
194 | static 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 | |
209 | static 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 | |
221 | static 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 | |
243 | static void CvtMemoryToSegment (ExprDesc* ED) |
244 | /* Convert a memory area into a segment by searching the list of run segments |
245 | ** in this memory area and assigning the nearest one. |
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 | |
292 | static const SegDesc* FindSeg (SegDesc** const List, unsigned Count, const Segment* S) |
293 | /* Search for a segment in the given list of segment descriptors and return |
294 | ** the descriptor for a segment if we found it, and NULL if not. |
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 | |
312 | static const SegDesc* O65FindSeg (const O65Desc* D, const Segment* S) |
313 | /* Search for a segment in the segment lists and return it's segment descriptor */ |
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 | |
342 | static void O65ParseExpr (ExprNode* Expr, ExprDesc* D, int Sign) |
343 | /* Extract and evaluate all constant factors in an subtree that has only |
344 | ** additions and subtractions. If anything other than additions and |
345 | ** subtractions are found, D->TooComplex is set to true. |
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 | |
447 | static 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 | |
464 | static void FreeO65RelocTab (O65RelocTab* R) |
465 | /* Free a relocation table */ |
466 | { |
467 | xfree (R->Buf); |
468 | xfree (R); |
469 | } |
470 | |
471 | |
472 | |
473 | static 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 | |
493 | static 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 | |
502 | static 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 | |
516 | static 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 | |
539 | static 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 | |
553 | static void (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 | |
593 | static unsigned O65WriteExpr (ExprNode* E, int Signed, unsigned Size, |
594 | unsigned long Offs, void* Data) |
595 | /* Called from SegWrite for an expression. Evaluate the expression, check the |
596 | ** range and write the expression value to the file, update the relocation |
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 | |
772 | static 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 | |
817 | static 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 | |
832 | static 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 | |
847 | static 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 | |
864 | static 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 | |
881 | static 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 | |
903 | static 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 | |
911 | static 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 | |
919 | static 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 | |
1013 | O65Desc* 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 | |
1056 | void 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 | |
1086 | void 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 | |
1094 | void 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 | |
1102 | void 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 | |
1110 | void 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 | |
1118 | void 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 | |
1136 | void 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 | |
1149 | void 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 | |
1177 | ExtSym* 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 | |
1186 | void 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 | |
1195 | ExtSym* 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 | |
1204 | void 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 | |
1221 | static 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 | |
1289 | static 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 | |
1305 | static void (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 | |
1329 | static void (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 | |
1345 | void 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 | |