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