From bcc0e653de5b3e943d6f40178df92805cbe7892c Mon Sep 17 00:00:00 2001 From: "B. Watson" Date: Tue, 25 Feb 2025 02:35:44 -0500 Subject: listamsb: added (detokenize atari microsoft basic files). --- Makefile | 2 +- listamsb.c | 251 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 252 insertions(+), 1 deletion(-) create mode 100644 listamsb.c diff --git a/Makefile b/Makefile index 186a5e1..608c1a7 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ CC=gcc CFLAGS=-Wall $(COPT) -ansi -D_GNU_SOURCE -DVERSION=\"$(VERSION)\" # BINS and SCRIPTS go in $BINDIR, DOCS go in $DOCDIR -BINS=a8eol atr2xfd atrsize axe blob2c blob2xex cart2xex cxrefbas dumpbas fenders protbas renumbas rom2cart unmac65 unprotbas vxrefbas xex1to2 xexamine xexcat xexsplit xfd2atr listbas a8cat a8xd whichbas bas2aplus +BINS=a8eol atr2xfd atrsize axe blob2c blob2xex cart2xex cxrefbas dumpbas fenders protbas renumbas rom2cart unmac65 unprotbas vxrefbas xex1to2 xexamine xexcat xexsplit xfd2atr listbas a8cat a8xd whichbas bas2aplus listamsb SCRIPTS=dasm2atasm diffbas a8diff MANS=a8eol.1 xfd2atr.1 atr2xfd.1 blob2c.1 cart2xex.1 fenders.1 xexsplit.1 xexcat.1 atrsize.1 rom2cart.1 unmac65.1 axe.1 dasm2atasm.1 blob2xex.1 xexamine.1 xex1to2.1 unprotbas.1 protbas.1 renumbas.1 dumpbas.1 vxrefbas.1 cxrefbas.1 listbas.1 a8cat.1 a8xd.1 whichbas.1 diffbas.1 a8diff.1 bas2aplus.1 MAN5S=xex.5 diff --git a/listamsb.c b/listamsb.c new file mode 100644 index 0000000..6890d37 --- /dev/null +++ b/listamsb.c @@ -0,0 +1,251 @@ +#include +#include +#include +#include + +#include "amsbtok.h" + +#define MIN_STD_TOK 0x80 /* END */ +#define MAX_STD_TOK 0xf8 /* < */ + +#define MIN_EXT_TOK 0xa3 /* SGN */ +#define MAX_EXT_TOK 0xc5 /* STACK */ + +#ifndef BUFSIZ +#define BUFSIZ 4096 +#endif + +const char *self; + +char pipe_command[BUFSIZ + 1] = { "a8cat" }; + +int verbose = 0; +int raw_output = 0; + +int need_pclose = 0; +int bytes_read = 0; + +FILE *infile; +FILE *outfile; + +void set_self(const char *argv0) { + char *p; + + self = argv0; + p = strrchr(self, '/'); + if(p) self = p + 1; +} + +void die(const char *msg) { + fprintf(stderr, "%s: %s\n", self, msg); + exit(1); +} + +unsigned char read_byte(void) { + int c; + + c = fgetc(infile); + + if(c < 0) + die("unexpected EOF, file truncated?"); + + bytes_read++; + return (unsigned char)c; +} + +int read_word(void) { + int w; + + w = read_byte(); + w |= (read_byte() << 8); + + return w; +} + +/* return value is program length */ +int read_header(void) { + unsigned char b; + int proglen; + + b = read_byte(); + if(b) die("first byte not $00, not an AMSB file"); + + proglen = read_word(); + + /* TODO: sanity check proglen */ + return proglen; +} + +void unknown_token(int lineno, unsigned char byte, int ext) { + fprintf(outfile, "", (ext ? "ff ": ""), byte); +} + +int next_line(void) { + int ptr, lineno, was_ff, in_string, start; + unsigned char byte; + + /* pointer to last token on the line, offset by whatever MEMLO + happened to be when the file was SAVEd. 0 means this is the + last line, otherwise we don't use its value. */ + ptr = read_word(); + if(!ptr) { + if(verbose) + fprintf(stderr, "end of program\n"); + return -1; + } + + /* TODO: sanity check line number: it must be < 64000, and greater than + the line before it (if there was one). */ + lineno = read_word(); + if(verbose) + fprintf(stderr, "found line number %d\n", lineno); + + fprintf(outfile, "%d ", lineno); + + was_ff = 0; + in_string = 0; + start = 1; + + /* walk and print the tokens. when we hit a null byte, we're done. */ + while(1) { + byte = read_byte(); + + /* lines that consist entirely of a comment introduced with a + single-quote or exclamation point have a : before the comment + start character. don't print it. */ + if(start) { + start = 0; + if(byte == ':') continue; + } + + if(in_string) { + if(byte == 0x00 || byte == '|') { + /* end of string */ + in_string = 0; + putc('"', outfile); + + /* if we read a null, that means the line ends with a string + that's missing its closing double-quote. */ + if(byte == 0x00) { + break; + } else { + continue; + } + } + putc(byte, outfile); + } else if(byte == '"') { + /* strings start but *don't end* with a double-quote */ + in_string = 1; + putc(byte, outfile); + } else if(was_ff) { + if(byte >= MIN_EXT_TOK && byte <= MAX_EXT_TOK) { + fprintf(outfile, "%s", ext_tokens[byte - MIN_EXT_TOK]); + } else { + unknown_token(lineno, byte, 1); + } + was_ff = 0; + } else if(byte == 0xff) { + was_ff = 1; + } else if(byte >= MIN_STD_TOK && byte <= MAX_STD_TOK) { + fprintf(outfile, "%s", std_tokens[byte - MIN_STD_TOK]); + } else if(byte >= 0x80) { + unknown_token(lineno, byte, 0); + } else { + if(!byte) break; + putc(byte, outfile); + } + } + + putc(0x9b, outfile); + + return lineno; +} + +void print_help(void) { + printf("%s - detokenize Atari Microsoft BASIC files\n\n", self); + printf("Usage: %s [-a] [-v] [-h] [-i] [-u] [-t] [-m] [-s] [file]\n", self); + puts(" -a: raw ATASCII output"); + puts(" -v: verbose"); + puts(" -h: show this help"); + puts(" -i -u -t -m -s: passed to a8cat (try 'a8cat -h')"); + puts("file must be a tokenized (SAVEd) AMSB file. if not given, reads from stdin."); +} + +void parse_args(int argc, char **argv) { + char tmp[10]; + int opt; + + while( (opt = getopt(argc, argv, "vaiutmsh")) != -1) { + switch(opt) { + case 'a': raw_output = 1; break; + case 'h': print_help(); exit(0); + case 'v': verbose = 1; break; + case 'i': + case 'u': + case 't': + case 'm': + case 's': + if(strlen(pipe_command) > (BUFSIZ - 10)) + die("too many a8cat options"); + sprintf(tmp, " -%c", opt); + strcat(pipe_command, tmp); + break; + + default: print_help(); exit(1); + } + } + + if(optind >= argc) { + infile = stdin; + } else { + infile = fopen(argv[optind], "rb"); + if(!infile) { + perror(argv[optind]); + exit(1); + } + } +} + +void open_output() { + if(raw_output) { + if(isatty(fileno(stdout))) + die("refusing to write raw ATASCII to a terminal"); + outfile = stdout; + } else { + if(verbose) + fprintf(stderr, "using pipe for output: %s\n", pipe_command); + outfile = popen(pipe_command, "w"); + if(!outfile) { + perror(pipe_command); + exit(1); + } + need_pclose = 1; + } +} + +int main(int argc, char **argv) { + int proglen, lineno; + + set_self(argv[0]); + + parse_args(argc, argv); + + open_output(); + + proglen = read_header(); + if(verbose) + fprintf(stderr, "proglen == %d (%04x)\n", proglen, proglen); + + while( (lineno = next_line()) != -1 ) + if(verbose) + fprintf(stderr, "line %d read OK\n", lineno); + + /* TODO: sanity check proglen vs. file position */ + if(verbose) + fprintf(stderr, "read %d bytes\n", bytes_read);; + + if(need_pclose) pclose(outfile); + + exit(0); +} -- cgit v1.2.3