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