aboutsummaryrefslogtreecommitdiff
path: root/bas.c
blob: 64c4f3f48b3a75fc743d7fa815a0b1a9c99d5c74 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
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;
}