1 /*****************************************************************************/
5 /* cc65 debug info handling */
9 /* (C) 2010-2011, 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 /*****************************************************************************/
49 /*****************************************************************************/
51 /*****************************************************************************/
55 /* Use this for debugging - beware, lots of output */
58 /* Version numbers of the debug format we understand */
63 typedef struct StrBuf StrBuf;
65 char* Buf; /* Pointer to buffer */
66 unsigned Len; /* Length of the string */
67 unsigned Allocated; /* Size of allocated memory */
70 /* Initializer for a string buffer */
71 #define STRBUF_INITIALIZER { 0, 0, 0 }
73 /* An array of pointers that grows if needed */
74 typedef struct Collection Collection;
76 unsigned Count; /* Number of items in the list */
77 unsigned Size; /* Size of allocated array */
78 void** Items; /* Array with dynamic size */
81 /* Initializer for static collections */
82 #define COLLECTION_INITIALIZER { 0, 0, 0 }
84 /* Line info management */
85 typedef struct LineInfoListEntry LineInfoListEntry;
86 struct LineInfoListEntry {
87 cc65_addr Addr; /* Unique address */
88 unsigned Count; /* Number of LineInfos for this address */
89 void* Data; /* Either LineInfo* or LineInfo** */
92 typedef struct LineInfoList LineInfoList;
94 unsigned Count; /* Number of entries */
95 LineInfoListEntry* List; /* Dynamic array with entries */
100 /* Data structure containing information from the debug info file. A pointer
101 * to this structure is passed as handle to callers from the outside.
103 typedef struct DbgInfo DbgInfo;
105 Collection SegInfoByName; /* Segment infos sorted by name */
106 Collection SegInfoById; /* Segment infos sorted by id */
107 Collection FileInfoByName; /* File infos sorted by name */
108 Collection FileInfoById; /* File infos sorted by id */
109 Collection LineInfos; /* List of all line infos */
110 LineInfoList LineInfoByAddr; /* Line infos sorted by unique address */
111 Collection SymInfoByName; /* Symbol infos sorted by name */
112 Collection SymInfoByVal; /* Symbol infos sorted by value */
118 TOK_INVALID, /* Invalid token */
119 TOK_EOF, /* End of file reached */
121 TOK_INTCON, /* Integer constant */
122 TOK_STRCON, /* String constant */
130 TOK_ABSOLUTE, /* ABSOLUTE keyword */
131 TOK_ADDRSIZE, /* ADDRSIZE keyword */
132 TOK_COUNT, /* COUNT keyword */
133 TOK_EQUATE, /* EQUATE keyword */
134 TOK_FILE, /* FILE keyword */
135 TOK_ID, /* ID keyword */
136 TOK_LABEL, /* LABEL keyword */
137 TOK_LINE, /* LINE keyword */
138 TOK_LONG, /* LONG_keyword */
139 TOK_MAJOR, /* MAJOR keyword */
140 TOK_MINOR, /* MINOR keyword */
141 TOK_MTIME, /* MTIME keyword */
142 TOK_NAME, /* NAME keyword */
143 TOK_OUTPUTNAME, /* OUTPUTNAME keyword */
144 TOK_OUTPUTOFFS, /* OUTPUTOFFS keyword */
145 TOK_RANGE, /* RANGE keyword */
146 TOK_RO, /* RO keyword */
147 TOK_RW, /* RW keyword */
148 TOK_SEGMENT, /* SEGMENT keyword */
149 TOK_SIZE, /* SIZE keyword */
150 TOK_START, /* START keyword */
151 TOK_SYM, /* SYM keyword */
152 TOK_TYPE, /* TYPE keyword */
153 TOK_VALUE, /* VALUE keyword */
154 TOK_VERSION, /* VERSION keyword */
155 TOK_ZEROPAGE, /* ZEROPAGE keyword */
157 TOK_IDENT, /* To catch unknown keywords */
160 /* Data used when parsing the debug info file */
161 typedef struct InputData InputData;
163 const char* FileName; /* Name of input file */
164 cc65_line Line; /* Current line number */
165 unsigned Col; /* Current column number */
166 cc65_line SLine; /* Line number at start of token */
167 unsigned SCol; /* Column number at start of token */
168 unsigned Errors; /* Number of errors */
169 FILE* F; /* Input file */
170 int C; /* Input character */
171 Token Tok; /* Token from input stream */
172 unsigned long IVal; /* Integer constant */
173 StrBuf SVal; /* String constant */
174 cc65_errorfunc Error; /* Function called in case of errors */
175 unsigned MajorVersion; /* Major version number */
176 unsigned MinorVersion; /* Minor version number */
177 DbgInfo* Info; /* Pointer to debug info */
180 /* Internally used segment info struct */
181 typedef struct SegInfo SegInfo;
183 unsigned Id; /* Id of segment */
184 cc65_addr Start; /* Start address of segment */
185 cc65_addr Size; /* Size of segment */
186 char* OutputName; /* Name of output file */
187 unsigned long OutputOffs; /* Offset in output file */
188 char SegName[1]; /* Name of segment */
191 /* Internally used file info struct */
192 typedef struct FileInfo FileInfo;
194 unsigned Id; /* Id of file */
195 unsigned long Size; /* Size of file */
196 unsigned long MTime; /* Modification time */
197 Collection LineInfoByLine; /* Line infos sorted by line */
198 char FileName[1]; /* Name of file with full path */
201 /* Internally used line info struct */
202 typedef struct LineInfo LineInfo;
204 cc65_addr Start; /* Start of data range */
205 cc65_addr End; /* End of data range */
206 cc65_line Line; /* Line number */
208 unsigned Id; /* Id of file */
209 FileInfo* Info; /* Pointer to file info */
212 unsigned Id; /* Id of segment */
213 SegInfo* Info; /* Pointer to segment info */
215 cc65_line_type Type; /* Type of line */
216 unsigned Count; /* Nesting counter for macros */
219 /* Internally used symbol info struct */
220 typedef struct SymInfo SymInfo;
222 cc65_symbol_type Type; /* Type of symbol */
223 long Value; /* Value of symbol */
224 char SymName[1]; /* Name of symbol */
229 /*****************************************************************************/
231 /*****************************************************************************/
235 static void NextToken (InputData* D);
236 /* Read the next token from the input stream */
240 /*****************************************************************************/
241 /* Memory allocation */
242 /*****************************************************************************/
246 static void* xmalloc (size_t Size)
247 /* Allocate memory, check for out of memory condition. Do some debugging */
251 /* Allow zero sized requests and return NULL in this case */
254 /* Allocate memory */
257 /* Check for errors */
261 /* Return a pointer to the block */
267 static void* xrealloc (void* P, size_t Size)
268 /* Reallocate a memory block, check for out of memory */
270 /* Reallocate the block */
271 void* N = realloc (P, Size);
273 /* Check for errors */
274 assert (N != 0 || Size == 0);
276 /* Return the pointer to the new block */
282 static void xfree (void* Block)
283 /* Free the block, do some debugging */
290 /*****************************************************************************/
291 /* Dynamic strings */
292 /*****************************************************************************/
296 static void SB_Done (StrBuf* B)
297 /* Free the data of a string buffer (but not the struct itself) */
306 static void SB_Realloc (StrBuf* B, unsigned NewSize)
307 /* Reallocate the string buffer space, make sure at least NewSize bytes are
311 /* Get the current size, use a minimum of 8 bytes */
312 unsigned NewAllocated = B->Allocated;
313 if (NewAllocated == 0) {
317 /* Round up to the next power of two */
318 while (NewAllocated < NewSize) {
322 /* Reallocate the buffer. Beware: The allocated size may be zero while the
323 * length is not. This means that we have a buffer that wasn't allocated
327 /* Just reallocate the block */
328 B->Buf = xrealloc (B->Buf, NewAllocated);
330 /* Allocate a new block and copy */
331 B->Buf = memcpy (xmalloc (NewAllocated), B->Buf, B->Len);
334 /* Remember the new block size */
335 B->Allocated = NewAllocated;
340 static void SB_CheapRealloc (StrBuf* B, unsigned NewSize)
341 /* Reallocate the string buffer space, make sure at least NewSize bytes are
342 * available. This function won't copy the old buffer contents over to the new
343 * buffer and may be used if the old contents are overwritten later.
346 /* Get the current size, use a minimum of 8 bytes */
347 unsigned NewAllocated = B->Allocated;
348 if (NewAllocated == 0) {
352 /* Round up to the next power of two */
353 while (NewAllocated < NewSize) {
357 /* Free the old buffer if there is one */
362 /* Allocate a fresh block */
363 B->Buf = xmalloc (NewAllocated);
365 /* Remember the new block size */
366 B->Allocated = NewAllocated;
371 static unsigned SB_GetLen (const StrBuf* B)
372 /* Return the length of the buffer contents */
379 static const char* SB_GetConstBuf (const StrBuf* B)
380 /* Return a buffer pointer */
387 static void SB_Terminate (StrBuf* B)
388 /* Zero terminate the given string buffer. NOTE: The terminating zero is not
389 * accounted for in B->Len, if you want that, you have to use AppendChar!
392 unsigned NewLen = B->Len + 1;
393 if (NewLen > B->Allocated) {
394 SB_Realloc (B, NewLen);
396 B->Buf[B->Len] = '\0';
401 static void SB_Clear (StrBuf* B)
402 /* Clear the string buffer (make it empty) */
409 static void SB_CopyBuf (StrBuf* Target, const char* Buf, unsigned Size)
410 /* Copy Buf to Target, discarding the old contents of Target */
413 if (Target->Allocated < Size) {
414 SB_CheapRealloc (Target, Size);
416 memcpy (Target->Buf, Buf, Size);
423 static void SB_Copy (StrBuf* Target, const StrBuf* Source)
424 /* Copy Source to Target, discarding the old contents of Target */
426 SB_CopyBuf (Target, Source->Buf, Source->Len);
431 static void SB_AppendChar (StrBuf* B, int C)
432 /* Append a character to a string buffer */
434 unsigned NewLen = B->Len + 1;
435 if (NewLen > B->Allocated) {
436 SB_Realloc (B, NewLen);
438 B->Buf[B->Len] = (char) C;
444 static char* SB_StrDup (const StrBuf* B)
445 /* Return the contents of B as a dynamically allocated string. The string
446 * will always be NUL terminated.
449 /* Allocate memory */
450 char* S = xmalloc (B->Len + 1);
452 /* Copy the string */
453 memcpy (S, B->Buf, B->Len);
458 /* And return the result */
464 /*****************************************************************************/
466 /*****************************************************************************/
470 static Collection* InitCollection (Collection* C)
471 /* Initialize a collection and return it. */
473 /* Intialize the fields. */
478 /* Return the new struct */
484 static void DoneCollection (Collection* C)
485 /* Free the data for a collection. This will not free the data contained in
489 /* Free the pointer array */
492 /* Clear the fields, so the collection may be reused (or DoneCollection
502 static unsigned CollCount (const Collection* C)
503 /* Return the number of items in the collection */
510 static void CollGrow (Collection* C, unsigned Size)
511 /* Grow the collection C so it is able to hold Size items without a resize
512 * being necessary. This can be called for performance reasons if the number
513 * of items to be placed in the collection is known in advance.
518 /* Ignore the call if the collection is already large enough */
519 if (Size <= C->Size) {
523 /* Grow the collection */
525 NewItems = xmalloc (C->Size * sizeof (void*));
526 memcpy (NewItems, C->Items, C->Count * sizeof (void*));
533 static void CollInsert (Collection* C, void* Item, unsigned Index)
534 /* Insert the data at the given position in the collection */
536 /* Check for invalid indices */
537 assert (Index <= C->Count);
539 /* Grow the array if necessary */
540 if (C->Count >= C->Size) {
542 CollGrow (C, (C->Size == 0)? 8 : C->Size * 2);
545 /* Move the existing elements if needed */
546 if (C->Count != Index) {
547 memmove (C->Items+Index+1, C->Items+Index, (C->Count-Index) * sizeof (void*));
551 /* Store the new item */
552 C->Items[Index] = Item;
557 static void CollAppend (Collection* C, void* Item)
558 /* Append an item to the end of the collection */
560 /* Insert the item at the end of the current list */
561 CollInsert (C, Item, C->Count);
566 static void* CollAt (Collection* C, unsigned Index)
567 /* Return the item at the given index */
569 /* Check the index */
570 assert (Index < C->Count);
572 /* Return the element */
573 return C->Items[Index];
578 static void* CollFirst (Collection* C)
579 /* Return the first item in a collection */
581 /* We must have at least one entry */
582 assert (C->Count > 0);
584 /* Return the element */
590 static void CollDelete (Collection* C, unsigned Index)
591 /* Remove the item with the given index from the collection. This will not
592 * free the item itself, just the pointer. All items with higher indices
593 * will get moved to a lower position.
596 /* Check the index */
597 assert (Index < C->Count);
599 /* Remove the item pointer */
601 memmove (C->Items+Index, C->Items+Index+1, (C->Count-Index) * sizeof (void*));
606 static void CollQuickSort (Collection* C, int Lo, int Hi,
607 int (*Compare) (const void*, const void*))
608 /* Internal recursive sort function. */
610 /* Get a pointer to the items */
611 void** Items = C->Items;
618 while (I <= J && Compare (Items[Lo], Items[I]) >= 0) {
621 while (I <= J && Compare (Items[Lo], Items[J]) < 0) {
626 void* Tmp = Items[I];
635 void* Tmp = Items[J];
636 Items[J] = Items[Lo];
639 if (J > (Hi + Lo) / 2) {
640 CollQuickSort (C, J + 1, Hi, Compare);
643 CollQuickSort (C, Lo, J - 1, Compare);
651 void CollSort (Collection* C, int (*Compare) (const void*, const void*))
652 /* Sort the collection using the given compare function. */
655 CollQuickSort (C, 0, C->Count-1, Compare);
661 /*****************************************************************************/
662 /* Debugging stuff */
663 /*****************************************************************************/
670 #define DBGPRINT(format, ...) printf ((format), __VA_ARGS__)
674 static void DumpFileInfo (Collection* FileInfos)
675 /* Dump a list of file infos */
680 for (I = 0; I < CollCount (FileInfos); ++I) {
681 const FileInfo* FI = CollAt (FileInfos, I);
682 printf ("File info %u:\n"
688 (unsigned long) FI->Size,
689 (unsigned long) FI->MTime);
695 static void DumpOneLineInfo (unsigned Num, LineInfo* LI)
696 /* Dump one line info entry */
698 printf (" Index: %u\n"
701 " Range: 0x%06lX-0x%06lX\n"
705 LI->File.Info->FileName,
706 (unsigned long) LI->Line,
707 (unsigned long) LI->Start,
708 (unsigned long) LI->End,
715 static void DumpLineInfo (LineInfoList* L)
716 /* Dump a list of line infos */
721 for (I = 0; I < L->Count; ++I) {
722 const LineInfoListEntry* E = &L->List[I];
723 printf ("Addr: %lu\n", (unsigned long) E->Addr);
725 DumpOneLineInfo (0, E->Data);
727 for (J = 0; J < E->Count; ++J) {
728 DumpOneLineInfo (J, ((LineInfo**) E->Data)[J]);
736 static void DumpData (InputData* D)
737 /* Dump internal data to stdout for debugging */
740 DumpFileInfo (&D->Info->FileInfoById);
741 DumpLineInfo (&D->Info->LineInfoByAddr);
746 #define DBGPRINT(format, ...)
754 /*****************************************************************************/
756 /*****************************************************************************/
760 static SegInfo* NewSegInfo (const StrBuf* SegName, unsigned Id,
761 cc65_addr Start, cc65_addr Size,
762 const StrBuf* OutputName, unsigned long OutputOffs)
763 /* Create a new SegInfo struct and return it */
765 /* Allocate memory */
766 SegInfo* S = xmalloc (sizeof (SegInfo) + SB_GetLen (SegName));
772 if (SB_GetLen (OutputName) > 0) {
773 /* Output file given */
774 S->OutputName = SB_StrDup (OutputName);
775 S->OutputOffs = OutputOffs;
777 /* No output file given */
781 memcpy (S->SegName, SB_GetConstBuf (SegName), SB_GetLen (SegName) + 1);
789 static void FreeSegInfo (SegInfo* S)
790 /* Free a SegInfo struct */
792 xfree (S->OutputName);
798 static int CompareSegInfoByName (const void* L, const void* R)
799 /* Helper function to sort segment infos in a collection by name */
801 /* Sort by file name */
802 return strcmp (((const SegInfo*) L)->SegName,
803 ((const SegInfo*) R)->SegName);
808 static int CompareSegInfoById (const void* L, const void* R)
809 /* Helper function to sort segment infos in a collection by id */
811 if (((const SegInfo*) L)->Id > ((const SegInfo*) R)->Id) {
813 } else if (((const SegInfo*) L)->Id < ((const SegInfo*) R)->Id) {
822 /*****************************************************************************/
824 /*****************************************************************************/
828 static LineInfo* NewLineInfo (unsigned File, unsigned Seg, cc65_line Line,
829 cc65_addr Start, cc65_addr End,
830 cc65_line_type Type, unsigned Count)
831 /* Create a new LineInfo struct and return it */
833 /* Allocate memory */
834 LineInfo* L = xmalloc (sizeof (LineInfo));
851 static void FreeLineInfo (LineInfo* L)
852 /* Free a LineInfo struct */
859 static int CompareLineInfoByAddr (const void* L, const void* R)
860 /* Helper function to sort line infos in a collection by address. Line infos
861 * with smaller start address are considered smaller. If start addresses are
862 * equal, line infos with smaller end address are considered smaller. This
863 * means, that when CompareLineInfoByAddr is used for sorting, a range with
864 * identical start addresses will have smaller ranges first, followed by
868 /* Sort by start of range */
869 if (((const LineInfo*) L)->Start > ((const LineInfo*) R)->Start) {
871 } else if (((const LineInfo*) L)->Start < ((const LineInfo*) R)->Start) {
873 } else if (((const LineInfo*) L)->End > ((const LineInfo*) R)->End) {
875 } else if (((const LineInfo*) L)->End < ((const LineInfo*) R)->End) {
884 static int CompareLineInfoByLine (const void* L, const void* R)
885 /* Helper function to sort line infos in a collection by line. If the line
886 * is identical, sort by the address of the range.
889 if (((const LineInfo*) L)->Line > ((const LineInfo*) R)->Line) {
891 } else if (((const LineInfo*) L)->Line < ((const LineInfo*) R)->Line) {
894 return CompareLineInfoByAddr (L, R);
900 /*****************************************************************************/
902 /*****************************************************************************/
906 static FileInfo* NewFileInfo (const StrBuf* FileName, unsigned Id,
907 unsigned long Size, unsigned long MTime)
908 /* Create a new FileInfo struct and return it */
910 /* Allocate memory */
911 FileInfo* F = xmalloc (sizeof (FileInfo) + SB_GetLen (FileName));
917 InitCollection (&F->LineInfoByLine);
918 memcpy (F->FileName, SB_GetConstBuf (FileName), SB_GetLen (FileName) + 1);
926 static void FreeFileInfo (FileInfo* F)
927 /* Free a FileInfo struct */
929 /* Delete the collection with the line infos */
930 DoneCollection (&F->LineInfoByLine);
932 /* Free the file info structure itself */
938 static int CompareFileInfoByName (const void* L, const void* R)
939 /* Helper function to sort file infos in a collection by name */
941 /* Sort by file name */
942 return strcmp (((const FileInfo*) L)->FileName,
943 ((const FileInfo*) R)->FileName);
948 static int CompareFileInfoById (const void* L, const void* R)
949 /* Helper function to sort file infos in a collection by id */
951 if (((const FileInfo*) L)->Id > ((const FileInfo*) R)->Id) {
953 } else if (((const FileInfo*) L)->Id < ((const FileInfo*) R)->Id) {
962 /*****************************************************************************/
964 /*****************************************************************************/
968 static SymInfo* NewSymInfo (const StrBuf* Name, long Val, cc65_symbol_type Type)
969 /* Create a new SymInfo struct, intialize and return it */
971 /* Allocate memory */
972 SymInfo* S = xmalloc (sizeof (SymInfo) + SB_GetLen (Name));
977 memcpy (S->SymName, SB_GetConstBuf (Name), SB_GetLen (Name) + 1);
985 static void FreeSymInfo (SymInfo* S)
986 /* Free a SymInfo struct */
993 static int CompareSymInfoByName (const void* L, const void* R)
994 /* Helper function to sort symbol infos in a collection by name */
996 /* Sort by symbol name */
997 return strcmp (((const SymInfo*) L)->SymName,
998 ((const SymInfo*) R)->SymName);
1003 static int CompareSymInfoByVal (const void* L, const void* R)
1004 /* Helper function to sort symbol infos in a collection by value */
1006 /* Sort by symbol value. If both are equal, sort by symbol name so it
1007 * looks nice when such a list is returned.
1009 if (((const SymInfo*) L)->Value > ((const SymInfo*) R)->Value) {
1011 } else if (((const SymInfo*) L)->Value < ((const SymInfo*) R)->Value) {
1014 return CompareSymInfoByName (L, R);
1020 /*****************************************************************************/
1022 /*****************************************************************************/
1026 static void InitLineInfoList (LineInfoList* L)
1027 /* Initialize a line info list */
1035 static void CreateLineInfoList (LineInfoList* L, Collection* LineInfos)
1036 /* Create a LineInfoList from a Collection with line infos. The collection
1037 * must be sorted by ascending start addresses.
1042 LineInfoListEntry* List;
1043 unsigned StartIndex;
1048 /* Initialize and check if there's something to do */
1051 if (CollCount (LineInfos) == 0) {
1056 /* Step 1: Determine the number of unique address entries needed */
1057 LI = CollFirst (LineInfos);
1058 L->Count += (LI->End - LI->Start) + 1;
1060 for (I = 1; I < CollCount (LineInfos); ++I) {
1062 /* Get next entry */
1063 LI = CollAt (LineInfos, I);
1065 /* Check for additional unique addresses in this line info */
1066 if (LI->Start > End) {
1067 L->Count += (LI->End - LI->Start) + 1;
1069 } else if (LI->End > End) {
1070 L->Count += (LI->End - End);
1076 /* Step 2: Allocate memory and initialize it */
1077 L->List = List = xmalloc (L->Count * sizeof (*List));
1078 for (I = 0; I < L->Count; ++I) {
1083 /* Step 3: Determine the number of entries per unique address */
1085 LI = CollFirst (LineInfos);
1089 for (J = StartIndex, Addr = LI->Start; Addr <= LI->End; ++J, ++Addr) {
1090 List[J].Addr = Addr;
1093 for (I = 1; I < CollCount (LineInfos); ++I) {
1095 /* Get next entry */
1096 LI = CollAt (LineInfos, I);
1098 /* Determine the start index of the next range. Line infos are sorted
1099 * by ascending start address, so the start address of the next entry
1100 * is always larger than the previous one - we don't need to check
1103 if (LI->Start <= End) {
1104 /* Range starts within out already known linear range */
1105 StartIndex += (unsigned) (LI->Start - Start);
1107 if (LI->End > End) {
1111 /* Range starts after the already known */
1112 StartIndex += (unsigned) (End - Start) + 1;
1116 for (J = StartIndex, Addr = LI->Start; Addr <= LI->End; ++J, ++Addr) {
1117 List[J].Addr = Addr;
1122 /* Step 4: Allocate memory for the indirect tables */
1123 for (I = 0, List = L->List; I < L->Count; ++I, ++List) {
1125 /* For a count of 1, we store the pointer to the lineinfo for this
1126 * address in the Data pointer directly. For counts > 1, we allocate
1127 * an array of pointers and reset the counter, so we can use it as
1128 * an index later. This is dangerous programming since it disables
1129 * all possible checks!
1131 if (List->Count > 1) {
1132 List->Data = xmalloc (List->Count * sizeof (LineInfo*));
1137 /* Step 5: Enter the data into the table */
1139 LI = CollFirst (LineInfos);
1143 for (J = StartIndex, Addr = LI->Start; Addr <= LI->End; ++J, ++Addr) {
1144 assert (List[J].Addr == Addr);
1145 if (List[J].Count == 1 && List[J].Data == 0) {
1148 ((LineInfo**) List[J].Data)[List[J].Count++] = LI;
1151 for (I = 1; I < CollCount (LineInfos); ++I) {
1153 /* Get next entry */
1154 LI = CollAt (LineInfos, I);
1156 /* Determine the start index of the next range. Line infos are sorted
1157 * by ascending start address, so the start address of the next entry
1158 * is always larger than the previous one - we don't need to check
1161 if (LI->Start <= End) {
1162 /* Range starts within out already known linear range */
1163 StartIndex += (unsigned) (LI->Start - Start);
1165 if (LI->End > End) {
1169 /* Range starts after the already known */
1170 StartIndex += (unsigned) (End - Start) + 1;
1174 for (J = StartIndex, Addr = LI->Start; Addr <= LI->End; ++J, ++Addr) {
1175 assert (List[J].Addr == Addr);
1176 if (List[J].Count == 1 && List[J].Data == 0) {
1179 ((LineInfo**) List[J].Data)[List[J].Count++] = LI;
1187 static void DoneLineInfoList (LineInfoList* L)
1188 /* Delete the contents of a line info list */
1192 /* Delete the line info and the indirect data */
1193 for (I = 0; I < L->Count; ++I) {
1195 /* Get a pointer to the entry */
1196 LineInfoListEntry* E = &L->List[I];
1198 /* Check for indirect memory */
1200 /* LineInfo addressed indirectly */
1205 /* Delete the list */
1211 /*****************************************************************************/
1213 /*****************************************************************************/
1217 static DbgInfo* NewDbgInfo (void)
1218 /* Create a new DbgInfo struct and return it */
1220 /* Allocate memory */
1221 DbgInfo* Info = xmalloc (sizeof (DbgInfo));
1224 InitCollection (&Info->SegInfoByName);
1225 InitCollection (&Info->SegInfoById);
1226 InitCollection (&Info->FileInfoByName);
1227 InitCollection (&Info->FileInfoById);
1228 InitCollection (&Info->LineInfos);
1229 InitLineInfoList (&Info->LineInfoByAddr);
1230 InitCollection (&Info->SymInfoByName);
1231 InitCollection (&Info->SymInfoByVal);
1239 static void FreeDbgInfo (DbgInfo* Info)
1240 /* Free a DbgInfo struct */
1244 /* Free segment info */
1245 for (I = 0; I < CollCount (&Info->SegInfoByName); ++I) {
1246 FreeSegInfo (CollAt (&Info->SegInfoByName, I));
1248 DoneCollection (&Info->SegInfoByName);
1249 DoneCollection (&Info->SegInfoById);
1251 /* Free file info */
1252 for (I = 0; I < CollCount (&Info->FileInfoByName); ++I) {
1253 FreeFileInfo (CollAt (&Info->FileInfoByName, I));
1255 DoneCollection (&Info->FileInfoByName);
1256 DoneCollection (&Info->FileInfoById);
1258 /* Free line info */
1259 for (I = 0; I < CollCount (&Info->LineInfos); ++I) {
1260 FreeLineInfo (CollAt (&Info->LineInfos, I));
1262 DoneCollection (&Info->LineInfos);
1263 DoneLineInfoList (&Info->LineInfoByAddr);
1265 /* Free symbol info */
1266 for (I = 0; I < CollCount (&Info->SymInfoByName); ++I) {
1267 FreeSymInfo (CollAt (&Info->SymInfoByName, I));
1269 DoneCollection (&Info->SymInfoByName);
1270 DoneCollection (&Info->SymInfoByVal);
1272 /* Free the structure itself */
1278 /*****************************************************************************/
1279 /* Helper functions */
1280 /*****************************************************************************/
1284 static void CopyLineInfo (cc65_linedata* D, const LineInfo* L)
1285 /* Copy data from a LineInfo struct to the cc65_linedata struct returned to
1289 D->source_name = L->File.Info->FileName;
1290 D->source_size = L->File.Info->Size;
1291 D->source_mtime = L->File.Info->MTime;
1292 D->source_line = L->Line;
1293 D->line_start = L->Start;
1294 D->line_end = L->End;
1295 if (L->Seg.Info->OutputName) {
1296 D->output_name = L->Seg.Info->OutputName;
1297 D->output_offs = L->Seg.Info->OutputOffs + L->Start - L->Seg.Info->Start;
1302 D->line_type = L->Type;
1303 D->count = L->Count;
1308 static void CopySymInfo (cc65_symboldata* D, const SymInfo* S)
1309 /* Copy data from a SymInfo struct to the cc65_symboldata struct returned to
1313 D->symbol_name = S->SymName;
1314 D->symbol_type = S->Type;
1315 D->symbol_value = S->Value;
1320 static void ParseError (InputData* D, cc65_error_severity Type, const char* Msg, ...)
1321 /* Call the user supplied parse error function */
1327 /* Test-format the error message so we know how much space to allocate */
1329 MsgSize = vsnprintf (0, 0, Msg, ap);
1332 /* Allocate memory */
1333 E = xmalloc (sizeof (*E) + MsgSize);
1335 /* Write data to E */
1337 E->name = D->FileName;
1339 E->column = D->SCol;
1341 vsnprintf (E->errormsg, MsgSize+1, Msg, ap);
1344 /* Call the caller:-) */
1347 /* Free the data structure */
1351 if (Type == CC65_ERROR) {
1358 static void SkipLine (InputData* D)
1359 /* Error recovery routine. Skip tokens until EOL or EOF is reached */
1361 while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1368 static void UnexpectedToken (InputData* D)
1369 /* Call ParseError with a message about an unexpected input token */
1371 ParseError (D, CC65_ERROR, "Unexpected input token %d", D->Tok);
1377 static void UnknownKeyword (InputData* D)
1378 /* Print a warning about an unknown keyword in the file. Try to do smart
1379 * recovery, so if later versions of the debug information add additional
1380 * keywords, this code may be able to at least ignore them.
1383 /* Output a warning */
1384 ParseError (D, CC65_WARNING, "Unknown keyword \"%s\" - skipping",
1385 SB_GetConstBuf (&D->SVal));
1387 /* Skip the identifier */
1390 /* If an equal sign follows, ignore anything up to the next line end
1391 * or comma. If a comma or line end follows, we're already done. If
1392 * we have none of both, we ignore the remainder of the line.
1394 if (D->Tok == TOK_EQUAL) {
1396 while (D->Tok != TOK_COMMA && D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1399 } else if (D->Tok != TOK_COMMA && D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1406 /*****************************************************************************/
1407 /* Scanner and parser */
1408 /*****************************************************************************/
1412 static int DigitVal (int C)
1413 /* Return the value for a numeric digit. Return -1 if C is invalid */
1417 } else if (isxdigit (C)) {
1418 return toupper (C) - 'A' + 10;
1426 static void NextChar (InputData* D)
1427 /* Read the next character from the input. Count lines and columns */
1429 /* Check if we've encountered EOF before */
1431 D->C = fgetc (D->F);
1443 static void NextToken (InputData* D)
1444 /* Read the next token from the input stream */
1446 static const struct KeywordEntry {
1447 const char Keyword[16];
1449 } KeywordTable[] = {
1450 { "absolute", TOK_ABSOLUTE },
1451 { "addrsize", TOK_ADDRSIZE },
1452 { "count", TOK_COUNT },
1453 { "equate", TOK_EQUATE },
1454 { "file", TOK_FILE },
1456 { "label", TOK_LABEL },
1457 { "line", TOK_LINE },
1458 { "long", TOK_LONG },
1459 { "major", TOK_MAJOR },
1460 { "minor", TOK_MINOR },
1461 { "mtime", TOK_MTIME },
1462 { "name", TOK_NAME },
1463 { "outputname", TOK_OUTPUTNAME },
1464 { "outputoffs", TOK_OUTPUTOFFS },
1465 { "range", TOK_RANGE },
1468 { "segment", TOK_SEGMENT },
1469 { "size", TOK_SIZE },
1470 { "start", TOK_START },
1472 { "type", TOK_TYPE },
1473 { "value", TOK_VALUE },
1474 { "version", TOK_VERSION },
1475 { "zeropage", TOK_ZEROPAGE },
1479 /* Skip whitespace */
1480 while (D->C == ' ' || D->C == '\t') {
1484 /* Remember the current position as start of the next token */
1489 if (D->C == '_' || isalpha (D->C)) {
1491 const struct KeywordEntry* Entry;
1493 /* Read the identifier */
1494 SB_Clear (&D->SVal);
1495 while (D->C == '_' || isalnum (D->C)) {
1496 SB_AppendChar (&D->SVal, D->C);
1499 SB_Terminate (&D->SVal);
1501 /* Search the identifier in the keyword table */
1502 Entry = bsearch (SB_GetConstBuf (&D->SVal),
1504 sizeof (KeywordTable) / sizeof (KeywordTable[0]),
1505 sizeof (KeywordTable[0]),
1506 (int (*)(const void*, const void*)) strcmp);
1510 D->Tok = Entry->Tok;
1516 if (isdigit (D->C)) {
1521 if (toupper (D->C) == 'X') {
1531 while ((Val = DigitVal (D->C)) >= 0 && Val < Base) {
1532 D->IVal = D->IVal * Base + Val;
1535 D->Tok = TOK_INTCON;
1539 /* Other characters */
1563 SB_Clear (&D->SVal);
1566 if (D->C == '\n' || D->C == EOF) {
1567 ParseError (D, CC65_ERROR, "Unterminated string constant");
1574 SB_AppendChar (&D->SVal, D->C);
1577 SB_Terminate (&D->SVal);
1578 D->Tok = TOK_STRCON;
1591 ParseError (D, CC65_ERROR, "Invalid input character `%c'", D->C);
1598 static int TokenFollows (InputData* D, Token Tok, const char* Name)
1599 /* Check for a comma */
1601 if (D->Tok != Tok) {
1602 ParseError (D, CC65_ERROR, "%s expected", Name);
1612 static int IntConstFollows (InputData* D)
1613 /* Check for an integer constant */
1615 return TokenFollows (D, TOK_INTCON, "Integer constant");
1620 static int StrConstFollows (InputData* D)
1621 /* Check for a string literal */
1623 return TokenFollows (D, TOK_STRCON, "String literal");
1628 static int Consume (InputData* D, Token Tok, const char* Name)
1629 /* Check for a token and consume it. Return true if the token was comsumed,
1630 * return false otherwise.
1633 if (TokenFollows (D, Tok, Name)) {
1643 static int ConsumeEqual (InputData* D)
1644 /* Consume an equal sign */
1646 return Consume (D, TOK_EQUAL, "'='");
1651 static int ConsumeMinus (InputData* D)
1652 /* Consume a minus sign */
1654 return Consume (D, TOK_MINUS, "'-'");
1659 static void ConsumeEOL (InputData* D)
1660 /* Consume an end-of-line token, if we aren't at end-of-file */
1662 if (D->Tok != TOK_EOF) {
1663 if (D->Tok != TOK_EOL) {
1664 ParseError (D, CC65_ERROR, "Extra tokens in line");
1673 static void ParseFile (InputData* D)
1674 /* Parse a FILE line */
1677 unsigned long Size = 0;
1678 unsigned long MTime = 0;
1679 StrBuf FileName = STRBUF_INITIALIZER;
1687 ibRequired = ibId | ibFileName | ibSize | ibMTime,
1688 } InfoBits = ibNone;
1690 /* Skip the FILE token */
1693 /* More stuff follows */
1698 /* Check for an unknown keyword */
1699 if (D->Tok == TOK_IDENT) {
1704 /* Something we know? */
1705 if (D->Tok != TOK_ID && D->Tok != TOK_MTIME &&
1706 D->Tok != TOK_NAME && D->Tok != TOK_SIZE) {
1711 /* Remember the token, skip it, check for equal */
1714 if (!ConsumeEqual (D)) {
1718 /* Check what the token was */
1722 if (!IntConstFollows (D)) {
1731 if (!IntConstFollows (D)) {
1736 InfoBits |= ibMTime;
1740 if (!StrConstFollows (D)) {
1743 SB_Copy (&FileName, &D->SVal);
1744 SB_Terminate (&FileName);
1745 InfoBits |= ibFileName;
1750 if (!IntConstFollows (D)) {
1760 UnexpectedToken (D);
1766 if (D->Tok != TOK_COMMA) {
1772 /* Check for end of line */
1773 if (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1774 UnexpectedToken (D);
1779 /* Check for required information */
1780 if ((InfoBits & ibRequired) != ibRequired) {
1781 ParseError (D, CC65_ERROR, "Required attributes missing");
1785 /* Create the file info and remember it */
1786 F = NewFileInfo (&FileName, Id, Size, MTime);
1787 CollAppend (&D->Info->FileInfoByName, F);
1790 /* Entry point in case of errors */
1791 SB_Done (&FileName);
1797 static void ParseLine (InputData* D)
1798 /* Parse a LINE line */
1801 unsigned Segment = 0;
1803 cc65_addr Start = 0;
1805 cc65_line_type Type = CC65_LINE_ASM;
1816 ibRequired = ibFile | ibSegment | ibLine | ibRange,
1817 } InfoBits = ibNone;
1819 /* Skip the LINE token */
1822 /* More stuff follows */
1827 /* Check for an unknown keyword */
1828 if (D->Tok == TOK_IDENT) {
1833 /* Something we know? */
1834 if (D->Tok != TOK_COUNT && D->Tok != TOK_FILE &&
1835 D->Tok != TOK_LINE && D->Tok != TOK_RANGE &&
1836 D->Tok != TOK_SEGMENT && D->Tok != TOK_TYPE) {
1841 /* Remember the token, skip it, check for equal */
1844 if (!ConsumeEqual (D)) {
1848 /* Check what the token was */
1852 if (!IntConstFollows (D)) {
1861 if (!IntConstFollows (D)) {
1864 Line = (cc65_line) D->IVal;
1870 if (!IntConstFollows (D)) {
1873 Start = (cc65_addr) D->IVal;
1875 if (!ConsumeMinus (D)) {
1878 if (!IntConstFollows (D)) {
1881 End = (cc65_addr) D->IVal;
1883 InfoBits |= ibRange;
1887 if (!IntConstFollows (D)) {
1891 InfoBits |= ibSegment;
1896 if (!IntConstFollows (D)) {
1899 Type = (cc65_line_type) D->IVal;
1905 if (!IntConstFollows (D)) {
1909 InfoBits |= ibCount;
1915 UnexpectedToken (D);
1921 if (D->Tok != TOK_COMMA) {
1927 /* Check for end of line */
1928 if (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1929 UnexpectedToken (D);
1934 /* Check for required information */
1935 if ((InfoBits & ibRequired) != ibRequired) {
1936 ParseError (D, CC65_ERROR, "Required attributes missing");
1940 /* Create the line info and remember it */
1941 L = NewLineInfo (File, Segment, Line, Start, End, Type, Count);
1942 CollAppend (&D->Info->LineInfos, L);
1945 /* Entry point in case of errors */
1951 static void ParseSegment (InputData* D)
1952 /* Parse a SEGMENT line */
1955 cc65_addr Start = 0;
1957 StrBuf SegName = STRBUF_INITIALIZER;
1958 StrBuf OutputName = STRBUF_INITIALIZER;
1959 unsigned long OutputOffs = 0;
1971 ibRequired = ibId | ibSegName | ibStart | ibSize | ibAddrSize | ibType,
1972 } InfoBits = ibNone;
1974 /* Skip the SEGMENT token */
1977 /* More stuff follows */
1982 /* Check for an unknown keyword */
1983 if (D->Tok == TOK_IDENT) {
1988 /* Something we know? */
1989 if (D->Tok != TOK_ADDRSIZE && D->Tok != TOK_ID &&
1990 D->Tok != TOK_NAME && D->Tok != TOK_OUTPUTNAME &&
1991 D->Tok != TOK_OUTPUTOFFS && D->Tok != TOK_SIZE &&
1992 D->Tok != TOK_START && D->Tok != TOK_TYPE) {
1997 /* Remember the token, skip it, check for equal */
2000 if (!ConsumeEqual (D)) {
2004 /* Check what the token was */
2009 InfoBits |= ibAddrSize;
2013 if (!IntConstFollows (D)) {
2022 if (!StrConstFollows (D)) {
2025 SB_Copy (&SegName, &D->SVal);
2026 SB_Terminate (&SegName);
2027 InfoBits |= ibSegName;
2031 case TOK_OUTPUTNAME:
2032 if (!StrConstFollows (D)) {
2035 SB_Copy (&OutputName, &D->SVal);
2036 SB_Terminate (&OutputName);
2037 InfoBits |= ibOutputName;
2041 case TOK_OUTPUTOFFS:
2042 if (!IntConstFollows (D)) {
2045 OutputOffs = D->IVal;
2047 InfoBits |= ibOutputOffs;
2051 if (!IntConstFollows (D)) {
2060 if (!IntConstFollows (D)) {
2063 Start = (cc65_addr) D->IVal;
2065 InfoBits |= ibStart;
2075 UnexpectedToken (D);
2081 if (D->Tok != TOK_COMMA) {
2087 /* Check for end of line */
2088 if (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
2089 UnexpectedToken (D);
2094 /* Check for required and/or matched information */
2095 if ((InfoBits & ibRequired) != ibRequired) {
2096 ParseError (D, CC65_ERROR, "Required attributes missing");
2099 InfoBits &= (ibOutputName | ibOutputOffs);
2100 if (InfoBits != ibNone && InfoBits != (ibOutputName | ibOutputOffs)) {
2101 ParseError (D, CC65_ERROR,
2102 "Attributes \"outputname\" and \"outputoffs\" must be paired");
2106 /* Fix OutputOffs if not given */
2107 if (InfoBits == ibNone) {
2111 /* Create the segment info and remember it */
2112 S = NewSegInfo (&SegName, Id, Start, Size, &OutputName, OutputOffs);
2113 CollAppend (&D->Info->SegInfoByName, S);
2116 /* Entry point in case of errors */
2118 SB_Done (&OutputName);
2124 static void ParseSym (InputData* D)
2125 /* Parse a SYM line */
2127 cc65_symbol_type Type;
2129 StrBuf SymName = STRBUF_INITIALIZER;
2137 ibRequired = ibSymName | ibValue | ibAddrSize | ibType,
2138 } InfoBits = ibNone;
2140 /* Skip the SYM token */
2143 /* More stuff follows */
2148 /* Check for an unknown keyword */
2149 if (D->Tok == TOK_IDENT) {
2154 /* Something we know? */
2155 if (D->Tok != TOK_ADDRSIZE && D->Tok != TOK_NAME &&
2156 D->Tok != TOK_TYPE && D->Tok != TOK_VALUE) {
2161 /* Remember the token, skip it, check for equal */
2164 if (!ConsumeEqual (D)) {
2168 /* Check what the token was */
2173 InfoBits |= ibAddrSize;
2177 if (!StrConstFollows (D)) {
2180 SB_Copy (&SymName, &D->SVal);
2181 SB_Terminate (&SymName);
2182 InfoBits |= ibSymName;
2189 Type = CC65_SYM_EQUATE;
2192 Type = CC65_SYM_LABEL;
2195 ParseError (D, CC65_ERROR,
2196 "Unknown value for attribute \"type\"");
2205 if (!IntConstFollows (D)) {
2209 InfoBits |= ibValue;
2215 UnexpectedToken (D);
2221 if (D->Tok != TOK_COMMA) {
2227 /* Check for end of line */
2228 if (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
2229 UnexpectedToken (D);
2234 /* Check for required and/or matched information */
2235 if ((InfoBits & ibRequired) != ibRequired) {
2236 ParseError (D, CC65_ERROR, "Required attributes missing");
2240 /* Create the symbol info and remember it */
2241 S = NewSymInfo (&SymName, Value, Type);
2242 CollAppend (&D->Info->SymInfoByName, S);
2243 CollAppend (&D->Info->SymInfoByVal, S);
2246 /* Entry point in case of errors */
2253 static void ParseVersion (InputData* D)
2254 /* Parse a VERSION line */
2260 ibRequired = ibMajor | ibMinor,
2261 } InfoBits = ibNone;
2263 /* Skip the VERSION token */
2266 /* More stuff follows */
2267 while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
2273 if (!ConsumeEqual (D)) {
2276 if (!IntConstFollows (D)) {
2279 D->MajorVersion = D->IVal;
2281 InfoBits |= ibMajor;
2286 if (!ConsumeEqual (D)) {
2289 if (!IntConstFollows (D)) {
2292 D->MinorVersion = D->IVal;
2294 InfoBits |= ibMinor;
2298 /* Try to skip unknown keywords that may have been added by
2305 UnexpectedToken (D);
2310 /* Comma follows before next attribute */
2311 if (D->Tok == TOK_COMMA) {
2313 } else if (D->Tok == TOK_EOL || D->Tok == TOK_EOF) {
2316 UnexpectedToken (D);
2321 /* Check for required information */
2322 if ((InfoBits & ibRequired) != ibRequired) {
2323 ParseError (D, CC65_ERROR, "Required attributes missing");
2328 /* Entry point in case of errors */
2334 /*****************************************************************************/
2335 /* Data processing */
2336 /*****************************************************************************/
2340 static SegInfo* FindSegInfoById (InputData* D, unsigned Id)
2341 /* Find the SegInfo with a given Id */
2343 /* Get a pointer to the segment info collection */
2344 Collection* SegInfos = &D->Info->SegInfoById;
2346 /* Do a binary search */
2348 int Hi = (int) CollCount (SegInfos) - 1;
2352 int Cur = (Lo + Hi) / 2;
2355 SegInfo* CurItem = CollAt (SegInfos, Cur);
2358 if (Id > CurItem->Id) {
2360 } else if (Id < CurItem->Id) {
2374 static FileInfo* FindFileInfoByName (Collection* FileInfos, const char* FileName)
2375 /* Find the FileInfo for a given file name */
2377 /* Do a binary search */
2379 int Hi = (int) CollCount (FileInfos) - 1;
2383 int Cur = (Lo + Hi) / 2;
2386 FileInfo* CurItem = CollAt (FileInfos, Cur);
2389 int Res = strcmp (CurItem->FileName, FileName);
2394 } else if (Res > 0) {
2408 static FileInfo* FindFileInfoById (Collection* FileInfos, unsigned Id)
2409 /* Find the FileInfo with a given Id */
2411 /* Do a binary search */
2413 int Hi = (int) CollCount (FileInfos) - 1;
2417 int Cur = (Lo + Hi) / 2;
2420 FileInfo* CurItem = CollAt (FileInfos, Cur);
2423 if (Id > CurItem->Id) {
2425 } else if (Id < CurItem->Id) {
2439 static void ProcessSegInfo (InputData* D)
2440 /* Postprocess segment infos */
2444 /* Get pointers to the segment info collections */
2445 Collection* SegInfoByName = &D->Info->SegInfoByName;
2446 Collection* SegInfoById = &D->Info->SegInfoById;
2448 /* Sort the segment infos by name */
2449 CollSort (SegInfoByName, CompareSegInfoByName);
2451 /* Copy all items over to the collection that will get sorted by id */
2452 for (I = 0; I < CollCount (SegInfoByName); ++I) {
2453 CollAppend (SegInfoById, CollAt (SegInfoByName, I));
2456 /* Sort this collection */
2457 CollSort (SegInfoById, CompareSegInfoById);
2462 static void ProcessFileInfo (InputData* D)
2463 /* Postprocess file infos */
2465 /* Get pointers to the file info collections */
2466 Collection* FileInfoByName = &D->Info->FileInfoByName;
2467 Collection* FileInfoById = &D->Info->FileInfoById;
2469 /* First, sort the file infos, so we can check for duplicates and do
2472 CollSort (FileInfoByName, CompareFileInfoByName);
2474 /* Cannot work on an empty collection */
2475 if (CollCount (FileInfoByName) > 0) {
2477 /* Walk through the file infos sorted by name and check for duplicates.
2478 * If we find some, warn and remove them, so the file infos are unique
2481 FileInfo* F = CollAt (FileInfoByName, 0);
2483 while (I < CollCount (FileInfoByName)) {
2484 FileInfo* Next = CollAt (FileInfoByName, I);
2485 if (strcmp (F->FileName, Next->FileName) == 0) {
2486 /* Warn only if time stamp and/or size is different */
2487 if (F->Size != Next->Size || F->MTime != Next->MTime) {
2490 "Duplicate file entry for \"%s\"",
2493 /* Remove the duplicate entry */
2494 FreeFileInfo (Next);
2495 CollDelete (FileInfoByName, I);
2497 /* This one is ok, check the next entry */
2503 /* Copy the file infos to another collection that will be sorted by id */
2504 for (I = 0; I < CollCount (FileInfoByName); ++I) {
2505 CollAppend (FileInfoById, CollAt (FileInfoByName, I));
2508 /* Sort this collection */
2509 CollSort (FileInfoById, CompareFileInfoById);
2515 static void ProcessLineInfo (InputData* D)
2516 /* Postprocess line infos */
2518 /* Get pointers to the collections */
2519 Collection* LineInfos = &D->Info->LineInfos;
2520 Collection* FileInfos = &D->Info->FileInfoByName;
2522 /* Walk over the line infos and replace the id numbers of file and segment
2523 * with pointers to the actual structs. Add the line info to each file
2524 * where it is defined.
2527 FileInfo* LastFileInfo = 0;
2528 SegInfo* LastSegInfo = 0;
2529 while (I < CollCount (LineInfos)) {
2534 /* Get LineInfo struct */
2535 LineInfo* L = CollAt (LineInfos, I);
2537 /* Find the FileInfo that corresponds to Id. We cache the last file
2538 * info in LastFileInfo to speedup searching.
2540 if (LastFileInfo && LastFileInfo->Id == L->File.Id) {
2543 F = FindFileInfoById (&D->Info->FileInfoById, L->File.Id);
2545 /* If we have no corresponding file info, print a warning and
2546 * remove the line info.
2551 "No file info for file with id %u",
2554 CollDelete (LineInfos, I);
2558 /* Otherwise remember it for later */
2562 /* Replace the file id by a pointer to the file info */
2565 /* Find the SegInfo that corresponds to Id. We cache the last file
2566 * info in LastSegInfo to speedup searching.
2568 if (LastSegInfo && LastSegInfo->Id == L->Seg.Id) {
2571 S = FindSegInfoById (D, L->Seg.Id);
2573 /* If we have no corresponding segment info, print a warning and
2574 * remove the line info.
2579 "No segment info for segment with id %u",
2582 CollDelete (LineInfos, I);
2586 /* Otherwise remember it for later */
2590 /* Replace the segment id by a pointer to the segment info */
2593 /* Add this line info to the file where it is defined */
2594 CollAppend (&F->LineInfoByLine, L);
2600 /* Walk over all files and sort the line infos for each file so we can
2601 * do a binary search later.
2603 for (I = 0; I < CollCount (FileInfos); ++I) {
2605 /* Get a pointer to this file info */
2606 FileInfo* F = CollAt (FileInfos, I);
2608 /* Sort the line infos for this file */
2609 CollSort (&F->LineInfoByLine, CompareLineInfoByLine);
2612 /* Sort the collection with all line infos by address */
2613 CollSort (LineInfos, CompareLineInfoByAddr);
2615 /* Create the line info list from the line info collection */
2616 CreateLineInfoList (&D->Info->LineInfoByAddr, LineInfos);
2621 static LineInfoListEntry* FindLineInfoByAddr (const LineInfoList* L, cc65_addr Addr)
2622 /* Find the index of a LineInfo for a given address. Returns 0 if no such
2623 * lineinfo was found.
2626 /* Do a binary search */
2628 int Hi = (int) L->Count - 1;
2632 int Cur = (Lo + Hi) / 2;
2635 LineInfoListEntry* CurItem = &L->List[Cur];
2638 if (CurItem->Addr > Addr) {
2640 } else if (CurItem->Addr < Addr) {
2654 static LineInfo* FindLineInfoByLine (FileInfo* F, cc65_line Line)
2655 /* Find the LineInfo for a given line number */
2661 /* Get a pointer to the line info collection for this file */
2662 Collection* LineInfoByLine = &F->LineInfoByLine;
2664 /* Do a binary search */
2666 Hi = (int) CollCount (LineInfoByLine) - 1;
2670 int Cur = (Lo + Hi) / 2;
2673 LineInfo* CurItem = CollAt (LineInfoByLine, Cur);
2676 if (Line < CurItem->Line) {
2678 } else if (Line > CurItem->Line) {
2692 static void ProcessSymInfo (InputData* D)
2693 /* Postprocess symbol infos */
2695 /* Get pointers to the symbol info collections */
2696 Collection* SymInfoByName = &D->Info->SymInfoByName;
2697 Collection* SymInfoByVal = &D->Info->SymInfoByVal;
2699 /* Sort the symbol infos */
2700 CollSort (SymInfoByName, CompareSymInfoByName);
2701 CollSort (SymInfoByVal, CompareSymInfoByVal);
2706 static int FindSymInfoByName (Collection* SymInfos, const char* SymName, int* Index)
2707 /* Find the SymInfo for a given file name. The function returns true if the
2708 * name was found. In this case, Index contains the index of the first item
2709 * that matches. If the item wasn't found, the function returns false and
2710 * Index contains the insert position for SymName.
2713 /* Do a binary search */
2715 int Hi = (int) CollCount (SymInfos) - 1;
2720 int Cur = (Lo + Hi) / 2;
2723 SymInfo* CurItem = CollAt (SymInfos, Cur);
2726 int Res = strcmp (CurItem->SymName, SymName);
2733 /* Since we may have duplicates, repeat the search until we've
2734 * the first item that has a match.
2742 /* Pass back the index. This is also the insert position */
2749 static int FindSymInfoByValue (Collection* SymInfos, long Value, int* Index)
2750 /* Find the SymInfo for a given value. The function returns true if the
2751 * value was found. In this case, Index contains the index of the first item
2752 * that matches. If the item wasn't found, the function returns false and
2753 * Index contains the insert position for the given value.
2756 /* Do a binary search */
2758 int Hi = (int) CollCount (SymInfos) - 1;
2763 int Cur = (Lo + Hi) / 2;
2766 SymInfo* CurItem = CollAt (SymInfos, Cur);
2769 if (Value > CurItem->Value) {
2773 /* Since we may have duplicates, repeat the search until we've
2774 * the first item that has a match.
2776 if (Value == CurItem->Value) {
2782 /* Pass back the index. This is also the insert position */
2789 /*****************************************************************************/
2791 /*****************************************************************************/
2795 cc65_dbginfo cc65_read_dbginfo (const char* FileName, cc65_errorfunc ErrFunc)
2796 /* Parse the debug info file with the given name. On success, the function
2797 * will return a pointer to an opaque cc65_dbginfo structure, that must be
2798 * passed to the other functions in this module to retrieve information.
2799 * errorfunc is called in case of warnings and errors. If the file cannot be
2800 * read successfully, NULL is returned.
2803 /* Data structure used to control scanning and parsing */
2805 0, /* Name of input file */
2806 1, /* Line number */
2808 0, /* Line at start of current token */
2809 0, /* Column at start of current token */
2810 0, /* Number of errors */
2812 ' ', /* Input character */
2813 TOK_INVALID, /* Input token */
2814 0, /* Integer constant */
2815 STRBUF_INITIALIZER, /* String constant */
2816 0, /* Function called in case of errors */
2817 0, /* Major version number */
2818 0, /* Minor version number */
2819 0, /* Pointer to debug info */
2821 D.FileName = FileName;
2824 /* Open the input file */
2825 D.F = fopen (D.FileName, "r");
2828 ParseError (&D, CC65_ERROR,
2829 "Cannot open input file \"%s\": %s",
2830 D.FileName, strerror (errno));
2834 /* Create a new debug info struct */
2835 D.Info = NewDbgInfo ();
2837 /* Prime the pump */
2840 /* The first line in the file must specify version information */
2841 if (D.Tok != TOK_VERSION) {
2842 ParseError (&D, CC65_ERROR,
2843 "\"version\" keyword missing in first line - this is not "
2844 "a valid debug info file");
2847 /* Parse the version directive and check the version */
2849 if (D.MajorVersion > VER_MAJOR) {
2850 ParseError (&D, CC65_WARNING,
2851 "The format of this debug info file is newer than what we "
2852 "know. Will proceed but probably fail. Version found = %u, "
2853 "version supported = %u",
2854 D.MajorVersion, VER_MAJOR);
2859 while (D.Tok != TOK_EOF) {
2880 /* Output a warning, then skip the line with the unknown
2881 * keyword that may have been added by a later version.
2883 ParseError (&D, CC65_WARNING,
2884 "Unknown keyword \"%s\" - skipping",
2885 SB_GetConstBuf (&D.SVal));
2891 UnexpectedToken (&D);
2895 /* EOL or EOF must follow */
2900 /* Close the file */
2903 /* Free memory allocated for SVal */
2906 /* In case of errors, delete the debug info already allocated and
2910 /* Free allocated stuff */
2912 for (I = 0; I < CollCount (&D.Info->LineInfos); ++I) {
2913 FreeLineInfo (CollAt (&D.Info->LineInfos, I));
2915 DoneCollection (&D.Info->LineInfos);
2916 FreeDbgInfo (D.Info);
2920 /* We do now have all the information from the input file. Do
2923 ProcessSegInfo (&D);
2924 ProcessFileInfo (&D);
2925 ProcessLineInfo (&D);
2926 ProcessSymInfo (&D);
2933 /* Return the debug info struct that was created */
2939 void cc65_free_dbginfo (cc65_dbginfo Handle)
2940 /* Free debug information read from a file */
2943 FreeDbgInfo (Handle);
2949 cc65_lineinfo* cc65_lineinfo_byaddr (cc65_dbginfo Handle, unsigned long Addr)
2950 /* Return line information for the given address. The function returns 0
2951 * if no line information was found.
2954 LineInfoListEntry* E;
2955 cc65_lineinfo* D = 0;
2957 /* Check the parameter */
2958 assert (Handle != 0);
2960 /* Search in the line infos for address */
2961 E = FindLineInfoByAddr (&((DbgInfo*) Handle)->LineInfoByAddr, Addr);
2963 /* Do we have line infos? */
2968 /* Prepare the struct we will return to the caller */
2969 D = xmalloc (sizeof (*D) + (E->Count - 1) * sizeof (D->data[0]));
2970 D->count = E->Count;
2971 if (E->Count == 1) {
2972 CopyLineInfo (D->data, E->Data);
2974 for (I = 0; I < D->count; ++I) {
2976 CopyLineInfo (D->data + I, ((LineInfo**) E->Data)[I]);
2981 /* Return the struct we've created */
2987 cc65_lineinfo* cc65_lineinfo_byname (cc65_dbginfo Handle, const char* FileName,
2989 /* Return line information for a file/line number combination. The function
2990 * returns NULL if no line information was found.
2998 /* Check the parameter */
2999 assert (Handle != 0);
3001 /* The handle is actually a pointer to a debug info struct */
3002 Info = (DbgInfo*) Handle;
3004 /* Get the file info */
3005 F = FindFileInfoByName (&Info->FileInfoByName, FileName);
3007 /* File not found */
3011 /* Search in the file for the given line */
3012 L = FindLineInfoByLine (F, Line);
3014 /* Line not found */
3018 /* Prepare the struct we will return to the caller */
3019 D = xmalloc (sizeof (*D));
3023 CopyLineInfo (D->data, L);
3025 /* Return the allocated struct */
3031 void cc65_free_lineinfo (cc65_dbginfo Handle, cc65_lineinfo* Info)
3032 /* Free line info returned by one of the other functions */
3034 /* Just for completeness, check the handle */
3035 assert (Handle != 0);
3037 /* Just free the memory */
3043 cc65_sourceinfo* cc65_get_sourcelist (cc65_dbginfo Handle)
3044 /* Return a list of all source files */
3047 Collection* FileInfoByName;
3051 /* Check the parameter */
3052 assert (Handle != 0);
3054 /* The handle is actually a pointer to a debug info struct */
3055 Info = (DbgInfo*) Handle;
3057 /* Get a pointer to the file list */
3058 FileInfoByName = &Info->FileInfoByName;
3060 /* Allocate memory for the data structure returned to the caller */
3061 D = xmalloc (sizeof (*D) - sizeof (D->data[0]) +
3062 CollCount (FileInfoByName) * sizeof (D->data[0]));
3064 /* Fill in the data */
3065 D->count = CollCount (FileInfoByName);
3066 for (I = 0; I < CollCount (FileInfoByName); ++I) {
3069 const FileInfo* F = CollAt (FileInfoByName, I);
3072 D->data[I].source_name = F->FileName;
3073 D->data[I].source_size = F->Size;
3074 D->data[I].source_mtime = F->MTime;
3077 /* Return the result */
3083 void cc65_free_sourceinfo (cc65_dbginfo Handle, cc65_sourceinfo* Info)
3084 /* Free a source info record */
3086 /* Just for completeness, check the handle */
3087 assert (Handle != 0);
3089 /* Free the memory */
3095 cc65_segmentinfo* cc65_get_segmentlist (cc65_dbginfo Handle)
3096 /* Return a list of all segments referenced in the debug information */
3099 Collection* SegInfoByName;
3100 cc65_segmentinfo* D;
3103 /* Check the parameter */
3104 assert (Handle != 0);
3106 /* The handle is actually a pointer to a debug info struct */
3107 Info = (DbgInfo*) Handle;
3109 /* Get a pointer to the segment list */
3110 SegInfoByName = &Info->SegInfoByName;
3112 /* Allocate memory for the data structure returned to the caller */
3113 D = xmalloc (sizeof (*D) - sizeof (D->data[0]) +
3114 CollCount (SegInfoByName) * sizeof (D->data[0]));
3116 /* Fill in the data */
3117 D->count = CollCount (SegInfoByName);
3118 for (I = 0; I < CollCount (SegInfoByName); ++I) {
3121 const SegInfo* S = CollAt (SegInfoByName, I);
3124 D->data[I].segment_name = S->SegName;
3125 D->data[I].segment_start = S->Start;
3126 D->data[I].segment_size = S->Size;
3127 D->data[I].output_name = S->OutputName;
3128 D->data[I].output_offs = S->OutputOffs;
3131 /* Return the result */
3137 void cc65_free_segmentinfo (cc65_dbginfo Handle, cc65_segmentinfo* Info)
3138 /* Free a segment info record */
3140 /* Just for completeness, check the handle */
3141 assert (Handle != 0);
3143 /* Free the memory */
3149 cc65_symbolinfo* cc65_symbol_byname (cc65_dbginfo Handle, const char* Name)
3150 /* Return a list of symbols with a given name. The function returns NULL if
3151 * no symbol with this name was found.
3155 Collection* SymInfoByName;
3161 /* Check the parameter */
3162 assert (Handle != 0);
3164 /* The handle is actually a pointer to a debug info struct */
3165 Info = (DbgInfo*) Handle;
3167 /* Get a pointer to the symbol list */
3168 SymInfoByName = &Info->SymInfoByName;
3170 /* Search for the symbol */
3171 if (!FindSymInfoByName (SymInfoByName, Name, &Index)) {
3176 /* Index contains the position. Count how many symbols with this name
3177 * we have. Skip the first one, since we have at least one.
3180 while ((unsigned) Index + Count < CollCount (SymInfoByName)) {
3181 const SymInfo* S = CollAt (SymInfoByName, (unsigned) Index + Count);
3182 if (strcmp (S->SymName, Name) != 0) {
3188 /* Allocate memory for the data structure returned to the caller */
3189 D = xmalloc (sizeof (*D) + (Count - 1) * sizeof (D->data[0]));
3191 /* Fill in the data */
3193 for (I = 0; I < Count; ++I) {
3195 CopySymInfo (D->data + I, CollAt (SymInfoByName, Index++));
3198 /* Return the result */
3204 cc65_symbolinfo* cc65_symbol_inrange (cc65_dbginfo Handle, cc65_addr Start, cc65_addr End)
3205 /* Return a list of labels in the given range. End is inclusive. The function
3206 * return NULL if no symbols within the given range are found. Non label
3207 * symbols are ignored and not returned.
3211 Collection* SymInfoByVal;
3212 Collection SymInfoList = COLLECTION_INITIALIZER;
3217 /* Check the parameter */
3218 assert (Handle != 0);
3220 /* The handle is actually a pointer to a debug info struct */
3221 Info = (DbgInfo*) Handle;
3223 /* Get a pointer to the symbol list */
3224 SymInfoByVal = &Info->SymInfoByVal;
3226 /* Search for the symbol. Because we're searching for a range, we cannot
3227 * make use of the function result.
3229 FindSymInfoByValue (SymInfoByVal, Start, &Index);
3231 /* Start from the given index, check all symbols until the end address is
3232 * reached. Place all symbols into SymInfoList for later.
3234 for (I = Index; I < CollCount (SymInfoByVal); ++I) {
3237 SymInfo* Item = CollAt (SymInfoByVal, I);
3239 /* The collection is sorted by address, so if we get a value larger
3240 * than the end address, we're done.
3242 if (Item->Value > (long) End) {
3246 /* Ignore non-labels */
3247 if (Item->Type != CC65_SYM_LABEL) {
3251 /* Ok, remember this one */
3252 CollAppend (&SymInfoList, Item);
3255 /* If we don't have any labels within the range, bail out. No memory has
3256 * been allocated for SymInfoList.
3258 if (CollCount (&SymInfoList) == 0) {
3262 /* Allocate memory for the data structure returned to the caller */
3263 D = xmalloc (sizeof (*D) + (CollCount (&SymInfoList)- 1) * sizeof (D->data[0]));
3265 /* Fill in the data */
3266 D->count = CollCount (&SymInfoList);
3267 for (I = 0; I < CollCount (&SymInfoList); ++I) {
3269 CopySymInfo (D->data + I, CollAt (&SymInfoList, I));
3272 /* Free the collection */
3273 DoneCollection (&SymInfoList);
3275 /* Return the result */
3281 void cc65_free_symbolinfo (cc65_dbginfo Handle, cc65_symbolinfo* Info)
3282 /* Free a symbol info record */
3284 /* Just for completeness, check the handle */
3285 assert (Handle != 0);
3287 /* Free the memory */