@@ -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 */ | ||||