diff --git a/src/assembler/parse_util.c b/src/assembler/parse_util.c index db20736..974a1ad 100644 --- a/src/assembler/parse_util.c +++ b/src/assembler/parse_util.c @@ -56,6 +56,34 @@ static bool adjust_for_indirection(ASMArgParseInfo *ap_info) } /* + Calculate the mask field for an ASMArgImmediate based on its uval/sval. +*/ +static void calculate_immediate_mask(ASMArgImmediate *imm) +{ + imm->mask = 0; + if (imm->sval < 0) { + if (imm->sval >= INT8_MIN) + imm->mask |= IMM_S8; + if (imm->sval >= INT8_MIN + 2) + imm->mask |= IMM_REL; + } else { + imm->mask = IMM_U16; + if (imm->uval <= UINT8_MAX) + imm->mask |= IMM_U8; + if (imm->uval <= INT8_MAX) + imm->mask |= IMM_S8; + if (imm->uval <= INT8_MAX + 2) + imm->mask |= IMM_REL; + if (imm->uval <= 7) + imm->mask |= IMM_BIT; + if (!(imm->uval & ~0x38)) + imm->mask |= IMM_RST; + if (imm->uval <= 2) + imm->mask |= IMM_IM; + } +} + +/* Read in a boolean value and store it in *result. */ bool parse_bool(bool *result, const char *arg, ssize_t size) @@ -303,12 +331,6 @@ bool argparse_immediate(ASMArgImmediate *result, ASMArgParseInfo ai) if (ai.size <= 0) return false; - const ASMDefine *define = asm_deftable_find(ai.deftable, ai.arg, ai.size); - if (define) { - *result = define->value; - return true; - } - bool negative = false; ssize_t i = 0; @@ -318,9 +340,23 @@ bool argparse_immediate(ASMArgImmediate *result, ASMArgParseInfo ai) if (++i >= ai.size) return false; } + ai.arg += i; + ai.size -= i; + + const ASMDefine *define = asm_deftable_find(ai.deftable, ai.arg, ai.size); + if (define) { + if (negative) { + result->uval = define->value.uval; + result->sval = -define->value.sval; + calculate_immediate_mask(result); + } else { + *result = define->value; + } + return true; + } uint32_t uval; - if (!parse_uint32_t(&uval, ai.arg + i, ai.size - i) || uval > UINT16_MAX) + if (!parse_uint32_t(&uval, ai.arg, ai.size) || uval > UINT16_MAX) return false; int32_t sval = negative ? -uval : uval; @@ -329,28 +365,7 @@ bool argparse_immediate(ASMArgImmediate *result, ASMArgParseInfo ai) result->uval = uval; result->sval = sval; - result->mask = 0; - - if (sval < 0) { - if (sval >= INT8_MIN) - result->mask |= IMM_S8; - if (sval >= INT8_MIN + 2) - result->mask |= IMM_REL; - } else { - result->mask = IMM_U16; - if (uval <= UINT8_MAX) - result->mask |= IMM_U8; - if (uval <= INT8_MAX) - result->mask |= IMM_S8; - if (uval <= INT8_MAX + 2) - result->mask |= IMM_REL; - if (uval <= 7) - result->mask |= IMM_BIT; - if (!(uval & ~0x38)) - result->mask |= IMM_RST; - if (uval <= 2) - result->mask |= IMM_IM; - } + calculate_immediate_mask(result); return true; } @@ -425,11 +440,8 @@ bool argparse_label(ASMArgLabel *result, ASMArgParseInfo ai) if (ai.size <= 0 || ai.size >= MAX_SYMBOL_SIZE) return false; - // Validate the label characters: for (const char *i = ai.arg; i < ai.arg + ai.size; i++) { - char c = *i; - if (!((c >= 'a' && c <= 'z') || c == '_' || c == '.' || - (i != ai.arg && c >= '0' && c <= '9'))) + if (!is_valid_symbol_char(*i, i == ai.arg)) return false; } diff --git a/src/assembler/preprocessor.c b/src/assembler/preprocessor.c index 7a482b7..8a38314 100644 --- a/src/assembler/preprocessor.c +++ b/src/assembler/preprocessor.c @@ -85,15 +85,6 @@ else FAIL_ON_COND_(true, ED_PP_UNKNOWN) /* - Return whether the given character is a valid label character. -*/ -static inline bool is_valid_label_char(char c, bool first) -{ - return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || - (!first && c >= '0' && c <= '9') || c == '_' || c == '.'; -} - -/* Functions similar memcpy, but lowercases the characters along the way. */ static void memcpy_lc(char *restrict dst, const char *restrict src, size_t n) @@ -121,7 +112,7 @@ static size_t read_labels( start++; i = start; - while (i < length && is_valid_label_char(source[i], i == start)) + while (i < length && is_valid_symbol_char(source[i], i == start)) i++; if (i == start || i == length || source[i] != ':') { diff --git a/src/assembler/tokenizer.c b/src/assembler/tokenizer.c index 042fc15..81bbde2 100644 --- a/src/assembler/tokenizer.c +++ b/src/assembler/tokenizer.c @@ -12,6 +12,7 @@ #include "../logging.h" #include "../mmu.h" #include "../rom.h" +#include "../util.h" /* Internal structs */ @@ -128,6 +129,84 @@ static ErrorInfo* add_label_to_table( } /* + Handle a define directive by adding an entry to the define table. + + Return NULL on success and an ErrorInfo object on failure. +*/ +static ErrorInfo* handle_define_directive( + const ASMLine *line, ASMDefineTable *deftab) +{ + if (!DIRECTIVE_HAS_ARG(line, DIR_DEFINE)) + return error_info_create(line, ET_PREPROC, ED_PP_NO_ARG); + + size_t start = DIRECTIVE_OFFSET(line, DIR_DEFINE) + 1, i; + for (i = start; i < line->length; i++) { + if (!is_valid_symbol_char(line->data[i], i == start)) { + if (line->data[i] == ' ' && i > start) { + i++; + break; + } + return error_info_create(line, ET_PREPROC, ED_PP_BAD_ARG); + } + } + + if (i >= line->length) // Missing value for define + return error_info_create(line, ET_PREPROC, ED_PP_NO_ARG); + + const char *key = line->data + start; + size_t keylen = i - start - 1; + + const ASMDefine *current = asm_deftable_find(deftab, key, keylen); + if (current) { + ErrorInfo *ei = error_info_create(line, ET_PREPROC, ED_PP_DUPLICATE); + error_info_append(ei, current->line); + return ei; + } + + ASMArgImmediate imm; + ASMArgParseInfo info = { + .arg = line->data + i, .size = line->length - i, .deftable = deftab}; + if (!argparse_immediate(&imm, info)) + return error_info_create(line, ET_PREPROC, ED_PP_BAD_ARG); + + ASMDefine *define = malloc(sizeof(ASMDefine)); + if (!define) + OUT_OF_MEMORY() + + if (!(define->name = strndup(key, keylen))) + OUT_OF_MEMORY() + + define->value = imm; + define->line = line; + asm_deftable_insert(deftab, define); + return NULL; +} + +/* + Handle an undefine directive by remove an entry in the define table. + + Return NULL on success and an ErrorInfo object on failure. +*/ +static ErrorInfo* handle_undef_directive( + const ASMLine *line, ASMDefineTable *deftab) +{ + if (!DIRECTIVE_HAS_ARG(line, DIR_UNDEF)) + return error_info_create(line, ET_PREPROC, ED_PP_NO_ARG); + + size_t offset = DIRECTIVE_OFFSET(line, DIR_UNDEF) + 1; + const char *arg = line->data + offset; + size_t size = line->length - offset, i; + + for (i = 0; i < size; i++) { + if (!is_valid_symbol_char(arg[i], i == 0)) + return error_info_create(line, ET_PREPROC, ED_PP_BAD_ARG); + } + + asm_deftable_remove(deftab, arg, size); + return NULL; +} + +/* Handle an origin directive by updating the offset. Return NULL on success and an ErrorInfo object on failure. @@ -410,10 +489,12 @@ ErrorInfo* tokenize(AssemblerState *state) } else if (IS_LOCAL_DIRECTIVE(line)) { if (IS_DIRECTIVE(line, DIR_DEFINE)) { - // TODO + if ((ei = handle_define_directive(line, deftab))) + goto cleanup; } else if (IS_DIRECTIVE(line, DIR_UNDEF)) { - // TODO + if ((ei = handle_undef_directive(line, deftab))) + goto cleanup; } else if (IS_DIRECTIVE(line, DIR_ORIGIN)) { if ((ei = handle_origin_directive(line, &offset))) diff --git a/src/util.c b/src/util.c index feb25eb..7c7b409 100644 --- a/src/util.c +++ b/src/util.c @@ -54,6 +54,15 @@ uint64_t get_time_ns() } /* + Return whether the given character is valid in a symbol (label, define). +*/ +bool is_valid_symbol_char(char c, bool first) +{ + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || + (!first && c >= '0' && c <= '9') || c == '_' || c == '.'; +} + +/* Return the name of the region encoded by the given region code. The given code should not be larger than one nibble. NULL is returned if diff --git a/src/util.h b/src/util.h index ad14235..64f21c9 100644 --- a/src/util.h +++ b/src/util.h @@ -3,6 +3,7 @@ #pragma once +#include #include #define INVALID_SIZE_CODE 0x8 @@ -12,6 +13,7 @@ uint8_t bcd_encode(uint8_t); uint8_t bcd_decode(uint8_t); uint64_t get_time_ns(); +bool is_valid_symbol_char(char, bool); const char* region_code_to_string(uint8_t); uint8_t region_string_to_code(const char*); size_t size_code_to_bytes(uint8_t);