aboutsummaryrefslogtreecommitdiff
path: root/bas.c
diff options
context:
space:
mode:
Diffstat (limited to 'bas.c')
-rw-r--r--bas.c122
1 files changed, 99 insertions, 23 deletions
diff --git a/bas.c b/bas.c
index efb985e..e5df296 100644
--- a/bas.c
+++ b/bas.c
@@ -10,7 +10,26 @@
#include "bas.h"
+/* caller's parse_args() should set this for the -v option */
int verbose = 0;
+
+/* hex constants work the same way in Turbo BASIC and BASIC XL/XE: they're
+ exactly like numeric constants, 6-byte BCD FP, but introduced with
+ OP_HEXCONST instead of OP_NUMCONST.
+ If allow_hex_const is set, on_exp_token will be called for OP_HEXCONST.
+ Otherwise, a warning will be printed, on_exp_token will NOT be called,
+ but the constant is skipped the same as a regular numeric, to keep
+ in sync with the token stream. */
+int allow_hex_const = 0;
+
+/* BASIC/A+ uses the same cmd tokens for REM and DATA that BASIC does,
+ but not for the ERROR- token. Unfortunately bas.c needs to know it's
+ an A+ program so it can handle this token correctly. */
+int aplus_errtok_hack = 0;
+
+/* BASIC XL token 0x5a is followed by a single "subtoken", this skips it. */
+int bxl_exttok_hack = 0;
+
unsigned short lomem;
unsigned short vntp;
unsigned short vntd;
@@ -60,6 +79,7 @@ void readfile(void) {
else if(filelen > MAX_PROG_SIZE)
fprintf(stderr, "Warning: file is %d bytes, suspiciously large for a BASIC program.\n", filelen);
fclose(input_file);
+ input_file = NULL; /* so caller can tell it's closed */
if(filelen < MIN_PROG_SIZE)
die("File too short to be a BASIC program (truncated?)\n");
}
@@ -69,6 +89,7 @@ int writefile(void) {
outbytes = fwrite(program, 1, filelen, output_file);
fclose(output_file);
+ output_file = NULL; /* so caller can tell it's closed */
if(verbose) fprintf(stderr, "Wrote %d bytes.\n", outbytes);
return outbytes;
}
@@ -90,6 +111,8 @@ void dump_header_vars(void) {
}
void parse_header(void) {
+ int vntp_offset;
+
lomem = getword(0);
vntp = getword(2);
vntd = getword(4);
@@ -97,18 +120,31 @@ void parse_header(void) {
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(lomem) die("This doesn't look like an Atari BASIC program (no $0000 signature).");
+ if(vnstart > 0x0e) {
+ if(verbose)
+ fprintf(stderr, "VNTP is $%02x (not ($100), adjusting pointers.\n", vntp);
+ vntp_offset = vnstart - 0x0e;
+ vnstart -= vntp_offset;
+ vvstart -= vntp_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();
+
+ /* these checks are actually kind of conservative. */
+ if(lomem) die("Not an Atari BASIC program (no $0000 signature).");
+ if(vntp < 0x100) die("Not an Atari BASIC program (invalid VNTP).");
+ if(vvtp < vntd) die("Not an Atari BASIC program (invalid VVTP).");
+ if(starp < vvtp) die("Not an Atari BASIC program (invalid STARP).");
}
void update_header(void) {
@@ -171,23 +207,28 @@ int vntable_ok(void) {
/* first pass: bad = 1 if all the bytes in the table have the same
value, no matter what it is. */
vp = vnstart + 1;
- bad = 1;
- while(vp < vvstart - 1) {
- if(program[vp] != program[vnstart]) {
- bad = 0;
- break;
+
+ /* don't do this check if the table is only one byte long! */
+ if(vp < vvstart - 1) {
+ bad = 1;
+ while(vp < vvstart - 1) {
+ if(program[vp] != program[vnstart]) {
+ bad = 0;
+ break;
+ }
+ vp++;
}
- vp++;
+ if(bad) return 0;
}
- if(bad) return 0;
/* 2nd pass: bad = 1 if there's any invalid character in the table. */
vp = vnstart;
while(vp < vvstart) {
unsigned char c = program[vp];
- /* treat a null byte as end-of-table, ignore any junk between it and VNTP. */
- if(c == 0) break;
+ /* allow for (but don't require) dummy byte at VNTD. used to just
+ quit when we hit 0, but 0 might be part of a scrambled table. */
+ if(c == 0 && vp == (vvstart - 1)) break;
vp++;
@@ -196,6 +237,10 @@ int vntable_ok(void) {
/* numbers and letters are allowed, inverse or normal. */
c &= 0x7f;
+ if(c == '_') {
+ fprintf(stderr, "%s: Underscore in variable name; Turbo or BASIC XE?\n", self);
+ continue;
+ }
if(c >= 0x30 && c <= 0x39) continue;
if(c >= 0x41 && c <= 0x5a) continue;
@@ -290,14 +335,22 @@ void walk_code(unsigned int startlineno, unsigned int endlineno) {
lineno = tmpno;
offset = program[linepos + 2];
nextpos = linepos + offset;
+ if(nextpos > filelen) {
+ fprintf(stderr, "Warning: program truncated in the middle of line %d.\n", lineno);
+ return;
+ }
end = nextpos;
pos = linepos;
- if(offset < 6) {
+ if(offset < 5) {
+ /* actually, real Atari BASIC's minimum offset is 6. however, if you use
+ the "--" (line of dashes, command token 0x54) in Turbo BASIC XL, you
+ get an offset of 5, because there's no end-of-line after it.
+ it seems better to accomodate Turbo here. */
CALL(on_bad_line_length);
offset = program[linepos + 2]; /* on_bad_line_length fixed it (we hope) */
- if(offset < 6)
+ if(offset < 5)
die("Fatal: Program is code-protected; unprotect it first.");
}
@@ -313,34 +366,57 @@ void walk_code(unsigned int startlineno, unsigned int endlineno) {
pos = linepos + 3;
while(pos < nextpos) { /* loop over statements within a line */
soffset = program[pos];
+ if(!soffset) {
+ fprintf(stderr, "Fatal: next-statement offset is 0 at line %d, pos %04x\n", lineno, pos);
+ exit(1);
+ }
end = linepos + soffset;
CALL(on_start_stmt);
while(pos < end) { /* loop over tokens within a statement */
pos++;
CALL(on_cmd_token);
- switch(program[pos]) {
- case CMD_REM:
- case CMD_DATA:
- case CMD_ERROR:
- pos++;
- CALL(on_text);
- pos = end;
- break;
- default:
- pos++;
- break;
+
+ tok = program[pos];
+ if((tok == CMD_REM) || (tok == CMD_DATA) || /* same in A+ */
+ (aplus_errtok_hack && tok == 0x53) || /* A+'s ERROR- */
+ (!aplus_errtok_hack && tok == CMD_ERROR))
+ {
+ pos++;
+ CALL(on_text);
+ pos = end;
+ } else if(bxl_exttok_hack && tok == 0x5a) {
+ pos += 2; /* skip subtoken */
+ } else {
+ pos++;
}
while(pos < end) { /* loop over operators */
tok = program[pos];
switch(tok) {
+ case 0: /* Turbo variables numbered >= $80 */
+ CALL(on_exp_token);
+ /* on_exp_token callback better know what to do with $00! */
+ pos += 2;
+ break;
case OP_NUMCONST:
CALL(on_exp_token);
pos++;
CALL(on_num_const);
pos += 6;
break;
+ case OP_HEXCONST:
+ if(allow_hex_const) {
+ CALL(on_exp_token);
+ } else {
+ fprintf(stderr, "%s: found Turbo/BXL/BXE hex constant at line %d, skipping\n", self, lineno);
+ }
+ pos++;
+ if(allow_hex_const) {
+ CALL(on_num_const);
+ }
+ pos += 6;
+ break;
case OP_STRCONST:
CALL(on_exp_token);
pos++;