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 */
64ListLine* LineList = 0; /* List of listing lines */
65ListLine* LineCur = 0; /* Current listing line */
66ListLine* LineLast = 0; /* Last (current) listing line */
67
68/* Page and other formatting */
69int PageLength = -1; /* Length of a listing page */
70static unsigned PageNumber = 1; /* Current listing page number */
71static int PageLines = 0; /* Current line on page */
72static unsigned ListBytes = 12; /* Number of bytes to list for one line */
73
74/* Switch the listing on/off */
75static int ListingEnabled = 1; /* Enabled if > 0 */
76
77
78
79/*****************************************************************************/
80/* Code */
81/*****************************************************************************/
82
83
84
85void 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
129void 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
144void 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
159void 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
170void 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
203static 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
218static 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
243static 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
265static 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
274static 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
296void 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