aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--listamsb.117
-rw-r--r--listamsb.c97
-rw-r--r--listamsb.rst15
3 files changed, 115 insertions, 14 deletions
diff --git a/listamsb.1 b/listamsb.1
index 5a75eb2..c416032 100644
--- a/listamsb.1
+++ b/listamsb.1
@@ -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
diff --git a/listamsb.c b/listamsb.c
index 9acc300..be6b307 100644
--- a/listamsb.c
+++ b/listamsb.c
@@ -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