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