aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorB. Watson <urchlay@slackware.uk>2025-02-25 02:35:44 -0500
committerB. Watson <urchlay@slackware.uk>2025-02-25 02:35:44 -0500
commitbcc0e653de5b3e943d6f40178df92805cbe7892c (patch)
treee9164e31fd2eb7758c3f6154a42b7de3798f5e3e
parenta464da6bb2caf93d68b4377c9dac10906f59669f (diff)
downloadbw-atari8-tools-bcc0e653de5b3e943d6f40178df92805cbe7892c.tar.gz
listamsb: added (detokenize atari microsoft basic files).
-rw-r--r--Makefile2
-rw-r--r--listamsb.c251
2 files changed, 252 insertions, 1 deletions
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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#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, "<unknown %stoken ", (ext ? "extended " : ""));
+ fprintf(outfile, "%s%02x>", (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);
+}