1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* config.c */ |
4 | /* */ |
5 | /* Target configuration file for the ld65 linker */ |
6 | /* */ |
7 | /* */ |
8 | /* */ |
9 | /* (c) 1998-2013, Ullrich von Bassewitz */ |
10 | /* Roemerstrasse 52 */ |
11 | /* D-70794 Filderstadt */ |
12 | /* EMail: uz@cc65.org */ |
13 | /* */ |
14 | /* With contributions from: */ |
15 | /* */ |
16 | /* - "David M. Lloyd" <david.lloyd@redhat.com> */ |
17 | /* */ |
18 | /* */ |
19 | /* This software is provided 'as-is', without any expressed or implied */ |
20 | /* warranty. In no event will the authors be held liable for any damages */ |
21 | /* arising from the use of this software. */ |
22 | /* */ |
23 | /* Permission is granted to anyone to use this software for any purpose, */ |
24 | /* including commercial applications, and to alter it and redistribute it */ |
25 | /* freely, subject to the following restrictions: */ |
26 | /* */ |
27 | /* 1. The origin of this software must not be misrepresented; you must not */ |
28 | /* claim that you wrote the original software. If you use this software */ |
29 | /* in a product, an acknowledgment in the product documentation would be */ |
30 | /* appreciated but is not required. */ |
31 | /* 2. Altered source versions must be plainly marked as such, and must not */ |
32 | /* be misrepresented as being the original software. */ |
33 | /* 3. This notice may not be removed or altered from any source */ |
34 | /* distribution. */ |
35 | /* */ |
36 | /*****************************************************************************/ |
37 | |
38 | |
39 | |
40 | #include <stdio.h> |
41 | #include <stdlib.h> |
42 | #include <string.h> |
43 | #include <errno.h> |
44 | |
45 | /* common */ |
46 | #include "addrsize.h" |
47 | #include "bitops.h" |
48 | #include "check.h" |
49 | #include "print.h" |
50 | #include "segdefs.h" |
51 | #include "target.h" |
52 | #include "xmalloc.h" |
53 | #include "xsprintf.h" |
54 | |
55 | /* ld65 */ |
56 | #include "alignment.h" |
57 | #include "bin.h" |
58 | #include "binfmt.h" |
59 | #include "cfgexpr.h" |
60 | #include "condes.h" |
61 | #include "config.h" |
62 | #include "error.h" |
63 | #include "exports.h" |
64 | #include "expr.h" |
65 | #include "global.h" |
66 | #include "memarea.h" |
67 | #include "o65.h" |
68 | #include "objdata.h" |
69 | #include "scanner.h" |
70 | #include "spool.h" |
71 | #include "xex.h" |
72 | |
73 | |
74 | |
75 | /*****************************************************************************/ |
76 | /* Data */ |
77 | /*****************************************************************************/ |
78 | |
79 | |
80 | |
81 | /* Remember which sections we had encountered */ |
82 | static enum { |
83 | SE_NONE = 0x0000, |
84 | SE_MEMORY = 0x0001, |
85 | SE_SEGMENTS = 0x0002, |
86 | SE_FEATURES = 0x0004, |
87 | SE_FILES = 0x0008, |
88 | SE_FORMATS = 0x0010, |
89 | SE_SYMBOLS = 0x0020 |
90 | } SectionsEncountered = SE_NONE; |
91 | |
92 | |
93 | |
94 | /* File list */ |
95 | static Collection FileList = STATIC_COLLECTION_INITIALIZER; |
96 | |
97 | /* Memory list */ |
98 | static Collection MemoryAreas = STATIC_COLLECTION_INITIALIZER; |
99 | |
100 | /* Memory attributes */ |
101 | #define MA_START 0x0001 |
102 | #define MA_SIZE 0x0002 |
103 | #define MA_TYPE 0x0004 |
104 | #define MA_FILE 0x0008 |
105 | #define MA_DEFINE 0x0010 |
106 | #define MA_FILL 0x0020 |
107 | #define MA_FILLVAL 0x0040 |
108 | #define MA_BANK 0x0080 |
109 | |
110 | /* Segment list */ |
111 | static Collection SegDescList = STATIC_COLLECTION_INITIALIZER; |
112 | |
113 | /* Segment attributes */ |
114 | #define SA_TYPE 0x0001 |
115 | #define SA_LOAD 0x0002 |
116 | #define SA_RUN 0x0004 |
117 | #define SA_ALIGN 0x0008 |
118 | #define SA_ALIGN_LOAD 0x0010 |
119 | #define SA_DEFINE 0x0020 |
120 | #define SA_OFFSET 0x0040 |
121 | #define SA_START 0x0080 |
122 | #define SA_OPTIONAL 0x0100 |
123 | #define SA_FILLVAL 0x0200 |
124 | |
125 | /* Symbol types used in the CfgSymbol structure */ |
126 | typedef enum { |
127 | CfgSymExport, /* Not really used in struct CfgSymbol */ |
128 | CfgSymImport, /* Dito */ |
129 | CfgSymWeak, /* Like export but weak */ |
130 | CfgSymO65Export, /* An o65 export */ |
131 | CfgSymO65Import, /* An o65 import */ |
132 | } CfgSymType; |
133 | |
134 | /* Symbol structure. It is used for o65 imports and exports, but also for |
135 | ** symbols from the SYMBOLS sections (symbols defined in the config file or |
136 | ** forced imports). |
137 | */ |
138 | typedef struct CfgSymbol CfgSymbol; |
139 | struct CfgSymbol { |
140 | CfgSymType Type; /* Type of symbol */ |
141 | LineInfo* LI; /* Config file position */ |
142 | unsigned Name; /* Symbol name */ |
143 | ExprNode* Value; /* Symbol value if any */ |
144 | unsigned AddrSize; /* Address size of symbol */ |
145 | }; |
146 | |
147 | /* Collections with symbols */ |
148 | static Collection CfgSymbols = STATIC_COLLECTION_INITIALIZER; |
149 | |
150 | /* Descriptor holding information about the binary formats */ |
151 | static BinDesc* BinFmtDesc = 0; |
152 | static O65Desc* O65FmtDesc = 0; |
153 | static XexDesc* XexFmtDesc = 0; |
154 | |
155 | |
156 | |
157 | /*****************************************************************************/ |
158 | /* Forwards */ |
159 | /*****************************************************************************/ |
160 | |
161 | |
162 | |
163 | static File* NewFile (unsigned Name); |
164 | /* Create a new file descriptor and insert it into the list */ |
165 | |
166 | |
167 | |
168 | /*****************************************************************************/ |
169 | /* List management */ |
170 | /*****************************************************************************/ |
171 | |
172 | |
173 | |
174 | static File* FindFile (unsigned Name) |
175 | /* Find a file with a given name. */ |
176 | { |
177 | unsigned I; |
178 | for (I = 0; I < CollCount (&FileList); ++I) { |
179 | File* F = CollAtUnchecked (&FileList, I); |
180 | if (F->Name == Name) { |
181 | return F; |
182 | } |
183 | } |
184 | return 0; |
185 | } |
186 | |
187 | |
188 | |
189 | static File* GetFile (unsigned Name) |
190 | /* Get a file entry with the given name. Create a new one if needed. */ |
191 | { |
192 | File* F = FindFile (Name); |
193 | if (F == 0) { |
194 | /* Create a new one */ |
195 | F = NewFile (Name); |
196 | } |
197 | return F; |
198 | } |
199 | |
200 | |
201 | |
202 | static void FileInsert (File* F, MemoryArea* M) |
203 | /* Insert the memory area into the files list */ |
204 | { |
205 | M->F = F; |
206 | CollAppend (&F->MemoryAreas, M); |
207 | } |
208 | |
209 | |
210 | |
211 | static MemoryArea* CfgFindMemory (unsigned Name) |
212 | /* Find the memory are with the given name. Return NULL if not found */ |
213 | { |
214 | unsigned I; |
215 | for (I = 0; I < CollCount (&MemoryAreas); ++I) { |
216 | MemoryArea* M = CollAtUnchecked (&MemoryAreas, I); |
217 | if (M->Name == Name) { |
218 | return M; |
219 | } |
220 | } |
221 | return 0; |
222 | } |
223 | |
224 | |
225 | |
226 | static MemoryArea* CfgGetMemory (unsigned Name) |
227 | /* Find the memory are with the given name. Print an error on an invalid name */ |
228 | { |
229 | MemoryArea* M = CfgFindMemory (Name); |
230 | if (M == 0) { |
231 | CfgError (&CfgErrorPos, "Invalid memory area '%s'" , GetString (Name)); |
232 | } |
233 | return M; |
234 | } |
235 | |
236 | |
237 | |
238 | static SegDesc* CfgFindSegDesc (unsigned Name) |
239 | /* Find the segment descriptor with the given name, return NULL if not found. */ |
240 | { |
241 | unsigned I; |
242 | for (I = 0; I < CollCount (&SegDescList); ++I) { |
243 | SegDesc* S = CollAtUnchecked (&SegDescList, I); |
244 | if (S->Name == Name) { |
245 | /* Found */ |
246 | return S; |
247 | } |
248 | } |
249 | |
250 | /* Not found */ |
251 | return 0; |
252 | } |
253 | |
254 | |
255 | |
256 | static void MemoryInsert (MemoryArea* M, SegDesc* S) |
257 | /* Insert the segment descriptor into the memory area list */ |
258 | { |
259 | /* Insert the segment into the segment list of the memory area */ |
260 | CollAppend (&M->SegList, S); |
261 | } |
262 | |
263 | |
264 | |
265 | /*****************************************************************************/ |
266 | /* Constructors/Destructors */ |
267 | /*****************************************************************************/ |
268 | |
269 | |
270 | |
271 | static CfgSymbol* NewCfgSymbol (CfgSymType Type, unsigned Name) |
272 | /* Create a new CfgSymbol structure with the given type and name. The |
273 | ** current config file position is recorded in the returned struct. The |
274 | ** created struct is inserted into the CfgSymbols collection and returned. |
275 | */ |
276 | { |
277 | /* Allocate memory */ |
278 | CfgSymbol* Sym = xmalloc (sizeof (CfgSymbol)); |
279 | |
280 | /* Initialize the fields */ |
281 | Sym->Type = Type; |
282 | Sym->LI = GenLineInfo (&CfgErrorPos); |
283 | Sym->Name = Name; |
284 | Sym->Value = 0; |
285 | Sym->AddrSize = ADDR_SIZE_INVALID; |
286 | |
287 | /* Insert the symbol into the collection */ |
288 | CollAppend (&CfgSymbols, Sym); |
289 | |
290 | /* Return the initialized struct */ |
291 | return Sym; |
292 | } |
293 | |
294 | |
295 | |
296 | static File* NewFile (unsigned Name) |
297 | /* Create a new file descriptor and insert it into the list */ |
298 | { |
299 | /* Allocate memory */ |
300 | File* F = xmalloc (sizeof (File)); |
301 | |
302 | /* Initialize the fields */ |
303 | F->Name = Name; |
304 | F->Flags = 0; |
305 | F->Format = BINFMT_DEFAULT; |
306 | F->Size = 0; |
307 | InitCollection (&F->MemoryAreas); |
308 | |
309 | /* Insert the struct into the list */ |
310 | CollAppend (&FileList, F); |
311 | |
312 | /* ...and return it */ |
313 | return F; |
314 | } |
315 | |
316 | |
317 | |
318 | static MemoryArea* CreateMemoryArea (const FilePos* Pos, unsigned Name) |
319 | /* Create a new memory area and insert it into the list */ |
320 | { |
321 | /* Check for duplicate names */ |
322 | MemoryArea* M = CfgFindMemory (Name); |
323 | if (M) { |
324 | CfgError (&CfgErrorPos, |
325 | "Memory area '%s' defined twice" , |
326 | GetString (Name)); |
327 | } |
328 | |
329 | /* Create a new memory area */ |
330 | M = NewMemoryArea (Pos, Name); |
331 | |
332 | /* Insert the struct into the list ... */ |
333 | CollAppend (&MemoryAreas, M); |
334 | |
335 | /* ...and return it */ |
336 | return M; |
337 | } |
338 | |
339 | |
340 | |
341 | static SegDesc* NewSegDesc (unsigned Name) |
342 | /* Create a segment descriptor and insert it into the list */ |
343 | { |
344 | |
345 | /* Check for duplicate names */ |
346 | SegDesc* S = CfgFindSegDesc (Name); |
347 | if (S) { |
348 | CfgError (&CfgErrorPos, "Segment '%s' defined twice" , GetString (Name)); |
349 | } |
350 | |
351 | /* Allocate memory */ |
352 | S = xmalloc (sizeof (SegDesc)); |
353 | |
354 | /* Initialize the fields */ |
355 | S->Name = Name; |
356 | S->LI = GenLineInfo (&CfgErrorPos); |
357 | S->Seg = 0; |
358 | S->Attr = 0; |
359 | S->Flags = 0; |
360 | S->FillVal = 0; |
361 | S->RunAlignment = 1; |
362 | S->LoadAlignment = 1; |
363 | |
364 | /* Insert the struct into the list ... */ |
365 | CollAppend (&SegDescList, S); |
366 | |
367 | /* ...and return it */ |
368 | return S; |
369 | } |
370 | |
371 | |
372 | |
373 | static void FreeSegDesc (SegDesc* S) |
374 | /* Free a segment descriptor */ |
375 | { |
376 | FreeLineInfo (S->LI); |
377 | xfree (S); |
378 | } |
379 | |
380 | |
381 | |
382 | /*****************************************************************************/ |
383 | /* Config file parsing */ |
384 | /*****************************************************************************/ |
385 | |
386 | |
387 | |
388 | static void FlagAttr (unsigned* Flags, unsigned Mask, const char* Name) |
389 | /* Check if the item is already defined. Print an error if so. If not, set |
390 | ** the marker that we have a definition now. |
391 | */ |
392 | { |
393 | if (*Flags & Mask) { |
394 | CfgError (&CfgErrorPos, "%s is already defined" , Name); |
395 | } |
396 | *Flags |= Mask; |
397 | } |
398 | |
399 | |
400 | |
401 | static void AttrCheck (unsigned Attr, unsigned Mask, const char* Name) |
402 | /* Check that a mandatory attribute was given */ |
403 | { |
404 | if ((Attr & Mask) == 0) { |
405 | CfgError (&CfgErrorPos, "%s attribute is missing" , Name); |
406 | } |
407 | } |
408 | |
409 | |
410 | |
411 | static void ParseMemory (void) |
412 | /* Parse a MEMORY section */ |
413 | { |
414 | static const IdentTok Attributes [] = { |
415 | { "BANK" , CFGTOK_BANK }, |
416 | { "DEFINE" , CFGTOK_DEFINE }, |
417 | { "FILE" , CFGTOK_FILE }, |
418 | { "FILL" , CFGTOK_FILL }, |
419 | { "FILLVAL" , CFGTOK_FILLVAL }, |
420 | { "SIZE" , CFGTOK_SIZE }, |
421 | { "START" , CFGTOK_START }, |
422 | { "TYPE" , CFGTOK_TYPE }, |
423 | }; |
424 | static const IdentTok Types [] = { |
425 | { "RO" , CFGTOK_RO }, |
426 | { "RW" , CFGTOK_RW }, |
427 | }; |
428 | |
429 | while (CfgTok == CFGTOK_IDENT) { |
430 | |
431 | /* Create a new entry on the heap */ |
432 | MemoryArea* M = CreateMemoryArea (&CfgErrorPos, GetStrBufId (&CfgSVal)); |
433 | |
434 | /* Skip the name and the following colon */ |
435 | CfgNextTok (); |
436 | CfgConsumeColon (); |
437 | |
438 | /* Read the attributes */ |
439 | while (CfgTok == CFGTOK_IDENT) { |
440 | |
441 | /* Map the identifier to a token */ |
442 | cfgtok_t AttrTok; |
443 | CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute" ); |
444 | AttrTok = CfgTok; |
445 | |
446 | /* An optional assignment follows */ |
447 | CfgNextTok (); |
448 | CfgOptionalAssign (); |
449 | |
450 | /* Check which attribute was given */ |
451 | switch (AttrTok) { |
452 | |
453 | case CFGTOK_BANK: |
454 | FlagAttr (&M->Attr, MA_BANK, "BANK" ); |
455 | M->BankExpr = CfgExpr (); |
456 | break; |
457 | |
458 | case CFGTOK_DEFINE: |
459 | FlagAttr (&M->Attr, MA_DEFINE, "DEFINE" ); |
460 | /* Map the token to a boolean */ |
461 | CfgBoolToken (); |
462 | if (CfgTok == CFGTOK_TRUE) { |
463 | M->Flags |= MF_DEFINE; |
464 | } |
465 | CfgNextTok (); |
466 | break; |
467 | |
468 | case CFGTOK_FILE: |
469 | FlagAttr (&M->Attr, MA_FILE, "FILE" ); |
470 | CfgAssureStr (); |
471 | /* Get the file entry and insert the memory area */ |
472 | FileInsert (GetFile (GetStrBufId (&CfgSVal)), M); |
473 | CfgNextTok (); |
474 | break; |
475 | |
476 | case CFGTOK_FILL: |
477 | FlagAttr (&M->Attr, MA_FILL, "FILL" ); |
478 | /* Map the token to a boolean */ |
479 | CfgBoolToken (); |
480 | if (CfgTok == CFGTOK_TRUE) { |
481 | M->Flags |= MF_FILL; |
482 | } |
483 | CfgNextTok (); |
484 | break; |
485 | |
486 | case CFGTOK_FILLVAL: |
487 | FlagAttr (&M->Attr, MA_FILLVAL, "FILLVAL" ); |
488 | M->FillVal = (unsigned char) CfgCheckedConstExpr (0, 0xFF); |
489 | break; |
490 | |
491 | case CFGTOK_SIZE: |
492 | FlagAttr (&M->Attr, MA_SIZE, "SIZE" ); |
493 | M->SizeExpr = CfgExpr (); |
494 | break; |
495 | |
496 | case CFGTOK_START: |
497 | FlagAttr (&M->Attr, MA_START, "START" ); |
498 | M->StartExpr = CfgExpr (); |
499 | break; |
500 | |
501 | case CFGTOK_TYPE: |
502 | FlagAttr (&M->Attr, MA_TYPE, "TYPE" ); |
503 | CfgSpecialToken (Types, ENTRY_COUNT (Types), "TYPE" ); |
504 | if (CfgTok == CFGTOK_RO) { |
505 | M->Flags |= MF_RO; |
506 | } |
507 | CfgNextTok (); |
508 | break; |
509 | |
510 | default: |
511 | FAIL ("Unexpected attribute token" ); |
512 | |
513 | } |
514 | |
515 | /* Skip an optional comma */ |
516 | CfgOptionalComma (); |
517 | } |
518 | |
519 | /* Skip the semicolon */ |
520 | CfgConsumeSemi (); |
521 | |
522 | /* Check for mandatory parameters */ |
523 | AttrCheck (M->Attr, MA_START, "START" ); |
524 | AttrCheck (M->Attr, MA_SIZE, "SIZE" ); |
525 | |
526 | /* If we don't have a file name for output given, use the default |
527 | ** file name. |
528 | */ |
529 | if ((M->Attr & MA_FILE) == 0) { |
530 | FileInsert (GetFile (GetStringId (OutputName)), M); |
531 | OutputNameUsed = 1; |
532 | } |
533 | } |
534 | |
535 | /* Remember we had this section */ |
536 | SectionsEncountered |= SE_MEMORY; |
537 | } |
538 | |
539 | |
540 | |
541 | static void ParseFiles (void) |
542 | /* Parse a FILES section */ |
543 | { |
544 | static const IdentTok Attributes [] = { |
545 | { "FORMAT" , CFGTOK_FORMAT }, |
546 | }; |
547 | static const IdentTok Formats [] = { |
548 | { "ATARI" , CFGTOK_ATARIEXE }, |
549 | { "O65" , CFGTOK_O65 }, |
550 | { "BIN" , CFGTOK_BIN }, |
551 | { "BINARY" , CFGTOK_BIN }, |
552 | }; |
553 | |
554 | |
555 | /* The MEMORY section must preceed the FILES section */ |
556 | if ((SectionsEncountered & SE_MEMORY) == 0) { |
557 | CfgError (&CfgErrorPos, "MEMORY must precede FILES" ); |
558 | } |
559 | |
560 | /* Parse all files */ |
561 | while (CfgTok != CFGTOK_RCURLY) { |
562 | |
563 | File* F; |
564 | |
565 | /* We expect a string value here */ |
566 | CfgAssureStr (); |
567 | |
568 | /* Search for the file, it must exist */ |
569 | F = FindFile (GetStrBufId (&CfgSVal)); |
570 | if (F == 0) { |
571 | CfgError (&CfgErrorPos, |
572 | "File '%s' not found in MEMORY section" , |
573 | SB_GetConstBuf (&CfgSVal)); |
574 | } |
575 | |
576 | /* Skip the token and the following colon */ |
577 | CfgNextTok (); |
578 | CfgConsumeColon (); |
579 | |
580 | /* Read the attributes */ |
581 | while (CfgTok == CFGTOK_IDENT) { |
582 | |
583 | /* Map the identifier to a token */ |
584 | cfgtok_t AttrTok; |
585 | CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute" ); |
586 | AttrTok = CfgTok; |
587 | |
588 | /* An optional assignment follows */ |
589 | CfgNextTok (); |
590 | CfgOptionalAssign (); |
591 | |
592 | /* Check which attribute was given */ |
593 | switch (AttrTok) { |
594 | |
595 | case CFGTOK_FORMAT: |
596 | if (F->Format != BINFMT_DEFAULT) { |
597 | /* We've set the format already! */ |
598 | CfgError (&CfgErrorPos, |
599 | "Cannot set a file format twice" ); |
600 | } |
601 | /* Read the format token */ |
602 | CfgSpecialToken (Formats, ENTRY_COUNT (Formats), "Format" ); |
603 | switch (CfgTok) { |
604 | |
605 | case CFGTOK_BIN: |
606 | F->Format = BINFMT_BINARY; |
607 | break; |
608 | |
609 | case CFGTOK_O65: |
610 | F->Format = BINFMT_O65; |
611 | break; |
612 | |
613 | case CFGTOK_ATARIEXE: |
614 | F->Format = BINFMT_ATARIEXE; |
615 | break; |
616 | |
617 | default: |
618 | Error ("Unexpected format token" ); |
619 | } |
620 | break; |
621 | |
622 | default: |
623 | FAIL ("Unexpected attribute token" ); |
624 | |
625 | } |
626 | |
627 | /* Skip the attribute value and an optional comma */ |
628 | CfgNextTok (); |
629 | CfgOptionalComma (); |
630 | } |
631 | |
632 | /* Skip the semicolon */ |
633 | CfgConsumeSemi (); |
634 | |
635 | } |
636 | |
637 | /* Remember we had this section */ |
638 | SectionsEncountered |= SE_FILES; |
639 | } |
640 | |
641 | |
642 | |
643 | static void ParseSegments (void) |
644 | /* Parse a SEGMENTS section */ |
645 | { |
646 | static const IdentTok Attributes [] = { |
647 | { "ALIGN" , CFGTOK_ALIGN }, |
648 | { "ALIGN_LOAD" , CFGTOK_ALIGN_LOAD }, |
649 | { "DEFINE" , CFGTOK_DEFINE }, |
650 | { "FILLVAL" , CFGTOK_FILLVAL }, |
651 | { "LOAD" , CFGTOK_LOAD }, |
652 | { "OFFSET" , CFGTOK_OFFSET }, |
653 | { "OPTIONAL" , CFGTOK_OPTIONAL }, |
654 | { "RUN" , CFGTOK_RUN }, |
655 | { "START" , CFGTOK_START }, |
656 | { "TYPE" , CFGTOK_TYPE }, |
657 | }; |
658 | static const IdentTok Types [] = { |
659 | { "RO" , CFGTOK_RO }, |
660 | { "RW" , CFGTOK_RW }, |
661 | { "BSS" , CFGTOK_BSS }, |
662 | { "ZP" , CFGTOK_ZP }, |
663 | { "OVERWRITE" , CFGTOK_OVERWRITE }, |
664 | }; |
665 | |
666 | unsigned Count; |
667 | |
668 | /* The MEMORY section must preceed the SEGMENTS section */ |
669 | if ((SectionsEncountered & SE_MEMORY) == 0) { |
670 | CfgError (&CfgErrorPos, "MEMORY must precede SEGMENTS" ); |
671 | } |
672 | |
673 | while (CfgTok == CFGTOK_IDENT) { |
674 | |
675 | SegDesc* S; |
676 | |
677 | /* Create a new entry on the heap */ |
678 | S = NewSegDesc (GetStrBufId (&CfgSVal)); |
679 | |
680 | /* Skip the name and the following colon */ |
681 | CfgNextTok (); |
682 | CfgConsumeColon (); |
683 | |
684 | /* Read the attributes */ |
685 | while (CfgTok == CFGTOK_IDENT) { |
686 | |
687 | /* Map the identifier to a token */ |
688 | cfgtok_t AttrTok; |
689 | CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute" ); |
690 | AttrTok = CfgTok; |
691 | |
692 | /* An optional assignment follows */ |
693 | CfgNextTok (); |
694 | CfgOptionalAssign (); |
695 | |
696 | /* Check which attribute was given */ |
697 | switch (AttrTok) { |
698 | |
699 | case CFGTOK_ALIGN: |
700 | FlagAttr (&S->Attr, SA_ALIGN, "ALIGN" ); |
701 | S->RunAlignment = (unsigned) CfgCheckedConstExpr (1, MAX_ALIGNMENT); |
702 | S->Flags |= SF_ALIGN; |
703 | break; |
704 | |
705 | case CFGTOK_ALIGN_LOAD: |
706 | FlagAttr (&S->Attr, SA_ALIGN_LOAD, "ALIGN_LOAD" ); |
707 | S->LoadAlignment = (unsigned) CfgCheckedConstExpr (1, MAX_ALIGNMENT); |
708 | S->Flags |= SF_ALIGN_LOAD; |
709 | break; |
710 | |
711 | case CFGTOK_DEFINE: |
712 | FlagAttr (&S->Attr, SA_DEFINE, "DEFINE" ); |
713 | /* Map the token to a boolean */ |
714 | CfgBoolToken (); |
715 | if (CfgTok == CFGTOK_TRUE) { |
716 | S->Flags |= SF_DEFINE; |
717 | } |
718 | CfgNextTok (); |
719 | break; |
720 | |
721 | case CFGTOK_FILLVAL: |
722 | FlagAttr (&S->Attr, SA_FILLVAL, "FILLVAL" ); |
723 | S->FillVal = (unsigned char) CfgCheckedConstExpr (0, 0xFF); |
724 | S->Flags |= SF_FILLVAL; |
725 | break; |
726 | |
727 | case CFGTOK_LOAD: |
728 | FlagAttr (&S->Attr, SA_LOAD, "LOAD" ); |
729 | S->Load = CfgGetMemory (GetStrBufId (&CfgSVal)); |
730 | CfgNextTok (); |
731 | break; |
732 | |
733 | case CFGTOK_OFFSET: |
734 | FlagAttr (&S->Attr, SA_OFFSET, "OFFSET" ); |
735 | S->Addr = CfgCheckedConstExpr (0, 0x1000000); |
736 | S->Flags |= SF_OFFSET; |
737 | break; |
738 | |
739 | case CFGTOK_OPTIONAL: |
740 | FlagAttr (&S->Attr, SA_OPTIONAL, "OPTIONAL" ); |
741 | CfgBoolToken (); |
742 | if (CfgTok == CFGTOK_TRUE) { |
743 | S->Flags |= SF_OPTIONAL; |
744 | } |
745 | CfgNextTok (); |
746 | break; |
747 | |
748 | case CFGTOK_RUN: |
749 | FlagAttr (&S->Attr, SA_RUN, "RUN" ); |
750 | S->Run = CfgGetMemory (GetStrBufId (&CfgSVal)); |
751 | CfgNextTok (); |
752 | break; |
753 | |
754 | case CFGTOK_START: |
755 | FlagAttr (&S->Attr, SA_START, "START" ); |
756 | S->Addr = CfgCheckedConstExpr (1, 0x1000000); |
757 | S->Flags |= SF_START; |
758 | break; |
759 | |
760 | case CFGTOK_TYPE: |
761 | FlagAttr (&S->Attr, SA_TYPE, "TYPE" ); |
762 | CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type" ); |
763 | switch (CfgTok) { |
764 | case CFGTOK_RO: S->Flags |= SF_RO; break; |
765 | case CFGTOK_RW: /* Default */ break; |
766 | case CFGTOK_BSS: S->Flags |= SF_BSS; break; |
767 | case CFGTOK_ZP: S->Flags |= (SF_BSS | SF_ZP); break; |
768 | case CFGTOK_OVERWRITE: S->Flags |= (SF_OVERWRITE | SF_RO); break; |
769 | default: Internal ("Unexpected token: %d" , CfgTok); |
770 | } |
771 | CfgNextTok (); |
772 | break; |
773 | |
774 | default: |
775 | FAIL ("Unexpected attribute token" ); |
776 | |
777 | } |
778 | |
779 | /* Skip an optional comma */ |
780 | CfgOptionalComma (); |
781 | } |
782 | |
783 | /* Check for mandatory parameters */ |
784 | AttrCheck (S->Attr, SA_LOAD, "LOAD" ); |
785 | |
786 | /* Set defaults for stuff not given */ |
787 | if ((S->Attr & SA_RUN) == 0) { |
788 | S->Attr |= SA_RUN; |
789 | S->Run = S->Load; |
790 | } |
791 | |
792 | /* An attribute of ALIGN_LOAD doesn't make sense if there are no |
793 | ** separate run and load memory areas. |
794 | */ |
795 | if ((S->Flags & SF_ALIGN_LOAD) != 0 && (S->Load == S->Run)) { |
796 | CfgWarning (&CfgErrorPos, |
797 | "ALIGN_LOAD attribute specified, but no separate " |
798 | "LOAD and RUN memory areas assigned" ); |
799 | /* Remove the flag */ |
800 | S->Flags &= ~SF_ALIGN_LOAD; |
801 | } |
802 | |
803 | /* If the segment is marked as BSS style, it may not have separate |
804 | ** load and run memory areas, because it's is never written to disk. |
805 | */ |
806 | if ((S->Flags & SF_BSS) != 0 && (S->Load != S->Run)) { |
807 | CfgWarning (&CfgErrorPos, |
808 | "Segment with type 'bss' has both LOAD and RUN " |
809 | "memory areas assigned" ); |
810 | } |
811 | |
812 | /* Don't allow read/write data to be put into a readonly area */ |
813 | if ((S->Flags & SF_RO) == 0) { |
814 | if (S->Run->Flags & MF_RO) { |
815 | CfgError (&CfgErrorPos, |
816 | "Cannot put r/w segment '%s' in r/o memory area '%s'" , |
817 | GetString (S->Name), GetString (S->Run->Name)); |
818 | } |
819 | } |
820 | |
821 | /* Only one of ALIGN, START and OFFSET may be used */ |
822 | Count = ((S->Flags & SF_ALIGN) != 0) + |
823 | ((S->Flags & SF_OFFSET) != 0) + |
824 | ((S->Flags & SF_START) != 0); |
825 | if (Count > 1) { |
826 | CfgError (&CfgErrorPos, |
827 | "Only one of ALIGN, START, OFFSET may be used" ); |
828 | } |
829 | |
830 | /* Skip the semicolon */ |
831 | CfgConsumeSemi (); |
832 | } |
833 | |
834 | /* Remember we had this section */ |
835 | SectionsEncountered |= SE_SEGMENTS; |
836 | } |
837 | |
838 | |
839 | |
840 | static void ParseO65 (void) |
841 | /* Parse the o65 format section */ |
842 | { |
843 | static const IdentTok Attributes [] = { |
844 | { "EXPORT" , CFGTOK_EXPORT }, |
845 | { "IMPORT" , CFGTOK_IMPORT }, |
846 | { "TYPE" , CFGTOK_TYPE }, |
847 | { "OS" , CFGTOK_OS }, |
848 | { "ID" , CFGTOK_ID }, |
849 | { "VERSION" , CFGTOK_VERSION }, |
850 | }; |
851 | static const IdentTok Types [] = { |
852 | { "SMALL" , CFGTOK_SMALL }, |
853 | { "LARGE" , CFGTOK_LARGE }, |
854 | }; |
855 | static const IdentTok OperatingSystems [] = { |
856 | { "LUNIX" , CFGTOK_LUNIX }, |
857 | { "OSA65" , CFGTOK_OSA65 }, |
858 | { "CC65" , CFGTOK_CC65 }, |
859 | { "OPENCBM" , CFGTOK_OPENCBM }, |
860 | }; |
861 | |
862 | /* Bitmask to remember the attributes we got already */ |
863 | enum { |
864 | atNone = 0x0000, |
865 | atOS = 0x0001, |
866 | atOSVersion = 0x0002, |
867 | atType = 0x0004, |
868 | atImport = 0x0008, |
869 | atExport = 0x0010, |
870 | atID = 0x0020, |
871 | atVersion = 0x0040 |
872 | }; |
873 | unsigned AttrFlags = atNone; |
874 | |
875 | /* Remember the attributes read */ |
876 | unsigned OS = 0; /* Initialize to keep gcc happy */ |
877 | unsigned Version = 0; |
878 | |
879 | /* Read the attributes */ |
880 | while (CfgTok == CFGTOK_IDENT) { |
881 | |
882 | /* Map the identifier to a token */ |
883 | cfgtok_t AttrTok; |
884 | CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute" ); |
885 | AttrTok = CfgTok; |
886 | |
887 | /* An optional assignment follows */ |
888 | CfgNextTok (); |
889 | CfgOptionalAssign (); |
890 | |
891 | /* Check which attribute was given */ |
892 | switch (AttrTok) { |
893 | |
894 | case CFGTOK_EXPORT: |
895 | /* Remember we had this token (maybe more than once) */ |
896 | AttrFlags |= atExport; |
897 | /* We expect an identifier */ |
898 | CfgAssureIdent (); |
899 | /* Remember it as an export for later */ |
900 | NewCfgSymbol (CfgSymO65Export, GetStrBufId (&CfgSVal)); |
901 | /* Eat the identifier token */ |
902 | CfgNextTok (); |
903 | break; |
904 | |
905 | case CFGTOK_IMPORT: |
906 | /* Remember we had this token (maybe more than once) */ |
907 | AttrFlags |= atImport; |
908 | /* We expect an identifier */ |
909 | CfgAssureIdent (); |
910 | /* Remember it as an import for later */ |
911 | NewCfgSymbol (CfgSymO65Import, GetStrBufId (&CfgSVal)); |
912 | /* Eat the identifier token */ |
913 | CfgNextTok (); |
914 | break; |
915 | |
916 | case CFGTOK_TYPE: |
917 | /* Cannot have this attribute twice */ |
918 | FlagAttr (&AttrFlags, atType, "TYPE" ); |
919 | /* Get the type of the executable */ |
920 | CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type" ); |
921 | switch (CfgTok) { |
922 | |
923 | case CFGTOK_SMALL: |
924 | O65SetSmallModel (O65FmtDesc); |
925 | break; |
926 | |
927 | case CFGTOK_LARGE: |
928 | O65SetLargeModel (O65FmtDesc); |
929 | break; |
930 | |
931 | default: |
932 | CfgError (&CfgErrorPos, "Unexpected type token" ); |
933 | } |
934 | /* Eat the attribute token */ |
935 | CfgNextTok (); |
936 | break; |
937 | |
938 | case CFGTOK_OS: |
939 | /* Cannot use this attribute twice */ |
940 | FlagAttr (&AttrFlags, atOS, "OS" ); |
941 | /* Get the operating system. It may be specified as name or |
942 | ** as a number in the range 1..255. |
943 | */ |
944 | if (CfgTok == CFGTOK_INTCON) { |
945 | CfgRangeCheck (O65OS_MIN, O65OS_MAX); |
946 | OS = (unsigned) CfgIVal; |
947 | } else { |
948 | CfgSpecialToken (OperatingSystems, ENTRY_COUNT (OperatingSystems), "OS type" ); |
949 | switch (CfgTok) { |
950 | case CFGTOK_LUNIX: OS = O65OS_LUNIX; break; |
951 | case CFGTOK_OSA65: OS = O65OS_OSA65; break; |
952 | case CFGTOK_CC65: OS = O65OS_CC65; break; |
953 | case CFGTOK_OPENCBM: OS = O65OS_OPENCBM; break; |
954 | default: CfgError (&CfgErrorPos, "Unexpected OS token" ); |
955 | } |
956 | } |
957 | CfgNextTok (); |
958 | break; |
959 | |
960 | case CFGTOK_ID: |
961 | /* Cannot have this attribute twice */ |
962 | FlagAttr (&AttrFlags, atID, "ID" ); |
963 | /* We're expecting a number in the 0..$FFFF range*/ |
964 | ModuleId = (unsigned) CfgCheckedConstExpr (0, 0xFFFF); |
965 | break; |
966 | |
967 | case CFGTOK_VERSION: |
968 | /* Cannot have this attribute twice */ |
969 | FlagAttr (&AttrFlags, atVersion, "VERSION" ); |
970 | /* We're expecting a number in byte range */ |
971 | Version = (unsigned) CfgCheckedConstExpr (0, 0xFF); |
972 | break; |
973 | |
974 | default: |
975 | FAIL ("Unexpected attribute token" ); |
976 | |
977 | } |
978 | |
979 | /* Skip an optional comma */ |
980 | CfgOptionalComma (); |
981 | } |
982 | |
983 | /* Check if we have all mandatory attributes */ |
984 | AttrCheck (AttrFlags, atOS, "OS" ); |
985 | |
986 | /* Check for attributes that may not be combined */ |
987 | if (OS == O65OS_CC65) { |
988 | if ((AttrFlags & (atImport | atExport)) != 0 && ModuleId < 0x8000) { |
989 | CfgError (&CfgErrorPos, |
990 | "OS type CC65 may not have imports or exports for ids < $8000" ); |
991 | } |
992 | } else { |
993 | if (AttrFlags & atID) { |
994 | CfgError (&CfgErrorPos, |
995 | "Operating system does not support the ID attribute" ); |
996 | } |
997 | } |
998 | |
999 | /* Set the O65 operating system to use */ |
1000 | O65SetOS (O65FmtDesc, OS, Version, ModuleId); |
1001 | } |
1002 | |
1003 | |
1004 | |
1005 | static void ParseXex (void) |
1006 | /* Parse the o65 format section */ |
1007 | { |
1008 | static const IdentTok Attributes [] = { |
1009 | { "RUNAD" , CFGTOK_RUNAD }, |
1010 | { "INITAD" , CFGTOK_INITAD }, |
1011 | }; |
1012 | |
1013 | /* Remember the attributes read */ |
1014 | /* Bitmask to remember the attributes we got already */ |
1015 | enum { |
1016 | atNone = 0x0000, |
1017 | atRunAd = 0x0001, |
1018 | }; |
1019 | unsigned AttrFlags = atNone; |
1020 | Import *RunAd = 0; |
1021 | Import *InitAd; |
1022 | MemoryArea *InitMem; |
1023 | |
1024 | /* Read the attributes */ |
1025 | while (CfgTok == CFGTOK_IDENT) { |
1026 | |
1027 | /* Map the identifier to a token */ |
1028 | cfgtok_t AttrTok; |
1029 | CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute" ); |
1030 | AttrTok = CfgTok; |
1031 | |
1032 | /* An optional assignment follows */ |
1033 | CfgNextTok (); |
1034 | CfgOptionalAssign (); |
1035 | |
1036 | /* Check which attribute was given */ |
1037 | switch (AttrTok) { |
1038 | |
1039 | case CFGTOK_RUNAD: |
1040 | /* Cannot have this attribute twice */ |
1041 | FlagAttr (&AttrFlags, atRunAd, "RUNAD" ); |
1042 | /* We expect an identifier */ |
1043 | CfgAssureIdent (); |
1044 | /* Generate an import for the symbol */ |
1045 | RunAd = InsertImport (GenImport (GetStrBufId (&CfgSVal), ADDR_SIZE_ABS)); |
1046 | /* Remember the file position */ |
1047 | CollAppend (&RunAd->RefLines, GenLineInfo (&CfgErrorPos)); |
1048 | /* Eat the identifier token */ |
1049 | CfgNextTok (); |
1050 | break; |
1051 | |
1052 | case CFGTOK_INITAD: |
1053 | /* We expect a memory area followed by a colon and an identifier */ |
1054 | CfgAssureIdent (); |
1055 | InitMem = CfgGetMemory (GetStrBufId (&CfgSVal)); |
1056 | CfgNextTok (); |
1057 | CfgConsumeColon (); |
1058 | CfgAssureIdent (); |
1059 | /* Generate an import for the symbol */ |
1060 | InitAd = InsertImport (GenImport (GetStrBufId (&CfgSVal), ADDR_SIZE_ABS)); |
1061 | /* Remember the file position */ |
1062 | CollAppend (&InitAd->RefLines, GenLineInfo (&CfgErrorPos)); |
1063 | /* Eat the identifier token */ |
1064 | CfgNextTok (); |
1065 | /* Add to XEX */ |
1066 | if (XexAddInitAd (XexFmtDesc, InitMem, InitAd)) |
1067 | CfgError (&CfgErrorPos, "INITAD already given for memory area" ); |
1068 | break; |
1069 | |
1070 | default: |
1071 | FAIL ("Unexpected attribute token" ); |
1072 | |
1073 | } |
1074 | |
1075 | /* Skip an optional comma */ |
1076 | CfgOptionalComma (); |
1077 | } |
1078 | |
1079 | /* Set the RUNAD import if we have one */ |
1080 | if ( RunAd ) |
1081 | XexSetRunAd (XexFmtDesc, RunAd); |
1082 | } |
1083 | |
1084 | |
1085 | |
1086 | static void ParseFormats (void) |
1087 | /* Parse a target format section */ |
1088 | { |
1089 | static const IdentTok Formats [] = { |
1090 | { "O65" , CFGTOK_O65 }, |
1091 | { "BIN" , CFGTOK_BIN }, |
1092 | { "BINARY" , CFGTOK_BIN }, |
1093 | { "ATARI" , CFGTOK_ATARIEXE }, |
1094 | }; |
1095 | |
1096 | while (CfgTok == CFGTOK_IDENT) { |
1097 | |
1098 | /* Map the identifier to a token */ |
1099 | cfgtok_t FormatTok; |
1100 | CfgSpecialToken (Formats, ENTRY_COUNT (Formats), "Format" ); |
1101 | FormatTok = CfgTok; |
1102 | |
1103 | /* Skip the name and the following colon */ |
1104 | CfgNextTok (); |
1105 | CfgConsumeColon (); |
1106 | |
1107 | /* Parse the format options */ |
1108 | switch (FormatTok) { |
1109 | |
1110 | case CFGTOK_O65: |
1111 | ParseO65 (); |
1112 | break; |
1113 | |
1114 | case CFGTOK_ATARIEXE: |
1115 | ParseXex (); |
1116 | break; |
1117 | |
1118 | case CFGTOK_BIN: |
1119 | /* No attribibutes available */ |
1120 | break; |
1121 | |
1122 | default: |
1123 | Error ("Unexpected format token" ); |
1124 | } |
1125 | |
1126 | /* Skip the semicolon */ |
1127 | CfgConsumeSemi (); |
1128 | } |
1129 | |
1130 | |
1131 | /* Remember we had this section */ |
1132 | SectionsEncountered |= SE_FORMATS; |
1133 | } |
1134 | |
1135 | |
1136 | |
1137 | static void ParseConDes (void) |
1138 | /* Parse the CONDES feature */ |
1139 | { |
1140 | static const IdentTok Attributes [] = { |
1141 | { "COUNT" , CFGTOK_COUNT }, |
1142 | { "IMPORT" , CFGTOK_IMPORT }, |
1143 | { "LABEL" , CFGTOK_LABEL }, |
1144 | { "ORDER" , CFGTOK_ORDER }, |
1145 | { "SEGMENT" , CFGTOK_SEGMENT }, |
1146 | { "TYPE" , CFGTOK_TYPE }, |
1147 | }; |
1148 | |
1149 | static const IdentTok Types [] = { |
1150 | { "CONSTRUCTOR" , CFGTOK_CONSTRUCTOR }, |
1151 | { "DESTRUCTOR" , CFGTOK_DESTRUCTOR }, |
1152 | { "INTERRUPTOR" , CFGTOK_INTERRUPTOR }, |
1153 | }; |
1154 | |
1155 | static const IdentTok Orders [] = { |
1156 | { "DECREASING" , CFGTOK_DECREASING }, |
1157 | { "INCREASING" , CFGTOK_INCREASING }, |
1158 | }; |
1159 | |
1160 | /* Attribute values. */ |
1161 | unsigned Count = INVALID_STRING_ID; |
1162 | unsigned Label = INVALID_STRING_ID; |
1163 | unsigned SegName = INVALID_STRING_ID; |
1164 | ConDesImport Import; |
1165 | /* Initialize to avoid gcc warnings: */ |
1166 | int Type = -1; |
1167 | ConDesOrder Order = cdIncreasing; |
1168 | |
1169 | /* Bitmask to remember the attributes we got already */ |
1170 | enum { |
1171 | atNone = 0x0000, |
1172 | atCount = 0x0001, |
1173 | atImport = 0x0002, |
1174 | atLabel = 0x0004, |
1175 | atOrder = 0x0008, |
1176 | atSegName = 0x0010, |
1177 | atType = 0x0020, |
1178 | }; |
1179 | unsigned AttrFlags = atNone; |
1180 | |
1181 | /* Parse the attributes */ |
1182 | while (1) { |
1183 | |
1184 | /* Map the identifier to a token */ |
1185 | cfgtok_t AttrTok; |
1186 | CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute" ); |
1187 | AttrTok = CfgTok; |
1188 | |
1189 | /* An optional assignment follows */ |
1190 | CfgNextTok (); |
1191 | CfgOptionalAssign (); |
1192 | |
1193 | /* Check which attribute was given */ |
1194 | switch (AttrTok) { |
1195 | |
1196 | case CFGTOK_COUNT: |
1197 | /* Don't allow this twice */ |
1198 | FlagAttr (&AttrFlags, atCount, "COUNT" ); |
1199 | /* We expect an identifier */ |
1200 | CfgAssureIdent (); |
1201 | /* Remember the value for later */ |
1202 | Count = GetStrBufId (&CfgSVal); |
1203 | break; |
1204 | |
1205 | case CFGTOK_IMPORT: |
1206 | /* Don't allow this twice */ |
1207 | FlagAttr (&AttrFlags, atImport, "IMPORT" ); |
1208 | /* We expect an identifier */ |
1209 | CfgAssureIdent (); |
1210 | /* Remember value and position for later */ |
1211 | Import.Name = GetStrBufId (&CfgSVal); |
1212 | Import.Pos = CfgErrorPos; |
1213 | Import.AddrSize = ADDR_SIZE_ABS; |
1214 | break; |
1215 | |
1216 | case CFGTOK_LABEL: |
1217 | /* Don't allow this twice */ |
1218 | FlagAttr (&AttrFlags, atLabel, "LABEL" ); |
1219 | /* We expect an identifier */ |
1220 | CfgAssureIdent (); |
1221 | /* Remember the value for later */ |
1222 | Label = GetStrBufId (&CfgSVal); |
1223 | break; |
1224 | |
1225 | case CFGTOK_ORDER: |
1226 | /* Don't allow this twice */ |
1227 | FlagAttr (&AttrFlags, atOrder, "ORDER" ); |
1228 | CfgSpecialToken (Orders, ENTRY_COUNT (Orders), "Order" ); |
1229 | switch (CfgTok) { |
1230 | case CFGTOK_DECREASING: Order = cdDecreasing; break; |
1231 | case CFGTOK_INCREASING: Order = cdIncreasing; break; |
1232 | default: FAIL ("Unexpected order token" ); |
1233 | } |
1234 | break; |
1235 | |
1236 | case CFGTOK_SEGMENT: |
1237 | /* Don't allow this twice */ |
1238 | FlagAttr (&AttrFlags, atSegName, "SEGMENT" ); |
1239 | /* We expect an identifier */ |
1240 | CfgAssureIdent (); |
1241 | /* Remember the value for later */ |
1242 | SegName = GetStrBufId (&CfgSVal); |
1243 | break; |
1244 | |
1245 | case CFGTOK_TYPE: |
1246 | /* Don't allow this twice */ |
1247 | FlagAttr (&AttrFlags, atType, "TYPE" ); |
1248 | /* The type may be given as id or numerical */ |
1249 | if (CfgTok == CFGTOK_INTCON) { |
1250 | CfgRangeCheck (CD_TYPE_MIN, CD_TYPE_MAX); |
1251 | Type = (int) CfgIVal; |
1252 | } else { |
1253 | CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type" ); |
1254 | switch (CfgTok) { |
1255 | case CFGTOK_CONSTRUCTOR: Type = CD_TYPE_CON; break; |
1256 | case CFGTOK_DESTRUCTOR: Type = CD_TYPE_DES; break; |
1257 | case CFGTOK_INTERRUPTOR: Type = CD_TYPE_INT; break; |
1258 | default: FAIL ("Unexpected type token" ); |
1259 | } |
1260 | } |
1261 | break; |
1262 | |
1263 | default: |
1264 | FAIL ("Unexpected attribute token" ); |
1265 | |
1266 | } |
1267 | |
1268 | /* Skip the attribute value */ |
1269 | CfgNextTok (); |
1270 | |
1271 | /* Semicolon ends the ConDes decl, otherwise accept an optional comma */ |
1272 | if (CfgTok == CFGTOK_SEMI) { |
1273 | break; |
1274 | } else if (CfgTok == CFGTOK_COMMA) { |
1275 | CfgNextTok (); |
1276 | } |
1277 | } |
1278 | |
1279 | /* Check if we have all mandatory attributes */ |
1280 | AttrCheck (AttrFlags, atSegName, "SEGMENT" ); |
1281 | AttrCheck (AttrFlags, atLabel, "LABEL" ); |
1282 | AttrCheck (AttrFlags, atType, "TYPE" ); |
1283 | |
1284 | /* Check if the condes has already attributes defined */ |
1285 | if (ConDesHasSegName(Type) || ConDesHasLabel(Type)) { |
1286 | CfgError (&CfgErrorPos, |
1287 | "CONDES attributes for type %d are already defined" , |
1288 | Type); |
1289 | } |
1290 | |
1291 | /* Define the attributes */ |
1292 | ConDesSetSegName (Type, SegName); |
1293 | ConDesSetLabel (Type, Label); |
1294 | if (AttrFlags & atCount) { |
1295 | ConDesSetCountSym (Type, Count); |
1296 | } |
1297 | if (AttrFlags & atImport) { |
1298 | ConDesSetImport (Type, &Import); |
1299 | } |
1300 | if (AttrFlags & atOrder) { |
1301 | ConDesSetOrder (Type, Order); |
1302 | } |
1303 | } |
1304 | |
1305 | |
1306 | |
1307 | static void ParseStartAddress (void) |
1308 | /* Parse the STARTADDRESS feature */ |
1309 | { |
1310 | static const IdentTok Attributes [] = { |
1311 | { "DEFAULT" , CFGTOK_DEFAULT }, |
1312 | }; |
1313 | |
1314 | |
1315 | /* Attribute values. */ |
1316 | unsigned long DefStartAddr = 0; |
1317 | |
1318 | /* Bitmask to remember the attributes we got already */ |
1319 | enum { |
1320 | atNone = 0x0000, |
1321 | atDefault = 0x0001 |
1322 | }; |
1323 | unsigned AttrFlags = atNone; |
1324 | |
1325 | /* Parse the attributes */ |
1326 | while (1) { |
1327 | |
1328 | /* Map the identifier to a token */ |
1329 | cfgtok_t AttrTok; |
1330 | CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute" ); |
1331 | AttrTok = CfgTok; |
1332 | |
1333 | /* An optional assignment follows */ |
1334 | CfgNextTok (); |
1335 | CfgOptionalAssign (); |
1336 | |
1337 | /* Check which attribute was given */ |
1338 | switch (AttrTok) { |
1339 | |
1340 | case CFGTOK_DEFAULT: |
1341 | /* Don't allow this twice */ |
1342 | FlagAttr (&AttrFlags, atDefault, "DEFAULT" ); |
1343 | /* We expect a numeric expression */ |
1344 | DefStartAddr = CfgCheckedConstExpr (0, 0xFFFFFF); |
1345 | break; |
1346 | |
1347 | default: |
1348 | FAIL ("Unexpected attribute token" ); |
1349 | |
1350 | } |
1351 | |
1352 | /* Semicolon ends the ConDes decl, otherwise accept an optional comma */ |
1353 | if (CfgTok == CFGTOK_SEMI) { |
1354 | break; |
1355 | } else if (CfgTok == CFGTOK_COMMA) { |
1356 | CfgNextTok (); |
1357 | } |
1358 | } |
1359 | |
1360 | /* Check if we have all mandatory attributes */ |
1361 | AttrCheck (AttrFlags, atDefault, "DEFAULT" ); |
1362 | |
1363 | /* If no start address was given on the command line, use the one given |
1364 | ** here |
1365 | */ |
1366 | if (!HaveStartAddr) { |
1367 | StartAddr = DefStartAddr; |
1368 | } |
1369 | } |
1370 | |
1371 | |
1372 | |
1373 | static void ParseFeatures (void) |
1374 | /* Parse a features section */ |
1375 | { |
1376 | static const IdentTok Features [] = { |
1377 | { "CONDES" , CFGTOK_CONDES }, |
1378 | { "STARTADDRESS" , CFGTOK_STARTADDRESS }, |
1379 | }; |
1380 | |
1381 | while (CfgTok == CFGTOK_IDENT) { |
1382 | |
1383 | /* Map the identifier to a token */ |
1384 | cfgtok_t FeatureTok; |
1385 | CfgSpecialToken (Features, ENTRY_COUNT (Features), "Feature" ); |
1386 | FeatureTok = CfgTok; |
1387 | |
1388 | /* Skip the name and the following colon */ |
1389 | CfgNextTok (); |
1390 | CfgConsumeColon (); |
1391 | |
1392 | /* Parse the format options */ |
1393 | switch (FeatureTok) { |
1394 | |
1395 | case CFGTOK_CONDES: |
1396 | ParseConDes (); |
1397 | break; |
1398 | |
1399 | case CFGTOK_STARTADDRESS: |
1400 | ParseStartAddress (); |
1401 | break; |
1402 | |
1403 | |
1404 | default: |
1405 | FAIL ("Unexpected feature token" ); |
1406 | } |
1407 | |
1408 | /* Skip the semicolon */ |
1409 | CfgConsumeSemi (); |
1410 | } |
1411 | |
1412 | /* Remember we had this section */ |
1413 | SectionsEncountered |= SE_FEATURES; |
1414 | } |
1415 | |
1416 | |
1417 | |
1418 | static void ParseSymbols (void) |
1419 | /* Parse a symbols section */ |
1420 | { |
1421 | static const IdentTok Attributes[] = { |
1422 | { "ADDRSIZE" , CFGTOK_ADDRSIZE }, |
1423 | { "TYPE" , CFGTOK_TYPE }, |
1424 | { "VALUE" , CFGTOK_VALUE }, |
1425 | }; |
1426 | |
1427 | static const IdentTok AddrSizes [] = { |
1428 | { "ABS" , CFGTOK_ABS }, |
1429 | { "ABSOLUTE" , CFGTOK_ABS }, |
1430 | { "DIRECT" , CFGTOK_ZP }, |
1431 | { "DWORD" , CFGTOK_LONG }, |
1432 | { "FAR" , CFGTOK_FAR }, |
1433 | { "LONG" , CFGTOK_LONG }, |
1434 | { "NEAR" , CFGTOK_ABS }, |
1435 | { "ZEROPAGE" , CFGTOK_ZP }, |
1436 | { "ZP" , CFGTOK_ZP }, |
1437 | }; |
1438 | |
1439 | static const IdentTok Types [] = { |
1440 | { "EXPORT" , CFGTOK_EXPORT }, |
1441 | { "IMPORT" , CFGTOK_IMPORT }, |
1442 | { "WEAK" , CFGTOK_WEAK }, |
1443 | }; |
1444 | |
1445 | while (CfgTok == CFGTOK_IDENT) { |
1446 | |
1447 | /* Bitmask to remember the attributes we got already */ |
1448 | enum { |
1449 | atNone = 0x0000, |
1450 | atAddrSize = 0x0001, |
1451 | atType = 0x0002, |
1452 | atValue = 0x0004, |
1453 | }; |
1454 | unsigned AttrFlags = atNone; |
1455 | |
1456 | ExprNode* Value = 0; |
1457 | CfgSymType Type = CfgSymExport; |
1458 | unsigned char AddrSize = ADDR_SIZE_ABS; |
1459 | Import* Imp; |
1460 | Export* Exp; |
1461 | CfgSymbol* Sym; |
1462 | |
1463 | /* Remember the name */ |
1464 | unsigned Name = GetStrBufId (&CfgSVal); |
1465 | CfgNextTok (); |
1466 | |
1467 | /* New syntax - skip the colon */ |
1468 | CfgNextTok (); |
1469 | |
1470 | /* Parse the attributes */ |
1471 | while (1) { |
1472 | |
1473 | /* Map the identifier to a token */ |
1474 | cfgtok_t AttrTok; |
1475 | CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute" ); |
1476 | AttrTok = CfgTok; |
1477 | |
1478 | /* Skip the attribute name */ |
1479 | CfgNextTok (); |
1480 | |
1481 | /* An optional assignment follows */ |
1482 | CfgOptionalAssign (); |
1483 | |
1484 | /* Check which attribute was given */ |
1485 | switch (AttrTok) { |
1486 | |
1487 | case CFGTOK_ADDRSIZE: |
1488 | /* Don't allow this twice */ |
1489 | FlagAttr (&AttrFlags, atAddrSize, "ADDRSIZE" ); |
1490 | /* Map the type to a token */ |
1491 | CfgSpecialToken (AddrSizes, ENTRY_COUNT (AddrSizes), "AddrSize" ); |
1492 | switch (CfgTok) { |
1493 | case CFGTOK_ABS: AddrSize = ADDR_SIZE_ABS; break; |
1494 | case CFGTOK_FAR: AddrSize = ADDR_SIZE_FAR; break; |
1495 | case CFGTOK_LONG: AddrSize = ADDR_SIZE_LONG; break; |
1496 | case CFGTOK_ZP: AddrSize = ADDR_SIZE_ZP; break; |
1497 | default: |
1498 | Internal ("Unexpected token: %d" , CfgTok); |
1499 | } |
1500 | CfgNextTok (); |
1501 | break; |
1502 | |
1503 | case CFGTOK_TYPE: |
1504 | /* Don't allow this twice */ |
1505 | FlagAttr (&AttrFlags, atType, "TYPE" ); |
1506 | /* Map the type to a token */ |
1507 | CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type" ); |
1508 | switch (CfgTok) { |
1509 | case CFGTOK_EXPORT: Type = CfgSymExport; break; |
1510 | case CFGTOK_IMPORT: Type = CfgSymImport; break; |
1511 | case CFGTOK_WEAK: Type = CfgSymWeak; break; |
1512 | default: |
1513 | Internal ("Unexpected token: %d" , CfgTok); |
1514 | } |
1515 | CfgNextTok (); |
1516 | break; |
1517 | |
1518 | case CFGTOK_VALUE: |
1519 | /* Don't allow this twice */ |
1520 | FlagAttr (&AttrFlags, atValue, "VALUE" ); |
1521 | /* Value is an expression */ |
1522 | Value = CfgExpr (); |
1523 | break; |
1524 | |
1525 | default: |
1526 | FAIL ("Unexpected attribute token" ); |
1527 | |
1528 | } |
1529 | |
1530 | /* Semicolon ends the decl, otherwise accept an optional comma */ |
1531 | if (CfgTok == CFGTOK_SEMI) { |
1532 | break; |
1533 | } else if (CfgTok == CFGTOK_COMMA) { |
1534 | CfgNextTok (); |
1535 | } |
1536 | } |
1537 | |
1538 | /* We must have a type */ |
1539 | AttrCheck (AttrFlags, atType, "TYPE" ); |
1540 | |
1541 | /* Further actions depend on the type */ |
1542 | switch (Type) { |
1543 | |
1544 | case CfgSymExport: |
1545 | /* We must have a value */ |
1546 | AttrCheck (AttrFlags, atValue, "VALUE" ); |
1547 | /* Create the export */ |
1548 | Exp = CreateExprExport (Name, Value, AddrSize); |
1549 | CollAppend (&Exp->DefLines, GenLineInfo (&CfgErrorPos)); |
1550 | break; |
1551 | |
1552 | case CfgSymImport: |
1553 | /* An import must not have a value */ |
1554 | if (AttrFlags & atValue) { |
1555 | CfgError (&CfgErrorPos, "Imports must not have a value" ); |
1556 | } |
1557 | /* Generate the import */ |
1558 | Imp = InsertImport (GenImport (Name, AddrSize)); |
1559 | /* Remember the file position */ |
1560 | CollAppend (&Imp->RefLines, GenLineInfo (&CfgErrorPos)); |
1561 | break; |
1562 | |
1563 | case CfgSymWeak: |
1564 | /* We must have a value */ |
1565 | AttrCheck (AttrFlags, atValue, "VALUE" ); |
1566 | /* Remember the symbol for later */ |
1567 | Sym = NewCfgSymbol (CfgSymWeak, Name); |
1568 | Sym->Value = Value; |
1569 | Sym->AddrSize = AddrSize; |
1570 | break; |
1571 | |
1572 | default: |
1573 | Internal ("Unexpected symbol type %d" , Type); |
1574 | } |
1575 | |
1576 | /* Skip the semicolon */ |
1577 | CfgConsumeSemi (); |
1578 | } |
1579 | |
1580 | /* Remember we had this section */ |
1581 | SectionsEncountered |= SE_SYMBOLS; |
1582 | } |
1583 | |
1584 | |
1585 | |
1586 | static void ParseConfig (void) |
1587 | /* Parse the config file */ |
1588 | { |
1589 | static const IdentTok BlockNames [] = { |
1590 | { "MEMORY" , CFGTOK_MEMORY }, |
1591 | { "FILES" , CFGTOK_FILES }, |
1592 | { "SEGMENTS" , CFGTOK_SEGMENTS }, |
1593 | { "FORMATS" , CFGTOK_FORMATS }, |
1594 | { "FEATURES" , CFGTOK_FEATURES }, |
1595 | { "SYMBOLS" , CFGTOK_SYMBOLS }, |
1596 | }; |
1597 | cfgtok_t BlockTok; |
1598 | |
1599 | do { |
1600 | |
1601 | /* Read the block ident */ |
1602 | CfgSpecialToken (BlockNames, ENTRY_COUNT (BlockNames), "Block identifier" ); |
1603 | BlockTok = CfgTok; |
1604 | CfgNextTok (); |
1605 | |
1606 | /* Expected a curly brace */ |
1607 | CfgConsume (CFGTOK_LCURLY, "'{' expected" ); |
1608 | |
1609 | /* Read the block */ |
1610 | switch (BlockTok) { |
1611 | |
1612 | case CFGTOK_MEMORY: |
1613 | ParseMemory (); |
1614 | break; |
1615 | |
1616 | case CFGTOK_FILES: |
1617 | ParseFiles (); |
1618 | break; |
1619 | |
1620 | case CFGTOK_SEGMENTS: |
1621 | ParseSegments (); |
1622 | break; |
1623 | |
1624 | case CFGTOK_FORMATS: |
1625 | ParseFormats (); |
1626 | break; |
1627 | |
1628 | case CFGTOK_FEATURES: |
1629 | ParseFeatures (); |
1630 | break; |
1631 | |
1632 | case CFGTOK_SYMBOLS: |
1633 | ParseSymbols (); |
1634 | break; |
1635 | |
1636 | default: |
1637 | FAIL ("Unexpected block token" ); |
1638 | |
1639 | } |
1640 | |
1641 | /* Skip closing brace */ |
1642 | CfgConsume (CFGTOK_RCURLY, "'}' expected" ); |
1643 | |
1644 | } while (CfgTok != CFGTOK_EOF); |
1645 | } |
1646 | |
1647 | |
1648 | |
1649 | void CfgRead (void) |
1650 | /* Read the configuration */ |
1651 | { |
1652 | /* Create the descriptors for the binary formats */ |
1653 | BinFmtDesc = NewBinDesc (); |
1654 | O65FmtDesc = NewO65Desc (); |
1655 | XexFmtDesc = NewXexDesc (); |
1656 | |
1657 | /* If we have a config name given, open the file, otherwise we will read |
1658 | ** from a buffer. |
1659 | */ |
1660 | CfgOpenInput (); |
1661 | |
1662 | /* Parse the file */ |
1663 | ParseConfig (); |
1664 | |
1665 | /* Close the input file */ |
1666 | CfgCloseInput (); |
1667 | } |
1668 | |
1669 | |
1670 | |
1671 | /*****************************************************************************/ |
1672 | /* Config file processing */ |
1673 | /*****************************************************************************/ |
1674 | |
1675 | |
1676 | |
1677 | static void ProcessSegments (void) |
1678 | /* Process the SEGMENTS section */ |
1679 | { |
1680 | unsigned I; |
1681 | |
1682 | /* Walk over the list of segment descriptors */ |
1683 | I = 0; |
1684 | while (I < CollCount (&SegDescList)) { |
1685 | |
1686 | /* Get the next segment descriptor */ |
1687 | SegDesc* S = CollAtUnchecked (&SegDescList, I); |
1688 | |
1689 | /* Search for the actual segment in the input files. The function may |
1690 | ** return NULL (no such segment), this is checked later. |
1691 | */ |
1692 | S->Seg = SegFind (S->Name); |
1693 | |
1694 | /* If the segment is marked as BSS style, and if the segment exists |
1695 | ** in any of the object file, check that there's no initialized data |
1696 | ** in the segment. |
1697 | */ |
1698 | if ((S->Flags & SF_BSS) != 0 && S->Seg != 0 && !IsBSSType (S->Seg)) { |
1699 | CfgWarning (GetSourcePos (S->LI), |
1700 | "Segment '%s' with type 'bss' contains initialized data" , |
1701 | GetString (S->Name)); |
1702 | } |
1703 | |
1704 | /* If this segment does exist in any of the object files, insert the |
1705 | ** segment into the load/run memory areas. Otherwise print a warning |
1706 | ** and discard it, because the segment pointer in the descriptor is |
1707 | ** invalid. |
1708 | */ |
1709 | if (S->Seg != 0) { |
1710 | |
1711 | /* Insert the segment into the memory area list */ |
1712 | MemoryInsert (S->Run, S); |
1713 | if (S->Load != S->Run) { |
1714 | /* We have separate RUN and LOAD areas */ |
1715 | MemoryInsert (S->Load, S); |
1716 | } |
1717 | |
1718 | /* Use the fill value from the config */ |
1719 | S->Seg->FillVal = S->FillVal; |
1720 | |
1721 | /* Process the next segment descriptor in the next run */ |
1722 | ++I; |
1723 | |
1724 | } else { |
1725 | |
1726 | /* Print a warning if the segment is not optional */ |
1727 | if ((S->Flags & SF_OPTIONAL) == 0) { |
1728 | CfgWarning (&CfgErrorPos, |
1729 | "Segment '%s' does not exist" , |
1730 | GetString (S->Name)); |
1731 | } |
1732 | |
1733 | /* Discard the descriptor and remove it from the collection */ |
1734 | FreeSegDesc (S); |
1735 | CollDelete (&SegDescList, I); |
1736 | } |
1737 | } |
1738 | } |
1739 | |
1740 | |
1741 | |
1742 | static void ProcessSymbols (void) |
1743 | /* Process the SYMBOLS section */ |
1744 | { |
1745 | Export* E; |
1746 | |
1747 | /* Walk over all symbols */ |
1748 | unsigned I; |
1749 | for (I = 0; I < CollCount (&CfgSymbols); ++I) { |
1750 | |
1751 | /* Get the next symbol */ |
1752 | CfgSymbol* Sym = CollAtUnchecked (&CfgSymbols, I); |
1753 | |
1754 | /* Check what it is. */ |
1755 | switch (Sym->Type) { |
1756 | |
1757 | case CfgSymO65Export: |
1758 | /* Check if the export symbol is also defined as an import. */ |
1759 | if (O65GetImport (O65FmtDesc, Sym->Name) != 0) { |
1760 | CfgError ( |
1761 | GetSourcePos (Sym->LI), |
1762 | "Exported o65 symbol '%s' cannot also be an o65 import" , |
1763 | GetString (Sym->Name) |
1764 | ); |
1765 | } |
1766 | |
1767 | /* Check if we have this symbol defined already. The entry |
1768 | ** routine will check this also, but we get a more verbose |
1769 | ** error message when checking it here. |
1770 | */ |
1771 | if (O65GetExport (O65FmtDesc, Sym->Name) != 0) { |
1772 | CfgError ( |
1773 | GetSourcePos (Sym->LI), |
1774 | "Duplicate exported o65 symbol: '%s'" , |
1775 | GetString (Sym->Name) |
1776 | ); |
1777 | } |
1778 | |
1779 | /* Insert the symbol into the table */ |
1780 | O65SetExport (O65FmtDesc, Sym->Name); |
1781 | break; |
1782 | |
1783 | case CfgSymO65Import: |
1784 | /* Check if the import symbol is also defined as an export. */ |
1785 | if (O65GetExport (O65FmtDesc, Sym->Name) != 0) { |
1786 | CfgError ( |
1787 | GetSourcePos (Sym->LI), |
1788 | "Imported o65 symbol '%s' cannot also be an o65 export" , |
1789 | GetString (Sym->Name) |
1790 | ); |
1791 | } |
1792 | |
1793 | /* Check if we have this symbol defined already. The entry |
1794 | ** routine will check this also, but we get a more verbose |
1795 | ** error message when checking it here. |
1796 | */ |
1797 | if (O65GetImport (O65FmtDesc, Sym->Name) != 0) { |
1798 | CfgError ( |
1799 | GetSourcePos (Sym->LI), |
1800 | "Duplicate imported o65 symbol: '%s'" , |
1801 | GetString (Sym->Name) |
1802 | ); |
1803 | } |
1804 | |
1805 | /* Insert the symbol into the table */ |
1806 | O65SetImport (O65FmtDesc, Sym->Name); |
1807 | break; |
1808 | |
1809 | case CfgSymWeak: |
1810 | /* If the symbol is not defined until now, define it */ |
1811 | if ((E = FindExport (Sym->Name)) == 0 || IsUnresolvedExport (E)) { |
1812 | /* The symbol is undefined, generate an export */ |
1813 | E = CreateExprExport (Sym->Name, Sym->Value, Sym->AddrSize); |
1814 | CollAppend (&E->DefLines, Sym->LI); |
1815 | } |
1816 | break; |
1817 | |
1818 | default: |
1819 | Internal ("Unexpected symbol type %d" , Sym->Type); |
1820 | break; |
1821 | } |
1822 | } |
1823 | |
1824 | } |
1825 | |
1826 | |
1827 | |
1828 | static void CreateRunDefines (SegDesc* S, unsigned long SegAddr) |
1829 | /* Create the defines for a RUN segment */ |
1830 | { |
1831 | Export* E; |
1832 | StrBuf Buf = STATIC_STRBUF_INITIALIZER; |
1833 | |
1834 | /* Define the run address of the segment */ |
1835 | SB_Printf (&Buf, "__%s_RUN__" , GetString (S->Name)); |
1836 | E = CreateMemoryExport (GetStrBufId (&Buf), S->Run, SegAddr - S->Run->Start); |
1837 | CollAppend (&E->DefLines, S->LI); |
1838 | |
1839 | /* Define the size of the segment */ |
1840 | SB_Printf (&Buf, "__%s_SIZE__" , GetString (S->Name)); |
1841 | E = CreateConstExport (GetStrBufId (&Buf), S->Seg->Size); |
1842 | CollAppend (&E->DefLines, S->LI); |
1843 | |
1844 | S->Flags |= SF_RUN_DEF; |
1845 | SB_Done (&Buf); |
1846 | } |
1847 | |
1848 | |
1849 | |
1850 | static void CreateLoadDefines (SegDesc* S, unsigned long SegAddr) |
1851 | /* Create the defines for a LOAD segment */ |
1852 | { |
1853 | Export* E; |
1854 | StrBuf Buf = STATIC_STRBUF_INITIALIZER; |
1855 | |
1856 | /* Define the load address of the segment */ |
1857 | SB_Printf (&Buf, "__%s_LOAD__" , GetString (S->Name)); |
1858 | E = CreateMemoryExport (GetStrBufId (&Buf), S->Load, SegAddr - S->Load->Start); |
1859 | CollAppend (&E->DefLines, S->LI); |
1860 | |
1861 | S->Flags |= SF_LOAD_DEF; |
1862 | SB_Done (&Buf); |
1863 | } |
1864 | |
1865 | |
1866 | |
1867 | unsigned CfgProcess (void) |
1868 | /* Process the config file, after reading in object files and libraries. This |
1869 | ** includes postprocessing of the config file data; but also assigning segments, |
1870 | ** and defining segment/memory-area related symbols. The function will return |
1871 | ** the number of memory area overflows (so, zero means everything went OK). |
1872 | ** In case of overflows, a short mapfile can be generated later, to ease the |
1873 | ** user's task of re-arranging segments. |
1874 | */ |
1875 | { |
1876 | unsigned Overflows = 0; |
1877 | unsigned I; |
1878 | |
1879 | /* Postprocess symbols. We must do that first, since weak symbols are |
1880 | ** defined here, which may be needed later. |
1881 | */ |
1882 | ProcessSymbols (); |
1883 | |
1884 | /* Postprocess segments */ |
1885 | ProcessSegments (); |
1886 | |
1887 | /* Walk through each of the memory sections. Add up the sizes; and, check |
1888 | ** for an overflow of the section. Assign the start addresses of the |
1889 | ** segments while doing that. |
1890 | */ |
1891 | for (I = 0; I < CollCount (&MemoryAreas); ++I) { |
1892 | unsigned J; |
1893 | unsigned long Addr; |
1894 | unsigned Overwrites = 0; |
1895 | |
1896 | /* Get the next memory area */ |
1897 | MemoryArea* M = CollAtUnchecked (&MemoryAreas, I); |
1898 | |
1899 | /* Remember the offset in the output file */ |
1900 | M->FileOffs = M->F->Size; |
1901 | |
1902 | /* Remember if this is a relocatable memory area */ |
1903 | M->Relocatable = RelocatableBinFmt (M->F->Format); |
1904 | |
1905 | /* Resolve the start address expression, remember the start address, |
1906 | ** and mark the memory area as placed. |
1907 | */ |
1908 | if (!IsConstExpr (M->StartExpr)) { |
1909 | CfgError (GetSourcePos (M->LI), |
1910 | "Start address of memory area '%s' is not constant" , |
1911 | GetString (M->Name)); |
1912 | } |
1913 | Addr = M->Start = GetExprVal (M->StartExpr); |
1914 | M->Flags |= MF_PLACED; |
1915 | |
1916 | /* If requested, define the symbol for the start of the memory area. |
1917 | ** Doing it here means that the expression for the size of the area |
1918 | ** may reference this symbol. |
1919 | */ |
1920 | if (M->Flags & MF_DEFINE) { |
1921 | Export* E; |
1922 | StrBuf Buf = STATIC_STRBUF_INITIALIZER; |
1923 | |
1924 | /* Define the start of the memory area */ |
1925 | SB_Printf (&Buf, "__%s_START__" , GetString (M->Name)); |
1926 | E = CreateMemoryExport (GetStrBufId (&Buf), M, 0); |
1927 | CollAppend (&E->DefLines, M->LI); |
1928 | |
1929 | SB_Done (&Buf); |
1930 | } |
1931 | |
1932 | /* Resolve the size expression */ |
1933 | if (!IsConstExpr (M->SizeExpr)) { |
1934 | CfgError (GetSourcePos (M->LI), |
1935 | "Size of memory area '%s' is not constant" , |
1936 | GetString (M->Name)); |
1937 | } |
1938 | M->Size = GetExprVal (M->SizeExpr); |
1939 | |
1940 | /* Walk through the segments in this memory area */ |
1941 | for (J = 0; J < CollCount (&M->SegList); ++J) { |
1942 | /* Get the segment */ |
1943 | SegDesc* S = CollAtUnchecked (&M->SegList, J); |
1944 | |
1945 | /* Remember the start address before handling this segment */ |
1946 | unsigned long StartAddr = Addr; |
1947 | |
1948 | /* Take note of "overwrite" segments and make sure there are no |
1949 | ** other segment types following them in current memory region. |
1950 | */ |
1951 | if (S->Flags & SF_OVERWRITE) { |
1952 | if (S->Flags & (SF_OFFSET | SF_START)) { |
1953 | ++Overwrites; |
1954 | } else { |
1955 | CfgError (GetSourcePos (M->LI), |
1956 | "Segment '%s' of type 'overwrite' requires either" |
1957 | " 'Start' or 'Offset' attribute to be specified" , |
1958 | GetString (S->Name)); |
1959 | } |
1960 | } else { |
1961 | if (Overwrites > 0) { |
1962 | CfgError (GetSourcePos (M->LI), |
1963 | "Segment '%s' is preceded by at least one segment" |
1964 | " of type 'overwrite'" , |
1965 | GetString (S->Name)); |
1966 | } |
1967 | } |
1968 | |
1969 | /* Some actions depend on whether this is the load or run memory |
1970 | ** area. |
1971 | */ |
1972 | if (S->Run == M) { |
1973 | /* This is the run (and maybe load) memory area. Handle |
1974 | ** alignment and explict start address and offset. |
1975 | */ |
1976 | |
1977 | /* Check if the alignment for the segment from the linker |
1978 | ** config is a multiple for that of the segment. |
1979 | ** If START or OFFSET is provided instead of ALIGN, check |
1980 | ** if its address fits alignment requirements. |
1981 | */ |
1982 | unsigned long AlignedBy = (S->Flags & SF_START) ? S->Addr |
1983 | : (S->Flags & SF_OFFSET) ? (S->Addr + M->Start) |
1984 | : S->RunAlignment; |
1985 | if ((AlignedBy % S->Seg->Alignment) != 0) { |
1986 | /* Segment requires another alignment than configured |
1987 | ** in the linker. |
1988 | */ |
1989 | CfgWarning (GetSourcePos (S->LI), |
1990 | "Segment '%s' isn't aligned properly; the" |
1991 | " resulting executable might not be functional." , |
1992 | GetString (S->Name)); |
1993 | } |
1994 | |
1995 | if (S->Flags & SF_ALIGN) { |
1996 | /* Align the address */ |
1997 | unsigned long NewAddr = AlignAddr (Addr, S->RunAlignment); |
1998 | |
1999 | /* If the first segment placed in the memory area needs |
2000 | ** fill bytes for the alignment, emit a warning, since |
2001 | ** that is somewhat suspicious. |
2002 | */ |
2003 | if (M->FillLevel == 0 && NewAddr > Addr) { |
2004 | CfgWarning (GetSourcePos (S->LI), |
2005 | "The first segment in memory area '%s' " |
2006 | "needs fill bytes for alignment." , |
2007 | GetString (M->Name)); |
2008 | } |
2009 | |
2010 | /* Use the aligned address */ |
2011 | Addr = NewAddr; |
2012 | |
2013 | } else if ((S->Flags & (SF_OFFSET | SF_START)) != 0 && |
2014 | (M->Flags & MF_OVERFLOW) == 0) { |
2015 | /* Give the segment a fixed starting address */ |
2016 | unsigned long NewAddr = S->Addr; |
2017 | |
2018 | if (S->Flags & SF_OFFSET) { |
2019 | /* An offset was given, no address, make an address */ |
2020 | NewAddr += M->Start; |
2021 | } |
2022 | |
2023 | if (S->Flags & SF_OVERWRITE) { |
2024 | if (NewAddr < M->Start) { |
2025 | CfgError (GetSourcePos (S->LI), |
2026 | "Segment '%s' begins before memory area '%s'" , |
2027 | GetString (S->Name), GetString (M->Name)); |
2028 | } else { |
2029 | Addr = NewAddr; |
2030 | } |
2031 | } else { |
2032 | if (NewAddr < Addr) { |
2033 | /* Offset already too large */ |
2034 | ++Overflows; |
2035 | if (S->Flags & SF_OFFSET) { |
2036 | CfgWarning (GetSourcePos (S->LI), |
2037 | "Segment '%s' offset is too small in '%s' by %lu byte%c" , |
2038 | GetString (S->Name), GetString (M->Name), |
2039 | Addr - NewAddr, (Addr - NewAddr == 1) ? ' ' : 's'); |
2040 | } else { |
2041 | CfgWarning (GetSourcePos (S->LI), |
2042 | "Segment '%s' start address is too low in '%s' by %lu byte%c" , |
2043 | GetString (S->Name), GetString (M->Name), |
2044 | Addr - NewAddr, (Addr - NewAddr == 1) ? ' ' : 's'); |
2045 | } |
2046 | } else { |
2047 | Addr = NewAddr; |
2048 | } |
2049 | } |
2050 | } |
2051 | |
2052 | /* Set the start address of this segment, set the readonly flag |
2053 | ** in the segment, and remember if the segment is in a |
2054 | ** relocatable file or not. |
2055 | */ |
2056 | S->Seg->PC = Addr; |
2057 | S->Seg->ReadOnly = (S->Flags & SF_RO) != 0; |
2058 | |
2059 | /* Remember the run memory for this segment, which is also a |
2060 | ** flag that the segment has been placed. |
2061 | */ |
2062 | S->Seg->MemArea = M; |
2063 | |
2064 | } else if (S->Load == M) { |
2065 | /* This is the load memory area; *and*, run and load are |
2066 | ** different (because of the "else" above). Handle alignment. |
2067 | */ |
2068 | if (S->Flags & SF_ALIGN_LOAD) { |
2069 | /* Align the address */ |
2070 | Addr = AlignAddr (Addr, S->LoadAlignment); |
2071 | } |
2072 | } |
2073 | |
2074 | /* If this is the load memory area, and the segment doesn't have a |
2075 | ** fill value defined, use the one from the memory area. |
2076 | */ |
2077 | if (S->Load == M && (S->Flags & SF_FILLVAL) == 0) { |
2078 | S->Seg->FillVal = M->FillVal; |
2079 | } |
2080 | |
2081 | /* Increment the fill level of the memory area; and, check for an |
2082 | ** overflow. |
2083 | */ |
2084 | M->FillLevel = Addr + S->Seg->Size - M->Start; |
2085 | if (M->FillLevel > M->Size && (M->Flags & MF_OVERFLOW) == 0) { |
2086 | ++Overflows; |
2087 | M->Flags |= MF_OVERFLOW; |
2088 | CfgWarning (GetSourcePos (M->LI), |
2089 | "Segment '%s' overflows memory area '%s' by %lu byte%c" , |
2090 | GetString (S->Name), GetString (M->Name), |
2091 | M->FillLevel - M->Size, (M->FillLevel - M->Size == 1) ? ' ' : 's'); |
2092 | } |
2093 | |
2094 | /* If requested, define symbols for the start and size of the |
2095 | ** segment. |
2096 | */ |
2097 | if (S->Flags & SF_DEFINE) { |
2098 | if (S->Run == M && (S->Flags & SF_RUN_DEF) == 0) { |
2099 | CreateRunDefines (S, Addr); |
2100 | } |
2101 | if (S->Load == M && (S->Flags & SF_LOAD_DEF) == 0) { |
2102 | CreateLoadDefines (S, Addr); |
2103 | } |
2104 | } |
2105 | |
2106 | /* Calculate the new address */ |
2107 | Addr += S->Seg->Size; |
2108 | |
2109 | /* If this segment will go out to the file, or its place |
2110 | ** in the file will be filled, then increase the file size. |
2111 | */ |
2112 | if (S->Load == M && |
2113 | ((S->Flags & SF_BSS) == 0 || (M->Flags & MF_FILL) != 0)) { |
2114 | M->F->Size += Addr - StartAddr; |
2115 | } |
2116 | } |
2117 | |
2118 | /* If requested, define symbols for start, size, and offset of the |
2119 | ** memory area |
2120 | */ |
2121 | if (M->Flags & MF_DEFINE) { |
2122 | Export* E; |
2123 | StrBuf Buf = STATIC_STRBUF_INITIALIZER; |
2124 | |
2125 | /* Define the size of the memory area */ |
2126 | SB_Printf (&Buf, "__%s_SIZE__" , GetString (M->Name)); |
2127 | E = CreateConstExport (GetStrBufId (&Buf), M->Size); |
2128 | CollAppend (&E->DefLines, M->LI); |
2129 | |
2130 | /* Define the fill level of the memory area */ |
2131 | SB_Printf (&Buf, "__%s_LAST__" , GetString (M->Name)); |
2132 | E = CreateMemoryExport (GetStrBufId (&Buf), M, M->FillLevel); |
2133 | CollAppend (&E->DefLines, M->LI); |
2134 | |
2135 | /* Define the file offset of the memory area. This isn't of much |
2136 | ** use for relocatable output files. |
2137 | */ |
2138 | if (!M->Relocatable) { |
2139 | SB_Printf (&Buf, "__%s_FILEOFFS__" , GetString (M->Name)); |
2140 | E = CreateConstExport (GetStrBufId (&Buf), M->FileOffs); |
2141 | CollAppend (&E->DefLines, M->LI); |
2142 | } |
2143 | |
2144 | /* Throw away the string buffer */ |
2145 | SB_Done (&Buf); |
2146 | } |
2147 | |
2148 | /* If we didn't have an overflow, and are requested to fill the memory |
2149 | ** area, account for that in the file size. |
2150 | */ |
2151 | if ((M->Flags & MF_OVERFLOW) == 0 && (M->Flags & MF_FILL) != 0) { |
2152 | M->F->Size += (M->Size - M->FillLevel); |
2153 | } |
2154 | } |
2155 | |
2156 | /* Return the number of memory area overflows */ |
2157 | return Overflows; |
2158 | } |
2159 | |
2160 | |
2161 | |
2162 | void CfgWriteTarget (void) |
2163 | /* Write the target file(s) */ |
2164 | { |
2165 | unsigned I; |
2166 | |
2167 | /* Walk through the files list */ |
2168 | for (I = 0; I < CollCount (&FileList); ++I) { |
2169 | |
2170 | /* Get this entry */ |
2171 | File* F = CollAtUnchecked (&FileList, I); |
2172 | |
2173 | /* We don't need to look at files with no memory areas */ |
2174 | if (CollCount (&F->MemoryAreas) > 0) { |
2175 | |
2176 | /* Is there an output file? */ |
2177 | if (SB_GetLen (GetStrBuf (F->Name)) > 0) { |
2178 | |
2179 | /* Assign a proper binary format */ |
2180 | if (F->Format == BINFMT_DEFAULT) { |
2181 | F->Format = DefaultBinFmt; |
2182 | } |
2183 | |
2184 | /* Call the apropriate routine for the binary format */ |
2185 | switch (F->Format) { |
2186 | |
2187 | case BINFMT_BINARY: |
2188 | BinWriteTarget (BinFmtDesc, F); |
2189 | break; |
2190 | |
2191 | case BINFMT_O65: |
2192 | O65WriteTarget (O65FmtDesc, F); |
2193 | break; |
2194 | |
2195 | case BINFMT_ATARIEXE: |
2196 | XexWriteTarget (XexFmtDesc, F); |
2197 | break; |
2198 | |
2199 | default: |
2200 | Internal ("Invalid binary format: %u" , F->Format); |
2201 | |
2202 | } |
2203 | |
2204 | } else { |
2205 | |
2206 | /* No output file. Walk through the list and mark all segments |
2207 | ** loading into these memory areas in this file as dumped. |
2208 | */ |
2209 | unsigned J; |
2210 | for (J = 0; J < CollCount (&F->MemoryAreas); ++J) { |
2211 | |
2212 | unsigned K; |
2213 | |
2214 | /* Get this entry */ |
2215 | MemoryArea* M = CollAtUnchecked (&F->MemoryAreas, J); |
2216 | |
2217 | /* Debugging */ |
2218 | Print (stdout, 2, "Skipping '%s'...\n" , GetString (M->Name)); |
2219 | |
2220 | /* Walk throught the segments */ |
2221 | for (K = 0; K < CollCount (&M->SegList); ++K) { |
2222 | SegDesc* S = CollAtUnchecked (&M->SegList, K); |
2223 | if (S->Load == M) { |
2224 | /* Load area - mark the segment as dumped */ |
2225 | S->Seg->Dumped = 1; |
2226 | } |
2227 | } |
2228 | } |
2229 | } |
2230 | } |
2231 | } |
2232 | } |
2233 | |