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