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