diff --git a/src/assembler.c b/src/assembler.c index 8d4bee6..2555a3d 100644 --- a/src/assembler.c +++ b/src/assembler.c @@ -16,6 +16,9 @@ #define IS_LABEL(line) (line->data[line->length - 1] == ':') +/* Sentinel values for overlap table */ +const ASMLine header_sentinel, bounds_sentinel; + /* Add a given line, representing a label, to the symbol table. @@ -48,7 +51,21 @@ static ErrorInfo* add_label_to_table( } /* - Parse an instruction encoded in line into an ASMInstruction object. + Parse data encoded in a line into an ASMData object. + + On success, return NULL and store the instruction in *data_ptr. On failure, + return an ErrorInfo object; *data_ptr is not modified. +*/ +static ErrorInfo* parse_data( + const ASMLine *line, ASMData **data_ptr, size_t offset) +{ + // TODO + + return error_info_create(line, ET_PARSER, ED_PARSE_SYNTAX); +} + +/* + Parse an instruction encoded in a line into an ASMInstruction object. On success, return NULL and store the instruction in *inst_ptr. On failure, return an ErrorInfo object; *inst_ptr is not modified. @@ -62,6 +79,42 @@ static ErrorInfo* parse_instruction( } /* + Check if the range [offset, offset + length) overlaps with existing data. + + Return the ASMLine corresponding to the earliest data that overlaps, or + NULL if there is no overlap. +*/ +static const ASMLine* check_layout( + const ASMLine **overlap_table, size_t size, size_t offset, size_t length) +{ + if (offset + length >= size) + return &bounds_sentinel; + + for (size_t i = 0; i < length; i++) { + if (overlap_table[offset + i]) + return overlap_table[offset + i]; + } + return NULL; +} + +/* + Build and return an error message for overlapping or out-of-bounds lines. +*/ +static ErrorInfo* build_layout_error( + const ASMLine *line, const ASMLine *clash, const ASMLine *origin) +{ + ErrorInfo *ei = error_info_create(line, ET_LAYOUT, + (clash == &header_sentinel) ? ED_LYT_OVERLAP_HEAD : + (clash == &bounds_sentinel) ? ED_LYT_BOUNDS : ED_LYT_OVERLAP); + + if (origin) + error_info_append(ei, origin); + if (clash != &header_sentinel && clash != &bounds_sentinel) + error_info_append(ei, clash); + return ei; +} + +/* Tokenize ASMLines into ASMInstructions. NULL is returned on success and an ErrorInfo object is returned on failure. @@ -75,17 +128,21 @@ static ErrorInfo* tokenize(AssemblerState *state) if (!overlap_table) OUT_OF_MEMORY() - ASMLine header_indicator; - for (size_t i = 0; i < HEADER_SIZE; i++) - overlap_table[state->header.offset + i] = &header_indicator; - ErrorInfo *ei = NULL; - ASMInstruction dummy = {.next = NULL}, *inst, *prev = &dummy; - const ASMLine *line = state->lines, *origin = NULL; + ASMInstruction dummy_inst = {.next = NULL}, *inst, *prev_inst = &dummy_inst; + ASMData dummy_data = {.next = NULL}, *data, *prev_data = &dummy_data; + const ASMLine *line = state->lines, *origin = NULL, *clash; size_t offset = 0; + for (size_t i = 0; i < HEADER_SIZE; i++) + overlap_table[state->header.offset + i] = &header_sentinel; + while (line) { - if (IS_LOCAL_DIRECTIVE(line)) { + if (IS_LABEL(line)) { + if ((ei = add_label_to_table(state->symtable, line, offset))) + goto cleanup; + } + else if (IS_LOCAL_DIRECTIVE(line)) { if (IS_DIRECTIVE(line, DIR_ORIGIN)) { if (!DIRECTIVE_HAS_ARG(line, DIR_ORIGIN)) { ei = error_info_create(line, ET_PREPROC, ED_PP_NO_ARG); @@ -102,35 +159,42 @@ static ErrorInfo* tokenize(AssemblerState *state) origin = line; } else { - // TODO: first parse data item, then do same bounded check as - // with instructions below, then increment offset and - // ASMData list pointers appropriate - ei = error_info_create(line, ET_PREPROC, ED_PP_UNKNOWN); - goto cleanup; + if ((ei = parse_data(line, &data, offset))) + goto cleanup; + + offset += data->length; + prev_data->next = data; + prev_data = data; + + clash = check_layout(overlap_table, size, data->offset, data->length); + if (clash) { + ei = build_layout_error(line, clash, origin); + goto cleanup; + } + // TODO: enter data into overlap table } } - else if (IS_LABEL(line)) { - if ((ei = add_label_to_table(state->symtable, line, offset))) - goto cleanup; - } else { if ((ei = parse_instruction(line, &inst, offset))) goto cleanup; - // TODO: bounded check on range [offset, offset + inst->length) against overlap table - // if clash, use error with current line, - // then table line (if not header), - // then origin line (if non-null) - offset += inst->length; - prev->next = inst; - prev = inst; + prev_inst->next = inst; + prev_inst = inst; + + clash = check_layout(overlap_table, size, inst->offset, inst->length); + if (clash) { + ei = build_layout_error(line, clash, origin); + goto cleanup; + } + // TODO: enter inst into overlap table } line = line->next; } cleanup: - state->instructions = dummy.next; + state->instructions = dummy_inst.next; + state->data = dummy_data.next; free(overlap_table); return ei; } diff --git a/src/assembler/errors.c b/src/assembler/errors.c index 67ece6d..c76d75d 100644 --- a/src/assembler/errors.c +++ b/src/assembler/errors.c @@ -34,9 +34,9 @@ static const char *asm_error_descs[] = { "header offset exceeds given ROM size", // ED_LYT_HEADER_RANGE "declared ROM size in header exceeds actual size", // ED_LYT_DECLARE_RANGE "duplicate definitions for label", // ED_LYT_DUPE_LABELS - "location overlaps with ROM header", // ED_LYT_HEAD_OVERLAP - "location overlaps with previous instruction", // ED_LYT_INST_OVERLAP - "location overlaps with previous data", // ED_LYT_DATA_OVERLAP + "location is out of bounds for the ROM size", // ED_LYT_BOUNDS + "location overlaps with instruction or data", // ED_LYT_OVERLAP + "location overlaps with ROM header", // ED_LYT_OVERLAP_HEAD "syntax error" // ED_PARSE_SYNTAX }; diff --git a/src/assembler/errors.h b/src/assembler/errors.h index 87c3cf5..692a282 100644 --- a/src/assembler/errors.h +++ b/src/assembler/errors.h @@ -30,9 +30,9 @@ typedef enum { ED_LYT_HEADER_RANGE, ED_LYT_DECLARE_RANGE, ED_LYT_DUPE_LABELS, - ED_LYT_HEAD_OVERLAP, - ED_LYT_INST_OVERLAP, - ED_LYT_DATA_OVERLAP, + ED_LYT_BOUNDS, + ED_LYT_OVERLAP, + ED_LYT_OVERLAP_HEAD, ED_PARSE_SYNTAX } ASMErrorDesc;