]> git.sur5r.net Git - cc65/blob - src/ld65/config.c
Add support for a module id
[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-2002 Ullrich von Bassewitz                                       */
10 /*               Wacholderweg 14                                             */
11 /*               D-70597 Stuttgart                                           */
12 /* EMail:        uz@musoftware.de                                            */
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 "check.h"
43 #include "bitops.h"
44 #include "print.h"
45 #include "xmalloc.h"
46
47 /* ld65 */
48 #include "bin.h"
49 #include "binfmt.h"
50 #include "condes.h"
51 #include "error.h"
52 #include "exports.h"
53 #include "global.h"
54 #include "o65.h"
55 #include "scanner.h"
56 #include "config.h"
57
58
59
60 /*****************************************************************************/
61 /*                                   Data                                    */
62 /*****************************************************************************/
63
64
65
66 /* File list */
67 static File*            FileList;       /* Single linked list */
68 static unsigned         FileCount;      /* Number of entries in the list */
69
70
71
72 /* Memory list */
73 static Memory*          MemoryList;     /* Single linked list */
74 static Memory*          MemoryLast;     /* Last element in list */
75 static unsigned         MemoryCount;    /* Number of entries in the list */
76
77 /* Memory attributes */
78 #define MA_START        0x0001
79 #define MA_SIZE         0x0002
80 #define MA_TYPE         0x0004
81 #define MA_FILE         0x0008
82 #define MA_DEFINE       0x0010
83 #define MA_FILL         0x0020
84 #define MA_FILLVAL      0x0040
85
86
87
88 /* Segment list */
89 SegDesc*                SegDescList;    /* Single linked list */
90 unsigned                SegDescCount;   /* Number of entries in list */
91
92 /* Segment attributes */
93 #define SA_TYPE         0x0001
94 #define SA_LOAD         0x0002
95 #define SA_RUN          0x0004
96 #define SA_ALIGN        0x0008
97 #define SA_DEFINE       0x0010
98 #define SA_OFFSET       0x0020
99 #define SA_START        0x0040
100
101
102
103 /* Descriptor holding information about the binary formats */
104 static BinDesc* BinFmtDesc      = 0;
105 static O65Desc* O65FmtDesc      = 0;
106
107
108
109 /*****************************************************************************/
110 /*                                 Forwards                                  */
111 /*****************************************************************************/
112
113
114
115 static File* NewFile (const char* Name);
116 /* Create a new file descriptor and insert it into the list */
117
118
119
120 /*****************************************************************************/
121 /*                              List management                              */
122 /*****************************************************************************/
123
124
125
126 static File* FindFile (const char* Name)
127 /* Find a file with a given name. */
128 {
129     File* F = FileList;
130     while (F) {
131         if (strcmp (F->Name, Name) == 0) {
132             return F;
133         }
134         F = F->Next;
135     }
136     return 0;
137 }
138
139
140
141 static File* GetFile (const char* Name)
142 /* Get a file entry with the given name. Create a new one if needed. */
143 {
144     File* F = FindFile (Name);
145     if (F == 0) {
146         /* Create a new one */
147         F = NewFile (Name);
148     }
149     return F;
150 }
151
152
153
154 static void FileInsert (File* F, Memory* M)
155 /* Insert the memory area into the files list */
156 {
157     M->F = F;
158     if (F->MemList == 0) {
159         /* First entry */
160         F->MemList = M;
161     } else {
162         F->MemLast->FNext = M;
163     }
164     F->MemLast = M;
165 }
166
167
168
169 static Memory* CfgFindMemory (const char* Name)
170 /* Find the memory are with the given name. Return NULL if not found */
171 {
172     Memory* M = MemoryList;
173     while (M) {
174         if (strcmp (M->Name, Name) == 0) {
175             return M;
176         }
177         M = M->Next;
178     }
179     return 0;
180 }
181
182
183
184 static Memory* CfgGetMemory (const char* Name)
185 /* Find the memory are with the given name. Print an error on an invalid name */
186 {
187     Memory* M = CfgFindMemory (Name);
188     if (M == 0) {
189         CfgError ("Invalid memory area `%s'", Name);
190     }
191     return M;
192 }
193
194
195
196 static SegDesc* CfgFindSegDesc (const char* Name)
197 /* Find the segment descriptor with the given name, return NULL if not found. */
198 {
199     SegDesc* S = SegDescList;
200     while (S) {
201         if (strcmp (S->Name, Name) == 0) {
202             /* Found */
203             return S;
204         }
205         S = S->Next;
206     }
207
208     /* Not found */
209     return 0;
210 }
211
212
213
214 static void SegDescInsert (SegDesc* S)
215 /* Insert a segment descriptor into the list of segment descriptors */
216 {
217     /* Insert the struct into the list */
218     S->Next = SegDescList;
219     SegDescList = S;
220     ++SegDescCount;
221 }
222
223
224
225 static void MemoryInsert (Memory* M, SegDesc* S)
226 /* Insert the segment descriptor into the memory area list */
227 {
228     /* Create a new node for the entry */
229     MemListNode* N = xmalloc (sizeof (MemListNode));
230     N->Seg  = S;
231     N->Next = 0;
232
233     if (M->SegLast == 0) {
234         /* First entry */
235         M->SegList = N;
236     } else {
237         M->SegLast->Next = N;
238     }
239     M->SegLast = N;
240 }
241
242
243
244 /*****************************************************************************/
245 /*                         Constructors/Destructors                          */
246 /*****************************************************************************/
247
248
249
250 static File* NewFile (const char* Name)
251 /* Create a new file descriptor and insert it into the list */
252 {
253     /* Get the length of the name */
254     unsigned Len = strlen (Name);
255
256     /* Allocate memory */
257     File* F = xmalloc (sizeof (File) + Len);
258
259     /* Initialize the fields */
260     F->Flags   = 0;
261     F->Format  = BINFMT_DEFAULT;
262     F->MemList = 0;
263     F->MemLast = 0;
264     memcpy (F->Name, Name, Len);
265     F->Name [Len] = '\0';
266
267     /* Insert the struct into the list */
268     F->Next  = FileList;
269     FileList = F;
270     ++FileCount;
271
272     /* ...and return it */
273     return F;
274 }
275
276
277
278 static Memory* NewMemory (const char* Name)
279 /* Create a new memory section and insert it into the list */
280 {
281     /* Get the length of the name */
282     unsigned Len = strlen (Name);
283
284     /* Check for duplicate names */
285     Memory* M = CfgFindMemory (Name);
286     if (M) {
287         CfgError ("Memory area `%s' defined twice", Name);
288     }
289
290     /* Allocate memory */
291     M = xmalloc (sizeof (Memory) + Len);
292
293     /* Initialize the fields */
294     M->Next      = 0;
295     M->FNext     = 0;
296     M->Attr      = 0;
297     M->Flags     = 0;
298     M->Start     = 0;
299     M->Size      = 0;
300     M->FillLevel = 0;
301     M->FillVal   = 0;
302     M->SegList   = 0;
303     M->SegLast   = 0;
304     M->F         = 0;
305     memcpy (M->Name, Name, Len);
306     M->Name [Len] = '\0';
307
308     /* Insert the struct into the list */
309     if (MemoryLast == 0) {
310         /* First element */
311         MemoryList = M;
312     } else {
313         MemoryLast->Next = M;
314     }
315     MemoryLast = M;
316     ++MemoryCount;
317
318     /* ...and return it */
319     return M;
320 }
321
322
323
324 static SegDesc* NewSegDesc (const char* Name)
325 /* Create a segment descriptor */
326 {
327     Segment* Seg;
328
329     /* Get the length of the name */
330     unsigned Len = strlen (Name);
331
332     /* Check for duplicate names */
333     SegDesc* S = CfgFindSegDesc (Name);
334     if (S) {
335         CfgError ("Segment `%s' defined twice", Name);
336     }
337
338     /* Verify that the given segment does really exist */
339     Seg = SegFind (Name);
340     if (Seg == 0) {
341         CfgWarning ("Segment `%s' does not exist", Name);
342     }
343
344     /* Allocate memory */
345     S = xmalloc (sizeof (SegDesc) + Len);
346
347     /* Initialize the fields */
348     S->Next    = 0;
349     S->Seg     = Seg;
350     S->Attr    = 0;
351     S->Flags   = 0;
352     S->Align   = 0;
353     memcpy (S->Name, Name, Len);
354     S->Name [Len] = '\0';
355
356     /* ...and return it */
357     return S;
358 }
359
360
361
362 static void FreeSegDesc (SegDesc* S)
363 /* Free a segment descriptor */
364 {
365     xfree (S);
366 }
367
368
369
370 /*****************************************************************************/
371 /*                                   Code                                    */
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 ("%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 ("%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         Memory* M = NewMemory (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                     CfgAssureInt ();
443                     M->Start = CfgIVal;
444                     break;
445
446                 case CFGTOK_SIZE:
447                     FlagAttr (&M->Attr, MA_SIZE, "SIZE");
448                     CfgAssureInt ();
449                     M->Size = CfgIVal;
450                     break;
451
452                 case CFGTOK_TYPE:
453                     FlagAttr (&M->Attr, MA_TYPE, "TYPE");
454                     CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type");
455                     if (CfgTok == CFGTOK_RO) {
456                         M->Flags |= MF_RO;
457                     }
458                     break;
459
460                 case CFGTOK_FILE:
461                     FlagAttr (&M->Attr, MA_FILE, "FILE");
462                     CfgAssureStr ();
463                     /* Get the file entry and insert the memory area */
464                     FileInsert (GetFile (CfgSVal), M);
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                     break;
475
476                 case CFGTOK_FILL:
477                     FlagAttr (&M->Attr, MA_FILL, "FILL");
478                     /* Map the token to a boolean */
479                     CfgBoolToken ();
480                     if (CfgTok == CFGTOK_TRUE) {
481                         M->Flags |= MF_FILL;
482                     }
483                     break;
484
485                 case CFGTOK_FILLVAL:
486                     FlagAttr (&M->Attr, MA_FILLVAL, "FILLVAL");
487                     CfgAssureInt ();
488                     CfgRangeCheck (0, 0xFF);
489                     M->FillVal = (unsigned char) CfgIVal;
490                     break;
491
492                 default:
493                     FAIL ("Unexpected attribute token");
494
495             }
496
497             /* Skip the attribute value and an optional comma */
498             CfgNextTok ();
499             CfgOptionalComma ();
500         }
501
502         /* Skip the semicolon */
503         CfgConsumeSemi ();
504
505         /* Check for mandatory parameters */
506         AttrCheck (M->Attr, MA_START, "START");
507         AttrCheck (M->Attr, MA_SIZE, "SIZE");
508
509         /* If we don't have a file name for output given, use the default
510          * file name.
511          */
512         if ((M->Attr & MA_FILE) == 0) {
513             FileInsert (GetFile (OutputName), M);
514         }
515     }
516 }
517
518
519
520 static void ParseFiles (void)
521 /* Parse a FILES section */
522 {
523     static const IdentTok Attributes [] = {
524         {   "FORMAT",   CFGTOK_FORMAT   },
525     };
526     static const IdentTok Formats [] = {
527         {   "O65",      CFGTOK_O65           },
528         {   "BIN",      CFGTOK_BIN      },
529         {   "BINARY",   CFGTOK_BIN      },
530     };
531
532
533     /* Parse all files */
534     while (CfgTok != CFGTOK_RCURLY) {
535
536         File* F;
537
538         /* We expect a string value here */
539         CfgAssureStr ();
540
541         /* Search for the file, it must exist */
542         F = FindFile (CfgSVal);
543         if (F == 0) {
544             CfgError ("No such file: `%s'", CfgSVal);
545         }
546
547         /* Skip the token and the following colon */
548         CfgNextTok ();
549         CfgConsumeColon ();
550
551         /* Read the attributes */
552         while (CfgTok == CFGTOK_IDENT) {
553
554             /* Map the identifier to a token */
555             cfgtok_t AttrTok;
556             CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
557             AttrTok = CfgTok;
558
559             /* An optional assignment follows */
560             CfgNextTok ();
561             CfgOptionalAssign ();
562
563             /* Check which attribute was given */
564             switch (AttrTok) {
565
566                 case CFGTOK_FORMAT:
567                     if (F->Format != BINFMT_DEFAULT) {
568                         /* We've set the format already! */
569                         Error ("Cannot set a file format twice");
570                     }
571                     /* Read the format token */
572                     CfgSpecialToken (Formats, ENTRY_COUNT (Formats), "Format");
573                     switch (CfgTok) {
574
575                         case CFGTOK_BIN:
576                             F->Format = BINFMT_BINARY;
577                             break;
578
579                         case CFGTOK_O65:
580                             F->Format = BINFMT_O65;
581                             break;
582
583                         default:
584                             Error ("Unexpected format token");
585                     }
586                     break;
587
588                 default:
589                     FAIL ("Unexpected attribute token");
590
591             }
592
593             /* Skip the attribute value and an optional comma */
594             CfgNextTok ();
595             CfgOptionalComma ();
596         }
597
598         /* Skip the semicolon */
599         CfgConsumeSemi ();
600
601     }
602 }
603
604
605
606 static void ParseSegments (void)
607 /* Parse a SEGMENTS section */
608 {
609     static const IdentTok Attributes [] = {
610         {   "LOAD",     CFGTOK_LOAD     },
611         {   "RUN",      CFGTOK_RUN      },
612         {   "TYPE",     CFGTOK_TYPE     },
613         {   "ALIGN",    CFGTOK_ALIGN    },
614         {   "DEFINE",   CFGTOK_DEFINE   },
615         {   "OFFSET",   CFGTOK_OFFSET   },
616         {   "START",    CFGTOK_START    },
617     };
618     static const IdentTok Types [] = {
619         {   "RO",       CFGTOK_RO       },
620         {   "RW",       CFGTOK_RW       },
621         {   "BSS",      CFGTOK_BSS      },
622         {   "ZP",       CFGTOK_ZP       },
623         {   "WP",       CFGTOK_WPROT    },
624         {   "WPROT",    CFGTOK_WPROT    },
625     };
626
627     unsigned Count;
628
629     while (CfgTok == CFGTOK_IDENT) {
630
631         SegDesc* S;
632
633         /* Create a new entry on the heap */
634         S = NewSegDesc (CfgSVal);
635
636         /* Skip the name and the following colon */
637         CfgNextTok ();
638         CfgConsumeColon ();
639
640         /* Read the attributes */
641         while (CfgTok == CFGTOK_IDENT) {
642
643             /* Map the identifier to a token */
644             cfgtok_t AttrTok;
645             CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
646             AttrTok = CfgTok;
647
648             /* An optional assignment follows */
649             CfgNextTok ();
650             CfgOptionalAssign ();
651
652             /* Check which attribute was given */
653             switch (AttrTok) {
654
655                 case CFGTOK_LOAD:
656                     FlagAttr (&S->Attr, SA_LOAD, "LOAD");
657                     S->Load = CfgGetMemory (CfgSVal);
658                     break;
659
660                 case CFGTOK_RUN:
661                     FlagAttr (&S->Attr, SA_RUN, "RUN");
662                     S->Run = CfgGetMemory (CfgSVal);
663                     break;
664
665                 case CFGTOK_TYPE:
666                     FlagAttr (&S->Attr, SA_TYPE, "TYPE");
667                     CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type");
668                     switch (CfgTok) {
669                         case CFGTOK_RO:    S->Flags |= SF_RO;               break;
670                         case CFGTOK_RW:    /* Default */                    break;
671                         case CFGTOK_BSS:   S->Flags |= SF_BSS;              break;
672                         case CFGTOK_ZP:    S->Flags |= (SF_BSS | SF_ZP);    break;
673                         case CFGTOK_WPROT: S->Flags |= (SF_RO | SF_WPROT);  break;
674                         default:           Internal ("Unexpected token: %d", CfgTok);
675                     }
676                     break;
677
678                 case CFGTOK_ALIGN:
679                     CfgAssureInt ();
680                     FlagAttr (&S->Attr, SA_ALIGN, "ALIGN");
681                     CfgRangeCheck (1, 0x10000);
682                     S->Align = BitFind (CfgIVal);
683                     if ((0x01UL << S->Align) != CfgIVal) {
684                         CfgError ("Alignment must be a power of 2");
685                     }
686                     S->Flags |= SF_ALIGN;
687                     break;
688
689                 case CFGTOK_DEFINE:
690                     FlagAttr (&S->Attr, SA_DEFINE, "DEFINE");
691                     /* Map the token to a boolean */
692                     CfgBoolToken ();
693                     if (CfgTok == CFGTOK_TRUE) {
694                         S->Flags |= SF_DEFINE;
695                     }
696                     break;
697
698                 case CFGTOK_OFFSET:
699                     CfgAssureInt ();
700                     FlagAttr (&S->Attr, SA_OFFSET, "OFFSET");
701                     CfgRangeCheck (1, 0x1000000);
702                     S->Addr   = CfgIVal;
703                     S->Flags |= SF_OFFSET;
704                     break;
705
706                 case CFGTOK_START:
707                     CfgAssureInt ();
708                     FlagAttr (&S->Attr, SA_START, "START");
709                     CfgRangeCheck (1, 0x1000000);
710                     S->Addr   = CfgIVal;
711                     S->Flags |= SF_START;
712                     break;
713
714                 default:
715                     FAIL ("Unexpected attribute token");
716
717             }
718
719             /* Skip the attribute value and an optional comma */
720             CfgNextTok ();
721             CfgOptionalComma ();
722         }
723
724         /* Skip the semicolon */
725         CfgConsumeSemi ();
726
727         /* Check for mandatory parameters */
728         AttrCheck (S->Attr, SA_LOAD, "LOAD");
729
730         /* Set defaults for stuff not given */
731         if ((S->Attr & SA_RUN) == 0) {
732             S->Attr |= SA_RUN;
733             S->Run = S->Load;
734         } else {
735             /* Both attributes given */
736             S->Flags |= SF_LOAD_AND_RUN;
737         }
738         if ((S->Attr & SA_ALIGN) == 0) {
739             S->Attr |= SA_ALIGN;
740             S->Align = 0;
741         }
742
743         /* If the segment is marked as BSS style, check that there's no
744          * initialized data in the segment.
745          */
746         if ((S->Flags & SF_BSS) != 0 && !IsBSSType (S->Seg)) {
747             Warning ("%s(%u): Segment with type `bss' contains initialized data",
748                      CfgGetName (), CfgErrorLine);
749         }
750
751         /* Don't allow read/write data to be put into a readonly area */
752         if ((S->Flags & SF_RO) == 0) {
753             if (S->Run->Flags & MF_RO) {
754                 CfgError ("Cannot put r/w segment `%s' in r/o memory area `%s'",
755                           S->Name, S->Run->Name);
756             }
757         }
758
759         /* Only one of ALIGN, START and OFFSET may be used */
760         Count = ((S->Flags & SF_ALIGN)  != 0) +
761                 ((S->Flags & SF_OFFSET) != 0) +
762                 ((S->Flags & SF_START)  != 0);
763         if (Count > 1) {
764             CfgError ("Only one of ALIGN, START, OFFSET may be used");
765         }
766
767         /* If this segment does exist in any of the object files, insert the
768          * descriptor into the list of segment descriptors. Otherwise discard
769          * it silently, because the segment pointer in the descriptor is
770          * invalid.
771          */
772         if (S->Seg != 0) {
773             /* Insert the descriptor into the list of all descriptors */
774             SegDescInsert (S);
775             /* Insert the segment into the memory area list */
776             MemoryInsert (S->Run, S);
777             if ((S->Flags & SF_LOAD_AND_RUN) != 0) {
778                 /* We have a separate RUN area given */
779                 MemoryInsert (S->Load, S);
780             }
781         } else {
782             /* Segment does not exist, discard the descriptor */
783             FreeSegDesc (S);
784         }
785     }
786 }
787
788
789
790 static void ParseO65 (void)
791 /* Parse the o65 format section */
792 {
793     static const IdentTok Attributes [] = {
794         {   "EXPORT",   CFGTOK_EXPORT           },
795         {   "IMPORT",   CFGTOK_IMPORT           },
796         {   "TYPE",     CFGTOK_TYPE             },
797         {   "OS",       CFGTOK_OS               },
798         {   "ID",       CFGTOK_ID               },
799         {   "VERSION",  CFGTOK_VERSION          },
800     };
801     static const IdentTok Types [] = {
802         {   "SMALL",    CFGTOK_SMALL            },
803         {   "LARGE",    CFGTOK_LARGE            },
804     };
805     static const IdentTok OperatingSystems [] = {
806         {   "LUNIX",    CFGTOK_LUNIX            },
807         {   "OSA65",    CFGTOK_OSA65            },
808         {   "CC65",     CFGTOK_CC65             },
809     };
810
811     /* Bitmask to remember the attributes we got already */
812     enum {
813         atNone          = 0x0000,
814         atOS            = 0x0001,
815         atOSVersion     = 0x0002,
816         atType          = 0x0004,
817         atImport        = 0x0008,
818         atExport        = 0x0010,
819         atID            = 0x0020,
820         atVersion       = 0x0040
821     };
822     unsigned AttrFlags = atNone;
823
824     /* Remember the attributes read */
825     unsigned OS = 0;            /* Initialize to keep gcc happy */
826     unsigned Version = 0;
827
828     /* Read the attributes */
829     while (CfgTok == CFGTOK_IDENT) {
830
831         /* Map the identifier to a token */
832         cfgtok_t AttrTok;
833         CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
834         AttrTok = CfgTok;
835
836         /* An optional assignment follows */
837         CfgNextTok ();
838         CfgOptionalAssign ();
839
840         /* Check which attribute was given */
841         switch (AttrTok) {
842
843             case CFGTOK_EXPORT:
844                 /* Remember we had this token (maybe more than once) */
845                 AttrFlags |= atExport;
846                 /* We expect an identifier */
847                 CfgAssureIdent ();
848                 /* Check if the export symbol is also defined as an import. */
849                 if (O65GetImport (O65FmtDesc, CfgSVal) != 0) {
850                     CfgError ("Exported symbol `%s' cannot be an import", CfgSVal);
851                 }
852                 /* Check if we have this symbol defined already. The entry
853                  * routine will check this also, but we get a more verbose
854                  * error message when checking it here.
855                  */
856                 if (O65GetExport (O65FmtDesc, CfgSVal) != 0) {
857                     CfgError ("Duplicate exported symbol: `%s'", CfgSVal);
858                 }
859                 /* Insert the symbol into the table */
860                 O65SetExport (O65FmtDesc, CfgSVal);
861                 break;
862
863             case CFGTOK_IMPORT:
864                 /* Remember we had this token (maybe more than once) */
865                 AttrFlags |= atImport;
866                 /* We expect an identifier */
867                 CfgAssureIdent ();
868                 /* Check if the imported symbol is also defined as an export. */
869                 if (O65GetExport (O65FmtDesc, CfgSVal) != 0) {
870                     CfgError ("Imported symbol `%s' cannot be an export", CfgSVal);
871                 }
872                 /* Check if we have this symbol defined already. The entry
873                  * routine will check this also, but we get a more verbose
874                  * error message when checking it here.
875                  */
876                 if (O65GetImport (O65FmtDesc, CfgSVal) != 0) {
877                     CfgError ("Duplicate imported symbol: `%s'", CfgSVal);
878                 }
879                 /* Insert the symbol into the table */
880                 O65SetImport (O65FmtDesc, CfgSVal);
881                 break;
882
883             case CFGTOK_TYPE:
884                 /* Cannot have this attribute twice */
885                 FlagAttr (&AttrFlags, atType, "TYPE");
886                 /* Get the type of the executable */
887                 CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type");
888                 switch (CfgTok) {
889
890                     case CFGTOK_SMALL:
891                         O65SetSmallModel (O65FmtDesc);
892                         break;
893
894                     case CFGTOK_LARGE:
895                         O65SetLargeModel (O65FmtDesc);
896                         break;
897
898                     default:
899                         CfgError ("Unexpected type token");
900                 }
901                 break;
902
903             case CFGTOK_OS:
904                 /* Cannot use this attribute twice */
905                 FlagAttr (&AttrFlags, atOS, "OS");
906                 /* Get the operating system */
907                 CfgSpecialToken (OperatingSystems, ENTRY_COUNT (OperatingSystems), "OS type");
908                 switch (CfgTok) {
909                     case CFGTOK_LUNIX:  OS = O65OS_LUNIX;       break;
910                     case CFGTOK_OSA65:  OS = O65OS_OSA65;       break;
911                     case CFGTOK_CC65:   OS = O65OS_CC65;        break;
912                     default:            CfgError ("Unexpected OS token");
913                 }
914                 break;
915
916             case CFGTOK_ID:
917                 /* Cannot have this attribute twice */
918                 FlagAttr (&AttrFlags, atID, "ID");
919                 /* We're expecting a number in the 0..$FFFF range*/
920                 CfgAssureInt ();
921                 CfgRangeCheck (0, 0xFFFF);
922                 ModuleId = (unsigned) CfgIVal;
923                 break;
924
925             case CFGTOK_VERSION:
926                 /* Cannot have this attribute twice */
927                 FlagAttr (&AttrFlags, atVersion, "VERSION");
928                 /* We're expecting a number in byte range */
929                 CfgAssureInt ();
930                 CfgRangeCheck (0, 0xFF);
931                 Version = (unsigned) CfgIVal;
932                 break;
933
934             default:
935                 FAIL ("Unexpected attribute token");
936
937         }
938
939         /* Skip the attribute value and an optional comma */
940         CfgNextTok ();
941         CfgOptionalComma ();
942     }
943
944     /* Check if we have all mandatory attributes */
945     AttrCheck (AttrFlags, atOS, "OS");
946
947     /* Check for attributes that may not be combined */
948     if (OS == O65OS_CC65) {
949         if ((AttrFlags & (atImport | atExport)) != 0) {
950             CfgError ("OS type CC65 may not have imports or exports");
951         }
952     } else {
953         if (AttrFlags & atID) {
954             CfgError ("Operating system does not support the ID attribute");
955         }
956     }
957
958     /* Set the O65 operating system to use */
959     O65SetOS (O65FmtDesc, OS, Version, ModuleId);
960 }
961
962
963
964 static void ParseFormats (void)
965 /* Parse a target format section */
966 {
967     static const IdentTok Formats [] = {
968         {   "O65",      CFGTOK_O65      },
969         {   "BIN",      CFGTOK_BIN      },
970         {   "BINARY",   CFGTOK_BIN      },
971     };
972
973     while (CfgTok == CFGTOK_IDENT) {
974
975         /* Map the identifier to a token */
976         cfgtok_t FormatTok;
977         CfgSpecialToken (Formats, ENTRY_COUNT (Formats), "Format");
978         FormatTok = CfgTok;
979
980         /* Skip the name and the following colon */
981         CfgNextTok ();
982         CfgConsumeColon ();
983
984         /* Parse the format options */
985         switch (FormatTok) {
986
987             case CFGTOK_O65:
988                 ParseO65 ();
989                 break;
990
991             case CFGTOK_BIN:
992                 /* No attribibutes available */
993                 break;
994
995             default:
996                 Error ("Unexpected format token");
997         }
998
999         /* Skip the semicolon */
1000         CfgConsumeSemi ();
1001     }
1002 }
1003
1004
1005
1006 static void ParseConDes (void)
1007 /* Parse the CONDES feature */
1008 {
1009     static const IdentTok Attributes [] = {
1010         {   "SEGMENT",          CFGTOK_SEGMENT          },
1011         {   "LABEL",            CFGTOK_LABEL            },
1012         {   "COUNT",            CFGTOK_COUNT            },
1013         {   "TYPE",             CFGTOK_TYPE             },
1014         {   "ORDER",            CFGTOK_ORDER            },
1015     };
1016
1017     static const IdentTok Types [] = {
1018         {   "CONSTRUCTOR",      CFGTOK_CONSTRUCTOR      },
1019         {   "DESTRUCTOR",       CFGTOK_DESTRUCTOR       },
1020     };
1021
1022     static const IdentTok Orders [] = {
1023         {   "DECREASING",       CFGTOK_DECREASING       },
1024         {   "INCREASING",       CFGTOK_INCREASING       },
1025     };
1026
1027     /* Attribute values. */
1028     char SegName[sizeof (CfgSVal)];
1029     char Label[sizeof (CfgSVal)];
1030     char Count[sizeof (CfgSVal)];
1031     /* Initialize to avoid gcc warnings: */
1032     int Type = -1;
1033     ConDesOrder Order = cdIncreasing;
1034
1035     /* Bitmask to remember the attributes we got already */
1036     enum {
1037         atNone          = 0x0000,
1038         atSegName       = 0x0001,
1039         atLabel         = 0x0002,
1040         atCount         = 0x0004,
1041         atType          = 0x0008,
1042         atOrder         = 0x0010
1043     };
1044     unsigned AttrFlags = atNone;
1045
1046     /* Parse the attributes */
1047     while (1) {
1048
1049         /* Map the identifier to a token */
1050         cfgtok_t AttrTok;
1051         CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
1052         AttrTok = CfgTok;
1053
1054         /* An optional assignment follows */
1055         CfgNextTok ();
1056         CfgOptionalAssign ();
1057
1058         /* Check which attribute was given */
1059         switch (AttrTok) {
1060
1061             case CFGTOK_SEGMENT:
1062                 /* Don't allow this twice */
1063                 FlagAttr (&AttrFlags, atSegName, "SEGMENT");
1064                 /* We expect an identifier */
1065                 CfgAssureIdent ();
1066                 /* Remember the value for later */
1067                 strcpy (SegName, CfgSVal);
1068                 break;
1069
1070             case CFGTOK_LABEL:
1071                 /* Don't allow this twice */
1072                 FlagAttr (&AttrFlags, atLabel, "LABEL");
1073                 /* We expect an identifier */
1074                 CfgAssureIdent ();
1075                 /* Remember the value for later */
1076                 strcpy (Label, CfgSVal);
1077                 break;
1078
1079             case CFGTOK_COUNT:
1080                 /* Don't allow this twice */
1081                 FlagAttr (&AttrFlags, atCount, "COUNT");
1082                 /* We expect an identifier */
1083                 CfgAssureIdent ();
1084                 /* Remember the value for later */
1085                 strcpy (Count, CfgSVal);
1086                 break;
1087
1088             case CFGTOK_TYPE:
1089                 /* Don't allow this twice */
1090                 FlagAttr (&AttrFlags, atType, "TYPE");
1091                 /* The type may be given as id or numerical */
1092                 if (CfgTok == CFGTOK_INTCON) {
1093                     CfgRangeCheck (CD_TYPE_MIN, CD_TYPE_MAX);
1094                     Type = (int) CfgIVal;
1095                 } else {
1096                     CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type");
1097                     switch (CfgTok) {
1098                         case CFGTOK_CONSTRUCTOR: Type = CD_TYPE_CON;    break;
1099                         case CFGTOK_DESTRUCTOR:  Type = CD_TYPE_DES;    break;
1100                         default: FAIL ("Unexpected type token");
1101                     }
1102                 }
1103                 break;
1104
1105             case CFGTOK_ORDER:
1106                 /* Don't allow this twice */
1107                 FlagAttr (&AttrFlags, atOrder, "ORDER");
1108                 CfgSpecialToken (Orders, ENTRY_COUNT (Orders), "Order");
1109                 switch (CfgTok) {
1110                     case CFGTOK_DECREASING: Order = cdDecreasing;       break;
1111                     case CFGTOK_INCREASING: Order = cdIncreasing;       break;
1112                     default: FAIL ("Unexpected order token");
1113                 }
1114                 break;
1115
1116             default:
1117                 FAIL ("Unexpected attribute token");
1118
1119         }
1120
1121         /* Skip the attribute value */
1122         CfgNextTok ();
1123
1124         /* Semicolon ends the ConDes decl, otherwise accept an optional comma */
1125         if (CfgTok == CFGTOK_SEMI) {
1126             break;
1127         } else if (CfgTok == CFGTOK_COMMA) {
1128             CfgNextTok ();
1129         }
1130     }
1131
1132     /* Check if we have all mandatory attributes */
1133     AttrCheck (AttrFlags, atSegName, "SEGMENT");
1134     AttrCheck (AttrFlags, atLabel, "LABEL");
1135     AttrCheck (AttrFlags, atType, "TYPE");
1136
1137     /* Check if the condes has already attributes defined */
1138     if (ConDesHasSegName(Type) || ConDesHasLabel(Type)) {
1139         CfgError ("CONDES attributes for type %d are already defined", Type);
1140     }
1141
1142     /* Define the attributes */
1143     ConDesSetSegName (Type, SegName);
1144     ConDesSetLabel (Type, Label);
1145     if (AttrFlags & atCount) {
1146         ConDesSetCountSym (Type, Count);
1147     }
1148     if (AttrFlags & atOrder) {
1149         ConDesSetOrder (Type, Order);
1150     }
1151 }
1152
1153
1154
1155 static void ParseFeatures (void)
1156 /* Parse a features section */
1157 {
1158     static const IdentTok Features [] = {
1159         {   "CONDES",   CFGTOK_CONDES   },
1160     };
1161
1162     while (CfgTok == CFGTOK_IDENT) {
1163
1164         /* Map the identifier to a token */
1165         cfgtok_t FeatureTok;
1166         CfgSpecialToken (Features, ENTRY_COUNT (Features), "Feature");
1167         FeatureTok = CfgTok;
1168
1169         /* Skip the name and the following colon */
1170         CfgNextTok ();
1171         CfgConsumeColon ();
1172
1173         /* Parse the format options */
1174         switch (FeatureTok) {
1175
1176             case CFGTOK_CONDES:
1177                 ParseConDes ();
1178                 break;
1179
1180             default:
1181                 Error ("Unexpected feature token");
1182         }
1183
1184         /* Skip the semicolon */
1185         CfgConsumeSemi ();
1186     }
1187 }
1188
1189
1190
1191 static void ParseSymbols (void)
1192 /* Parse a symbols section */
1193 {
1194     while (CfgTok == CFGTOK_IDENT) {
1195
1196         long Val;
1197
1198         /* Remember the name */
1199         char Name [sizeof (CfgSVal)];
1200         strcpy (Name, CfgSVal);
1201         CfgNextTok ();
1202
1203         /* Allow an optional assignment */
1204         CfgOptionalAssign ();
1205
1206         /* Make sure the next token is an integer, read and skip it */
1207         CfgAssureInt ();
1208         Val = CfgIVal;
1209         CfgNextTok ();
1210
1211         /* Generate an export with the given value */
1212         CreateConstExport (Name, Val);
1213
1214         /* Skip the semicolon */
1215         CfgConsumeSemi ();
1216     }
1217 }
1218
1219
1220
1221 static void ParseConfig (void)
1222 /* Parse the config file */
1223 {
1224     static const IdentTok BlockNames [] = {
1225         {   "MEMORY",   CFGTOK_MEMORY   },
1226         {   "FILES",    CFGTOK_FILES    },
1227         {   "SEGMENTS", CFGTOK_SEGMENTS },
1228         {   "FORMATS",  CFGTOK_FORMATS  },
1229         {   "FEATURES", CFGTOK_FEATURES },
1230         {   "SYMBOLS",  CFGTOK_SYMBOLS  },
1231     };
1232     cfgtok_t BlockTok;
1233
1234     do {
1235
1236         /* Read the block ident */
1237         CfgSpecialToken (BlockNames, ENTRY_COUNT (BlockNames), "Block identifier");
1238         BlockTok = CfgTok;
1239         CfgNextTok ();
1240
1241         /* Expected a curly brace */
1242         CfgConsume (CFGTOK_LCURLY, "`{' expected");
1243
1244         /* Read the block */
1245         switch (BlockTok) {
1246
1247             case CFGTOK_MEMORY:
1248                 ParseMemory ();
1249                 break;
1250
1251             case CFGTOK_FILES:
1252                 ParseFiles ();
1253                 break;
1254
1255             case CFGTOK_SEGMENTS:
1256                 ParseSegments ();
1257                 break;
1258
1259             case CFGTOK_FORMATS:
1260                 ParseFormats ();
1261                 break;
1262
1263             case CFGTOK_FEATURES:
1264                 ParseFeatures ();
1265                 break;
1266
1267             case CFGTOK_SYMBOLS:
1268                 ParseSymbols ();
1269                 break;
1270
1271             default:
1272                 FAIL ("Unexpected block token");
1273
1274         }
1275
1276         /* Skip closing brace */
1277         CfgConsume (CFGTOK_RCURLY, "`}' expected");
1278
1279     } while (CfgTok != CFGTOK_EOF);
1280 }
1281
1282
1283
1284 void CfgRead (void)
1285 /* Read the configuration */
1286 {
1287     /* Create the descriptors for the binary formats */
1288     BinFmtDesc = NewBinDesc ();
1289     O65FmtDesc = NewO65Desc ();
1290
1291     /* If we have a config name given, open the file, otherwise we will read
1292      * from a buffer.
1293      */
1294     CfgOpenInput ();
1295
1296     /* Parse the file */
1297     ParseConfig ();
1298
1299     /* Close the input file */
1300     CfgCloseInput ();
1301 }
1302
1303
1304
1305 static void CreateRunDefines (Memory* M, SegDesc* S, unsigned long Addr)
1306 /* Create the defines for a RUN segment */
1307 {
1308     char Buf [256];
1309
1310     sprintf (Buf, "__%s_RUN__", S->Name);
1311     CreateMemExport (Buf, M, Addr - M->Start);
1312     sprintf (Buf, "__%s_SIZE__", S->Name);
1313     CreateConstExport (Buf, S->Seg->Size);
1314     S->Flags |= SF_RUN_DEF;
1315 }
1316
1317
1318
1319 static void CreateLoadDefines (Memory* M, SegDesc* S, unsigned long Addr)
1320 /* Create the defines for a LOAD segment */
1321 {
1322     char Buf [256];
1323
1324     sprintf (Buf, "__%s_LOAD__", S->Name);
1325     CreateMemExport (Buf, M, Addr - M->Start);
1326     S->Flags |= SF_LOAD_DEF;
1327 }
1328
1329
1330
1331 void CfgAssignSegments (void)
1332 /* Assign segments, define linker symbols where requested */
1333 {
1334     /* Walk through each of the memory sections. Add up the sizes and check
1335      * for an overflow of the section. Assign the start addresses of the
1336      * segments while doing this.
1337      */
1338     Memory* M = MemoryList;
1339     while (M) {
1340
1341         /* Get the start address of this memory area */
1342         unsigned long Addr = M->Start;
1343
1344         /* Walk through the segments in this memory area */
1345         MemListNode* N = M->SegList;
1346         while (N) {
1347
1348             /* Get the segment from the node */
1349             SegDesc* S = N->Seg;
1350
1351             /* Handle ALIGN and OFFSET/START */
1352             if (S->Flags & SF_ALIGN) {
1353                 /* Align the address */
1354                 unsigned long Val = (0x01UL << S->Align) - 1;
1355                 Addr = (Addr + Val) & ~Val;
1356             } else if (S->Flags & (SF_OFFSET | SF_START)) {
1357                 /* Give the segment a fixed starting address */
1358                 unsigned long NewAddr = S->Addr;
1359                 if (S->Flags & SF_OFFSET) {
1360                     /* An offset was given, no address, make an address */
1361                     NewAddr += M->Start;
1362                 }
1363                 if (Addr > NewAddr) {
1364                     /* Offset already too large */
1365                     if (S->Flags & SF_OFFSET) {
1366                         Error ("Offset too small in `%s', segment `%s'",
1367                                M->Name, S->Name);
1368                     } else {
1369                         Error ("Start address too low in `%s', segment `%s'",
1370                                M->Name, S->Name);
1371                     }
1372                 }
1373                 Addr = NewAddr;
1374             }
1375
1376             /* If this is the run area, set the start address of this segment */
1377             if (S->Run == M) {
1378                 S->Seg->PC = Addr;
1379             }
1380
1381             /* Increment the fill level of the memory area and check for an
1382              * overflow.
1383              */
1384             M->FillLevel = Addr + S->Seg->Size - M->Start;
1385             if (M->FillLevel > M->Size) {
1386                 Error ("Memory area overflow in `%s', segment `%s' (%lu bytes)",
1387                        M->Name, S->Name, M->FillLevel - M->Size);
1388             }
1389
1390             /* If requested, define symbols for the start and size of the
1391              * segment.
1392              */
1393             if (S->Flags & SF_DEFINE) {
1394                 if ((S->Flags & SF_LOAD_AND_RUN) && S->Run == S->Load) {
1395                     /* RUN and LOAD given and in one memory area.
1396                      * Be careful: We will encounter this code twice, the
1397                      * first time when walking the RUN list, second time when
1398                      * walking the LOAD list. Be sure to define only the
1399                      * relevant symbols on each walk.
1400                      */
1401                     if (S->Load == M) {
1402                         if ((S->Flags & SF_LOAD_DEF) == 0) {
1403                             CreateLoadDefines (M, S, Addr);
1404                         } else {
1405                             CHECK ((S->Flags & SF_RUN_DEF) == 0);
1406                             CreateRunDefines (M, S, Addr);
1407                         }
1408                     }
1409                 } else {
1410                     /* RUN and LOAD in different memory areas, or RUN not
1411                      * given, so RUN defaults to LOAD. In the latter case, we
1412                      * have only one copy of the segment in the area.
1413                      */
1414                     if (S->Run == M) {
1415                         CreateRunDefines (M, S, Addr);
1416                     }
1417                     if (S->Load == M) {
1418                         CreateLoadDefines (M, S, Addr);
1419                     }
1420                 }
1421             }
1422
1423             /* Calculate the new address */
1424             Addr += S->Seg->Size;
1425
1426             /* Next segment */
1427             N = N->Next;
1428         }
1429
1430         /* If requested, define symbols for start and size of the memory area */
1431         if (M->Flags & MF_DEFINE) {
1432             char Buf [256];
1433             sprintf (Buf, "__%s_START__", M->Name);
1434             CreateMemExport (Buf, M, 0);
1435             sprintf (Buf, "__%s_SIZE__", M->Name);
1436             CreateConstExport (Buf, M->Size);
1437             sprintf (Buf, "__%s_LAST__", M->Name);
1438             CreateConstExport (Buf, M->FillLevel);
1439         }
1440
1441         /* Next memory area */
1442         M = M->Next;
1443     }
1444 }
1445
1446
1447
1448 void CfgWriteTarget (void)
1449 /* Write the target file(s) */
1450 {
1451     Memory* M;
1452
1453     /* Walk through the files list */
1454     File* F = FileList;
1455     while (F) {
1456         /* We don't need to look at files with no memory areas */
1457         if (F->MemList) {
1458
1459             /* Is there an output file? */
1460             if (strlen (F->Name) > 0) {
1461
1462                 /* Assign a proper binary format */
1463                 if (F->Format == BINFMT_DEFAULT) {
1464                     F->Format = DefaultBinFmt;
1465                 }
1466
1467                 /* Call the apropriate routine for the binary format */
1468                 switch (F->Format) {
1469
1470                     case BINFMT_BINARY:
1471                         BinWriteTarget (BinFmtDesc, F);
1472                         break;
1473
1474                     case BINFMT_O65:
1475                         O65WriteTarget (O65FmtDesc, F);
1476                         break;
1477
1478                     default:
1479                         Internal ("Invalid binary format: %u", F->Format);
1480
1481                 }
1482
1483             } else {
1484
1485                 /* No output file. Walk through the list and mark all segments
1486                  * loading into these memory areas in this file as dumped.
1487                  */
1488                 M = F->MemList;
1489                 while (M) {
1490
1491                     MemListNode* N;
1492
1493                     /* Debugging */
1494                     Print (stdout, 2, "Skipping `%s'...\n", M->Name);
1495
1496                     /* Walk throught the segments */
1497                     N = M->SegList;
1498                     while (N) {
1499                         if (N->Seg->Load == M) {
1500                             /* Load area - mark the segment as dumped */
1501                             N->Seg->Seg->Dumped = 1;
1502                         }
1503
1504                         /* Next segment node */
1505                         N = N->Next;
1506                     }
1507                     /* Next memory area */
1508                     M = M->FNext;
1509                 }
1510             }
1511         }
1512
1513         /* Next file */
1514         F = F->Next;
1515     }
1516 }
1517
1518
1519