Start stored program work.

This commit is contained in:
Lawrence Kesteloot 2018-08-01 13:34:26 -07:00
parent 491f8ef1b5
commit a57bbe3c65

353
main.c
View File

@ -9,11 +9,13 @@ unsigned char title_length = 9;
#define T_HOME 0x80 #define T_HOME 0x80
#define T_PRINT 0x81 #define T_PRINT 0x81
#define T_LIST 0x82
// List of tokens. The token value is the index plus 0x80. // List of tokens. The token value is the index plus 0x80.
static unsigned char *TOKEN[] = { static unsigned char *TOKEN[] = {
"HOME", "HOME",
"PRINT", "PRINT",
"LIST",
}; };
static int TOKEN_COUNT = sizeof(TOKEN)/sizeof(TOKEN[0]); static int TOKEN_COUNT = sizeof(TOKEN)/sizeof(TOKEN[0]);
@ -28,10 +30,18 @@ unsigned char g_input_buffer[40];
int g_input_buffer_length = 0; int g_input_buffer_length = 0;
// Compiled binary. // Compiled binary.
char g_compiled[128]; unsigned char g_compiled[128];
int g_compiled_length = 0; int g_compiled_length = 0;
void (*g_compiled_function)() = (void (*)()) g_compiled; void (*g_compiled_function)() = (void (*)()) g_compiled;
// Stored program. Each line is:
// - Two bytes for pointer to next line (or zero if none).
// - Two bytes for line number.
// - Program line.
// - Nul.
unsigned char g_program[1024];
unsigned char *g_program_head;
/** /**
* Return the memory location of the cursor. * Return the memory location of the cursor.
*/ */
@ -116,10 +126,147 @@ static void print(unsigned char *s) {
} }
} }
/**
* Print an unsigned integer.
*/
static void print_int(unsigned int i) {
// Is this the best way to do this? I've seen it done backwards, where
// digits are added to a buffer least significant digit first, then reversed,
// but this seems faster.
if (i >= 10000) {
int r = i / 10000;
print_char('0' + r);
i -= r*10000;
}
if (i >= 1000) {
int r = i / 1000;
print_char('0' + r);
i -= r*1000;
}
if (i >= 100) {
int r = i / 100;
print_char('0' + r);
i -= r*100;
}
if (i >= 10) {
int r = i / 10;
print_char('0' + r);
i -= r*10;
}
print_char('0' + i);
}
/**
* Copy a memory buffer. Source and destination must not overlap.
*/
static void memcpy(unsigned char *dest, unsigned char *src, int count) {
while (count-- > 0) {
*dest++ = *src++;
}
}
/**
* Get the length of a nul-terminated string.
*/
static int strlen(unsigned char *s) {
unsigned char *original = s;
while (*s != '\0') {
s += 1;
}
return s - original;
}
/**
* Print the tokenized string, with tokens displayed as their full text.
* Prints a newline at the end.
*/
static void print_detokenized(unsigned char *s) {
while (*s != '\0') {
if (*s >= 0x80) {
print_char(' ');
print(TOKEN[*s - 0x80]);
print_char(' ');
} else {
print_char(*s);
}
s += 1;
}
print_char('\n');
}
/**
* Get the pointer to the next line in the stored program. Returns 0
* if we're at the end.
*/
static unsigned char *get_next_line(unsigned char *line) {
return *((unsigned char **) line);
}
/**
* Get the line number of a stored program line.
*/
static unsigned int get_line_number(unsigned char *line) {
return *((unsigned int *) (line + 2));
}
/**
* Return a pointer to the end of the program. This is one byte PAST the
* last bytes in the program, which are three nuls. If there's no program
* at all, returns the beginning of the program buffer. The "line" parameter is
* an optional starting point, to use as an optimization instead of starting
* from the beginning.
*/
static unsigned char *get_end_of_program(unsigned char *line) {
if (g_program_head == 0) {
// No program.
return g_program;
}
if (line == 0) {
line = g_program_head;
}
while (1) {
unsigned char *next_line = get_next_line(line);
if (next_line == 0) {
// Last line of program.
// Skip the line header (next pointer and line number).
line += 4;
// Skip the line itself and the three nuls.
return line + strlen(line) + 3;
}
line = next_line;
}
}
static void print_statement() { static void print_statement() {
print("Hello world!\n"); print("Hello world!\n");
} }
/**
* List the stored program.
*/
static void list_statement() {
unsigned char *line = g_program_head;
while (line != 0) {
print_int(get_line_number(line));
print_char(' ');
print_detokenized(line + 4);
// Next line.
line = get_next_line(line);
}
}
/** /**
* If a starts with string b, returns the position in a after b. Else returns null. * If a starts with string b, returns the position in a after b. Else returns null.
*/ */
@ -232,21 +379,38 @@ static unsigned int tokenize(unsigned char *s) {
} }
/** /**
* Print the tokenized string, with tokens displayed as their full text. * Find the stored program line with the given line number. If there
* Prints a line number first if it's not 0xFFFF. Prints a newline at the end. * is no stored program, returns null. If the line exists, returns
* a pointer to it. If the line does not exist, returns a pointer to
* the previous line.
*/ */
static void print_detokenized(unsigned int line_number, unsigned char *s) { static unsigned char *find_line(unsigned int line_number) {
while (*s != '\0') { unsigned char *line;
if (*s >= 0x80) {
print(TOKEN[*s - 0x80]);
} else {
print_char(*s);
}
s += 1; if (g_program_head == 0) {
// No program.
return 0;
} }
print_char('\n'); line = g_program_head;
while (1) {
unsigned char *next_line;
// See if we hit it.
if (get_line_number(line) == line_number) {
return line;
}
// See if we're at the end or if the next line is too far.
next_line = get_next_line(line);
if (next_line == 0 || get_line_number(next_line) > line_number) {
return line;
}
// Go to next line.
line = next_line;
}
} }
/** /**
@ -262,85 +426,126 @@ static void process_input_buffer() {
// Tokenize in-place. // Tokenize in-place.
line_number = tokenize(g_input_buffer); line_number = tokenize(g_input_buffer);
if (line_number == 0xFFFF) {
// Immediate mode.
s = g_input_buffer;
s = g_input_buffer; // Compile the line of BASIC.
g_compiled_length = 0;
// Compile the line of BASIC. do {
g_compiled_length = 0; char error = 0;
do { // Default to being done after one statement.
char error = 0; done = 1;
// Default to being done after one statement. if (*s == '\0' || *s == ':') {
done = 1; // Empty statement.
} else if (*s == T_HOME) {
if (*s == '\0' || *s == ':') { s += 1;
// Empty statement. add_call(home);
} else if (*s == T_HOME) { } else if (*s == T_PRINT) {
s += 1;
add_call(home);
} else if (*s == T_PRINT) {
s += 1;
// TODO: Parse expression.
add_call(print_statement);
} else {
error = 1;
}
// Now we're at the end of our statement.
if (!error) {
if (*s == ':') {
// Skip colon.
s += 1; s += 1;
// Next statement. // TODO: Parse expression.
done = 0; add_call(print_statement);
} else if (*s != '\0') { } else if (*s == T_LIST) {
// Junk at the end of the statement. s += 1;
add_call(list_statement);
} else {
error = 1; error = 1;
} }
// Now we're at the end of our statement.
if (!error) {
if (*s == ':') {
// Skip colon.
s += 1;
// Next statement.
done = 0;
} else if (*s != '\0') {
// Junk at the end of the statement.
error = 1;
}
}
if (error) {
add_call(syntax_error);
}
} while (!done);
// Return from function.
add_return();
if (g_compiled_length > sizeof(g_compiled)) {
// TODO: Check while adding bytes, not at the end.
print("\n?Binary length exceeded");
} else {
// Call it.
g_compiled_function();
} }
if (error) {
add_call(syntax_error);
}
} while (!done);
// Return from function.
add_return();
if (g_compiled_length > sizeof(g_compiled)) {
// TODO: Check while adding bytes, not at the end.
print("\n?Binary length exceeded");
} else { } else {
// Call it. unsigned char *line = find_line(line_number);
g_compiled_function();
// Stored mode. Add line to program.
if (line == 0) {
// New program. Just add line.
g_program_head = g_program;
// No next line.
g_program[0] = 0;
g_program[1] = 0;
// Line number.
g_program[2] = line_number & 0xFF;
g_program[3] = line_number >> 8;
// Include the three nuls.
memcpy(g_program + 4, g_input_buffer, strlen(g_input_buffer) + 3);
} else {
// Program exists. Insert or replace line.
if (get_line_number(line) == line_number) {
// Line exists. Replace or delete line.
if (g_input_buffer[0] == '\0') {
// Empty line, delete old one.
} else {
// Replace line.
}
} else {
// Line doesn't exist. Insert or append it.
}
}
} }
} }
int main(void) int main(void)
{ {
int i; int blink;
// Initialize variables.
g_program_head = 0;
// Initialize UI.
home(); home();
/*
// Display the character set. // Display the character set.
for (i = 0; i < 256; i++) { if (0) {
volatile unsigned char *loc; int i;
// Fails with: unhandled instruction B2 for (i = 0; i < 256; i++) {
move_cursor(i % 16, i >> 4); volatile unsigned char *loc;
// Works. // Fails with: unhandled instruction B2
// move_cursor(i & 0x0F, i >> 4); move_cursor(i % 16, i >> 4);
loc = cursor_pos(); // Works.
*loc = i; // move_cursor(i & 0x0F, i >> 4);
loc = cursor_pos();
*loc = i;
}
while(1);
} }
while(1);
*/
// Print title.
// Title.
move_cursor((40 - title_length) / 2, 0); move_cursor((40 - title_length) / 2, 0);
print(title); print(title);
@ -348,19 +553,19 @@ int main(void)
print("\n\n]"); print("\n\n]");
// Keyboard input. // Keyboard input.
i = 0; blink = 0;
g_input_buffer_length = 0; g_input_buffer_length = 0;
show_cursor(); show_cursor();
while(1) { while(1) {
// Blink cursor. // Blink cursor.
i += 1; blink += 1;
if (i == 3000) { if (blink == 3000) {
if (g_showing_cursor) { if (g_showing_cursor) {
hide_cursor(); hide_cursor();
} else { } else {
show_cursor(); show_cursor();
} }
i = 0; blink = 0;
} }
if(keyboard_test()) { if(keyboard_test()) {