// // RMAC - Renamed Macro Assembler for all Atari computers // LISTING.C - Listing Output // Copyright (C) 199x Landon Dyer, 2011-2021 Reboot and Friends // RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 // Source utilised with the kind permission of Landon Dyer // // -------------------------------------------------------------------------- // 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 // 012345678901234567890123456789012345678901234567890123456789012345678901234567 // filename.... Renamed Macro Assembler N.N.NN (Unknown) // nnnnn aaaaaaaa dddddddddddddddddddd T source code // nnnnn aaaaaaaa dddddddddddddddd // nnnnn =vvvvvvvv #include "listing.h" #include "error.h" #include "procln.h" #include "sect.h" #include "token.h" #include "version.h" char * list_fname; // Listing filename uint8_t subttl[TITLESIZ]; // Current subtitle int listing; // Listing level int pagelen = 61; // Lines on a page int nlines; // #lines on page so far LONG lsloc; // `sloc' at start of line // Private static int lcursect; // `cursect' at start of line static int llineno; // `curlineno' at start of line static int pageno; // Current page number static int pagewidth; // #columns on a page static int subflag; // 0, don't do .eject on subttl (set 1) static char lnimage[IMAGESIZ]; // Image of output line static char title[TITLESIZ]; // Current title static char datestr[20]; // Current date dd-mon-yyyy static char timestr[20]; // Current time hh:mm:ss [am|pm] static char buf[IMAGESIZ]; // Buffer for numbers static long unused; // For supressing 'write' warnings static char * month[16] = { "", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "", "", "" }; // // Eject the page, reset the line count and bump the page number // int eject(void) { if (listing > 0 && list_pag) { println("\f"); nlines = 0; } return 0; } // // Return GEMDOS format date // uint32_t dos_date(void) { uint32_t v; struct tm * tm; time_t tloc; time(&tloc); tm = localtime(&tloc); v = ((tm->tm_year - 80) << 9) | ((tm->tm_mon + 1) << 5) | tm->tm_mday; return v; } // // Return GEMDOS format time // uint32_t dos_time(void) { uint32_t v; struct tm * tm; time_t tloc; time(&tloc); tm = localtime(&tloc); v = (tm->tm_hour << 11) | (tm->tm_min) << 5 | tm->tm_sec; return v; } // // Generate a time string // void time_string(char * buf, uint32_t time) { int hour; char * ampm; hour = (time >> 11); if (hour > 12) { hour -= 12; ampm = "pm"; } else ampm = "am"; sprintf(buf, "%d:%02d:%02d %s", hour, (int)((time >> 5) & 0x3F), (int)((time & 0x1F) << 1), ampm); } // // Generate a date string // void date_string(char * buf, uint32_t date) { sprintf(buf, "%d-%s-%d", (int)(date & 0x1F), month[(date >> 5) & 0xF], (int)((date >> 9) + 1980)); } // // Transform letters a-f in the address and data columns of the listing to // uppercase. (People seem to like uppercase hex better in assembly-language // listings....) // void uc_ln(char * ln) { int i; char j; for(i=LOC_COL; i= 'a' && j <= 'f') ln[i] = (char)(j - 0x20); } } // // Fill region 'dest' with 'len' characters 'c' and null terminate the region // void lnfill(char * dest, int len, char chr) { while (len--) *dest++ = chr; *dest = EOS; } // // Create listing file with the appropriate name // void list_setup(void) { char fnbuf[FNSIZ]; strcpy(fnbuf, list_fname); if (*fnbuf == EOS) { strcpy(fnbuf, firstfname); fext(fnbuf, ".prn", 1); } list_fname = NULL; if ((list_fd = open(fnbuf, _OPEN_FLAGS, _PERM_MODE)) < 0) CantCreateFile(fnbuf); } // // Tag listing with a character, typically for errors or warnings // void taglist(char chr) { lnimage[TAG_COL + 1] = chr; } // // Print a line to the listing file // void println(const char * ln) { unsigned int length; // Create listing file, if necessary if (list_fname != NULL) list_setup(); length = strlen(ln); unused = write(list_fd, ln, length); unused = write(list_fd, "\n", 1L); } // // Ship line 'ln' out; do page breaks and title stuff // void ship_ln(const char * ln) { // If listing level is <= 0, then don't print anything if (listing <= 0) return; if (list_pag) { // Notice bottom of page if (nlines >= pagelen - BOT_MAR) eject(); // Print title, boilerplate, and subtitle at top of page if (nlines == 0) { pageno++; println(""); date_string(datestr, dos_date()); time_string(timestr, dos_time()); sprintf(buf, "%-40s%-20s Page %-4d %s %s RMAC %01i.%01i.%02i (%s)", title, curfname, pageno, timestr, datestr, MAJOR, MINOR, PATCH, PLATFORM); println(buf); sprintf(buf, "%s", subttl); println(buf); println(""); nlines = 4; } } println(ln); nlines++; } // // Initialize listing generator // void InitListing(void) { subflag = 0; pageno = 0; nlines = 0; pagelen = 61; pagewidth = 132; strcpy(title, ""); strcpy(subttl, ""); date_string(datestr, dos_date()); time_string(timestr, dos_time()); } // // Listing EOL // void listeol(void) { CHUNK * ch; char * p; int col; LONG count; int fixcount; DEBUG printf("~list: lsloc=$%X sloc=$%X\n", lsloc, sloc); if (lsloc != sloc) { sprintf(buf, "%08X", lsloc); strncpy(lnimage + LOC_COL, buf, 8); } if (llineno != curlineno) { sprintf(buf, "%5d", llineno); strncpy(lnimage + LN_COL, buf, 5); } // List bytes only when section stayed the same and the section is not a // "no-data" (SBSS) section. An extra annoyance is caused by "ds.b" in a // microprocessor mode, which prints out bytes of zero as if they had been // deposited with dcb. The fix (kludge) is an extra variable which records // the fact that a 'ds.x' directive generated all the data, and it // shouldn't be listed SaveSection(); // Update section variables if (lcursect == cursect && (sect[lcursect].scattr & SBSS) == 0 && lsloc != sloc && just_bss == 0) { ch = sect[lcursect].sfcode; if (lcursect != M6502) { for(; ch!=NULL; ch=ch->chnext) { if (lsloc >= ch->chloc && lsloc < (ch->chloc + ch->ch_size)) break; } } // Fatal: Can't find chunk holding code if (ch == NULL) { nochunk: interror(6); // Can't find generated code in section } p = ch->chptr + (lsloc - ch->chloc); col = DATA_COL; fixcount = 0; for(count=sloc-lsloc; count--; col+=2, ++lsloc) { if (col >= DATA_END) { // Ship the line col = DATA_COL; uc_ln(lnimage); ship_ln(lnimage); lnfill(lnimage, SRC_COL, SPACE); // Fill with spaces sprintf(buf, "%08X", lsloc); strncpy(lnimage + LOC_COL, buf, 8); } if (lcursect != M6502 && lsloc >= (ch->chloc + ch->ch_size)) { if ((ch = ch->chnext) == NULL) goto nochunk; p = ch->chptr; } if (!fixcount) fixcount = fixtest(lcursect, lsloc); if (fixcount) { fixcount--; strcpy(buf, "xx"); p++; // Advance anyway } else sprintf(buf, "%02x", *p++ & 0xff); strncpy(lnimage + col, buf, 2); } // Flush partial line if (col > DATA_COL) { uc_ln(lnimage); ship_ln(lnimage); } } else { uc_ln(lnimage); ship_ln(lnimage); } } // // Copy current (saved) line to output buffer and tag it with a character // void lstout(char tag) { char * s; char * d; lsloc = sloc; lcursect = cursect; llineno = curlineno; lnfill(lnimage, SRC_COL, SPACE); // Fill with spaces lnimage[TAG_COL] = tag; // Copy line image and handle control characters d = lnimage + SRC_COL; for(s=lnbuf; *s; ++s) { if (*s >= 0x20 || *s == '\t') *d++ = *s; else { *d++ = '^'; *d++ = (char)(*s + 0x40); } } *d++ = EOS; } // // Output a value to listing // int listvalue(uint32_t v) { sprintf(buf, "=%08X", v); strncpy(lnimage + DATA_COL - 1, buf, 9); return 0; } /* * .subttl [-] "string" * * Set subtitle; * o leading '-' supresses page eject * o don't .eject on first .subttl, but do so on all other ones, * o arrange not to print the .subttl directive * */ int d_subttl(void) { int ejectok; ejectok = 1; if (*tok == '-') { ejectok = 0; ++tok; } if (*tok != STRING) return error("missing string"); // strcpy(subttl, (char *)tok[1]); strcpy(subttl, string[tok[1]]); tok += 2; // Always eject on pages 2+ if (ejectok && (subflag || pageno > 1)) eject(); subflag = 1; return 0; } // // Set title on titles not on the first page, do an eject and clobber the // subtitle // int d_title(void) { if (*tok != STRING) return error("missing string"); // strcpy(title, (char*)tok[1]); strcpy(title, string[tok[1]]); tok += 2; if (pageno > 1) { strcpy(subttl, ""); eject(); } return 0; }