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 */
131 TOK_ABSOLUTE = TOK_FIRST_KEYWORD, /* ABSOLUTE keyword */
132 TOK_ADDRSIZE, /* ADDRSIZE keyword */
133 TOK_COUNT, /* COUNT keyword */
134 TOK_EQUATE, /* EQUATE keyword */
135 TOK_FILE, /* FILE keyword */
136 TOK_ID, /* ID keyword */
137 TOK_LABEL, /* LABEL keyword */
138 TOK_LINE, /* LINE keyword */
139 TOK_LONG, /* LONG_keyword */
140 TOK_MAJOR, /* MAJOR keyword */
141 TOK_MINOR, /* MINOR keyword */
142 TOK_MTIME, /* MTIME keyword */
143 TOK_NAME, /* NAME keyword */
144 TOK_OUTPUTNAME, /* OUTPUTNAME keyword */
145 TOK_OUTPUTOFFS, /* OUTPUTOFFS keyword */
146 TOK_RANGE, /* RANGE keyword */
147 TOK_RO, /* RO keyword */
148 TOK_RW, /* RW keyword */
149 TOK_SEGMENT, /* SEGMENT keyword */
150 TOK_SIZE, /* SIZE keyword */
151 TOK_START, /* START keyword */
152 TOK_SYM, /* SYM keyword */
153 TOK_TYPE, /* TYPE keyword */
154 TOK_VALUE, /* VALUE keyword */
155 TOK_VERSION, /* VERSION keyword */
156 TOK_ZEROPAGE, /* ZEROPAGE keyword */
157 TOK_LAST_KEYWORD = TOK_ZEROPAGE,
159 TOK_IDENT, /* To catch unknown keywords */
162 /* Data used when parsing the debug info file */
163 typedef struct InputData InputData;
165 const char* FileName; /* Name of input file */
166 cc65_line Line; /* Current line number */
167 unsigned Col; /* Current column number */
168 cc65_line SLine; /* Line number at start of token */
169 unsigned SCol; /* Column number at start of token */
170 unsigned Errors; /* Number of errors */
171 FILE* F; /* Input file */
172 int C; /* Input character */
173 Token Tok; /* Token from input stream */
174 unsigned long IVal; /* Integer constant */
175 StrBuf SVal; /* String constant */
176 cc65_errorfunc Error; /* Function called in case of errors */
177 unsigned MajorVersion; /* Major version number */
178 unsigned MinorVersion; /* Minor version number */
179 DbgInfo* Info; /* Pointer to debug info */
182 /* Internally used segment info struct */
183 typedef struct SegInfo SegInfo;
185 unsigned Id; /* Id of segment */
186 cc65_addr Start; /* Start address of segment */
187 cc65_addr Size; /* Size of segment */
188 char* OutputName; /* Name of output file */
189 unsigned long OutputOffs; /* Offset in output file */
190 char SegName[1]; /* Name of segment */
193 /* Internally used file info struct */
194 typedef struct FileInfo FileInfo;
196 unsigned Id; /* Id of file */
197 unsigned long Size; /* Size of file */
198 unsigned long MTime; /* Modification time */
199 Collection LineInfoByLine; /* Line infos sorted by line */
200 char FileName[1]; /* Name of file with full path */
203 /* Internally used line info struct */
204 typedef struct LineInfo LineInfo;
206 cc65_addr Start; /* Start of data range */
207 cc65_addr End; /* End of data range */
208 cc65_line Line; /* Line number */
210 unsigned Id; /* Id of file */
211 FileInfo* Info; /* Pointer to file info */
214 unsigned Id; /* Id of segment */
215 SegInfo* Info; /* Pointer to segment info */
217 cc65_line_type Type; /* Type of line */
218 unsigned Count; /* Nesting counter for macros */
221 /* Internally used symbol info struct */
222 typedef struct SymInfo SymInfo;
224 cc65_symbol_type Type; /* Type of symbol */
225 long Value; /* Value of symbol */
226 cc65_size Size; /* Size of symbol */
227 char SymName[1]; /* Name of symbol */
232 /*****************************************************************************/
234 /*****************************************************************************/
238 static void NextToken (InputData* D);
239 /* Read the next token from the input stream */
243 /*****************************************************************************/
244 /* Memory allocation */
245 /*****************************************************************************/
249 static void* xmalloc (size_t Size)
250 /* Allocate memory, check for out of memory condition. Do some debugging */
254 /* Allow zero sized requests and return NULL in this case */
257 /* Allocate memory */
260 /* Check for errors */
264 /* Return a pointer to the block */
270 static void* xrealloc (void* P, size_t Size)
271 /* Reallocate a memory block, check for out of memory */
273 /* Reallocate the block */
274 void* N = realloc (P, Size);
276 /* Check for errors */
277 assert (N != 0 || Size == 0);
279 /* Return the pointer to the new block */
285 static void xfree (void* Block)
286 /* Free the block, do some debugging */
293 static cc65_lineinfo* new_cc65_lineinfo (unsigned Count)
294 /* Allocate and return a cc65_lineinfo struct that is able to hold Count
295 * entries. Initialize the count field of the returned struct.
298 cc65_lineinfo* L = xmalloc (sizeof (*L) - sizeof (L->data[0]) +
299 Count * sizeof (L->data[0]));
306 static cc65_sourceinfo* new_cc65_sourceinfo (unsigned Count)
307 /* Allocate and return a cc65_sourceinfo struct that is able to hold Count
308 * entries. Initialize the count field of the returned struct.
311 cc65_sourceinfo* S = xmalloc (sizeof (*S) - sizeof (S->data[0]) +
312 Count * sizeof (S->data[0]));
319 static cc65_segmentinfo* new_cc65_segmentinfo (unsigned Count)
320 /* Allocate and return a cc65_segmentinfo struct that is able to hold Count
321 * entries. Initialize the count field of the returned struct.
324 cc65_segmentinfo* S = xmalloc (sizeof (*S) - sizeof (S->data[0]) +
325 Count * sizeof (S->data[0]));
332 static cc65_symbolinfo* new_cc65_symbolinfo (unsigned Count)
333 /* Allocate and return a cc65_symbolinfo struct that is able to hold Count
334 * entries. Initialize the count field of the returned struct.
337 cc65_symbolinfo* S = xmalloc (sizeof (*S) - sizeof (S->data[0]) +
338 Count * sizeof (S->data[0]));
345 /*****************************************************************************/
346 /* Dynamic strings */
347 /*****************************************************************************/
351 static void SB_Done (StrBuf* B)
352 /* Free the data of a string buffer (but not the struct itself) */
361 static void SB_Realloc (StrBuf* B, unsigned NewSize)
362 /* Reallocate the string buffer space, make sure at least NewSize bytes are
366 /* Get the current size, use a minimum of 8 bytes */
367 unsigned NewAllocated = B->Allocated;
368 if (NewAllocated == 0) {
372 /* Round up to the next power of two */
373 while (NewAllocated < NewSize) {
377 /* Reallocate the buffer. Beware: The allocated size may be zero while the
378 * length is not. This means that we have a buffer that wasn't allocated
382 /* Just reallocate the block */
383 B->Buf = xrealloc (B->Buf, NewAllocated);
385 /* Allocate a new block and copy */
386 B->Buf = memcpy (xmalloc (NewAllocated), B->Buf, B->Len);
389 /* Remember the new block size */
390 B->Allocated = NewAllocated;
395 static void SB_CheapRealloc (StrBuf* B, unsigned NewSize)
396 /* Reallocate the string buffer space, make sure at least NewSize bytes are
397 * available. This function won't copy the old buffer contents over to the new
398 * buffer and may be used if the old contents are overwritten later.
401 /* Get the current size, use a minimum of 8 bytes */
402 unsigned NewAllocated = B->Allocated;
403 if (NewAllocated == 0) {
407 /* Round up to the next power of two */
408 while (NewAllocated < NewSize) {
412 /* Free the old buffer if there is one */
417 /* Allocate a fresh block */
418 B->Buf = xmalloc (NewAllocated);
420 /* Remember the new block size */
421 B->Allocated = NewAllocated;
426 static unsigned SB_GetLen (const StrBuf* B)
427 /* Return the length of the buffer contents */
434 static const char* SB_GetConstBuf (const StrBuf* B)
435 /* Return a buffer pointer */
442 static void SB_Terminate (StrBuf* B)
443 /* Zero terminate the given string buffer. NOTE: The terminating zero is not
444 * accounted for in B->Len, if you want that, you have to use AppendChar!
447 unsigned NewLen = B->Len + 1;
448 if (NewLen > B->Allocated) {
449 SB_Realloc (B, NewLen);
451 B->Buf[B->Len] = '\0';
456 static void SB_Clear (StrBuf* B)
457 /* Clear the string buffer (make it empty) */
464 static void SB_CopyBuf (StrBuf* Target, const char* Buf, unsigned Size)
465 /* Copy Buf to Target, discarding the old contents of Target */
468 if (Target->Allocated < Size) {
469 SB_CheapRealloc (Target, Size);
471 memcpy (Target->Buf, Buf, Size);
478 static void SB_Copy (StrBuf* Target, const StrBuf* Source)
479 /* Copy Source to Target, discarding the old contents of Target */
481 SB_CopyBuf (Target, Source->Buf, Source->Len);
486 static void SB_AppendChar (StrBuf* B, int C)
487 /* Append a character to a string buffer */
489 unsigned NewLen = B->Len + 1;
490 if (NewLen > B->Allocated) {
491 SB_Realloc (B, NewLen);
493 B->Buf[B->Len] = (char) C;
499 static char* SB_StrDup (const StrBuf* B)
500 /* Return the contents of B as a dynamically allocated string. The string
501 * will always be NUL terminated.
504 /* Allocate memory */
505 char* S = xmalloc (B->Len + 1);
507 /* Copy the string */
508 memcpy (S, B->Buf, B->Len);
513 /* And return the result */
519 /*****************************************************************************/
521 /*****************************************************************************/
525 static Collection* InitCollection (Collection* C)
526 /* Initialize a collection and return it. */
528 /* Intialize the fields. */
533 /* Return the new struct */
539 static void DoneCollection (Collection* C)
540 /* Free the data for a collection. This will not free the data contained in
544 /* Free the pointer array */
547 /* Clear the fields, so the collection may be reused (or DoneCollection
557 static unsigned CollCount (const Collection* C)
558 /* Return the number of items in the collection */
565 static void CollGrow (Collection* C, unsigned Size)
566 /* Grow the collection C so it is able to hold Size items without a resize
567 * being necessary. This can be called for performance reasons if the number
568 * of items to be placed in the collection is known in advance.
573 /* Ignore the call if the collection is already large enough */
574 if (Size <= C->Size) {
578 /* Grow the collection */
580 NewItems = xmalloc (C->Size * sizeof (void*));
581 memcpy (NewItems, C->Items, C->Count * sizeof (void*));
588 static void CollInsert (Collection* C, void* Item, unsigned Index)
589 /* Insert the data at the given position in the collection */
591 /* Check for invalid indices */
592 assert (Index <= C->Count);
594 /* Grow the array if necessary */
595 if (C->Count >= C->Size) {
597 CollGrow (C, (C->Size == 0)? 8 : C->Size * 2);
600 /* Move the existing elements if needed */
601 if (C->Count != Index) {
602 memmove (C->Items+Index+1, C->Items+Index, (C->Count-Index) * sizeof (void*));
606 /* Store the new item */
607 C->Items[Index] = Item;
612 static void CollAppend (Collection* C, void* Item)
613 /* Append an item to the end of the collection */
615 /* Insert the item at the end of the current list */
616 CollInsert (C, Item, C->Count);
621 static void* CollAt (Collection* C, unsigned Index)
622 /* Return the item at the given index */
624 /* Check the index */
625 assert (Index < C->Count);
627 /* Return the element */
628 return C->Items[Index];
633 static void* CollFirst (Collection* C)
634 /* Return the first item in a collection */
636 /* We must have at least one entry */
637 assert (C->Count > 0);
639 /* Return the element */
645 static void CollDelete (Collection* C, unsigned Index)
646 /* Remove the item with the given index from the collection. This will not
647 * free the item itself, just the pointer. All items with higher indices
648 * will get moved to a lower position.
651 /* Check the index */
652 assert (Index < C->Count);
654 /* Remove the item pointer */
656 memmove (C->Items+Index, C->Items+Index+1, (C->Count-Index) * sizeof (void*));
661 static void CollQuickSort (Collection* C, int Lo, int Hi,
662 int (*Compare) (const void*, const void*))
663 /* Internal recursive sort function. */
665 /* Get a pointer to the items */
666 void** Items = C->Items;
673 while (I <= J && Compare (Items[Lo], Items[I]) >= 0) {
676 while (I <= J && Compare (Items[Lo], Items[J]) < 0) {
681 void* Tmp = Items[I];
690 void* Tmp = Items[J];
691 Items[J] = Items[Lo];
694 if (J > (Hi + Lo) / 2) {
695 CollQuickSort (C, J + 1, Hi, Compare);
698 CollQuickSort (C, Lo, J - 1, Compare);
706 void CollSort (Collection* C, int (*Compare) (const void*, const void*))
707 /* Sort the collection using the given compare function. */
710 CollQuickSort (C, 0, C->Count-1, Compare);
716 /*****************************************************************************/
717 /* Debugging stuff */
718 /*****************************************************************************/
725 #define DBGPRINT(format, ...) printf ((format), __VA_ARGS__)
729 static void DumpFileInfo (Collection* FileInfos)
730 /* Dump a list of file infos */
735 for (I = 0; I < CollCount (FileInfos); ++I) {
736 const FileInfo* FI = CollAt (FileInfos, I);
737 printf ("File info %u:\n"
743 (unsigned long) FI->Size,
744 (unsigned long) FI->MTime);
750 static void DumpOneLineInfo (unsigned Num, LineInfo* LI)
751 /* Dump one line info entry */
753 printf (" Index: %u\n"
756 " Range: 0x%06lX-0x%06lX\n"
760 LI->File.Info->FileName,
761 (unsigned long) LI->Line,
762 (unsigned long) LI->Start,
763 (unsigned long) LI->End,
770 static void DumpLineInfo (LineInfoList* L)
771 /* Dump a list of line infos */
776 for (I = 0; I < L->Count; ++I) {
777 const LineInfoListEntry* E = &L->List[I];
778 printf ("Addr: %lu\n", (unsigned long) E->Addr);
780 DumpOneLineInfo (0, E->Data);
782 for (J = 0; J < E->Count; ++J) {
783 DumpOneLineInfo (J, ((LineInfo**) E->Data)[J]);
791 static void DumpData (InputData* D)
792 /* Dump internal data to stdout for debugging */
795 DumpFileInfo (&D->Info->FileInfoById);
796 DumpLineInfo (&D->Info->LineInfoByAddr);
801 #define DBGPRINT(format, ...)
809 /*****************************************************************************/
811 /*****************************************************************************/
815 static SegInfo* NewSegInfo (const StrBuf* SegName, unsigned Id,
816 cc65_addr Start, cc65_addr Size,
817 const StrBuf* OutputName, unsigned long OutputOffs)
818 /* Create a new SegInfo struct and return it */
820 /* Allocate memory */
821 SegInfo* S = xmalloc (sizeof (SegInfo) + SB_GetLen (SegName));
827 if (SB_GetLen (OutputName) > 0) {
828 /* Output file given */
829 S->OutputName = SB_StrDup (OutputName);
830 S->OutputOffs = OutputOffs;
832 /* No output file given */
836 memcpy (S->SegName, SB_GetConstBuf (SegName), SB_GetLen (SegName) + 1);
844 static void FreeSegInfo (SegInfo* S)
845 /* Free a SegInfo struct */
847 xfree (S->OutputName);
853 static int CompareSegInfoByName (const void* L, const void* R)
854 /* Helper function to sort segment infos in a collection by name */
856 /* Sort by file name */
857 return strcmp (((const SegInfo*) L)->SegName,
858 ((const SegInfo*) R)->SegName);
863 static int CompareSegInfoById (const void* L, const void* R)
864 /* Helper function to sort segment infos in a collection by id */
866 if (((const SegInfo*) L)->Id > ((const SegInfo*) R)->Id) {
868 } else if (((const SegInfo*) L)->Id < ((const SegInfo*) R)->Id) {
877 /*****************************************************************************/
879 /*****************************************************************************/
883 static LineInfo* NewLineInfo (unsigned File, unsigned Seg, cc65_line Line,
884 cc65_addr Start, cc65_addr End,
885 cc65_line_type Type, unsigned Count)
886 /* Create a new LineInfo struct and return it */
888 /* Allocate memory */
889 LineInfo* L = xmalloc (sizeof (LineInfo));
906 static void FreeLineInfo (LineInfo* L)
907 /* Free a LineInfo struct */
914 static int CompareLineInfoByAddr (const void* L, const void* R)
915 /* Helper function to sort line infos in a collection by address. Line infos
916 * with smaller start address are considered smaller. If start addresses are
917 * equal, line infos with smaller end address are considered smaller. This
918 * means, that when CompareLineInfoByAddr is used for sorting, a range with
919 * identical start addresses will have smaller ranges first, followed by
923 /* Sort by start of range */
924 if (((const LineInfo*) L)->Start > ((const LineInfo*) R)->Start) {
926 } else if (((const LineInfo*) L)->Start < ((const LineInfo*) R)->Start) {
928 } else if (((const LineInfo*) L)->End > ((const LineInfo*) R)->End) {
930 } else if (((const LineInfo*) L)->End < ((const LineInfo*) R)->End) {
939 static int CompareLineInfoByLine (const void* L, const void* R)
940 /* Helper function to sort line infos in a collection by line. If the line
941 * is identical, sort by the address of the range.
944 if (((const LineInfo*) L)->Line > ((const LineInfo*) R)->Line) {
946 } else if (((const LineInfo*) L)->Line < ((const LineInfo*) R)->Line) {
949 return CompareLineInfoByAddr (L, R);
955 /*****************************************************************************/
957 /*****************************************************************************/
961 static FileInfo* NewFileInfo (const StrBuf* FileName, unsigned Id,
962 unsigned long Size, unsigned long MTime)
963 /* Create a new FileInfo struct and return it */
965 /* Allocate memory */
966 FileInfo* F = xmalloc (sizeof (FileInfo) + SB_GetLen (FileName));
972 InitCollection (&F->LineInfoByLine);
973 memcpy (F->FileName, SB_GetConstBuf (FileName), SB_GetLen (FileName) + 1);
981 static void FreeFileInfo (FileInfo* F)
982 /* Free a FileInfo struct */
984 /* Delete the collection with the line infos */
985 DoneCollection (&F->LineInfoByLine);
987 /* Free the file info structure itself */
993 static int CompareFileInfoByName (const void* L, const void* R)
994 /* Helper function to sort file infos in a collection by name */
996 /* Sort by file name. If names are equal, sort by timestamp,
997 * then sort by size. Which means, identical files will go
1000 int Res = strcmp (((const FileInfo*) L)->FileName,
1001 ((const FileInfo*) R)->FileName);
1005 if (((const FileInfo*) L)->MTime > ((const FileInfo*) R)->MTime) {
1007 } else if (((const FileInfo*) L)->MTime < ((const FileInfo*) R)->MTime) {
1010 if (((const FileInfo*) L)->Size > ((const FileInfo*) R)->Size) {
1012 } else if (((const FileInfo*) L)->Size < ((const FileInfo*) R)->Size) {
1021 static int CompareFileInfoById (const void* L, const void* R)
1022 /* Helper function to sort file infos in a collection by id */
1024 if (((const FileInfo*) L)->Id > ((const FileInfo*) R)->Id) {
1026 } else if (((const FileInfo*) L)->Id < ((const FileInfo*) R)->Id) {
1035 /*****************************************************************************/
1037 /*****************************************************************************/
1041 static SymInfo* NewSymInfo (const StrBuf* Name, long Val,
1042 cc65_symbol_type Type, cc65_size Size)
1043 /* Create a new SymInfo struct, intialize and return it */
1045 /* Allocate memory */
1046 SymInfo* S = xmalloc (sizeof (SymInfo) + SB_GetLen (Name));
1052 memcpy (S->SymName, SB_GetConstBuf (Name), SB_GetLen (Name) + 1);
1060 static void FreeSymInfo (SymInfo* S)
1061 /* Free a SymInfo struct */
1068 static int CompareSymInfoByName (const void* L, const void* R)
1069 /* Helper function to sort symbol infos in a collection by name */
1071 /* Sort by symbol name */
1072 return strcmp (((const SymInfo*) L)->SymName,
1073 ((const SymInfo*) R)->SymName);
1078 static int CompareSymInfoByVal (const void* L, const void* R)
1079 /* Helper function to sort symbol infos in a collection by value */
1081 /* Sort by symbol value. If both are equal, sort by symbol name so it
1082 * looks nice when such a list is returned.
1084 if (((const SymInfo*) L)->Value > ((const SymInfo*) R)->Value) {
1086 } else if (((const SymInfo*) L)->Value < ((const SymInfo*) R)->Value) {
1089 return CompareSymInfoByName (L, R);
1095 /*****************************************************************************/
1097 /*****************************************************************************/
1101 static void InitLineInfoList (LineInfoList* L)
1102 /* Initialize a line info list */
1110 static void CreateLineInfoList (LineInfoList* L, Collection* LineInfos)
1111 /* Create a LineInfoList from a Collection with line infos. The collection
1112 * must be sorted by ascending start addresses.
1117 LineInfoListEntry* List;
1118 unsigned StartIndex;
1123 /* Initialize and check if there's something to do */
1126 if (CollCount (LineInfos) == 0) {
1131 /* Step 1: Determine the number of unique address entries needed */
1132 LI = CollFirst (LineInfos);
1133 L->Count += (LI->End - LI->Start) + 1;
1135 for (I = 1; I < CollCount (LineInfos); ++I) {
1137 /* Get next entry */
1138 LI = CollAt (LineInfos, I);
1140 /* Check for additional unique addresses in this line info */
1141 if (LI->Start > End) {
1142 L->Count += (LI->End - LI->Start) + 1;
1144 } else if (LI->End > End) {
1145 L->Count += (LI->End - End);
1151 /* Step 2: Allocate memory and initialize it */
1152 L->List = List = xmalloc (L->Count * sizeof (*List));
1153 for (I = 0; I < L->Count; ++I) {
1158 /* Step 3: Determine the number of entries per unique address */
1160 LI = CollFirst (LineInfos);
1164 for (J = StartIndex, Addr = LI->Start; Addr <= LI->End; ++J, ++Addr) {
1165 List[J].Addr = Addr;
1168 for (I = 1; I < CollCount (LineInfos); ++I) {
1170 /* Get next entry */
1171 LI = CollAt (LineInfos, I);
1173 /* Determine the start index of the next range. Line infos are sorted
1174 * by ascending start address, so the start address of the next entry
1175 * is always larger than the previous one - we don't need to check
1178 if (LI->Start <= End) {
1179 /* Range starts within out already known linear range */
1180 StartIndex += (unsigned) (LI->Start - Start);
1182 if (LI->End > End) {
1186 /* Range starts after the already known */
1187 StartIndex += (unsigned) (End - Start) + 1;
1191 for (J = StartIndex, Addr = LI->Start; Addr <= LI->End; ++J, ++Addr) {
1192 List[J].Addr = Addr;
1197 /* Step 4: Allocate memory for the indirect tables */
1198 for (I = 0, List = L->List; I < L->Count; ++I, ++List) {
1200 /* For a count of 1, we store the pointer to the lineinfo for this
1201 * address in the Data pointer directly. For counts > 1, we allocate
1202 * an array of pointers and reset the counter, so we can use it as
1203 * an index later. This is dangerous programming since it disables
1204 * all possible checks!
1206 if (List->Count > 1) {
1207 List->Data = xmalloc (List->Count * sizeof (LineInfo*));
1212 /* Step 5: Enter the data into the table */
1214 LI = CollFirst (LineInfos);
1218 for (J = StartIndex, Addr = LI->Start; Addr <= LI->End; ++J, ++Addr) {
1219 assert (List[J].Addr == Addr);
1220 if (List[J].Count == 1 && List[J].Data == 0) {
1223 ((LineInfo**) List[J].Data)[List[J].Count++] = LI;
1226 for (I = 1; I < CollCount (LineInfos); ++I) {
1228 /* Get next entry */
1229 LI = CollAt (LineInfos, I);
1231 /* Determine the start index of the next range. Line infos are sorted
1232 * by ascending start address, so the start address of the next entry
1233 * is always larger than the previous one - we don't need to check
1236 if (LI->Start <= End) {
1237 /* Range starts within out already known linear range */
1238 StartIndex += (unsigned) (LI->Start - Start);
1240 if (LI->End > End) {
1244 /* Range starts after the already known */
1245 StartIndex += (unsigned) (End - Start) + 1;
1249 for (J = StartIndex, Addr = LI->Start; Addr <= LI->End; ++J, ++Addr) {
1250 assert (List[J].Addr == Addr);
1251 if (List[J].Count == 1 && List[J].Data == 0) {
1254 ((LineInfo**) List[J].Data)[List[J].Count++] = LI;
1262 static void DoneLineInfoList (LineInfoList* L)
1263 /* Delete the contents of a line info list */
1267 /* Delete the line info and the indirect data */
1268 for (I = 0; I < L->Count; ++I) {
1270 /* Get a pointer to the entry */
1271 LineInfoListEntry* E = &L->List[I];
1273 /* Check for indirect memory */
1275 /* LineInfo addressed indirectly */
1280 /* Delete the list */
1286 /*****************************************************************************/
1288 /*****************************************************************************/
1292 static DbgInfo* NewDbgInfo (void)
1293 /* Create a new DbgInfo struct and return it */
1295 /* Allocate memory */
1296 DbgInfo* Info = xmalloc (sizeof (DbgInfo));
1299 InitCollection (&Info->SegInfoByName);
1300 InitCollection (&Info->SegInfoById);
1301 InitCollection (&Info->FileInfoByName);
1302 InitCollection (&Info->FileInfoById);
1303 InitCollection (&Info->LineInfos);
1304 InitLineInfoList (&Info->LineInfoByAddr);
1305 InitCollection (&Info->SymInfoByName);
1306 InitCollection (&Info->SymInfoByVal);
1314 static void FreeDbgInfo (DbgInfo* Info)
1315 /* Free a DbgInfo struct */
1319 /* Free segment info */
1320 for (I = 0; I < CollCount (&Info->SegInfoByName); ++I) {
1321 FreeSegInfo (CollAt (&Info->SegInfoByName, I));
1323 DoneCollection (&Info->SegInfoByName);
1324 DoneCollection (&Info->SegInfoById);
1326 /* Free file info */
1327 for (I = 0; I < CollCount (&Info->FileInfoByName); ++I) {
1328 FreeFileInfo (CollAt (&Info->FileInfoByName, I));
1330 DoneCollection (&Info->FileInfoByName);
1331 DoneCollection (&Info->FileInfoById);
1333 /* Free line info */
1334 for (I = 0; I < CollCount (&Info->LineInfos); ++I) {
1335 FreeLineInfo (CollAt (&Info->LineInfos, I));
1337 DoneCollection (&Info->LineInfos);
1338 DoneLineInfoList (&Info->LineInfoByAddr);
1340 /* Free symbol info */
1341 for (I = 0; I < CollCount (&Info->SymInfoByName); ++I) {
1342 FreeSymInfo (CollAt (&Info->SymInfoByName, I));
1344 DoneCollection (&Info->SymInfoByName);
1345 DoneCollection (&Info->SymInfoByVal);
1347 /* Free the structure itself */
1353 /*****************************************************************************/
1354 /* Helper functions */
1355 /*****************************************************************************/
1359 static void CopyLineInfo (cc65_linedata* D, const LineInfo* L)
1360 /* Copy data from a LineInfo struct to the cc65_linedata struct returned to
1364 D->source_name = L->File.Info->FileName;
1365 D->source_size = L->File.Info->Size;
1366 D->source_mtime = L->File.Info->MTime;
1367 D->source_line = L->Line;
1368 D->line_start = L->Start;
1369 D->line_end = L->End;
1370 if (L->Seg.Info->OutputName) {
1371 D->output_name = L->Seg.Info->OutputName;
1372 D->output_offs = L->Seg.Info->OutputOffs + L->Start - L->Seg.Info->Start;
1377 D->line_type = L->Type;
1378 D->count = L->Count;
1383 static void CopySymInfo (cc65_symboldata* D, const SymInfo* S)
1384 /* Copy data from a SymInfo struct to the cc65_symboldata struct returned to
1388 D->symbol_name = S->SymName;
1389 D->symbol_type = S->Type;
1390 D->symbol_size = S->Size;
1391 D->symbol_value = S->Value;
1396 static void ParseError (InputData* D, cc65_error_severity Type, const char* Msg, ...)
1397 /* Call the user supplied parse error function */
1403 /* Test-format the error message so we know how much space to allocate */
1405 MsgSize = vsnprintf (0, 0, Msg, ap);
1408 /* Allocate memory */
1409 E = xmalloc (sizeof (*E) + MsgSize);
1411 /* Write data to E */
1413 E->name = D->FileName;
1415 E->column = D->SCol;
1417 vsnprintf (E->errormsg, MsgSize+1, Msg, ap);
1420 /* Call the caller:-) */
1423 /* Free the data structure */
1427 if (Type == CC65_ERROR) {
1434 static void SkipLine (InputData* D)
1435 /* Error recovery routine. Skip tokens until EOL or EOF is reached */
1437 while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1444 static void UnexpectedToken (InputData* D)
1445 /* Call ParseError with a message about an unexpected input token */
1447 ParseError (D, CC65_ERROR, "Unexpected input token %d", D->Tok);
1453 static void UnknownKeyword (InputData* D)
1454 /* Print a warning about an unknown keyword in the file. Try to do smart
1455 * recovery, so if later versions of the debug information add additional
1456 * keywords, this code may be able to at least ignore them.
1459 /* Output a warning */
1460 ParseError (D, CC65_WARNING, "Unknown keyword \"%s\" - skipping",
1461 SB_GetConstBuf (&D->SVal));
1463 /* Skip the identifier */
1466 /* If an equal sign follows, ignore anything up to the next line end
1467 * or comma. If a comma or line end follows, we're already done. If
1468 * we have none of both, we ignore the remainder of the line.
1470 if (D->Tok == TOK_EQUAL) {
1472 while (D->Tok != TOK_COMMA && D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1475 } else if (D->Tok != TOK_COMMA && D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1482 /*****************************************************************************/
1483 /* Scanner and parser */
1484 /*****************************************************************************/
1488 static int DigitVal (int C)
1489 /* Return the value for a numeric digit. Return -1 if C is invalid */
1493 } else if (isxdigit (C)) {
1494 return toupper (C) - 'A' + 10;
1502 static void NextChar (InputData* D)
1503 /* Read the next character from the input. Count lines and columns */
1505 /* Check if we've encountered EOF before */
1507 D->C = fgetc (D->F);
1519 static void NextToken (InputData* D)
1520 /* Read the next token from the input stream */
1522 static const struct KeywordEntry {
1523 const char Keyword[16];
1525 } KeywordTable[] = {
1526 { "absolute", TOK_ABSOLUTE },
1527 { "addrsize", TOK_ADDRSIZE },
1528 { "count", TOK_COUNT },
1529 { "equate", TOK_EQUATE },
1530 { "file", TOK_FILE },
1532 { "label", TOK_LABEL },
1533 { "line", TOK_LINE },
1534 { "long", TOK_LONG },
1535 { "major", TOK_MAJOR },
1536 { "minor", TOK_MINOR },
1537 { "mtime", TOK_MTIME },
1538 { "name", TOK_NAME },
1539 { "outputname", TOK_OUTPUTNAME },
1540 { "outputoffs", TOK_OUTPUTOFFS },
1541 { "range", TOK_RANGE },
1544 { "seg", TOK_SEGMENT },
1545 { "segment", TOK_SEGMENT },
1546 { "size", TOK_SIZE },
1547 { "start", TOK_START },
1549 { "type", TOK_TYPE },
1550 { "value", TOK_VALUE },
1551 { "version", TOK_VERSION },
1552 { "zeropage", TOK_ZEROPAGE },
1556 /* Skip whitespace */
1557 while (D->C == ' ' || D->C == '\t' || D->C == '\r') {
1561 /* Remember the current position as start of the next token */
1566 if (D->C == '_' || isalpha (D->C)) {
1568 const struct KeywordEntry* Entry;
1570 /* Read the identifier */
1571 SB_Clear (&D->SVal);
1572 while (D->C == '_' || isalnum (D->C)) {
1573 SB_AppendChar (&D->SVal, D->C);
1576 SB_Terminate (&D->SVal);
1578 /* Search the identifier in the keyword table */
1579 Entry = bsearch (SB_GetConstBuf (&D->SVal),
1581 sizeof (KeywordTable) / sizeof (KeywordTable[0]),
1582 sizeof (KeywordTable[0]),
1583 (int (*)(const void*, const void*)) strcmp);
1587 D->Tok = Entry->Tok;
1593 if (isdigit (D->C)) {
1598 if (toupper (D->C) == 'X') {
1608 while ((Val = DigitVal (D->C)) >= 0 && Val < Base) {
1609 D->IVal = D->IVal * Base + Val;
1612 D->Tok = TOK_INTCON;
1616 /* Other characters */
1640 SB_Clear (&D->SVal);
1643 if (D->C == '\n' || D->C == EOF) {
1644 ParseError (D, CC65_ERROR, "Unterminated string constant");
1651 SB_AppendChar (&D->SVal, D->C);
1654 SB_Terminate (&D->SVal);
1655 D->Tok = TOK_STRCON;
1668 ParseError (D, CC65_ERROR, "Invalid input character `%c'", D->C);
1675 static int TokenIsKeyword (Token Tok)
1676 /* Return true if the given token is a keyword */
1678 return (Tok >= TOK_FIRST_KEYWORD && Tok <= TOK_LAST_KEYWORD);
1683 static int TokenFollows (InputData* D, Token Tok, const char* Name)
1684 /* Check for a specific token that follows. */
1686 if (D->Tok != Tok) {
1687 ParseError (D, CC65_ERROR, "%s expected", Name);
1697 static int IntConstFollows (InputData* D)
1698 /* Check for an integer constant */
1700 return TokenFollows (D, TOK_INTCON, "Integer constant");
1705 static int StrConstFollows (InputData* D)
1706 /* Check for a string literal */
1708 return TokenFollows (D, TOK_STRCON, "String literal");
1713 static int Consume (InputData* D, Token Tok, const char* Name)
1714 /* Check for a token and consume it. Return true if the token was comsumed,
1715 * return false otherwise.
1718 if (TokenFollows (D, Tok, Name)) {
1728 static int ConsumeEqual (InputData* D)
1729 /* Consume an equal sign */
1731 return Consume (D, TOK_EQUAL, "'='");
1736 static int ConsumeMinus (InputData* D)
1737 /* Consume a minus sign */
1739 return Consume (D, TOK_MINUS, "'-'");
1744 static void ConsumeEOL (InputData* D)
1745 /* Consume an end-of-line token, if we aren't at end-of-file */
1747 if (D->Tok != TOK_EOF) {
1748 if (D->Tok != TOK_EOL) {
1749 ParseError (D, CC65_ERROR, "Extra tokens in line");
1758 static void ParseFile (InputData* D)
1759 /* Parse a FILE line */
1762 unsigned long Size = 0;
1763 unsigned long MTime = 0;
1764 StrBuf FileName = STRBUF_INITIALIZER;
1772 ibRequired = ibId | ibFileName | ibSize | ibMTime,
1773 } InfoBits = ibNone;
1775 /* Skip the FILE token */
1778 /* More stuff follows */
1783 /* Something we know? */
1784 if (D->Tok != TOK_ID && D->Tok != TOK_MTIME &&
1785 D->Tok != TOK_NAME && D->Tok != TOK_SIZE) {
1787 /* Try smart error recovery */
1788 if (D->Tok == TOK_IDENT || TokenIsKeyword (D->Tok)) {
1797 /* Remember the token, skip it, check for equal */
1800 if (!ConsumeEqual (D)) {
1804 /* Check what the token was */
1808 if (!IntConstFollows (D)) {
1817 if (!IntConstFollows (D)) {
1822 InfoBits |= ibMTime;
1826 if (!StrConstFollows (D)) {
1829 SB_Copy (&FileName, &D->SVal);
1830 SB_Terminate (&FileName);
1831 InfoBits |= ibFileName;
1836 if (!IntConstFollows (D)) {
1846 UnexpectedToken (D);
1852 if (D->Tok != TOK_COMMA) {
1858 /* Check for end of line */
1859 if (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1860 UnexpectedToken (D);
1865 /* Check for required information */
1866 if ((InfoBits & ibRequired) != ibRequired) {
1867 ParseError (D, CC65_ERROR, "Required attributes missing");
1871 /* Create the file info and remember it */
1872 F = NewFileInfo (&FileName, Id, Size, MTime);
1873 CollAppend (&D->Info->FileInfoByName, F);
1876 /* Entry point in case of errors */
1877 SB_Done (&FileName);
1883 static void ParseLine (InputData* D)
1884 /* Parse a LINE line */
1887 unsigned Segment = 0;
1889 cc65_addr Start = 0;
1891 cc65_line_type Type = CC65_LINE_ASM;
1902 ibRequired = ibFile | ibSegment | ibLine | ibRange,
1903 } InfoBits = ibNone;
1905 /* Skip the LINE token */
1908 /* More stuff follows */
1913 /* Something we know? */
1914 if (D->Tok != TOK_COUNT && D->Tok != TOK_FILE &&
1915 D->Tok != TOK_LINE && D->Tok != TOK_RANGE &&
1916 D->Tok != TOK_SEGMENT && D->Tok != TOK_TYPE) {
1918 /* Try smart error recovery */
1919 if (D->Tok == TOK_IDENT || TokenIsKeyword (D->Tok)) {
1928 /* Remember the token, skip it, check for equal */
1931 if (!ConsumeEqual (D)) {
1935 /* Check what the token was */
1939 if (!IntConstFollows (D)) {
1948 if (!IntConstFollows (D)) {
1951 Line = (cc65_line) D->IVal;
1957 if (!IntConstFollows (D)) {
1960 Start = (cc65_addr) D->IVal;
1962 if (!ConsumeMinus (D)) {
1965 if (!IntConstFollows (D)) {
1968 End = (cc65_addr) D->IVal;
1970 InfoBits |= ibRange;
1974 if (!IntConstFollows (D)) {
1978 InfoBits |= ibSegment;
1983 if (!IntConstFollows (D)) {
1986 Type = (cc65_line_type) D->IVal;
1992 if (!IntConstFollows (D)) {
1996 InfoBits |= ibCount;
2002 UnexpectedToken (D);
2008 if (D->Tok != TOK_COMMA) {
2014 /* Check for end of line */
2015 if (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
2016 UnexpectedToken (D);
2021 /* Check for required information */
2022 if ((InfoBits & ibRequired) != ibRequired) {
2023 ParseError (D, CC65_ERROR, "Required attributes missing");
2027 /* Create the line info and remember it */
2028 L = NewLineInfo (File, Segment, Line, Start, End, Type, Count);
2029 CollAppend (&D->Info->LineInfos, L);
2032 /* Entry point in case of errors */
2038 static void ParseSegment (InputData* D)
2039 /* Parse a SEGMENT line */
2042 cc65_addr Start = 0;
2044 StrBuf SegName = STRBUF_INITIALIZER;
2045 StrBuf OutputName = STRBUF_INITIALIZER;
2046 unsigned long OutputOffs = 0;
2058 ibRequired = ibId | ibSegName | ibStart | ibSize | ibAddrSize | ibType,
2059 } InfoBits = ibNone;
2061 /* Skip the SEGMENT token */
2064 /* More stuff follows */
2069 /* Something we know? */
2070 if (D->Tok != TOK_ADDRSIZE && D->Tok != TOK_ID &&
2071 D->Tok != TOK_NAME && D->Tok != TOK_OUTPUTNAME &&
2072 D->Tok != TOK_OUTPUTOFFS && D->Tok != TOK_SIZE &&
2073 D->Tok != TOK_START && D->Tok != TOK_TYPE) {
2075 /* Try smart error recovery */
2076 if (D->Tok == TOK_IDENT || TokenIsKeyword (D->Tok)) {
2084 /* Remember the token, skip it, check for equal */
2087 if (!ConsumeEqual (D)) {
2091 /* Check what the token was */
2096 InfoBits |= ibAddrSize;
2100 if (!IntConstFollows (D)) {
2109 if (!StrConstFollows (D)) {
2112 SB_Copy (&SegName, &D->SVal);
2113 SB_Terminate (&SegName);
2114 InfoBits |= ibSegName;
2118 case TOK_OUTPUTNAME:
2119 if (!StrConstFollows (D)) {
2122 SB_Copy (&OutputName, &D->SVal);
2123 SB_Terminate (&OutputName);
2124 InfoBits |= ibOutputName;
2128 case TOK_OUTPUTOFFS:
2129 if (!IntConstFollows (D)) {
2132 OutputOffs = D->IVal;
2134 InfoBits |= ibOutputOffs;
2138 if (!IntConstFollows (D)) {
2147 if (!IntConstFollows (D)) {
2150 Start = (cc65_addr) D->IVal;
2152 InfoBits |= ibStart;
2162 UnexpectedToken (D);
2168 if (D->Tok != TOK_COMMA) {
2174 /* Check for end of line */
2175 if (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
2176 UnexpectedToken (D);
2181 /* Check for required and/or matched information */
2182 if ((InfoBits & ibRequired) != ibRequired) {
2183 ParseError (D, CC65_ERROR, "Required attributes missing");
2186 InfoBits &= (ibOutputName | ibOutputOffs);
2187 if (InfoBits != ibNone && InfoBits != (ibOutputName | ibOutputOffs)) {
2188 ParseError (D, CC65_ERROR,
2189 "Attributes \"outputname\" and \"outputoffs\" must be paired");
2193 /* Fix OutputOffs if not given */
2194 if (InfoBits == ibNone) {
2198 /* Create the segment info and remember it */
2199 S = NewSegInfo (&SegName, Id, Start, Size, &OutputName, OutputOffs);
2200 CollAppend (&D->Info->SegInfoByName, S);
2203 /* Entry point in case of errors */
2205 SB_Done (&OutputName);
2211 static void ParseSym (InputData* D)
2212 /* Parse a SYM line */
2214 cc65_symbol_type Type;
2217 StrBuf SymName = STRBUF_INITIALIZER;
2226 ibRequired = ibSymName | ibValue | ibAddrSize | ibType,
2227 } InfoBits = ibNone;
2229 /* Skip the SYM token */
2232 /* More stuff follows */
2237 /* Something we know? */
2238 if (D->Tok != TOK_ADDRSIZE && D->Tok != TOK_NAME &&
2239 D->Tok != TOK_SIZE && D->Tok != TOK_TYPE &&
2240 D->Tok != TOK_VALUE) {
2242 /* Try smart error recovery */
2243 if (D->Tok == TOK_IDENT || TokenIsKeyword (D->Tok)) {
2252 /* Remember the token, skip it, check for equal */
2255 if (!ConsumeEqual (D)) {
2259 /* Check what the token was */
2264 InfoBits |= ibAddrSize;
2268 if (!StrConstFollows (D)) {
2271 SB_Copy (&SymName, &D->SVal);
2272 SB_Terminate (&SymName);
2273 InfoBits |= ibSymName;
2278 if (!IntConstFollows (D)) {
2281 Size = (cc65_size) D->IVal;
2289 Type = CC65_SYM_EQUATE;
2292 Type = CC65_SYM_LABEL;
2295 ParseError (D, CC65_ERROR,
2296 "Unknown value for attribute \"type\"");
2305 if (!IntConstFollows (D)) {
2309 InfoBits |= ibValue;
2315 UnexpectedToken (D);
2321 if (D->Tok != TOK_COMMA) {
2327 /* Check for end of line */
2328 if (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
2329 UnexpectedToken (D);
2334 /* Check for required and/or matched information */
2335 if ((InfoBits & ibRequired) != ibRequired) {
2336 ParseError (D, CC65_ERROR, "Required attributes missing");
2340 /* Create the symbol info and remember it */
2341 S = NewSymInfo (&SymName, Value, Type, Size);
2342 CollAppend (&D->Info->SymInfoByName, S);
2343 CollAppend (&D->Info->SymInfoByVal, S);
2346 /* Entry point in case of errors */
2353 static void ParseVersion (InputData* D)
2354 /* Parse a VERSION line */
2360 ibRequired = ibMajor | ibMinor,
2361 } InfoBits = ibNone;
2363 /* Skip the VERSION token */
2366 /* More stuff follows */
2367 while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
2373 if (!ConsumeEqual (D)) {
2376 if (!IntConstFollows (D)) {
2379 D->MajorVersion = D->IVal;
2381 InfoBits |= ibMajor;
2386 if (!ConsumeEqual (D)) {
2389 if (!IntConstFollows (D)) {
2392 D->MinorVersion = D->IVal;
2394 InfoBits |= ibMinor;
2398 /* Try to skip unknown keywords that may have been added by
2405 UnexpectedToken (D);
2410 /* Comma follows before next attribute */
2411 if (D->Tok == TOK_COMMA) {
2413 } else if (D->Tok == TOK_EOL || D->Tok == TOK_EOF) {
2416 UnexpectedToken (D);
2421 /* Check for required information */
2422 if ((InfoBits & ibRequired) != ibRequired) {
2423 ParseError (D, CC65_ERROR, "Required attributes missing");
2428 /* Entry point in case of errors */
2434 /*****************************************************************************/
2435 /* Data processing */
2436 /*****************************************************************************/
2440 static SegInfo* FindSegInfoById (InputData* D, unsigned Id)
2441 /* Find the SegInfo with a given Id */
2443 /* Get a pointer to the segment info collection */
2444 Collection* SegInfos = &D->Info->SegInfoById;
2446 /* Do a binary search */
2448 int Hi = (int) CollCount (SegInfos) - 1;
2452 int Cur = (Lo + Hi) / 2;
2455 SegInfo* CurItem = CollAt (SegInfos, Cur);
2458 if (Id > CurItem->Id) {
2460 } else if (Id < CurItem->Id) {
2474 static int FindFileInfoByName (Collection* FileInfos, const char* FileName,
2476 /* Find the FileInfo for a given file name. The function returns true if the
2477 * name was found. In this case, Index contains the index of the first item
2478 * that matches. If the item wasn't found, the function returns false and
2479 * Index contains the insert position for FileName.
2482 /* Do a binary search */
2484 int Hi = (int) CollCount (FileInfos) - 1;
2489 int Cur = (Lo + Hi) / 2;
2492 FileInfo* CurItem = CollAt (FileInfos, Cur);
2495 int Res = strcmp (CurItem->FileName, FileName);
2502 /* Since we may have duplicates, repeat the search until we've
2503 * the first item that has a match.
2511 /* Pass back the index. This is also the insert position */
2518 static FileInfo* FindFileInfoById (Collection* FileInfos, unsigned Id)
2519 /* Find the FileInfo with a given Id */
2521 /* Do a binary search */
2523 int Hi = (int) CollCount (FileInfos) - 1;
2527 int Cur = (Lo + Hi) / 2;
2530 FileInfo* CurItem = CollAt (FileInfos, Cur);
2533 if (Id > CurItem->Id) {
2535 } else if (Id < CurItem->Id) {
2549 static void ProcessSegInfo (InputData* D)
2550 /* Postprocess segment infos */
2554 /* Get pointers to the segment info collections */
2555 Collection* SegInfoByName = &D->Info->SegInfoByName;
2556 Collection* SegInfoById = &D->Info->SegInfoById;
2558 /* Sort the segment infos by name */
2559 CollSort (SegInfoByName, CompareSegInfoByName);
2561 /* Copy all items over to the collection that will get sorted by id */
2562 for (I = 0; I < CollCount (SegInfoByName); ++I) {
2563 CollAppend (SegInfoById, CollAt (SegInfoByName, I));
2566 /* Sort this collection */
2567 CollSort (SegInfoById, CompareSegInfoById);
2572 static void ProcessFileInfo (InputData* D)
2573 /* Postprocess file infos */
2577 /* Get pointers to the file info collections */
2578 Collection* FileInfoByName = &D->Info->FileInfoByName;
2579 Collection* FileInfoById = &D->Info->FileInfoById;
2581 /* First, sort the file infos, so we can do a binary search */
2582 CollSort (FileInfoByName, CompareFileInfoByName);
2584 /* Copy the file infos to another collection that will be sorted by id */
2585 for (I = 0; I < CollCount (FileInfoByName); ++I) {
2586 CollAppend (FileInfoById, CollAt (FileInfoByName, I));
2589 /* Sort this collection */
2590 CollSort (FileInfoById, CompareFileInfoById);
2595 static void ProcessLineInfo (InputData* D)
2596 /* Postprocess line infos */
2598 /* Get pointers to the collections */
2599 Collection* LineInfos = &D->Info->LineInfos;
2600 Collection* FileInfos = &D->Info->FileInfoByName;
2602 /* Walk over the line infos and replace the id numbers of file and segment
2603 * with pointers to the actual structs. Add the line info to each file
2604 * where it is defined.
2607 FileInfo* LastFileInfo = 0;
2608 SegInfo* LastSegInfo = 0;
2609 while (I < CollCount (LineInfos)) {
2614 /* Get LineInfo struct */
2615 LineInfo* L = CollAt (LineInfos, I);
2617 /* Find the FileInfo that corresponds to Id. We cache the last file
2618 * info in LastFileInfo to speedup searching.
2620 if (LastFileInfo && LastFileInfo->Id == L->File.Id) {
2623 F = FindFileInfoById (&D->Info->FileInfoById, L->File.Id);
2625 /* If we have no corresponding file info, print a warning and
2626 * remove the line info.
2631 "No file info for file with id %u",
2634 CollDelete (LineInfos, I);
2638 /* Otherwise remember it for later */
2642 /* Replace the file id by a pointer to the file info */
2645 /* Find the SegInfo that corresponds to Id. We cache the last file
2646 * info in LastSegInfo to speedup searching.
2648 if (LastSegInfo && LastSegInfo->Id == L->Seg.Id) {
2651 S = FindSegInfoById (D, L->Seg.Id);
2653 /* If we have no corresponding segment info, print a warning and
2654 * remove the line info.
2659 "No segment info for segment with id %u",
2662 CollDelete (LineInfos, I);
2666 /* Otherwise remember it for later */
2670 /* Replace the segment id by a pointer to the segment info */
2673 /* Add this line info to the file where it is defined */
2674 CollAppend (&F->LineInfoByLine, L);
2680 /* Walk over all files and sort the line infos for each file so we can
2681 * do a binary search later.
2683 for (I = 0; I < CollCount (FileInfos); ++I) {
2685 /* Get a pointer to this file info */
2686 FileInfo* F = CollAt (FileInfos, I);
2688 /* Sort the line infos for this file */
2689 CollSort (&F->LineInfoByLine, CompareLineInfoByLine);
2692 /* Sort the collection with all line infos by address */
2693 CollSort (LineInfos, CompareLineInfoByAddr);
2695 /* Create the line info list from the line info collection */
2696 CreateLineInfoList (&D->Info->LineInfoByAddr, LineInfos);
2701 static LineInfoListEntry* FindLineInfoByAddr (const LineInfoList* L, cc65_addr Addr)
2702 /* Find the index of a LineInfo for a given address. Returns 0 if no such
2703 * lineinfo was found.
2706 /* Do a binary search */
2708 int Hi = (int) L->Count - 1;
2712 int Cur = (Lo + Hi) / 2;
2715 LineInfoListEntry* CurItem = &L->List[Cur];
2718 if (CurItem->Addr > Addr) {
2720 } else if (CurItem->Addr < Addr) {
2734 static LineInfo* FindLineInfoByLine (FileInfo* F, cc65_line Line)
2735 /* Find the LineInfo for a given line number */
2741 /* Get a pointer to the line info collection for this file */
2742 Collection* LineInfoByLine = &F->LineInfoByLine;
2744 /* Do a binary search */
2746 Hi = (int) CollCount (LineInfoByLine) - 1;
2750 int Cur = (Lo + Hi) / 2;
2753 LineInfo* CurItem = CollAt (LineInfoByLine, Cur);
2756 if (Line < CurItem->Line) {
2758 } else if (Line > CurItem->Line) {
2772 static void ProcessSymInfo (InputData* D)
2773 /* Postprocess symbol infos */
2775 /* Get pointers to the symbol info collections */
2776 Collection* SymInfoByName = &D->Info->SymInfoByName;
2777 Collection* SymInfoByVal = &D->Info->SymInfoByVal;
2779 /* Sort the symbol infos */
2780 CollSort (SymInfoByName, CompareSymInfoByName);
2781 CollSort (SymInfoByVal, CompareSymInfoByVal);
2786 static int FindSymInfoByName (Collection* SymInfos, const char* SymName, unsigned* Index)
2787 /* Find the SymInfo for a given file name. The function returns true if the
2788 * name was found. In this case, Index contains the index of the first item
2789 * that matches. If the item wasn't found, the function returns false and
2790 * Index contains the insert position for SymName.
2793 /* Do a binary search */
2795 int Hi = (int) CollCount (SymInfos) - 1;
2800 int Cur = (Lo + Hi) / 2;
2803 SymInfo* CurItem = CollAt (SymInfos, Cur);
2806 int Res = strcmp (CurItem->SymName, SymName);
2813 /* Since we may have duplicates, repeat the search until we've
2814 * the first item that has a match.
2822 /* Pass back the index. This is also the insert position */
2829 static int FindSymInfoByValue (Collection* SymInfos, long Value, unsigned* Index)
2830 /* Find the SymInfo for a given value. The function returns true if the
2831 * value was found. In this case, Index contains the index of the first item
2832 * that matches. If the item wasn't found, the function returns false and
2833 * Index contains the insert position for the given value.
2836 /* Do a binary search */
2838 int Hi = (int) CollCount (SymInfos) - 1;
2843 int Cur = (Lo + Hi) / 2;
2846 SymInfo* CurItem = CollAt (SymInfos, Cur);
2849 if (Value > CurItem->Value) {
2853 /* Since we may have duplicates, repeat the search until we've
2854 * the first item that has a match.
2856 if (Value == CurItem->Value) {
2862 /* Pass back the index. This is also the insert position */
2869 /*****************************************************************************/
2871 /*****************************************************************************/
2875 cc65_dbginfo cc65_read_dbginfo (const char* FileName, cc65_errorfunc ErrFunc)
2876 /* Parse the debug info file with the given name. On success, the function
2877 * will return a pointer to an opaque cc65_dbginfo structure, that must be
2878 * passed to the other functions in this module to retrieve information.
2879 * errorfunc is called in case of warnings and errors. If the file cannot be
2880 * read successfully, NULL is returned.
2883 /* Data structure used to control scanning and parsing */
2885 0, /* Name of input file */
2886 1, /* Line number */
2888 0, /* Line at start of current token */
2889 0, /* Column at start of current token */
2890 0, /* Number of errors */
2892 ' ', /* Input character */
2893 TOK_INVALID, /* Input token */
2894 0, /* Integer constant */
2895 STRBUF_INITIALIZER, /* String constant */
2896 0, /* Function called in case of errors */
2897 0, /* Major version number */
2898 0, /* Minor version number */
2899 0, /* Pointer to debug info */
2901 D.FileName = FileName;
2904 /* Open the input file */
2905 D.F = fopen (D.FileName, "rt");
2908 ParseError (&D, CC65_ERROR,
2909 "Cannot open input file \"%s\": %s",
2910 D.FileName, strerror (errno));
2914 /* Create a new debug info struct */
2915 D.Info = NewDbgInfo ();
2917 /* Prime the pump */
2920 /* The first line in the file must specify version information */
2921 if (D.Tok != TOK_VERSION) {
2922 ParseError (&D, CC65_ERROR,
2923 "\"version\" keyword missing in first line - this is not "
2924 "a valid debug info file");
2928 /* Parse the version directive */
2931 /* Do several checks on the version number */
2932 if (D.MajorVersion < VER_MAJOR) {
2935 "This is an old version of the debug info format that is no "
2936 "longer supported. Version found = %u.%u, version supported "
2938 D.MajorVersion, D.MinorVersion, VER_MAJOR, VER_MINOR
2941 } else if (D.MajorVersion == VER_MAJOR && D.MinorVersion > VER_MINOR) {
2944 "This is a slightly newer version of the debug info format. "
2945 "It might work, but you may get errors about unknown keywords "
2946 "and similar. Version found = %u.%u, version supported = %u.%u",
2947 D.MajorVersion, D.MinorVersion, VER_MAJOR, VER_MINOR
2949 } else if (D.MajorVersion > VER_MAJOR) {
2952 "The format of this debug info file is newer than what we "
2953 "know. Will proceed but probably fail. Version found = %u.%u, "
2954 "version supported = %u.%u",
2955 D.MajorVersion, D.MinorVersion, VER_MAJOR, VER_MINOR
2961 while (D.Tok != TOK_EOF) {
2982 /* Output a warning, then skip the line with the unknown
2983 * keyword that may have been added by a later version.
2985 ParseError (&D, CC65_WARNING,
2986 "Unknown keyword \"%s\" - skipping",
2987 SB_GetConstBuf (&D.SVal));
2993 UnexpectedToken (&D);
2997 /* EOL or EOF must follow */
3002 /* Close the file */
3005 /* Free memory allocated for SVal */
3008 /* In case of errors, delete the debug info already allocated and
3012 /* Free allocated stuff */
3014 for (I = 0; I < CollCount (&D.Info->LineInfos); ++I) {
3015 FreeLineInfo (CollAt (&D.Info->LineInfos, I));
3017 DoneCollection (&D.Info->LineInfos);
3018 FreeDbgInfo (D.Info);
3022 /* We do now have all the information from the input file. Do
3025 ProcessSegInfo (&D);
3026 ProcessFileInfo (&D);
3027 ProcessLineInfo (&D);
3028 ProcessSymInfo (&D);
3035 /* Return the debug info struct that was created */
3041 void cc65_free_dbginfo (cc65_dbginfo Handle)
3042 /* Free debug information read from a file */
3045 FreeDbgInfo (Handle);
3051 cc65_lineinfo* cc65_lineinfo_byaddr (cc65_dbginfo Handle, unsigned long Addr)
3052 /* Return line information for the given address. The function returns 0
3053 * if no line information was found.
3056 LineInfoListEntry* E;
3057 cc65_lineinfo* D = 0;
3059 /* Check the parameter */
3060 assert (Handle != 0);
3062 /* Search in the line infos for address */
3063 E = FindLineInfoByAddr (&((DbgInfo*) Handle)->LineInfoByAddr, Addr);
3065 /* Do we have line infos? */
3070 /* Prepare the struct we will return to the caller */
3071 D = new_cc65_lineinfo (E->Count);
3072 if (E->Count == 1) {
3073 CopyLineInfo (D->data, E->Data);
3075 for (I = 0; I < D->count; ++I) {
3077 CopyLineInfo (D->data + I, ((LineInfo**) E->Data)[I]);
3082 /* Return the struct we've created */
3088 cc65_lineinfo* cc65_lineinfo_byname (cc65_dbginfo Handle, const char* FileName,
3090 /* Return line information for a file/line number combination. The function
3091 * returns NULL if no line information was found.
3099 Collection LineInfoList = COLLECTION_INITIALIZER;
3101 /* Check the parameter */
3102 assert (Handle != 0);
3104 /* The handle is actually a pointer to a debug info struct */
3105 Info = (DbgInfo*) Handle;
3107 /* Search for the first file with this name */
3108 Found = FindFileInfoByName (&Info->FileInfoByName, FileName, &Index);
3113 /* Loop over all files with this name */
3114 F = CollAt (&Info->FileInfoByName, Index);
3117 /* Search in the file for the given line */
3118 LineInfo* L = FindLineInfoByLine (F, Line);
3120 /* Remember the line info */
3121 CollAppend (&LineInfoList, L);
3127 /* If the index is valid, check if the next entry is a file with the
3130 if (Index < CollCount (&Info->FileInfoByName)) {
3131 F = CollAt (&Info->FileInfoByName, Index);
3132 Found = (strcmp (F->FileName, FileName) == 0);
3138 /* Check if we have entries */
3139 if (CollCount (&LineInfoList) == 0) {
3144 /* Prepare the struct we will return to the caller */
3145 D = new_cc65_lineinfo (CollCount (&LineInfoList));
3148 for (Index = 0; Index < CollCount (&LineInfoList); ++Index) {
3149 CopyLineInfo (D->data + Index, CollAt (&LineInfoList, Index));
3152 /* Delete the temporary data collection */
3153 DoneCollection (&LineInfoList);
3155 /* Return the allocated struct */
3161 void cc65_free_lineinfo (cc65_dbginfo Handle, cc65_lineinfo* Info)
3162 /* Free line info returned by one of the other functions */
3164 /* Just for completeness, check the handle */
3165 assert (Handle != 0);
3167 /* Just free the memory */
3173 cc65_sourceinfo* cc65_get_sourcelist (cc65_dbginfo Handle)
3174 /* Return a list of all source files */
3177 Collection* FileInfoByName;
3181 /* Check the parameter */
3182 assert (Handle != 0);
3184 /* The handle is actually a pointer to a debug info struct */
3185 Info = (DbgInfo*) Handle;
3187 /* Get a pointer to the file list */
3188 FileInfoByName = &Info->FileInfoByName;
3190 /* Allocate memory for the data structure returned to the caller.
3191 * Note: To simplify things, we will allocate the maximum amount of
3192 * memory, we may need later. This saves us the overhead of walking
3195 D = new_cc65_sourceinfo (CollCount (FileInfoByName));
3197 /* Fill in the data, skipping duplicate entries */
3199 for (I = 0; I < CollCount (FileInfoByName); ++I) {
3202 const FileInfo* F = CollAt (FileInfoByName, I);
3204 /* If this is not the first entry, compare it to the last one and
3205 * don't add it if it is identical.
3207 if (I > 0 && CompareFileInfoByName (F, CollAt (FileInfoByName, I-1)) == 0) {
3212 D->data[D->count].source_name = F->FileName;
3213 D->data[D->count].source_size = F->Size;
3214 D->data[D->count].source_mtime = F->MTime;
3216 /* One more valid entry */
3220 /* Return the result */
3226 void cc65_free_sourceinfo (cc65_dbginfo Handle, cc65_sourceinfo* Info)
3227 /* Free a source info record */
3229 /* Just for completeness, check the handle */
3230 assert (Handle != 0);
3232 /* Free the memory */
3238 cc65_segmentinfo* cc65_get_segmentlist (cc65_dbginfo Handle)
3239 /* Return a list of all segments referenced in the debug information */
3242 Collection* SegInfoByName;
3243 cc65_segmentinfo* D;
3246 /* Check the parameter */
3247 assert (Handle != 0);
3249 /* The handle is actually a pointer to a debug info struct */
3250 Info = (DbgInfo*) Handle;
3252 /* Get a pointer to the segment list */
3253 SegInfoByName = &Info->SegInfoByName;
3255 /* Allocate memory for the data structure returned to the caller */
3256 D = new_cc65_segmentinfo (CollCount (SegInfoByName));
3258 /* Fill in the data */
3259 D->count = CollCount (SegInfoByName);
3260 for (I = 0; I < CollCount (SegInfoByName); ++I) {
3263 const SegInfo* S = CollAt (SegInfoByName, I);
3266 D->data[I].segment_name = S->SegName;
3267 D->data[I].segment_start = S->Start;
3268 D->data[I].segment_size = S->Size;
3269 D->data[I].output_name = S->OutputName;
3270 D->data[I].output_offs = S->OutputOffs;
3273 /* Return the result */
3279 void cc65_free_segmentinfo (cc65_dbginfo Handle, cc65_segmentinfo* Info)
3280 /* Free a segment info record */
3282 /* Just for completeness, check the handle */
3283 assert (Handle != 0);
3285 /* Free the memory */
3291 cc65_symbolinfo* cc65_symbol_byname (cc65_dbginfo Handle, const char* Name)
3292 /* Return a list of symbols with a given name. The function returns NULL if
3293 * no symbol with this name was found.
3297 Collection* SymInfoByName;
3303 /* Check the parameter */
3304 assert (Handle != 0);
3306 /* The handle is actually a pointer to a debug info struct */
3307 Info = (DbgInfo*) Handle;
3309 /* Get a pointer to the symbol list */
3310 SymInfoByName = &Info->SymInfoByName;
3312 /* Search for the symbol */
3313 if (!FindSymInfoByName (SymInfoByName, Name, &Index)) {
3318 /* Index contains the position. Count how many symbols with this name
3319 * we have. Skip the first one, since we have at least one.
3322 while ((unsigned) Index + Count < CollCount (SymInfoByName)) {
3323 const SymInfo* S = CollAt (SymInfoByName, (unsigned) Index + Count);
3324 if (strcmp (S->SymName, Name) != 0) {
3330 /* Allocate memory for the data structure returned to the caller */
3331 D = new_cc65_symbolinfo (Count);
3333 /* Fill in the data */
3335 for (I = 0; I < Count; ++I) {
3337 CopySymInfo (D->data + I, CollAt (SymInfoByName, Index++));
3340 /* Return the result */
3346 cc65_symbolinfo* cc65_symbol_inrange (cc65_dbginfo Handle, cc65_addr Start, cc65_addr End)
3347 /* Return a list of labels in the given range. End is inclusive. The function
3348 * return NULL if no symbols within the given range are found. Non label
3349 * symbols are ignored and not returned.
3353 Collection* SymInfoByVal;
3354 Collection SymInfoList = COLLECTION_INITIALIZER;
3359 /* Check the parameter */
3360 assert (Handle != 0);
3362 /* The handle is actually a pointer to a debug info struct */
3363 Info = (DbgInfo*) Handle;
3365 /* Get a pointer to the symbol list */
3366 SymInfoByVal = &Info->SymInfoByVal;
3368 /* Search for the symbol. Because we're searching for a range, we cannot
3369 * make use of the function result.
3371 FindSymInfoByValue (SymInfoByVal, Start, &Index);
3373 /* Start from the given index, check all symbols until the end address is
3374 * reached. Place all symbols into SymInfoList for later.
3376 for (I = Index; I < CollCount (SymInfoByVal); ++I) {
3379 SymInfo* Item = CollAt (SymInfoByVal, I);
3381 /* The collection is sorted by address, so if we get a value larger
3382 * than the end address, we're done.
3384 if (Item->Value > (long) End) {
3388 /* Ignore non-labels */
3389 if (Item->Type != CC65_SYM_LABEL) {
3393 /* Ok, remember this one */
3394 CollAppend (&SymInfoList, Item);
3397 /* If we don't have any labels within the range, bail out. No memory has
3398 * been allocated for SymInfoList.
3400 if (CollCount (&SymInfoList) == 0) {
3404 /* Allocate memory for the data structure returned to the caller */
3405 D = new_cc65_symbolinfo (CollCount (&SymInfoList));
3407 /* Fill in the data */
3408 D->count = CollCount (&SymInfoList);
3409 for (I = 0; I < CollCount (&SymInfoList); ++I) {
3411 CopySymInfo (D->data + I, CollAt (&SymInfoList, I));
3414 /* Free the collection */
3415 DoneCollection (&SymInfoList);
3417 /* Return the result */
3423 void cc65_free_symbolinfo (cc65_dbginfo Handle, cc65_symbolinfo* Info)
3424 /* Free a symbol info record */
3426 /* Just for completeness, check the handle */
3427 assert (Handle != 0);
3429 /* Free the memory */