| @@ -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. | Read in a boolean value and store it in *result. | ||||
| */ | */ | ||||
| bool parse_bool(bool *result, const char *arg, ssize_t size) | 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) | if (ai.size <= 0) | ||||
| return false; | return false; | ||||
| const ASMDefine *define = asm_deftable_find(ai.deftable, ai.arg, ai.size); | |||||
| if (define) { | |||||
| *result = define->value; | |||||
| return true; | |||||
| } | |||||
| bool negative = false; | bool negative = false; | ||||
| ssize_t i = 0; | ssize_t i = 0; | ||||
| @@ -318,9 +340,23 @@ bool argparse_immediate(ASMArgImmediate *result, ASMArgParseInfo ai) | |||||
| if (++i >= ai.size) | if (++i >= ai.size) | ||||
| return false; | 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; | 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; | return false; | ||||
| int32_t sval = negative ? -uval : uval; | int32_t sval = negative ? -uval : uval; | ||||
| @@ -329,28 +365,7 @@ bool argparse_immediate(ASMArgImmediate *result, ASMArgParseInfo ai) | |||||
| result->uval = uval; | result->uval = uval; | ||||
| result->sval = sval; | 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; | return true; | ||||
| } | } | ||||
| @@ -425,11 +440,8 @@ bool argparse_label(ASMArgLabel *result, ASMArgParseInfo ai) | |||||
| if (ai.size <= 0 || ai.size >= MAX_SYMBOL_SIZE) | if (ai.size <= 0 || ai.size >= MAX_SYMBOL_SIZE) | ||||
| return false; | return false; | ||||
| // Validate the label characters: | |||||
| for (const char *i = ai.arg; i < ai.arg + ai.size; i++) { | 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; | return false; | ||||
| } | } | ||||
| @@ -85,15 +85,6 @@ | |||||
| else FAIL_ON_COND_(true, ED_PP_UNKNOWN) | 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. | Functions similar memcpy, but lowercases the characters along the way. | ||||
| */ | */ | ||||
| static void memcpy_lc(char *restrict dst, const char *restrict src, size_t n) | static void memcpy_lc(char *restrict dst, const char *restrict src, size_t n) | ||||
| @@ -121,7 +112,7 @@ static size_t read_labels( | |||||
| start++; | start++; | ||||
| i = 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++; | i++; | ||||
| if (i == start || i == length || source[i] != ':') { | if (i == start || i == length || source[i] != ':') { | ||||
| @@ -12,6 +12,7 @@ | |||||
| #include "../logging.h" | #include "../logging.h" | ||||
| #include "../mmu.h" | #include "../mmu.h" | ||||
| #include "../rom.h" | #include "../rom.h" | ||||
| #include "../util.h" | |||||
| /* Internal structs */ | /* 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. | Handle an origin directive by updating the offset. | ||||
| Return NULL on success and an ErrorInfo object on failure. | Return NULL on success and an ErrorInfo object on failure. | ||||
| @@ -410,10 +489,12 @@ ErrorInfo* tokenize(AssemblerState *state) | |||||
| } | } | ||||
| else if (IS_LOCAL_DIRECTIVE(line)) { | else if (IS_LOCAL_DIRECTIVE(line)) { | ||||
| if (IS_DIRECTIVE(line, DIR_DEFINE)) { | if (IS_DIRECTIVE(line, DIR_DEFINE)) { | ||||
| // TODO | |||||
| if ((ei = handle_define_directive(line, deftab))) | |||||
| goto cleanup; | |||||
| } | } | ||||
| else if (IS_DIRECTIVE(line, DIR_UNDEF)) { | else if (IS_DIRECTIVE(line, DIR_UNDEF)) { | ||||
| // TODO | |||||
| if ((ei = handle_undef_directive(line, deftab))) | |||||
| goto cleanup; | |||||
| } | } | ||||
| else if (IS_DIRECTIVE(line, DIR_ORIGIN)) { | else if (IS_DIRECTIVE(line, DIR_ORIGIN)) { | ||||
| if ((ei = handle_origin_directive(line, &offset))) | 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. | 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 | The given code should not be larger than one nibble. NULL is returned if | ||||
| @@ -3,6 +3,7 @@ | |||||
| #pragma once | #pragma once | ||||
| #include <stdbool.h> | |||||
| #include <stdint.h> | #include <stdint.h> | ||||
| #define INVALID_SIZE_CODE 0x8 | #define INVALID_SIZE_CODE 0x8 | ||||
| @@ -12,6 +13,7 @@ | |||||
| uint8_t bcd_encode(uint8_t); | uint8_t bcd_encode(uint8_t); | ||||
| uint8_t bcd_decode(uint8_t); | uint8_t bcd_decode(uint8_t); | ||||
| uint64_t get_time_ns(); | uint64_t get_time_ns(); | ||||
| bool is_valid_symbol_char(char, bool); | |||||
| const char* region_code_to_string(uint8_t); | const char* region_code_to_string(uint8_t); | ||||
| uint8_t region_string_to_code(const char*); | uint8_t region_string_to_code(const char*); | ||||
| size_t size_code_to_bytes(uint8_t); | size_t size_code_to_bytes(uint8_t); | ||||