diff options
Diffstat (limited to 'bas.c')
-rw-r--r-- | bas.c | 122 |
1 files changed, 99 insertions, 23 deletions
@@ -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++; |