mirror of
https://github.com/bradgrantham/apple2a.git
synced 2025-01-02 22:33:20 +00:00
Handle forward GOTOs.
This commit is contained in:
parent
d5ce35342e
commit
013056e778
184
main.c
184
main.c
@ -89,6 +89,9 @@ uint8_t title_length = 9;
|
|||||||
// Maximum number of operators in the operator stack.
|
// Maximum number of operators in the operator stack.
|
||||||
#define MAX_OP_STACK 16
|
#define MAX_OP_STACK 16
|
||||||
|
|
||||||
|
// Maximum number of forward GOTOs.
|
||||||
|
#define MAX_FORWARD_GOTO 16
|
||||||
|
|
||||||
// Test for whether a character is a digit.
|
// Test for whether a character is a digit.
|
||||||
#define IS_DIGIT(ch) ((ch) >= '0' && (ch) <= '9')
|
#define IS_DIGIT(ch) ((ch) >= '0' && (ch) <= '9')
|
||||||
|
|
||||||
@ -96,6 +99,28 @@ uint8_t title_length = 9;
|
|||||||
#define IS_FIRST_VARIABLE_LETTER(ch) ((ch) >= 'A' && (ch) <= 'Z')
|
#define IS_FIRST_VARIABLE_LETTER(ch) ((ch) >= 'A' && (ch) <= 'Z')
|
||||||
#define IS_SUBSEQUENT_VARIABLE_LETTER(ch) (IS_FIRST_VARIABLE_LETTER(ch) || IS_DIGIT(ch))
|
#define IS_SUBSEQUENT_VARIABLE_LETTER(ch) (IS_FIRST_VARIABLE_LETTER(ch) || IS_DIGIT(ch))
|
||||||
|
|
||||||
|
// Info for each "forward GOTO", which is a GOTO to a line that we've
|
||||||
|
// not compiled yet.
|
||||||
|
typedef struct {
|
||||||
|
// The line number the GOTO is on. This is for error messages.
|
||||||
|
uint16_t source_line_number;
|
||||||
|
|
||||||
|
// The line number it's trying to jump to.
|
||||||
|
uint16_t target_line_number;
|
||||||
|
|
||||||
|
// The address of the JMP instructions. This is 0 if this entry is unused.
|
||||||
|
uint8_t *jmp_address;
|
||||||
|
} ForwardGoto;
|
||||||
|
|
||||||
|
// Info for each compiled line.
|
||||||
|
typedef struct {
|
||||||
|
// The line's number.
|
||||||
|
uint16_t line_number;
|
||||||
|
|
||||||
|
// The address in memory where its code was compiled.
|
||||||
|
uint8_t *code;
|
||||||
|
} LineInfo;
|
||||||
|
|
||||||
// List of tokens. The token value is the index plus 0x80.
|
// List of tokens. The token value is the index plus 0x80.
|
||||||
static uint8_t *TOKEN[] = {
|
static uint8_t *TOKEN[] = {
|
||||||
"HOME",
|
"HOME",
|
||||||
@ -139,17 +164,20 @@ void (*g_compiled_function)() = (void (*)()) g_compiled;
|
|||||||
// - Nul.
|
// - Nul.
|
||||||
uint8_t g_program[1024];
|
uint8_t g_program[1024];
|
||||||
|
|
||||||
// Address of each line of code when compiled (for GOTO statements).
|
// Info about each compiled line.
|
||||||
// Each line takes two words: one for the line number and
|
LineInfo g_line_info[MAX_LINES];
|
||||||
// one for the address in memory of the compiled code.
|
uint8_t g_line_info_count;
|
||||||
uint16_t g_line_address[MAX_LINES*2];
|
|
||||||
uint16_t g_line_address_count;
|
|
||||||
|
|
||||||
// Operator stack, of the expression-evaluation routines. These are from the
|
// Operator stack, of the expression-evaluation routines. These are from the
|
||||||
// OP_ constants.
|
// OP_ constants.
|
||||||
uint8_t g_op_stack[MAX_OP_STACK];
|
uint8_t g_op_stack[MAX_OP_STACK];
|
||||||
uint8_t g_op_stack_size = 0;
|
uint8_t g_op_stack_size = 0;
|
||||||
|
|
||||||
|
// List of all forward GOTOs. These are packed at the beginning, so the
|
||||||
|
// first invalid (jmp_address == 0) entry marks the end.
|
||||||
|
ForwardGoto g_forward_goto[MAX_FORWARD_GOTO];
|
||||||
|
uint8_t g_forward_goto_count = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Print the tokenized string, with tokens displayed as their full text.
|
* Print the tokenized string, with tokens displayed as their full text.
|
||||||
* Prints a newline at the end.
|
* Prints a newline at the end.
|
||||||
@ -350,18 +378,20 @@ static uint8_t find_variable(uint8_t **buffer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the address of a line in the compiled buffer, or 0xFFFF if not found.
|
* Find the address of a line in the compiled buffer, or 0 if not found.
|
||||||
*/
|
*/
|
||||||
static uint16_t find_line_address(uint16_t line_number) {
|
static uint8_t *find_line_address(uint16_t line_number) {
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < g_line_address_count; i++) {
|
for (i = 0; i < g_line_info_count; i++) {
|
||||||
if (g_line_address[i*2] == line_number) {
|
LineInfo *l = &g_line_info[i];
|
||||||
return g_line_address[i*2 + 1];
|
|
||||||
|
if (l->line_number == line_number) {
|
||||||
|
return l->code;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0xFFFF;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -593,6 +623,7 @@ static uint16_t tokenize(uint8_t *s) {
|
|||||||
int16_t i;
|
int16_t i;
|
||||||
uint8_t *skipped = 0;
|
uint8_t *skipped = 0;
|
||||||
|
|
||||||
|
// Try every token.
|
||||||
for (i = 0; i < TOKEN_COUNT; i++) {
|
for (i = 0; i < TOKEN_COUNT; i++) {
|
||||||
skipped = skip_over(s, TOKEN[i]);
|
skipped = skip_over(s, TOKEN[i]);
|
||||||
if (skipped != 0) {
|
if (skipped != 0) {
|
||||||
@ -636,12 +667,84 @@ static uint8_t *find_line(uint16_t line_number) {
|
|||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new entry to the list for forward GOTOs. Returns whether successful.
|
||||||
|
*/
|
||||||
|
static uint8_t add_forward_goto(uint16_t source_line_number, uint16_t target_line_number,
|
||||||
|
uint8_t *jmp_address) {
|
||||||
|
|
||||||
|
ForwardGoto *f;
|
||||||
|
|
||||||
|
if (g_forward_goto_count == MAX_FORWARD_GOTO) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
f = &g_forward_goto[g_forward_goto_count++];
|
||||||
|
f->source_line_number = source_line_number;
|
||||||
|
f->target_line_number = target_line_number;
|
||||||
|
f->jmp_address = jmp_address;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Go through the list of forward GOTOs and set their jumps to this code.
|
||||||
|
*/
|
||||||
|
static void fix_up_forward_gotos(uint16_t line_number, uint8_t *code) {
|
||||||
|
int i;
|
||||||
|
ForwardGoto *f;
|
||||||
|
uint16_t addr = (uint16_t) code;
|
||||||
|
|
||||||
|
for (i = 0; i < g_forward_goto_count; i++) {
|
||||||
|
f = &g_forward_goto[i];
|
||||||
|
|
||||||
|
if (f->target_line_number == line_number) {
|
||||||
|
// Fill in jump address.
|
||||||
|
f->jmp_address[1] = addr & 0xFF;
|
||||||
|
f->jmp_address[2] = addr >> 8;
|
||||||
|
|
||||||
|
// Swap last entry with this one. It's okay if these
|
||||||
|
// are the same entry.
|
||||||
|
*f = g_forward_goto[g_forward_goto_count - 1];
|
||||||
|
|
||||||
|
// Reduce size of array.
|
||||||
|
g_forward_goto_count -= 1;
|
||||||
|
|
||||||
|
// Re-process this entry, since we've swapped it.
|
||||||
|
i -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an entry to the list of line infos. Returns whether successful.
|
||||||
|
*/
|
||||||
|
static uint8_t add_line_info(uint16_t line_number, uint8_t *code) {
|
||||||
|
LineInfo *l;
|
||||||
|
|
||||||
|
if (g_line_info_count == MAX_LINES) {
|
||||||
|
// TODO not sure what to do here.
|
||||||
|
print("Program too large");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
l = &g_line_info[g_line_info_count++];
|
||||||
|
l->line_number = line_number;
|
||||||
|
l->code = code;
|
||||||
|
|
||||||
|
// Fix up any forward GOTOs to this line.
|
||||||
|
fix_up_forward_gotos(line_number, code);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call to configure the compilation step.
|
* Call to configure the compilation step.
|
||||||
*/
|
*/
|
||||||
static void set_up_compile(void) {
|
static void set_up_compile(void) {
|
||||||
g_compiled_length = 0;
|
g_compiled_length = 0;
|
||||||
g_line_address_count = 0;
|
g_line_info_count = 0;
|
||||||
|
g_forward_goto_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -726,17 +829,21 @@ static void compile_buffer(uint8_t *buffer, uint16_t line_number) {
|
|||||||
error = 1;
|
error = 1;
|
||||||
} else {
|
} else {
|
||||||
uint16_t target_line_number = parse_uint16(&s);
|
uint16_t target_line_number = parse_uint16(&s);
|
||||||
uint16_t addr = find_line_address(target_line_number);
|
uint16_t addr = (uint16_t) find_line_address(target_line_number);
|
||||||
|
|
||||||
if (addr == 0xFFFF) {
|
if (addr == 0) {
|
||||||
// Line not found.
|
// Line not found. Must be a forward GOTO. Record it
|
||||||
// TODO better error message.
|
// and keep going.
|
||||||
error = 1;
|
uint8_t success = add_forward_goto(line_number, target_line_number,
|
||||||
} else {
|
&g_compiled[g_compiled_length]);
|
||||||
g_compiled[g_compiled_length++] = I_JMP_ABS;
|
if (!success) {
|
||||||
g_compiled[g_compiled_length++] = addr & 0xFF;
|
// TODO handle error.
|
||||||
g_compiled[g_compiled_length++] = addr >> 8;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g_compiled[g_compiled_length++] = I_JMP_ABS;
|
||||||
|
g_compiled[g_compiled_length++] = addr & 0xFF;
|
||||||
|
g_compiled[g_compiled_length++] = addr >> 8;
|
||||||
}
|
}
|
||||||
} else if (*s == T_IF) {
|
} else if (*s == T_IF) {
|
||||||
uint16_t saved_compiled_length = g_compiled_length;
|
uint16_t saved_compiled_length = g_compiled_length;
|
||||||
@ -824,6 +931,7 @@ static void compile_buffer(uint8_t *buffer, uint16_t line_number) {
|
|||||||
} else {
|
} else {
|
||||||
add_call(syntax_error);
|
add_call(syntax_error);
|
||||||
}
|
}
|
||||||
|
// Terminate program.
|
||||||
// TODO This won't work after a GOSUB. Maybe we should have our
|
// TODO This won't work after a GOSUB. Maybe we should have our
|
||||||
// own stack for that.
|
// own stack for that.
|
||||||
add_return();
|
add_return();
|
||||||
@ -840,11 +948,30 @@ static void compile_buffer(uint8_t *buffer, uint16_t line_number) {
|
|||||||
* Complete the compilation buffer and run it.
|
* Complete the compilation buffer and run it.
|
||||||
*/
|
*/
|
||||||
static void complete_compile_and_execute(void) {
|
static void complete_compile_and_execute(void) {
|
||||||
|
int i;
|
||||||
|
|
||||||
// Return from function.
|
// Return from function.
|
||||||
add_return();
|
add_return();
|
||||||
|
|
||||||
|
// Forward GOTOs that couldn't be resolved are changed to
|
||||||
|
// jumps to error messages.
|
||||||
|
for (i = 0; i < g_forward_goto_count; i++) {
|
||||||
|
ForwardGoto *f = &g_forward_goto[i];
|
||||||
|
uint16_t addr = (uint16_t) &g_compiled[g_compiled_length];
|
||||||
|
|
||||||
|
// Jump to end of buffer.
|
||||||
|
f->jmp_address[1] = addr & 0xFF;
|
||||||
|
f->jmp_address[2] = addr >> 8;
|
||||||
|
|
||||||
|
// Add code at end of buffer to show error.
|
||||||
|
compile_load_ax(f->source_line_number);
|
||||||
|
add_call(undefined_statement_error);
|
||||||
|
// Terminate program.
|
||||||
|
add_return();
|
||||||
|
}
|
||||||
|
|
||||||
// Dump compiled buffer to the terminal.
|
// Dump compiled buffer to the terminal.
|
||||||
{
|
if (1) {
|
||||||
int i;
|
int i;
|
||||||
uint8_t *debug_port = (uint8_t *) 0xBFFE;
|
uint8_t *debug_port = (uint8_t *) 0xBFFE;
|
||||||
|
|
||||||
@ -894,21 +1021,14 @@ static void compile_stored_program(void) {
|
|||||||
|
|
||||||
while ((next_line = get_next_line(line)) != 0) {
|
while ((next_line = get_next_line(line)) != 0) {
|
||||||
uint16_t line_number = get_line_number(line);
|
uint16_t line_number = get_line_number(line);
|
||||||
|
uint8_t success = add_line_info(line_number, g_compiled + g_compiled_length);
|
||||||
|
|
||||||
// Store address of line in compiled buffer.
|
// Compile just this line.
|
||||||
if (g_line_address_count == MAX_LINES) {
|
|
||||||
// TODO not sure what to do here.
|
|
||||||
print("Program too large");
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
g_line_address[g_line_address_count++] = line_number;
|
|
||||||
g_line_address[g_line_address_count++] = (uint16_t) (g_compiled + g_compiled_length);
|
|
||||||
}
|
|
||||||
|
|
||||||
compile_buffer(line + 4, line_number);
|
compile_buffer(line + 4, line_number);
|
||||||
|
|
||||||
line = next_line;
|
line = next_line;
|
||||||
}
|
}
|
||||||
|
|
||||||
complete_compile_and_execute();
|
complete_compile_and_execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,6 +239,14 @@ void syntax_error_in_line(uint16_t line_number) {
|
|||||||
// No linefeed, assume prompt will do it.
|
// No linefeed, assume prompt will do it.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display an error for a GOTO that went to a line that doesn't exist.
|
||||||
|
*/
|
||||||
|
void undefined_statement_error(uint16_t line_number) {
|
||||||
|
print("\n?UNDEF'D STATEMENT ERROR IN ");
|
||||||
|
print_int(line_number);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Switch to graphics mode.
|
* Switch to graphics mode.
|
||||||
*/
|
*/
|
||||||
|
@ -35,6 +35,7 @@ void print_newline(void);
|
|||||||
|
|
||||||
void syntax_error(void);
|
void syntax_error(void);
|
||||||
void syntax_error_in_line(uint16_t line_number);
|
void syntax_error_in_line(uint16_t line_number);
|
||||||
|
void undefined_statement_error(uint16_t line_number);
|
||||||
|
|
||||||
void gr_statement(void);
|
void gr_statement(void);
|
||||||
void text_statement(void);
|
void text_statement(void);
|
||||||
|
Loading…
Reference in New Issue
Block a user