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