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