diff options
-rw-r--r-- | listamsb.1 | 17 | ||||
-rw-r--r-- | listamsb.c | 97 | ||||
-rw-r--r-- | listamsb.rst | 15 |
3 files changed, 115 insertions, 14 deletions
@@ -27,12 +27,12 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "LISTAMSB" 1 "2025-03-05" "0.2.1" "Urchlay's Atari 8-bit Tools" +.TH "LISTAMSB" 1 "2025-03-07" "0.2.1" "Urchlay's Atari 8-bit Tools" .SH NAME listamsb \- List the source of a tokenized Atari Microsoft BASIC program .SH SYNOPSIS .sp -listamsb [\fB\-l\fP] | [\fB\-C\fP] | [[\fB\-a\fP] [\fB\-c\fP] [\fB\-v\fP] [\fB\-h\fP] [\fB\-i\fP] [\fB\-u\fP] [\fB\-t\fP] [\fB\-m\fP] [\fB\-n\fP] [\fB\-s\fP] ...] [\fB\-r\fP \fIline\-range\fP] [\fBinput\-file\fP] [\fBoutput\-file\fP] +listamsb [\fB\-l\fP] | [\fB\-C\fP] | [\fB\-D\fP] | [[\fB\-a\fP] [\fB\-c\fP] [\fB\-v\fP] [\fB\-h\fP] [\fB\-i\fP] [\fB\-u\fP] [\fB\-t\fP] [\fB\-m\fP] [\fB\-n\fP] [\fB\-s\fP] ...] [\fB\-r\fP \fIline\-range\fP] [\fBinput\-file\fP] [\fBoutput\-file\fP] .SH DESCRIPTION .sp \fBlistamsb\fP acts like the \fILIST\fP command in Atari Microsoft BASIC. It reads a @@ -76,10 +76,15 @@ parser requires the spaces. Also, if any comment\-only lines are the target of GOTO or GOSUB, the program won\(aqt run because all comment\-only lines are removed. .sp -This option must be used with an \fBoutput\-file\fP, a pipe, or -redirection: \fBlistamsb\fP will not write tokenized BASIC to a -terminal. \fBa8cat\fP is not used, with this option. None of the -other options have any effect with \fB\-C\fP\&. +This option must be used with an \fBoutput\-file\fP, since seeking is done. +.TP +.B \fB\-D\fP +"Decrunch" a crunched program. Puts spaces where they\(aqre required for +AMSB\(aqs parser. The result will be editable in AMSB. Of course, any +comments that were removed during crunching will not magically be +retored (they\(aqre gone). +.sp +This option must be used with an \fBoutput\-file\fP, since seeking is done. .TP .B \fB\-r\fP \fIline\-range\fP Show only part of the listing. \fIline\-range\fP can be a single line, or @@ -4,6 +4,7 @@ #include <errno.h> #include <string.h> #include <stdarg.h> +#include <ctype.h> #include "amsbtok.h" @@ -81,7 +82,8 @@ int startline = 0; /* -r */ int endline = 65536; /* -r */ int unlock_mode = 0; /* -l */ int initial_eol = 1; /* -n */ -int crunch = 0; /* -C */ +int crunch = 0; /* -C, -D */ +int decrunch = 0; /* -D */ int locked = 0; int need_pclose = 0; @@ -478,6 +480,10 @@ void unlock_program(void) { exit(0); } +void write_code(int ptr, int lineno, const char *code) { + /* TODO */ +} + int crunch_line(void) { unsigned char code[MAX_LINE_LEN_HARD + 1], byte; int lineno, ptr, codelen = 0, in_string = 0, in_comment = 0, commentstart = 0; @@ -542,6 +548,89 @@ int crunch_line(void) { return codelen; } +void expand_token(unsigned char t, unsigned char *buf) { + if(t < 0x80) { + buf[0] = t; + buf[1] = 0; + return; + } + + strcpy((char *)buf, std_tokens[t - MIN_STD_TOK]); +} + +int need_space_between(unsigned char t1, unsigned char t2) { + unsigned char tok1[10], tok2[10]; + unsigned char t1last, t2first; + + if(!t1) return 0; /* start of line */ + if(!t2) return 0; /* end of line */ + if(t1 < 0x80 && t2 < 0x80) return 0; /* 2 ASCII chars */ + + expand_token(t1, tok1); + expand_token(t2, tok2); + t1last = tok1[strlen((char *)tok1) - 1]; /* "PRINT" => "T" */ + t2first = tok2[0]; /* "PRINT" => "P" */ + + return(isalnum(t1last) && isalnum(t2first)); +} + +int decrunch_line(void) { + unsigned char code[MAX_LINE_LEN_HARD + 1], byte = 0, prev; + int lineno, ptr, codelen = 0, in_string = 0, in_comment = 0; + static int addr = 0x700; /* doesn't really matter where */ + + ptr = read_prog_word(); + if(!ptr) return -1; + lineno = read_prog_word(); + + verbose(2, "decrunching line %d", lineno); + + while(1) { + if(codelen >= MAX_LINE_LEN_HARD) + die("line %d too long, decrunching failed", lineno); + prev = byte; + byte = read_prog_byte(); + + if(byte != 0) { + if(in_string) { + if(byte == '|') + in_string = 0; + } else if(in_comment) { + /* NOP */ + } else { + if(byte == '"') { + in_string = 1; + } else if(byte == TOK_REM || byte == TOK_SQUOTE || byte == TOK_BANG) { + in_comment = 1; + } else if(byte == 0xff) { + /* extended/function tokens never need a space before/after them */ + code[codelen++] = byte; + byte = read_prog_byte(); + code[codelen++] = byte; + byte = read_prog_byte(); + } else if(need_space_between(prev, byte)) { + code[codelen++] = ' '; + } + } + } + + code[codelen++] = byte; + if(byte == 0) break; + } + + codelen += 4; /* account for ptr and lineno */ + addr += codelen; + + fputc(addr & 0xff, outfile); + fputc((addr >> 8) & 0xff, outfile); + fputc(lineno & 0xff, outfile); + fputc((lineno >> 8) & 0xff, outfile); + fputs((char *)code, outfile); + fputc(0x00, outfile); + + return codelen; +} + void crunch_program(void) { int newproglen = 0, linelen = 0; @@ -549,7 +638,7 @@ void crunch_program(void) { fputc(0x00, outfile); /* length LSB (fill in later) */ fputc(0x00, outfile); /* length MSB */ - while((linelen = crunch_line()) != -1) + while((linelen = decrunch ? decrunch_line() : crunch_line()) != -1) newproglen += linelen; /* trailing $00 $00 */ @@ -578,6 +667,7 @@ void print_help(void) { puts(" -a: raw ATASCII output"); puts(" -c: check only (no listing)"); puts(" -C: crunch program"); + puts(" -D: decrunch program"); puts(" -l: lock or unlock program"); puts(" -n: no blank line at start of listing"); puts(" -v: verbosity"); @@ -638,8 +728,9 @@ void parse_args(int argc, char **argv) { } } - while( (opt = getopt(argc, argv, "Cnlr:cvaiutmsh")) != -1) { + while( (opt = getopt(argc, argv, "DCnlr:cvaiutmsh")) != -1) { switch(opt) { + case 'D': crunch = decrunch = 1; break; case 'C': crunch = 1; break; case 'l': unlock_mode = 1; break; case 'c': check_only = 1; break; diff --git a/listamsb.rst b/listamsb.rst index 23d21fb..63d72ea 100644 --- a/listamsb.rst +++ b/listamsb.rst @@ -11,7 +11,7 @@ List the source of a tokenized Atari Microsoft BASIC program SYNOPSIS ======== -listamsb [**-l**] | [**-C**] | [[**-a**\] [**-c**] [**-v**\] [**-h**\] [**-i**\] [**-u**\] [**-t**\] [**-m**\] [**-n**\] [**-s**\] ...] [**-r** *line-range*] [**input-file**\] [**output-file**\] +listamsb [**-l**] | [**-C**] | [**-D**] | [[**-a**\] [**-c**] [**-v**\] [**-h**\] [**-i**\] [**-u**\] [**-t**\] [**-m**\] [**-n**\] [**-s**\] ...] [**-r** *line-range*] [**input-file**\] [**output-file**\] DESCRIPTION =========== @@ -58,10 +58,15 @@ OPTIONS target of GOTO or GOSUB, the program won't run because all comment-only lines are removed. - This option must be used with an **output-file**, a pipe, or - redirection: **listamsb** will not write tokenized BASIC to a - terminal. **a8cat** is not used, with this option. None of the - other options have any effect with **-C**. + This option must be used with an **output-file**, since seeking is done. + +**-D** + "Decrunch" a crunched program. Puts spaces where they're required for + AMSB's parser. The result will be editable in AMSB. Of course, any + comments that were removed during crunching will not magically be + retored (they're gone). + + This option must be used with an **output-file**, since seeking is done. **-r** *line-range* Show only part of the listing. *line-range* can be a single line, or |