diff --git a/error.cpp b/error.cpp new file mode 100644 index 0000000..df03bd2 --- /dev/null +++ b/error.cpp @@ -0,0 +1,18 @@ +#include "error.h" +#include +#include "tokenizer.h" + +static char errormsg[255]; +static bool raised = false; + + +void error_raise(const char* msg) { + raised = true; + sprintf(errormsg, "ERROR: %s at %d:%d.", msg, tkn_get_line()+1, tkn_get_row()+1); +} + +bool error_raised() { return raised; } + +void error_print() { + printf("%s", errormsg); +} diff --git a/error.h b/error.h new file mode 100644 index 0000000..727c622 --- /dev/null +++ b/error.h @@ -0,0 +1,5 @@ +#pragma once + +void error_raise(const char* msg); +bool error_raised(); +void error_print(); diff --git a/main.cpp b/main.cpp index 6949319..ac6f038 100644 --- a/main.cpp +++ b/main.cpp @@ -7,7 +7,7 @@ int main(int argc, char** argv) { - vm_init("test.bin"); + vm_init("test.bas"); vdp_init(); vm_register_out_port(10, vdp_data_out); vm_register_out_port(11, vdp_cmd_out); diff --git a/parser.cpp b/parser.cpp new file mode 100644 index 0000000..bc3b5a6 --- /dev/null +++ b/parser.cpp @@ -0,0 +1,793 @@ +#include "parser.h" +#include "tokenizer.h" +#include "error.h" +#include +#include +#include + +#define MAX_CODE_SIZE 65536 +#define MAX_LABELS 256 +#define MAX_EXTERNAL_FUNCTIONS 256 +#define MAX_IDENTIFIER_LENGTH 40 + +struct t_label { + char name[MAX_IDENTIFIER_LENGTH]; + word address; +}; + +struct t_variable { + char name[MAX_IDENTIFIER_LENGTH]; + int index = 0; + int size = 1; + t_variable* next{ nullptr }; +}; + +struct t_external_function { + char name[MAX_IDENTIFIER_LENGTH]; + int num_parameters = 0; +}; + +struct t_string { + char string[256]; + word references[256]; + int num_references = 1; +}; + +static byte* code; // [MAX_CODE_SIZE]; +static word codepos = 0; + +static t_label known_labels[MAX_LABELS]; +static int num_known_labels = 0; +static t_label unknown_labels[MAX_LABELS]; +static int num_unknown_labels = 0; +static int num_anonymous_labels = 0; + +static char* breaklabel = nullptr; +static char* contlabel = nullptr; + +static t_variable* variables = nullptr; +static int num_variables = 0; +static bool variable_is_array = false; + +static t_external_function external_functions[MAX_EXTERNAL_FUNCTIONS]; +static int num_external_functions = 0; + +static t_string strings[256]; +static int num_strings = 0; + +static bool parser_finished = false; + +/****************************************************************************************/ +/* GENERIC FUNCTIONS */ +/****************************************************************************************/ + +static void int_to_string(int value, char* label) { + for (int i = 0; i < 7; i++) label[i] = '0'; + label[7] = 0; + int i = 6; + while (value > 0) { + label[i] = 48 + (value % 10); + value = value / 10; + } +} + +/****************************************************************************************/ +/* BYTECODE GENERATOR */ +/****************************************************************************************/ + +static void emmit(const byte value) { + code[codepos++] = value; +} + +static void emmit_w(const word value) { + code[codepos++] = value & 255; + code[codepos++] = value >> 8; +} + +static void patch(const word address, const word dest) { + code[address] = dest & 255; + code[address + 1] = dest >> 8; +} + +static const word get_current_address() { + return codepos; +} + +/****************************************************************************************/ +/* VARIABLE MANAGEMENT */ +/****************************************************************************************/ + +static int get_variable_index(const char* string, const int array_size = 1) { + t_variable* variable = variables; + t_variable* last = variables; + while (variable != nullptr) { + if (strcmp(variable->name, string) == 0) { variable_is_array = (variable->size > 1); return variable->index; } + last = variable; + variable = variable->next; + } + t_variable* newvar = (t_variable*)malloc(sizeof(t_variable)); + //newvar->name = (char*)malloc(strlen(string) + 1); + strcpy(newvar->name, string); + newvar->index = num_variables; + num_variables += array_size; + newvar->size = array_size; + variable_is_array = (newvar->size > 1); + newvar->next = nullptr; + if (last != nullptr) { + last->next = newvar; + } + else { + variables = newvar; + } + return newvar->index; +} + +static bool is_variable_array() { return variable_is_array; } + +/****************************************************************************************/ +/* LABEL MANAGEMENT */ +/****************************************************************************************/ + +static const word get_label_address(const char* string) { + for (int i = 0; i < num_known_labels; i++) { if (strcmp(known_labels[i].name, string) == 0) return known_labels[i].address; } + //unknown_labels[num_unknown_labels].name = (char*)malloc(strlen(string) + 1); + strcpy(unknown_labels[num_unknown_labels].name, string); + unknown_labels[num_unknown_labels].address = get_current_address(); + num_unknown_labels++; + return 0; +} + +static void register_label_address(const char* string) { + // If the label already exists, vomit an error + for (int i = 0; i < num_known_labels; i++) { + if (strcmp(known_labels[i].name, string) == 0) { + parser_finished = true; + error_raise("Duplicate label"); + return; + } + } + // register new label + //known_labels[num_known_labels].name = (char*)malloc(strlen(string) + 1); + strcpy(known_labels[num_known_labels].name, string); + known_labels[num_known_labels++].address = get_current_address(); + // patch and remove any references awaiting for this label + int i = 0; + while (i < num_unknown_labels) { + if (strcmp(unknown_labels[i].name, string) == 0) { + patch(unknown_labels[i].address, get_current_address()); + if (i < num_unknown_labels - 1) { + //free(unknown_labels[i].name); + unknown_labels[i].address = unknown_labels[num_unknown_labels - 1].address; + strcpy(unknown_labels[i].name, unknown_labels[num_unknown_labels - 1].name); + //unknown_labels[i].name = unknown_labels[num_unknown_labels - 1].name; + } + num_unknown_labels--; + } + else { + i++; + } + } + num_known_labels++; +} + +static void generate_anonymous_labelname(char* dest) { + int_to_string(num_anonymous_labels++, dest); +} + +/****************************************************************************************/ +/* STRING MANAGEMENT */ +/****************************************************************************************/ + +static int register_string(const char* string) { + for (int i = 0; i < num_strings; i++) { + if (strcmp(strings[i].string, string) == 0) { strings[i].references[strings[i].num_references++] = get_current_address(); return 0; } + } + strcpy(strings[num_strings].string, string); + strings[num_strings++].references[0] = get_current_address(); + return 0; +} + +static void append_strings() { + code[0] = codepos & 255; + code[1] = codepos >> 8; + emmit(num_strings); + for (int i = 0; i < num_strings; i++) { + for (int j = 0; j < strings[i].num_references; j++) { patch(strings[i].references[j], get_current_address()); } + char len = strlen(strings[i].string); + emmit(len); + for (int j = 0; j < len; j++) { emmit(strings[i].string[j]); } + } +} + +/****************************************************************************************/ +/* PARSER */ +/****************************************************************************************/ + +static void parse_expression(); +static void parse_statements(); + +#define EXPECT(X, Y) if (tkn_get_token() != X) { parser_finished = true; error_raise(Y); return; } + +static void parse_concat_atom() { + if (tkn_get_token() == TOKEN_STRING) { + emmit(OP_SETX); + emmit_w(register_string(tkn_get_string())); + emmit(OP_JSR); + emmit_w(get_label_address("_sys_string_load")); + tkn_next(); + return; + } + if (tkn_get_token() == TOKEN_IDENTIFIER) { + int ivar = get_variable_index(tkn_get_string()); + if (!is_variable_array()) { parser_finished = true; error_raise("Expected string constant or variable"); return; } + emmit(OP_SETX); + emmit_w(ivar); + emmit(OP_JSR); + emmit_w(get_label_address("_sys_string_load")); + tkn_next(); + return; + } + parser_finished = true; error_raise("Syntax error"); + return; +} + +static void parse_concatenation() { + parse_concat_atom(); + while (tkn_get_token() == TOKEN_COMMA) { + tkn_next(); parse_concat_atom(); + emmit(OP_CONCAT); + } +} + +static void parse_expr_atom() { + // NUM, VAR, UNARY, PAREN + if (tkn_get_token() == TOKEN_NUMBER) { + emmit(OP_PUSH); + emmit(tkn_get_value()); + tkn_next(); + return; + } + if (tkn_get_token() == TOKEN_IDENTIFIER) { + int ivar = get_variable_index(tkn_get_string()); + if (is_variable_array()) { + tkn_next(); EXPECT(TOKEN_LPAR, "Expected array index"); + tkn_next(); parse_expr_atom(); + EXPECT(TOKEN_RPAR, "Expected ')'"); + emmit(OP_SETY); + emmit(OP_LOADI); + emmit_w(ivar); + } else { + emmit(OP_LOAD); + emmit_w(ivar); + } + tkn_next(); + return; + } + if (tkn_get_token() == TOKEN_MINUS) { + tkn_next(); + parse_expr_atom(); + emmit(OP_NEG); + return; + } + if (tkn_get_token() == TOKEN_NOT) { + tkn_next(); + parse_expr_atom(); + emmit(OP_NOT); + return; + } + if (tkn_get_token() == TOKEN_LPAR) { + tkn_next(); + parse_expression(); + EXPECT(TOKEN_RPAR, "Expected ')'"); + tkn_next(); + return; + } + parser_finished = true; error_raise("Syntax error"); + return; +} + +static void parse_expr_mul() { + parse_expr_atom(); + while (tkn_get_token() == TOKEN_ASTERISC || tkn_get_token() == TOKEN_SLASH || tkn_get_token() == TOKEN_MOD) { + t_tokentype operat = tkn_get_token(); + tkn_next(); + parse_expr_atom(); + switch (operat) { + case TOKEN_ASTERISC: emmit(OP_MUL); break; + case TOKEN_SLASH: emmit(OP_DIV); break; + case TOKEN_MOD: emmit(OP_MOD); break; + } + } +} +static void parse_expr_sum() { + parse_expr_mul(); + while (tkn_get_token() == TOKEN_PLUS || tkn_get_token() == TOKEN_MINUS) { + t_tokentype operat = tkn_get_token(); + tkn_next(); + parse_expr_mul(); + switch (operat) { + case TOKEN_PLUS: emmit(OP_ADD); break; + case TOKEN_MINUS: emmit(OP_SUB); break; + } + } +} +static void parse_expr_bool() { + parse_expr_sum(); + while (tkn_get_token() == TOKEN_AND || tkn_get_token() == TOKEN_OR) { + t_tokentype operat = tkn_get_token(); + tkn_next(); + parse_expr_sum(); + switch (operat) { + case TOKEN_AND: emmit(OP_AND); break; + case TOKEN_OR: emmit(OP_OR); break; + } + } +} +static void parse_expression() { + parse_expr_bool(); + while (tkn_get_token() == TOKEN_EQ || tkn_get_token() == TOKEN_LT || tkn_get_token() == TOKEN_GT || tkn_get_token() == TOKEN_LEQ || tkn_get_token() == TOKEN_GEQ) { + t_tokentype operat = tkn_get_token(); + tkn_next(); + parse_expr_bool(); + switch (operat) { + case TOKEN_EQ: emmit(OP_EQ); break; + case TOKEN_LT: emmit(OP_LT); break; + case TOKEN_GT: emmit(OP_GT); break; + case TOKEN_LEQ: emmit(OP_LEQ); break; + case TOKEN_GEQ: emmit(OP_GEQ); break; + } + } +} + +static void parse_dim() { + EXPECT(TOKEN_IDENTIFIER, "Expected variable"); + char varname[40]; + strcpy(varname, tkn_get_string()); + tkn_next(); EXPECT(TOKEN_LPAR, "Expected array size"); + tkn_next(); EXPECT(TOKEN_NUMBER, "Expected integer constant as array size"); + int ivar = get_variable_index(varname, tkn_get_value()); + tkn_next(); EXPECT(TOKEN_RPAR, "Expected ')'"); + tkn_next(); +} + +static void parse_inc() { + EXPECT(TOKEN_IDENTIFIER, "Expected variable"); + int ivar = get_variable_index(tkn_get_string()); + if (is_variable_array()) { + tkn_next(); EXPECT(TOKEN_LPAR, "Expected array index"); + tkn_next(); parse_expr_atom(); + EXPECT(TOKEN_RPAR, "Expected ')'"); + emmit(OP_SETY); + emmit(OP_LOADI); + emmit_w(ivar); + emmit(OP_INC); + emmit(OP_STOREI); + emmit_w(ivar); + } else { + emmit(OP_LOAD); + emmit_w(ivar); + emmit(OP_INC); + emmit(OP_STORE); + emmit_w(ivar); + } + tkn_next(); +} + +static void parse_dec() { + EXPECT(TOKEN_IDENTIFIER, "Expected variable"); + int ivar = get_variable_index(tkn_get_string()); + if (is_variable_array()) { + tkn_next(); EXPECT(TOKEN_LPAR, "Expected array index"); + tkn_next(); parse_expr_atom(); + EXPECT(TOKEN_RPAR, "Expected ')'"); + emmit(OP_SETY); + emmit(OP_LOADI); + emmit_w(ivar); + emmit(OP_DEC); + emmit(OP_STOREI); + emmit_w(ivar); + } + else { + emmit(OP_LOAD); + emmit_w(ivar); + emmit(OP_DEC); + emmit(OP_STORE); + emmit_w(ivar); + } + tkn_next(); +} + +static void parse_let() { + EXPECT(TOKEN_IDENTIFIER, "Expected variable"); + int ivar = get_variable_index(tkn_get_string()); + if (is_variable_array()) { + tkn_next(); + if (tkn_get_token() == TOKEN_COLON) { + tkn_next(); parse_concatenation(); + emmit(OP_SETX); + emmit_w(ivar); + emmit(OP_JSR); + emmit_w(get_label_address("_sys_string_store")); + return; + } else { + EXPECT(TOKEN_LPAR, "Expected ':' or '('"); + tkn_next(); parse_expr_atom(); + EXPECT(TOKEN_RPAR, "Expected ')'"); + emmit(OP_SETY); + } + } + tkn_next(); + EXPECT(TOKEN_EQ, "Expected '='"); + tkn_next(); parse_expression(); + if (is_variable_array()) { + emmit(OP_STOREI); + emmit_w(ivar); + } else { + emmit(OP_STORE); + emmit_w(ivar); + } +} + +static void parse_if() { + char* previous_breaklabel = breaklabel; + parse_expression(); + EXPECT(TOKEN_THEN, "Expected 'THEN'"); + tkn_next(); + emmit(OP_JNT); + char elselabel[8]; + generate_anonymous_labelname(elselabel); + char endlabel[8]; + generate_anonymous_labelname(endlabel); + breaklabel = endlabel; + bool else_visited = false; + emmit_w(get_label_address(elselabel)); + parse_statements(); + if (tkn_get_token() == TOKEN_ELSE) { + else_visited = true; + emmit(OP_JMP); + emmit_w(get_label_address(endlabel)); + register_label_address(elselabel); + tkn_next(); + parse_statements(); + } + if (tkn_get_token() == TOKEN_END) { + if (!else_visited) register_label_address(elselabel); + register_label_address(endlabel); + tkn_next(); + breaklabel = previous_breaklabel; + return; + } + parser_finished = true; + if (!else_visited) error_raise("Expected 'ELSE', 'END' or a statement"); else error_raise("Expected 'END' or a statement"); +} + +static void parse_for() { + char* previous_breaklabel = breaklabel; + char* previous_contlabel = contlabel; + EXPECT(TOKEN_IDENTIFIER, "Expected variable"); + int ivar = get_variable_index(tkn_get_string()); + tkn_next(); + EXPECT(TOKEN_EQ, "Expected '='"); + tkn_next(); + parse_expression(); + emmit(OP_STORE); + emmit_w(ivar); + char forlabel[8]; + generate_anonymous_labelname(forlabel); + char endlabel[8]; + generate_anonymous_labelname(endlabel); + char continuelabel[8]; + generate_anonymous_labelname(continuelabel); + breaklabel = endlabel; + contlabel = continuelabel; + register_label_address(forlabel); + EXPECT(TOKEN_TO, "Expected 'TO'"); + tkn_next(); + parse_expression(); + emmit(OP_LOAD); + emmit_w(ivar); + emmit(OP_EQ); + emmit(OP_JTR); + emmit_w(get_label_address(endlabel)); + //int endLabel = GetCurrentAddress(); + parse_statements(); + EXPECT(TOKEN_END, "Expected 'END'"); + tkn_next(); + register_label_address(continuelabel); + emmit(OP_LOAD); + emmit_w(ivar); + emmit(OP_INC); + emmit(OP_STORE); + emmit_w(ivar); + emmit(OP_JMP); + emmit_w(get_label_address(forlabel)); + register_label_address(endlabel); + //Patch(endLabel, GetCurrentAddress()); + breaklabel = previous_breaklabel; + contlabel = previous_contlabel; +} + +static void parse_break() { + if (breaklabel == nullptr) { parser_finished = true; error_raise("Can't break outside of a loop or condition"); return; } + emmit(OP_JMP); + emmit_w(get_label_address(breaklabel)); +} + +static void parse_continue() { + if (contlabel == nullptr) { parser_finished = true; error_raise("Can't continue outside of a loop"); return; } + emmit(OP_JMP); + emmit_w(get_label_address(contlabel)); +} + +static void parse_goto() { + emmit(OP_JMP); + EXPECT(TOKEN_IDENTIFIER, "Expected label name"); + emmit_w(get_label_address(tkn_get_string())); + tkn_next(); +} + +static void parse_gosub() { + emmit(OP_JSR); + EXPECT(TOKEN_IDENTIFIER, "Expected label name"); + emmit_w(get_label_address(tkn_get_string())); + tkn_next(); +} + +static void parse_return() { + emmit(OP_RET); +} + +static void parse_label() { + register_label_address(tkn_get_string()); + tkn_next(); +} + +static void parse_call() { + for (int i = 0; i < num_external_functions; i++) { + if (strcmp(external_functions[i].name, tkn_get_string()) == 0) { + if (external_functions[i].num_parameters == -1) { + tkn_next(); + EXPECT(TOKEN_STRING, "Expected string"); + emmit(OP_PUSH); + emmit(register_string(tkn_get_string())); + } else { + tkn_next(); + for (int j = 0; j < external_functions[i].num_parameters; j++) { + //if (j != 0) EXPECT(TOKEN_COMMA, "Expected comma"); + parse_expression(); + } + } + emmit(OP_CALL); + emmit(i); + } + } +} + +static void parse_strleft() { + EXPECT(TOKEN_IDENTIFIER, "Expected variable"); + int ivar = get_variable_index(tkn_get_string()); + tkn_next(); parse_expression(); + emmit(OP_SETX); + emmit_w(ivar); + emmit(OP_JSR); + emmit_w(get_label_address("_sys_string_left")); +} + +static void parse_strlen() { + EXPECT(TOKEN_IDENTIFIER, "Expected variable"); + int ivar = get_variable_index(tkn_get_string()); + emmit(OP_LOAD); + emmit_w(ivar); + tkn_next(); +} + +static void parse_debug() { + parse_expression(); + emmit(OP_JSR); + emmit_w(get_label_address("_sys_string_debug")); +} + +static void parse_chr() { + parse_expression(); + emmit(OP_PUSH); + emmit(1); +} + +static void parse_str() { + parse_expression(); + emmit(OP_JSR); + emmit_w(get_label_address("_sys_string_str")); +} + +static void parse_setsprite() { + parse_expression(); + parse_expression(); + parse_expression(); + emmit(OP_JSR); + emmit_w(get_label_address("_sys_vdp_setsprite")); +} + +static void parse_putsprite() { + parse_expression(); + parse_expression(); + parse_expression(); + emmit(OP_JSR); + emmit_w(get_label_address("_sys_vdp_putsprite")); +} + +static void parse_locate() { + parse_expression(); + parse_expression(); + emmit(OP_JSR); + emmit_w(get_label_address("_sys_vdp_locate")); +} + +static void parse_print() { + parse_concatenation(); + emmit(OP_JSR); + emmit_w(get_label_address("_sys_vdp_print")); +} + +static void parse_putchar() { + parse_expression(); + parse_expression(); + parse_expression(); + parse_expression(); + emmit(OP_JSR); + emmit_w(get_label_address("_sys_vdp_putchar")); +} + +static void parse_setchar() { + parse_expression(); + parse_concatenation(); + emmit(OP_JSR); + emmit_w(get_label_address("_sys_vdp_setchar")); +} + +static void parse_keypressed() { + parse_expression(); + emmit(OP_JSR); + emmit_w(get_label_address("_sys_input_keypressed")); +} + +static void parse_anykey() { + emmit(OP_PUSH); + emmit(0); + emmit(OP_JSR); + emmit_w(get_label_address("_sys_input_keypressed")); +} + +static void parse_updatescr() { + emmit(OP_JSR); + emmit_w(get_label_address("_sys_vdp_flip")); +} + +static void parse_color() { + parse_expression(); + emmit(OP_JSR); + emmit_w(get_label_address("_sys_vdp_color")); +} + +static void parse_border() { + parse_expression(); + emmit(OP_JSR); + emmit_w(get_label_address("_sys_vdp_border")); +} + +static void parse_statements() { + while (!parser_finished) { + switch (tkn_get_token()) { + case TOKEN_DIM: + tkn_next(); parse_dim(); break; + case TOKEN_LET: + tkn_next(); parse_let(); break; + case TOKEN_INC: + tkn_next(); parse_inc(); break; + case TOKEN_DEC: + tkn_next(); parse_dec(); break; + case TOKEN_IF: + tkn_next(); parse_if(); break; + case TOKEN_FOR: + tkn_next(); parse_for(); break; + case TOKEN_BREAK: + tkn_next(); parse_break(); break; + case TOKEN_CONTINUE: + tkn_next(); parse_continue(); break; + case TOKEN_GOTO: + tkn_next(); parse_goto(); break; + case TOKEN_GOSUB: + tkn_next(); parse_gosub(); break; + case TOKEN_RETURN: + tkn_next(); parse_return(); break; + case TOKEN_LABEL: + parse_label(); break; + case TOKEN_REM: + tkn_next(); break; + + case TOKEN_STRLEFT: + tkn_next(); parse_strleft(); break; + case TOKEN_STRLEN: + tkn_next(); parse_strlen(); break; + case TOKEN_DEBUG: + tkn_next(); parse_debug(); break; + case TOKEN_CHR: + tkn_next(); parse_chr(); break; + case TOKEN_STR: + tkn_next(); parse_str(); break; + case TOKEN_SETSPRITE: + tkn_next(); parse_setsprite(); break; + case TOKEN_PUTSPRITE: + tkn_next(); parse_putsprite(); break; + case TOKEN_LOCATE: + tkn_next(); parse_locate(); break; + case TOKEN_PRINT: + tkn_next(); parse_print(); break; + case TOKEN_PUTCHAR: + tkn_next(); parse_putchar(); break; + case TOKEN_SETCHAR: + tkn_next(); parse_setchar(); break; + case TOKEN_KEYPRESSED: + tkn_next(); parse_keypressed(); break; + case TOKEN_ANYKEY: + tkn_next(); parse_anykey(); break; + case TOKEN_UPDATESCR: + tkn_next(); parse_updatescr(); break; + case TOKEN_COLOR: + tkn_next(); parse_color(); break; + case TOKEN_BORDER: + tkn_next(); parse_border(); break; + + case TOKEN_IDENTIFIER: + for (int i = 0; i < num_external_functions; i++) { + if (strcmp(external_functions[i].name, tkn_get_string()) == 0) { parse_call(); break; } + } + parse_let(); break; + default: + return; + } + } +} + +static void include_labels() { + FILE *f = fopen("rom.lbl", "rb"); + byte num_labels; + fread(&num_labels, 1, 1, f); + for (int i = 0; i < num_labels; i++) { + word address; fread(&address, 2, 1, f); + byte len; fread(&len, 1, 1, f); + char label[50]; + fread(label, len, 1, f); + label[len] = 0; + strcpy(known_labels[num_known_labels].name, label); + known_labels[num_known_labels++].address = address; + } + fclose(f); + +} + +/****************************************************************************************/ +/* INTERFACE */ +/****************************************************************************************/ + +void parser_parse(const char* buffer, byte* mem) { + + tkn_init(buffer); + parser_finished = false; + include_labels(); + code = mem; + codepos = 2; + tkn_next(); + + parse_statements(); + append_strings(); + + //return code; +} + +const int parser_get_codesize() { return codepos; } +const int parser_get_memory_usage() { return num_variables; } + +void parser_register_external_function(const char* name, const int num_parameters) { + strcpy(external_functions[num_external_functions].name, name); + external_functions[num_external_functions++].num_parameters = num_parameters; +} diff --git a/parser.h b/parser.h new file mode 100644 index 0000000..f43086b --- /dev/null +++ b/parser.h @@ -0,0 +1,80 @@ +#pragma once + +enum OPS { + OP_NOP = 0, + OP_PUSH, + OP_POP, + OP_DUP, + OP_SWAP, + + OP_LOAD, + OP_LOADI, + OP_STORE, + OP_STOREI, + + OP_LOADXY, + OP_STOREXY, + OP_SETX, + OP_SETY, + OP_SETZ, + OP_GETY, + OP_GETZ, + OP_INCX, + OP_DECX, + OP_INCY, + OP_DECY, + OP_INCZ, + OP_DECZ, + + OP_JMP, + OP_JNT, + OP_JTR, + OP_JSR, + OP_RET, + OP_CALL, + + OP_RJ, + OP_RB, + OP_RJZ, + OP_RJN, + OP_RBZ, + OP_RBN, + + OP_RJYZ, + OP_RJYN, + OP_RBYZ, + OP_RBYN, + OP_RJZZ, + OP_RJZN, + OP_RBZZ, + OP_RBZN, + + OP_ADD, + OP_SUB, + OP_MUL, + OP_DIV, + OP_MOD, + OP_AND, + OP_OR, + OP_NOT, + OP_NEG, + OP_INC, + OP_DEC, + OP_CONCAT, + + OP_EQ, + OP_NEQ, + OP_LT, + OP_GT, + OP_LEQ, + OP_GEQ, + + OP_IN, + OP_OUT, + OP_SLEEP, +}; + +void parser_parse(const char* buffer, unsigned char* mem); +const int parser_get_codesize(); +const int parser_get_memory_usage(); +void parser_register_external_function(const char* name, const int num_parameters); diff --git a/rom.lbl b/rom.lbl new file mode 100644 index 0000000..37a390c Binary files /dev/null and b/rom.lbl differ diff --git a/test.bas b/test.bas new file mode 100644 index 0000000..422b424 --- /dev/null +++ b/test.bas @@ -0,0 +1,21 @@ +locate 2 1 +print "HOLA JAILERS" +border 5 +spr = 250 +setsprite 0 spr 0 +x = 0 +dx = 1 +count = 5 +peiv: +putsprite 0 x 60 +updatescr +x = x + dx +if x = 128 then dx = -1 end +if x = 8 then dx = 1 end +dec count +if count = 0 then + count = 5 + if spr = 250 then spr = 251 else spr = 250 end + setsprite 0 spr 1 +end +goto peiv \ No newline at end of file diff --git a/tokenizer.cpp b/tokenizer.cpp new file mode 100644 index 0000000..ba40913 --- /dev/null +++ b/tokenizer.cpp @@ -0,0 +1,142 @@ +#include "tokenizer.h" + +static bool streq(const char* a, const char* b) { + while (*a != 0 && *b != 0) { + if (*a != *b) return false; + a++; b++; + } + return *a == *b; +} + +static const char* ptr; +static t_tokentype current_token = TOKEN_ERROR; +static char identifier[255]; +static int value; +static int line = 0; +static int row = 0; +static int current_tokenline = 0; +static int current_tokenrow = 0; + +struct t_token_op { + char* str; + t_tokentype tokentype; +}; + +t_token_op tkn_tokens[] = { + { "let", TOKEN_LET }, + { "if", TOKEN_IF }, + { "then", TOKEN_THEN }, + { "else", TOKEN_ELSE }, + { "end", TOKEN_END }, + { "for", TOKEN_FOR }, + { "to", TOKEN_TO }, + { "break", TOKEN_BREAK }, + { "continue", TOKEN_CONTINUE }, + { "goto", TOKEN_GOTO }, + { "gosub", TOKEN_GOSUB }, + { "return", TOKEN_RETURN }, + { "rem", TOKEN_REM }, + { "quit", TOKEN_QUIT }, + { "and", TOKEN_AND }, + { "or", TOKEN_OR }, + { "not", TOKEN_NOT }, + { "mod", TOKEN_MOD }, + { "dim", TOKEN_DIM }, + { "inc", TOKEN_INC }, + { "dec", TOKEN_DEC }, + { "strleft", TOKEN_STRLEFT }, + { "strlen", TOKEN_STRLEN }, + { "debug", TOKEN_DEBUG }, + { "chr", TOKEN_CHR }, + { "str", TOKEN_STR }, + { "setsprite", TOKEN_SETSPRITE }, + { "putsprite", TOKEN_PUTSPRITE }, + { "locate", TOKEN_LOCATE }, + { "print", TOKEN_PRINT }, + { "putchar", TOKEN_PUTCHAR }, + { "setchar", TOKEN_SETCHAR }, + { "keypressed", TOKEN_KEYPRESSED }, + { "anykey", TOKEN_ANYKEY }, + { "updatescr", TOKEN_UPDATESCR }, + { "color", TOKEN_COLOR }, + { "border", TOKEN_BORDER }, +}; + +#define CCHR *ptr +#define NEXT if(*ptr==10){line++;row=0;}else{row++;};ptr++ + +static void tkn_do_next() { + identifier[0] = 0; + value = 0; + char* id = identifier; + // Ignore whitespace + while (CCHR != 0 && CCHR != 10 && CCHR <= 32) { NEXT; } + // If zero, we are at the end of the file + current_tokenline = line; current_tokenrow = row; + if (CCHR == 0) { current_token = TOKEN_ENDFILE; return; } + // if 13, we are at the end of the line + if (CCHR == 10) { NEXT; current_token = TOKEN_ENDLINE; return; } + // if 34, grab the string + if (CCHR == 34) { + NEXT; + while (CCHR != 0 && CCHR != 34) { *id = CCHR; id++; NEXT; } + if (CCHR == 0) { current_token = TOKEN_ERROR; return; } + *id = 0; NEXT; + current_token = TOKEN_STRING; return; + } + // If digit, grab the number + if (CCHR >= 48 && CCHR <= 57) { + do { value = value * 10 + (CCHR - 48); NEXT; } while (CCHR >= 48 && CCHR <= 57); + current_token = TOKEN_NUMBER; return; + } + // if letter, grab the identifier + if ((CCHR >= 65 && CCHR <= 90) || (CCHR >= 97 && CCHR <= 122)) { + do { *id = CCHR; id++; NEXT; } while ((CCHR >= 48 && CCHR <= 57) || (CCHR >= 65 && CCHR <= 90) || (CCHR >= 97 && CCHR <= 122) || CCHR == 95); + *id = 0; + int i = 0; while (identifier[i] != 0) { if (identifier[i] < 95) { identifier[i] |= 32; }; i++; } // to lowecase + // if it ends with ':', it's a label + if (CCHR == ':') { NEXT; current_token = TOKEN_LABEL; return; } + // If it matches a keyword, it's a keyword + for (auto token : tkn_tokens) { + if (streq(token.str, identifier)) { + current_token = token.tokentype; + if (current_token == TOKEN_REM) { while (CCHR != 0 && CCHR != 13) { NEXT; } } + return; + } + } + // else, it's an identifier + current_token = TOKEN_IDENTIFIER; return; + } + // Check if it's any of the operators + if (CCHR == '+') { NEXT; current_token = TOKEN_PLUS; return; } + if (CCHR == '-') { NEXT; current_token = TOKEN_MINUS; return; } + if (CCHR == '*') { NEXT; current_token = TOKEN_ASTERISC; return; } + if (CCHR == '/') { NEXT; current_token = TOKEN_SLASH; return; } + if (CCHR == '(') { NEXT; current_token = TOKEN_LPAR; return; } + if (CCHR == ')') { NEXT; current_token = TOKEN_RPAR; return; } + if (CCHR == ':') { NEXT; current_token = TOKEN_COLON; return; } + if (CCHR == ',') { NEXT; current_token = TOKEN_COMMA; return; } + if (CCHR == '<') { NEXT; if (CCHR == '=') { NEXT; current_token = TOKEN_LEQ; } else { current_token = TOKEN_LT; } return; } + if (CCHR == '>') { NEXT; if (CCHR == '=') { NEXT; current_token = TOKEN_GEQ; } else { current_token = TOKEN_GT; } return; } + if (CCHR == '=') { NEXT; current_token = TOKEN_EQ; return; } + if (CCHR == '%') { NEXT; current_token = TOKEN_MOD; return; } + if (CCHR == 39) { NEXT; current_token = TOKEN_REM; while (CCHR != 0 && CCHR != 10) { NEXT; }; return; } + + // if no match, it's an error + current_token = TOKEN_ERROR; return; +} + +void tkn_init(const char* buffer) { + ptr = buffer; + current_token = TOKEN_ERROR; + value = line = row = current_tokenline = current_tokenrow = 0; + identifier[0] = 0; +} + +void tkn_next() { do { tkn_do_next(); } while (current_token == TOKEN_REM || current_token == TOKEN_ENDLINE); } + +t_tokentype tkn_get_token() { return current_token; } +char* tkn_get_string() { return identifier; } +int tkn_get_value() { return value; } +int tkn_get_line() { return current_tokenline; } +int tkn_get_row() { return current_tokenrow; } diff --git a/tokenizer.h b/tokenizer.h new file mode 100644 index 0000000..54f6bc2 --- /dev/null +++ b/tokenizer.h @@ -0,0 +1,77 @@ +#pragma once + +typedef unsigned char byte; +typedef unsigned short word; + +enum t_tokentype { + TOKEN_ERROR, + TOKEN_NUMBER, + TOKEN_STRING, + + TOKEN_IDENTIFIER, + TOKEN_LABEL, + TOKEN_DIM, + + TOKEN_LET, + TOKEN_IF, + TOKEN_ELSE, + TOKEN_END, + TOKEN_FOR, + TOKEN_BREAK, + TOKEN_CONTINUE, + TOKEN_GOTO, + TOKEN_GOSUB, + TOKEN_RETURN, + TOKEN_REM, + TOKEN_QUIT, + TOKEN_INC, + TOKEN_DEC, + + TOKEN_THEN, + TOKEN_TO, + + TOKEN_AND, + TOKEN_OR, + TOKEN_NOT, + TOKEN_MOD, + TOKEN_PLUS, + TOKEN_MINUS, + TOKEN_ASTERISC, + TOKEN_SLASH, + TOKEN_LPAR, + TOKEN_RPAR, + TOKEN_COLON, + TOKEN_COMMA, + TOKEN_LT, + TOKEN_GT, + TOKEN_LEQ, + TOKEN_GEQ, + TOKEN_EQ, + TOKEN_ENDLINE, + TOKEN_ENDFILE, + + TOKEN_STRLEFT, + TOKEN_STRLEN, + TOKEN_DEBUG, + TOKEN_CHR, + TOKEN_STR, + TOKEN_SETSPRITE, + TOKEN_PUTSPRITE, + TOKEN_LOCATE, + TOKEN_PRINT, + TOKEN_PUTCHAR, + TOKEN_SETCHAR, + TOKEN_KEYPRESSED, + TOKEN_ANYKEY, + TOKEN_UPDATESCR, + TOKEN_COLOR, + TOKEN_BORDER, +}; + +void tkn_init(const char* buffer); +void tkn_next(); +t_tokentype tkn_get_token(); +char* tkn_get_string(); +int tkn_get_value(); +int tkn_get_line(); +int tkn_get_row(); diff --git a/vbvm.vcxproj b/vbvm.vcxproj index 10dcec1..e3faf46 100644 --- a/vbvm.vcxproj +++ b/vbvm.vcxproj @@ -56,7 +56,7 @@ WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - Windows + Console true SDL2.lib;SDL2main.lib;%(AdditionalDependencies) @@ -79,15 +79,21 @@ + + + + + + diff --git a/vbvm.vcxproj.filters b/vbvm.vcxproj.filters index d149be8..c28762b 100644 --- a/vbvm.vcxproj.filters +++ b/vbvm.vcxproj.filters @@ -27,6 +27,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files + @@ -44,5 +53,14 @@ Header Files + + Header Files + + + Header Files + + + Header Files + \ No newline at end of file diff --git a/vdp.cpp b/vdp.cpp index cdb46fb..42f6c81 100644 --- a/vdp.cpp +++ b/vdp.cpp @@ -126,21 +126,17 @@ static void flip() { Uint8 col = sprites[i].col; Uint8* sbt = &screen_buffer[((x - 8) * 4) + ((y - 8) * 128 * 4)]; for (int l = 0; l < 8; l++) { - if (y >= 8) { - if (((x+0) >= 8) && (*cm & 128)) memcpy(sbt, palette[col & 0x0F], 4); sbt += 4; - if (((x + 1) >= 8) && (*cm & 64)) memcpy(sbt, palette[col & 0x0F], 4); sbt += 4; - if (((x + 2) >= 8) && (*cm & 32)) memcpy(sbt, palette[col & 0x0F], 4); sbt += 4; - if (((x + 3) >= 8) && (*cm & 16)) memcpy(sbt, palette[col & 0x0F], 4); sbt += 4; - if (((x + 4) >= 8) && (*cm & 8)) memcpy(sbt, palette[col & 0x0F], 4); sbt += 4; - if (((x+5) >= 8) && (*cm & 4)) - memcpy(sbt, palette[col & 0x0F], 4); - sbt += 4; - if (((x+6) >= 8) && (*cm & 2)) - memcpy(sbt, palette[col & 0x0F], 4); - sbt += 4; - if (((x+7) >= 8) && (*cm & 1)) - memcpy(sbt, palette[col & 0x0F], 4); - sbt += (512 - 32 + 4); + if (y >= 8 && y < 104) { + if (((x + 0) >= 8) && ((x + 0) < 136) && (*cm & 128)) memcpy(sbt, palette[col & 0x0F], 4); sbt += 4; + if (((x + 1) >= 8) && ((x + 1) < 136) && (*cm & 64)) memcpy(sbt, palette[col & 0x0F], 4); sbt += 4; + if (((x + 2) >= 8) && ((x + 2) < 136) && (*cm & 32)) memcpy(sbt, palette[col & 0x0F], 4); sbt += 4; + if (((x + 3) >= 8) && ((x + 3) < 136) && (*cm & 16)) memcpy(sbt, palette[col & 0x0F], 4); sbt += 4; + if (((x + 4) >= 8) && ((x + 4) < 136) && (*cm & 8)) memcpy(sbt, palette[col & 0x0F], 4); sbt += 4; + if (((x + 5) >= 8) && ((x + 5) < 136) && (*cm & 4)) memcpy(sbt, palette[col & 0x0F], 4); sbt += 4; + if (((x + 6) >= 8) && ((x + 6) < 136) && (*cm & 2)) memcpy(sbt, palette[col & 0x0F], 4); sbt += 4; + if (((x + 7) >= 8) && ((x + 7) < 136) && (*cm & 1)) memcpy(sbt, palette[col & 0x0F], 4); sbt += (512 - 32 + 4); + } else { + sbt += 512; } cm++; y++; } @@ -201,7 +197,7 @@ void vdp_cmd_out(const unsigned char& value) { break; case VDP_CMD_BORDER: border = data_stack[0] & 0xF; - SDL_SetRenderDrawColor(sdlRenderer, palette[border][3], palette[border][2], palette[border][1], palette[border][0]); + SDL_SetRenderDrawColor(sdlRenderer, palette[border][0], palette[border][1], palette[border][2], palette[border][3]); break; } data_stack_pos = 0; diff --git a/vm.cpp b/vm.cpp index 8a5f7f7..726b9ec 100644 --- a/vm.cpp +++ b/vm.cpp @@ -1,5 +1,8 @@ #include "vm.h" #include +#include +#include "parser.h" +#include "error.h" #define MAX_EXTERNAL_CALLS 256 #define MAX_DATA_STACK 256 @@ -8,7 +11,7 @@ typedef unsigned char byte; typedef unsigned short word; -enum OPS { +/*enum OPS { OP_NOP = 0, OP_PUSH, OP_POP, @@ -80,7 +83,7 @@ enum OPS { OP_IN, OP_OUT, OP_SLEEP, -}; +};*/ typedef void(*t_extcall)(t_stack&); t_extcall external_calls[MAX_EXTERNAL_CALLS]; @@ -113,12 +116,39 @@ static void load_rom() { } static void load_program(const char* filename) { - FILE *f = fopen(filename, "rb"); +/* FILE *f = fopen(filename, "rb"); fseek(f, 0, SEEK_END); long fsize = ftell(f); fseek(f, 0, SEEK_SET); fread(mem, fsize, 1, f); + fclose(f);*/ + FILE *f = fopen(filename, "rb"); + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); //same as rewind(f); + + char *program = (char*)malloc(fsize + 1); + fread(program, fsize, 1, f); fclose(f); + + program[fsize] = 0; + + //unsigned char* rawCode = + parser_parse(program, mem); + free(program); + + if (error_raised()) { + error_print(); + //getchar(); + } + else { + printf("Compilation complete!\nProgram size: %d bytes\nMemory usage: %d bytes\n", parser_get_codesize(), parser_get_memory_usage()); + /*FILE *f = fopen("test.bin", "wb"); + if (f == nullptr) perror("Error");// printf("Error: %d (%s)\n", errno, strerror(errno)); + fwrite(rawCode, parser_get_codesize(), 1, f); + fclose(f);*/ + } + } void vm_init(const char* filename) {