2fcf315c48739d057dbd46ddad1d3cc1acdd9534
[rmac] / listing.c
1 //
2 // RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System
3 // LISTING.C - Listing Output
4 // Copyright (C) 199x Landon Dyer, 2011-2012 Reboot and Friends
5 // RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986
6 // Source utilised with the kind permission of Landon Dyer
7 //
8 // --------------------------------------------------------------------------
9 // 0    0    1    1    2    2    3    3    4    4    5    5    6    6    7    7
10 // 012345678901234567890123456789012345678901234567890123456789012345678901234567
11 // filename....                         Reboot's Macro Assembler N.N.NN (Unknown)
12 // nnnnn  aaaaaaaa  dddddddddddddddddddd T source code
13 // nnnnn  aaaaaaaa  dddddddddddddddd
14 // nnnnn           =vvvvvvvv
15
16 #include "listing.h"
17 #include "version.h"
18 #include "token.h"
19 #include "procln.h"
20 #include "sect.h"
21 #include "error.h"
22
23 char * list_fname;                                      // Listing filename
24 uint8_t subttl[TITLESIZ];                       // Current subtitle
25 int listing;                                            // Listing level
26 int pagelen = 61;                                       // Lines on a page
27 int nlines;                                                     // #lines on page so far
28 LONG lsloc;                                                     // `sloc' at start of line
29
30 // Private
31 static int lcursect;                            // `cursect' at start of line
32 static int llineno;                                     // `curlineno' at start of line
33 static int pageno;                                      // Current page number
34 static int pagewidth;                           // #columns on a page
35 static int subflag;                                     // 0, don't do .eject on subttl (set 1)
36 static char lnimage[IMAGESIZ];          // Image of output line
37 static char title[TITLESIZ];            // Current title
38 static char datestr[20];                        // Current date dd-mon-yyyy
39 static char timestr[20];                        // Current time hh:mm:ss [am|pm]
40 static char buf[IMAGESIZ];                      // Buffer for numbers
41 static long unused;                                     // For supressing 'write' warnings
42
43 static char * month[16] = {
44         "",    "Jan", "Feb", "Mar",
45         "Apr", "May", "Jun", "Jul",
46         "Aug", "Sep", "Oct", "Nov",
47         "Dec", "",    "",    ""
48 };
49
50
51 //
52 // Eject the page, reset the line count and bump the page number
53 //
54 int eject(void)
55 {
56         if (listing > 0)
57         {
58                 println("\f");
59                 nlines = 0;
60         }
61
62         return 0;
63 }
64
65
66 //
67 // Return GEMDOS format date
68 //
69 VALUE dos_date(void)
70 {
71         VALUE v;
72         struct tm * tm;
73         time_t tloc;
74
75         time(&tloc);
76         tm = localtime(&tloc);
77         v = ((tm->tm_year - 80) << 9) | ((tm->tm_mon + 1) << 5) | tm->tm_mday;
78
79         return v;
80 }
81
82
83 //
84 // Return GEMDOS format time
85 //
86 VALUE dos_time(void)
87 {
88         VALUE v;
89         struct tm * tm;
90         time_t tloc;
91
92         time(&tloc);
93         tm = localtime(&tloc);
94         v = (tm->tm_hour << 11) | (tm->tm_min) << 5 | tm->tm_sec;
95
96         return v;
97 }
98
99
100 //
101 // Generate a time string
102 //
103 void time_string(char * buf, VALUE time)
104 {
105         int hour;
106         char * ampm;
107
108         hour = (time >> 11);
109
110         if (hour > 12)
111         {
112                 hour -= 12;
113                 ampm = "pm";
114         }
115         else
116                 ampm = "am";
117
118         sprintf(buf, "%d:%02d:%02d %s",
119                 hour, (int)((time >> 5) & 0x3F), (int)((time & 0x1F) << 1), ampm);
120 }
121
122
123 //
124 // Generate a date string
125 //
126 void date_string(char * buf, VALUE date)
127 {
128         sprintf(buf, "%d-%s-%d",
129                 (int)(date & 0x1F), month[(date >> 5) & 0xF], (int)((date >> 9) + 1980));
130 }
131
132
133 //
134 // Transform letters a-f in the address and data columns of the listing to
135 // uppercase. (People seem  to like uppercase hex better in assembly-language
136 // listings....)
137 //
138 void uc_ln(char * ln)
139 {
140         int i;
141         char j;
142
143         for(i=LOC_COL; i<SRC_COL; ++i)
144         {
145                 if ((j = ln[i]) >= 'a' && j <= 'f')
146                         ln[i] = (char)(j - 0x20);
147         }
148 }
149
150
151 //
152 // Fill region 'dest' with 'len' characters 'c' and null terminate the region
153 //
154 void lnfill(char * dest, int len, char chr)
155 {
156         while (len--)
157                 *dest++ = chr;
158
159         *dest = EOS;
160 }
161
162
163 //
164 // Create listing file with the appropriate name
165 //
166 void list_setup(void)
167 {
168         char fnbuf[FNSIZ];
169
170         strcpy(fnbuf, list_fname);
171
172         if (*fnbuf == EOS)
173         {
174                 strcpy(fnbuf, firstfname);
175                 fext(fnbuf, ".prn", 1);
176         }
177
178         list_fname = NULL;
179
180         if ((list_fd = open(fnbuf, _OPEN_FLAGS, _PERM_MODE)) < 0)
181                 cantcreat(fnbuf);
182 }
183
184
185 //
186 // Tag listing with a character, typically for errors or warnings
187 //
188 void taglist(char chr)
189 {
190         lnimage[TAG_COL + 1] = chr;
191 }
192
193
194 //
195 // Print a line to the listing file
196 //
197 void println(const char * ln)
198 {
199         unsigned int length;
200
201         //  Create listing file, if necessary
202         if (list_fname != NULL)
203                 list_setup();
204
205         length = strlen(ln);
206         unused = write(list_fd, ln, length);
207         unused = write(list_fd, "\n", 1L);
208 }
209
210
211 //
212 // Ship line 'ln' out; do page breaks and title stuff
213 //
214 void ship_ln(const char * ln)
215 {
216         // If listing level is <= 0, then don't print anything
217         if (listing <= 0)
218                 return;
219
220         if (list_pag)
221         {
222                 // Notice bottom of page
223                 if (nlines >= pagelen - BOT_MAR)
224                         eject();
225
226                 // Print title, boilerplate, and subtitle at top of page
227                 if (nlines == 0)
228                 {
229                         pageno++;
230                         println("");
231                         date_string(datestr, dos_date());
232                         time_string(timestr, dos_time());
233                         sprintf(buf,
234                                 "%-40s%-20s Page %-4d    %s %s        RMAC %01i.%01i.%02i (%s)",
235                                 title, curfname, pageno, timestr, datestr, MAJOR, MINOR, PATCH,
236                                 PLATFORM);
237                         println(buf);
238                         sprintf(buf, "%s", subttl);
239                         println(buf);
240                         println("");
241                         nlines = 4;
242                 }
243         }
244
245         println(ln);
246         nlines++;
247 }
248
249
250 //
251 // Initialize listing generator
252 //
253 void InitListing(void)
254 {
255         extern VALUE dos_date(), dos_time();
256
257         subflag = 0;
258         pageno = 0;
259         nlines = 0;
260         pagelen = 61;
261         pagewidth = 132;
262         strcpy(title, "");
263         strcpy(subttl, "");
264         date_string(datestr, dos_date());
265         time_string(timestr, dos_time());
266 }
267
268
269 //
270 // Listing EOL
271 //
272 void listeol(void)
273 {
274         CHUNK * ch;
275         char * p;
276         int col;
277         LONG count;
278         int fixcount;
279
280         DEBUG printf("~list: lsloc=$%X sloc=$%X\n", lsloc, sloc);
281
282         if (lsloc != sloc)
283         {
284                 sprintf(buf, "%08X", lsloc);
285                 strncpy(lnimage + LOC_COL, buf, 8);
286         }
287
288         if (llineno != curlineno)
289         {
290                 sprintf(buf, "%5d", llineno);
291                 strncpy(lnimage + LN_COL, buf, 5);
292         }
293
294         // List bytes only when section stayed the same and the section is not a
295         // "no-data" (SBSS) section. An extra annoyance is caused by "ds.b" in a
296         // microprocessor mode, which prints out bytes of zero as if they had been
297         // deposited with dcb. The fix (kludge) is an extra variable which records
298         // the fact that a 'ds.x' directive generated all the data, and it
299         // shouldn't be listed
300         SaveSection();          // Update section variables
301
302         if (lcursect == cursect && (sect[lcursect].scattr & SBSS) == 0
303                 && lsloc != sloc && just_bss == 0)
304         {
305                 ch = sect[lcursect].sfcode;
306
307                 if (lcursect != M6502)
308         {
309                 for(; ch!=NULL; ch=ch->chnext)
310                 {
311                         if (lsloc >= ch->chloc && lsloc < (ch->chloc + ch->ch_size))
312                                 break;
313                 }
314         }
315
316                 // Fatal: Can't find chunk holding code
317                 if (ch == NULL)
318                 {
319 nochunk:
320                         interror(6);    // Can't find generated code in section
321                 }
322
323                 p =  ch->chptr + (lsloc - ch->chloc);
324                 col = DATA_COL;
325                 fixcount = 0;
326
327                 for(count=sloc-lsloc; count--; col+=2, ++lsloc)
328                 {
329                         if (col >= DATA_END)
330                         {
331                                 // Ship the line
332                                 col = DATA_COL;
333                                 uc_ln(lnimage);
334                                 ship_ln(lnimage);
335                                 lnfill(lnimage, SRC_COL, SPACE);        // Fill with spaces
336                                 sprintf(buf, "%08X", lsloc);
337                                 strncpy(lnimage + LOC_COL, buf, 8);
338                         }
339
340                         if (lcursect != M6502 &&
341                     lsloc >= (ch->chloc + ch->ch_size))
342                         {
343                                 if ((ch = ch->chnext) == NULL)
344                                         goto nochunk;
345
346                                 p = ch->chptr;
347                         }
348
349                         if (!fixcount)
350                                 fixcount = fixtest(lcursect, lsloc);
351
352                         if (fixcount)
353                         {
354                                 fixcount--;
355                                 strcpy(buf, "xx");
356                                 p++;            // Advance anyway
357                         }
358                         else
359                                 sprintf(buf, "%02x", *p++ & 0xff);
360
361                         strncpy(lnimage + col, buf, 2);
362                 }
363
364                 // Flush partial line
365                 if (col > DATA_COL)
366                 {
367                         uc_ln(lnimage);
368                         ship_ln(lnimage);
369                 }
370         }
371         else
372         {
373                 uc_ln(lnimage);
374                 ship_ln(lnimage);
375         }
376 }
377
378
379 //
380 // Copy current (saved) line to output buffer and tag it with a character
381 //
382 void lstout(char tag)
383 {
384         char * s;
385         char * d;
386
387         lsloc = sloc;
388         lcursect = cursect;
389         llineno = curlineno;
390
391         lnfill(lnimage, SRC_COL, SPACE);        // Fill with spaces
392         lnimage[TAG_COL] = tag;
393
394         // Copy line image and handle control characters
395         d = lnimage + SRC_COL;
396
397         for(s=lnbuf; *s; ++s)
398         {
399                 if (*s >= 0x20 || *s == '\t')
400                         *d++ = *s;
401                 else
402                 {
403                         *d++ = '^';
404                         *d++ = (char)(*s + 0x40);
405                 }
406         }
407
408         *d++ = EOS;
409 }
410
411
412 //
413 // Output a value to listing
414 //
415 int listvalue(VALUE v)
416 {
417         sprintf(buf, "=%08X", v);
418         strncpy(lnimage + DATA_COL - 1, buf, 9);
419         return 0;
420 }
421
422
423 /*
424  *  .subttl [-] "string"
425  *
426  *  Set subtitle;
427  *    o  leading '-' supresses page eject
428  *    o  don't .eject on first .subttl, but do so on all other ones,
429  *    o  arrange not to print the .subttl directive
430  *
431  */
432 int d_subttl(void)
433 {
434         int ejectok;
435         ejectok = 1;
436
437         if (*tok == '-')
438         {
439                 ejectok = 0;
440                 ++tok;
441         }
442
443         if (*tok != STRING)
444                 return error("missing string");
445
446 //      strcpy(subttl, (char *)tok[1]);
447         strcpy(subttl, string[tok[1]]);
448
449         tok += 2;
450
451         // Always eject on pages 2+
452         if (ejectok && (subflag || pageno > 1))
453                 eject();
454
455         subflag = 1;
456
457         return 0;
458 }
459
460
461 //
462 // Set title on titles not on the first page, do an eject and clobber the
463 // subtitle
464 //
465 int d_title(void)
466 {
467         if (*tok != STRING)
468                 return error("missing string");
469
470 //      strcpy(title, (char*)tok[1]);
471         strcpy(title, string[tok[1]]);
472         tok += 2;
473
474         if (pageno > 1)
475         {
476                 strcpy(subttl, "");
477                 eject();
478         }
479
480         return 0;
481 }
482