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