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