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 */
97 TOK_INVALID, /* Invalid token */
98 TOK_EOF, /* End of file reached */
100 TOK_INTCON, /* Integer constant */
101 TOK_STRCON, /* String constant */
109 TOK_ABSOLUTE, /* ABSOLUTE keyword */
110 TOK_ADDRSIZE, /* ADDRSIZE keyword */
111 TOK_EQUATE, /* EQUATE keyword */
112 TOK_FILE, /* FILE keyword */
113 TOK_ID, /* ID keyword */
114 TOK_LABEL, /* LABEL keyword */
115 TOK_LINE, /* LINE keyword */
116 TOK_LONG, /* LONG_keyword */
117 TOK_MAJOR, /* MAJOR keyword */
118 TOK_MINOR, /* MINOR keyword */
119 TOK_MTIME, /* MTIME keyword */
120 TOK_NAME, /* NAME keyword */
121 TOK_OUTPUTNAME, /* OUTPUTNAME keyword */
122 TOK_OUTPUTOFFS, /* OUTPUTOFFS keyword */
123 TOK_RANGE, /* RANGE keyword */
124 TOK_RO, /* RO keyword */
125 TOK_RW, /* RW keyword */
126 TOK_SEGMENT, /* SEGMENT keyword */
127 TOK_SIZE, /* SIZE keyword */
128 TOK_START, /* START keyword */
129 TOK_SYM, /* SYM keyword */
130 TOK_TYPE, /* TYPE keyword */
131 TOK_VALUE, /* VALUE keyword */
132 TOK_VERSION, /* VERSION keyword */
133 TOK_ZEROPAGE, /* ZEROPAGE keyword */
135 TOK_IDENT, /* To catch unknown keywords */
138 /* Data used when parsing the debug info file */
139 typedef struct InputData InputData;
141 const char* FileName; /* Name of input file */
142 cc65_line Line; /* Current line number */
143 unsigned Col; /* Current column number */
144 cc65_line SLine; /* Line number at start of token */
145 unsigned SCol; /* Column number at start of token */
146 unsigned Errors; /* Number of errors */
147 FILE* F; /* Input file */
148 int C; /* Input character */
149 Token Tok; /* Token from input stream */
150 unsigned long IVal; /* Integer constant */
151 StrBuf SVal; /* String constant */
152 cc65_errorfunc Error; /* Function called in case of errors */
153 unsigned MajorVersion; /* Major version number */
154 unsigned MinorVersion; /* Minor version number */
155 Collection LineInfos; /* Line information */
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 */
681 static int CompareSegInfoByName (const void* L, const void* R)
682 /* Helper function to sort segment infos in a collection by name */
684 /* Sort by file name */
685 return strcmp (((const SegInfo*) L)->SegName,
686 ((const SegInfo*) R)->SegName);
691 static int CompareSegInfoById (const void* L, const void* R)
692 /* Helper function to sort segment infos in a collection by id */
694 if (((const SegInfo*) L)->Id > ((const SegInfo*) R)->Id) {
696 } else if (((const SegInfo*) L)->Id < ((const SegInfo*) R)->Id) {
705 /*****************************************************************************/
707 /*****************************************************************************/
711 static LineInfo* NewLineInfo (unsigned File, unsigned Seg, cc65_line Line,
712 cc65_addr Start, cc65_addr End)
713 /* Create a new LineInfo struct and return it */
715 /* Allocate memory */
716 LineInfo* L = xmalloc (sizeof (LineInfo));
731 static void FreeLineInfo (LineInfo* L)
732 /* Free a LineInfo struct */
739 static int CompareLineInfoByAddr (const void* L, const void* R)
740 /* Helper function to sort line infos in a collection by address */
742 /* Sort by start of range */
743 if (((const LineInfo*) L)->Start > ((const LineInfo*) R)->Start) {
745 } else if (((const LineInfo*) L)->Start < ((const LineInfo*) R)->Start) {
754 static int CompareLineInfoByLine (const void* L, const void* R)
755 /* Helper function to sort line infos in a collection by line */
757 if (((const LineInfo*) L)->Line > ((const LineInfo*) R)->Line) {
759 } else if (((const LineInfo*) L)->Line < ((const LineInfo*) R)->Line) {
768 /*****************************************************************************/
770 /*****************************************************************************/
774 static FileInfo* NewFileInfo (const StrBuf* FileName, unsigned Id,
775 unsigned long Size, unsigned long MTime)
776 /* Create a new FileInfo struct and return it */
778 /* Allocate memory */
779 FileInfo* F = xmalloc (sizeof (FileInfo) + SB_GetLen (FileName));
785 F->Start = ~(cc65_addr)0;
787 InitCollection (&F->LineInfoByAddr);
788 InitCollection (&F->LineInfoByLine);
789 memcpy (F->FileName, SB_GetConstBuf (FileName), SB_GetLen (FileName) + 1);
797 static void FreeFileInfo (FileInfo* F)
798 /* Free a FileInfo struct */
802 /* Walk through the collection with line infos and delete them */
803 for (I = 0; I < CollCount (&F->LineInfoByAddr); ++I) {
804 FreeLineInfo (CollAt (&F->LineInfoByAddr, I));
806 DoneCollection (&F->LineInfoByAddr);
807 DoneCollection (&F->LineInfoByLine);
809 /* Free the file info structure itself */
815 static int CompareFileInfoByName (const void* L, const void* R)
816 /* Helper function to sort file infos in a collection by name */
818 /* Sort by file name */
819 return strcmp (((const FileInfo*) L)->FileName,
820 ((const FileInfo*) R)->FileName);
825 static int CompareFileInfoById (const void* L, const void* R)
826 /* Helper function to sort file infos in a collection by id */
828 if (((const FileInfo*) L)->Id > ((const FileInfo*) R)->Id) {
830 } else if (((const FileInfo*) L)->Id < ((const FileInfo*) R)->Id) {
839 /*****************************************************************************/
841 /*****************************************************************************/
845 static DbgInfo* NewDbgInfo (void)
846 /* Create a new DbgInfo struct and return it */
848 /* Allocate memory */
849 DbgInfo* Info = xmalloc (sizeof (DbgInfo));
852 InitCollection (&Info->SegInfoByName);
853 InitCollection (&Info->SegInfoById);
854 InitCollection (&Info->FileInfoByName);
855 InitCollection (&Info->FileInfoById);
863 static void FreeDbgInfo (DbgInfo* Info)
864 /* Free a DbgInfo struct */
868 /* Free segment info */
869 for (I = 0; I < CollCount (&Info->SegInfoByName); ++I) {
870 FreeSegInfo (CollAt (&Info->SegInfoByName, I));
872 DoneCollection (&Info->SegInfoByName);
873 DoneCollection (&Info->SegInfoById);
876 for (I = 0; I < CollCount (&Info->FileInfoByName); ++I) {
877 FreeFileInfo (CollAt (&Info->FileInfoByName, I));
879 DoneCollection (&Info->FileInfoByName);
880 DoneCollection (&Info->FileInfoById);
882 /* Free the structure itself */
888 /*****************************************************************************/
889 /* Helper functions */
890 /*****************************************************************************/
894 static void CopyLineInfo (cc65_linedata* D, const LineInfo* L)
895 /* Copy data from a LineInfo struct to the cc65_linedata struct returned to
899 D->source_name = L->File.Info->FileName;
900 D->source_size = L->File.Info->Size;
901 D->source_mtime = L->File.Info->MTime;
902 D->source_line = L->Line;
903 D->line_start = L->Start;
904 D->line_end = L->End;
905 if (L->Seg.Info->OutputName) {
906 D->output_name = L->Seg.Info->OutputName;
907 D->output_offs = L->Seg.Info->OutputOffs + L->Start - L->Seg.Info->Start;
916 static void ParseError (InputData* D, cc65_error_severity Type, const char* Msg, ...)
917 /* Call the user supplied parse error function */
923 /* Test-format the error message so we know how much space to allocate */
925 MsgSize = vsnprintf (0, 0, Msg, ap);
928 /* Allocate memory */
929 E = xmalloc (sizeof (*E) + MsgSize);
931 /* Write data to E */
933 E->name = D->FileName;
937 vsnprintf (E->errormsg, MsgSize+1, Msg, ap);
940 /* Call the caller:-) */
943 /* Free the data structure */
947 if (Type == CC65_ERROR) {
954 static void SkipLine (InputData* D)
955 /* Error recovery routine. Skip tokens until EOL or EOF is reached */
957 while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
964 static void UnexpectedToken (InputData* D)
965 /* Call ParseError with a message about an unexpected input token */
967 ParseError (D, CC65_ERROR, "Unexpected input token %d", D->Tok);
973 static void UnknownKeyword (InputData* D)
974 /* Print a warning about an unknown keyword in the file. Try to do smart
975 * recovery, so if later versions of the debug information add additional
976 * keywords, this code may be able to at least ignore them.
979 /* Output a warning */
980 ParseError (D, CC65_WARNING, "Unknown keyword \"%s\" - skipping",
981 SB_GetConstBuf (&D->SVal));
983 /* Skip the identifier */
986 /* If an equal sign follows, ignore anything up to the next line end
987 * or comma. If a comma or line end follows, we're already done. If
988 * we have none of both, we ignore the remainder of the line.
990 if (D->Tok == TOK_EQUAL) {
992 while (D->Tok != TOK_COMMA && D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
995 } else if (D->Tok != TOK_COMMA && D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1002 /*****************************************************************************/
1003 /* Scanner and parser */
1004 /*****************************************************************************/
1008 static int DigitVal (int C)
1009 /* Return the value for a numeric digit. Return -1 if C is invalid */
1013 } else if (isxdigit (C)) {
1014 return toupper (C) - 'A' + 10;
1022 static void NextChar (InputData* D)
1023 /* Read the next character from the input. Count lines and columns */
1025 /* Check if we've encountered EOF before */
1027 D->C = fgetc (D->F);
1039 static void NextToken (InputData* D)
1040 /* Read the next token from the input stream */
1042 static const struct KeywordEntry {
1043 const char Keyword[16];
1045 } KeywordTable[] = {
1046 { "absolute", TOK_ABSOLUTE },
1047 { "addrsize", TOK_ADDRSIZE },
1048 { "equate", TOK_EQUATE },
1049 { "file", TOK_FILE },
1051 { "label", TOK_LABEL },
1052 { "line", TOK_LINE },
1053 { "long", TOK_LONG },
1054 { "major", TOK_MAJOR },
1055 { "minor", TOK_MINOR },
1056 { "mtime", TOK_MTIME },
1057 { "name", TOK_NAME },
1058 { "outputname", TOK_OUTPUTNAME },
1059 { "outputoffs", TOK_OUTPUTOFFS },
1060 { "range", TOK_RANGE },
1063 { "segment", TOK_SEGMENT },
1064 { "size", TOK_SIZE },
1065 { "start", TOK_START },
1067 { "type", TOK_TYPE },
1068 { "value", TOK_VALUE },
1069 { "version", TOK_VERSION },
1070 { "zeropage", TOK_ZEROPAGE },
1074 /* Skip whitespace */
1075 while (D->C == ' ' || D->C == '\t') {
1079 /* Remember the current position as start of the next token */
1084 if (D->C == '_' || isalpha (D->C)) {
1086 const struct KeywordEntry* Entry;
1088 /* Read the identifier */
1089 SB_Clear (&D->SVal);
1090 while (D->C == '_' || isalnum (D->C)) {
1091 SB_AppendChar (&D->SVal, D->C);
1094 SB_Terminate (&D->SVal);
1096 /* Search the identifier in the keyword table */
1097 Entry = bsearch (SB_GetConstBuf (&D->SVal),
1099 sizeof (KeywordTable) / sizeof (KeywordTable[0]),
1100 sizeof (KeywordTable[0]),
1101 (int (*)(const void*, const void*)) strcmp);
1105 D->Tok = Entry->Tok;
1111 if (isdigit (D->C)) {
1116 if (toupper (D->C) == 'X') {
1126 while ((Val = DigitVal (D->C)) >= 0 && Val < Base) {
1127 D->IVal = D->IVal * Base + Val;
1130 D->Tok = TOK_INTCON;
1134 /* Other characters */
1158 SB_Clear (&D->SVal);
1161 if (D->C == '\n' || D->C == EOF) {
1162 ParseError (D, CC65_ERROR, "Unterminated string constant");
1169 SB_AppendChar (&D->SVal, D->C);
1172 SB_Terminate (&D->SVal);
1173 D->Tok = TOK_STRCON;
1186 ParseError (D, CC65_ERROR, "Invalid input character `%c'", D->C);
1193 static int TokenFollows (InputData* D, Token Tok, const char* Name)
1194 /* Check for a comma */
1196 if (D->Tok != Tok) {
1197 ParseError (D, CC65_ERROR, "%s expected", Name);
1207 static int IntConstFollows (InputData* D)
1208 /* Check for an integer constant */
1210 return TokenFollows (D, TOK_INTCON, "Integer constant");
1215 static int StrConstFollows (InputData* D)
1216 /* Check for a string literal */
1218 return TokenFollows (D, TOK_STRCON, "String literal");
1223 static int Consume (InputData* D, Token Tok, const char* Name)
1224 /* Check for a token and consume it. Return true if the token was comsumed,
1225 * return false otherwise.
1228 if (TokenFollows (D, Tok, Name)) {
1238 static int ConsumeEqual (InputData* D)
1239 /* Consume an equal sign */
1241 return Consume (D, TOK_EQUAL, "'='");
1246 static int ConsumeMinus (InputData* D)
1247 /* Consume a minus sign */
1249 return Consume (D, TOK_MINUS, "'-'");
1254 static void ConsumeEOL (InputData* D)
1255 /* Consume an end-of-line token, if we aren't at end-of-file */
1257 if (D->Tok != TOK_EOF) {
1258 if (D->Tok != TOK_EOL) {
1259 ParseError (D, CC65_ERROR, "Extra tokens in line");
1268 static void ParseFile (InputData* D)
1269 /* Parse a FILE line */
1273 unsigned long MTime;
1274 StrBuf FileName = STRBUF_INITIALIZER;
1282 ibRequired = ibId | ibFileName | ibSize | ibMTime,
1283 } InfoBits = ibNone;
1285 /* Skip the FILE token */
1288 /* More stuff follows */
1293 /* Check for an unknown keyword */
1294 if (D->Tok == TOK_IDENT) {
1299 /* Something we know? */
1300 if (D->Tok != TOK_ID && D->Tok != TOK_NAME &&
1301 D->Tok != TOK_SIZE && D->Tok != TOK_MTIME) {
1306 /* Remember the token, skip it, check for equal */
1309 if (!ConsumeEqual (D)) {
1313 /* Check what the token was */
1317 if (!IntConstFollows (D)) {
1326 if (!StrConstFollows (D)) {
1329 SB_Copy (&FileName, &D->SVal);
1330 SB_Terminate (&FileName);
1331 InfoBits |= ibFileName;
1336 if (!IntConstFollows (D)) {
1345 if (!IntConstFollows (D)) {
1350 InfoBits |= ibMTime;
1355 UnexpectedToken (D);
1361 if (D->Tok != TOK_COMMA) {
1367 /* Check for end of line */
1368 if (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1369 UnexpectedToken (D);
1374 /* Check for required information */
1375 if ((InfoBits & ibRequired) != ibRequired) {
1376 ParseError (D, CC65_ERROR, "Required attributes missing");
1380 /* Create the file info and remember it */
1381 F = NewFileInfo (&FileName, Id, Size, MTime);
1382 CollAppend (&D->Info->FileInfoByName, F);
1385 /* Entry point in case of errors */
1386 SB_Done (&FileName);
1392 static void ParseLine (InputData* D)
1393 /* Parse a LINE line */
1407 ibRequired = ibFile | ibSegment | ibLine | ibRange,
1408 } InfoBits = ibNone;
1410 /* Skip the LINE token */
1413 /* More stuff follows */
1418 /* Check for an unknown keyword */
1419 if (D->Tok == TOK_IDENT) {
1424 /* Something we know? */
1425 if (D->Tok != TOK_FILE && D->Tok != TOK_SEGMENT &&
1426 D->Tok != TOK_LINE && D->Tok != TOK_RANGE) {
1431 /* Remember the token, skip it, check for equal */
1434 if (!ConsumeEqual (D)) {
1438 /* Check what the token was */
1442 if (!IntConstFollows (D)) {
1451 if (!IntConstFollows (D)) {
1455 InfoBits |= ibSegment;
1460 if (!IntConstFollows (D)) {
1463 Line = (cc65_line) D->IVal;
1469 if (!IntConstFollows (D)) {
1472 Start = (cc65_addr) D->IVal;
1474 if (!ConsumeMinus (D)) {
1477 if (!IntConstFollows (D)) {
1480 End = (cc65_addr) D->IVal;
1482 InfoBits |= ibRange;
1487 UnexpectedToken (D);
1493 if (D->Tok != TOK_COMMA) {
1499 /* Check for end of line */
1500 if (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1501 UnexpectedToken (D);
1506 /* Check for required information */
1507 if ((InfoBits & ibRequired) != ibRequired) {
1508 ParseError (D, CC65_ERROR, "Required attributes missing");
1512 /* Create the line info and remember it */
1513 L = NewLineInfo (File, Segment, Line, Start, End);
1514 CollAppend (&D->LineInfos, L);
1517 /* Entry point in case of errors */
1523 static void ParseSegment (InputData* D)
1524 /* Parse a SEGMENT line */
1529 StrBuf SegName = STRBUF_INITIALIZER;
1530 StrBuf OutputName = STRBUF_INITIALIZER;
1531 unsigned long OutputOffs;
1543 ibRequired = ibId | ibSegName | ibStart | ibSize | ibAddrSize | ibType,
1544 } InfoBits = ibNone;
1546 /* Skip the SEGMENT token */
1549 /* More stuff follows */
1554 /* Check for an unknown keyword */
1555 if (D->Tok == TOK_IDENT) {
1560 /* Something we know? */
1561 if (D->Tok != TOK_ADDRSIZE && D->Tok != TOK_ID &&
1562 D->Tok != TOK_NAME && D->Tok != TOK_OUTPUTNAME &&
1563 D->Tok != TOK_OUTPUTOFFS && D->Tok != TOK_SIZE &&
1564 D->Tok != TOK_START && D->Tok != TOK_TYPE) {
1569 /* Remember the token, skip it, check for equal */
1572 if (!ConsumeEqual (D)) {
1576 /* Check what the token was */
1580 if (!IntConstFollows (D)) {
1589 if (!StrConstFollows (D)) {
1592 SB_Copy (&SegName, &D->SVal);
1593 SB_Terminate (&SegName);
1594 InfoBits |= ibSegName;
1598 case TOK_OUTPUTNAME:
1599 if (!StrConstFollows (D)) {
1602 SB_Copy (&OutputName, &D->SVal);
1603 SB_Terminate (&OutputName);
1604 InfoBits |= ibOutputName;
1608 case TOK_OUTPUTOFFS:
1609 if (!IntConstFollows (D)) {
1612 OutputOffs = D->IVal;
1614 InfoBits |= ibOutputOffs;
1618 if (!IntConstFollows (D)) {
1621 Start = (cc65_addr) D->IVal;
1623 InfoBits |= ibStart;
1627 if (!IntConstFollows (D)) {
1637 InfoBits |= ibAddrSize;
1647 UnexpectedToken (D);
1653 if (D->Tok != TOK_COMMA) {
1659 /* Check for end of line */
1660 if (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1661 UnexpectedToken (D);
1666 /* Check for required information */
1667 if ((InfoBits & ibRequired) != ibRequired) {
1668 ParseError (D, CC65_ERROR, "Required attributes missing");
1672 /* Create the segment info and remember it */
1673 S = NewSegInfo (&SegName, Id, Start, Size, &OutputName, OutputOffs);
1674 CollAppend (&D->Info->SegInfoByName, S);
1677 /* Entry point in case of errors */
1679 SB_Done (&OutputName);
1685 static void ParseSym (InputData* D)
1686 /* Parse a SYM line */
1688 /* Skip the SYM token */
1697 static void ParseVersion (InputData* D)
1698 /* Parse a VERSION line */
1704 ibRequired = ibMajor | ibMinor,
1705 } InfoBits = ibNone;
1707 /* Skip the VERSION token */
1710 /* More stuff follows */
1711 while (D->Tok != TOK_EOL && D->Tok != TOK_EOF) {
1717 if (!ConsumeEqual (D)) {
1720 if (!IntConstFollows (D)) {
1723 D->MajorVersion = D->IVal;
1725 InfoBits |= ibMajor;
1730 if (!ConsumeEqual (D)) {
1733 if (!IntConstFollows (D)) {
1736 D->MinorVersion = D->IVal;
1738 InfoBits |= ibMinor;
1742 /* Try to skip unknown keywords that may have been added by
1749 UnexpectedToken (D);
1754 /* Comma follows before next attribute */
1755 if (D->Tok == TOK_COMMA) {
1757 } else if (D->Tok == TOK_EOL || D->Tok == TOK_EOF) {
1760 UnexpectedToken (D);
1765 /* Check for required information */
1766 if ((InfoBits & ibRequired) != ibRequired) {
1767 ParseError (D, CC65_ERROR, "Required attributes missing");
1772 /* Entry point in case of errors */
1778 /*****************************************************************************/
1779 /* Data processing */
1780 /*****************************************************************************/
1784 static SegInfo* FindSegInfoById (InputData* D, unsigned Id)
1785 /* Find the SegInfo with a given Id */
1787 /* Get a pointer to the segment info collection */
1788 Collection* SegInfos = &D->Info->SegInfoById;
1790 /* Do a binary search */
1792 int Hi = (int) CollCount (SegInfos) - 1;
1796 int Cur = (Lo + Hi) / 2;
1799 SegInfo* CurItem = CollAt (SegInfos, Cur);
1802 if (Id > CurItem->Id) {
1804 } else if (Id < CurItem->Id) {
1818 static FileInfo* FindFileInfoByName (Collection* FileInfos, const char* FileName)
1819 /* Find the FileInfo for a given file name */
1821 /* Do a binary search */
1823 int Hi = (int) CollCount (FileInfos) - 1;
1827 int Cur = (Lo + Hi) / 2;
1830 FileInfo* CurItem = CollAt (FileInfos, Cur);
1833 int Res = strcmp (CurItem->FileName, FileName);
1838 } else if (Res > 0) {
1852 static FileInfo* FindFileInfoById (Collection* FileInfos, unsigned Id)
1853 /* Find the FileInfo with a given Id */
1855 /* Do a binary search */
1857 int Hi = (int) CollCount (FileInfos) - 1;
1861 int Cur = (Lo + Hi) / 2;
1864 FileInfo* CurItem = CollAt (FileInfos, Cur);
1867 if (Id > CurItem->Id) {
1869 } else if (Id < CurItem->Id) {
1883 static void ProcessSegInfo (InputData* D)
1884 /* Postprocess segment infos */
1888 /* Get pointers to the segment info collections */
1889 Collection* SegInfoByName = &D->Info->SegInfoByName;
1890 Collection* SegInfoById = &D->Info->SegInfoById;
1892 /* Sort the segment infos by name */
1893 CollSort (SegInfoByName, CompareSegInfoByName);
1895 /* Copy all items over to the collection that will get sorted by id */
1896 for (I = 0; I < CollCount (SegInfoByName); ++I) {
1897 CollAppend (SegInfoById, CollAt (SegInfoByName, I));
1900 /* Sort this collection */
1901 CollSort (SegInfoById, CompareSegInfoById);
1906 static void ProcessFileInfo (InputData* D)
1907 /* Postprocess file infos */
1909 /* Get pointers to the file info collections */
1910 Collection* FileInfoByName = &D->Info->FileInfoByName;
1911 Collection* FileInfoById = &D->Info->FileInfoById;
1913 /* First, sort the file infos, so we can check for duplicates and do
1916 CollSort (FileInfoByName, CompareFileInfoByName);
1918 /* Cannot work on an empty collection */
1919 if (CollCount (FileInfoByName) > 0) {
1921 /* Walk through the file infos sorted by name and check for duplicates.
1922 * If we find some, warn and remove them, so the file infos are unique
1925 FileInfo* F = CollAt (FileInfoByName, 0);
1927 while (I < CollCount (FileInfoByName)) {
1928 FileInfo* Next = CollAt (FileInfoByName, I);
1929 if (strcmp (F->FileName, Next->FileName) == 0) {
1930 /* Warn only if time stamp and/or size is different */
1931 if (F->Size != Next->Size || F->MTime != Next->MTime) {
1934 "Duplicate file entry for \"%s\"",
1937 /* Remove the duplicate entry */
1938 FreeFileInfo (Next);
1939 CollDelete (FileInfoByName, I);
1941 /* This one is ok, check the next entry */
1947 /* Copy the file infos to another collection that will be sorted by id */
1948 for (I = 0; I < CollCount (FileInfoByName); ++I) {
1949 CollAppend (FileInfoById, CollAt (FileInfoByName, I));
1952 /* Sort this collection */
1953 CollSort (FileInfoById, CompareFileInfoById);
1959 static void ProcessLineInfo (InputData* D)
1960 /* Postprocess line infos */
1962 /* Get pointers to the collections */
1963 Collection* LineInfos = &D->LineInfos;
1964 Collection* FileInfos = &D->Info->FileInfoByName;
1966 /* Walk over the line infos and replace the id numbers of file and segment
1967 * with pointers to the actual structs. Add the line info to each file
1968 * where it is defined.
1971 FileInfo* LastFileInfo = 0;
1972 SegInfo* LastSegInfo = 0;
1973 while (I < CollCount (LineInfos)) {
1978 /* Get LineInfo struct */
1979 LineInfo* L = CollAt (LineInfos, I);
1981 /* Find the FileInfo that corresponds to Id. We cache the last file
1982 * info in LastFileInfo to speedup searching.
1984 if (LastFileInfo && LastFileInfo->Id == L->File.Id) {
1987 F = FindFileInfoById (&D->Info->FileInfoById, L->File.Id);
1989 /* If we have no corresponding file info, print a warning and
1990 * remove the line info.
1995 "No file info for file with id %u",
1998 CollDelete (LineInfos, I);
2002 /* Otherwise remember it for later */
2006 /* Replace the file id by a pointer to the file info */
2009 /* Find the SegInfo that corresponds to Id. We cache the last file
2010 * info in LastSegInfo to speedup searching.
2012 if (LastSegInfo && LastSegInfo->Id == L->Seg.Id) {
2015 S = FindSegInfoById (D, L->Seg.Id);
2017 /* If we have no corresponding segment info, print a warning and
2018 * remove the line info.
2023 "No segment info for segment with id %u",
2026 CollDelete (LineInfos, I);
2030 /* Otherwise remember it for later */
2034 /* Replace the segment id by a pointer to the segment info */
2037 /* Add this line info to the file where it is defined */
2038 CollAppend (&F->LineInfoByAddr, L);
2039 CollAppend (&F->LineInfoByLine, L);
2045 /* Walk over all files and sort the line infos for each file so we can
2046 * do a binary search later.
2048 for (I = 0; I < CollCount (FileInfos); ++I) {
2050 /* Get a pointer to this file info */
2051 FileInfo* F = CollAt (FileInfos, I);
2053 /* Sort the line infos for this file */
2054 CollSort (&F->LineInfoByAddr, CompareLineInfoByAddr);
2055 CollSort (&F->LineInfoByLine, CompareLineInfoByLine);
2057 /* If there are line info entries, place the first and last address
2058 * of into the FileInfo struct itself, so we can rule out a FileInfo
2059 * quickly when mapping an address to a line info.
2061 if (CollCount (&F->LineInfoByAddr) > 0) {
2062 F->Start = ((const LineInfo*) CollFirst (&F->LineInfoByAddr))->Start;
2063 F->End = ((const LineInfo*) CollLast (&F->LineInfoByAddr))->End;
2071 static LineInfo* FindLineInfoByAddr (FileInfo* F, cc65_addr Addr)
2072 /* Find the LineInfo for a given address */
2074 Collection* LineInfoByAddr;
2079 /* Each file info contains the first and last address for which line
2080 * info is available, so we can rule out non matching ones quickly.
2082 if (Addr < F->Start || Addr > F->End) {
2086 /* Get a pointer to the line info collection for this file */
2087 LineInfoByAddr = &F->LineInfoByAddr;
2089 /* Do a binary search */
2091 Hi = (int) CollCount (LineInfoByAddr) - 1;
2095 int Cur = (Lo + Hi) / 2;
2098 LineInfo* CurItem = CollAt (LineInfoByAddr, Cur);
2101 if (Addr < CurItem->Start) {
2103 } else if (Addr > CurItem->End) {
2117 static LineInfo* FindLineInfoByLine (FileInfo* F, cc65_line Line)
2118 /* Find the LineInfo for a given line number */
2124 /* Get a pointer to the line info collection for this file */
2125 Collection* LineInfoByLine = &F->LineInfoByLine;
2127 /* Do a binary search */
2129 Hi = (int) CollCount (LineInfoByLine) - 1;
2133 int Cur = (Lo + Hi) / 2;
2136 LineInfo* CurItem = CollAt (LineInfoByLine, Cur);
2139 if (Line < CurItem->Line) {
2141 } else if (Line > CurItem->Line) {
2155 /*****************************************************************************/
2157 /*****************************************************************************/
2161 cc65_dbginfo cc65_read_dbginfo (const char* FileName, cc65_errorfunc ErrFunc)
2162 /* Parse the debug info file with the given name. On success, the function
2163 * will return a pointer to an opaque cc65_dbginfo structure, that must be
2164 * passed to the other functions in this module to retrieve information.
2165 * errorfunc is called in case of warnings and errors. If the file cannot be
2166 * read successfully, NULL is returned.
2169 /* Data structure used to control scanning and parsing */
2171 0, /* Name of input file */
2172 1, /* Line number */
2174 0, /* Line at start of current token */
2175 0, /* Column at start of current token */
2176 0, /* Number of errors */
2178 ' ', /* Input character */
2179 TOK_INVALID, /* Input token */
2180 0, /* Integer constant */
2181 STRBUF_INITIALIZER, /* String constant */
2182 0, /* Function called in case of errors */
2183 0, /* Major version number */
2184 0, /* Minor version number */
2185 COLLECTION_INITIALIZER, /* Line information */
2186 0, /* Pointer to debug info */
2188 D.FileName = FileName;
2191 /* Open the input file */
2192 D.F = fopen (D.FileName, "r");
2195 ParseError (&D, CC65_ERROR,
2196 "Cannot open input file \"%s\": %s",
2197 D.FileName, strerror (errno));
2201 /* Create a new debug info struct */
2202 D.Info = NewDbgInfo ();
2204 /* Prime the pump */
2207 /* The first line in the file must specify version information */
2208 if (D.Tok != TOK_VERSION) {
2209 ParseError (&D, CC65_ERROR,
2210 "\"version\" keyword missing in first line - this is not "
2211 "a valid debug info file");
2214 /* Parse the version directive and check the version */
2216 if (D.MajorVersion > VER_MAJOR) {
2217 ParseError (&D, CC65_WARNING,
2218 "The format of this debug info file is newer than what we "
2219 "know. Will proceed but probably fail. Version found = %u, "
2220 "version supported = %u",
2221 D.MajorVersion, VER_MAJOR);
2226 while (D.Tok != TOK_EOF) {
2247 /* Output a warning, then skip the line with the unknown
2248 * keyword that may have been added by a later version.
2250 ParseError (&D, CC65_WARNING,
2251 "Unknown keyword \"%s\" - skipping",
2252 SB_GetConstBuf (&D.SVal));
2258 UnexpectedToken (&D);
2262 /* EOL or EOF must follow */
2267 /* Close the file */
2270 /* Free memory allocated for SVal */
2273 /* In case of errors, delete the debug info already allocated and
2277 /* Free allocated stuff */
2279 for (I = 0; I < CollCount (&D.LineInfos); ++I) {
2280 FreeLineInfo (CollAt (&D.LineInfos, I));
2282 DoneCollection (&D.LineInfos);
2283 FreeDbgInfo (D.Info);
2287 /* We do now have all the information from the input file. Do
2290 ProcessSegInfo (&D);
2291 ProcessFileInfo (&D);
2292 ProcessLineInfo (&D);
2294 /* Free the collection that contained the line info */
2295 DoneCollection (&D.LineInfos);
2297 /* Return the debug info struct that was created */
2303 void cc65_free_dbginfo (cc65_dbginfo Handle)
2304 /* Free debug information read from a file */
2307 FreeDbgInfo (Handle);
2313 cc65_lineinfo* cc65_lineinfo_byaddr (cc65_dbginfo Handle, unsigned long Addr)
2314 /* Return line information for the given address. The function returns 0
2315 * if no line information was found.
2319 Collection* FileInfoByName;
2320 cc65_lineinfo* D = 0;
2322 /* We will place a list of line infos in a collection */
2323 Collection LineInfos = COLLECTION_INITIALIZER;
2325 /* Check the parameter */
2326 assert (Handle != 0);
2328 /* Walk over all files and search for matching line infos */
2329 FileInfoByName = &((DbgInfo*) Handle)->FileInfoByName;
2330 for (I = 0; I < CollCount (FileInfoByName); ++I) {
2331 /* Check if the file contains line info for this address */
2332 LineInfo* L = FindLineInfoByAddr (CollAt (FileInfoByName, I), Addr);
2334 CollAppend (&LineInfos, L);
2338 /* Do we have line infos? */
2339 if (CollCount (&LineInfos) > 0) {
2341 /* Prepare the struct we will return to the caller */
2342 D = xmalloc (sizeof (*D) +
2343 (CollCount (&LineInfos) - 1) * sizeof (D->data[0]));
2344 D->count = CollCount (&LineInfos);
2345 for (I = 0; I < D->count; ++I) {
2347 CopyLineInfo (D->data + I, CollAt (&LineInfos, I));
2351 /* Free the line info collection */
2352 DoneCollection (&LineInfos);
2354 /* Return the struct we've created */
2360 cc65_lineinfo* cc65_lineinfo_byname (cc65_dbginfo Handle, const char* FileName,
2362 /* Return line information for a file/line number combination. The function
2363 * returns NULL if no line information was found.
2371 /* Check the parameter */
2372 assert (Handle != 0);
2374 /* The handle is actually a pointer to a debug info struct */
2375 Info = (DbgInfo*) Handle;
2377 /* Get the file info */
2378 F = FindFileInfoByName (&Info->FileInfoByName, FileName);
2380 /* File not found */
2384 /* Search in the file for the given line */
2385 L = FindLineInfoByLine (F, Line);
2387 /* Line not found */
2391 /* Prepare the struct we will return to the caller */
2392 D = xmalloc (sizeof (*D));
2396 CopyLineInfo (D->data, L);
2398 /* Return the allocated struct */
2404 void cc65_free_lineinfo (cc65_dbginfo Handle, cc65_lineinfo* Info)
2405 /* Free line info returned by one of the other functions */
2407 /* Just for completeness, check the handle */
2408 assert (Handle != 0);
2410 /* Just free the memory */
2416 cc65_sourceinfo* cc65_get_sourcelist (cc65_dbginfo Handle)
2417 /* Return a list of all source files */
2420 Collection* FileInfoByName;
2424 /* Check the parameter */
2425 assert (Handle != 0);
2427 /* The handle is actually a pointer to a debug info struct */
2428 Info = (DbgInfo*) Handle;
2430 /* Get a pointer to the file list */
2431 FileInfoByName = &Info->FileInfoByName;
2433 /* Allocate memory for the data structure returned to the caller */
2434 D = xmalloc (sizeof (*D) - sizeof (D->data[0]) +
2435 CollCount (FileInfoByName) * sizeof (D->data[0]));
2437 /* Fill in the data */
2438 D->count = CollCount (FileInfoByName);
2439 for (I = 0; I < CollCount (FileInfoByName); ++I) {
2442 FileInfo* F = CollAt (FileInfoByName, I);
2445 D->data[I].source_name = F->FileName;
2446 D->data[I].source_size = F->Size;
2447 D->data[I].source_mtime = F->MTime;
2450 /* Return the result */
2456 void cc65_free_sourceinfo (cc65_dbginfo Handle, cc65_sourceinfo* Info)
2457 /* Free a source info record */
2459 /* Just for completeness, check the handle */
2460 assert (Handle != 0);
2462 /* Free the memory */
2468 cc65_segmentinfo* cc65_get_segmentlist (cc65_dbginfo Handle)
2469 /* Return a list of all segments referenced in the debug information */
2472 Collection* SegInfoByName;
2473 cc65_segmentinfo* D;
2476 /* Check the parameter */
2477 assert (Handle != 0);
2479 /* The handle is actually a pointer to a debug info struct */
2480 Info = (DbgInfo*) Handle;
2482 /* Get a pointer to the file list */
2483 SegInfoByName = &Info->SegInfoByName;
2485 /* Allocate memory for the data structure returned to the caller */
2486 D = xmalloc (sizeof (*D) - sizeof (D->data[0]) +
2487 CollCount (SegInfoByName) * sizeof (D->data[0]));
2489 /* Fill in the data */
2490 D->count = CollCount (SegInfoByName);
2491 for (I = 0; I < CollCount (SegInfoByName); ++I) {
2494 SegInfo* S = CollAt (SegInfoByName, I);
2497 D->data[I].segment_name = S->SegName;
2498 D->data[I].segment_start = S->Start;
2499 D->data[I].segment_size = S->Size;
2500 D->data[I].output_name = S->OutputName;
2501 D->data[I].output_offs = S->OutputOffs;
2504 /* Return the result */
2510 void cc65_free_segmentinfo (cc65_dbginfo Handle, cc65_segmentinfo* Info)
2511 /* Free a segment info record */
2513 /* Just for completeness, check the handle */
2514 assert (Handle != 0);
2516 /* Free the memory */