aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--bas.c183
-rw-r--r--bas.h80
-rw-r--r--unprotbas.c272
4 files changed, 298 insertions, 241 deletions
diff --git a/Makefile b/Makefile
index d1bb32f..ff04def 100644
--- a/Makefile
+++ b/Makefile
@@ -49,6 +49,10 @@ RST2MAN=rst2man
all: $(BINS) manpages symlinks subdirs
+unprotbas: bas.o
+
+bas.o: bas.c bas.h
+
subdirs:
for dir in $(SUBDIRS); do make -C $$dir COPT=$(COPT); done
diff --git a/bas.c b/bas.c
new file mode 100644
index 0000000..64c4f3f
--- /dev/null
+++ b/bas.c
@@ -0,0 +1,183 @@
+/* bas.c - API for writing standalone programs that deal with
+ tokenized Atari 8-bit BASIC program. */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+
+#include "bas.h"
+
+int verbose = 0;
+unsigned short lomem;
+unsigned short vntp;
+unsigned short vntd;
+unsigned short vvtp;
+unsigned short stmtab;
+unsigned short stmcur;
+unsigned short starp;
+unsigned short codestart;
+unsigned short code_end;
+unsigned short vnstart;
+unsigned short vvstart;
+int filelen;
+const char *self;
+unsigned char program[BUFSIZE];
+FILE *input_file;
+FILE *output_file;
+
+void die(const char *msg) {
+ fprintf(stderr, "%s: %s\n", self, msg);
+ exit(1);
+}
+
+/* read entire file into memory */
+int readfile(void) {
+ int got = fread(program, 1, BUFSIZE - 1, input_file);
+ if(verbose) fprintf(stderr, "Read %d bytes.\n", got);
+ if(!feof(input_file))
+ fprintf(stderr, "Warning: file is >64KB, way too big for a BASIC program.\n");
+ else if(got > MAX_PROG_SIZE)
+ fprintf(stderr, "Warning: file is %d bytes, suspiciously large for a BASIC program.\n", got);
+ fclose(input_file);
+ if(got < MIN_PROG_SIZE)
+ die("File too short to be a BASIC program (truncated?)\n");
+ return got;
+}
+
+/* get a 16-bit value from the file, in 6502 LSB/MSB order. */
+unsigned short getword(int addr) {
+ return program[addr] | (program[addr + 1] << 8);
+}
+
+void setword(int addr, int value) {
+ program[addr] = value & 0xff;
+ program[addr + 1] = value >> 8;
+}
+
+void dump_header_vars(void) {
+ fprintf(stderr, "LOMEM $%04x VNTP $%04x VNTD $%04x VVTP $%04x\n", lomem, vntp, vntd, vvtp);
+ fprintf(stderr, "STMTAB $%04x STMCUR $%04x STARP $%04x\n", stmtab, stmcur, starp);
+ fprintf(stderr, "vnstart $%04x, vvstart $%04x, codestart $%04x, code_end $%04x\n", vnstart, vvstart, codestart, code_end);
+}
+
+void parse_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 - TBL_OFFSET - (vntp - 256);
+ vnstart = vntp - TBL_OFFSET;
+ vvstart = vvtp - TBL_OFFSET;
+ code_end = starp - TBL_OFFSET;
+
+ if(filelen < code_end) {
+ fprintf(stderr, "Warning: file is truncated: %d bytes, should be %d.\n", filelen, code_end);
+ }
+
+ if(verbose) dump_header_vars();
+}
+
+void update_header(void) {
+ setword(0, lomem);
+ setword(2, vntp);
+ setword(4, vntd);
+ setword(6, vvtp);
+ setword(8, stmtab);
+ setword(10, stmcur);
+ setword(12, starp);
+}
+
+/* sometimes the variable name table isn't large enough to hold
+ the generated variable names. move_code() makes more space,
+ by moving the rest of the program (including the variable value
+ table) up in memory. */
+void move_code(int offset) {
+ unsigned char *dest = program + vvstart + offset;
+
+ if(dest < program || ((filelen + offset) > (BUFSIZE - 1))) {
+ die("Attempt to move memory out of range; corrupt header bytes?\n");
+ }
+
+ memmove(dest, program + vvstart, filelen);
+
+ vntd += offset;
+ vvtp += offset;
+ stmtab += offset;
+ stmcur += offset;
+ starp += offset;
+ update_header();
+ parse_header();
+ filelen += offset;
+}
+
+void adjust_vntable_size(int oldsize, int newsize) {
+ int move_by;
+ if(oldsize != newsize) {
+ move_by = newsize - oldsize;
+ if(verbose) fprintf(stderr,
+ "Need %d bytes for vntable, have %d, moving VVTP by %d to $%04x.\n",
+ newsize, oldsize, move_by, vvtp + move_by);
+ move_code(move_by);
+ }
+}
+
+void invalid_args(const char *arg) {
+ fprintf(stderr, "%s: Invalid argument '%s'.\n\n", self, arg);
+ exit(1);
+}
+
+FILE *open_file(const char *name, const char *mode) {
+ FILE *fp;
+ if(!(fp = fopen(name, mode))) {
+ perror(name);
+ exit(1);
+ }
+ return fp;
+}
+
+void open_input(const char *name) {
+ if(!name) {
+ if(isatty(fileno(stdin))) {
+ die("Can't read binary data from the terminal.");
+ }
+ if(freopen(NULL, "rb", stdin)) {
+ input_file = stdin;
+ return;
+ } else {
+ perror("stdin");
+ exit(1);
+ }
+ }
+
+ input_file = open_file(name, "rb");
+}
+
+void open_output(const char *name) {
+ if(!name || (strcmp(name, "-") == 0)) {
+ if(isatty(fileno(stdout))) {
+ die("Refusing to write binary data to the terminal.");
+ }
+ if(freopen(NULL, "wb", stdout)) {
+ output_file = stdout;
+ return;
+ } else {
+ perror("stdout");
+ exit(1);
+ }
+ }
+ output_file = open_file(name, "wb");
+}
+
+extern void set_self(const char *argv0) {
+ char *p;
+
+ self = argv0;
+ p = strrchr(self, '/');
+ if(p) self = p + 1;
+}
diff --git a/bas.h b/bas.h
new file mode 100644
index 0000000..3a65567
--- /dev/null
+++ b/bas.h
@@ -0,0 +1,80 @@
+/* bas.h - API for writing standalone programs that deal with
+ tokenized Atari 8-bit BASIC program. */
+
+/* maximum size of the program in memory. 64KB is actually way overkill. */
+#define BUFSIZE 65536
+
+/* the difference between the VVTP and VNTP values in the file, and the
+ actual file positions of the variable names and values. */
+#define TBL_OFFSET 0xf2
+
+/* minimum program size, for a program that has no variables and
+ only one line of code (the immediate line 32768, consisting only of
+ one token, which would be CSAVE). anything smaller than this, we
+ can't process. */
+#define MIN_PROG_SIZE 21
+
+/* maximum practical size for a BASIC program. if a file exceeds this
+ size, we warn about it, but otherwise process it normally.
+ this value is derived by subtracting the default LOMEM without DOS
+ ($0700) from the start of the display list in GR.0 ($9c20, on a 48K Atari).
+ */
+#define MAX_PROG_SIZE 38176
+
+/* maximum number of variables in the variable name and value tables. this
+ could be 128, but "ERROR- 4" still expands the tables. Entries >128
+ don't have tokens, can't be referred to in code, but we'll preserve
+ them anyway. */
+#define MAXVARS 256
+
+/* tokenized colon (statement separator) */
+#define TOK_COLON 0x14
+
+/* variable types, bits 6-7 of byte 0 of each vvtable entry. */
+#define TYPE_SCALAR 0
+#define TYPE_ARRAY 1
+#define TYPE_STRING 2
+
+/* BASIC 14-byte header values */
+extern unsigned short lomem;
+extern unsigned short vntp;
+extern unsigned short vntd;
+extern unsigned short vvtp;
+extern unsigned short stmtab;
+extern unsigned short stmcur;
+extern unsigned short starp;
+
+/* positions where various parts of the file start,
+ derived from the header vars above. */
+extern unsigned short codestart;
+extern unsigned short code_end;
+extern unsigned short vnstart;
+extern unsigned short vvstart;
+extern int filelen;
+
+/* name of executable, taken from argv[0] */
+extern const char *self;
+
+/* entire file gets read into memory (for now) */
+extern unsigned char program[BUFSIZE];
+
+/* file handles */
+extern FILE *input_file;
+extern FILE *output_file;
+
+extern int verbose;
+
+extern void set_self(const char *argv0);
+extern void die(const char *msg);
+extern int readfile(void);
+extern unsigned short getword(int addr);
+extern void setword(int addr, int value);
+extern void dump_header_vars(void);
+extern void parse_header(void);
+extern void update_header(void);
+extern void move_code(int offset);
+extern void adjust_vntable_size(int oldsize, int newsize);
+extern void invalid_args(const char *arg);
+extern FILE *open_file(const char *name, const char *mode);
+extern void open_input(const char *name);
+extern void open_output(const char *name);
diff --git a/unprotbas.c b/unprotbas.c
index 3486ac4..8c5c14a 100644
--- a/unprotbas.c
+++ b/unprotbas.c
@@ -5,6 +5,8 @@
#include <ctype.h>
#include <time.h>
+#include "bas.h"
+
/* attempt to fix a "list-protected" Atari 8-bit BASIC program.
we don't fully detokenize, so this won't fix truly corrupted
files.
@@ -16,43 +18,6 @@
or whatever), we "fix" that by making up new variable names.
*/
-/* maximum size of the program in memory. 64KB is actually way overkill. */
-#define BUFSIZE 65536
-
-/* the difference between the VVTP and VNTP values in the file, and the
- actual file positions of the variable names and values. */
-#define TBL_OFFSET 0xf2
-
-/* minimum program size, for a program that has no variables and
- only one line of code (the immediate line 32768, consisting only of
- one token, which would be CSAVE). anything smaller than this, we
- can't process. */
-#define MIN_PROG_SIZE 21
-
-/* maximum practical size for a BASIC program. if a file exceeds this
- size, we warn about it, but otherwise process it normally.
- this value is derived by subtracting the default LOMEM without DOS
- ($0700) from the start of the display list in GR.0 ($9c20, on a 48K Atari).
- */
-#define MAX_PROG_SIZE 38176
-
-/* maximum number of variables in the variable name and value tables. this
- could be 128, but "ERROR- 4" still expands the tables. Entries >128
- don't have tokens, can't be referred to in code, but we'll preserve
- them anyway. */
-#define MAXVARS 256
-
-/* tokenized colon (statement separator) */
-#define TOK_COLON 0x14
-
-/* variable types, bits 6-7 of byte 0 of each vvtable entry. */
-#define TYPE_SCALAR 0
-#define TYPE_ARRAY 1
-#define TYPE_STRING 2
-
-/* entire file gets read into memory (for now) */
-unsigned char data[BUFSIZE];
-
/* for the -p/-pc options: 32767 END */
unsigned char badcode[] = {
0xff, 0x7f, /* line number 32767 */
@@ -74,108 +39,19 @@ unsigned char varnames[BUFSIZE];
unsigned char *varmap[MAXVARS];
int varmap_count;
-/* BASIC 14-byte header values */
-unsigned short lomem;
-unsigned short vntp;
-unsigned short vntd;
-unsigned short vvtp;
-unsigned short stmtab;
-unsigned short stmcur;
-unsigned short starp;
-
-/* positions where various parts of the file start,
- derived from the header vars above. */
-unsigned short codestart;
-unsigned short code_end;
-unsigned short vnstart;
-unsigned short vvstart;
-int filelen;
-
-/* name of executable, taken from argv[0] */
-char *self;
-
/* these are set by the various command-line switches */
int keepvars = 0;
int forcevars = 0;
int keepgarbage = 1;
int checkonly = 0;
int was_protected = 0;
-int verbose = 0;
int readmap = 0;
int writemap = 0;
int protect_vars = 0;
int protect_code = 0;
-/* file handles */
-FILE *input_file = NULL;
-FILE *output_file = NULL;
char *output_filename = NULL;
-void die(const char *msg) {
- fprintf(stderr, "%s: %s\n", self, msg);
- exit(1);
-}
-
-/* read entire file into memory */
-int readfile(void) {
- int got = fread(data, 1, BUFSIZE - 1, input_file);
- if(verbose) fprintf(stderr, "Read %d bytes.\n", got);
- if(!feof(input_file))
- fprintf(stderr, "Warning: file is >64KB, way too big for a BASIC program.\n");
- else if(got > MAX_PROG_SIZE)
- fprintf(stderr, "Warning: file is %d bytes, suspiciously large for a BASIC program.\n", got);
- fclose(input_file);
- if(got < MIN_PROG_SIZE)
- die("File too short to be a BASIC program (truncated?)\n");
- return got;
-}
-
-/* get a 16-bit value from the file, in 6502 LSB/MSB order. */
-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 VNTP $%04x VNTD $%04x VVTP $%04x\n", lomem, vntp, vntd, vvtp);
- fprintf(stderr, "STMTAB $%04x STMCUR $%04x STARP $%04x\n", stmtab, stmcur, starp);
- fprintf(stderr, "vnstart $%04x, vvstart $%04x, codestart $%04x, code_end $%04x\n", vnstart, vvstart, codestart, code_end);
-}
-
-void parse_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 - TBL_OFFSET - (vntp - 256);
- vnstart = vntp - TBL_OFFSET;
- vvstart = vvtp - TBL_OFFSET;
- code_end = starp - TBL_OFFSET;
-
- if(filelen < code_end) {
- fprintf(stderr, "Warning: file is truncated: %d bytes, should be %d.\n", filelen, code_end);
- }
-
- if(verbose) dump_header_vars();
-}
-
-void update_header(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,
@@ -212,12 +88,12 @@ void update_header(void) {
*/
int fixline(int linepos) {
/* +3 here to skip the line number + line length */
- int offset = data[linepos + 3];
+ int offset = program[linepos + 3];
- while(data[linepos + offset - 1] == TOK_COLON)
- offset = data[linepos + offset];
+ while(program[linepos + offset - 1] == TOK_COLON)
+ offset = program[linepos + offset];
- data[linepos + 2] = offset;
+ program[linepos + 2] = offset;
return offset;
}
@@ -236,7 +112,7 @@ int fixcode(void) {
}
lineno = tmpno;
- offset = data[pos + 2];
+ offset = program[pos + 2];
/* fprintf(stderr, "pos %d, line #%d, offset %d\n", pos, lineno, offset); */
if(offset < 6) {
if(verbose) fprintf(stderr, "Found invalid offset %d (<6) at line %d, file offset $%04x.\n", offset, lineno, pos);
@@ -255,12 +131,12 @@ int fixcode(void) {
if(filelen > pos) {
int i, same = 1;
for(i = pos; i < filelen; i++) {
- if(data[i] != data[pos]) same = 0;
+ if(program[i] != program[pos]) same = 0;
}
if(verbose) {
fprintf(stderr, "Trailing garbage at EOF, %d bytes, ", filelen - pos);
if(same)
- fprintf(stderr, "all $%02x", data[pos]);
+ fprintf(stderr, "all $%02x", program[pos]);
else
fprintf(stderr, "maybe valid data");
fprintf(stderr, ", %s.\n", (keepgarbage ? "keeping" : "removing"));
@@ -285,7 +161,7 @@ void breakcode(void) {
if(tmpno == 32768) {
break;
} else {
- offset = data[pos + 2];
+ offset = program[pos + 2];
if(!offset) {
fprintf(stderr, "%s: program already was code-protected.\n", self);
exit(2);
@@ -301,10 +177,10 @@ void breakcode(void) {
/* pos is now the start of line 32768, move it up to make room for
the new line */
offset = sizeof(badcode);
- memmove(data + pos + offset, data + pos, filelen);
+ memmove(program + pos + offset, program + pos, filelen);
/* insert new line */
- memmove(data + pos, badcode, offset);
+ memmove(program + pos, badcode, offset);
if(verbose)
fprintf(stderr, "Inserted line 32767 with invalid offset at file offset $%04x.\n", pos);
@@ -317,29 +193,6 @@ void breakcode(void) {
filelen += offset;
}
-/* sometimes the variable name table isn't large enough to hold
- the generated variable names. move_code() makes more space,
- by moving the rest of the program (including the variable value
- table) up in memory. */
-void move_code(int offset) {
- unsigned char *dest = data + vvstart + offset;
-
- if(dest < data || ((filelen + offset) > (BUFSIZE - 1))) {
- die("Attempt to move memory out of range; corrupt header bytes?\n");
- }
-
- memmove(dest, data + vvstart, filelen);
-
- vntd += offset;
- vvtp += offset;
- stmtab += offset;
- stmcur += offset;
- starp += offset;
- update_header();
- parse_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
@@ -378,7 +231,7 @@ int vntable_ok(void) {
vp = vnstart + 1;
bad = 1;
while(vp < vvstart - 1) {
- if(data[vp] != data[vnstart]) {
+ if(program[vp] != program[vnstart]) {
bad = 0;
break;
}
@@ -389,7 +242,7 @@ int vntable_ok(void) {
/* 2nd pass: bad = 1 if there's any invalid character in the table. */
vp = vnstart;
while(vp < vvstart) {
- unsigned char c = data[vp];
+ unsigned char c = program[vp];
/* treat a null byte as end-of-table, ignore any junk between it and VNTP. */
if(c == 0) break;
@@ -423,15 +276,15 @@ int rebuild_vntable(int write) {
while(vv < codestart) {
unsigned char sigil = 0;
/* type: scalar = 0, array = 1, string = 2 */
- unsigned char type = data[vv] >> 6;
- /* fprintf(stderr, "%04x: %04x, %d\n", vv, data[vv], type); */
+ unsigned char type = program[vv] >> 6;
+ /* fprintf(stderr, "%04x: %04x, %d\n", vv, program[vv], type); */
if(varnum == MAXVARS) {
fprintf(stderr, "Warning: skipping variable numbers >=%d in value table.\n", MAXVARS);
break;
}
- if(varnum != data[vv+1]) {
+ if(varnum != program[vv+1]) {
fprintf(stderr, "Warning: variable #%d value is corrupt!\n", varnum);
}
@@ -445,13 +298,13 @@ int rebuild_vntable(int write) {
}
if(varname < 26) {
- if(write) data[vp] = ('A' + varname);
+ if(write) program[vp] = ('A' + varname);
size++;
} else {
varname -= 26;
if(write) {
- data[vp++] = 'A' + varname / 9;
- data[vp] = '1' + varname % 9;
+ program[vp++] = 'A' + varname / 9;
+ program[vp] = '1' + varname % 9;
}
size += 2;
}
@@ -459,9 +312,9 @@ int rebuild_vntable(int write) {
if(sigil) {
size++;
vp++;
- if(write) data[vp++] = sigil;
+ if(write) program[vp++] = sigil;
} else {
- if(write) data[vp] |= 0x80;
+ if(write) program[vp] |= 0x80;
vp++;
}
@@ -472,23 +325,12 @@ int rebuild_vntable(int write) {
/* there's supposed to be a null byte at the end of the table, unless
all 128 table slots are used... except really, there can be >=129
entries, and there's always a null byte. */
- if(write) data[vp] = 0;
+ if(write) program[vp] = 0;
size++;
return size;
}
-void adjust_vntable_size(int oldsize, int newsize) {
- int move_by;
- if(oldsize != newsize) {
- move_by = newsize - oldsize;
- if(verbose) fprintf(stderr,
- "Need %d bytes for vntable, have %d, moving VVTP by %d to $%04x.\n",
- newsize, oldsize, move_by, vvtp + move_by);
- move_code(move_by);
- }
-}
-
int fixvars(void) {
int old_vntable_size, new_vntable_size;
@@ -515,8 +357,8 @@ void write_var_map(void) {
die("Can't create map file for -w option.");
}
- for(vp = vnstart; (vp < vntd) && (data[vp] != 0); vp++) {
- unsigned char c = data[vp];
+ for(vp = vnstart; (vp < vntd) && (program[vp] != 0); vp++) {
+ unsigned char c = program[vp];
if(c < 0x80) {
fputc(c, f);
} else {
@@ -564,7 +406,7 @@ void check_varname(const unsigned char *name, int line) {
if(c == 0) c = name[0];
/* c now has the last char of the name, make sure it matches the variable type */
- type = data[vvstart + 8 * (line - 1)] >> 6;
+ type = program[vvstart + 8 * (line - 1)] >> 6;
/* type: scalar = 0, array = 1, string = 2 */
if(type == TYPE_SCALAR) {
if(c == '$')
@@ -650,7 +492,7 @@ void apply_var_map(void) {
i = vvstart - vnstart;
adjust_vntable_size(i, newp);
- memmove(data + vnstart, new_vntable, newp);
+ memmove(program + vnstart, new_vntable, newp);
}
void scramble_vars(void) {
@@ -670,9 +512,9 @@ void scramble_vars(void) {
for(i = vnstart; i < vvstart - 1; i++)
if(varname_char == -1)
- data[i] = (rand() >> 8) & 0xff;
+ program[i] = (rand() >> 8) & 0xff;
else
- data[i] = varname_char & 0xff;
+ program[i] = varname_char & 0xff;
if(verbose) {
i -= vnstart;
@@ -705,62 +547,10 @@ void print_help(void) {
fprintf(stderr, "Use - as a filename to read from stdin and/or write to stdout.\n");
}
-void invalid_args(const char *arg) {
- fprintf(stderr, "%s: Invalid argument '%s'.\n\n", self, arg);
- print_help();
- exit(1);
-}
-
-FILE *open_file(const char *name, const char *mode) {
- FILE *fp;
- if(!(fp = fopen(name, mode))) {
- perror(name);
- exit(1);
- }
- return fp;
-}
-
-void open_input(const char *name) {
- if(!name) {
- if(isatty(fileno(stdin))) {
- die("Can't read binary data from the terminal.");
- }
- if(freopen(NULL, "rb", stdin)) {
- input_file = stdin;
- return;
- } else {
- perror("stdin");
- exit(1);
- }
- }
-
- input_file = open_file(name, "rb");
-}
-
-void open_output(const char *name) {
- if(!name || (strcmp(name, "-") == 0)) {
- if(isatty(fileno(stdout))) {
- die("Refusing to write binary data to the terminal.");
- }
- if(freopen(NULL, "wb", stdout)) {
- output_file = stdout;
- return;
- } else {
- perror("stdout");
- exit(1);
- }
- }
-
- output_file = open_file(name, "wb");
-}
-
void parse_args(int argc, char **argv) {
- char *p;
int xopt_used = 0;
- self = *argv;
- p = strrchr(self, '/');
- if(p) self = p + 1;
+ set_self(*argv);
if(argc < 2) {
print_help();
@@ -916,7 +706,7 @@ int main(int argc, char **argv) {
/* we don't open the output file until all processing is done, to
avoid leaving invalid output files if we exit on error. */
open_output(output_filename);
- outbytes = fwrite(data, 1, filelen, output_file);
+ outbytes = fwrite(program, 1, filelen, output_file);
fclose(output_file);
if(verbose) fprintf(stderr, "Wrote %d bytes.\n", outbytes);