@@ -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); | ||||