From 415688d5c541e5f895ce3cb7e0ef676a77d55c7e Mon Sep 17 00:00:00 2001 From: Raimon Zamora Date: Mon, 30 Jan 2017 14:57:38 +0100 Subject: [PATCH] Now 'vbgame' is included inside this project. Some fixes. --- error.cpp | 18 + error.h | 5 + main.cpp | 2 +- parser.cpp | 793 +++++++++++++++++++++++++++++++++++++++++++ parser.h | 80 +++++ rom.lbl | Bin 0 -> 287 bytes test.bas | 21 ++ tokenizer.cpp | 142 ++++++++ tokenizer.h | 77 +++++ vbvm.vcxproj | 8 +- vbvm.vcxproj.filters | 18 + vdp.cpp | 28 +- vm.cpp | 36 +- 13 files changed, 1207 insertions(+), 21 deletions(-) create mode 100644 error.cpp create mode 100644 error.h create mode 100644 parser.cpp create mode 100644 parser.h create mode 100644 rom.lbl create mode 100644 test.bas create mode 100644 tokenizer.cpp create mode 100644 tokenizer.h 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 0000000000000000000000000000000000000000..37a390c9dc0eda969c4db7c53ab379ba118f99af GIT binary patch literal 287 zcmZ9GF%H5o3`MyBEg&&)09Fpf5~Yn>BBCU+-Adg&aRH9O!p6qgNUN$!S)bnfcDa$A zqJb1KTHh)cv?*ktBjSK?`qQA{^I9`db?rsUb&6LxOr$qa5imux43NRt%`ndGXu42q zhCzy@O`Ye*{KcrGuD)q}W+?5C=q1}FYn+YmY^Dw_;3vCO1vG$PIZlc7X;$4~hzLYr Eege~UMF0Q* literal 0 HcmV?d00001 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) {