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 /*****************************************************************************/
55 /* Version numbers of the debug format we understand */
60 typedef struct StrBuf StrBuf;
62 char* Buf; /* Pointer to buffer */
63 unsigned Len; /* Length of the string */
64 unsigned Allocated; /* Size of allocated memory */
67 /* Initializer for a string buffer */
68 #define STRBUF_INITIALIZER { 0, 0, 0 }
70 /* An array of pointers that grows if needed */
71 typedef struct Collection Collection;
73 unsigned Count; /* Number of items in the list */
74 unsigned Size; /* Size of allocated array */
75 void** Items; /* Array with dynamic size */
78 /* Initializer for static collections */
79 #define COLLECTION_INITIALIZER { 0, 0, 0 }
83 /* Data structure containing information from the debug info file. A pointer
84 * to this structure is passed as handle to callers from the outside.
86 typedef struct DbgInfo DbgInfo;
88 Collection SegInfoByName; /* Segment infos sorted by name */
89 Collection SegInfoById; /* Segment infos sorted by id */
90 Collection FileInfoByName; /* File infos sorted by name */
91 Collection FileInfoById; /* File infos sorted by id */
92 Collection LineInfoByAddr; /* Line information sorted by address */
98 TOK_INVALID, /* Invalid token */
99 TOK_EOF, /* End of file reached */
101 TOK_INTCON, /* Integer constant */
102 TOK_STRCON, /* String constant */
110 TOK_ABSOLUTE, /* ABSOLUTE keyword */
111 TOK_ADDRSIZE, /* ADDRSIZE keyword */
112 TOK_EQUATE, /* EQUATE keyword */
113 TOK_FILE, /* FILE keyword */
114 TOK_ID, /* ID keyword */
115 TOK_LABEL, /* LABEL keyword */
116 TOK_LINE, /* LINE keyword */
117 TOK_LONG, /* LONG_keyword */
118 TOK_MAJOR, /* MAJOR keyword */
119 TOK_MINOR, /* MINOR keyword */
120 TOK_MTIME, /* MTIME keyword */
121 TOK_NAME, /* NAME keyword */
122 TOK_OUTPUTNAME, /* OUTPUTNAME keyword */
123 TOK_OUTPUTOFFS, /* OUTPUTOFFS keyword */
124 TOK_RANGE, /* RANGE keyword */
125 TOK_RO, /* RO keyword */
126 TOK_RW, /* RW keyword */
127 TOK_SEGMENT, /* SEGMENT keyword */
128 TOK_SIZE, /* SIZE keyword */
129 TOK_START, /* START keyword */
130 TOK_SYM, /* SYM keyword */
131 TOK_TYPE, /* TYPE keyword */
132 TOK_VALUE, /* VALUE keyword */
133 TOK_VERSION, /* VERSION keyword */
134 TOK_ZEROPAGE, /* ZEROPAGE keyword */
136 TOK_IDENT, /* To catch unknown keywords */
139 /* Data used when parsing the debug info file */
140 typedef struct InputData InputData;
142 const char* FileName; /* Name of input file */
143 cc65_line Line; /* Current line number */
144 unsigned Col; /* Current column number */
145 cc65_line SLine; /* Line number at start of token */
146 unsigned SCol; /* Column number at start of token */
147 unsigned Errors; /* Number of errors */
148 FILE* F; /* Input file */
149 int C; /* Input character */
150 Token Tok; /* Token from input stream */
151 unsigned long IVal; /* Integer constant */
152 StrBuf SVal; /* String constant */
153 cc65_errorfunc Error; /* Function called in case of errors */
154 unsigned MajorVersion; /* Major version number */
155 unsigned MinorVersion; /* Minor version number */
156 DbgInfo* Info; /* Pointer to debug info */
159 /* Internally used segment info struct */
160 typedef struct SegInfo SegInfo;
162 unsigned Id; /* Id of segment */
163 cc65_addr Start; /* Start address of segment */
164 cc65_addr Size; /* Size of segment */
165 char* OutputName; /* Name of output file */
166 unsigned long OutputOffs; /* Offset in output file */
167 char SegName[1]; /* Name of segment */
170 /* Internally used file info struct */
171 typedef struct FileInfo FileInfo;
173 unsigned Id; /* Id of file */
174 unsigned long Size; /* Size of file */
175 unsigned long MTime; /* Modification time */
176 cc65_addr Start; /* Start address of line infos */
177 cc65_addr End; /* End address of line infos */
178 Collection LineInfoByAddr; /* Line infos sorted by address */
179 Collection LineInfoByLine; /* Line infos sorted by line */
180 char FileName[1]; /* Name of file with full path */
183 /* Internally used line info struct */
184 typedef struct LineInfo LineInfo;
186 cc65_addr Start; /* Start of data range */
187 cc65_addr End; /* End of data range */
188 cc65_line Line; /* Line number */
190 unsigned Id; /* Id of file */
191 FileInfo* Info; /* Pointer to file info */
194 unsigned Id; /* Id of segment */
195 SegInfo* Info; /* Pointer to segment info */
201 /*****************************************************************************/
203 /*****************************************************************************/
207 static void NextToken (InputData* D);
208 /* Read the next token from the input stream */
212 /*****************************************************************************/
213 /* Memory allocation */
214 /*****************************************************************************/
218 static void* xmalloc (size_t Size)
219 /* Allocate memory, check for out of memory condition. Do some debugging */
223 /* Allow zero sized requests and return NULL in this case */
226 /* Allocate memory */
229 /* Check for errors */
233 /* Return a pointer to the block */
239 static void* xrealloc (void* P, size_t Size)
240 /* Reallocate a memory block, check for out of memory */
242 /* Reallocate the block */
243 void* N = realloc (P, Size);
245 /* Check for errors */
246 assert (N != 0 || Size == 0);
248 /* Return the pointer to the new block */
254 static void xfree (void* Block)
255 /* Free the block, do some debugging */
262 /*****************************************************************************/
263 /* Dynamic strings */
264 /*****************************************************************************/
268 static void SB_Done (StrBuf* B)
269 /* Free the data of a string buffer (but not the struct itself) */
278 static void SB_Realloc (StrBuf* B, unsigned NewSize)
279 /* Reallocate the string buffer space, make sure at least NewSize bytes are
283 /* Get the current size, use a minimum of 8 bytes */
284 unsigned NewAllocated = B->Allocated;
285 if (NewAllocated == 0) {
289 /* Round up to the next power of two */
290 while (NewAllocated < NewSize) {
294 /* Reallocate the buffer. Beware: The allocated size may be zero while the
295 * length is not. This means that we have a buffer that wasn't allocated
299 /* Just reallocate the block */
300 B->Buf = xrealloc (B->Buf, NewAllocated);
302 /* Allocate a new block and copy */
303 B->Buf = memcpy (xmalloc (NewAllocated), B->Buf, B->Len);
306 /* Remember the new block size */
307 B->Allocated = NewAllocated;
312 static void SB_CheapRealloc (StrBuf* B, unsigned NewSize)
313 /* Reallocate the string buffer space, make sure at least NewSize bytes are
314 * available. This function won't copy the old buffer contents over to the new
315 * buffer and may be used if the old contents are overwritten later.
318 /* Get the current size, use a minimum of 8 bytes */
319 unsigned NewAllocated = B->Allocated;
320 if (NewAllocated == 0) {
324 /* Round up to the next power of two */
325 while (NewAllocated < NewSize) {
329 /* Free the old buffer if there is one */
334 /* Allocate a fresh block */
335 B->Buf = xmalloc (NewAllocated);
337 /* Remember the new block size */
338 B->Allocated = NewAllocated;
343 static unsigned SB_GetLen (const StrBuf* B)
344 /* Return the length of the buffer contents */
351 static const char* SB_GetConstBuf (const StrBuf* B)
352 /* Return a buffer pointer */
359 static void SB_Terminate (StrBuf* B)
360 /* Zero terminate the given string buffer. NOTE: The terminating zero is not
361 * accounted for in B->Len, if you want that, you have to use AppendChar!
364 unsigned NewLen = B->Len + 1;
365 if (NewLen > B->Allocated) {
366 SB_Realloc (B, NewLen);
368 B->Buf[B->Len] = '\0';
373 static void SB_Clear (StrBuf* B)
374 /* Clear the string buffer (make it empty) */
381 static void SB_CopyBuf (StrBuf* Target, const char* Buf, unsigned Size)
382 /* Copy Buf to Target, discarding the old contents of Target */
385 if (Target->Allocated < Size) {
386 SB_CheapRealloc (Target, Size);
388 memcpy (Target->Buf, Buf, Size);
395 static void SB_Copy (StrBuf* Target, const StrBuf* Source)
396 /* Copy Source to Target, discarding the old contents of Target */
398 SB_CopyBuf (Target, Source->Buf, Source->Len);
403 static void SB_AppendChar (StrBuf* B, int C)
404 /* Append a character to a string buffer */
406 unsigned NewLen = B->Len + 1;
407 if (NewLen > B->Allocated) {
408 SB_Realloc (B, NewLen);
410 B->Buf[B->Len] = (char) C;
416 static char* SB_StrDup (const StrBuf* B)
417 /* Return the contents of B as a dynamically allocated string. The string
418 * will always be NUL terminated.
421 /* Allocate memory */
422 char* S = xmalloc (B->Len + 1);
424 /* Copy the string */
425 memcpy (S, B->Buf, B->Len);
430 /* And return the result */
436 /*****************************************************************************/
438 /*****************************************************************************/
442 static Collection* InitCollection (Collection* C)
443 /* Initialize a collection and return it. */
445 /* Intialize the fields. */
450 /* Return the new struct */
456 static void DoneCollection (Collection* C)
457 /* Free the data for a collection. This will not free the data contained in
461 /* Free the pointer array */
467 static unsigned CollCount (const Collection* C)
468 /* Return the number of items in the collection */
475 static void CollGrow (Collection* C, unsigned Size)
476 /* Grow the collection C so it is able to hold Size items without a resize
477 * being necessary. This can be called for performance reasons if the number
478 * of items to be placed in the collection is known in advance.
483 /* Ignore the call if the collection is already large enough */
484 if (Size <= C->Size) {
488 /* Grow the collection */
490 NewItems = xmalloc (C->Size * sizeof (void*));
491 memcpy (NewItems, C->Items, C->Count * sizeof (void*));
498 static void CollInsert (Collection* C, void* Item, unsigned Index)
499 /* Insert the data at the given position in the collection */
501 /* Check for invalid indices */
502 assert (Index <= C->Count);
504 /* Grow the array if necessary */
505 if (C->Count >= C->Size) {
507 CollGrow (C, (C->Size == 0)? 8 : C->Size * 2);
510 /* Move the existing elements if needed */
511 if (C->Count != Index) {
512 memmove (C->Items+Index+1, C->Items+Index, (C->Count-Index) * sizeof (void*));
516 /* Store the new item */
517 C->Items[Index] = Item;
522 static void CollAppend (Collection* C, void* Item)
523 /* Append an item to the end of the collection */
525 /* Insert the item at the end of the current list */
526 CollInsert (C, Item, C->Count);
531 static void* CollAt (Collection* C, unsigned Index)
532 /* Return the item at the given index */
534 /* Check the index */
535 assert (Index < C->Count);
537 /* Return the element */
538 return C->Items[Index];
543 static void* CollFirst (Collection* C)
544 /* Return the first item in a collection */
546 /* We must have at least one entry */
547 assert (C->Count > 0);
549 /* Return the element */
555 static void* CollLast (Collection* C)
556 /* Return the last item in a collection */
558 /* We must have at least one entry */
559 assert (C->Count > 0);
561 /* Return the element */
562 return C->Items[C->Count-1];
567 static void CollDelete (Collection* C, unsigned Index)
568 /* Remove the item with the given index from the collection. This will not
569 * free the item itself, just the pointer. All items with higher indices
570 * will get moved to a lower position.
573 /* Check the index */
574 assert (Index < C->Count);
576 /* Remove the item pointer */
578 memmove (C->Items+Index, C->Items+Index+1, (C->Count-Index) * sizeof (void*));
583 static void CollQuickSort (Collection* C, int Lo, int Hi,
584 int (*Compare) (const void*, const void*))
585 /* Internal recursive sort function. */
587 /* Get a pointer to the items */
588 void** Items = C->Items;
595 while (I <= J && Compare (Items[Lo], Items[I]) >= 0) {
598 while (I <= J && Compare (Items[Lo], Items[J]) < 0) {
603 void* Tmp = Items[I];
612 void* Tmp = Items[J];
613 Items[J] = Items[Lo];
616 if (J > (Hi + Lo) / 2) {
617 CollQuickSort (C, J + 1, Hi, Compare);
620 CollQuickSort (C, Lo, J - 1, Compare);
628 void CollSort (Collection* C, int (*Compare) (const void*, const void*))
629 /* Sort the collection using the given compare function. */
632 CollQuickSort (C, 0, C->Count-1, Compare);
638 /*****************************************************************************/
640 /*****************************************************************************/
644 static SegInfo* NewSegInfo (const StrBuf* SegName, unsigned Id,
645 cc65_addr Start, cc65_addr Size,
646 const StrBuf* OutputName, unsigned long OutputOffs)
647 /* Create a new SegInfo struct and return it */
649 /* Allocate memory */
650 SegInfo* S = xmalloc (sizeof (SegInfo) + SB_GetLen (SegName));
656 if (SB_GetLen (OutputName) > 0) {
657 /* Output file given */
658 S->OutputName = SB_StrDup (OutputName);
659 S->OutputOffs = OutputOffs;
661 /* No output file given */
665 memcpy (S->SegName, SB_GetConstBuf (SegName), SB_GetLen (SegName) + 1);
673 static void FreeSegInfo (SegInfo* S)
674 /* Free a SegInfo struct */
676 xfree (S->OutputName);
682 static int CompareSegInfoByName (const void* L, const void* R)
683 /* Helper function to sort segment infos in a collection by name */
685 /* Sort by file name */
686 return strcmp (((const SegInfo*) L)->SegName,
687 ((const SegInfo*) R)->SegName);
692 static int CompareSegInfoById (const void* L, const void* R)
693 /* Helper function to sort segment infos in a collection by id */
695 if (((const SegInfo*) L)->Id > ((const SegInfo*) R)->Id) {
697 } else if (((const SegInfo*) L)->Id < ((const SegInfo*) R)->Id) {
706 /*****************************************************************************/
708 /*****************************************************************************/
712 static LineInfo* NewLineInfo (unsigned File, unsigned Seg, cc65_line Line,
713 cc65_addr Start, cc65_addr End)
714 /* Create a new LineInfo struct and return it */
716 /* Allocate memory */
717 LineInfo* L = xmalloc (sizeof (LineInfo));
732 static void FreeLineInfo (LineInfo* L)
733 /* Free a LineInfo struct */
740 static int CompareLineInfoByAddr (const void* L, const void* R)
741 /* Helper function to sort line infos in a collection by address */
743 /* Sort by start of range */
744 if (((const LineInfo*) L)->Start > ((const LineInfo*) R)->Start) {
746 } else if (((const LineInfo*) L)->Start < ((const LineInfo*) R)->Start) {
755 static int CompareLineInfoByLine (const void* L, const void* R)
756 /* Helper function to sort line infos in a collection by line */
758 if (((const LineInfo*) L)->Line > ((const LineInfo*) R)->Line) {
760 } else if (((const LineInfo*) L)->Line < ((const LineInfo*) R)->Line) {
769 /*****************************************************************************/
771 /*****************************************************************************/
775 static FileInfo* NewFileInfo (const StrBuf* FileName, unsigned Id,
776 unsigned long Size, unsigned long MTime)
777 /* Create a new FileInfo struct and return it */
779 /* Allocate memory */
780 FileInfo* F = xmalloc (sizeof (FileInfo) + SB_GetLen (FileName));
786 F->Start = ~(cc65_addr)0;
788 InitCollection (&F->LineInfoByAddr);
789 InitCollection (&F->LineInfoByLine);
790 memcpy (F->FileName, SB_GetConstBuf (FileName), SB_GetLen (FileName) + 1);
798 static void FreeFileInfo (FileInfo* F)
799 /* Free a FileInfo struct */
803 /* Walk through the collection with line infos and delete them */
804 for (I = 0; I < CollCount (&F->LineInfoByAddr); ++I) {
805 FreeLineInfo (CollAt (&F->LineInfoByAddr, I));
807 DoneCollection (&F->LineInfoByAddr);
808 DoneCollection (&F->LineInfoByLine);
810 /* Free the file info structure itself */
816 static int CompareFileInfoByName (const void* L, const void* R)
817 /* Helper function to sort file infos in a collection by name */
819 /* Sort by file name */
820 return strcmp (((const FileInfo*) L)->FileName,
821 ((const FileInfo*) R)->FileName);
826 static int CompareFileInfoById (const void* L, const void* R)
827 /* Helper function to sort file infos in a collection by id */
829 if (((const FileInfo*) L)->Id > ((const FileInfo*) R)->Id) {
831 } else if (((const FileInfo*) L)->Id < ((const FileInfo*) R)->Id) {
840 /*****************************************************************************/
842 /*****************************************************************************/
846 static DbgInfo* NewDbgInfo (void)
847 /* Create a new DbgInfo struct and return it */
849 /* Allocate memory */
850 DbgInfo* Info = xmalloc (sizeof (DbgInfo));
853 InitCollection (&Info->SegInfoByName);
854 InitCollection (&Info->SegInfoById);
855 InitCollection (&Info->FileInfoByName);
856 InitCollection (&Info->FileInfoById);
857 InitCollection (&Info->LineInfoByAddr);
865 static void FreeDbgInfo (DbgInfo* Info)
866 /* Free a DbgInfo struct */
870 /* Free segment info */
871 for (I = 0; I < CollCount (&Info->SegInfoByName); ++I) {
872 FreeSegInfo (CollAt (&Info->SegInfoByName, I));
874 DoneCollection (&Info->SegInfoByName);
875 DoneCollection (&Info->SegInfoById);
878 for (I = 0; I < CollCount (&Info->FileInfoByName); ++I) {
879 FreeFileInfo (CollAt (&Info->FileInfoByName, I));
881 DoneCollection (&Info->FileInfoByName);
882 DoneCollection (&Info->FileInfoById);
885 DoneCollection (&Info->LineInfoByAddr);
887 /* Free the structure itself */
893 /*****************************************************************************/
894 /* Helper functions */
895 /*****************************************************************************/
899 static void CopyLineInfo (cc65_linedata* D, const LineInfo* L)
900 /* Copy data from a LineInfo struct to the cc65_linedata struct returned to
904 D->source_name = L->File.Info->FileName;
905 D->source_size = L->File.Info->Size;
906 D->source_mtime = L->File.Info->MTime;
907 D->source_line = L->Line;
908 D->line_start = L->Start;
909 D->line_end = L->End;
910 if (L->Seg.Info->OutputName) {
911 D->output_name = L->Seg.Info->OutputName;
912 D->output_offs = L->Seg.Info->OutputOffs + L->Start - L->Seg.Info->Start;
921 static void ParseError (InputData* D, cc65_error_severity Type, const char* Msg, ...)
922 /* Call the user supplied parse error function */
928 /* Test-format the error message so we know how much space to allocate */
930 MsgSize = vsnprintf (0, 0, Msg, ap);
933 /* Allocate memory */
934 E = xmalloc (sizeof (*E) + MsgSize);
936 /* Write data to E */
938 E->name = D->FileName;
942 vsnprintf (E->errormsg, MsgSize+1, Msg, ap);
945 /* Call the caller:-) */
948 /* Free the data structure */
952 if (Type == CC65_ERROR) {
959 static void SkipLine (InputData* D)
960 /* Error recovery routine. Skip tokens until EOL or EOF is reached */
962 while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
969 static void UnexpectedToken (InputData* D)
970 /* Call ParseError with a message about an unexpected input token */
972 ParseError (D, CC65_ERROR, "Unexpected input token %d", D->Tok);
978 static void UnknownKeyword (InputData* D)
979 /* Print a warning about an unknown keyword in the file. Try to do smart
980 * recovery, so if later versions of the debug information add additional
981 * keywords, this code may be able to at least ignore them.
984 /* Output a warning */
985 ParseError (D, CC65_WARNING, "Unknown keyword \"%s\" - skipping",
986 SB_GetConstBuf (&D->SVal));
988 /* Skip the identifier */
991 /* If an equal sign follows, ignore anything up to the next line end
992 * or comma. If a comma or line end follows, we're already done. If
993 * we have none of both, we ignore the remainder of the line.
995 if (D->Tok == TOK_EQUAL) {
997 while (D->Tok != TOK_COMMA && D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1000 } else if (D->Tok != TOK_COMMA && D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1007 /*****************************************************************************/
1008 /* Scanner and parser */
1009 /*****************************************************************************/
1013 static int DigitVal (int C)
1014 /* Return the value for a numeric digit. Return -1 if C is invalid */
1018 } else if (isxdigit (C)) {
1019 return toupper (C) - 'A' + 10;
1027 static void NextChar (InputData* D)
1028 /* Read the next character from the input. Count lines and columns */
1030 /* Check if we've encountered EOF before */
1032 D->C = fgetc (D->F);
1044 static void NextToken (InputData* D)
1045 /* Read the next token from the input stream */
1047 static const struct KeywordEntry {
1048 const char Keyword[16];
1050 } KeywordTable[] = {
1051 { "absolute", TOK_ABSOLUTE },
1052 { "addrsize", TOK_ADDRSIZE },
1053 { "equate", TOK_EQUATE },
1054 { "file", TOK_FILE },
1056 { "label", TOK_LABEL },
1057 { "line", TOK_LINE },
1058 { "long", TOK_LONG },
1059 { "major", TOK_MAJOR },
1060 { "minor", TOK_MINOR },
1061 { "mtime", TOK_MTIME },
1062 { "name", TOK_NAME },
1063 { "outputname", TOK_OUTPUTNAME },
1064 { "outputoffs", TOK_OUTPUTOFFS },
1065 { "range", TOK_RANGE },
1068 { "segment", TOK_SEGMENT },
1069 { "size", TOK_SIZE },
1070 { "start", TOK_START },
1072 { "type", TOK_TYPE },
1073 { "value", TOK_VALUE },
1074 { "version", TOK_VERSION },
1075 { "zeropage", TOK_ZEROPAGE },
1079 /* Skip whitespace */
1080 while (D->C == ' ' || D->C == '\t') {
1084 /* Remember the current position as start of the next token */
1089 if (D->C == '_' || isalpha (D->C)) {
1091 const struct KeywordEntry* Entry;
1093 /* Read the identifier */
1094 SB_Clear (&D->SVal);
1095 while (D->C == '_' || isalnum (D->C)) {
1096 SB_AppendChar (&D->SVal, D->C);
1099 SB_Terminate (&D->SVal);
1101 /* Search the identifier in the keyword table */
1102 Entry = bsearch (SB_GetConstBuf (&D->SVal),
1104 sizeof (KeywordTable) / sizeof (KeywordTable[0]),
1105 sizeof (KeywordTable[0]),
1106 (int (*)(const void*, const void*)) strcmp);
1110 D->Tok = Entry->Tok;
1116 if (isdigit (D->C)) {
1121 if (toupper (D->C) == 'X') {
1131 while ((Val = DigitVal (D->C)) >= 0 && Val < Base) {
1132 D->IVal = D->IVal * Base + Val;
1135 D->Tok = TOK_INTCON;
1139 /* Other characters */
1163 SB_Clear (&D->SVal);
1166 if (D->C == '\n' || D->C == EOF) {
1167 ParseError (D, CC65_ERROR, "Unterminated string constant");
1174 SB_AppendChar (&D->SVal, D->C);
1177 SB_Terminate (&D->SVal);
1178 D->Tok = TOK_STRCON;
1191 ParseError (D, CC65_ERROR, "Invalid input character `%c'", D->C);
1198 static int TokenFollows (InputData* D, Token Tok, const char* Name)
1199 /* Check for a comma */
1201 if (D->Tok != Tok) {
1202 ParseError (D, CC65_ERROR, "%s expected", Name);
1212 static int IntConstFollows (InputData* D)
1213 /* Check for an integer constant */
1215 return TokenFollows (D, TOK_INTCON, "Integer constant");
1220 static int StrConstFollows (InputData* D)
1221 /* Check for a string literal */
1223 return TokenFollows (D, TOK_STRCON, "String literal");
1228 static int Consume (InputData* D, Token Tok, const char* Name)
1229 /* Check for a token and consume it. Return true if the token was comsumed,
1230 * return false otherwise.
1233 if (TokenFollows (D, Tok, Name)) {
1243 static int ConsumeEqual (InputData* D)
1244 /* Consume an equal sign */
1246 return Consume (D, TOK_EQUAL, "'='");
1251 static int ConsumeMinus (InputData* D)
1252 /* Consume a minus sign */
1254 return Consume (D, TOK_MINUS, "'-'");
1259 static void ConsumeEOL (InputData* D)
1260 /* Consume an end-of-line token, if we aren't at end-of-file */
1262 if (D->Tok != TOK_EOF) {
1263 if (D->Tok != TOK_EOL) {
1264 ParseError (D, CC65_ERROR, "Extra tokens in line");
1273 static void ParseFile (InputData* D)
1274 /* Parse a FILE line */
1278 unsigned long MTime;
1279 StrBuf FileName = STRBUF_INITIALIZER;
1287 ibRequired = ibId | ibFileName | ibSize | ibMTime,
1288 } InfoBits = ibNone;
1290 /* Skip the FILE token */
1293 /* More stuff follows */
1298 /* Check for an unknown keyword */
1299 if (D->Tok == TOK_IDENT) {
1304 /* Something we know? */
1305 if (D->Tok != TOK_ID && D->Tok != TOK_MTIME &&
1306 D->Tok != TOK_NAME && D->Tok != TOK_SIZE) {
1311 /* Remember the token, skip it, check for equal */
1314 if (!ConsumeEqual (D)) {
1318 /* Check what the token was */
1322 if (!IntConstFollows (D)) {
1331 if (!IntConstFollows (D)) {
1336 InfoBits |= ibMTime;
1340 if (!StrConstFollows (D)) {
1343 SB_Copy (&FileName, &D->SVal);
1344 SB_Terminate (&FileName);
1345 InfoBits |= ibFileName;
1350 if (!IntConstFollows (D)) {
1360 UnexpectedToken (D);
1366 if (D->Tok != TOK_COMMA) {
1372 /* Check for end of line */
1373 if (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1374 UnexpectedToken (D);
1379 /* Check for required information */
1380 if ((InfoBits & ibRequired) != ibRequired) {
1381 ParseError (D, CC65_ERROR, "Required attributes missing");
1385 /* Create the file info and remember it */
1386 F = NewFileInfo (&FileName, Id, Size, MTime);
1387 CollAppend (&D->Info->FileInfoByName, F);
1390 /* Entry point in case of errors */
1391 SB_Done (&FileName);
1397 static void ParseLine (InputData* D)
1398 /* Parse a LINE line */
1412 ibRequired = ibFile | ibSegment | ibLine | ibRange,
1413 } InfoBits = ibNone;
1415 /* Skip the LINE token */
1418 /* More stuff follows */
1423 /* Check for an unknown keyword */
1424 if (D->Tok == TOK_IDENT) {
1429 /* Something we know? */
1430 if (D->Tok != TOK_FILE && D->Tok != TOK_LINE &&
1431 D->Tok != TOK_RANGE && D->Tok != TOK_SEGMENT) {
1436 /* Remember the token, skip it, check for equal */
1439 if (!ConsumeEqual (D)) {
1443 /* Check what the token was */
1447 if (!IntConstFollows (D)) {
1456 if (!IntConstFollows (D)) {
1459 Line = (cc65_line) D->IVal;
1465 if (!IntConstFollows (D)) {
1468 Start = (cc65_addr) D->IVal;
1470 if (!ConsumeMinus (D)) {
1473 if (!IntConstFollows (D)) {
1476 End = (cc65_addr) D->IVal;
1478 InfoBits |= ibRange;
1482 if (!IntConstFollows (D)) {
1486 InfoBits |= ibSegment;
1492 UnexpectedToken (D);
1498 if (D->Tok != TOK_COMMA) {
1504 /* Check for end of line */
1505 if (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1506 UnexpectedToken (D);
1511 /* Check for required information */
1512 if ((InfoBits & ibRequired) != ibRequired) {
1513 ParseError (D, CC65_ERROR, "Required attributes missing");
1517 /* Create the line info and remember it */
1518 L = NewLineInfo (File, Segment, Line, Start, End);
1519 CollAppend (&D->Info->LineInfoByAddr, L);
1522 /* Entry point in case of errors */
1528 static void ParseSegment (InputData* D)
1529 /* Parse a SEGMENT line */
1534 StrBuf SegName = STRBUF_INITIALIZER;
1535 StrBuf OutputName = STRBUF_INITIALIZER;
1536 unsigned long OutputOffs;
1548 ibRequired = ibId | ibSegName | ibStart | ibSize | ibAddrSize | ibType,
1549 } InfoBits = ibNone;
1551 /* Skip the SEGMENT token */
1554 /* More stuff follows */
1559 /* Check for an unknown keyword */
1560 if (D->Tok == TOK_IDENT) {
1565 /* Something we know? */
1566 if (D->Tok != TOK_ADDRSIZE && D->Tok != TOK_ID &&
1567 D->Tok != TOK_NAME && D->Tok != TOK_OUTPUTNAME &&
1568 D->Tok != TOK_OUTPUTOFFS && D->Tok != TOK_SIZE &&
1569 D->Tok != TOK_START && D->Tok != TOK_TYPE) {
1574 /* Remember the token, skip it, check for equal */
1577 if (!ConsumeEqual (D)) {
1581 /* Check what the token was */
1586 InfoBits |= ibAddrSize;
1590 if (!IntConstFollows (D)) {
1599 if (!StrConstFollows (D)) {
1602 SB_Copy (&SegName, &D->SVal);
1603 SB_Terminate (&SegName);
1604 InfoBits |= ibSegName;
1608 case TOK_OUTPUTNAME:
1609 if (!StrConstFollows (D)) {
1612 SB_Copy (&OutputName, &D->SVal);
1613 SB_Terminate (&OutputName);
1614 InfoBits |= ibOutputName;
1618 case TOK_OUTPUTOFFS:
1619 if (!IntConstFollows (D)) {
1622 OutputOffs = D->IVal;
1624 InfoBits |= ibOutputOffs;
1628 if (!IntConstFollows (D)) {
1637 if (!IntConstFollows (D)) {
1640 Start = (cc65_addr) D->IVal;
1642 InfoBits |= ibStart;
1652 UnexpectedToken (D);
1658 if (D->Tok != TOK_COMMA) {
1664 /* Check for end of line */
1665 if (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1666 UnexpectedToken (D);
1671 /* Check for required and/or matched information */
1672 if ((InfoBits & ibRequired) != ibRequired) {
1673 ParseError (D, CC65_ERROR, "Required attributes missing");
1676 InfoBits &= (ibOutputName | ibOutputOffs);
1677 if (InfoBits != ibNone && InfoBits != (ibOutputName | ibOutputOffs)) {
1678 ParseError (D, CC65_ERROR,
1679 "Attributes \"outputname\" and \"outputoffs\" must be paired");
1683 /* Fix OutputOffs if not given */
1684 if (InfoBits == ibNone) {
1688 /* Create the segment info and remember it */
1689 S = NewSegInfo (&SegName, Id, Start, Size, &OutputName, OutputOffs);
1690 CollAppend (&D->Info->SegInfoByName, S);
1693 /* Entry point in case of errors */
1695 SB_Done (&OutputName);
1701 static void ParseSym (InputData* D)
1702 /* Parse a SYM line */
1704 cc65_symbol_type Type;
1706 StrBuf SymName = STRBUF_INITIALIZER;
1713 ibRequired = ibSymName | ibValue | ibAddrSize | ibType,
1714 } InfoBits = ibNone;
1716 /* Skip the SYM token */
1719 /* More stuff follows */
1724 /* Check for an unknown keyword */
1725 if (D->Tok == TOK_IDENT) {
1730 /* Something we know? */
1731 if (D->Tok != TOK_ADDRSIZE && D->Tok != TOK_NAME &&
1732 D->Tok != TOK_TYPE && D->Tok != TOK_VALUE) {
1737 /* Remember the token, skip it, check for equal */
1740 if (!ConsumeEqual (D)) {
1744 /* Check what the token was */
1749 InfoBits |= ibAddrSize;
1753 if (!StrConstFollows (D)) {
1756 SB_Copy (&SymName, &D->SVal);
1757 SB_Terminate (&SymName);
1758 InfoBits |= ibSymName;
1765 Type = CC65_SYM_EQUATE;
1768 Type = CC65_SYM_LABEL;
1771 ParseError (D, CC65_ERROR,
1772 "Unknown value for attribute \"type\"");
1781 if (!IntConstFollows (D)) {
1785 InfoBits |= ibValue;
1791 UnexpectedToken (D);
1797 if (D->Tok != TOK_COMMA) {
1803 /* Check for end of line */
1804 if (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1805 UnexpectedToken (D);
1810 /* Check for required and/or matched information */
1811 if ((InfoBits & ibRequired) != ibRequired) {
1812 ParseError (D, CC65_ERROR, "Required attributes missing");
1816 /* Create the symbol info and remember it */
1818 S = NewSegInfo (&SegName, Id, Start, Size, &OutputName, OutputOffs);
1819 CollAppend (&D->Info->SegInfoByName, S);
1823 /* Entry point in case of errors */
1830 static void ParseVersion (InputData* D)
1831 /* Parse a VERSION line */
1837 ibRequired = ibMajor | ibMinor,
1838 } InfoBits = ibNone;
1840 /* Skip the VERSION token */
1843 /* More stuff follows */
1844 while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1850 if (!ConsumeEqual (D)) {
1853 if (!IntConstFollows (D)) {
1856 D->MajorVersion = D->IVal;
1858 InfoBits |= ibMajor;
1863 if (!ConsumeEqual (D)) {
1866 if (!IntConstFollows (D)) {
1869 D->MinorVersion = D->IVal;
1871 InfoBits |= ibMinor;
1875 /* Try to skip unknown keywords that may have been added by
1882 UnexpectedToken (D);
1887 /* Comma follows before next attribute */
1888 if (D->Tok == TOK_COMMA) {
1890 } else if (D->Tok == TOK_EOL || D->Tok == TOK_EOF) {
1893 UnexpectedToken (D);
1898 /* Check for required information */
1899 if ((InfoBits & ibRequired) != ibRequired) {
1900 ParseError (D, CC65_ERROR, "Required attributes missing");
1905 /* Entry point in case of errors */
1911 /*****************************************************************************/
1912 /* Data processing */
1913 /*****************************************************************************/
1917 static SegInfo* FindSegInfoById (InputData* D, unsigned Id)
1918 /* Find the SegInfo with a given Id */
1920 /* Get a pointer to the segment info collection */
1921 Collection* SegInfos = &D->Info->SegInfoById;
1923 /* Do a binary search */
1925 int Hi = (int) CollCount (SegInfos) - 1;
1929 int Cur = (Lo + Hi) / 2;
1932 SegInfo* CurItem = CollAt (SegInfos, Cur);
1935 if (Id > CurItem->Id) {
1937 } else if (Id < CurItem->Id) {
1951 static FileInfo* FindFileInfoByName (Collection* FileInfos, const char* FileName)
1952 /* Find the FileInfo for a given file name */
1954 /* Do a binary search */
1956 int Hi = (int) CollCount (FileInfos) - 1;
1960 int Cur = (Lo + Hi) / 2;
1963 FileInfo* CurItem = CollAt (FileInfos, Cur);
1966 int Res = strcmp (CurItem->FileName, FileName);
1971 } else if (Res > 0) {
1985 static FileInfo* FindFileInfoById (Collection* FileInfos, unsigned Id)
1986 /* Find the FileInfo with a given Id */
1988 /* Do a binary search */
1990 int Hi = (int) CollCount (FileInfos) - 1;
1994 int Cur = (Lo + Hi) / 2;
1997 FileInfo* CurItem = CollAt (FileInfos, Cur);
2000 if (Id > CurItem->Id) {
2002 } else if (Id < CurItem->Id) {
2016 static void ProcessSegInfo (InputData* D)
2017 /* Postprocess segment infos */
2021 /* Get pointers to the segment info collections */
2022 Collection* SegInfoByName = &D->Info->SegInfoByName;
2023 Collection* SegInfoById = &D->Info->SegInfoById;
2025 /* Sort the segment infos by name */
2026 CollSort (SegInfoByName, CompareSegInfoByName);
2028 /* Copy all items over to the collection that will get sorted by id */
2029 for (I = 0; I < CollCount (SegInfoByName); ++I) {
2030 CollAppend (SegInfoById, CollAt (SegInfoByName, I));
2033 /* Sort this collection */
2034 CollSort (SegInfoById, CompareSegInfoById);
2039 static void ProcessFileInfo (InputData* D)
2040 /* Postprocess file infos */
2042 /* Get pointers to the file info collections */
2043 Collection* FileInfoByName = &D->Info->FileInfoByName;
2044 Collection* FileInfoById = &D->Info->FileInfoById;
2046 /* First, sort the file infos, so we can check for duplicates and do
2049 CollSort (FileInfoByName, CompareFileInfoByName);
2051 /* Cannot work on an empty collection */
2052 if (CollCount (FileInfoByName) > 0) {
2054 /* Walk through the file infos sorted by name and check for duplicates.
2055 * If we find some, warn and remove them, so the file infos are unique
2058 FileInfo* F = CollAt (FileInfoByName, 0);
2060 while (I < CollCount (FileInfoByName)) {
2061 FileInfo* Next = CollAt (FileInfoByName, I);
2062 if (strcmp (F->FileName, Next->FileName) == 0) {
2063 /* Warn only if time stamp and/or size is different */
2064 if (F->Size != Next->Size || F->MTime != Next->MTime) {
2067 "Duplicate file entry for \"%s\"",
2070 /* Remove the duplicate entry */
2071 FreeFileInfo (Next);
2072 CollDelete (FileInfoByName, I);
2074 /* This one is ok, check the next entry */
2080 /* Copy the file infos to another collection that will be sorted by id */
2081 for (I = 0; I < CollCount (FileInfoByName); ++I) {
2082 CollAppend (FileInfoById, CollAt (FileInfoByName, I));
2085 /* Sort this collection */
2086 CollSort (FileInfoById, CompareFileInfoById);
2092 static void ProcessLineInfo (InputData* D)
2093 /* Postprocess line infos */
2095 /* Get pointers to the collections */
2096 Collection* LineInfos = &D->Info->LineInfoByAddr;
2097 Collection* FileInfos = &D->Info->FileInfoByName;
2099 /* Walk over the line infos and replace the id numbers of file and segment
2100 * with pointers to the actual structs. Add the line info to each file
2101 * where it is defined.
2104 FileInfo* LastFileInfo = 0;
2105 SegInfo* LastSegInfo = 0;
2106 while (I < CollCount (LineInfos)) {
2111 /* Get LineInfo struct */
2112 LineInfo* L = CollAt (LineInfos, I);
2114 /* Find the FileInfo that corresponds to Id. We cache the last file
2115 * info in LastFileInfo to speedup searching.
2117 if (LastFileInfo && LastFileInfo->Id == L->File.Id) {
2120 F = FindFileInfoById (&D->Info->FileInfoById, L->File.Id);
2122 /* If we have no corresponding file info, print a warning and
2123 * remove the line info.
2128 "No file info for file with id %u",
2131 CollDelete (LineInfos, I);
2135 /* Otherwise remember it for later */
2139 /* Replace the file id by a pointer to the file info */
2142 /* Find the SegInfo that corresponds to Id. We cache the last file
2143 * info in LastSegInfo to speedup searching.
2145 if (LastSegInfo && LastSegInfo->Id == L->Seg.Id) {
2148 S = FindSegInfoById (D, L->Seg.Id);
2150 /* If we have no corresponding segment info, print a warning and
2151 * remove the line info.
2156 "No segment info for segment with id %u",
2159 CollDelete (LineInfos, I);
2163 /* Otherwise remember it for later */
2167 /* Replace the segment id by a pointer to the segment info */
2170 /* Add this line info to the file where it is defined */
2171 CollAppend (&F->LineInfoByAddr, L);
2172 CollAppend (&F->LineInfoByLine, L);
2178 /* Walk over all files and sort the line infos for each file so we can
2179 * do a binary search later.
2181 for (I = 0; I < CollCount (FileInfos); ++I) {
2183 /* Get a pointer to this file info */
2184 FileInfo* F = CollAt (FileInfos, I);
2186 /* Sort the line infos for this file */
2187 CollSort (&F->LineInfoByAddr, CompareLineInfoByAddr);
2188 CollSort (&F->LineInfoByLine, CompareLineInfoByLine);
2190 /* If there are line info entries, place the first and last address
2191 * of into the FileInfo struct itself, so we can rule out a FileInfo
2192 * quickly when mapping an address to a line info.
2194 if (CollCount (&F->LineInfoByAddr) > 0) {
2195 F->Start = ((const LineInfo*) CollFirst (&F->LineInfoByAddr))->Start;
2196 F->End = ((const LineInfo*) CollLast (&F->LineInfoByAddr))->End;
2200 /* Sort the collection with all line infos by address */
2201 CollSort (LineInfos, CompareLineInfoByAddr);
2206 static LineInfo* FindLineInfoByAddr (FileInfo* F, cc65_addr Addr)
2207 /* Find the LineInfo for a given address */
2209 Collection* LineInfoByAddr;
2214 /* Each file info contains the first and last address for which line
2215 * info is available, so we can rule out non matching ones quickly.
2217 if (Addr < F->Start || Addr > F->End) {
2221 /* Get a pointer to the line info collection for this file */
2222 LineInfoByAddr = &F->LineInfoByAddr;
2224 /* Do a binary search */
2226 Hi = (int) CollCount (LineInfoByAddr) - 1;
2230 int Cur = (Lo + Hi) / 2;
2233 LineInfo* CurItem = CollAt (LineInfoByAddr, Cur);
2236 if (Addr < CurItem->Start) {
2238 } else if (Addr > CurItem->End) {
2252 static LineInfo* FindLineInfoByLine (FileInfo* F, cc65_line Line)
2253 /* Find the LineInfo for a given line number */
2259 /* Get a pointer to the line info collection for this file */
2260 Collection* LineInfoByLine = &F->LineInfoByLine;
2262 /* Do a binary search */
2264 Hi = (int) CollCount (LineInfoByLine) - 1;
2268 int Cur = (Lo + Hi) / 2;
2271 LineInfo* CurItem = CollAt (LineInfoByLine, Cur);
2274 if (Line < CurItem->Line) {
2276 } else if (Line > CurItem->Line) {
2290 /*****************************************************************************/
2292 /*****************************************************************************/
2296 cc65_dbginfo cc65_read_dbginfo (const char* FileName, cc65_errorfunc ErrFunc)
2297 /* Parse the debug info file with the given name. On success, the function
2298 * will return a pointer to an opaque cc65_dbginfo structure, that must be
2299 * passed to the other functions in this module to retrieve information.
2300 * errorfunc is called in case of warnings and errors. If the file cannot be
2301 * read successfully, NULL is returned.
2304 /* Data structure used to control scanning and parsing */
2306 0, /* Name of input file */
2307 1, /* Line number */
2309 0, /* Line at start of current token */
2310 0, /* Column at start of current token */
2311 0, /* Number of errors */
2313 ' ', /* Input character */
2314 TOK_INVALID, /* Input token */
2315 0, /* Integer constant */
2316 STRBUF_INITIALIZER, /* String constant */
2317 0, /* Function called in case of errors */
2318 0, /* Major version number */
2319 0, /* Minor version number */
2320 0, /* Pointer to debug info */
2322 D.FileName = FileName;
2325 /* Open the input file */
2326 D.F = fopen (D.FileName, "r");
2329 ParseError (&D, CC65_ERROR,
2330 "Cannot open input file \"%s\": %s",
2331 D.FileName, strerror (errno));
2335 /* Create a new debug info struct */
2336 D.Info = NewDbgInfo ();
2338 /* Prime the pump */
2341 /* The first line in the file must specify version information */
2342 if (D.Tok != TOK_VERSION) {
2343 ParseError (&D, CC65_ERROR,
2344 "\"version\" keyword missing in first line - this is not "
2345 "a valid debug info file");
2348 /* Parse the version directive and check the version */
2350 if (D.MajorVersion > VER_MAJOR) {
2351 ParseError (&D, CC65_WARNING,
2352 "The format of this debug info file is newer than what we "
2353 "know. Will proceed but probably fail. Version found = %u, "
2354 "version supported = %u",
2355 D.MajorVersion, VER_MAJOR);
2360 while (D.Tok != TOK_EOF) {
2381 /* Output a warning, then skip the line with the unknown
2382 * keyword that may have been added by a later version.
2384 ParseError (&D, CC65_WARNING,
2385 "Unknown keyword \"%s\" - skipping",
2386 SB_GetConstBuf (&D.SVal));
2392 UnexpectedToken (&D);
2396 /* EOL or EOF must follow */
2401 /* Close the file */
2404 /* Free memory allocated for SVal */
2407 /* In case of errors, delete the debug info already allocated and
2411 /* Free allocated stuff */
2413 for (I = 0; I < CollCount (&D.Info->LineInfoByAddr); ++I) {
2414 FreeLineInfo (CollAt (&D.Info->LineInfoByAddr, I));
2416 DoneCollection (&D.Info->LineInfoByAddr);
2417 FreeDbgInfo (D.Info);
2421 /* We do now have all the information from the input file. Do
2424 ProcessSegInfo (&D);
2425 ProcessFileInfo (&D);
2426 ProcessLineInfo (&D);
2428 /* Return the debug info struct that was created */
2434 void cc65_free_dbginfo (cc65_dbginfo Handle)
2435 /* Free debug information read from a file */
2438 FreeDbgInfo (Handle);
2444 cc65_lineinfo* cc65_lineinfo_byaddr (cc65_dbginfo Handle, unsigned long Addr)
2445 /* Return line information for the given address. The function returns 0
2446 * if no line information was found.
2450 Collection* FileInfoByName;
2451 cc65_lineinfo* D = 0;
2453 /* We will place a list of line infos in a collection */
2454 Collection LineInfos = COLLECTION_INITIALIZER;
2456 /* Check the parameter */
2457 assert (Handle != 0);
2459 /* Walk over all files and search for matching line infos */
2460 FileInfoByName = &((DbgInfo*) Handle)->FileInfoByName;
2461 for (I = 0; I < CollCount (FileInfoByName); ++I) {
2462 /* Check if the file contains line info for this address */
2463 LineInfo* L = FindLineInfoByAddr (CollAt (FileInfoByName, I), Addr);
2465 CollAppend (&LineInfos, L);
2469 /* Do we have line infos? */
2470 if (CollCount (&LineInfos) > 0) {
2472 /* Prepare the struct we will return to the caller */
2473 D = xmalloc (sizeof (*D) +
2474 (CollCount (&LineInfos) - 1) * sizeof (D->data[0]));
2475 D->count = CollCount (&LineInfos);
2476 for (I = 0; I < D->count; ++I) {
2478 CopyLineInfo (D->data + I, CollAt (&LineInfos, I));
2482 /* Free the line info collection */
2483 DoneCollection (&LineInfos);
2485 /* Return the struct we've created */
2491 cc65_lineinfo* cc65_lineinfo_byname (cc65_dbginfo Handle, const char* FileName,
2493 /* Return line information for a file/line number combination. The function
2494 * returns NULL if no line information was found.
2502 /* Check the parameter */
2503 assert (Handle != 0);
2505 /* The handle is actually a pointer to a debug info struct */
2506 Info = (DbgInfo*) Handle;
2508 /* Get the file info */
2509 F = FindFileInfoByName (&Info->FileInfoByName, FileName);
2511 /* File not found */
2515 /* Search in the file for the given line */
2516 L = FindLineInfoByLine (F, Line);
2518 /* Line not found */
2522 /* Prepare the struct we will return to the caller */
2523 D = xmalloc (sizeof (*D));
2527 CopyLineInfo (D->data, L);
2529 /* Return the allocated struct */
2535 void cc65_free_lineinfo (cc65_dbginfo Handle, cc65_lineinfo* Info)
2536 /* Free line info returned by one of the other functions */
2538 /* Just for completeness, check the handle */
2539 assert (Handle != 0);
2541 /* Just free the memory */
2547 cc65_sourceinfo* cc65_get_sourcelist (cc65_dbginfo Handle)
2548 /* Return a list of all source files */
2551 Collection* FileInfoByName;
2555 /* Check the parameter */
2556 assert (Handle != 0);
2558 /* The handle is actually a pointer to a debug info struct */
2559 Info = (DbgInfo*) Handle;
2561 /* Get a pointer to the file list */
2562 FileInfoByName = &Info->FileInfoByName;
2564 /* Allocate memory for the data structure returned to the caller */
2565 D = xmalloc (sizeof (*D) - sizeof (D->data[0]) +
2566 CollCount (FileInfoByName) * sizeof (D->data[0]));
2568 /* Fill in the data */
2569 D->count = CollCount (FileInfoByName);
2570 for (I = 0; I < CollCount (FileInfoByName); ++I) {
2573 FileInfo* F = CollAt (FileInfoByName, I);
2576 D->data[I].source_name = F->FileName;
2577 D->data[I].source_size = F->Size;
2578 D->data[I].source_mtime = F->MTime;
2581 /* Return the result */
2587 void cc65_free_sourceinfo (cc65_dbginfo Handle, cc65_sourceinfo* Info)
2588 /* Free a source info record */
2590 /* Just for completeness, check the handle */
2591 assert (Handle != 0);
2593 /* Free the memory */
2599 cc65_segmentinfo* cc65_get_segmentlist (cc65_dbginfo Handle)
2600 /* Return a list of all segments referenced in the debug information */
2603 Collection* SegInfoByName;
2604 cc65_segmentinfo* D;
2607 /* Check the parameter */
2608 assert (Handle != 0);
2610 /* The handle is actually a pointer to a debug info struct */
2611 Info = (DbgInfo*) Handle;
2613 /* Get a pointer to the file list */
2614 SegInfoByName = &Info->SegInfoByName;
2616 /* Allocate memory for the data structure returned to the caller */
2617 D = xmalloc (sizeof (*D) - sizeof (D->data[0]) +
2618 CollCount (SegInfoByName) * sizeof (D->data[0]));
2620 /* Fill in the data */
2621 D->count = CollCount (SegInfoByName);
2622 for (I = 0; I < CollCount (SegInfoByName); ++I) {
2625 SegInfo* S = CollAt (SegInfoByName, I);
2628 D->data[I].segment_name = S->SegName;
2629 D->data[I].segment_start = S->Start;
2630 D->data[I].segment_size = S->Size;
2631 D->data[I].output_name = S->OutputName;
2632 D->data[I].output_offs = S->OutputOffs;
2635 /* Return the result */
2641 void cc65_free_segmentinfo (cc65_dbginfo Handle, cc65_segmentinfo* Info)
2642 /* Free a segment info record */
2644 /* Just for completeness, check the handle */
2645 assert (Handle != 0);
2647 /* Free the memory */