#include #include #include #include #include #include #include "bas.h" #include "bcdfp.h" typedef struct { unsigned short lineno; unsigned short pos; unsigned char cmd; } lineref_t; lineref_t *linerefs[32769]; int refcounts[32769]; int lines_exist[32769]; unsigned char last_cmd, on_op; int last_cmd_pos; void add_lineref(unsigned short from, unsigned short pos) { lineref_t *p; int c; unsigned short to; to = fp2int(program + pos); if(to > 32767) return; p = linerefs[to]; c = refcounts[to]; if(c) { p = realloc(p, sizeof(lineref_t) * (c + 1)); } else { p = malloc(sizeof(lineref_t)); } if(!p) die("Out of memory."); linerefs[to] = p; linerefs[to][c].lineno = from; linerefs[to][c].pos = pos; linerefs[to][c].cmd = last_cmd; c++; refcounts[to] = c; } /* makes sure a numeric constant isn't start of an expression. */ int is_standalone_num(unsigned short pos) { if(program[pos] != OP_NUMCONST) return 0; switch(program[pos + 7]) { case OP_EOS: case OP_EOL: case OP_COMMA: return 1; default: return 0; } } CALLBACK(start_stmt) { lines_exist[lineno] = 1; } CALLBACK(got_cmd) { last_cmd = tok; last_cmd_pos = pos; on_op = 0; } void computed_msg(unsigned short lineno) { static int last_lineno = -1; char *cmd; /* avoid duplicate warnings */ if(lineno == last_lineno) return; last_lineno = lineno; switch(last_cmd) { case CMD_GOTO: cmd = "GOTO"; break; case CMD_GO_TO: cmd = "GO TO"; break; case CMD_GOSUB: cmd = "GOSUB"; break; case CMD_RESTORE: cmd = "RESTORE"; break; case CMD_TRAP: cmd = "TRAP"; break; /* case CMD_IF: cmd = "IF/THEN"; break; */ case CMD_ON: if(on_op == OP_GOSUB) cmd = "ON/GOSUB"; else cmd = "ON/GOTO"; break; default: /* should never happen! */ cmd = "???"; break; } fprintf(stderr, "Computed %s at line %d\n", cmd, lineno); } CALLBACK(got_var) { switch(last_cmd) { case CMD_GOTO: case CMD_GO_TO: case CMD_GOSUB: case CMD_RESTORE: case CMD_TRAP: computed_msg(lineno); break; default: break; } } CALLBACK(got_exp) { unsigned char last_tok = program[pos - 1]; int standalone; if(last_cmd == CMD_ON) { if(tok == OP_GOTO || tok == OP_GOSUB) on_op = tok; } if(tok != OP_NUMCONST) return; standalone = is_standalone_num(pos); switch(last_cmd) { case CMD_GOTO: case CMD_GO_TO: case CMD_GOSUB: case CMD_RESTORE: case CMD_TRAP: if((pos == last_cmd_pos + 1) && standalone) { add_lineref(lineno, pos + 1); } else { computed_msg(lineno); } break; case CMD_IF: if(last_tok == OP_THEN) { add_lineref(lineno, pos + 1); } break; case CMD_ON: { switch(last_tok) { case OP_GOTO: case OP_GOSUB: case OP_COMMA: if(standalone) add_lineref(lineno, pos + 1); else computed_msg(lineno); break; default: break; } } break; default: break; } } void build_ref_table(void) { on_start_stmt = start_stmt; on_cmd_token = got_cmd; on_exp_token = got_exp; on_var_token = got_var; walk_all_code(); } void print_ref_table(void) { char c; int i, j; for(i = 0; i < 32768; i++) { if(refcounts[i]) { if(!lines_exist[i]) putchar('!'); printf("%d: ", i); for(j = 0; j < refcounts[i]; j++) { printf("%d:", linerefs[i][j].lineno); switch(linerefs[i][j].cmd) { case CMD_GOTO: c = 'G'; break; case CMD_GO_TO: c = 'G'; break; case CMD_GOSUB: c = 'S'; break; case CMD_RESTORE: c = 'R'; break; case CMD_TRAP: c = 'T'; break; case CMD_IF: c = 'I'; break; case CMD_ON: c = 'O'; break; default: c = '?'; break; } putchar(c); putchar(' '); } putchar('\n'); } } } void print_help(void) { fprintf(stderr, "Usage: %s [-v] program.bas\n", self); exit(0); } void parse_args(int argc, char **argv) { int opt; while( (opt = getopt(argc, argv, "v")) != -1) { switch(opt) { case 'v': verbose = 1; break; default: print_help(); exit(1); } } if(optind >= argc) die("No input file given (use - for stdin)."); else open_input(argv[optind]); } int main(int argc, char **argv) { set_self(*argv); parse_general_args(argc, argv, print_help); parse_args(argc, argv); readfile(); parse_header(); build_ref_table(); print_ref_table(); return 0; }