]> git.sur5r.net Git - cc65/blob - src/ca65/listing.c
Removed (pretty inconsistently used) tab chars from source code base.
[cc65] / src / ca65 / listing.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 listing.c                                 */
4 /*                                                                           */
5 /*                Listing support for the ca65 crossassembler                */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 2000-2011, Ullrich von Bassewitz                                      */
10 /*                Roemerstrasse 52                                           */
11 /*                D-70794 Filderstadt                                        */
12 /* EMail:         uz@cc65.org                                                */
13 /*                                                                           */
14 /*                                                                           */
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.                                    */
18 /*                                                                           */
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:                            */
22 /*                                                                           */
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              */
30 /*    distribution.                                                          */
31 /*                                                                           */
32 /*****************************************************************************/
33
34
35
36 #include <stdio.h>
37 #include <string.h>
38 #include <errno.h>
39
40 /* common */
41 #include "check.h"
42 #include "fname.h"
43 #include "fragdefs.h"
44 #include "strbuf.h"
45 #include "version.h"
46 #include "xmalloc.h"
47
48 /* ca65 */
49 #include "error.h"
50 #include "filetab.h"
51 #include "global.h"
52 #include "listing.h"
53 #include "segment.h"
54
55
56
57 /*****************************************************************************/
58 /*                                   Data                                    */
59 /*****************************************************************************/
60
61
62
63 /* Single linked list of lines */
64 ListLine*       LineList = 0;           /* List of listing lines */
65 ListLine*       LineCur  = 0;           /* Current listing line */
66 ListLine*       LineLast = 0;           /* Last (current) listing line */
67
68 /* Page and other formatting */
69 int             PageLength = -1;        /* Length of a listing page */
70 static unsigned PageNumber = 1;         /* Current listing page number */
71 static int      PageLines  = 0;         /* Current line on page */
72 static unsigned ListBytes  = 12;        /* Number of bytes to list for one line */
73
74 /* Switch the listing on/off */
75 static int      ListingEnabled = 1;     /* Enabled if > 0 */
76
77
78
79 /*****************************************************************************/
80 /*                                   Code                                    */
81 /*****************************************************************************/
82
83
84
85 void NewListingLine (const StrBuf* Line, unsigned char File, unsigned char Depth)
86 /* Create a new ListLine struct and insert it */
87 {
88     /* Store only if listing is enabled */
89     if (SB_GetLen (&ListingName) > 0) {
90
91         ListLine* L;
92
93         /* Get the length of the line */
94         unsigned Len = SB_GetLen (Line);
95
96         /* Ignore trailing newlines */
97         while (Len > 0 && SB_AtUnchecked (Line, Len-1) == '\n') {
98             --Len;
99         }
100
101         /* Allocate memory */
102         L = xmalloc (sizeof (ListLine) + Len);
103
104         /* Initialize the fields. */
105         L->Next         = 0;
106         L->FragList     = 0;
107         L->FragLast     = 0;
108         L->PC           = GetPC ();
109         L->Reloc        = GetRelocMode ();
110         L->File         = File;
111         L->Depth        = Depth;
112         L->Output       = (ListingEnabled > 0);
113         L->ListBytes    = (unsigned char) ListBytes;
114         memcpy (L->Line, SB_GetConstBuf (Line), Len);
115         L->Line[Len] = '\0';
116
117         /* Insert the line into the list of lines */
118         if (LineList == 0) {
119             LineList = L;
120         } else {
121             LineLast->Next = L;
122         }
123         LineLast = L;
124     }
125 }
126
127
128
129 void EnableListing (void)
130 /* Enable output of lines to the listing */
131 {
132     if (SB_GetLen (&ListingName) > 0) {
133         /* If we're about to enable the listing, do this for the current line
134          * also, so we will see the source line that did this.
135          */
136         if (ListingEnabled++ == 0) {
137             LineCur->Output = 1;
138         }
139     }
140 }
141
142
143
144 void DisableListing (void)
145 /* Disable output of lines to the listing */
146 {
147     if (SB_GetLen (&ListingName) > 0) {
148         if (ListingEnabled == 0) {
149             /* Cannot switch the listing off once more */
150             Error ("Counter underflow");
151         } else {
152             --ListingEnabled;
153         }
154     }
155 }
156
157
158
159 void SetListBytes (int Bytes)
160 /* Set the maximum number of bytes listed for one line */
161 {
162     if (Bytes < 0) {
163         Bytes = 0;      /* Encode "unlimited" as zero */
164     }
165     ListBytes = Bytes;
166 }
167
168
169
170 void InitListingLine (void)
171 /* Initialize the current listing line */
172 {
173     if (SB_GetLen (&ListingName) > 0) {
174         /* Make the last loaded line the current line */
175         /* ###### This code is a hack! We really need to do it right
176          * as soon as we know, how:-(
177          */
178         if (LineCur && LineCur->Next && LineCur->Next != LineLast) {
179             ListLine* L = LineCur;
180             do {
181                 L = L->Next;
182                 /* Set the values for this line */
183                 CHECK (L != 0);
184                 L->PC            = GetPC ();
185                 L->Reloc         = GetRelocMode ();
186                 L->Output        = (ListingEnabled > 0);
187                 L->ListBytes = (unsigned char) ListBytes;
188             } while (L->Next != LineLast);
189         }
190         LineCur = LineLast;
191
192         /* Set the values for this line */
193         CHECK (LineCur != 0);
194         LineCur->PC         = GetPC ();
195         LineCur->Reloc      = GetRelocMode ();
196         LineCur->Output     = (ListingEnabled > 0);
197         LineCur->ListBytes  = (unsigned char) ListBytes;
198     }
199 }
200
201
202
203 static char* AddHex (char* S, unsigned Val)
204 /* Add a hex byte in ASCII to the given string and return the new pointer */
205 {
206     static const char HexTab [16] = {
207         '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
208     };
209
210     *S++ = HexTab [(Val >> 4) & 0x0F];
211     *S++ = HexTab [Val & 0x0F];
212
213     return S;
214 }
215
216
217
218 static void PrintPageHeader (FILE* F, const ListLine* L)
219 /* Print the header for a new page. It is assumed that the given line is the
220  * last line of the previous page.
221  */
222 {
223     /* Gte a pointer to the current input file */
224     const StrBuf* CurFile = GetFileName (L->File);
225
226     /* Print the header on the new page */
227     fprintf (F,
228              "ca65 V%s\n"
229              "Main file   : %s\n"
230              "Current file: %.*s\n"
231              "\n",
232              GetVersionAsString (),
233              InFile,
234              (int) SB_GetLen (CurFile), SB_GetConstBuf (CurFile));
235
236     /* Count pages, reset lines */
237     ++PageNumber;
238     PageLines = 4;
239 }
240
241
242
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 */
245 {
246     /* Print the given line */
247     fprintf (F, "%s%s\n", Header, Line);
248
249     /* Increment the current line */
250     ++PageLines;
251
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.
254      */
255     if (PageLength > 0 && PageLines >= PageLength && L->Next != 0) {
256         /* Do a formfeed */
257         putc ('\f', F);
258         /* Print the header on the new page */
259         PrintPageHeader (F, L);
260     }
261 }
262
263
264
265 static char* AddMult (char* S, char C, unsigned Count)
266 /* Add multiple instances of character C to S, return updated S. */
267 {
268     memset (S, C, Count);
269     return S + Count;
270 }
271
272
273
274 static char* MakeLineHeader (char* H, const ListLine* L)
275 /* Prepare the line header */
276 {
277     char Mode;
278     char Depth;
279
280     /* Setup the PC mode */
281     Mode = (L->Reloc)? 'r' : ' ';
282
283     /* Set up the include depth */
284     Depth = (L->Depth < 10)? L->Depth + '0' : '+';
285
286     /* Format the line */
287     sprintf (H, "%06lX%c %c", L->PC, Mode, Depth);
288     memset (H+9, ' ', LINE_HEADER_LEN-9);
289
290     /* Return the buffer */
291     return H;
292 }
293
294
295
296 void CreateListing (void)
297 /* Create the listing */
298 {
299     FILE* F;
300     Fragment* Frag;
301     ListLine* L;
302     char HeaderBuf [LINE_HEADER_LEN+1];
303
304     /* Open the real listing file */
305     F = fopen (SB_GetConstBuf (&ListingName), "w");
306     if (F == 0) {
307         Fatal ("Cannot open listing file `%s': %s",
308                SB_GetConstBuf (&ListingName),
309                strerror (errno));
310     }
311
312     /* Reset variables, print the header for the first page */
313     PageNumber = 0;
314     PrintPageHeader (F, LineList);
315
316     /* Terminate the header buffer. The last byte will never get overwritten */
317     HeaderBuf [LINE_HEADER_LEN] = '\0';
318
319     /* Walk through all listing lines */
320     L = LineList;
321     while (L) {
322
323         char* Buf;
324         char* B;
325         unsigned Count;
326         unsigned I;
327         char* Line;
328
329         /* If we should not output this line, go to the next */
330         if (L->Output == 0) {
331             L = L->Next;
332             continue;
333         }
334
335         /* If we don't have a fragment list for this line, things are easy */
336         if (L->FragList == 0) {
337             PrintLine (F, MakeLineHeader (HeaderBuf, L), L->Line, L);
338             L = L->Next;
339             continue;
340         }
341
342         /* Count the number of bytes in the complete fragment list */
343         Count = 0;
344         Frag = L->FragList;
345         while (Frag) {
346             Count += Frag->Len;
347             Frag = Frag->LineList;
348         }
349
350         /* Allocate memory for the given number of bytes */
351         Buf = xmalloc (Count*2+1);
352
353         /* Copy an ASCII representation of the bytes into the buffer */
354         B = Buf;
355         Frag = L->FragList;
356         while (Frag) {
357
358             /* Write data depending on the type */
359             switch (Frag->Type) {
360
361                 case FRAG_LITERAL:
362                     for (I = 0; I < Frag->Len; ++I) {
363                         B = AddHex (B, Frag->V.Data[I]);
364                     }
365                     break;
366
367                 case FRAG_EXPR:
368                 case FRAG_SEXPR:
369                     B = AddMult (B, 'r', Frag->Len*2);
370                     break;
371
372                 case FRAG_FILL:
373                     B = AddMult (B, 'x', Frag->Len*2);
374                     break;
375
376                 default:
377                     Internal ("Invalid fragment type: %u", Frag->Type);
378
379             }
380
381             /* Next fragment */
382             Frag = Frag->LineList;
383
384         }
385
386         /* Limit the number of bytes actually printed */
387         if (L->ListBytes != 0) {
388             /* Not unlimited */
389             if (Count > L->ListBytes) {
390                 Count = L->ListBytes;
391             }
392         }
393
394         /* Output the data. The format of a listing line is:
395          *
396          *      PPPPPPm I  11 22 33 44
397          *
398          * where
399          *
400          *      PPPPPP  is the PC
401          *      m       is the mode ('r' or empty)
402          *      I       is the include level
403          *      11 ..   are code or data bytes
404          */
405         Line = L->Line;
406         B    = Buf;
407         while (Count) {
408
409             unsigned    Chunk;
410             char*       P;
411
412             /* Prepare the line header */
413             MakeLineHeader (HeaderBuf, L);
414
415             /* Get the number of bytes for the next line */
416             Chunk = Count;
417             if (Chunk > 4) {
418                 Chunk = 4;
419             }
420             Count -= Chunk;
421
422             /* Increment the program counter. Since we don't need the PC stored
423              * in the LineList object for anything else, just increment this
424              * variable.
425              */
426             L->PC += Chunk;
427
428             /* Copy the bytes into the line */
429             P = HeaderBuf + 11;
430             for (I = 0; I < Chunk; ++I) {
431                 *P++ = *B++;
432                 *P++ = *B++;
433                 *P++ = ' ';
434             }
435
436             /* Output this line */
437             PrintLine (F, HeaderBuf, Line, L);
438
439             /* Don't output a line twice */
440             Line = "";
441
442         }
443
444         /* Delete the temporary buffer */
445         xfree (Buf);
446
447         /* Next line */
448         L = L->Next;
449
450     }
451
452     /* Close the listing file */
453     (void) fclose (F);
454 }
455
456
457