diff --git a/src/assembler/directives.h b/src/assembler/directives.h index 4ed54fe..f43cb51 100644 --- a/src/assembler/directives.h +++ b/src/assembler/directives.h @@ -5,17 +5,19 @@ #include -#define DIRECTIVE_MARKER '.' -#define DIR_INCLUDE ".include" -#define DIR_ORIGIN ".org" -#define DIR_OPTIMIZER ".optimizer" -#define DIR_ROM_SIZE ".rom_size" -#define DIR_ROM_HEADER ".rom_header" -#define DIR_ROM_CHECKSUM ".rom_checksum" -#define DIR_ROM_PRODUCT ".rom_product" -#define DIR_ROM_VERSION ".rom_version" -#define DIR_ROM_REGION ".rom_region" -#define DIR_ROM_DECLSIZE ".rom_declsize" +#define DIRECTIVE_MARKER '.' +#define NUM_DIRECTIVES 10 + +#define DIR_INCLUDE ".include" +#define DIR_ORIGIN ".org" +#define DIR_OPTIMIZER ".optimizer" +#define DIR_ROM_SIZE ".rom_size" +#define DIR_ROM_HEADER ".rom_header" +#define DIR_ROM_CHECKSUM ".rom_checksum" +#define DIR_ROM_PRODUCT ".rom_product" +#define DIR_ROM_VERSION ".rom_version" +#define DIR_ROM_REGION ".rom_region" +#define DIR_ROM_DECLSIZE ".rom_declsize" #define DIRECTIVE_HAS_ARG(line, d) ((line)->length > strlen(d)) @@ -26,3 +28,7 @@ #define DIRECTIVE_OFFSET(line, d) \ (DIRECTIVE_HAS_ARG(line, d) ? strlen(d) : 0) + +#define DIRECTIVE_IS_AUTO(line, d) \ + (line->length - (DIRECTIVE_OFFSET(line, d) + 1) == 4 && \ + !strncmp(line->data + (DIRECTIVE_OFFSET(line, d) + 1), "auto", 4)) diff --git a/src/assembler/parse_util.c b/src/assembler/parse_util.c index bf8bc4e..52ac542 100644 --- a/src/assembler/parse_util.c +++ b/src/assembler/parse_util.c @@ -10,10 +10,10 @@ /* Read in a boolean argument from the given line and store it in *result. - auto_val is used if the argument's value is "auto". Return true on success - and false on failure; in the latter case, *result is not modified. + Return true on success and false on failure; in the latter case, *result is + not modified. */ -bool parse_bool(bool *result, const ASMLine *line, const char *directive, bool auto_val) +bool parse_bool(bool *result, const ASMLine *line, const char *directive) { size_t offset = DIRECTIVE_OFFSET(line, directive) + 1; const char *arg = line->data + offset; @@ -35,11 +35,9 @@ bool parse_bool(bool *result, const ASMLine *line, const char *directive, bool a if (!strncmp(arg, "off", 3)) return (*result = false), true; return false; - case 4: // true, auto + case 4: // true if (!strncmp(arg, "true", 4)) return (*result = true), true; - if (!strncmp(arg, "auto", 4)) - return (*result = auto_val), true; return false; case 5: // false if (!strncmp(arg, "false", 5)) @@ -55,7 +53,7 @@ bool parse_bool(bool *result, const ASMLine *line, const char *directive, bool a Return true on success and false on failure; in the latter case, *result is not modified. */ -bool parse_uint32(uint32_t *result, const ASMLine *line, const char *directive) +bool parse_uint32_t(uint32_t *result, const ASMLine *line, const char *directive) { size_t offset = DIRECTIVE_OFFSET(line, directive) + 1; const char *str = line->data + offset; @@ -103,10 +101,10 @@ bool parse_uint32(uint32_t *result, const ASMLine *line, const char *directive) Return true on success and false on failure; in the latter case, *result is not modified. */ -bool parse_uint16(uint16_t *result, const ASMLine *line, const char *directive) +bool parse_uint16_t(uint16_t *result, const ASMLine *line, const char *directive) { uint32_t value; - if (parse_uint32(&value, line, directive) && value <= UINT16_MAX) + if (parse_uint32_t(&value, line, directive) && value <= UINT16_MAX) return (*result = value), true; return false; } @@ -117,10 +115,10 @@ bool parse_uint16(uint16_t *result, const ASMLine *line, const char *directive) Return true on success and false on failure; in the latter case, *result is not modified. */ -bool parse_uint8(uint8_t *result, const ASMLine *line, const char *directive) +bool parse_uint8_t(uint8_t *result, const ASMLine *line, const char *directive) { uint32_t value; - if (parse_uint32(&value, line, directive) && value <= UINT8_MAX) + if (parse_uint32_t(&value, line, directive) && value <= UINT8_MAX) return (*result = value), true; return false; } diff --git a/src/assembler/parse_util.h b/src/assembler/parse_util.h index db1212b..d535e57 100644 --- a/src/assembler/parse_util.h +++ b/src/assembler/parse_util.h @@ -8,7 +8,11 @@ #include "state.h" -bool parse_bool(bool*, const ASMLine*, const char*, bool); -bool parse_uint32(uint32_t*, const ASMLine*, const char*); -bool parse_uint16(uint16_t*, const ASMLine*, const char*); -bool parse_uint8(uint8_t*, const ASMLine*, const char*); +#define parse__Bool parse_bool + +/* Functions */ + +bool parse_bool(bool*, const ASMLine*, const char*); +bool parse_uint32_t(uint32_t*, const ASMLine*, const char*); +bool parse_uint16_t(uint16_t*, const ASMLine*, const char*); +bool parse_uint8_t(uint8_t*, const ASMLine*, const char*); diff --git a/src/assembler/preprocessor.c b/src/assembler/preprocessor.c index ff1f804..80abfc3 100644 --- a/src/assembler/preprocessor.c +++ b/src/assembler/preprocessor.c @@ -19,32 +19,60 @@ #define MAX_REGION_SIZE 32 -/* Helper defines for preprocess() */ +/* Helper macros for preprocess() */ -#define SAVE_ARG(line, first, oldval, newval) \ - if (first && oldval != newval) { \ - ei = error_info_create(line, ET_PREPROC, ED_PP_DUPLICATE); \ - error_info_append(ei, first); \ - goto cleanup; \ - } \ - oldval = newval; \ - first = line; - -#define FAIL_ON_COND(cond, err_desc) \ - if ((cond)) { \ +#define FAIL(err_desc) \ + { \ ei = error_info_create(line, ET_PREPROC, err_desc); \ goto cleanup; \ } -#define REQUIRE_ARG(line, d) \ - FAIL_ON_COND(!DIRECTIVE_HAS_ARG(line, d), ED_PP_NO_ARG) +#define FAIL_ON_COND(cond, err_desc) \ + if ((cond)) FAIL(err_desc) #define VALIDATE(retval) \ FAIL_ON_COND(!(retval), ED_PP_BAD_ARG) -#define RANGE_CHECK(arg, bound) \ +#define CLAMP_RANGE(bound) \ FAIL_ON_COND(arg > bound, ED_PP_ARG_RANGE) +#define GEN_PARSER_CALL(arg_type) \ + parse_##arg_type((arg_type*) &arg, line, directive) + +#define USE_PARSER(arg_type) \ + VALIDATE(GEN_PARSER_CALL(arg_type)) + +#define PARSER_BRANCH(arg_type, true_part, false_part) \ + if (GEN_PARSER_CALL(arg_type)) {true_part} else {false_part} + +#define BEGIN_DIRECTIVE_BLOCK \ + ssize_t first_ctr = -1; \ + if (0) {} + +#define BEGIN_DIRECTIVE(d, arg_type, dest_loc, auto_val) \ + else if (first_ctr++, IS_DIRECTIVE(line, d)) { \ + directive = d; \ + FAIL_ON_COND(!DIRECTIVE_HAS_ARG(line, directive), ED_PP_NO_ARG) \ + arg_type arg; \ + arg_type* dest = &(dest_loc); \ + if (DIRECTIVE_IS_AUTO(line, directive)) { \ + arg = auto_val; \ + } else { + +#define END_DIRECTIVE \ + } \ + if (firsts[first_ctr] && *dest != arg) { \ + ei = error_info_create(line, ET_PREPROC, ED_PP_DUPLICATE); \ + error_info_append(ei, firsts[first_ctr]); \ + goto cleanup; \ + } \ + *dest = arg; \ + firsts[first_ctr] = line; \ + } + +#define END_DIRECTIVE_BLOCK \ + else FAIL(ED_PP_UNKNOWN) + /* Preprocess a single source line (source, length) into a normalized ASMLine. @@ -332,86 +360,74 @@ ErrorInfo* preprocess(AssemblerState *state, const LineBuffer *source) &state->includes))) return ei; + const ASMLine *firsts[NUM_DIRECTIVES]; + for (size_t i = 0; i < NUM_DIRECTIVES; i++) + firsts[i] = NULL; + ASMLine dummy = {.next = state->lines}; ASMLine *prev, *line = &dummy, *next = state->lines, *condemned = NULL; - - const ASMLine *first_optimizer = NULL, *first_offset = NULL, - *first_checksum = NULL, *first_product = NULL, - *first_version = NULL, *first_region = NULL; + const char *directive; while ((prev = line, line = next)) { next = line->next; - if (line->data[0] == DIRECTIVE_MARKER) { - if (IS_DIRECTIVE(line, DIR_ORIGIN)) - continue; // Origins are handled by tokenizer - - DEBUG("- handling directive: %.*s", (int) line->length, line->data) - - if (IS_DIRECTIVE(line, DIR_OPTIMIZER)) { - REQUIRE_ARG(line, DIR_OPTIMIZER) - bool arg; - VALIDATE(parse_bool(&arg, line, DIR_OPTIMIZER, false)) - SAVE_ARG(line, first_optimizer, state->optimizer, arg) - } - else if (IS_DIRECTIVE(line, DIR_ROM_SIZE)) { - // TODO - // state->rom_size <-- value check - // auto - } - else if (IS_DIRECTIVE(line, DIR_ROM_HEADER)) { - REQUIRE_ARG(line, DIR_ROM_HEADER) - uint16_t arg; - VALIDATE(parse_uint16(&arg, line, DIR_ROM_HEADER)) // auto - VALIDATE(is_header_offset_valid(arg)) - SAVE_ARG(line, first_offset, state->header.offset, arg) - } - else if (IS_DIRECTIVE(line, DIR_ROM_CHECKSUM)) { - REQUIRE_ARG(line, DIR_ROM_CHECKSUM) - bool arg; - VALIDATE(parse_bool(&arg, line, DIR_ROM_CHECKSUM, true)) - SAVE_ARG(line, first_checksum, state->header.checksum, arg) - } - else if (IS_DIRECTIVE(line, DIR_ROM_PRODUCT)) { - REQUIRE_ARG(line, DIR_ROM_PRODUCT) - uint32_t arg; - VALIDATE(parse_uint32(&arg, line, DIR_ROM_PRODUCT)) // auto - RANGE_CHECK(arg, 160000) - SAVE_ARG(line, first_product, state->header.product_code, arg) - } - else if (IS_DIRECTIVE(line, DIR_ROM_VERSION)) { - REQUIRE_ARG(line, DIR_ROM_VERSION) - uint8_t arg; - VALIDATE(parse_uint8(&arg, line, DIR_ROM_VERSION)) // auto - RANGE_CHECK(arg, 0x10) - SAVE_ARG(line, first_version, state->header.version, arg) - } - else if (IS_DIRECTIVE(line, DIR_ROM_REGION)) { - REQUIRE_ARG(line, DIR_ROM_REGION) - uint8_t arg; - if (parse_uint8(&arg, line, DIR_ROM_REGION)) { // auto - RANGE_CHECK(arg, 0x10) - VALIDATE(region_code_to_string(arg)) - } else { - VALIDATE(parse_region_string(&arg, line)) - } - SAVE_ARG(line, first_region, state->header.region, arg) - } - else if (IS_DIRECTIVE(line, DIR_ROM_DECLSIZE)) { - // TODO - // state->header.rom_size <-- value/range check - // auto - } - else { - ei = error_info_create(line, ET_PREPROC, ED_PP_UNKNOWN); - goto cleanup; - } - - // Remove directive from lines, and schedule it for deletion: - line->next = condemned; - condemned = line; - prev->next = next; - line = prev; - } + if (line->data[0] != DIRECTIVE_MARKER) + continue; + if (IS_DIRECTIVE(line, DIR_ORIGIN)) + continue; // Origins are handled by tokenizer + + DEBUG("- handling directive: %.*s", (int) line->length, line->data) + + BEGIN_DIRECTIVE_BLOCK + + BEGIN_DIRECTIVE(DIR_OPTIMIZER, bool, state->optimizer, false) + USE_PARSER(bool) + END_DIRECTIVE + + BEGIN_DIRECTIVE(DIR_ROM_SIZE, size_t, state->rom_size, 0) + // TODO: fixme + FAIL(ED_PP_UNKNOWN) + END_DIRECTIVE + + BEGIN_DIRECTIVE(DIR_ROM_HEADER, size_t, state->header.offset, DEFAULT_HEADER_OFFSET) + USE_PARSER(uint16_t) + VALIDATE(is_header_offset_valid(arg)) + END_DIRECTIVE + + BEGIN_DIRECTIVE(DIR_ROM_CHECKSUM, bool, state->header.checksum, true) + USE_PARSER(bool) + END_DIRECTIVE + + BEGIN_DIRECTIVE(DIR_ROM_PRODUCT, uint32_t, state->header.product_code, 0) + USE_PARSER(uint32_t) + CLAMP_RANGE(160000) + END_DIRECTIVE + + BEGIN_DIRECTIVE(DIR_ROM_VERSION, uint8_t, state->header.version, 0) + USE_PARSER(uint8_t) + CLAMP_RANGE(0x10) + END_DIRECTIVE + + BEGIN_DIRECTIVE(DIR_ROM_REGION, uint8_t, state->header.region, DEFAULT_REGION) + PARSER_BRANCH(uint8_t, { + CLAMP_RANGE(0x10) + VALIDATE(region_code_to_string(arg)) + }, { + VALIDATE(parse_region_string(&arg, line)) + }) + END_DIRECTIVE + + BEGIN_DIRECTIVE(DIR_ROM_DECLSIZE, uint8_t, state->header.rom_size, 0) + // TODO: fixme + FAIL(ED_PP_UNKNOWN) + END_DIRECTIVE + + END_DIRECTIVE_BLOCK + + // Remove directive from lines, and schedule it for deletion: + line->next = condemned; + condemned = line; + prev->next = next; + line = prev; } // TODO: if giving rom size, check header offset is in rom size range @@ -419,6 +435,10 @@ ErrorInfo* preprocess(AssemblerState *state, const LineBuffer *source) state->rom_size = 8; // TODO + cleanup: + asm_lines_free(condemned); + state->lines = dummy.next; // Fix list head if first line was a directive + #ifdef DEBUG_MODE DEBUG("Dumping ASMLines:") const ASMLine *temp = state->lines; @@ -429,8 +449,5 @@ ErrorInfo* preprocess(AssemblerState *state, const LineBuffer *source) } #endif - cleanup: - asm_lines_free(condemned); - state->lines = dummy.next; // Fix list head if first line was a directive return ei; } diff --git a/src/assembler/state.c b/src/assembler/state.c index 5f0776a..5b79fde 100644 --- a/src/assembler/state.c +++ b/src/assembler/state.c @@ -6,10 +6,6 @@ #include "state.h" #include "io.h" #include "../logging.h" -#include "../util.h" - -#define DEFAULT_HEADER_OFFSET 0x7FF0 -#define DEFAULT_REGION "GG Export" /* Initialize default values in an AssemblerState object. @@ -20,7 +16,7 @@ void state_init(AssemblerState *state) state->header.checksum = true; state->header.product_code = 0; state->header.version = 0; - state->header.region = region_string_to_code(DEFAULT_REGION); + state->header.region = DEFAULT_REGION; state->header.rom_size = 0; state->optimizer = false; state->rom_size = 0; diff --git a/src/assembler/state.h b/src/assembler/state.h index 0202aa7..477186a 100644 --- a/src/assembler/state.h +++ b/src/assembler/state.h @@ -9,6 +9,9 @@ #include "../assembler.h" +#define DEFAULT_HEADER_OFFSET 0x7FF0 +#define DEFAULT_REGION 6 // GG Export + #define SYMBOL_TABLE_BUCKETS 128 /* Structs */