4fadfefb4278efba66037ec9ab65224e2a080554
[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 char 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         // Notice bottom of page
221         if (nlines >= pagelen - BOT_MAR)
222                 eject();
223
224         // Print title, boilerplate, and subtitle at top of page
225         if (nlines == 0)
226         {
227                 pageno++;
228                 println("");
229                 date_string(datestr, dos_date());
230                 time_string(timestr, dos_time());
231                 sprintf(buf,
232                         "%-40s%-20s Page %-4d    %s %s        RMAC %01i.%01i.%02i (%s)",
233                         title, curfname, pageno, timestr, datestr, MAJOR, MINOR, PATCH,
234                         PLATFORM);
235                 println(buf);
236                 sprintf(buf, "%s", subttl);
237                 println(buf);
238                 println("");
239                 nlines = 4;
240         }
241
242         println(ln);
243         nlines++;
244 }
245
246
247 //
248 // Initialize listing generator
249 //
250 void InitListing(void)
251 {
252         extern VALUE dos_date(), dos_time();
253
254         subflag = 0;
255         pageno = 0;
256         nlines = 0;
257         pagelen = 61;
258         pagewidth = 132;
259         strcpy(title, "");
260         strcpy(subttl, "");
261         date_string(datestr, dos_date());
262         time_string(timestr, dos_time());
263 }
264
265
266 //
267 // Listing EOL
268 //
269 void listeol(void)
270 {
271         CHUNK * ch;
272         char * p;
273         int col;
274         LONG count;
275         int fixcount;
276
277         DEBUG printf("~list: lsloc=$%X sloc=$%X\n", lsloc, sloc);
278
279         if (lsloc != sloc)
280         {
281                 sprintf(buf, "%08X", lsloc);
282                 strncpy(lnimage + LOC_COL, buf, 8);
283         }
284
285         if (llineno != curlineno)
286         {
287                 sprintf(buf, "%5d", llineno);
288                 strncpy(lnimage + LN_COL, buf, 5);
289         }
290
291         // List bytes only when section stayed the same and the section is not a
292         // "no-data" (SBSS) section. An extra annoyance is caused by "ds.b" in a
293         // microprocessor mode, which prints out bytes of zero as if they had been
294         // deposited with dcb. The fix (kludge) is an extra variable which records
295         // the fact that a 'ds.x' directive generated all the data, and it
296         // shouldn't be listed
297         SaveSection();          // Update section variables
298
299         if (lcursect == cursect && (sect[lcursect].scattr & SBSS) == 0
300                 && lsloc != sloc && just_bss == 0)
301         {
302                 ch = sect[lcursect].sfcode;
303
304                 for(; ch!=NULL; ch=ch->chnext)
305                 {
306                         if (lsloc >= ch->chloc && lsloc < (ch->chloc + ch->ch_size))
307                                 break;
308                 }
309
310                 // Fatal: Can't find chunk holding code
311                 if (ch == NULL)
312                 {
313 nochunk:
314                         interror(6);    // Can't find generated code in section
315                 }
316
317                 p =  ch->chptr + (lsloc - ch->chloc);
318                 col = DATA_COL;
319                 fixcount = 0;
320
321                 for(count=sloc-lsloc; count--; col+=2, ++lsloc)
322                 {
323                         if (col >= DATA_END)
324                         {
325                                 // Ship the line
326                                 col = DATA_COL;
327                                 uc_ln(lnimage);
328                                 ship_ln(lnimage);
329                                 lnfill(lnimage, SRC_COL, SPACE);        // Fill with spaces
330                                 sprintf(buf, "%08X", lsloc);
331                                 strncpy(lnimage + LOC_COL, buf, 8);
332                         }
333
334                         if (lsloc >= (ch->chloc + ch->ch_size))
335                         {
336                                 if ((ch = ch->chnext) == NULL)
337                                         goto nochunk;
338
339                                 p = ch->chptr;
340                         }
341
342                         if (!fixcount)
343                                 fixcount = fixtest(lcursect, lsloc);
344
345                         if (fixcount)
346                         {
347                                 fixcount--;
348                                 strcpy(buf, "xx");
349                                 p++;            // Advance anyway
350                         }
351                         else 
352                                 sprintf(buf, "%02x", *p++ & 0xff);
353
354                         strncpy(lnimage + col, buf, 2);
355                 }
356
357                 // Flush partial line 
358                 if (col > DATA_COL)
359                 {
360                         uc_ln(lnimage);
361                         ship_ln(lnimage);
362                 }
363         }
364         else
365         {
366                 uc_ln(lnimage);
367                 ship_ln(lnimage);
368         }
369 }
370
371
372 //
373 // Copy current (saved) line to output buffer and tag it with a character
374 //
375 void lstout(char tag)
376 {
377         char * s;
378         char * d;
379
380         lsloc = sloc;
381         lcursect = cursect;
382         llineno = curlineno;
383
384         lnfill(lnimage, SRC_COL, SPACE);        // Fill with spaces
385         lnimage[TAG_COL] = tag;
386
387         // Copy line image and handle control characters
388         d = lnimage + SRC_COL;
389
390         for(s=lnbuf; *s; ++s)
391         {
392                 if (*s >= 0x20 || *s == '\t')
393                         *d++ = *s;
394                 else
395                 {
396                         *d++ = '^';
397                         *d++ = (char)(*s + 0x40);
398                 }
399         }
400
401         *d++ = EOS;
402 }
403
404
405 //
406 // Output a value to listing
407 //
408 int listvalue(VALUE v)
409 {
410         sprintf(buf, "=%08X", v);
411         strncpy(lnimage + DATA_COL - 1, buf, 9);
412         return 0;
413 }
414
415
416 /*
417  *  .subttl [-] "string"
418  *
419  *  Set subtitle;
420  *    o  leading '-' supresses page eject
421  *    o  don't .eject on first .subttl, but do so on all other ones,
422  *    o  arrange not to print the .subttl directive
423  *
424  */
425 int d_subttl(void)
426 {
427         int ejectok;
428         ejectok = 1;
429
430         if (*tok == '-')
431         {
432                 ejectok = 0;
433                 ++tok;
434         }
435
436         if (*tok != STRING)
437                 return error("missing string");
438
439 //      strcpy(subttl, (char *)tok[1]);
440         strcpy(subttl, string[tok[1]]);
441
442         tok += 2;
443
444         // Always eject on pages 2+ 
445         if (ejectok && (subflag || pageno > 1))
446                 eject();
447
448         subflag = 1;
449
450         return 0;
451 }
452
453
454 //
455 // Set title on titles not on the first page, do an eject and clobber the
456 // subtitle
457 //
458 int d_title(void)
459 {
460         if (*tok != STRING)
461                 return error("missing string");
462         
463 //      strcpy(title, (char*)tok[1]);
464         strcpy(title, string[tok[1]]);
465         tok += 2;
466
467         if (pageno > 1)
468         {
469                 strcpy(subttl, "");
470                 eject();
471         }
472
473         return 0;
474 }
475