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