1 /*****************************************************************************/
5 /* Target configuration file for the ld65 linker */
9 /* (C) 1998-2010, Ullrich von Bassewitz */
10 /* Roemerstrasse 52 */
11 /* D-70794 Filderstadt */
12 /* EMail: uz@cc65.org */
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. */
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: */
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 */
32 /*****************************************************************************/
64 /*****************************************************************************/
66 /*****************************************************************************/
70 /* Remember which sections we had encountered */
79 } SectionsEncountered = SE_NONE;
84 static Collection FileList = STATIC_COLLECTION_INITIALIZER;
87 static Collection MemoryList = STATIC_COLLECTION_INITIALIZER;
89 /* Memory attributes */
90 #define MA_START 0x0001
91 #define MA_SIZE 0x0002
92 #define MA_TYPE 0x0004
93 #define MA_FILE 0x0008
94 #define MA_DEFINE 0x0010
95 #define MA_FILL 0x0020
96 #define MA_FILLVAL 0x0040
101 SegDesc* SegDescList; /* Single linked list */
102 unsigned SegDescCount; /* Number of entries in list */
104 /* Segment attributes */
105 #define SA_TYPE 0x0001
106 #define SA_LOAD 0x0002
107 #define SA_RUN 0x0004
108 #define SA_ALIGN 0x0008
109 #define SA_ALIGN_LOAD 0x0010
110 #define SA_DEFINE 0x0020
111 #define SA_OFFSET 0x0040
112 #define SA_START 0x0080
113 #define SA_OPTIONAL 0x0100
117 /* Descriptor holding information about the binary formats */
118 static BinDesc* BinFmtDesc = 0;
119 static O65Desc* O65FmtDesc = 0;
123 /*****************************************************************************/
125 /*****************************************************************************/
129 static File* NewFile (unsigned Name);
130 /* Create a new file descriptor and insert it into the list */
134 /*****************************************************************************/
135 /* List management */
136 /*****************************************************************************/
140 static File* FindFile (unsigned Name)
141 /* Find a file with a given name. */
144 for (I = 0; I < CollCount (&FileList); ++I) {
145 File* F = CollAtUnchecked (&FileList, I);
146 if (F->Name == Name) {
155 static File* GetFile (unsigned Name)
156 /* Get a file entry with the given name. Create a new one if needed. */
158 File* F = FindFile (Name);
160 /* Create a new one */
168 static void FileInsert (File* F, Memory* M)
169 /* Insert the memory area into the files list */
172 CollAppend (&F->MemList, M);
177 static Memory* CfgFindMemory (unsigned Name)
178 /* Find the memory are with the given name. Return NULL if not found */
181 for (I = 0; I < CollCount (&MemoryList); ++I) {
182 Memory* M = CollAt (&MemoryList, I);
183 if (M->Name == Name) {
192 static Memory* CfgGetMemory (unsigned Name)
193 /* Find the memory are with the given name. Print an error on an invalid name */
195 Memory* M = CfgFindMemory (Name);
197 CfgError ("Invalid memory area `%s'", GetString (Name));
204 static SegDesc* CfgFindSegDesc (unsigned Name)
205 /* Find the segment descriptor with the given name, return NULL if not found. */
207 SegDesc* S = SegDescList;
209 if (S->Name == Name) {
222 static void SegDescInsert (SegDesc* S)
223 /* Insert a segment descriptor into the list of segment descriptors */
225 /* Insert the struct into the list */
226 S->Next = SegDescList;
233 static void MemoryInsert (Memory* M, SegDesc* S)
234 /* Insert the segment descriptor into the memory area list */
236 /* Create a new node for the entry */
237 MemListNode* N = xmalloc (sizeof (MemListNode));
241 if (M->SegLast == 0) {
245 M->SegLast->Next = N;
252 /*****************************************************************************/
253 /* Constructors/Destructors */
254 /*****************************************************************************/
258 static File* NewFile (unsigned Name)
259 /* Create a new file descriptor and insert it into the list */
261 /* Allocate memory */
262 File* F = xmalloc (sizeof (File));
264 /* Initialize the fields */
267 F->Format = BINFMT_DEFAULT;
268 InitCollection (&F->MemList);
270 /* Insert the struct into the list */
271 CollAppend (&FileList, F);
273 /* ...and return it */
279 static Memory* NewMemory (unsigned Name)
280 /* Create a new memory section and insert it into the list */
282 /* Check for duplicate names */
283 Memory* M = CfgFindMemory (Name);
285 CfgError ("Memory area `%s' defined twice", GetString (Name));
288 /* Allocate memory */
289 M = xmalloc (sizeof (Memory));
291 /* Initialize the fields */
304 /* Insert the struct into the list */
305 CollAppend (&MemoryList, M);
307 /* ...and return it */
313 static SegDesc* NewSegDesc (unsigned Name)
314 /* Create a segment descriptor */
318 /* Check for duplicate names */
319 SegDesc* S = CfgFindSegDesc (Name);
321 CfgError ("Segment `%s' defined twice", GetString (Name));
324 /* Search for the actual segment in the input files. The function may
325 * return NULL (no such segment), this is checked later.
327 Seg = SegFind (Name);
329 /* Allocate memory */
330 S = xmalloc (sizeof (SegDesc));
332 /* Initialize the fields */
340 /* ...and return it */
346 static void FreeSegDesc (SegDesc* S)
347 /* Free a segment descriptor */
354 /*****************************************************************************/
356 /*****************************************************************************/
360 static void FlagAttr (unsigned* Flags, unsigned Mask, const char* Name)
361 /* Check if the item is already defined. Print an error if so. If not, set
362 * the marker that we have a definition now.
366 CfgError ("%s is already defined", Name);
373 static void AttrCheck (unsigned Attr, unsigned Mask, const char* Name)
374 /* Check that a mandatory attribute was given */
376 if ((Attr & Mask) == 0) {
377 CfgError ("%s attribute is missing", Name);
383 static void ParseMemory (void)
384 /* Parse a MEMORY section */
386 static const IdentTok Attributes [] = {
387 { "START", CFGTOK_START },
388 { "SIZE", CFGTOK_SIZE },
389 { "TYPE", CFGTOK_TYPE },
390 { "FILE", CFGTOK_FILE },
391 { "DEFINE", CFGTOK_DEFINE },
392 { "FILL", CFGTOK_FILL },
393 { "FILLVAL", CFGTOK_FILLVAL },
395 static const IdentTok Types [] = {
400 while (CfgTok == CFGTOK_IDENT) {
402 /* Create a new entry on the heap */
403 Memory* M = NewMemory (GetStrBufId (&CfgSVal));
405 /* Skip the name and the following colon */
409 /* Read the attributes */
410 while (CfgTok == CFGTOK_IDENT) {
412 /* Map the identifier to a token */
414 CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
417 /* An optional assignment follows */
419 CfgOptionalAssign ();
421 /* Check which attribute was given */
425 FlagAttr (&M->Attr, MA_START, "START");
426 M->Start = CfgIntExpr ();
430 FlagAttr (&M->Attr, MA_SIZE, "SIZE");
431 M->Size = CfgIntExpr ();
435 FlagAttr (&M->Attr, MA_TYPE, "TYPE");
436 CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type");
437 if (CfgTok == CFGTOK_RO) {
444 FlagAttr (&M->Attr, MA_FILE, "FILE");
446 /* Get the file entry and insert the memory area */
447 FileInsert (GetFile (GetStrBufId (&CfgSVal)), M);
452 FlagAttr (&M->Attr, MA_DEFINE, "DEFINE");
453 /* Map the token to a boolean */
455 if (CfgTok == CFGTOK_TRUE) {
456 M->Flags |= MF_DEFINE;
462 FlagAttr (&M->Attr, MA_FILL, "FILL");
463 /* Map the token to a boolean */
465 if (CfgTok == CFGTOK_TRUE) {
472 FlagAttr (&M->Attr, MA_FILLVAL, "FILLVAL");
473 M->FillVal = (unsigned char) CfgCheckedIntExpr (0, 0xFF);
477 FAIL ("Unexpected attribute token");
481 /* Skip an optional comma */
485 /* Skip the semicolon */
488 /* Check for mandatory parameters */
489 AttrCheck (M->Attr, MA_START, "START");
490 AttrCheck (M->Attr, MA_SIZE, "SIZE");
492 /* If we don't have a file name for output given, use the default
495 if ((M->Attr & MA_FILE) == 0) {
496 FileInsert (GetFile (GetStringId (OutputName)), M);
500 /* Remember we had this section */
501 SectionsEncountered |= SE_MEMORY;
506 static void ParseFiles (void)
507 /* Parse a FILES section */
509 static const IdentTok Attributes [] = {
510 { "FORMAT", CFGTOK_FORMAT },
512 static const IdentTok Formats [] = {
513 { "O65", CFGTOK_O65 },
514 { "BIN", CFGTOK_BIN },
515 { "BINARY", CFGTOK_BIN },
519 /* The MEMORY section must preceed the FILES section */
520 if ((SectionsEncountered & SE_MEMORY) == 0) {
521 CfgError ("MEMORY must precede FILES");
524 /* Parse all files */
525 while (CfgTok != CFGTOK_RCURLY) {
529 /* We expect a string value here */
532 /* Search for the file, it must exist */
533 F = FindFile (GetStrBufId (&CfgSVal));
535 CfgError ("File `%s' not found in MEMORY section",
536 SB_GetConstBuf (&CfgSVal));
539 /* Skip the token and the following colon */
543 /* Read the attributes */
544 while (CfgTok == CFGTOK_IDENT) {
546 /* Map the identifier to a token */
548 CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
551 /* An optional assignment follows */
553 CfgOptionalAssign ();
555 /* Check which attribute was given */
559 if (F->Format != BINFMT_DEFAULT) {
560 /* We've set the format already! */
561 Error ("Cannot set a file format twice");
563 /* Read the format token */
564 CfgSpecialToken (Formats, ENTRY_COUNT (Formats), "Format");
568 F->Format = BINFMT_BINARY;
572 F->Format = BINFMT_O65;
576 Error ("Unexpected format token");
581 FAIL ("Unexpected attribute token");
585 /* Skip the attribute value and an optional comma */
590 /* Skip the semicolon */
595 /* Remember we had this section */
596 SectionsEncountered |= SE_FILES;
601 static void ParseSegments (void)
602 /* Parse a SEGMENTS section */
604 static const IdentTok Attributes [] = {
605 { "ALIGN", CFGTOK_ALIGN },
606 { "ALIGN_LOAD", CFGTOK_ALIGN_LOAD },
607 { "DEFINE", CFGTOK_DEFINE },
608 { "LOAD", CFGTOK_LOAD },
609 { "OFFSET", CFGTOK_OFFSET },
610 { "OPTIONAL", CFGTOK_OPTIONAL },
611 { "RUN", CFGTOK_RUN },
612 { "START", CFGTOK_START },
613 { "TYPE", CFGTOK_TYPE },
615 static const IdentTok Types [] = {
618 { "BSS", CFGTOK_BSS },
625 /* The MEMORY section must preceed the SEGMENTS section */
626 if ((SectionsEncountered & SE_MEMORY) == 0) {
627 CfgError ("MEMORY must precede SEGMENTS");
630 while (CfgTok == CFGTOK_IDENT) {
634 /* Create a new entry on the heap */
635 S = NewSegDesc (GetStrBufId (&CfgSVal));
637 /* Skip the name and the following colon */
641 /* Read the attributes */
642 while (CfgTok == CFGTOK_IDENT) {
644 /* Map the identifier to a token */
646 CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
649 /* An optional assignment follows */
651 CfgOptionalAssign ();
653 /* Check which attribute was given */
657 FlagAttr (&S->Attr, SA_ALIGN, "ALIGN");
658 Val = CfgCheckedIntExpr (1, 0x10000);
659 S->Align = BitFind (Val);
660 if ((0x01L << S->Align) != Val) {
661 CfgError ("Alignment must be a power of 2");
663 S->Flags |= SF_ALIGN;
666 case CFGTOK_ALIGN_LOAD:
667 FlagAttr (&S->Attr, SA_ALIGN_LOAD, "ALIGN_LOAD");
668 Val = CfgCheckedIntExpr (1, 0x10000);
669 S->AlignLoad = BitFind (Val);
670 if ((0x01L << S->AlignLoad) != Val) {
671 CfgError ("Alignment must be a power of 2");
673 S->Flags |= SF_ALIGN_LOAD;
677 FlagAttr (&S->Attr, SA_DEFINE, "DEFINE");
678 /* Map the token to a boolean */
680 if (CfgTok == CFGTOK_TRUE) {
681 S->Flags |= SF_DEFINE;
687 FlagAttr (&S->Attr, SA_LOAD, "LOAD");
688 S->Load = CfgGetMemory (GetStrBufId (&CfgSVal));
693 FlagAttr (&S->Attr, SA_OFFSET, "OFFSET");
694 S->Addr = CfgCheckedIntExpr (1, 0x1000000);
695 S->Flags |= SF_OFFSET;
698 case CFGTOK_OPTIONAL:
699 FlagAttr (&S->Attr, SA_OPTIONAL, "OPTIONAL");
701 if (CfgTok == CFGTOK_TRUE) {
702 S->Flags |= SF_OPTIONAL;
708 FlagAttr (&S->Attr, SA_RUN, "RUN");
709 S->Run = CfgGetMemory (GetStrBufId (&CfgSVal));
714 FlagAttr (&S->Attr, SA_START, "START");
715 S->Addr = CfgCheckedIntExpr (1, 0x1000000);
716 S->Flags |= SF_START;
720 FlagAttr (&S->Attr, SA_TYPE, "TYPE");
721 CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type");
723 case CFGTOK_RO: S->Flags |= SF_RO; break;
724 case CFGTOK_RW: /* Default */ break;
725 case CFGTOK_BSS: S->Flags |= SF_BSS; break;
726 case CFGTOK_ZP: S->Flags |= (SF_BSS | SF_ZP); break;
727 default: Internal ("Unexpected token: %d", CfgTok);
733 FAIL ("Unexpected attribute token");
737 /* Skip an optional comma */
741 /* Check for mandatory parameters */
742 AttrCheck (S->Attr, SA_LOAD, "LOAD");
744 /* Set defaults for stuff not given */
745 if ((S->Attr & SA_RUN) == 0) {
750 /* If the segment is marked as BSS style, and if the segment exists
751 * in any of the object file, check that there's no initialized data
754 if ((S->Flags & SF_BSS) != 0 && S->Seg != 0 && !IsBSSType (S->Seg)) {
755 Warning ("%s(%u): Segment with type `bss' contains initialized data",
756 CfgGetName (), CfgErrorLine);
759 /* An attribute of ALIGN_LOAD doesn't make sense if there are no
760 * separate run and load memory areas.
762 if ((S->Flags & SF_ALIGN_LOAD) != 0 && (S->Load == S->Run)) {
763 Warning ("%s(%u): ALIGN_LOAD attribute specified, but no separate "
764 "LOAD and RUN memory areas assigned",
765 CfgGetName (), CfgErrorLine);
766 /* Remove the flag */
767 S->Flags &= ~SF_ALIGN_LOAD;
770 /* If the segment is marked as BSS style, it may not have separate
771 * load and run memory areas, because it's is never written to disk.
773 if ((S->Flags & SF_BSS) != 0 && (S->Load != S->Run)) {
774 Warning ("%s(%u): Segment with type `bss' has both LOAD and RUN "
775 "memory areas assigned", CfgGetName (), CfgErrorLine);
778 /* Don't allow read/write data to be put into a readonly area */
779 if ((S->Flags & SF_RO) == 0) {
780 if (S->Run->Flags & MF_RO) {
781 CfgError ("Cannot put r/w segment `%s' in r/o memory area `%s'",
782 GetString (S->Name), GetString (S->Run->Name));
786 /* Only one of ALIGN, START and OFFSET may be used */
787 Count = ((S->Flags & SF_ALIGN) != 0) +
788 ((S->Flags & SF_OFFSET) != 0) +
789 ((S->Flags & SF_START) != 0);
791 CfgError ("Only one of ALIGN, START, OFFSET may be used");
794 /* If this segment does exist in any of the object files, insert the
795 * descriptor into the list of segment descriptors. Otherwise print a
796 * warning and discard it, because the segment pointer in the
797 * descriptor is invalid.
800 /* Insert the descriptor into the list of all descriptors */
802 /* Insert the segment into the memory area list */
803 MemoryInsert (S->Run, S);
804 if (S->Load != S->Run) {
805 /* We have separate RUN and LOAD areas */
806 MemoryInsert (S->Load, S);
809 /* Print a warning if the segment is not optional */
810 if ((S->Flags & SF_OPTIONAL) == 0) {
811 CfgWarning ("Segment `%s' does not exist", GetString (S->Name));
813 /* Discard the descriptor */
817 /* Skip the semicolon */
821 /* Remember we had this section */
822 SectionsEncountered |= SE_SEGMENTS;
827 static void ParseO65 (void)
828 /* Parse the o65 format section */
830 static const IdentTok Attributes [] = {
831 { "EXPORT", CFGTOK_EXPORT },
832 { "IMPORT", CFGTOK_IMPORT },
833 { "TYPE", CFGTOK_TYPE },
836 { "VERSION", CFGTOK_VERSION },
838 static const IdentTok Types [] = {
839 { "SMALL", CFGTOK_SMALL },
840 { "LARGE", CFGTOK_LARGE },
842 static const IdentTok OperatingSystems [] = {
843 { "LUNIX", CFGTOK_LUNIX },
844 { "OSA65", CFGTOK_OSA65 },
845 { "CC65", CFGTOK_CC65 },
846 { "OPENCBM", CFGTOK_OPENCBM },
849 /* Bitmask to remember the attributes we got already */
853 atOSVersion = 0x0002,
860 unsigned AttrFlags = atNone;
862 /* Remember the attributes read */
864 unsigned OS = 0; /* Initialize to keep gcc happy */
865 unsigned Version = 0;
867 /* Read the attributes */
868 while (CfgTok == CFGTOK_IDENT) {
870 /* Map the identifier to a token */
872 CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
875 /* An optional assignment follows */
877 CfgOptionalAssign ();
879 /* Check which attribute was given */
883 /* Remember we had this token (maybe more than once) */
884 AttrFlags |= atExport;
885 /* We expect an identifier */
887 /* Convert the string into a string index */
888 CfgSValId = GetStrBufId (&CfgSVal);
889 /* Check if the export symbol is also defined as an import. */
890 if (O65GetImport (O65FmtDesc, CfgSValId) != 0) {
891 CfgError ("Exported symbol `%s' cannot be an import",
892 SB_GetConstBuf (&CfgSVal));
894 /* Check if we have this symbol defined already. The entry
895 * routine will check this also, but we get a more verbose
896 * error message when checking it here.
898 if (O65GetExport (O65FmtDesc, CfgSValId) != 0) {
899 CfgError ("Duplicate exported symbol: `%s'",
900 SB_GetConstBuf (&CfgSVal));
902 /* Insert the symbol into the table */
903 O65SetExport (O65FmtDesc, CfgSValId);
904 /* Eat the identifier token */
909 /* Remember we had this token (maybe more than once) */
910 AttrFlags |= atImport;
911 /* We expect an identifier */
913 /* Convert the string into a string index */
914 CfgSValId = GetStrBufId (&CfgSVal);
915 /* Check if the imported symbol is also defined as an export. */
916 if (O65GetExport (O65FmtDesc, CfgSValId) != 0) {
917 CfgError ("Imported symbol `%s' cannot be an export",
918 SB_GetConstBuf (&CfgSVal));
920 /* Check if we have this symbol defined already. The entry
921 * routine will check this also, but we get a more verbose
922 * error message when checking it here.
924 if (O65GetImport (O65FmtDesc, CfgSValId) != 0) {
925 CfgError ("Duplicate imported symbol: `%s'",
926 SB_GetConstBuf (&CfgSVal));
928 /* Insert the symbol into the table */
929 O65SetImport (O65FmtDesc, CfgSValId);
930 /* Eat the identifier token */
935 /* Cannot have this attribute twice */
936 FlagAttr (&AttrFlags, atType, "TYPE");
937 /* Get the type of the executable */
938 CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type");
942 O65SetSmallModel (O65FmtDesc);
946 O65SetLargeModel (O65FmtDesc);
950 CfgError ("Unexpected type token");
952 /* Eat the attribute token */
957 /* Cannot use this attribute twice */
958 FlagAttr (&AttrFlags, atOS, "OS");
959 /* Get the operating system. It may be specified as name or
960 * as a number in the range 1..255.
962 if (CfgTok == CFGTOK_INTCON) {
963 CfgRangeCheck (O65OS_MIN, O65OS_MAX);
964 OS = (unsigned) CfgIVal;
966 CfgSpecialToken (OperatingSystems, ENTRY_COUNT (OperatingSystems), "OS type");
968 case CFGTOK_LUNIX: OS = O65OS_LUNIX; break;
969 case CFGTOK_OSA65: OS = O65OS_OSA65; break;
970 case CFGTOK_CC65: OS = O65OS_CC65; break;
971 case CFGTOK_OPENCBM: OS = O65OS_OPENCBM; break;
972 default: CfgError ("Unexpected OS token");
979 /* Cannot have this attribute twice */
980 FlagAttr (&AttrFlags, atID, "ID");
981 /* We're expecting a number in the 0..$FFFF range*/
982 ModuleId = (unsigned) CfgCheckedIntExpr (0, 0xFFFF);
986 /* Cannot have this attribute twice */
987 FlagAttr (&AttrFlags, atVersion, "VERSION");
988 /* We're expecting a number in byte range */
989 Version = (unsigned) CfgCheckedIntExpr (0, 0xFF);
993 FAIL ("Unexpected attribute token");
997 /* Skip an optional comma */
1001 /* Check if we have all mandatory attributes */
1002 AttrCheck (AttrFlags, atOS, "OS");
1004 /* Check for attributes that may not be combined */
1005 if (OS == O65OS_CC65) {
1006 if ((AttrFlags & (atImport | atExport)) != 0 && ModuleId < 0x8000) {
1007 CfgError ("OS type CC65 may not have imports or exports for ids < $8000");
1010 if (AttrFlags & atID) {
1011 CfgError ("Operating system does not support the ID attribute");
1015 /* Set the O65 operating system to use */
1016 O65SetOS (O65FmtDesc, OS, Version, ModuleId);
1021 static void ParseFormats (void)
1022 /* Parse a target format section */
1024 static const IdentTok Formats [] = {
1025 { "O65", CFGTOK_O65 },
1026 { "BIN", CFGTOK_BIN },
1027 { "BINARY", CFGTOK_BIN },
1030 while (CfgTok == CFGTOK_IDENT) {
1032 /* Map the identifier to a token */
1034 CfgSpecialToken (Formats, ENTRY_COUNT (Formats), "Format");
1037 /* Skip the name and the following colon */
1041 /* Parse the format options */
1042 switch (FormatTok) {
1049 /* No attribibutes available */
1053 Error ("Unexpected format token");
1056 /* Skip the semicolon */
1061 /* Remember we had this section */
1062 SectionsEncountered |= SE_FORMATS;
1067 static void ParseConDes (void)
1068 /* Parse the CONDES feature */
1070 static const IdentTok Attributes [] = {
1071 { "SEGMENT", CFGTOK_SEGMENT },
1072 { "LABEL", CFGTOK_LABEL },
1073 { "COUNT", CFGTOK_COUNT },
1074 { "TYPE", CFGTOK_TYPE },
1075 { "ORDER", CFGTOK_ORDER },
1078 static const IdentTok Types [] = {
1079 { "CONSTRUCTOR", CFGTOK_CONSTRUCTOR },
1080 { "DESTRUCTOR", CFGTOK_DESTRUCTOR },
1081 { "INTERRUPTOR", CFGTOK_INTERRUPTOR },
1084 static const IdentTok Orders [] = {
1085 { "DECREASING", CFGTOK_DECREASING },
1086 { "INCREASING", CFGTOK_INCREASING },
1089 /* Attribute values. */
1090 unsigned SegName = INVALID_STRING_ID;
1091 unsigned Label = INVALID_STRING_ID;
1092 unsigned Count = INVALID_STRING_ID;
1093 /* Initialize to avoid gcc warnings: */
1095 ConDesOrder Order = cdIncreasing;
1097 /* Bitmask to remember the attributes we got already */
1106 unsigned AttrFlags = atNone;
1108 /* Parse the attributes */
1111 /* Map the identifier to a token */
1113 CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
1116 /* An optional assignment follows */
1118 CfgOptionalAssign ();
1120 /* Check which attribute was given */
1123 case CFGTOK_SEGMENT:
1124 /* Don't allow this twice */
1125 FlagAttr (&AttrFlags, atSegName, "SEGMENT");
1126 /* We expect an identifier */
1128 /* Remember the value for later */
1129 SegName = GetStrBufId (&CfgSVal);
1133 /* Don't allow this twice */
1134 FlagAttr (&AttrFlags, atLabel, "LABEL");
1135 /* We expect an identifier */
1137 /* Remember the value for later */
1138 Label = GetStrBufId (&CfgSVal);
1142 /* Don't allow this twice */
1143 FlagAttr (&AttrFlags, atCount, "COUNT");
1144 /* We expect an identifier */
1146 /* Remember the value for later */
1147 Count = GetStrBufId (&CfgSVal);
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;
1158 CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type");
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");
1169 /* Don't allow this twice */
1170 FlagAttr (&AttrFlags, atOrder, "ORDER");
1171 CfgSpecialToken (Orders, ENTRY_COUNT (Orders), "Order");
1173 case CFGTOK_DECREASING: Order = cdDecreasing; break;
1174 case CFGTOK_INCREASING: Order = cdIncreasing; break;
1175 default: FAIL ("Unexpected order token");
1180 FAIL ("Unexpected attribute token");
1184 /* Skip the attribute value */
1187 /* Semicolon ends the ConDes decl, otherwise accept an optional comma */
1188 if (CfgTok == CFGTOK_SEMI) {
1190 } else if (CfgTok == CFGTOK_COMMA) {
1195 /* Check if we have all mandatory attributes */
1196 AttrCheck (AttrFlags, atSegName, "SEGMENT");
1197 AttrCheck (AttrFlags, atLabel, "LABEL");
1198 AttrCheck (AttrFlags, atType, "TYPE");
1200 /* Check if the condes has already attributes defined */
1201 if (ConDesHasSegName(Type) || ConDesHasLabel(Type)) {
1202 CfgError ("CONDES attributes for type %d are already defined", Type);
1205 /* Define the attributes */
1206 ConDesSetSegName (Type, SegName);
1207 ConDesSetLabel (Type, Label);
1208 if (AttrFlags & atCount) {
1209 ConDesSetCountSym (Type, Count);
1211 if (AttrFlags & atOrder) {
1212 ConDesSetOrder (Type, Order);
1218 static void ParseStartAddress (void)
1219 /* Parse the STARTADDRESS feature */
1221 static const IdentTok Attributes [] = {
1222 { "DEFAULT", CFGTOK_DEFAULT },
1226 /* Attribute values. */
1227 unsigned long DefStartAddr = 0;
1229 /* Bitmask to remember the attributes we got already */
1234 unsigned AttrFlags = atNone;
1236 /* Parse the attributes */
1239 /* Map the identifier to a token */
1241 CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
1244 /* An optional assignment follows */
1246 CfgOptionalAssign ();
1248 /* Check which attribute was given */
1251 case CFGTOK_DEFAULT:
1252 /* Don't allow this twice */
1253 FlagAttr (&AttrFlags, atDefault, "DEFAULT");
1254 /* We expect a numeric expression */
1255 DefStartAddr = CfgCheckedIntExpr (0, 0xFFFFFF);
1259 FAIL ("Unexpected attribute token");
1263 /* Semicolon ends the ConDes decl, otherwise accept an optional comma */
1264 if (CfgTok == CFGTOK_SEMI) {
1266 } else if (CfgTok == CFGTOK_COMMA) {
1271 /* Check if we have all mandatory attributes */
1272 AttrCheck (AttrFlags, atDefault, "DEFAULT");
1274 /* If no start address was given on the command line, use the one given
1277 if (!HaveStartAddr) {
1278 StartAddr = DefStartAddr;
1284 static void ParseFeatures (void)
1285 /* Parse a features section */
1287 static const IdentTok Features [] = {
1288 { "CONDES", CFGTOK_CONDES },
1289 { "STARTADDRESS", CFGTOK_STARTADDRESS },
1292 while (CfgTok == CFGTOK_IDENT) {
1294 /* Map the identifier to a token */
1295 cfgtok_t FeatureTok;
1296 CfgSpecialToken (Features, ENTRY_COUNT (Features), "Feature");
1297 FeatureTok = CfgTok;
1299 /* Skip the name and the following colon */
1303 /* Parse the format options */
1304 switch (FeatureTok) {
1310 case CFGTOK_STARTADDRESS:
1311 ParseStartAddress ();
1316 FAIL ("Unexpected feature token");
1319 /* Skip the semicolon */
1323 /* Remember we had this section */
1324 SectionsEncountered |= SE_FEATURES;
1329 static void ParseSymbols (void)
1330 /* Parse a symbols section */
1332 static const IdentTok Attributes[] = {
1333 { "VALUE", CFGTOK_VALUE },
1334 { "WEAK", CFGTOK_WEAK },
1337 while (CfgTok == CFGTOK_IDENT) {
1343 /* Remember the name */
1344 unsigned Name = GetStrBufId (&CfgSVal);
1347 /* Support both, old and new syntax here. New syntax is a colon
1348 * followed by an attribute list, old syntax is an optional equal
1349 * sign plus a value.
1351 if (CfgTok != CFGTOK_COLON) {
1355 /* Allow an optional assignment */
1356 CfgOptionalAssign ();
1358 /* Make sure the next token is an integer expression, read and
1361 Val = CfgIntExpr ();
1365 /* Bitmask to remember the attributes we got already */
1371 unsigned AttrFlags = atNone;
1374 /* New syntax - skip the colon */
1377 /* Parse the attributes */
1380 /* Map the identifier to a token */
1382 CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
1385 /* Skip the attribute name */
1388 /* An optional assignment follows */
1389 CfgOptionalAssign ();
1391 /* Check which attribute was given */
1395 /* Don't allow this twice */
1396 FlagAttr (&AttrFlags, atValue, "VALUE");
1397 /* We expect a numeric expression */
1398 Val = CfgIntExpr ();
1402 /* Don't allow this twice */
1403 FlagAttr (&AttrFlags, atWeak, "WEAK");
1405 Weak = (CfgTok == CFGTOK_TRUE);
1410 FAIL ("Unexpected attribute token");
1414 /* Semicolon ends the decl, otherwise accept an optional comma */
1415 if (CfgTok == CFGTOK_SEMI) {
1417 } else if (CfgTok == CFGTOK_COMMA) {
1422 /* Check if we have all mandatory attributes */
1423 AttrCheck (AttrFlags, atValue, "VALUE");
1425 /* Weak is optional, the default are non weak symbols */
1426 if ((AttrFlags & atWeak) == 0) {
1432 /* Check if the symbol is already defined */
1433 if ((E = FindExport (Name)) != 0 && !IsUnresolvedExport (E)) {
1434 /* If the symbol is not marked as weak, this is an error.
1435 * Otherwise ignore the symbol from the config.
1438 CfgError ("Symbol `%s' is already defined", GetString (Name));
1441 /* The symbol is undefined, generate an export */
1442 CreateConstExport (Name, Val);
1445 /* Skip the semicolon */
1449 /* Remember we had this section */
1450 SectionsEncountered |= SE_SYMBOLS;
1455 static void ParseConfig (void)
1456 /* Parse the config file */
1458 static const IdentTok BlockNames [] = {
1459 { "MEMORY", CFGTOK_MEMORY },
1460 { "FILES", CFGTOK_FILES },
1461 { "SEGMENTS", CFGTOK_SEGMENTS },
1462 { "FORMATS", CFGTOK_FORMATS },
1463 { "FEATURES", CFGTOK_FEATURES },
1464 { "SYMBOLS", CFGTOK_SYMBOLS },
1470 /* Read the block ident */
1471 CfgSpecialToken (BlockNames, ENTRY_COUNT (BlockNames), "Block identifier");
1475 /* Expected a curly brace */
1476 CfgConsume (CFGTOK_LCURLY, "`{' expected");
1478 /* Read the block */
1489 case CFGTOK_SEGMENTS:
1493 case CFGTOK_FORMATS:
1497 case CFGTOK_FEATURES:
1501 case CFGTOK_SYMBOLS:
1506 FAIL ("Unexpected block token");
1510 /* Skip closing brace */
1511 CfgConsume (CFGTOK_RCURLY, "`}' expected");
1513 } while (CfgTok != CFGTOK_EOF);
1519 /* Read the configuration */
1521 /* Create the descriptors for the binary formats */
1522 BinFmtDesc = NewBinDesc ();
1523 O65FmtDesc = NewO65Desc ();
1525 /* If we have a config name given, open the file, otherwise we will read
1530 /* Parse the file */
1533 /* Close the input file */
1539 static void CreateRunDefines (SegDesc* S, unsigned long SegAddr)
1540 /* Create the defines for a RUN segment */
1542 StrBuf Buf = STATIC_STRBUF_INITIALIZER;
1544 SB_Printf (&Buf, "__%s_RUN__", GetString (S->Name));
1545 CreateMemoryExport (GetStrBufId (&Buf), S->Run, SegAddr - S->Run->Start);
1546 SB_Printf (&Buf, "__%s_SIZE__", GetString (S->Name));
1547 CreateConstExport (GetStrBufId (&Buf), S->Seg->Size);
1548 S->Flags |= SF_RUN_DEF;
1554 static void CreateLoadDefines (SegDesc* S, unsigned long SegAddr)
1555 /* Create the defines for a LOAD segment */
1557 StrBuf Buf = STATIC_STRBUF_INITIALIZER;
1559 SB_Printf (&Buf, "__%s_LOAD__", GetString (S->Name));
1560 CreateMemoryExport (GetStrBufId (&Buf), S->Load, SegAddr - S->Load->Start);
1561 S->Flags |= SF_LOAD_DEF;
1567 unsigned CfgAssignSegments (void)
1568 /* Assign segments, define linker symbols where requested. The function will
1569 * return the number of memory area overflows (so zero means anything went ok).
1570 * In case of overflows, a short mapfile can be generated later, to ease the
1571 * task of rearranging segments for the user.
1574 unsigned Overflows = 0;
1576 /* Walk through each of the memory sections. Add up the sizes and check
1577 * for an overflow of the section. Assign the start addresses of the
1578 * segments while doing this.
1581 for (I = 0; I < CollCount (&MemoryList); ++I) {
1585 /* Get this entry */
1586 Memory* M = CollAtUnchecked (&MemoryList, I);
1588 /* Get the start address of this memory area */
1589 unsigned long Addr = M->Start;
1591 /* Remember if this is a relocatable memory area */
1592 M->Relocatable = RelocatableBinFmt (M->F->Format);
1594 /* Walk through the segments in this memory area */
1598 /* Get the segment from the node */
1599 SegDesc* S = N->Seg;
1601 /* Some actions depend on wether this is the load or run memory
1606 /* This is the run (and maybe load) memory area. Handle
1607 * alignment and explict start address and offset.
1609 if (S->Flags & SF_ALIGN) {
1610 /* Align the address */
1611 unsigned long Val = (0x01UL << S->Align) - 1;
1612 Addr = (Addr + Val) & ~Val;
1613 } else if (S->Flags & (SF_OFFSET | SF_START)) {
1614 /* Give the segment a fixed starting address */
1615 unsigned long NewAddr = S->Addr;
1616 if (S->Flags & SF_OFFSET) {
1617 /* An offset was given, no address, make an address */
1618 NewAddr += M->Start;
1620 if (Addr > NewAddr) {
1621 /* Offset already too large */
1622 if (S->Flags & SF_OFFSET) {
1623 Error ("Offset too small in `%s', segment `%s'",
1624 GetString (M->Name), GetString (S->Name));
1626 Error ("Start address too low in `%s', segment `%s'",
1627 GetString (M->Name), GetString (S->Name));
1633 /* Set the start address of this segment, set the readonly flag
1634 * in the segment and and remember if the segment is in a
1635 * relocatable file or not.
1638 S->Seg->ReadOnly = (S->Flags & SF_RO) != 0;
1639 S->Seg->Relocatable = M->Relocatable;
1641 } else if (S->Load == M) {
1643 /* This is the load memory area, *and* run and load are
1644 * different (because of the "else" above). Handle alignment.
1646 if (S->Flags & SF_ALIGN_LOAD) {
1647 /* Align the address */
1648 unsigned long Val = (0x01UL << S->AlignLoad) - 1;
1649 Addr = (Addr + Val) & ~Val;
1654 /* Increment the fill level of the memory area and check for an
1657 M->FillLevel = Addr + S->Seg->Size - M->Start;
1658 if (M->FillLevel > M->Size && (M->Flags & MF_OVERFLOW) == 0) {
1660 M->Flags |= MF_OVERFLOW;
1661 Warning ("Memory area overflow in `%s', segment `%s' (%lu bytes)",
1662 GetString (M->Name), GetString (S->Name),
1663 M->FillLevel - M->Size);
1666 /* If requested, define symbols for the start and size of the
1669 if (S->Flags & SF_DEFINE) {
1670 if (S->Run == M && (S->Flags & SF_RUN_DEF) == 0) {
1671 CreateRunDefines (S, Addr);
1673 if (S->Load == M && (S->Flags & SF_LOAD_DEF) == 0) {
1674 CreateLoadDefines (S, Addr);
1678 /* Calculate the new address */
1679 Addr += S->Seg->Size;
1685 /* If requested, define symbols for start and size of the memory area */
1686 if (M->Flags & MF_DEFINE) {
1687 StrBuf Buf = STATIC_STRBUF_INITIALIZER;
1688 SB_Printf (&Buf, "__%s_START__", GetString (M->Name));
1689 CreateMemoryExport (GetStrBufId (&Buf), M, 0);
1690 SB_Printf (&Buf, "__%s_SIZE__", GetString (M->Name));
1691 CreateConstExport (GetStrBufId (&Buf), M->Size);
1692 SB_Printf (&Buf, "__%s_LAST__", GetString (M->Name));
1693 CreateMemoryExport (GetStrBufId (&Buf), M, M->FillLevel);
1699 /* Return the number of memory area overflows */
1705 void CfgWriteTarget (void)
1706 /* Write the target file(s) */
1710 /* Walk through the files list */
1711 for (I = 0; I < CollCount (&FileList); ++I) {
1713 /* Get this entry */
1714 File* F = CollAtUnchecked (&FileList, I);
1716 /* We don't need to look at files with no memory areas */
1717 if (CollCount (&F->MemList) > 0) {
1719 /* Is there an output file? */
1720 if (SB_GetLen (GetStrBuf (F->Name)) > 0) {
1722 /* Assign a proper binary format */
1723 if (F->Format == BINFMT_DEFAULT) {
1724 F->Format = DefaultBinFmt;
1727 /* Call the apropriate routine for the binary format */
1728 switch (F->Format) {
1731 BinWriteTarget (BinFmtDesc, F);
1735 O65WriteTarget (O65FmtDesc, F);
1739 Internal ("Invalid binary format: %u", F->Format);
1745 /* No output file. Walk through the list and mark all segments
1746 * loading into these memory areas in this file as dumped.
1749 for (J = 0; J < CollCount (&F->MemList); ++J) {
1753 /* Get this entry */
1754 Memory* M = CollAtUnchecked (&F->MemList, J);
1757 Print (stdout, 2, "Skipping `%s'...\n", GetString (M->Name));
1759 /* Walk throught the segments */
1762 if (N->Seg->Load == M) {
1763 /* Load area - mark the segment as dumped */
1764 N->Seg->Seg->Dumped = 1;
1767 /* Next segment node */