1 /*****************************************************************************/
5 /* cc65 debug info handling */
9 /* (C) 2010, Ullrich von Bassewitz */
10 /* Roemerstrasse 52 */
11 /* D-70794 Filderstadt */
12 /* EMail: uz@cc65.org */
15 /* This software is provided 'as-is', without any expressed or implied */
16 /* warranty. In no event will the authors be held liable for any damages */
17 /* arising from the use of this software. */
19 /* Permission is granted to anyone to use this software for any purpose, */
20 /* including commercial applications, and to alter it and redistribute it */
21 /* freely, subject to the following restrictions: */
23 /* 1. The origin of this software must not be misrepresented; you must not */
24 /* claim that you wrote the original software. If you use this software */
25 /* in a product, an acknowledgment in the product documentation would be */
26 /* appreciated but is not required. */
27 /* 2. Altered source versions must be plainly marked as such, and must not */
28 /* be misrepresented as being the original software. */
29 /* 3. This notice may not be removed or altered from any source */
32 /*****************************************************************************/
49 /*****************************************************************************/
51 /*****************************************************************************/
56 typedef struct StrBuf StrBuf;
58 char* Buf; /* Pointer to buffer */
59 unsigned Len; /* Length of the string */
60 unsigned Allocated; /* Size of allocated memory */
63 /* Initializer for a string buffer */
64 #define STRBUF_INITIALIZER { 0, 0, 0 }
66 /* An array of pointers that grows if needed */
67 typedef struct Collection Collection;
69 unsigned Count; /* Number of items in the list */
70 unsigned Size; /* Size of allocated array */
71 void** Items; /* Array with dynamic size */
74 /* Initializer for static collections */
75 #define COLLECTION_INITIALIZER { 0, 0, 0 }
79 /* Data structure containing information from the debug info file. A pointer
80 * to this structure is passed as handle to callers from the outside.
82 typedef struct DbgInfo DbgInfo;
84 Collection SegInfoByName; /* Segment infos sorted by name */
85 Collection SegInfoById; /* Segment infos sorted by id */
86 Collection FileInfoByName; /* File infos sorted by name */
87 Collection FileInfoById; /* File infos sorted by id */
93 TOK_INVALID, /* Invalid token */
94 TOK_EOF, /* End of file reached */
96 TOK_INTCON, /* Integer constant */
97 TOK_STRCON, /* String constant */
105 TOK_ABSOLUTE, /* ABSOLUTE keyword */
106 TOK_ADDRSIZE, /* ADDRSIZE keyword */
107 TOK_EQUATE, /* EQUATE keyword */
108 TOK_FILE, /* FILE keyword */
109 TOK_ID, /* ID keyword */
110 TOK_LABEL, /* LABEL keyword */
111 TOK_LINE, /* LINE keyword */
112 TOK_LONG, /* LONG_keyword */
113 TOK_MAJOR, /* MAJOR keyword */
114 TOK_MINOR, /* MINOR keyword */
115 TOK_MTIME, /* MTIME keyword */
116 TOK_NAME, /* NAME keyword */
117 TOK_RANGE, /* RANGE keyword */
118 TOK_RO, /* RO keyword */
119 TOK_RW, /* RW keyword */
120 TOK_SEGMENT, /* SEGMENT keyword */
121 TOK_SIZE, /* SIZE keyword */
122 TOK_START, /* START keyword */
123 TOK_SYM, /* SYM keyword */
124 TOK_TYPE, /* TYPE keyword */
125 TOK_VALUE, /* VALUE keyword */
126 TOK_VERSION, /* VERSION keyword */
127 TOK_ZEROPAGE, /* ZEROPAGE keyword */
129 TOK_IDENT, /* To catch unknown keywords */
132 /* Data used when parsing the debug info file */
133 typedef struct InputData InputData;
135 const char* FileName; /* Name of input file */
136 cc65_line Line; /* Current line number */
137 unsigned Col; /* Current column number */
138 cc65_line SLine; /* Line number at start of token */
139 unsigned SCol; /* Column number at start of token */
140 unsigned Errors; /* Number of errors */
141 FILE* F; /* Input file */
142 int C; /* Input character */
143 Token Tok; /* Token from input stream */
144 unsigned long IVal; /* Integer constant */
145 StrBuf SVal; /* String constant */
146 cc65_errorfunc Error; /* Function called in case of errors */
147 unsigned MajorVersion; /* Major version number */
148 unsigned MinorVersion; /* Minor version number */
149 Collection LineInfos; /* Line information */
150 DbgInfo* Info; /* Pointer to debug info */
153 /* Internally used segment info struct */
154 typedef struct SegInfo SegInfo;
156 unsigned Id; /* Id of segment */
157 cc65_addr Start; /* Start address of segment */
158 cc65_addr Size; /* Size of segment */
159 char SegName[1]; /* Name of segment */
162 /* Internally used file info struct */
163 typedef struct FileInfo FileInfo;
165 unsigned Id; /* Id of file */
166 unsigned long Size; /* Size of file */
167 unsigned long MTime; /* Modification time */
168 cc65_addr Start; /* Start address of line infos */
169 cc65_addr End; /* End address of line infos */
170 Collection LineInfoByAddr; /* Line infos sorted by address */
171 Collection LineInfoByLine; /* Line infos sorted by line */
172 char FileName[1]; /* Name of file with full path */
175 /* Internally used line info struct */
176 typedef struct LineInfo LineInfo;
178 cc65_addr Start; /* Start of data range */
179 cc65_addr End; /* End of data range */
180 cc65_line Line; /* Line number */
182 unsigned Id; /* Id of file */
183 FileInfo* Info; /* Pointer to file info */
186 unsigned Id; /* Id of segment */
187 SegInfo* Info; /* Pointer to segment info */
193 /*****************************************************************************/
195 /*****************************************************************************/
199 static void NextToken (InputData* D);
200 /* Read the next token from the input stream */
204 /*****************************************************************************/
205 /* Memory allocation */
206 /*****************************************************************************/
210 static void* xmalloc (size_t Size)
211 /* Allocate memory, check for out of memory condition. Do some debugging */
215 /* Allow zero sized requests and return NULL in this case */
218 /* Allocate memory */
221 /* Check for errors */
225 /* Return a pointer to the block */
231 static void* xrealloc (void* P, size_t Size)
232 /* Reallocate a memory block, check for out of memory */
234 /* Reallocate the block */
235 void* N = realloc (P, Size);
237 /* Check for errors */
238 assert (N != 0 || Size == 0);
240 /* Return the pointer to the new block */
246 static void xfree (void* Block)
247 /* Free the block, do some debugging */
254 /*****************************************************************************/
255 /* Dynamic strings */
256 /*****************************************************************************/
260 static void SB_Done (StrBuf* B)
261 /* Free the data of a string buffer (but not the struct itself) */
270 static void SB_Realloc (StrBuf* B, unsigned NewSize)
271 /* Reallocate the string buffer space, make sure at least NewSize bytes are
275 /* Get the current size, use a minimum of 8 bytes */
276 unsigned NewAllocated = B->Allocated;
277 if (NewAllocated == 0) {
281 /* Round up to the next power of two */
282 while (NewAllocated < NewSize) {
286 /* Reallocate the buffer. Beware: The allocated size may be zero while the
287 * length is not. This means that we have a buffer that wasn't allocated
291 /* Just reallocate the block */
292 B->Buf = xrealloc (B->Buf, NewAllocated);
294 /* Allocate a new block and copy */
295 B->Buf = memcpy (xmalloc (NewAllocated), B->Buf, B->Len);
298 /* Remember the new block size */
299 B->Allocated = NewAllocated;
304 static void SB_CheapRealloc (StrBuf* B, unsigned NewSize)
305 /* Reallocate the string buffer space, make sure at least NewSize bytes are
306 * available. This function won't copy the old buffer contents over to the new
307 * buffer and may be used if the old contents are overwritten later.
310 /* Get the current size, use a minimum of 8 bytes */
311 unsigned NewAllocated = B->Allocated;
312 if (NewAllocated == 0) {
316 /* Round up to the next power of two */
317 while (NewAllocated < NewSize) {
321 /* Free the old buffer if there is one */
326 /* Allocate a fresh block */
327 B->Buf = xmalloc (NewAllocated);
329 /* Remember the new block size */
330 B->Allocated = NewAllocated;
335 static unsigned SB_GetLen (const StrBuf* B)
336 /* Return the length of the buffer contents */
343 static const char* SB_GetConstBuf (const StrBuf* B)
344 /* Return a buffer pointer */
351 static void SB_Terminate (StrBuf* B)
352 /* Zero terminate the given string buffer. NOTE: The terminating zero is not
353 * accounted for in B->Len, if you want that, you have to use AppendChar!
356 unsigned NewLen = B->Len + 1;
357 if (NewLen > B->Allocated) {
358 SB_Realloc (B, NewLen);
360 B->Buf[B->Len] = '\0';
365 static void SB_Clear (StrBuf* B)
366 /* Clear the string buffer (make it empty) */
373 static void SB_CopyBuf (StrBuf* Target, const char* Buf, unsigned Size)
374 /* Copy Buf to Target, discarding the old contents of Target */
377 if (Target->Allocated < Size) {
378 SB_CheapRealloc (Target, Size);
380 memcpy (Target->Buf, Buf, Size);
387 static void SB_Copy (StrBuf* Target, const StrBuf* Source)
388 /* Copy Source to Target, discarding the old contents of Target */
390 SB_CopyBuf (Target, Source->Buf, Source->Len);
395 static void SB_AppendChar (StrBuf* B, int C)
396 /* Append a character to a string buffer */
398 unsigned NewLen = B->Len + 1;
399 if (NewLen > B->Allocated) {
400 SB_Realloc (B, NewLen);
402 B->Buf[B->Len] = (char) C;
408 /*****************************************************************************/
410 /*****************************************************************************/
414 static Collection* InitCollection (Collection* C)
415 /* Initialize a collection and return it. */
417 /* Intialize the fields. */
422 /* Return the new struct */
428 static void DoneCollection (Collection* C)
429 /* Free the data for a collection. This will not free the data contained in
433 /* Free the pointer array */
439 static unsigned CollCount (const Collection* C)
440 /* Return the number of items in the collection */
447 static void CollGrow (Collection* C, unsigned Size)
448 /* Grow the collection C so it is able to hold Size items without a resize
449 * being necessary. This can be called for performance reasons if the number
450 * of items to be placed in the collection is known in advance.
455 /* Ignore the call if the collection is already large enough */
456 if (Size <= C->Size) {
460 /* Grow the collection */
462 NewItems = xmalloc (C->Size * sizeof (void*));
463 memcpy (NewItems, C->Items, C->Count * sizeof (void*));
470 static void CollInsert (Collection* C, void* Item, unsigned Index)
471 /* Insert the data at the given position in the collection */
473 /* Check for invalid indices */
474 assert (Index <= C->Count);
476 /* Grow the array if necessary */
477 if (C->Count >= C->Size) {
479 CollGrow (C, (C->Size == 0)? 8 : C->Size * 2);
482 /* Move the existing elements if needed */
483 if (C->Count != Index) {
484 memmove (C->Items+Index+1, C->Items+Index, (C->Count-Index) * sizeof (void*));
488 /* Store the new item */
489 C->Items[Index] = Item;
494 static void CollAppend (Collection* C, void* Item)
495 /* Append an item to the end of the collection */
497 /* Insert the item at the end of the current list */
498 CollInsert (C, Item, C->Count);
503 static void* CollAt (Collection* C, unsigned Index)
504 /* Return the item at the given index */
506 /* Check the index */
507 assert (Index < C->Count);
509 /* Return the element */
510 return C->Items[Index];
515 static void* CollFirst (Collection* C)
516 /* Return the first item in a collection */
518 /* We must have at least one entry */
519 assert (C->Count > 0);
521 /* Return the element */
527 static void* CollLast (Collection* C)
528 /* Return the last item in a collection */
530 /* We must have at least one entry */
531 assert (C->Count > 0);
533 /* Return the element */
534 return C->Items[C->Count-1];
539 static void CollDelete (Collection* C, unsigned Index)
540 /* Remove the item with the given index from the collection. This will not
541 * free the item itself, just the pointer. All items with higher indices
542 * will get moved to a lower position.
545 /* Check the index */
546 assert (Index < C->Count);
548 /* Remove the item pointer */
550 memmove (C->Items+Index, C->Items+Index+1, (C->Count-Index) * sizeof (void*));
555 static void CollQuickSort (Collection* C, int Lo, int Hi,
556 int (*Compare) (const void*, const void*))
557 /* Internal recursive sort function. */
559 /* Get a pointer to the items */
560 void** Items = C->Items;
567 while (I <= J && Compare (Items[Lo], Items[I]) >= 0) {
570 while (I <= J && Compare (Items[Lo], Items[J]) < 0) {
575 void* Tmp = Items[I];
584 void* Tmp = Items[J];
585 Items[J] = Items[Lo];
588 if (J > (Hi + Lo) / 2) {
589 CollQuickSort (C, J + 1, Hi, Compare);
592 CollQuickSort (C, Lo, J - 1, Compare);
600 void CollSort (Collection* C, int (*Compare) (const void*, const void*))
601 /* Sort the collection using the given compare function. */
604 CollQuickSort (C, 0, C->Count-1, Compare);
610 /*****************************************************************************/
612 /*****************************************************************************/
616 static SegInfo* NewSegInfo (const StrBuf* SegName, unsigned Id,
617 cc65_addr Start, cc65_addr Size)
618 /* Create a new SegInfo struct and return it */
620 /* Allocate memory */
621 SegInfo* S = xmalloc (sizeof (SegInfo) + SB_GetLen (SegName));
627 memcpy (S->SegName, SB_GetConstBuf (SegName), SB_GetLen (SegName) + 1);
635 static void FreeSegInfo (SegInfo* S)
636 /* Free a SegInfo struct */
643 static int CompareSegInfoByName (const void* L, const void* R)
644 /* Helper function to sort segment infos in a collection by name */
646 /* Sort by file name */
647 return strcmp (((const SegInfo*) L)->SegName,
648 ((const SegInfo*) R)->SegName);
653 static int CompareSegInfoById (const void* L, const void* R)
654 /* Helper function to sort segment infos in a collection by id */
656 if (((const SegInfo*) L)->Id > ((const SegInfo*) R)->Id) {
658 } else if (((const SegInfo*) L)->Id < ((const SegInfo*) R)->Id) {
667 /*****************************************************************************/
669 /*****************************************************************************/
673 static LineInfo* NewLineInfo (unsigned File, unsigned Seg, cc65_line Line,
674 cc65_addr Start, cc65_addr End)
675 /* Create a new LineInfo struct and return it */
677 /* Allocate memory */
678 LineInfo* L = xmalloc (sizeof (LineInfo));
693 static void FreeLineInfo (LineInfo* L)
694 /* Free a LineInfo struct */
701 static int CompareLineInfoByAddr (const void* L, const void* R)
702 /* Helper function to sort line infos in a collection by address */
704 /* Sort by start of range */
705 if (((const LineInfo*) L)->Start > ((const LineInfo*) R)->Start) {
707 } else if (((const LineInfo*) L)->Start < ((const LineInfo*) R)->Start) {
716 static int CompareLineInfoByLine (const void* L, const void* R)
717 /* Helper function to sort line infos in a collection by line */
719 if (((const LineInfo*) L)->Line > ((const LineInfo*) R)->Line) {
721 } else if (((const LineInfo*) L)->Line < ((const LineInfo*) R)->Line) {
730 /*****************************************************************************/
732 /*****************************************************************************/
736 static FileInfo* NewFileInfo (const StrBuf* FileName, unsigned Id,
737 unsigned long Size, unsigned long MTime)
738 /* Create a new FileInfo struct and return it */
740 /* Allocate memory */
741 FileInfo* F = xmalloc (sizeof (FileInfo) + SB_GetLen (FileName));
747 F->Start = ~(cc65_addr)0;
749 InitCollection (&F->LineInfoByAddr);
750 InitCollection (&F->LineInfoByLine);
751 memcpy (F->FileName, SB_GetConstBuf (FileName), SB_GetLen (FileName) + 1);
759 static void FreeFileInfo (FileInfo* F)
760 /* Free a FileInfo struct */
764 /* Walk through the collection with line infos and delete them */
765 for (I = 0; I < CollCount (&F->LineInfoByAddr); ++I) {
766 FreeLineInfo (CollAt (&F->LineInfoByAddr, I));
768 DoneCollection (&F->LineInfoByAddr);
769 DoneCollection (&F->LineInfoByLine);
771 /* Free the file info structure itself */
777 static int CompareFileInfoByName (const void* L, const void* R)
778 /* Helper function to sort file infos in a collection by name */
780 /* Sort by file name */
781 return strcmp (((const FileInfo*) L)->FileName,
782 ((const FileInfo*) R)->FileName);
787 static int CompareFileInfoById (const void* L, const void* R)
788 /* Helper function to sort file infos in a collection by id */
790 if (((const FileInfo*) L)->Id > ((const FileInfo*) R)->Id) {
792 } else if (((const FileInfo*) L)->Id < ((const FileInfo*) R)->Id) {
801 /*****************************************************************************/
803 /*****************************************************************************/
807 static DbgInfo* NewDbgInfo (void)
808 /* Create a new DbgInfo struct and return it */
810 /* Allocate memory */
811 DbgInfo* Info = xmalloc (sizeof (DbgInfo));
814 InitCollection (&Info->SegInfoByName);
815 InitCollection (&Info->SegInfoById);
816 InitCollection (&Info->FileInfoByName);
817 InitCollection (&Info->FileInfoById);
825 static void FreeDbgInfo (DbgInfo* Info)
826 /* Free a DbgInfo struct */
830 /* Free segment info */
831 for (I = 0; I < CollCount (&Info->SegInfoByName); ++I) {
832 FreeSegInfo (CollAt (&Info->SegInfoByName, I));
834 DoneCollection (&Info->SegInfoByName);
835 DoneCollection (&Info->SegInfoById);
838 for (I = 0; I < CollCount (&Info->FileInfoByName); ++I) {
839 FreeFileInfo (CollAt (&Info->FileInfoByName, I));
841 DoneCollection (&Info->FileInfoByName);
842 DoneCollection (&Info->FileInfoById);
844 /* Free the structure itself */
850 /*****************************************************************************/
851 /* Helper functions */
852 /*****************************************************************************/
856 static void ParseError (InputData* D, cc65_error_severity Type, const char* Msg, ...)
857 /* Call the user supplied parse error function */
863 /* Test-format the error message so we know how much space to allocate */
865 MsgSize = vsnprintf (0, 0, Msg, ap);
868 /* Allocate memory */
869 E = xmalloc (sizeof (*E) + MsgSize);
871 /* Write data to E */
873 E->name = D->FileName;
877 vsnprintf (E->errormsg, MsgSize+1, Msg, ap);
880 /* Call the caller:-) */
883 /* Free the data structure */
887 if (Type == CC65_ERROR) {
894 static void SkipLine (InputData* D)
895 /* Error recovery routine. Skip tokens until EOL or EOF is reached */
897 while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
904 static void UnexpectedToken (InputData* D)
905 /* Call ParseError with a message about an unexpected input token */
907 ParseError (D, CC65_ERROR, "Unexpected input token %d", D->Tok);
913 static void MissingAttribute (InputData* D, const char* AttrName)
914 /* Print an error about a missing attribute */
916 ParseError (D, CC65_ERROR, "Attribute \"%s\" is mising", AttrName);
921 static void UnknownKeyword (InputData* D)
922 /* Print a warning about an unknown keyword in the file. Try to do smart
923 * recovery, so if later versions of the debug information add additional
924 * keywords, this code may be able to at least ignore them.
927 /* Output a warning */
928 ParseError (D, CC65_WARNING, "Unknown keyword \"%s\" - skipping",
929 SB_GetConstBuf (&D->SVal));
931 /* Skip the identifier */
934 /* If an equal sign follows, ignore anything up to the next line end
935 * or comma. If a comma or line end follows, we're already done. If
936 * we have none of both, we ignore the remainder of the line.
938 if (D->Tok == TOK_EQUAL) {
940 while (D->Tok != TOK_COMMA && D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
943 } else if (D->Tok != TOK_COMMA && D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
950 /*****************************************************************************/
951 /* Scanner and parser */
952 /*****************************************************************************/
956 static int DigitVal (int C)
957 /* Return the value for a numeric digit. Return -1 if C is invalid */
961 } else if (isxdigit (C)) {
962 return toupper (C) - 'A' + 10;
970 static void NextChar (InputData* D)
971 /* Read the next character from the input. Count lines and columns */
973 /* Check if we've encountered EOF before */
987 static void NextToken (InputData* D)
988 /* Read the next token from the input stream */
990 static const struct KeywordEntry {
991 const char Keyword[10];
994 { "absolute", TOK_ABSOLUTE },
995 { "addrsize", TOK_ADDRSIZE },
996 { "equate", TOK_EQUATE },
997 { "file", TOK_FILE },
999 { "label", TOK_LABEL },
1000 { "line", TOK_LINE },
1001 { "long", TOK_LONG },
1002 { "major", TOK_MAJOR },
1003 { "minor", TOK_MINOR },
1004 { "mtime", TOK_MTIME },
1005 { "name", TOK_NAME },
1006 { "range", TOK_RANGE },
1009 { "segment", TOK_SEGMENT },
1010 { "size", TOK_SIZE },
1011 { "start", TOK_START },
1013 { "type", TOK_TYPE },
1014 { "value", TOK_VALUE },
1015 { "version", TOK_VERSION },
1016 { "zeropage", TOK_ZEROPAGE },
1020 /* Skip whitespace */
1021 while (D->C == ' ' || D->C == '\t') {
1025 /* Remember the current position as start of the next token */
1030 if (D->C == '_' || isalpha (D->C)) {
1032 const struct KeywordEntry* Entry;
1034 /* Read the identifier */
1035 SB_Clear (&D->SVal);
1036 while (D->C == '_' || isalnum (D->C)) {
1037 SB_AppendChar (&D->SVal, D->C);
1040 SB_Terminate (&D->SVal);
1042 /* Search the identifier in the keyword table */
1043 Entry = bsearch (SB_GetConstBuf (&D->SVal),
1045 sizeof (KeywordTable) / sizeof (KeywordTable[0]),
1046 sizeof (KeywordTable[0]),
1047 (int (*)(const void*, const void*)) strcmp);
1051 D->Tok = Entry->Tok;
1057 if (isdigit (D->C)) {
1062 if (toupper (D->C) == 'X') {
1072 while ((Val = DigitVal (D->C)) >= 0 && Val < Base) {
1073 D->IVal = D->IVal * Base + Val;
1076 D->Tok = TOK_INTCON;
1080 /* Other characters */
1104 SB_Clear (&D->SVal);
1107 if (D->C == '\n' || D->C == EOF) {
1108 ParseError (D, CC65_ERROR, "Unterminated string constant");
1115 SB_AppendChar (&D->SVal, D->C);
1118 SB_Terminate (&D->SVal);
1119 D->Tok = TOK_STRCON;
1132 ParseError (D, CC65_ERROR, "Invalid input character `%c'", D->C);
1139 static int TokenFollows (InputData* D, Token Tok, const char* Name)
1140 /* Check for a comma */
1142 if (D->Tok != Tok) {
1143 ParseError (D, CC65_ERROR, "%s expected", Name);
1153 static int IntConstFollows (InputData* D)
1154 /* Check for an integer constant */
1156 return TokenFollows (D, TOK_INTCON, "Integer constant");
1161 static int StrConstFollows (InputData* D)
1162 /* Check for a string literal */
1164 return TokenFollows (D, TOK_STRCON, "String literal");
1169 static int Consume (InputData* D, Token Tok, const char* Name)
1170 /* Check for a token and consume it. Return true if the token was comsumed,
1171 * return false otherwise.
1174 if (TokenFollows (D, Tok, Name)) {
1184 static int ConsumeEqual (InputData* D)
1185 /* Consume an equal sign */
1187 return Consume (D, TOK_EQUAL, "'='");
1192 static int ConsumeMinus (InputData* D)
1193 /* Consume a minus sign */
1195 return Consume (D, TOK_MINUS, "'-'");
1200 static void ParseFile (InputData* D)
1201 /* Parse a FILE line */
1205 unsigned long MTime;
1206 StrBuf FileName = STRBUF_INITIALIZER;
1214 ibRequired = ibId | ibFileName | ibSize | ibMTime,
1215 } InfoBits = ibNone;
1217 /* Skip the FILE token */
1220 /* More stuff follows */
1225 /* Check for an unknown keyword */
1226 if (D->Tok == TOK_IDENT) {
1231 /* Something we know? */
1232 if (D->Tok != TOK_ID && D->Tok != TOK_NAME &&
1233 D->Tok != TOK_SIZE && D->Tok != TOK_MTIME) {
1238 /* Remember the token, skip it, check for equal */
1241 if (!ConsumeEqual (D)) {
1245 /* Check what the token was */
1249 if (!IntConstFollows (D)) {
1258 if (!StrConstFollows (D)) {
1261 SB_Copy (&FileName, &D->SVal);
1262 SB_Terminate (&FileName);
1263 InfoBits |= ibFileName;
1268 if (!IntConstFollows (D)) {
1277 if (!IntConstFollows (D)) {
1282 InfoBits |= ibMTime;
1287 UnexpectedToken (D);
1293 if (D->Tok != TOK_COMMA) {
1299 /* Check for end of line */
1300 if (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1301 UnexpectedToken (D);
1306 /* Check for required information */
1307 if (InfoBits != ibRequired) {
1308 ParseError (D, CC65_ERROR, "Required attributes missing");
1312 /* Create the file info and remember it */
1313 F = NewFileInfo (&FileName, Id, Size, MTime);
1314 CollAppend (&D->Info->FileInfoByName, F);
1317 /* Entry point in case of errors */
1318 SB_Done (&FileName);
1324 static void ParseLine (InputData* D)
1325 /* Parse a LINE line */
1339 ibRequired = ibFile | ibSegment | ibLine | ibRange,
1340 } InfoBits = ibNone;
1342 /* Skip the LINE token */
1345 /* More stuff follows */
1350 /* Check for an unknown keyword */
1351 if (D->Tok == TOK_IDENT) {
1356 /* Something we know? */
1357 if (D->Tok != TOK_FILE && D->Tok != TOK_SEGMENT &&
1358 D->Tok != TOK_LINE && D->Tok != TOK_RANGE) {
1363 /* Remember the token, skip it, check for equal */
1366 if (!ConsumeEqual (D)) {
1370 /* Check what the token was */
1374 if (!IntConstFollows (D)) {
1383 if (!IntConstFollows (D)) {
1387 InfoBits |= ibSegment;
1392 if (!IntConstFollows (D)) {
1395 Line = (cc65_line) D->IVal;
1401 if (!IntConstFollows (D)) {
1404 Start = (cc65_addr) D->IVal;
1406 if (!ConsumeMinus (D)) {
1409 if (!IntConstFollows (D)) {
1412 End = (cc65_addr) D->IVal;
1414 InfoBits |= ibRange;
1419 UnexpectedToken (D);
1425 if (D->Tok != TOK_COMMA) {
1431 /* Check for end of line */
1432 if (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1433 UnexpectedToken (D);
1438 /* Check for required information */
1439 if (InfoBits != ibRequired) {
1440 ParseError (D, CC65_ERROR, "Required attributes missing");
1444 /* Create the line info and remember it */
1445 L = NewLineInfo (File, Segment, Line, Start, End);
1446 CollAppend (&D->LineInfos, L);
1449 /* Entry point in case of errors */
1455 static void ParseSegment (InputData* D)
1456 /* Parse a SEGMENT line */
1461 StrBuf SegName = STRBUF_INITIALIZER;
1471 ibRequired = ibId | ibSegName | ibStart | ibSize | ibAddrSize | ibType,
1472 } InfoBits = ibNone;
1474 /* Skip the SEGMENT token */
1477 /* More stuff follows */
1482 /* Check for an unknown keyword */
1483 if (D->Tok == TOK_IDENT) {
1488 /* Something we know? */
1489 if (D->Tok != TOK_ID && D->Tok != TOK_NAME &&
1490 D->Tok != TOK_START && D->Tok != TOK_SIZE &&
1491 D->Tok != TOK_ADDRSIZE && D->Tok != TOK_TYPE) {
1496 /* Remember the token, skip it, check for equal */
1499 if (!ConsumeEqual (D)) {
1503 /* Check what the token was */
1507 if (!IntConstFollows (D)) {
1516 if (!StrConstFollows (D)) {
1519 SB_Copy (&SegName, &D->SVal);
1520 SB_Terminate (&SegName);
1521 InfoBits |= ibSegName;
1526 if (!IntConstFollows (D)) {
1529 Start = (cc65_addr) D->IVal;
1531 InfoBits |= ibStart;
1535 if (!IntConstFollows (D)) {
1545 InfoBits |= ibAddrSize;
1555 UnexpectedToken (D);
1561 if (D->Tok != TOK_COMMA) {
1567 /* Check for end of line */
1568 if (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1569 UnexpectedToken (D);
1574 /* Check for required information */
1575 if (InfoBits != ibRequired) {
1576 ParseError (D, CC65_ERROR, "Required attributes missing");
1580 /* Create the segment info and remember it */
1581 S = NewSegInfo (&SegName, Id, Start, Size);
1582 CollAppend (&D->Info->SegInfoByName, S);
1585 /* Entry point in case of errors */
1592 static void ParseSym (InputData* D)
1593 /* Parse a SYM line */
1595 /* Skip the SYM token */
1604 static void ParseVersion (InputData* D)
1605 /* Parse a VERSION line */
1607 enum { None = 0x00, Major = 0x01, Minor = 0x02 } InfoBits = None;
1609 /* Skip the VERSION token */
1612 /* More stuff follows */
1613 while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1619 if (!ConsumeEqual (D)) {
1622 if (!IntConstFollows (D)) {
1625 D->MajorVersion = D->IVal;
1632 if (!ConsumeEqual (D)) {
1635 if (!IntConstFollows (D)) {
1638 D->MinorVersion = D->IVal;
1644 /* Try to skip unknown keywords that may have been added by
1651 UnexpectedToken (D);
1656 /* Comma follows before next attribute */
1657 if (D->Tok == TOK_COMMA) {
1659 } else if (D->Tok == TOK_EOL || D->Tok == TOK_EOF) {
1662 UnexpectedToken (D);
1667 /* Check for required information */
1668 if ((InfoBits & Major) == None) {
1669 MissingAttribute (D, "major");
1672 if ((InfoBits & Minor) == None) {
1673 MissingAttribute (D, "minor");
1678 /* Entry point in case of errors */
1684 /*****************************************************************************/
1685 /* Data processing */
1686 /*****************************************************************************/
1690 static SegInfo* FindSegInfoById (InputData* D, unsigned Id)
1691 /* Find the SegInfo with a given Id */
1693 /* Get a pointer to the segment info collection */
1694 Collection* SegInfos = &D->Info->SegInfoById;
1696 /* Do a binary search */
1698 int Hi = (int) CollCount (SegInfos) - 1;
1702 int Cur = (Lo + Hi) / 2;
1705 SegInfo* CurItem = CollAt (SegInfos, Cur);
1708 if (Id > CurItem->Id) {
1710 } else if (Id < CurItem->Id) {
1724 static FileInfo* FindFileInfoByName (Collection* FileInfos, const char* FileName)
1725 /* Find the FileInfo for a given file name */
1727 /* Do a binary search */
1729 int Hi = (int) CollCount (FileInfos) - 1;
1733 int Cur = (Lo + Hi) / 2;
1736 FileInfo* CurItem = CollAt (FileInfos, Cur);
1739 int Res = strcmp (CurItem->FileName, FileName);
1744 } else if (Res > 0) {
1758 static FileInfo* FindFileInfoById (Collection* FileInfos, unsigned Id)
1759 /* Find the FileInfo with a given Id */
1761 /* Do a binary search */
1763 int Hi = (int) CollCount (FileInfos) - 1;
1767 int Cur = (Lo + Hi) / 2;
1770 FileInfo* CurItem = CollAt (FileInfos, Cur);
1773 if (Id > CurItem->Id) {
1775 } else if (Id < CurItem->Id) {
1789 static void ProcessSegInfo (InputData* D)
1790 /* Postprocess segment infos */
1794 /* Get pointers to the segment info collections */
1795 Collection* SegInfoByName = &D->Info->SegInfoByName;
1796 Collection* SegInfoById = &D->Info->SegInfoById;
1798 /* Sort the segment infos by name */
1799 CollSort (SegInfoByName, CompareSegInfoByName);
1801 /* Copy all items over to the collection that will get sorted by id */
1802 for (I = 0; I < CollCount (SegInfoByName); ++I) {
1803 CollAppend (SegInfoById, CollAt (SegInfoByName, I));
1806 /* Sort this collection */
1807 CollSort (SegInfoById, CompareSegInfoById);
1812 static void ProcessFileInfo (InputData* D)
1813 /* Postprocess file infos */
1815 /* Get pointers to the file info collections */
1816 Collection* FileInfoByName = &D->Info->FileInfoByName;
1817 Collection* FileInfoById = &D->Info->FileInfoById;
1819 /* First, sort the file infos, so we can check for duplicates and do
1822 CollSort (FileInfoByName, CompareFileInfoByName);
1824 /* Cannot work on an empty collection */
1825 if (CollCount (FileInfoByName) > 0) {
1827 /* Walk through the file infos sorted by name and check for duplicates.
1828 * If we find some, warn and remove them, so the file infos are unique
1831 FileInfo* F = CollAt (FileInfoByName, 0);
1833 while (I < CollCount (FileInfoByName)) {
1834 FileInfo* Next = CollAt (FileInfoByName, I);
1835 if (strcmp (F->FileName, Next->FileName) == 0) {
1836 /* Warn only if time stamp and/or size is different */
1837 if (F->Size != Next->Size || F->MTime != Next->MTime) {
1840 "Duplicate file entry for \"%s\"",
1843 /* Remove the duplicate entry */
1844 FreeFileInfo (Next);
1845 CollDelete (FileInfoByName, I);
1847 /* This one is ok, check the next entry */
1853 /* Copy the file infos to another collection that will be sorted by id */
1854 for (I = 0; I < CollCount (FileInfoByName); ++I) {
1855 CollAppend (FileInfoById, CollAt (FileInfoByName, I));
1858 /* Sort this collection */
1859 CollSort (FileInfoById, CompareFileInfoById);
1865 static void ProcessLineInfo (InputData* D)
1866 /* Postprocess line infos */
1868 /* Get pointers to the collections */
1869 Collection* LineInfos = &D->LineInfos;
1870 Collection* FileInfos = &D->Info->FileInfoByName;
1872 /* Walk over the line infos and replace the id numbers of file and segment
1873 * with pointers to the actual structs. Add the line info to each file
1874 * where it is defined.
1877 FileInfo* LastFileInfo = 0;
1878 SegInfo* LastSegInfo = 0;
1879 while (I < CollCount (LineInfos)) {
1884 /* Get LineInfo struct */
1885 LineInfo* L = CollAt (LineInfos, I);
1887 /* Find the FileInfo that corresponds to Id. We cache the last file
1888 * info in LastFileInfo to speedup searching.
1890 if (LastFileInfo && LastFileInfo->Id == L->File.Id) {
1893 F = FindFileInfoById (&D->Info->FileInfoById, L->File.Id);
1895 /* If we have no corresponding file info, print a warning and
1896 * remove the line info.
1901 "No file info for file with id %u",
1904 CollDelete (LineInfos, I);
1908 /* Otherwise remember it for later */
1912 /* Replace the file id by a pointer to the file info */
1915 /* Find the SegInfo that corresponds to Id. We cache the last file
1916 * info in LastSegInfo to speedup searching.
1918 if (LastSegInfo && LastSegInfo->Id == L->Seg.Id) {
1921 S = FindSegInfoById (D, L->Seg.Id);
1923 /* If we have no corresponding segment info, print a warning and
1924 * remove the line info.
1929 "No segment info for segment with id %u",
1932 CollDelete (LineInfos, I);
1936 /* Otherwise remember it for later */
1940 /* Replace the segment id by a pointer to the segment info */
1943 /* Add this line info to the file where it is defined */
1944 CollAppend (&F->LineInfoByAddr, L);
1945 CollAppend (&F->LineInfoByLine, L);
1951 /* Walk over all files and sort the line infos for each file so we can
1952 * do a binary search later.
1954 for (I = 0; I < CollCount (FileInfos); ++I) {
1956 /* Get a pointer to this file info */
1957 FileInfo* F = CollAt (FileInfos, I);
1959 /* Sort the line infos for this file */
1960 CollSort (&F->LineInfoByAddr, CompareLineInfoByAddr);
1961 CollSort (&F->LineInfoByLine, CompareLineInfoByLine);
1963 /* If there are line info entries, place the first and last address
1964 * of into the FileInfo struct itself, so we can rule out a FileInfo
1965 * quickly when mapping an address to a line info.
1967 if (CollCount (&F->LineInfoByAddr) > 0) {
1968 F->Start = ((const LineInfo*) CollFirst (&F->LineInfoByAddr))->Start;
1969 F->End = ((const LineInfo*) CollLast (&F->LineInfoByAddr))->End;
1977 static LineInfo* FindLineInfoByAddr (FileInfo* F, cc65_addr Addr)
1978 /* Find the LineInfo for a given address */
1980 Collection* LineInfoByAddr;
1985 /* Each file info contains the first and last address for which line
1986 * info is available, so we can rule out non matching ones quickly.
1988 if (Addr < F->Start || Addr > F->End) {
1992 /* Get a pointer to the line info collection for this file */
1993 LineInfoByAddr = &F->LineInfoByAddr;
1995 /* Do a binary search */
1997 Hi = (int) CollCount (LineInfoByAddr) - 1;
2001 int Cur = (Lo + Hi) / 2;
2004 LineInfo* CurItem = CollAt (LineInfoByAddr, Cur);
2007 if (Addr < CurItem->Start) {
2009 } else if (Addr > CurItem->End) {
2023 static LineInfo* FindLineInfoByLine (FileInfo* F, cc65_line Line)
2024 /* Find the LineInfo for a given line number */
2030 /* Get a pointer to the line info collection for this file */
2031 Collection* LineInfoByLine = &F->LineInfoByLine;
2033 /* Do a binary search */
2035 Hi = (int) CollCount (LineInfoByLine) - 1;
2039 int Cur = (Lo + Hi) / 2;
2042 LineInfo* CurItem = CollAt (LineInfoByLine, Cur);
2045 if (Line < CurItem->Line) {
2047 } else if (Line > CurItem->Line) {
2061 /*****************************************************************************/
2063 /*****************************************************************************/
2067 cc65_dbginfo cc65_read_dbginfo (const char* FileName, cc65_errorfunc ErrFunc)
2068 /* Parse the debug info file with the given name. On success, the function
2069 * will return a pointer to an opaque cc65_dbginfo structure, that must be
2070 * passed to the other functions in this module to retrieve information.
2071 * errorfunc is called in case of warnings and errors. If the file cannot be
2072 * read successfully, NULL is returned.
2075 /* Data structure used to control scanning and parsing */
2077 0, /* Name of input file */
2078 1, /* Line number */
2080 0, /* Line at start of current token */
2081 0, /* Column at start of current token */
2082 0, /* Number of errors */
2084 ' ', /* Input character */
2085 TOK_INVALID, /* Input token */
2086 0, /* Integer constant */
2087 STRBUF_INITIALIZER, /* String constant */
2088 0, /* Function called in case of errors */
2089 0, /* Major version number */
2090 0, /* Minor version number */
2091 COLLECTION_INITIALIZER, /* Line information */
2092 0, /* Pointer to debug info */
2094 D.FileName = FileName;
2097 /* Open the input file */
2098 D.F = fopen (D.FileName, "r");
2101 ParseError (&D, CC65_ERROR,
2102 "Cannot open input file \"%s\": %s",
2103 D.FileName, strerror (errno));
2107 /* Create a new debug info struct */
2108 D.Info = NewDbgInfo ();
2110 /* Prime the pump */
2114 while (D.Tok != TOK_EOF) {
2139 /* Output a warning, then skip the line with the unknown
2140 * keyword that may have been added by a later version.
2142 ParseError (&D, CC65_WARNING,
2143 "Unknown keyword \"%s\" - skipping",
2144 SB_GetConstBuf (&D.SVal));
2150 UnexpectedToken (&D);
2154 /* EOL or EOF must follow */
2155 if (D.Tok != TOK_EOF) {
2156 if (D.Tok != TOK_EOL) {
2157 ParseError (&D, 1, "Extra tokens in line");
2164 /* Close the file */
2167 /* Free memory allocated for SVal */
2170 /* In case of errors, delete the debug info already allocated and
2174 /* Free allocated stuff */
2176 for (I = 0; I < CollCount (&D.LineInfos); ++I) {
2177 FreeLineInfo (CollAt (&D.LineInfos, I));
2179 DoneCollection (&D.LineInfos);
2180 FreeDbgInfo (D.Info);
2184 /* We do now have all the information from the input file. Do
2187 ProcessSegInfo (&D);
2188 ProcessFileInfo (&D);
2189 ProcessLineInfo (&D);
2191 /* Free the collection that contained the line info */
2192 DoneCollection (&D.LineInfos);
2194 /* Return the debug info struct that was created */
2200 void cc65_free_dbginfo (cc65_dbginfo Handle)
2201 /* Free debug information read from a file */
2204 FreeDbgInfo (Handle);
2210 cc65_lineinfo* cc65_lineinfo_byaddr (cc65_dbginfo Handle, unsigned long Addr)
2211 /* Return line information for the given address. The function returns 0
2212 * if no line information was found.
2216 Collection* FileInfoByName;
2217 cc65_lineinfo* D = 0;
2219 /* We will place a list of line infos in a collection */
2220 Collection LineInfos = COLLECTION_INITIALIZER;
2222 /* Check the parameter */
2223 assert (Handle != 0);
2225 /* Walk over all files and search for matching line infos */
2226 FileInfoByName = &((DbgInfo*) Handle)->FileInfoByName;
2227 for (I = 0; I < CollCount (FileInfoByName); ++I) {
2228 /* Check if the file contains line info for this address */
2229 LineInfo* L = FindLineInfoByAddr (CollAt (FileInfoByName, I), Addr);
2231 CollAppend (&LineInfos, L);
2235 /* Do we have line infos? */
2236 if (CollCount (&LineInfos) > 0) {
2238 /* Prepare the struct we will return to the caller */
2239 D = xmalloc (sizeof (*D) +
2240 (CollCount (&LineInfos) - 1) * sizeof (D->data[0]));
2241 D->count = CollCount (&LineInfos);
2242 for (I = 0; I < D->count; ++I) {
2244 /* Pointer to this info */
2245 LineInfo* L = CollAt (&LineInfos, I);
2248 D->data[I].name = L->File.Info->FileName;
2249 D->data[I].size = L->File.Info->Size;
2250 D->data[I].mtime = L->File.Info->MTime;
2251 D->data[I].line = L->Line;
2252 D->data[I].start = L->Start;
2253 D->data[I].end = L->End;
2257 /* Free the line info collection */
2258 DoneCollection (&LineInfos);
2260 /* Return the struct we've created */
2266 cc65_lineinfo* cc65_lineinfo_byname (cc65_dbginfo Handle, const char* FileName,
2268 /* Return line information for a file/line number combination. The function
2269 * returns NULL if no line information was found.
2277 /* Check the parameter */
2278 assert (Handle != 0);
2280 /* The handle is actually a pointer to a debug info struct */
2281 Info = (DbgInfo*) Handle;
2283 /* Get the file info */
2284 F = FindFileInfoByName (&Info->FileInfoByName, FileName);
2286 /* File not found */
2290 /* Search in the file for the given line */
2291 L = FindLineInfoByLine (F, Line);
2293 /* Line not found */
2297 /* Prepare the struct we will return to the caller */
2298 D = xmalloc (sizeof (*D));
2302 D->data[0].name = L->File.Info->FileName;
2303 D->data[0].size = L->File.Info->Size;
2304 D->data[0].mtime = L->File.Info->MTime;
2305 D->data[0].line = L->Line;
2306 D->data[0].start = L->Start;
2307 D->data[0].end = L->End;
2309 /* Return the allocated struct */
2315 void cc65_free_lineinfo (cc65_dbginfo Handle, cc65_lineinfo* Info)
2316 /* Free line info returned by one of the other functions */
2318 /* Just for completeness, check the handle */
2319 assert (Handle != 0);
2321 /* Just free the memory */
2327 cc65_filelist* cc65_get_filelist (cc65_dbginfo Handle)
2328 /* Return a list of all files referenced in the debug information */
2331 Collection* FileInfoByName;
2335 /* Check the parameter */
2336 assert (Handle != 0);
2338 /* The handle is actually a pointer to a debug info struct */
2339 Info = (DbgInfo*) Handle;
2341 /* Get a pointer to the file list */
2342 FileInfoByName = &Info->FileInfoByName;
2344 /* Allocate memory for the data structure returned to the caller */
2345 D = xmalloc (sizeof (*D) - sizeof (D->data[0]) +
2346 CollCount (FileInfoByName) * sizeof (D->data[0]));
2348 /* Fill in the data */
2349 D->count = CollCount (FileInfoByName);
2350 for (I = 0; I < CollCount (FileInfoByName); ++I) {
2353 FileInfo* F = CollAt (FileInfoByName, I);
2356 D->data[I].name = F->FileName;
2357 D->data[I].size = F->Size;
2358 D->data[I].mtime = F->MTime;
2361 /* Return the result */
2367 void cc65_free_filelist (cc65_dbginfo Handle, cc65_filelist* List)
2368 /* Free a file list returned by cc65_get_filelist() */
2370 /* Just for completeness, check the handle */
2371 assert (Handle != 0);
2373 /* Just free the memory */
2379 cc65_segmentlist* cc65_get_segmentlist (cc65_dbginfo Handle)
2380 /* Return a list of all segments referenced in the debug information */
2383 Collection* SegInfoByName;
2384 cc65_segmentlist* D;
2387 /* Check the parameter */
2388 assert (Handle != 0);
2390 /* The handle is actually a pointer to a debug info struct */
2391 Info = (DbgInfo*) Handle;
2393 /* Get a pointer to the file list */
2394 SegInfoByName = &Info->SegInfoByName;
2396 /* Allocate memory for the data structure returned to the caller */
2397 D = xmalloc (sizeof (*D) - sizeof (D->data[0]) +
2398 CollCount (SegInfoByName) * sizeof (D->data[0]));
2400 /* Fill in the data */
2401 D->count = CollCount (SegInfoByName);
2402 for (I = 0; I < CollCount (SegInfoByName); ++I) {
2405 SegInfo* S = CollAt (SegInfoByName, I);
2408 D->data[I].name = S->SegName;
2409 D->data[I].start = S->Start;
2410 D->data[I].end = S->Start + S->Size - 1;
2413 /* Return the result */
2419 void cc65_free_segmentlist (cc65_dbginfo Handle, cc65_segmentlist* List)
2420 /* Free a file list returned by cc65_get_filelist() */
2422 /* Just for completeness, check the handle */
2423 assert (Handle != 0);
2425 /* Just free the memory */