diff options
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | listamsb.c | 251 | 
2 files changed, 252 insertions, 1 deletions
| @@ -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); +} | 
