diff --git a/src/assembler/directives.h b/src/assembler/directives.h index 9e51bd7..b83a71d 100644 --- a/src/assembler/directives.h +++ b/src/assembler/directives.h @@ -6,7 +6,7 @@ #include #define DIRECTIVE_MARKER '.' -#define NUM_DIRECTIVES 16 +#define NUM_DIRECTIVES 18 #define DIR_INCLUDE ".include" @@ -19,6 +19,8 @@ #define DIR_ROM_DECLSIZE ".rom_declsize" #define DIR_CROSS_BLOCKS ".cross_blocks" +#define DIR_DEFINE ".define" +#define DIR_UNDEF ".undef" #define DIR_ORIGIN ".org" #define DIR_BLOCK ".block" #define DIR_BYTE ".byte" @@ -35,9 +37,10 @@ (!DIRECTIVE_HAS_ARG(line, d) || (line)->data[strlen(d)] == ' ')) #define IS_LOCAL_DIRECTIVE(line) \ - (IS_DIRECTIVE(line, DIR_ORIGIN) || IS_DIRECTIVE(line, DIR_BLOCK) || \ - IS_DIRECTIVE(line, DIR_BYTE) || IS_DIRECTIVE(line, DIR_SPACE) || \ - IS_DIRECTIVE(line, DIR_ASCII) || IS_DIRECTIVE(line, DIR_ASCIZ) || \ + (IS_DIRECTIVE(line, DIR_DEFINE) || IS_DIRECTIVE(line, DIR_UNDEF) || \ + IS_DIRECTIVE(line, DIR_ORIGIN) || IS_DIRECTIVE(line, DIR_BLOCK) || \ + IS_DIRECTIVE(line, DIR_BYTE) || IS_DIRECTIVE(line, DIR_SPACE) || \ + IS_DIRECTIVE(line, DIR_ASCII) || IS_DIRECTIVE(line, DIR_ASCIZ) || \ IS_DIRECTIVE(line, DIR_ASCIIZ)) #define DIRECTIVE_OFFSET(line, d) \ diff --git a/src/assembler/hash_table.c b/src/assembler/hash_table.c new file mode 100644 index 0000000..c8e57e7 --- /dev/null +++ b/src/assembler/hash_table.c @@ -0,0 +1,104 @@ +/* Copyright (C) 2014-2015 Ben Kurtovic + Released under the terms of the MIT License. See LICENSE for details. */ + +#include +#include + +#include "hash_table.h" +#include "../logging.h" + +#define INITIAL_BUCKETS 127 + +#define GET_FIELD_(obj, offset, type) (*((type*) (((char*) obj) + offset))) + +#define NODE_KEY(tab, node) GET_FIELD_(node, tab->key_offset, char*) +#define NEXT_NODE(tab, node) GET_FIELD_(node, tab->next_offset, HashNode*) + +/* Internal structs */ + +struct HashNode { + char *key; + HashNode *next; +}; + +/* + Hash a string key into a hash table bucket index. + + This uses the djb2 algorithm: http://www.cse.yorku.ca/~oz/hash.html +*/ +static inline size_t hash_key(const HashTable *table, const char *key) +{ + size_t hash = 5381; + while (*key) + hash = ((hash << 5) + hash) + *(key++); + return hash % table->buckets; +} + +/* + Create and return a new HashTable. +*/ +HashTable* hash_table_new(size_t key_offset, size_t next_offset) +{ + HashTable *table; + if (!(table = malloc(sizeof(HashTable)))) + OUT_OF_MEMORY() + + if (!(table->nodes = calloc(INITIAL_BUCKETS, sizeof(HashNode*)))) + OUT_OF_MEMORY() + + table->buckets = INITIAL_BUCKETS; + table->key_offset = key_offset; + table->next_offset = next_offset; + return table; +} + +/* + Deallocate a HashTable. This function does nothing if the table is NULL. + + The given callback function is called on each node as it is removed from + the table. Typically, it will free() the node's members and then free() + the node itself. +*/ +void hash_table_free(HashTable *table, HashFreeCallback callback) +{ + if (!table) + return; + + for (size_t bucket = 0; bucket < table->buckets; bucket++) { + HashNode *node = table->nodes[bucket]; + while (node) { + HashNode *temp = NEXT_NODE(table, node); + callback(node); + node = temp; + } + } + free(table); +} + +/* + Search for a key in the hash table. + + Return the corresponding node on success and NULL on failure. +*/ +const HashNode* hash_table_find(const HashTable *table, const char *key) +{ + HashNode *node = table->nodes[hash_key(table, key)]; + while (node) { + if (!strcmp(key, NODE_KEY(table, node))) + return node; + node = NEXT_NODE(table, node); + } + return NULL; +} + +/* + Insert a node into the table. + + This doesn't check for duplicate keys, so you must do that beforehand. +*/ +void hash_table_insert(HashTable *table, HashNode *node) +{ + size_t index = hash_key(table, NODE_KEY(table, node)); + NEXT_NODE(table, node) = table->nodes[index]; + table->nodes[index] = node; +} diff --git a/src/assembler/hash_table.h b/src/assembler/hash_table.h new file mode 100644 index 0000000..306c373 --- /dev/null +++ b/src/assembler/hash_table.h @@ -0,0 +1,29 @@ +/* Copyright (C) 2014-2015 Ben Kurtovic + Released under the terms of the MIT License. See LICENSE for details. */ + +#pragma once + +#include + +#define hash_table_NEW(node, key, next) \ + hash_table_new(offsetof(node, key), offsetof(node, next)) + +/* Structs */ + +typedef struct HashNode HashNode; + +typedef struct { + HashNode **nodes; + size_t buckets; + size_t key_offset; + size_t next_offset; +} HashTable; + +typedef void (*HashFreeCallback)(HashNode*); + +/* Functions */ + +HashTable* hash_table_new(size_t, size_t); +void hash_table_free(HashTable*, HashFreeCallback); +const HashNode* hash_table_find(const HashTable*, const char*); +void hash_table_insert(HashTable*, HashNode*); diff --git a/src/assembler/inst_args.h b/src/assembler/inst_args.h index be6c5d4..ef2d0e0 100644 --- a/src/assembler/inst_args.h +++ b/src/assembler/inst_args.h @@ -5,7 +5,7 @@ #include -#include "state.h" +#define MAX_SYMBOL_SIZE 256 typedef enum { AT_REGISTER, diff --git a/src/assembler/state.c b/src/assembler/state.c index ef39172..adb449a 100644 --- a/src/assembler/state.c +++ b/src/assembler/state.c @@ -45,14 +45,15 @@ void state_free(AssemblerState *state) */ void asm_symtable_init(ASMSymbolTable **symtable_ptr) { - ASMSymbolTable *symtable; - if (!(symtable = malloc(sizeof(ASMSymbolTable)))) - OUT_OF_MEMORY() - - for (size_t bucket = 0; bucket < SYMBOL_TABLE_BUCKETS; bucket++) - symtable->buckets[bucket] = NULL; + *symtable_ptr = hash_table_NEW(ASMSymbol, symbol, next); +} - *symtable_ptr = symtable; +/* + Create and return a new ASMDefineTable. +*/ +ASMDefineTable* asm_deftable_new() +{ + return hash_table_NEW(ASMDefine, name, next); } /* @@ -109,36 +110,37 @@ void asm_data_free(ASMData *data) } /* + Callback function for freeing an ASMSymbol. +*/ +static void free_asm_symbol(HashNode *node) +{ + free(((ASMSymbol*) node)->symbol); + free(node); +} + +/* Deallocate an ASMSymbolTable. */ void asm_symtable_free(ASMSymbolTable *symtable) { - if (!symtable) - return; - - for (size_t bucket = 0; bucket < SYMBOL_TABLE_BUCKETS; bucket++) { - ASMSymbol *sym = symtable->buckets[bucket], *temp; - while (sym) { - temp = sym->next; - free(sym->symbol); - free(sym); - sym = temp; - } - } - free(symtable); + hash_table_free(symtable, free_asm_symbol); } /* - Hash a string key into a symbol table bucket index. + Callback function for freeing an ASMDefine. +*/ +static void free_asm_define(HashNode *node) +{ + free(((ASMDefine*) node)->name); + free(node); +} - This uses the djb2 algorithm: http://www.cse.yorku.ca/~oz/hash.html +/* + Deallocate an ASMDefineTable. */ -static inline size_t hash_key(const char *key) +void asm_deftable_free(ASMDefineTable *deftable) { - size_t hash = 5381; - while (*key) - hash = ((hash << 5) + hash) + *(key++); - return hash % SYMBOL_TABLE_BUCKETS; + hash_table_free(deftable, free_asm_define); } /* @@ -148,13 +150,7 @@ static inline size_t hash_key(const char *key) */ const ASMSymbol* asm_symtable_find(const ASMSymbolTable *tab, const char *key) { - ASMSymbol *symbol = tab->buckets[hash_key(key)]; - while (symbol) { - if (!strcmp(key, symbol->symbol)) - return symbol; - symbol = symbol->next; - } - return NULL; + return (ASMSymbol*) hash_table_find(tab, key); } /* @@ -164,9 +160,27 @@ const ASMSymbol* asm_symtable_find(const ASMSymbolTable *tab, const char *key) */ void asm_symtable_insert(ASMSymbolTable *tab, ASMSymbol *symbol) { - size_t index = hash_key(symbol->symbol); - symbol->next = tab->buckets[index]; - tab->buckets[index] = symbol; + hash_table_insert(tab, (HashNode*) symbol); +} + +/* + Search for a key in the define table. + + Return the corresponding ASMDefine on success and NULL on failure. +*/ +const ASMDefine* asm_deftable_find(const ASMDefineTable *tab, const char *key) +{ + return (ASMDefine*) hash_table_find(tab, key); +} + +/* + Insert an ASMDefine into the define table. + + This doesn't check for duplicate keys, so you must do that beforehand. +*/ +void asm_deftable_insert(ASMDefineTable *tab, ASMDefine *define) +{ + hash_table_insert(tab, (HashNode*) define); } #ifdef DEBUG_MODE diff --git a/src/assembler/state.h b/src/assembler/state.h index 9746662..ad4990b 100644 --- a/src/assembler/state.h +++ b/src/assembler/state.h @@ -7,15 +7,14 @@ #include #include +#include "hash_table.h" +#include "inst_args.h" #include "../assembler.h" #define DEFAULT_HEADER_OFFSET 0x7FF0 #define DEFAULT_REGION 6 // GG Export #define DEFAULT_DECLSIZE 0xC // 32 KB -#define MAX_SYMBOL_SIZE 256 -#define SYMBOL_TABLE_BUCKETS 128 - /* Structs */ struct ASMLine { @@ -63,9 +62,16 @@ struct ASMSymbol { }; typedef struct ASMSymbol ASMSymbol; -typedef struct { - ASMSymbol *buckets[SYMBOL_TABLE_BUCKETS]; -} ASMSymbolTable; +struct ASMDefine { + ASMArgImmediate value; + char *name; + const ASMLine *line; + struct ASMDefine *next; +}; +typedef struct ASMDefine ASMDefine; + +typedef HashTable ASMSymbolTable; +typedef HashTable ASMDefineTable; typedef struct { size_t offset; @@ -92,14 +98,18 @@ typedef struct { void state_init(AssemblerState*); void state_free(AssemblerState*); void asm_symtable_init(ASMSymbolTable**); +ASMDefineTable* asm_deftable_new(); void asm_lines_free(ASMLine*); void asm_includes_free(ASMInclude*); void asm_instructions_free(ASMInstruction*); void asm_data_free(ASMData*); void asm_symtable_free(ASMSymbolTable*); +void asm_deftable_free(ASMDefineTable*); const ASMSymbol* asm_symtable_find(const ASMSymbolTable*, const char*); void asm_symtable_insert(ASMSymbolTable*, ASMSymbol*); +const ASMDefine* asm_deftable_find(const ASMDefineTable*, const char*); +void asm_deftable_insert(ASMDefineTable*, ASMDefine*); #ifdef DEBUG_MODE void asm_lines_print(const ASMLine*); diff --git a/src/assembler/tokenizer.c b/src/assembler/tokenizer.c index 27ccb27..4cfc782 100644 --- a/src/assembler/tokenizer.c +++ b/src/assembler/tokenizer.c @@ -54,6 +54,35 @@ static inline int8_t default_bank_slot(uint8_t bank) } /* + Initialize an ASMLayoutInfo object. +*/ +static void init_layout_info(ASMLayoutInfo *li, AssemblerState *state) +{ + li->size = state->rom_size ? state->rom_size : ROM_SIZE_MAX; + li->origin = NULL; + li->bank = 0; + li->cross_blocks = state->cross_blocks; + + if (!(li->overlap_table = calloc(li->size, sizeof(const ASMLine*)))) + OUT_OF_MEMORY() + + if (!(li->overlap_origins = calloc(li->size, sizeof(const ASMLine*)))) + OUT_OF_MEMORY() + + for (size_t i = 0; i < HEADER_SIZE; i++) + li->overlap_table[state->header.offset + i] = &header_sentinel; +} + +/* + Free the resources allocated by an ASMLayoutInfo object. +*/ +static void free_layout_info(ASMLayoutInfo *li) +{ + free(li->overlap_table); + free(li->overlap_origins); +} + +/* Add a given line, representing a label, to the symbol table. Return NULL on success and an ErrorInfo object on failure (e.g. in the case @@ -242,7 +271,8 @@ static ErrorInfo* parse_data( return an ErrorInfo object; *inst_ptr is not modified. */ static ErrorInfo* parse_instruction( - const ASMLine *line, ASMInstruction **inst_ptr, size_t offset) + const ASMLine *line, ASMInstruction **inst_ptr, size_t offset, + ASMDefineTable *deftab) { char mnemonic[MAX_MNEMONIC_SIZE] = {0}; size_t i = 0; @@ -271,6 +301,7 @@ static ErrorInfo* parse_instruction( if (!parser) return error_info_create(line, ET_PARSER, ED_PS_OP_UNKNOWN); + // TODO: pass deftab here? ASMErrorDesc edesc = parser(&bytes, &length, &symbol, argstart, arglen); if (edesc != ED_NONE) return error_info_create(line, ET_PARSER, edesc); @@ -354,24 +385,16 @@ static ErrorInfo* check_layout( */ ErrorInfo* tokenize(AssemblerState *state) { - ASMLayoutInfo li = { - .size = state->rom_size ? state->rom_size : ROM_SIZE_MAX, - .origin = NULL, .bank = 0, .cross_blocks = state->cross_blocks - }; - li.overlap_table = calloc(li.size, sizeof(const ASMLine*)); - li.overlap_origins = calloc(li.size, sizeof(const ASMLine*)); - if (!li.overlap_table || !li.overlap_origins) - OUT_OF_MEMORY() - ErrorInfo *ei = NULL; + ASMLayoutInfo li; + ASMSlotInfo si = {.lines = {0}}; + ASMDefineTable *deftab = asm_deftable_new(); 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; size_t offset = 0; - ASMSlotInfo si = {.lines = {0}}; - for (size_t i = 0; i < HEADER_SIZE; i++) - li.overlap_table[state->header.offset + i] = &header_sentinel; + init_layout_info(&li, state); memset(si.slots, -1, MMU_NUM_ROM_BANKS); while (line) { @@ -385,7 +408,13 @@ ErrorInfo* tokenize(AssemblerState *state) goto cleanup; } else if (IS_LOCAL_DIRECTIVE(line)) { - if (IS_DIRECTIVE(line, DIR_ORIGIN)) { + if (IS_DIRECTIVE(line, DIR_DEFINE)) { + // TODO + } + else if (IS_DIRECTIVE(line, DIR_UNDEF)) { + // TODO + } + else if (IS_DIRECTIVE(line, DIR_ORIGIN)) { if ((ei = handle_origin_directive(line, &offset))) goto cleanup; @@ -412,7 +441,7 @@ ErrorInfo* tokenize(AssemblerState *state) } } else { - if ((ei = parse_instruction(line, &inst, offset))) + if ((ei = parse_instruction(line, &inst, offset, deftab))) goto cleanup; offset += inst->loc.length; @@ -428,7 +457,7 @@ ErrorInfo* tokenize(AssemblerState *state) cleanup: state->instructions = dummy_inst.next; state->data = dummy_data.next; - free(li.overlap_table); - free(li.overlap_origins); + free_layout_info(&li); + asm_deftable_free(deftab); return ei; }