| @@ -4,14 +4,32 @@ | |||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| #include "assembler.h" | #include "assembler.h" | ||||
| #include "assembler/directives.h" | |||||
| #include "assembler/errors.h" | #include "assembler/errors.h" | ||||
| #include "assembler/io.h" | #include "assembler/io.h" | ||||
| #include "assembler/parse_util.h" | |||||
| #include "assembler/preprocessor.h" | #include "assembler/preprocessor.h" | ||||
| #include "assembler/state.h" | #include "assembler/state.h" | ||||
| #include "logging.h" | #include "logging.h" | ||||
| #include "rom.h" | #include "rom.h" | ||||
| #include "util.h" | #include "util.h" | ||||
| #define IS_LABEL(line) (line->data[line->length - 1] == ':') | |||||
| /* | |||||
| Parse an instruction encoded in 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. | |||||
| */ | |||||
| static ErrorInfo* parse_instruction( | |||||
| const ASMLine *line, ASMInstruction **inst_ptr, size_t offset) | |||||
| { | |||||
| // TODO | |||||
| return error_info_create(line, ET_PARSER, ED_PARSE_SYNTAX); | |||||
| } | |||||
| /* | /* | ||||
| Tokenize ASMLines into ASMInstructions. | Tokenize ASMLines into ASMInstructions. | ||||
| @@ -21,25 +39,68 @@ | |||||
| */ | */ | ||||
| static ErrorInfo* tokenize(AssemblerState *state) | static ErrorInfo* tokenize(AssemblerState *state) | ||||
| { | { | ||||
| // TODO | |||||
| size_t size = state->rom_size ? state->rom_size : ROM_SIZE_MAX; | |||||
| const ASMLine **overlap_table = calloc(size, sizeof(const ASMLine*)); | |||||
| if (!overlap_table) | |||||
| OUT_OF_MEMORY() | |||||
| // verify no instructions clash with header offset | |||||
| // if rom size is set, verify nothing overflows | |||||
| // otherwise, check nothing overflows max rom size (1 MB) | |||||
| // TODO: fill overlap table for header with pointers to a dummy object | |||||
| ErrorInfo *ei = NULL; | |||||
| ASMInstruction dummy = {.next = NULL}, *inst, *prev = &dummy; | |||||
| const ASMLine *line = state->lines, *origin = NULL; | |||||
| size_t offset = 0; | |||||
| while (line) { | |||||
| if (IS_LOCAL_DIRECTIVE(line)) { | |||||
| if (!IS_DIRECTIVE(line, DIR_ORIGIN)) { | |||||
| // TODO | |||||
| ei = error_info_create(line, ET_PREPROC, ED_PP_UNKNOWN); | |||||
| goto error; | |||||
| } | |||||
| if (!DIRECTIVE_HAS_ARG(line, DIR_ORIGIN)) { | |||||
| ei = error_info_create(line, ET_PREPROC, ED_PP_NO_ARG); | |||||
| goto error; | |||||
| } | |||||
| uint32_t arg; | |||||
| if (!parse_uint32_t(&arg, line, DIR_ORIGIN)) { | |||||
| ei = error_info_create(line, ET_PREPROC, ED_PP_BAD_ARG); | |||||
| goto error; | |||||
| } | |||||
| offset = arg; | |||||
| origin = line; | |||||
| } | |||||
| else if (IS_LABEL(line)) { | |||||
| // TODO: add to symbol table | |||||
| } | |||||
| else { | |||||
| if ((ei = parse_instruction(line, &inst, offset))) | |||||
| goto error; | |||||
| // 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; | |||||
| } | |||||
| line = line->next; | |||||
| } | |||||
| (void) state; | |||||
| state->instructions = dummy.next; | |||||
| goto cleanup; | |||||
| #ifdef DEBUG_MODE | |||||
| DEBUG("Dumping ASMLines:") | |||||
| const ASMLine *temp = state->lines; | |||||
| while (temp) { | |||||
| DEBUG("- %-40.*s [%s:%02zu]", (int) temp->length, temp->data, | |||||
| temp->filename, temp->original->lineno) | |||||
| temp = temp->next; | |||||
| } | |||||
| #endif | |||||
| error: | |||||
| asm_instructions_free(dummy.next); | |||||
| return NULL; | |||||
| cleanup: | |||||
| free(overlap_table); | |||||
| return ei; | |||||
| } | } | ||||
| /* | /* | ||||
| @@ -51,7 +112,7 @@ static ErrorInfo* tokenize(AssemblerState *state) | |||||
| static ErrorInfo* resolve_defaults(AssemblerState *state) | static ErrorInfo* resolve_defaults(AssemblerState *state) | ||||
| { | { | ||||
| if (!state->rom_size) { | if (!state->rom_size) { | ||||
| state->rom_size = 32 << 10; | |||||
| state->rom_size = ROM_SIZE_MIN; | |||||
| // TODO: use highest instruction too | // TODO: use highest instruction too | ||||
| @@ -121,6 +182,10 @@ size_t assemble(const LineBuffer *source, uint8_t **binary_ptr, ErrorInfo **ei_p | |||||
| asm_symtable_init(&state.symtable); | asm_symtable_init(&state.symtable); | ||||
| #ifdef DEBUG_MODE | |||||
| asm_lines_print(state.lines); | |||||
| #endif | |||||
| if ((error_info = tokenize(&state))) | if ((error_info = tokenize(&state))) | ||||
| goto error; | goto error; | ||||
| @@ -6,10 +6,10 @@ | |||||
| #include <string.h> | #include <string.h> | ||||
| #define DIRECTIVE_MARKER '.' | #define DIRECTIVE_MARKER '.' | ||||
| #define NUM_DIRECTIVES 10 | |||||
| #define NUM_DIRECTIVES 15 | |||||
| #define DIR_INCLUDE ".include" | #define DIR_INCLUDE ".include" | ||||
| #define DIR_ORIGIN ".org" | |||||
| #define DIR_OPTIMIZER ".optimizer" | #define DIR_OPTIMIZER ".optimizer" | ||||
| #define DIR_ROM_SIZE ".rom_size" | #define DIR_ROM_SIZE ".rom_size" | ||||
| #define DIR_ROM_HEADER ".rom_header" | #define DIR_ROM_HEADER ".rom_header" | ||||
| @@ -19,6 +19,13 @@ | |||||
| #define DIR_ROM_REGION ".rom_region" | #define DIR_ROM_REGION ".rom_region" | ||||
| #define DIR_ROM_DECLSIZE ".rom_declsize" | #define DIR_ROM_DECLSIZE ".rom_declsize" | ||||
| #define DIR_ORIGIN ".org" | |||||
| #define DIR_ALIGN ".align" | |||||
| #define DIR_BYTE ".byte" | |||||
| #define DIR_ASCII ".ascii" | |||||
| #define DIR_ASCIZ ".asciz" | |||||
| #define DIR_ASCIIZ ".asciiz" | |||||
| #define DIRECTIVE_HAS_ARG(line, d) ((line)->length > strlen(d)) | #define DIRECTIVE_HAS_ARG(line, d) ((line)->length > strlen(d)) | ||||
| #define IS_DIRECTIVE(line, d) \ | #define IS_DIRECTIVE(line, d) \ | ||||
| @@ -26,6 +33,11 @@ | |||||
| !strncmp((line)->data, d, strlen(d)) && \ | !strncmp((line)->data, d, strlen(d)) && \ | ||||
| (!DIRECTIVE_HAS_ARG(line, d) || (line)->data[strlen(d)] == ' ')) | (!DIRECTIVE_HAS_ARG(line, d) || (line)->data[strlen(d)] == ' ')) | ||||
| #define IS_LOCAL_DIRECTIVE(line) \ | |||||
| (IS_DIRECTIVE(line, DIR_ORIGIN) || IS_DIRECTIVE(line, DIR_ALIGN) || \ | |||||
| IS_DIRECTIVE(line, DIR_BYTE) || IS_DIRECTIVE(line, DIR_ASCII) || \ | |||||
| IS_DIRECTIVE(line, DIR_ASCIZ) || IS_DIRECTIVE(line, DIR_ASCIIZ)) | |||||
| #define DIRECTIVE_OFFSET(line, d) \ | #define DIRECTIVE_OFFSET(line, d) \ | ||||
| (DIRECTIVE_HAS_ARG(line, d) ? strlen(d) : 0) | (DIRECTIVE_HAS_ARG(line, d) ? strlen(d) : 0) | ||||
| @@ -11,6 +11,35 @@ | |||||
| #define ERROR_TYPE(err_info) (asm_error_types[err_info->type]) | #define ERROR_TYPE(err_info) (asm_error_types[err_info->type]) | ||||
| #define ERROR_DESC(err_info) (asm_error_descs[err_info->desc]) | #define ERROR_DESC(err_info) (asm_error_descs[err_info->desc]) | ||||
| /* Error strings */ | |||||
| static const char *asm_error_types[] = { | |||||
| "include directive", // ET_INCLUDE | |||||
| "preprocessor", // ET_PREPROC | |||||
| "memory layout", // ET_LAYOUT | |||||
| "instruction parser" // ET_PARSER | |||||
| }; | |||||
| static const char *asm_error_descs[] = { | |||||
| "missing or invalid argument", // ED_INC_BAD_ARG | |||||
| "infinite recursion detected", // ED_INC_RECURSION | |||||
| "couldn't read included file", // ED_INC_FILE_READ | |||||
| "unknown directive", // ED_PP_UNKNOWN | |||||
| "multiple values for directive", // ED_PP_DUPLICATE | |||||
| "missing argument for directive", // ED_PP_NO_ARG | |||||
| "invalid argument for directive", // ED_PP_BAD_ARG | |||||
| "directive argument out of range", // ED_PP_ARG_RANGE | |||||
| "header offset exceeds given ROM size", // ED_LYT_HEADER_RANGE | |||||
| "declared ROM size in header exceeds actual size", // ED_LYT_DECLARE_RANGE | |||||
| "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 | |||||
| "syntax error" // ED_PARSE_SYNTAX | |||||
| }; | |||||
| /* Internal structs */ | /* Internal structs */ | ||||
| struct ASMErrorLine { | struct ASMErrorLine { | ||||
| @@ -11,7 +11,9 @@ | |||||
| typedef enum { | typedef enum { | ||||
| ET_INCLUDE, | ET_INCLUDE, | ||||
| ET_PREPROC | |||||
| ET_PREPROC, | |||||
| ET_LAYOUT, | |||||
| ET_PARSER | |||||
| } ASMErrorType; | } ASMErrorType; | ||||
| typedef enum { | typedef enum { | ||||
| @@ -24,30 +26,15 @@ typedef enum { | |||||
| ED_PP_NO_ARG, | ED_PP_NO_ARG, | ||||
| ED_PP_BAD_ARG, | ED_PP_BAD_ARG, | ||||
| ED_PP_ARG_RANGE, | ED_PP_ARG_RANGE, | ||||
| ED_PP_HEADER_RANGE, | |||||
| ED_PP_DECLARE_RANGE | |||||
| } ASMErrorDesc; | |||||
| /* Strings */ | |||||
| static const char *asm_error_types[] = { | |||||
| "include directive", | |||||
| "preprocessor" | |||||
| }; | |||||
| static const char *asm_error_descs[] = { | |||||
| "missing or invalid argument", | |||||
| "infinite recursion detected", | |||||
| "couldn't read included file", | |||||
| "unknown directive", | |||||
| "multiple values for directive", | |||||
| "missing argument for directive", | |||||
| "invalid argument for directive", | |||||
| "directive argument out of range", | |||||
| "header offset exceeds given ROM size", | |||||
| "declared ROM size in header exceeds actual size" | |||||
| }; | |||||
| ED_LYT_HEADER_RANGE, | |||||
| ED_LYT_DECLARE_RANGE, | |||||
| ED_LYT_HEAD_OVERLAP, | |||||
| ED_LYT_INST_OVERLAP, | |||||
| ED_LYT_DATA_OVERLAP, | |||||
| ED_PARSE_SYNTAX | |||||
| } ASMErrorDesc; | |||||
| /* Structs */ | /* Structs */ | ||||
| @@ -359,8 +359,8 @@ ErrorInfo* preprocess(AssemblerState *state, const LineBuffer *source) | |||||
| next = line->next; | next = line->next; | ||||
| if (line->data[0] != DIRECTIVE_MARKER) | if (line->data[0] != DIRECTIVE_MARKER) | ||||
| continue; | continue; | ||||
| if (IS_DIRECTIVE(line, DIR_ORIGIN)) | |||||
| continue; // Origins are handled by tokenizer | |||||
| if (IS_LOCAL_DIRECTIVE(line)) | |||||
| continue; // "Local" directives are handled by the tokenizer | |||||
| DEBUG("- handling directive: %.*s", (int) line->length, line->data) | DEBUG("- handling directive: %.*s", (int) line->length, line->data) | ||||
| @@ -426,13 +426,14 @@ ErrorInfo* preprocess(AssemblerState *state, const LineBuffer *source) | |||||
| } | } | ||||
| if (rom_size_line && state->header.offset + HEADER_SIZE > state->rom_size) { | if (rom_size_line && state->header.offset + HEADER_SIZE > state->rom_size) { | ||||
| ei = error_info_create(rom_size_line, ET_PREPROC, ED_PP_HEADER_RANGE); | |||||
| // TODO: maybe should force offset to be explicit, otherwise autofix | |||||
| ei = error_info_create(rom_size_line, ET_LAYOUT, ED_LYT_HEADER_RANGE); | |||||
| goto cleanup; | goto cleanup; | ||||
| } | } | ||||
| if (rom_size_line && rom_declsize_line && | if (rom_size_line && rom_declsize_line && | ||||
| size_code_to_bytes(state->header.rom_size) > state->rom_size) { | size_code_to_bytes(state->header.rom_size) > state->rom_size) { | ||||
| ei = error_info_create(rom_size_line, ET_PREPROC, ED_PP_DECLARE_RANGE); | |||||
| ei = error_info_create(rom_size_line, ET_LAYOUT, ED_LYT_DECLARE_RANGE); | |||||
| error_info_append(ei, rom_declsize_line); | error_info_append(ei, rom_declsize_line); | ||||
| goto cleanup; | goto cleanup; | ||||
| } | } | ||||
| @@ -101,3 +101,18 @@ void asm_symtable_free(ASMSymbolTable *symtable) | |||||
| } | } | ||||
| free(symtable); | free(symtable); | ||||
| } | } | ||||
| #ifdef DEBUG_MODE | |||||
| /* | |||||
| DEBUG FUNCTION: Print out an ASMLine list to stdout. | |||||
| */ | |||||
| void asm_lines_print(const ASMLine *line) | |||||
| { | |||||
| DEBUG("Dumping ASMLines:") | |||||
| while (line) { | |||||
| DEBUG("- %-40.*s [%s:%02zu]", (int) line->length, line->data, | |||||
| line->filename, line->original->lineno) | |||||
| line = line->next; | |||||
| } | |||||
| } | |||||
| #endif | |||||
| @@ -80,3 +80,7 @@ void asm_lines_free(ASMLine*); | |||||
| void asm_includes_free(ASMInclude*); | void asm_includes_free(ASMInclude*); | ||||
| void asm_instructions_free(ASMInstruction*); | void asm_instructions_free(ASMInstruction*); | ||||
| void asm_symtable_free(ASMSymbolTable*); | void asm_symtable_free(ASMSymbolTable*); | ||||
| #ifdef DEBUG_MODE | |||||
| void asm_lines_print(const ASMLine*); | |||||
| #endif | |||||
| @@ -6,6 +6,9 @@ | |||||
| #include <stdint.h> | #include <stdint.h> | ||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| #define ROM_SIZE_MIN (32 << 10) // 32 KB | |||||
| #define ROM_SIZE_MAX ( 1 << 20) // 1 MB | |||||
| #define HEADER_SIZE 16 | #define HEADER_SIZE 16 | ||||
| /* Error strings */ | /* Error strings */ | ||||