| @@ -49,21 +49,20 @@ | |||||
| #define INST_FUNC(mnemonic) \ | #define INST_FUNC(mnemonic) \ | ||||
| static ASMErrorDesc parse_inst_##mnemonic( \ | static ASMErrorDesc parse_inst_##mnemonic( \ | ||||
| uint8_t **bytes, size_t *length, char **symbol, const char *arg, size_t size) | |||||
| uint8_t **bytes, size_t *length, char **symbol, ASMArgParseInfo ap_info) \ | |||||
| #define INST_ERROR(desc) return ED_PS_##desc; | #define INST_ERROR(desc) return ED_PS_##desc; | ||||
| #define INST_TAKES_NO_ARGS \ | #define INST_TAKES_NO_ARGS \ | ||||
| if (arg) \ | |||||
| if (ap_info.arg) \ | |||||
| INST_ERROR(TOO_MANY_ARGS) \ | INST_ERROR(TOO_MANY_ARGS) \ | ||||
| (void) size; | |||||
| #define INST_TAKES_ARGS(lo, hi) \ | #define INST_TAKES_ARGS(lo, hi) \ | ||||
| if (!arg) \ | |||||
| if (!ap_info.arg) \ | |||||
| INST_ERROR(TOO_FEW_ARGS) \ | INST_ERROR(TOO_FEW_ARGS) \ | ||||
| ASMInstArg args[3]; \ | ASMInstArg args[3]; \ | ||||
| size_t nargs = 0; \ | size_t nargs = 0; \ | ||||
| ASMErrorDesc err = parse_args(args, &nargs, arg, size); \ | |||||
| ASMErrorDesc err = parse_args(args, &nargs, ap_info); \ | |||||
| if (err) \ | if (err) \ | ||||
| return err; \ | return err; \ | ||||
| if (nargs < lo) \ | if (nargs < lo) \ | ||||
| @@ -143,14 +142,16 @@ static uint8_t fill_bytes_variadic(uint8_t *bytes, size_t len, ...) | |||||
| Return ED_NONE (0) on success or an error code on failure. | Return ED_NONE (0) on success or an error code on failure. | ||||
| */ | */ | ||||
| static ASMErrorDesc parse_arg(ASMInstArg *arg, const char *str, size_t size) | |||||
| static ASMErrorDesc parse_arg( | |||||
| ASMInstArg *arg, const char *str, size_t size, ASMDefineTable *deftable) | |||||
| { | { | ||||
| #define TRY_PARSER(func, argtype, field) \ | #define TRY_PARSER(func, argtype, field) \ | ||||
| if (argparse_##func(&arg->data.field, str, size)) { \ | |||||
| if (argparse_##func(&arg->data.field, info)) { \ | |||||
| arg->type = argtype; \ | arg->type = argtype; \ | ||||
| return ED_NONE; \ | return ED_NONE; \ | ||||
| } | } | ||||
| ASMArgParseInfo info = {.arg = str, .size = size, .deftable = deftable}; | |||||
| DEBUG("parse_arg(): -->%.*s<-- %zu", (int) size, str, size) | DEBUG("parse_arg(): -->%.*s<-- %zu", (int) size, str, size) | ||||
| TRY_PARSER(register, AT_REGISTER, reg) | TRY_PARSER(register, AT_REGISTER, reg) | ||||
| TRY_PARSER(immediate, AT_IMMEDIATE, imm) | TRY_PARSER(immediate, AT_IMMEDIATE, imm) | ||||
| @@ -164,22 +165,24 @@ static ASMErrorDesc parse_arg(ASMInstArg *arg, const char *str, size_t size) | |||||
| } | } | ||||
| /* | /* | ||||
| Parse an argument string int ASMInstArg objects. | |||||
| Parse an argument string into ASMInstArg objects. | |||||
| Return ED_NONE (0) on success or an error code on failure. | Return ED_NONE (0) on success or an error code on failure. | ||||
| */ | */ | ||||
| static ASMErrorDesc parse_args( | static ASMErrorDesc parse_args( | ||||
| ASMInstArg args[3], size_t *nargs, const char *str, size_t size) | |||||
| ASMInstArg args[3], size_t *nargs, ASMArgParseInfo ap_info) | |||||
| { | { | ||||
| ASMErrorDesc err; | ASMErrorDesc err; | ||||
| size_t start = 0, i = 0; | |||||
| ASMDefineTable *dt = ap_info.deftable; | |||||
| const char *str = ap_info.arg; | |||||
| size_t size = ap_info.size, start = 0, i = 0; | |||||
| while (i < size) { | while (i < size) { | ||||
| char c = str[i]; | char c = str[i]; | ||||
| if (c == ',') { | if (c == ',') { | ||||
| if (i == start) | if (i == start) | ||||
| return ED_PS_ARG_SYNTAX; | return ED_PS_ARG_SYNTAX; | ||||
| if ((err = parse_arg(&args[*nargs], str + start, i - start))) | |||||
| if ((err = parse_arg(&args[*nargs], str + start, i - start, dt))) | |||||
| return err; | return err; | ||||
| (*nargs)++; | (*nargs)++; | ||||
| @@ -202,7 +205,7 @@ static ASMErrorDesc parse_args( | |||||
| } | } | ||||
| if (i > start) { | if (i > start) { | ||||
| if ((err = parse_arg(&args[*nargs], str + start, i - start))) | |||||
| if ((err = parse_arg(&args[*nargs], str + start, i - start, dt))) | |||||
| return err; | return err; | ||||
| (*nargs)++; | (*nargs)++; | ||||
| } | } | ||||
| @@ -6,6 +6,7 @@ | |||||
| #include <stdint.h> | #include <stdint.h> | ||||
| #include "errors.h" | #include "errors.h" | ||||
| #include "parse_util.h" | |||||
| #define MIN_MNEMONIC_SIZE 2 | #define MIN_MNEMONIC_SIZE 2 | ||||
| #define MAX_MNEMONIC_SIZE 4 | #define MAX_MNEMONIC_SIZE 4 | ||||
| @@ -13,7 +14,7 @@ | |||||
| /* Typedefs */ | /* Typedefs */ | ||||
| typedef ASMErrorDesc (*ASMInstParser)( | typedef ASMErrorDesc (*ASMInstParser)( | ||||
| uint8_t**, size_t*, char**, const char*, size_t); | |||||
| uint8_t**, size_t*, char**, ASMArgParseInfo); | |||||
| /* Functions */ | /* Functions */ | ||||
| @@ -25,15 +25,15 @@ | |||||
| */ | */ | ||||
| /* | /* | ||||
| Adjust *arg_ptr / *size_ptr for an indirect argument, like (hl) or (ix+*). | |||||
| Adjust *ap_info for an indirect argument, like (hl) or (ix+*). | |||||
| *size_ptr must be > 2 to begin with, and is assured to be > 0 on success. | |||||
| The two arguments are not modified on failure. | |||||
| ap_info->size must be > 2 to begin with, and will always be > 0 on success. | |||||
| *ap_info is not modified on failure. | |||||
| */ | */ | ||||
| static bool adjust_for_indirection(const char **arg_ptr, ssize_t *size_ptr) | |||||
| static bool adjust_for_indirection(ASMArgParseInfo *ap_info) | |||||
| { | { | ||||
| const char *arg = *arg_ptr; | |||||
| ssize_t size = *size_ptr; | |||||
| const char *arg = ap_info->arg; | |||||
| ssize_t size = ap_info->size; | |||||
| if (arg[0] != '(' || arg[size - 1] != ')') | if (arg[0] != '(' || arg[size - 1] != ')') | ||||
| return false; | return false; | ||||
| @@ -50,8 +50,8 @@ static bool adjust_for_indirection(const char **arg_ptr, ssize_t *size_ptr) | |||||
| return false; | return false; | ||||
| } | } | ||||
| *arg_ptr = arg; | |||||
| *size_ptr = size; | |||||
| ap_info->arg = arg; | |||||
| ap_info->size = size; | |||||
| return true; | return true; | ||||
| } | } | ||||
| @@ -208,19 +208,19 @@ bool parse_bytes(uint8_t **result, size_t *length, const char *arg, ssize_t size | |||||
| /* | /* | ||||
| Read in a register argument and store it in *result. | Read in a register argument and store it in *result. | ||||
| */ | */ | ||||
| bool argparse_register(ASMArgRegister *result, const char *arg, ssize_t size) | |||||
| bool argparse_register(ASMArgRegister *result, ASMArgParseInfo ai) | |||||
| { | { | ||||
| if (size < 1 || size > 3) | |||||
| if (ai.size < 1 || ai.size > 3) | |||||
| return false; | return false; | ||||
| char buf[3] = {'\0'}; | char buf[3] = {'\0'}; | ||||
| switch (size) { | |||||
| case 3: buf[2] = LCASE(arg[2]); | |||||
| case 2: buf[1] = LCASE(arg[1]); | |||||
| case 1: buf[0] = LCASE(arg[0]); | |||||
| switch (ai.size) { | |||||
| case 3: buf[2] = LCASE(ai.arg[2]); | |||||
| case 2: buf[1] = LCASE(ai.arg[1]); | |||||
| case 1: buf[0] = LCASE(ai.arg[0]); | |||||
| } | } | ||||
| switch (size) { | |||||
| switch (ai.size) { | |||||
| case 1: | case 1: | ||||
| switch (buf[0]) { | switch (buf[0]) { | ||||
| case 'a': return (*result = REG_A), true; | case 'a': return (*result = REG_A), true; | ||||
| @@ -263,18 +263,18 @@ bool argparse_register(ASMArgRegister *result, const char *arg, ssize_t size) | |||||
| /* | /* | ||||
| Read in a condition argument and store it in *result. | Read in a condition argument and store it in *result. | ||||
| */ | */ | ||||
| bool argparse_condition(ASMArgCondition *result, const char *arg, ssize_t size) | |||||
| bool argparse_condition(ASMArgCondition *result, ASMArgParseInfo ai) | |||||
| { | { | ||||
| if (size < 1 || size > 2) | |||||
| if (ai.size < 1 || ai.size > 2) | |||||
| return false; | return false; | ||||
| char buf[2] = {'\0'}; | char buf[2] = {'\0'}; | ||||
| switch (size) { | |||||
| case 2: buf[1] = LCASE(arg[1]); | |||||
| case 1: buf[0] = LCASE(arg[0]); | |||||
| switch (ai.size) { | |||||
| case 2: buf[1] = LCASE(ai.arg[1]); | |||||
| case 1: buf[0] = LCASE(ai.arg[0]); | |||||
| } | } | ||||
| switch (size) { | |||||
| switch (ai.size) { | |||||
| case 1: | case 1: | ||||
| switch (buf[0]) { | switch (buf[0]) { | ||||
| case 'n': return (*result = COND_N), true; | case 'n': return (*result = COND_N), true; | ||||
| @@ -298,23 +298,25 @@ bool argparse_condition(ASMArgCondition *result, const char *arg, ssize_t size) | |||||
| /* | /* | ||||
| Read in an immediate argument and store it in *result. | Read in an immediate argument and store it in *result. | ||||
| */ | */ | ||||
| bool argparse_immediate(ASMArgImmediate *result, const char *arg, ssize_t size) | |||||
| bool argparse_immediate(ASMArgImmediate *result, ASMArgParseInfo ai) | |||||
| { | { | ||||
| if (ai.size <= 0) | |||||
| return false; | |||||
| // TODO: lookup here for definition table | |||||
| bool negative = false; | bool negative = false; | ||||
| ssize_t i = 0; | ssize_t i = 0; | ||||
| if (size <= 0) | |||||
| return false; | |||||
| while (arg[i] == '-' || arg[i] == '+' || arg[i] == ' ') { | |||||
| if (arg[i] == '-') | |||||
| while (ai.arg[i] == '-' || ai.arg[i] == '+' || ai.arg[i] == ' ') { | |||||
| if (ai.arg[i] == '-') | |||||
| negative = !negative; | negative = !negative; | ||||
| if (++i >= size) | |||||
| if (++i >= ai.size) | |||||
| return false; | return false; | ||||
| } | } | ||||
| uint32_t uval; | uint32_t uval; | ||||
| if (!parse_uint32_t(&uval, arg + i, size - i) || uval > UINT16_MAX) | |||||
| if (!parse_uint32_t(&uval, ai.arg + i, ai.size - i) || uval > UINT16_MAX) | |||||
| return false; | return false; | ||||
| int32_t sval = negative ? -uval : uval; | int32_t sval = negative ? -uval : uval; | ||||
| @@ -351,20 +353,20 @@ bool argparse_immediate(ASMArgImmediate *result, const char *arg, ssize_t size) | |||||
| /* | /* | ||||
| Read in an indirect argument and store it in *result. | Read in an indirect argument and store it in *result. | ||||
| */ | */ | ||||
| bool argparse_indirect(ASMArgIndirect *result, const char *arg, ssize_t size) | |||||
| bool argparse_indirect(ASMArgIndirect *result, ASMArgParseInfo ai) | |||||
| { | { | ||||
| if (size < 3 || !adjust_for_indirection(&arg, &size)) | |||||
| if (ai.size < 3 || !adjust_for_indirection(&ai)) | |||||
| return false; | return false; | ||||
| ASMArgRegister reg; | ASMArgRegister reg; | ||||
| ASMArgImmediate imm; | ASMArgImmediate imm; | ||||
| if (argparse_register(®, arg, size)) { | |||||
| if (argparse_register(®, ai)) { | |||||
| if (reg == REG_BC || reg == REG_DE || reg == REG_HL) { | if (reg == REG_BC || reg == REG_DE || reg == REG_HL) { | ||||
| result->type = AT_REGISTER; | result->type = AT_REGISTER; | ||||
| result->addr.reg = reg; | result->addr.reg = reg; | ||||
| return true; | return true; | ||||
| } | } | ||||
| } else if (argparse_immediate(&imm, arg, size)) { | |||||
| } else if (argparse_immediate(&imm, ai)) { | |||||
| if (imm.mask & IMM_U16) { | if (imm.mask & IMM_U16) { | ||||
| result->type = AT_IMMEDIATE; | result->type = AT_IMMEDIATE; | ||||
| result->addr.imm = imm; | result->addr.imm = imm; | ||||
| @@ -377,31 +379,31 @@ bool argparse_indirect(ASMArgIndirect *result, const char *arg, ssize_t size) | |||||
| /* | /* | ||||
| Read in an indexed argument and store it in *result. | Read in an indexed argument and store it in *result. | ||||
| */ | */ | ||||
| bool argparse_indexed(ASMArgIndexed *result, const char *arg, ssize_t size) | |||||
| bool argparse_indexed(ASMArgIndexed *result, ASMArgParseInfo ai) | |||||
| { | { | ||||
| if (size < 4 || !adjust_for_indirection(&arg, &size) || size < 2) | |||||
| if (ai.size < 4 || !adjust_for_indirection(&ai) || ai.size < 2) | |||||
| return false; | return false; | ||||
| ASMArgRegister reg; | ASMArgRegister reg; | ||||
| if (arg[0] != 'i') | |||||
| if (ai.arg[0] != 'i') | |||||
| return false; | return false; | ||||
| if (arg[1] == 'x') | |||||
| if (ai.arg[1] == 'x') | |||||
| reg = REG_IX; | reg = REG_IX; | ||||
| else if (arg[1] == 'y') | |||||
| else if (ai.arg[1] == 'y') | |||||
| reg = REG_IY; | reg = REG_IY; | ||||
| else | else | ||||
| return false; | return false; | ||||
| arg += 2; | |||||
| size -= 2; | |||||
| if (size > 0 && arg[0] == ' ') { | |||||
| arg++; | |||||
| size--; | |||||
| ai.arg += 2; | |||||
| ai.size -= 2; | |||||
| if (ai.size > 0 && ai.arg[0] == ' ') { | |||||
| ai.arg++; | |||||
| ai.size--; | |||||
| } | } | ||||
| if (size > 0) { | |||||
| if (ai.size > 0) { | |||||
| ASMArgImmediate imm; | ASMArgImmediate imm; | ||||
| if (!argparse_immediate(&imm, arg, size) || !(imm.mask & IMM_S8)) | |||||
| if (!argparse_immediate(&imm, ai) || !(imm.mask & IMM_S8)) | |||||
| return false; | return false; | ||||
| result->offset = imm.sval; | result->offset = imm.sval; | ||||
| } else { | } else { | ||||
| @@ -414,20 +416,22 @@ bool argparse_indexed(ASMArgIndexed *result, const char *arg, ssize_t size) | |||||
| /* | /* | ||||
| Read in a label argument and store it in *result. | Read in a label argument and store it in *result. | ||||
| */ | */ | ||||
| bool argparse_label(ASMArgLabel *result, const char *arg, ssize_t size) | |||||
| bool argparse_label(ASMArgLabel *result, ASMArgParseInfo ai) | |||||
| { | { | ||||
| if (size >= MAX_SYMBOL_SIZE) | |||||
| if (ai.size <= 0 || ai.size >= MAX_SYMBOL_SIZE) | |||||
| return false; | return false; | ||||
| for (const char *i = arg; i < arg + size; i++) { | |||||
| // TODO: check for deftable | |||||
| for (const char *i = ai.arg; i < ai.arg + ai.size; i++) { | |||||
| char c = *i; | char c = *i; | ||||
| if (!((c >= 'a' && c <= 'z') || (i != arg && c >= '0' && c <= '9') || | |||||
| c == '_' || c == '.')) | |||||
| if (!((c >= 'a' && c <= 'z') || c == '_' || c == '.' || | |||||
| (i != ai.arg && c >= '0' && c <= '9'))) | |||||
| return false; | return false; | ||||
| } | } | ||||
| strncpy(result->text, arg, size); | |||||
| result->text[size] = '\0'; | |||||
| strncpy(result->text, ai.arg, ai.size); | |||||
| result->text[ai.size] = '\0'; | |||||
| return true; | return true; | ||||
| } | } | ||||
| @@ -11,6 +11,14 @@ | |||||
| #define dparse__Bool dparse_bool | #define dparse__Bool dparse_bool | ||||
| /* Structs */ | |||||
| typedef struct { | |||||
| const char *arg; | |||||
| ssize_t size; | |||||
| ASMDefineTable *deftable; | |||||
| } ASMArgParseInfo; | |||||
| /* Functions */ | /* Functions */ | ||||
| /* General parsers */ | /* General parsers */ | ||||
| @@ -20,12 +28,12 @@ bool parse_string(char**, size_t*, const char*, ssize_t); | |||||
| bool parse_bytes(uint8_t**, size_t*, const char*, ssize_t); | bool parse_bytes(uint8_t**, size_t*, const char*, ssize_t); | ||||
| /* Instruction argument parsers */ | /* Instruction argument parsers */ | ||||
| bool argparse_register(ASMArgRegister*, const char*, ssize_t); | |||||
| bool argparse_condition(ASMArgCondition*, const char*, ssize_t); | |||||
| bool argparse_immediate(ASMArgImmediate*, const char*, ssize_t); | |||||
| bool argparse_indirect(ASMArgIndirect*, const char*, ssize_t); | |||||
| bool argparse_indexed(ASMArgIndexed*, const char*, ssize_t); | |||||
| bool argparse_label(ASMArgLabel*, const char*, ssize_t); | |||||
| bool argparse_register(ASMArgRegister*, ASMArgParseInfo); | |||||
| bool argparse_condition(ASMArgCondition*, ASMArgParseInfo); | |||||
| bool argparse_immediate(ASMArgImmediate*, ASMArgParseInfo); | |||||
| bool argparse_indirect(ASMArgIndirect*, ASMArgParseInfo); | |||||
| bool argparse_indexed(ASMArgIndexed*, ASMArgParseInfo); | |||||
| bool argparse_label(ASMArgLabel*, ASMArgParseInfo); | |||||
| /* Preprocessor directive parsers */ | /* Preprocessor directive parsers */ | ||||
| bool dparse_bool(bool*, const ASMLine*, const char*); | bool dparse_bool(bool*, const ASMLine*, const char*); | ||||
| @@ -184,6 +184,16 @@ void asm_deftable_insert(ASMDefineTable *tab, ASMDefine *define) | |||||
| hash_table_insert(tab, (HashNode*) define); | hash_table_insert(tab, (HashNode*) define); | ||||
| } | } | ||||
| /* | |||||
| Remove an ASMDefine from the define table. | |||||
| Return true if the node was removed, or false if it was not found. | |||||
| */ | |||||
| bool asm_deftable_remove(ASMDefineTable *tab, const char *key) | |||||
| { | |||||
| return hash_table_remove(tab, key); | |||||
| } | |||||
| #ifdef DEBUG_MODE | #ifdef DEBUG_MODE | ||||
| /* | /* | ||||
| DEBUG FUNCTION: Print out an ASMLine list to stdout. | DEBUG FUNCTION: Print out an ASMLine list to stdout. | ||||
| @@ -110,6 +110,7 @@ const ASMSymbol* asm_symtable_find(const ASMSymbolTable*, const char*); | |||||
| void asm_symtable_insert(ASMSymbolTable*, ASMSymbol*); | void asm_symtable_insert(ASMSymbolTable*, ASMSymbol*); | ||||
| const ASMDefine* asm_deftable_find(const ASMDefineTable*, const char*); | const ASMDefine* asm_deftable_find(const ASMDefineTable*, const char*); | ||||
| void asm_deftable_insert(ASMDefineTable*, ASMDefine*); | void asm_deftable_insert(ASMDefineTable*, ASMDefine*); | ||||
| bool asm_deftable_remove(ASMDefineTable*, const char*); | |||||
| #ifdef DEBUG_MODE | #ifdef DEBUG_MODE | ||||
| void asm_lines_print(const ASMLine*); | void asm_lines_print(const ASMLine*); | ||||
| @@ -94,12 +94,13 @@ static ErrorInfo* add_label_to_table( | |||||
| if (line->length - 1 >= MAX_SYMBOL_SIZE) | if (line->length - 1 >= MAX_SYMBOL_SIZE) | ||||
| return error_info_create(line, ET_SYMBOL, ED_SYM_TOO_LONG); | return error_info_create(line, ET_SYMBOL, ED_SYM_TOO_LONG); | ||||
| ASMArgParseInfo info = {.arg = line->data, .size = line->length - 1}; | |||||
| ASMArgRegister reg; | ASMArgRegister reg; | ||||
| if (argparse_register(®, line->data, line->length - 1)) | |||||
| if (argparse_register(®, info)) | |||||
| return error_info_create(line, ET_SYMBOL, ED_SYM_IS_REGISTER); | return error_info_create(line, ET_SYMBOL, ED_SYM_IS_REGISTER); | ||||
| ASMArgCondition cond; | ASMArgCondition cond; | ||||
| if (argparse_condition(&cond, line->data, line->length - 1)) | |||||
| if (argparse_condition(&cond, info)) | |||||
| return error_info_create(line, ET_SYMBOL, ED_SYM_IS_CONDITION); | return error_info_create(line, ET_SYMBOL, ED_SYM_IS_CONDITION); | ||||
| char *symbol = strndup(line->data, line->length - 1); | char *symbol = strndup(line->data, line->length - 1); | ||||
| @@ -301,8 +302,8 @@ static ErrorInfo* parse_instruction( | |||||
| if (!parser) | if (!parser) | ||||
| return error_info_create(line, ET_PARSER, ED_PS_OP_UNKNOWN); | return error_info_create(line, ET_PARSER, ED_PS_OP_UNKNOWN); | ||||
| // TODO: pass deftab here? | |||||
| ASMErrorDesc edesc = parser(&bytes, &length, &symbol, argstart, arglen); | |||||
| ASMArgParseInfo ai = {.arg = argstart, .size = arglen, .deftable = deftab}; | |||||
| ASMErrorDesc edesc = parser(&bytes, &length, &symbol, ai); | |||||
| if (edesc != ED_NONE) | if (edesc != ED_NONE) | ||||
| return error_info_create(line, ET_PARSER, edesc); | return error_info_create(line, ET_PARSER, edesc); | ||||