aboutsummaryrefslogtreecommitdiff
path: root/bas.c
diff options
context:
space:
mode:
Diffstat (limited to 'bas.c')
-rw-r--r--bas.c183
1 files changed, 183 insertions, 0 deletions
diff --git a/bas.c b/bas.c
new file mode 100644
index 0000000..64c4f3f
--- /dev/null
+++ b/bas.c
@@ -0,0 +1,183 @@
+/* bas.c - API for writing standalone programs that deal with
+ tokenized Atari 8-bit BASIC program. */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+
+#include "bas.h"
+
+int verbose = 0;
+unsigned short lomem;
+unsigned short vntp;
+unsigned short vntd;
+unsigned short vvtp;
+unsigned short stmtab;
+unsigned short stmcur;
+unsigned short starp;
+unsigned short codestart;
+unsigned short code_end;
+unsigned short vnstart;
+unsigned short vvstart;
+int filelen;
+const char *self;
+unsigned char program[BUFSIZE];
+FILE *input_file;
+FILE *output_file;
+
+void die(const char *msg) {
+ fprintf(stderr, "%s: %s\n", self, msg);
+ exit(1);
+}
+
+/* read entire file into memory */
+int readfile(void) {
+ int got = fread(program, 1, BUFSIZE - 1, input_file);
+ if(verbose) fprintf(stderr, "Read %d bytes.\n", got);
+ if(!feof(input_file))
+ fprintf(stderr, "Warning: file is >64KB, way too big for a BASIC program.\n");
+ else if(got > MAX_PROG_SIZE)
+ fprintf(stderr, "Warning: file is %d bytes, suspiciously large for a BASIC program.\n", got);
+ fclose(input_file);
+ if(got < MIN_PROG_SIZE)
+ die("File too short to be a BASIC program (truncated?)\n");
+ return got;
+}
+
+/* get a 16-bit value from the file, in 6502 LSB/MSB order. */
+unsigned short getword(int addr) {
+ return program[addr] | (program[addr + 1] << 8);
+}
+
+void setword(int addr, int value) {
+ program[addr] = value & 0xff;
+ program[addr + 1] = value >> 8;
+}
+
+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);
+}
+
+void parse_header(void) {
+ lomem = getword(0);
+ vntp = getword(2);
+ vntd = getword(4);
+ vvtp = getword(6);
+ 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(filelen < code_end) {
+ fprintf(stderr, "Warning: file is truncated: %d bytes, should be %d.\n", filelen, code_end);
+ }
+
+ if(verbose) dump_header_vars();
+}
+
+void update_header(void) {
+ setword(0, lomem);
+ setword(2, vntp);
+ setword(4, vntd);
+ setword(6, vvtp);
+ setword(8, stmtab);
+ setword(10, stmcur);
+ setword(12, starp);
+}
+
+/* sometimes the variable name table isn't large enough to hold
+ the generated variable names. move_code() makes more space,
+ by moving the rest of the program (including the variable value
+ table) up in memory. */
+void move_code(int offset) {
+ unsigned char *dest = program + vvstart + offset;
+
+ if(dest < program || ((filelen + offset) > (BUFSIZE - 1))) {
+ die("Attempt to move memory out of range; corrupt header bytes?\n");
+ }
+
+ memmove(dest, program + vvstart, filelen);
+
+ vntd += offset;
+ vvtp += offset;
+ stmtab += offset;
+ stmcur += offset;
+ starp += offset;
+ update_header();
+ parse_header();
+ filelen += offset;
+}
+
+void adjust_vntable_size(int oldsize, int newsize) {
+ int move_by;
+ if(oldsize != newsize) {
+ move_by = newsize - oldsize;
+ if(verbose) fprintf(stderr,
+ "Need %d bytes for vntable, have %d, moving VVTP by %d to $%04x.\n",
+ newsize, oldsize, move_by, vvtp + move_by);
+ move_code(move_by);
+ }
+}
+
+void invalid_args(const char *arg) {
+ fprintf(stderr, "%s: Invalid argument '%s'.\n\n", self, arg);
+ exit(1);
+}
+
+FILE *open_file(const char *name, const char *mode) {
+ FILE *fp;
+ if(!(fp = fopen(name, mode))) {
+ perror(name);
+ exit(1);
+ }
+ return fp;
+}
+
+void open_input(const char *name) {
+ if(!name) {
+ if(isatty(fileno(stdin))) {
+ die("Can't read binary data from the terminal.");
+ }
+ if(freopen(NULL, "rb", stdin)) {
+ input_file = stdin;
+ return;
+ } else {
+ perror("stdin");
+ exit(1);
+ }
+ }
+
+ input_file = open_file(name, "rb");
+}
+
+void open_output(const char *name) {
+ if(!name || (strcmp(name, "-") == 0)) {
+ if(isatty(fileno(stdout))) {
+ die("Refusing to write binary data to the terminal.");
+ }
+ if(freopen(NULL, "wb", stdout)) {
+ output_file = stdout;
+ return;
+ } else {
+ perror("stdout");
+ exit(1);
+ }
+ }
+ output_file = open_file(name, "wb");
+}
+
+extern void set_self(const char *argv0) {
+ char *p;
+
+ self = argv0;
+ p = strrchr(self, '/');
+ if(p) self = p + 1;
+}