| @@ -6,7 +6,7 @@ | |||
| #include <string.h> | |||
| #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) \ | |||
| @@ -0,0 +1,104 @@ | |||
| /* Copyright (C) 2014-2015 Ben Kurtovic <ben.kurtovic@gmail.com> | |||
| Released under the terms of the MIT License. See LICENSE for details. */ | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #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; | |||
| } | |||
| @@ -0,0 +1,29 @@ | |||
| /* Copyright (C) 2014-2015 Ben Kurtovic <ben.kurtovic@gmail.com> | |||
| Released under the terms of the MIT License. See LICENSE for details. */ | |||
| #pragma once | |||
| #include <stddef.h> | |||
| #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*); | |||
| @@ -5,7 +5,7 @@ | |||
| #include <stdint.h> | |||
| #include "state.h" | |||
| #define MAX_SYMBOL_SIZE 256 | |||
| typedef enum { | |||
| AT_REGISTER, | |||
| @@ -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 | |||
| @@ -7,15 +7,14 @@ | |||
| #include <stddef.h> | |||
| #include <stdint.h> | |||
| #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*); | |||
| @@ -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; | |||
| } | |||