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