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