@@ -4,14 +4,32 @@ | |||
#include <stdlib.h> | |||
#include "assembler.h" | |||
#include "assembler/directives.h" | |||
#include "assembler/errors.h" | |||
#include "assembler/io.h" | |||
#include "assembler/parse_util.h" | |||
#include "assembler/preprocessor.h" | |||
#include "assembler/state.h" | |||
#include "logging.h" | |||
#include "rom.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. | |||
@@ -21,25 +39,68 @@ | |||
*/ | |||
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) | |||
{ | |||
if (!state->rom_size) { | |||
state->rom_size = 32 << 10; | |||
state->rom_size = ROM_SIZE_MIN; | |||
// 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); | |||
#ifdef DEBUG_MODE | |||
asm_lines_print(state.lines); | |||
#endif | |||
if ((error_info = tokenize(&state))) | |||
goto error; | |||
@@ -6,10 +6,10 @@ | |||
#include <string.h> | |||
#define DIRECTIVE_MARKER '.' | |||
#define NUM_DIRECTIVES 10 | |||
#define NUM_DIRECTIVES 15 | |||
#define DIR_INCLUDE ".include" | |||
#define DIR_ORIGIN ".org" | |||
#define DIR_OPTIMIZER ".optimizer" | |||
#define DIR_ROM_SIZE ".rom_size" | |||
#define DIR_ROM_HEADER ".rom_header" | |||
@@ -19,6 +19,13 @@ | |||
#define DIR_ROM_REGION ".rom_region" | |||
#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 IS_DIRECTIVE(line, d) \ | |||
@@ -26,6 +33,11 @@ | |||
!strncmp((line)->data, d, 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) \ | |||
(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_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 */ | |||
struct ASMErrorLine { | |||
@@ -11,7 +11,9 @@ | |||
typedef enum { | |||
ET_INCLUDE, | |||
ET_PREPROC | |||
ET_PREPROC, | |||
ET_LAYOUT, | |||
ET_PARSER | |||
} ASMErrorType; | |||
typedef enum { | |||
@@ -24,30 +26,15 @@ typedef enum { | |||
ED_PP_NO_ARG, | |||
ED_PP_BAD_ARG, | |||
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 */ | |||
@@ -359,8 +359,8 @@ ErrorInfo* preprocess(AssemblerState *state, const LineBuffer *source) | |||
next = line->next; | |||
if (line->data[0] != DIRECTIVE_MARKER) | |||
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) | |||
@@ -426,13 +426,14 @@ ErrorInfo* preprocess(AssemblerState *state, const LineBuffer *source) | |||
} | |||
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; | |||
} | |||
if (rom_size_line && rom_declsize_line && | |||
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); | |||
goto cleanup; | |||
} | |||
@@ -101,3 +101,18 @@ void asm_symtable_free(ASMSymbolTable *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_instructions_free(ASMInstruction*); | |||
void asm_symtable_free(ASMSymbolTable*); | |||
#ifdef DEBUG_MODE | |||
void asm_lines_print(const ASMLine*); | |||
#endif |
@@ -6,6 +6,9 @@ | |||
#include <stdint.h> | |||
#include <stdlib.h> | |||
#define ROM_SIZE_MIN (32 << 10) // 32 KB | |||
#define ROM_SIZE_MAX ( 1 << 20) // 1 MB | |||
#define HEADER_SIZE 16 | |||
/* Error strings */ | |||