aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorB. Watson <urchlay@slackware.uk>2025-03-05 20:51:47 -0500
committerB. Watson <urchlay@slackware.uk>2025-03-05 20:51:47 -0500
commit334920cf9cb772b75ed4379ace5dd68f521b77d4 (patch)
treeda2c161d3e7e77fd7133393734fe036ad01a49bc
parentd053b20b0cd20b8f86f38fcb3982ed3f2c5ebf6d (diff)
downloadbw-atari8-tools-334920cf9cb772b75ed4379ace5dd68f521b77d4.tar.gz
listamsb: add -C (crunch) option.
-rw-r--r--listamsb.116
-rw-r--r--listamsb.c111
-rw-r--r--listamsb.rst16
3 files changed, 136 insertions, 7 deletions
diff --git a/listamsb.1 b/listamsb.1
index 0115e36..5a75eb2 100644
--- a/listamsb.1
+++ b/listamsb.1
@@ -32,7 +32,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
listamsb \- List the source of a tokenized Atari Microsoft BASIC program
.SH SYNOPSIS
.sp
-listamsb [\fB\-l\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\-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
@@ -68,10 +68,24 @@ Check only. No output on stdout. Diagnostics are still printed on stderr,
and the exit status is unchanged. It\(aqs an error to give an \fBoutput\-file\fP with
this option.
.TP
+.B \fB\-C\fP
+"Crunch" program: remove comments and spaces that occur outside of a
+string. The resulting program is usually 5 to 10 percent smaller, and
+will RUN correctly, but will be difficult to edit in AMSB because its
+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\&.
+.TP
.B \fB\-r\fP \fIline\-range\fP
Show only part of the listing. \fIline\-range\fP can be a single line, or
a comma\- or hyphen\-separated pair of starting and ending line numbers (e.g. \fB100,200\fP, or
\fB1000\-2000\fP).
+.sp
If the start line number is omitted (e.g. \fB,100\fP), it will be treated as
\fB0\fP (meaning, list from the beginning of the program). If the ending line
number is omitted (e.g. \fB100,\fP), it means "list until the end of the program".
diff --git a/listamsb.c b/listamsb.c
index ed2f813..9acc300 100644
--- a/listamsb.c
+++ b/listamsb.c
@@ -81,6 +81,7 @@ int startline = 0; /* -r */
int endline = 65536; /* -r */
int unlock_mode = 0; /* -l */
int initial_eol = 1; /* -n */
+int crunch = 0; /* -C */
int locked = 0;
int need_pclose = 0;
@@ -439,7 +440,7 @@ int next_line(void) {
if(last_ptr != -1) {
int plen = ptr - last_ptr;
if(len != plen) {
- warn("line %d: EOL address doesn't match actual line length %d", lineno, len);
+ warn("line %d: EOL address doesn't match actual line length %d (should be %d)", lineno, len, plen);
}
}
@@ -477,12 +478,106 @@ void unlock_program(void) {
exit(0);
}
+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;
+ static int addr = 0x700; /* doesn't really matter where */
+
+ ptr = read_prog_word();
+ if(!ptr) return -1;
+ lineno = read_prog_word();
+
+ verbose(2, "crunching line %d", lineno);
+
+ while(1) {
+ if(codelen >= MAX_LINE_LEN_HARD)
+ die("line %d too long, crunching failed", lineno);
+ byte = read_prog_byte();
+
+ if(byte != 0) {
+ if(in_string) {
+ if(byte == '|')
+ in_string = 0;
+ } else if(in_comment) {
+ continue;
+ } else {
+ if(byte == '"')
+ in_string = 1;
+ else if(byte == TOK_REM || byte == TOK_SQUOTE || byte == TOK_BANG) {
+ in_comment = 1;
+ commentstart = codelen;
+ }
+ else if(byte == ' ')
+ continue;
+ }
+ }
+
+ code[codelen++] = byte;
+ if(byte == 0) break;
+ }
+
+ /* omit comment-only lines */
+ if(code[0] == TOK_REM) return 0;
+ if(code[0] == ':') {
+ if(code[1] == TOK_SQUOTE || code[1] == TOK_BANG)
+ return 0;
+ }
+
+ /* omit trailing comments */
+ if(commentstart) {
+ code[commentstart - 1] = 0; /* null out the colon before the comment */
+ codelen = commentstart;
+ }
+
+ 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;
+
+ fputc(0x00, outfile); /* signature (0 = not locked) */
+ fputc(0x00, outfile); /* length LSB (fill in later) */
+ fputc(0x00, outfile); /* length MSB */
+
+ while((linelen = crunch_line()) != -1)
+ newproglen += linelen;
+
+ /* trailing $00 $00 */
+ fputc(0x00, outfile);
+ fputc(0x00, outfile);
+
+ if(fseek(outfile, 1L, SEEK_SET) < 0)
+ os_err("fseek() failed");
+
+ newproglen += 2; /* account for trailing $00 $00 */
+ fputc(newproglen & 0xff, outfile);
+ fputc((newproglen >> 8) & 0xff, outfile);
+ fclose(outfile);
+
+ newproglen += 3;
+ verbose(1, "crunched %d byte program to %d bytes (%.1f%% savings)",
+ bytes_read,
+ newproglen,
+ 100.0 * (1.0 - (float)newproglen / (float)proglen));
+}
+
void print_help(void) {
printf("%s v" VERSION " - detokenize Atari Microsoft BASIC files\n", self);
puts("By B. Watson <urchlay@slackware.uk>, released under the WTFPL");
- printf("Usage: %s [[-l] | [-[avhinutms] ... ] [-r *start,end*]] <file> <outfile>\n", self);
+ printf("Usage: %s [[-l] | [-[acClnviutms] ... ] [-r *start,end*]] <file> <outfile>\n", self);
puts(" -a: raw ATASCII output");
puts(" -c: check only (no listing)");
+ puts(" -C: crunch program");
puts(" -l: lock or unlock program");
puts(" -n: no blank line at start of listing");
puts(" -v: verbosity");
@@ -543,8 +638,9 @@ void parse_args(int argc, char **argv) {
}
}
- while( (opt = getopt(argc, argv, "nlr:cvaiutmsh")) != -1) {
+ while( (opt = getopt(argc, argv, "Cnlr:cvaiutmsh")) != -1) {
switch(opt) {
+ case 'C': crunch = 1; break;
case 'l': unlock_mode = 1; break;
case 'c': check_only = 1; break;
case 'a': raw_output = 1; break;
@@ -589,6 +685,8 @@ void parse_args(int argc, char **argv) {
if(!freopen(outname, "wb", stdout))
os_err("%s: %s", outname, strerror(errno));
verbose(1, "redirected stdout to %s", outname);
+ } else if(crunch) {
+ os_err("can't use stdout with -C, must give an output filename");
}
}
@@ -599,10 +697,10 @@ void open_output() {
os_err("/dev/null: %s", strerror(errno));
}
verbose(1, "using /dev/null for output (check_only)");
- } else if(raw_output || unlock_mode) {
+ } else if(raw_output || unlock_mode || crunch) {
if(isatty(fileno(stdout))) {
os_err("refusing to write %s to a terminal",
- (unlock_mode ? "tokenized BASIC" : "raw ATASCII"));
+ (raw_output ? "raw ATASCII" : "tokenized BASIC"));
}
outfile = stdout;
verbose(1, "using stdout for output");
@@ -630,6 +728,9 @@ int main(int argc, char **argv) {
if(unlock_mode) {
unlock_program();
exit(0); /* don't need finish() here, no parsing done */
+ } else if(crunch) {
+ crunch_program();
+ exit(0);
}
start_listing();
diff --git a/listamsb.rst b/listamsb.rst
index 960e6dc..23d21fb 100644
--- a/listamsb.rst
+++ b/listamsb.rst
@@ -11,7 +11,7 @@ List the source of a tokenized Atari Microsoft BASIC program
SYNOPSIS
========
-listamsb [**-l**] | [[**-a**\] [**-c**] [**-v**\] [**-h**\] [**-i**\] [**-u**\] [**-t**\] [**-m**\] [**-n**\] [**-s**\] ...] [**-r** *line-range*] [**input-file**\] [**output-file**\]
+listamsb [**-l**] | [**-C**] | [[**-a**\] [**-c**] [**-v**\] [**-h**\] [**-i**\] [**-u**\] [**-t**\] [**-m**\] [**-n**\] [**-s**\] ...] [**-r** *line-range*] [**input-file**\] [**output-file**\]
DESCRIPTION
===========
@@ -50,10 +50,24 @@ OPTIONS
and the exit status is unchanged. It's an error to give an **output-file** with
this option.
+**-C**
+ "Crunch" program: remove comments and spaces that occur outside of a
+ string. The resulting program is usually 5 to 10 percent smaller, and
+ will RUN correctly, but will be difficult to edit in AMSB because its
+ parser requires the spaces. Also, if any comment-only lines are the
+ 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**.
+
**-r** *line-range*
Show only part of the listing. *line-range* can be a single line, or
a comma- or hyphen-separated pair of starting and ending line numbers (e.g. **100,200**, or
**1000-2000**).
+
If the start line number is omitted (e.g. **,100**), it will be treated as
**0** (meaning, list from the beginning of the program). If the ending line
number is omitted (e.g. **100,**), it means "list until the end of the program".