| @@ -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; | |||
| } | |||
| @@ -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] != ':') { | |||
| @@ -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))) | |||
| @@ -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 | |||
| @@ -3,6 +3,7 @@ | |||
| #pragma once | |||
| #include <stdbool.h> | |||
| #include <stdint.h> | |||
| #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); | |||