aboutsummaryrefslogtreecommitdiff
path: root/bas.c
diff options
context:
space:
mode:
Diffstat (limited to 'bas.c')
-rw-r--r--bas.c140
1 files changed, 116 insertions, 24 deletions
diff --git a/bas.c b/bas.c
index efb985e..5758110 100644
--- a/bas.c
+++ b/bas.c
@@ -10,7 +10,29 @@
#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 XL token 0x5a is followed by a single "subtoken", this skips it. */
+int bxl_exttok_hack = 0;
+
+/* 2 for OSS Integer BASIC, 6 for the BCD constants in all others. */
+int numconst_size = 6;
+
+/* BASIC/A+ and OSS Integer use the same cmd tokens for REM and DATA that
+ BASIC does, but not for the ERROR- token. bas.c needs to know what
+ token is ERROR- so it can handle it correctly. */
+int error_token = CMD_ERROR;
+
unsigned short lomem;
unsigned short vntp;
unsigned short vntd;
@@ -20,6 +42,8 @@ unsigned short stmcur;
unsigned short starp;
unsigned short codestart;
unsigned short code_end;
+unsigned short save_command_pos;
+unsigned short save_command_tok;
unsigned short vnstart;
unsigned short vvstart;
int filelen;
@@ -60,6 +84,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 +94,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;
}
@@ -87,9 +113,12 @@ 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);
+ fprintf(stderr, "save_command_pos $%04x (tok $%02x)\n", save_command_pos, save_command_tok);
}
void parse_header(void) {
+ int vntp_offset;
+
lomem = getword(0);
vntp = getword(2);
vntd = getword(4);
@@ -97,18 +126,42 @@ 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;
+ save_command_pos = stmcur - TBL_OFFSET;
+ save_command_tok = program[save_command_pos + 4];
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 == 0x77) {
+ numconst_size = 2;
+ if(verbose)
+ fprintf(stderr, "OSS Integer BASIC $7700 signature found, OK.\n");
+ } else if(lomem == 0) {
+ if(verbose)
+ fprintf(stderr, "BASIC $0000 signature found, OK.\n");
+ } else {
+ die("Not an Atari BASIC program (no $0000 or $7700 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 +224,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 +254,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 +352,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,33 +383,55 @@ 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 all */
+ tok == error_token)
+ {
+ 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;
+ pos += numconst_size;
+ 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 += numconst_size;
break;
case OP_STRCONST:
CALL(on_exp_token);