aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorB. Watson <urchlay@slackware.uk>2024-05-18 03:54:16 -0400
committerB. Watson <urchlay@slackware.uk>2024-05-18 03:54:16 -0400
commit60dd1bb5f3b3c95787043a9057d01d60740d85ce (patch)
tree82c53fe5051c8d1ba1a0f7a183c7ca8ee171bbf0
parent96af9bc891987f6fcc560a6e403c5ada541d8699 (diff)
downloadbw-atari8-tools-60dd1bb5f3b3c95787043a9057d01d60740d85ce.tar.gz
unprotbas: grow variable name table, if needed.
-rw-r--r--unprotbas.18
-rw-r--r--unprotbas.c113
-rw-r--r--unprotbas.rst6
3 files changed, 95 insertions, 32 deletions
diff --git a/unprotbas.1 b/unprotbas.1
index 92e6b66..3b8b832 100644
--- a/unprotbas.1
+++ b/unprotbas.1
@@ -27,7 +27,7 @@ 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 "UNPROTBAS" 1 "2024-05-17" "0.2.1" "Urchlay's Atari 8-bit Tools"
+.TH "UNPROTBAS" 1 "2024-05-18" "0.2.1" "Urchlay's Atari 8-bit Tools"
.SH NAME
unprotbas \- Unprotect LIST-protected Atari 8-bit BASIC programs
.SH SYNOPSIS
@@ -44,9 +44,9 @@ read from standard input.
.sp
\fBoutput\-file\fP will be the unprotected tokenized BASIC program. If it
already exists, it will be overwritten. Use \fI\-\fP to write to standard
-output, but \fB[TODO]\fP \fBunprotbas\fP will refuse to write to standard
-output if it\(aqs a terminal (since tokenized BASIC is binary data and
-may confuse the terminal).
+output, but \fBunprotbas\fP will refuse to write to standard output if
+it\(aqs a terminal (since tokenized BASIC is binary data and may confuse
+the terminal).
.SH OPTIONS
.INDENT 0.0
.TP
diff --git a/unprotbas.c b/unprotbas.c
index 9c45fbb..70741e3 100644
--- a/unprotbas.c
+++ b/unprotbas.c
@@ -8,6 +8,7 @@
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
+#include <string.h>
/* attempt to fix a "list-protected" Atari 8-bit BASIC program.
we don't fully detokenize, so this won't fix truly corrupted
@@ -71,6 +72,46 @@ unsigned short getword(int addr) {
return data[addr] | (data[addr + 1] << 8);
}
+void setword(int addr, int value) {
+ data[addr] = value & 0xff;
+ data[addr + 1] = value >> 8;
+}
+
+void dump_header_vars(void) {
+ fprintf(stderr, "LOMEM %04x\n", lomem);
+ fprintf(stderr, "VNTP %04x\n", vntp);
+ fprintf(stderr, "VNTD %04x\n", vntd);
+ fprintf(stderr, "VVTP %04x\n", vvtp);
+ fprintf(stderr, "STMTAB %04x, codestart %04x\n", stmtab, codestart);
+ fprintf(stderr, "STMCUR %04x\n", stmcur);
+ fprintf(stderr, "STARP %04x\n", starp);
+ fprintf(stderr, "vvstart %04x\n", vvstart);
+}
+
+void read_header(void) {
+ lomem = getword(0);
+ vntp = getword(2);
+ vntd = getword(4);
+ vvtp = getword(6);
+ stmtab = getword(8);
+ stmcur = getword(10);
+ starp = getword(12);
+ codestart = stmtab - STM_OFFSET - (vntp - 256);
+ vnstart = vntp - 256 + 14;
+ vvstart = vvtp - 256 + 14;
+ dump_header_vars();
+}
+
+void set_header_vars(void) {
+ setword(0, lomem);
+ setword(2, vntp);
+ setword(4, vntd);
+ setword(6, vvtp);
+ setword(8, stmtab);
+ setword(10, stmcur);
+ setword(12, starp);
+}
+
/* fixline() calculates & sets correct line length, by iterating
over the statement(s) within the line. the last statement's
offset will be the same as the line offset should have been,
@@ -157,6 +198,21 @@ int fixcode(void) {
return result;
}
+/* sometimes the variable name table isn't large enough to hold
+ the generated variable names. move_code_up() makes more space,
+ by moving the rest of the program (including the variable value
+ table) up in memory. */
+void move_code_up(int offset) {
+ memmove(data + vvstart + offset, data + vvstart, filelen);
+ vvtp += offset;
+ stmtab += offset;
+ stmcur += offset;
+ starp += offset;
+ set_header_vars();
+ read_header();
+ filelen += offset;
+}
+
/* Fixing the variables is a bit more work than it seems like
it might be, because the last byte of the name has to match
the type (inverse video "(" for numeric array, inverse "$" for
@@ -171,9 +227,10 @@ int fixcode(void) {
the file.
We can find the actual table size in the file by subtracting VNTP
- (start of variable name table) from VNTD (end of variable name table),
- and if we run out of space for the generated names, something is
- seriously off...
+ (start of variable name table) from VNTD (end of variable name table).
+ It's possible that the table size is too small for the generated
+ variable names, in which case we have to call move_code_up() to
+ make more room.
The maximum number of variable names is 128. If all 128 vars are in
use, the minimum table size is 230 (26 one-letter names, 102 2-letter
@@ -214,7 +271,9 @@ int fixvars(void) {
vp = vnstart;
while(vp < vvstart) {
unsigned char c = data[vp];
+ /*
fprintf(stderr, "%04x/%04x: %04x\n", vp, vvstart, c);
+ */
/* allow a null byte only at the end of the table! */
/* if(c == 0 && vp == vvstart - 1) break; */
@@ -237,7 +296,26 @@ int fixvars(void) {
}
if(!forcevars && !bad) return 0;
+ /* decide whether we have enough room. pretend every new variable name
+ is 3 bytes (really only true for the 10th and later strings and
+ arrays, but a little wasted space won't hurt anything). */
+ {
+ int vntblsize = vvstart - vnstart;
+ int varcount = (codestart - vvstart) / 8;
+ int neededsize = varcount * 3 + 1;
+ int move_up_by;
+
+ fprintf(stderr, "%d variables according to value table\n", varcount);
+ if(neededsize > vntblsize) {
+ move_up_by = neededsize - vntblsize;
+ fprintf(stderr, "need %d bytes for vntable, only have %d, moving up by %d to %04x\n",
+ neededsize, vntblsize, move_up_by, vvtp + move_up_by);
+ move_code_up(move_up_by);
+ }
+ }
+
vp = vnstart;
+ vv = vvstart;
while(vv < codestart) {
unsigned char sigil = 0;
/* type: scalar = 0, array = 1, string = 2 */
@@ -259,8 +337,8 @@ int fixvars(void) {
data[vp] = ('A' + varname);
} else {
varname -= 26;
- data[vp++] = 'A' + (varname / 9);
- data[vp] = ('1' + (varname % 9));
+ data[vp++] = 'A' + ((varname - 26) / 9);
+ data[vp] = ('1' + ((varname - 26) % 9));
}
if(sigil) {
@@ -327,6 +405,10 @@ void open_input(const char *name) {
void open_output(const char *name) {
if(!name) {
+ if(isatty(fileno(stdout))) {
+ fprintf(stderr, "%s: refusing to write binary data to standard output\n", self);
+ exit(1);
+ }
if(freopen(NULL, "wb", stdout)) {
output_file = stdout;
return;
@@ -381,29 +463,10 @@ int main(int argc, char **argv) {
parse_args(argc, argv);
filelen = readfile();
-
- lomem = getword(0);
- vntp = getword(2);
- vntd = getword(4);
- vvtp = getword(6);
- stmtab = getword(8);
- stmcur = getword(10);
- starp = getword(12);
- codestart = stmtab - STM_OFFSET - (vntp - 256);
- vnstart = vntp - 256 + 14;
- vvstart = vvtp - 256 + 14;
+ read_header();
if(lomem) die("This doesn't look like an Atari BASIC program (no $0000 signature)");
- fprintf(stderr, "LOMEM %04x\n", lomem);
- fprintf(stderr, "VNTP %04x\n", vntp);
- fprintf(stderr, "VNTD %04x\n", vntd);
- fprintf(stderr, "VVTP %04x\n", vvtp);
- fprintf(stderr, "STMTAB %04x, codestart %04x\n", stmtab, codestart);
- fprintf(stderr, "STMCUR %04x\n", stmcur);
- fprintf(stderr, "STARP %04x\n", starp);
- fprintf(stderr, "vvstart %04x\n", vvstart);
-
/*
fprintf(stderr, "data at STMTAB (we hope):\n");
for(int i=codestart; i<filelen; i++) {
diff --git a/unprotbas.rst b/unprotbas.rst
index 735681e..557d27d 100644
--- a/unprotbas.rst
+++ b/unprotbas.rst
@@ -25,9 +25,9 @@ read from standard input.
**output-file** will be the unprotected tokenized BASIC program. If it
already exists, it will be overwritten. Use *-* to write to standard
-output, but **[TODO]** **unprotbas** will refuse to write to standard
-output if it's a terminal (since tokenized BASIC is binary data and
-may confuse the terminal).
+output, but **unprotbas** will refuse to write to standard output if
+it's a terminal (since tokenized BASIC is binary data and may confuse
+the terminal).
OPTIONS
=======