1 /*****************************************************************************/
5 /* Listing support for the ca65 crossassembler */
9 /* (C) 2000-2008 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 /*****************************************************************************/
56 /*****************************************************************************/
58 /*****************************************************************************/
62 /* Single linked list of lines */
63 ListLine* LineList = 0; /* List of listing lines */
64 ListLine* LineCur = 0; /* Current listing line */
65 ListLine* LineLast = 0; /* Last (current) listing line */
67 /* Page and other formatting */
68 int PageLength = -1; /* Length of a listing page */
69 static unsigned PageNumber = 1; /* Current listing page number */
70 static int PageLines = 0; /* Current line on page */
71 static unsigned ListBytes = 12; /* Number of bytes to list for one line */
73 /* Switch the listing on/off */
74 static int ListingEnabled = 1; /* Enabled if > 0 */
78 /*****************************************************************************/
80 /*****************************************************************************/
84 void NewListingLine (const char* Line, unsigned char File, unsigned char Depth)
85 /* Create a new ListLine struct and insert it */
87 /* Store only if listing is enabled */
92 /* Get the length of the line */
93 unsigned Len = strlen (Line);
95 /* Ignore trailing newlines */
96 while (Len > 0 && Line[Len-1] == '\n') {
100 /* Allocate memory */
101 L = xmalloc (sizeof (ListLine) + Len);
103 /* Initialize the fields. */
108 L->Reloc = GetRelocMode ();
111 L->Output = (ListingEnabled > 0);
112 L->ListBytes = (unsigned char) ListBytes;
113 memcpy (L->Line, Line, Len);
114 L->Line [Len] = '\0';
116 /* Insert the line into the list of lines */
128 void EnableListing (void)
129 /* Enable output of lines to the listing */
132 /* If we're about to enable the listing, do this for the current line
133 * also, so we will see the source line that did this.
135 if (ListingEnabled++ == 0) {
143 void DisableListing (void)
144 /* Disable output of lines to the listing */
147 if (ListingEnabled == 0) {
148 /* Cannot switch the listing off once more */
149 Error ("Counter underflow");
158 void SetListBytes (int Bytes)
159 /* Set the maximum number of bytes listed for one line */
162 Bytes = 0; /* Encode "unlimited" as zero */
169 void InitListingLine (void)
170 /* Initialize the current listing line */
173 /* Make the last loaded line the current line */
174 /* ###### This code is a hack! We really need to do it right
175 * as soon as we know, how:-(
177 if (LineCur && LineCur->Next && LineCur->Next != LineLast) {
178 ListLine* L = LineCur;
181 /* Set the values for this line */
184 L->Reloc = GetRelocMode ();
185 L->Output = (ListingEnabled > 0);
186 L->ListBytes = (unsigned char) ListBytes;
187 } while (L->Next != LineLast);
191 /* Set the values for this line */
192 CHECK (LineCur != 0);
193 LineCur->PC = GetPC ();
194 LineCur->Reloc = GetRelocMode ();
195 LineCur->Output = (ListingEnabled > 0);
196 LineCur->ListBytes = (unsigned char) ListBytes;
202 static char* AddHex (char* S, unsigned Val)
203 /* Add a hex byte in ASCII to the given string and return the new pointer */
205 static const char HexTab [16] = {
206 '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
209 *S++ = HexTab [(Val >> 4) & 0x0F];
210 *S++ = HexTab [Val & 0x0F];
217 static void PrintPageHeader (FILE* F, const ListLine* L)
218 /* Print the header for a new page. It is assumed that the given line is the
219 * last line of the previous page.
222 /* Gte a pointer to the current input file */
223 const StrBuf* CurFile = GetFileName (L->File);
225 /* Print the header on the new page */
227 "ca65 V%u.%u.%u - %s\n"
229 "Current file: %.*s\n"
231 VER_MAJOR, VER_MINOR, VER_PATCH,
234 SB_GetLen (CurFile), SB_GetConstBuf (CurFile));
236 /* Count pages, reset lines */
243 static void PrintLine (FILE* F, const char* Header, const char* Line, const ListLine* L)
244 /* Print one line to the listing file, adding a newline and counting lines */
246 /* Print the given line */
247 fprintf (F, "%s%s\n", Header, Line);
249 /* Increment the current line */
252 /* Switch to a new page if needed. Do not switch, if the current line is
253 * the last one, to avoid pages that consist of just the header.
255 if (PageLength > 0 && PageLines >= PageLength && L->Next != 0) {
258 /* Print the header on the new page */
259 PrintPageHeader (F, L);
265 static char* AddMult (char* S, char C, unsigned Count)
266 /* Add multiple instances of character C to S, return updated S. */
268 memset (S, C, Count);
274 static char* MakeLineHeader (char* H, const ListLine* L)
275 /* Prepare the line header */
280 /* Setup the PC mode */
281 Mode = (L->Reloc)? 'r' : ' ';
283 /* Set up the include depth */
284 Depth = (L->Depth < 10)? L->Depth + '0' : '+';
286 /* Format the line */
287 sprintf (H, "%06lX%c %c", L->PC, Mode, Depth);
288 memset (H+9, ' ', LINE_HEADER_LEN-9);
290 /* Return the buffer */
296 void CreateListing (void)
297 /* Create the listing */
302 char HeaderBuf [LINE_HEADER_LEN+1];
304 /* Create the name of the listing file if needed */
306 ListFile = MakeFilename (InFile, ListExt);
309 /* Open the real listing file */
310 F = fopen (ListFile, "w");
312 Fatal ("Cannot open listing file: %s", strerror (errno));
315 /* Reset variables, print the header for the first page */
317 PrintPageHeader (F, LineList);
319 /* Terminate the header buffer. The last byte will never get overwritten */
320 HeaderBuf [LINE_HEADER_LEN] = '\0';
322 /* Walk through all listing lines */
332 /* If we should not output this line, go to the next */
333 if (L->Output == 0) {
338 /* If we don't have a fragment list for this line, things are easy */
339 if (L->FragList == 0) {
340 PrintLine (F, MakeLineHeader (HeaderBuf, L), L->Line, L);
345 /* Count the number of bytes in the complete fragment list */
350 Frag = Frag->LineList;
353 /* Allocate memory for the given number of bytes */
354 Buf = xmalloc (Count*2+1);
356 /* Copy an ASCII representation of the bytes into the buffer */
361 /* Write data depending on the type */
362 switch (Frag->Type) {
365 for (I = 0; I < Frag->Len; ++I) {
366 B = AddHex (B, Frag->V.Data[I]);
372 B = AddMult (B, 'r', Frag->Len*2);
376 B = AddMult (B, 'x', Frag->Len*2);
380 Internal ("Invalid fragment type: %u", Frag->Type);
385 Frag = Frag->LineList;
389 /* Limit the number of bytes actually printed */
390 if (L->ListBytes != 0) {
392 if (Count > L->ListBytes) {
393 Count = L->ListBytes;
397 /* Output the data. The format of a listing line is:
399 * PPPPPPm I 11 22 33 44
404 * m is the mode ('r' or empty)
405 * I is the include level
406 * 11 .. are code or data bytes
415 /* Prepare the line header */
416 MakeLineHeader (HeaderBuf, L);
418 /* Get the number of bytes for the next line */
425 /* Increment the program counter. Since we don't need the PC stored
426 * in the LineList object for anything else, just increment this
431 /* Copy the bytes into the line */
433 for (I = 0; I < Chunk; ++I) {
439 /* Output this line */
440 PrintLine (F, HeaderBuf, Line, L);
442 /* Don't output a line twice */
447 /* Delete the temporary buffer */
455 /* Close the listing file */