| @@ -6,7 +6,7 @@ | |||||
| #include <string.h> | #include <string.h> | ||||
| #define DIRECTIVE_MARKER '.' | #define DIRECTIVE_MARKER '.' | ||||
| #define NUM_DIRECTIVES 16 | |||||
| #define NUM_DIRECTIVES 18 | |||||
| #define DIR_INCLUDE ".include" | #define DIR_INCLUDE ".include" | ||||
| @@ -19,6 +19,8 @@ | |||||
| #define DIR_ROM_DECLSIZE ".rom_declsize" | #define DIR_ROM_DECLSIZE ".rom_declsize" | ||||
| #define DIR_CROSS_BLOCKS ".cross_blocks" | #define DIR_CROSS_BLOCKS ".cross_blocks" | ||||
| #define DIR_DEFINE ".define" | |||||
| #define DIR_UNDEF ".undef" | |||||
| #define DIR_ORIGIN ".org" | #define DIR_ORIGIN ".org" | ||||
| #define DIR_BLOCK ".block" | #define DIR_BLOCK ".block" | ||||
| #define DIR_BYTE ".byte" | #define DIR_BYTE ".byte" | ||||
| @@ -35,9 +37,10 @@ | |||||
| (!DIRECTIVE_HAS_ARG(line, d) || (line)->data[strlen(d)] == ' ')) | (!DIRECTIVE_HAS_ARG(line, d) || (line)->data[strlen(d)] == ' ')) | ||||
| #define IS_LOCAL_DIRECTIVE(line) \ | #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)) | IS_DIRECTIVE(line, DIR_ASCIIZ)) | ||||
| #define DIRECTIVE_OFFSET(line, d) \ | #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 <stdint.h> | ||||
| #include "state.h" | |||||
| #define MAX_SYMBOL_SIZE 256 | |||||
| typedef enum { | typedef enum { | ||||
| AT_REGISTER, | AT_REGISTER, | ||||
| @@ -45,14 +45,15 @@ void state_free(AssemblerState *state) | |||||
| */ | */ | ||||
| void asm_symtable_init(ASMSymbolTable **symtable_ptr) | 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. | Deallocate an ASMSymbolTable. | ||||
| */ | */ | ||||
| void asm_symtable_free(ASMSymbolTable *symtable) | 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) | 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) | 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 | #ifdef DEBUG_MODE | ||||
| @@ -7,15 +7,14 @@ | |||||
| #include <stddef.h> | #include <stddef.h> | ||||
| #include <stdint.h> | #include <stdint.h> | ||||
| #include "hash_table.h" | |||||
| #include "inst_args.h" | |||||
| #include "../assembler.h" | #include "../assembler.h" | ||||
| #define DEFAULT_HEADER_OFFSET 0x7FF0 | #define DEFAULT_HEADER_OFFSET 0x7FF0 | ||||
| #define DEFAULT_REGION 6 // GG Export | #define DEFAULT_REGION 6 // GG Export | ||||
| #define DEFAULT_DECLSIZE 0xC // 32 KB | #define DEFAULT_DECLSIZE 0xC // 32 KB | ||||
| #define MAX_SYMBOL_SIZE 256 | |||||
| #define SYMBOL_TABLE_BUCKETS 128 | |||||
| /* Structs */ | /* Structs */ | ||||
| struct ASMLine { | struct ASMLine { | ||||
| @@ -63,9 +62,16 @@ struct ASMSymbol { | |||||
| }; | }; | ||||
| typedef struct ASMSymbol 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 { | typedef struct { | ||||
| size_t offset; | size_t offset; | ||||
| @@ -92,14 +98,18 @@ typedef struct { | |||||
| void state_init(AssemblerState*); | void state_init(AssemblerState*); | ||||
| void state_free(AssemblerState*); | void state_free(AssemblerState*); | ||||
| void asm_symtable_init(ASMSymbolTable**); | void asm_symtable_init(ASMSymbolTable**); | ||||
| ASMDefineTable* asm_deftable_new(); | |||||
| void asm_lines_free(ASMLine*); | 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_data_free(ASMData*); | void asm_data_free(ASMData*); | ||||
| void asm_symtable_free(ASMSymbolTable*); | void asm_symtable_free(ASMSymbolTable*); | ||||
| void asm_deftable_free(ASMDefineTable*); | |||||
| const ASMSymbol* asm_symtable_find(const ASMSymbolTable*, const char*); | const ASMSymbol* asm_symtable_find(const ASMSymbolTable*, const char*); | ||||
| void asm_symtable_insert(ASMSymbolTable*, ASMSymbol*); | void asm_symtable_insert(ASMSymbolTable*, ASMSymbol*); | ||||
| const ASMDefine* asm_deftable_find(const ASMDefineTable*, const char*); | |||||
| void asm_deftable_insert(ASMDefineTable*, ASMDefine*); | |||||
| #ifdef DEBUG_MODE | #ifdef DEBUG_MODE | ||||
| void asm_lines_print(const ASMLine*); | 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. | 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 | 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. | return an ErrorInfo object; *inst_ptr is not modified. | ||||
| */ | */ | ||||
| static ErrorInfo* parse_instruction( | 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}; | char mnemonic[MAX_MNEMONIC_SIZE] = {0}; | ||||
| size_t i = 0; | size_t i = 0; | ||||
| @@ -271,6 +301,7 @@ static ErrorInfo* parse_instruction( | |||||
| if (!parser) | if (!parser) | ||||
| return error_info_create(line, ET_PARSER, ED_PS_OP_UNKNOWN); | return error_info_create(line, ET_PARSER, ED_PS_OP_UNKNOWN); | ||||
| // TODO: pass deftab here? | |||||
| ASMErrorDesc edesc = parser(&bytes, &length, &symbol, argstart, arglen); | ASMErrorDesc edesc = parser(&bytes, &length, &symbol, argstart, arglen); | ||||
| if (edesc != ED_NONE) | if (edesc != ED_NONE) | ||||
| return error_info_create(line, ET_PARSER, edesc); | return error_info_create(line, ET_PARSER, edesc); | ||||
| @@ -354,24 +385,16 @@ static ErrorInfo* check_layout( | |||||
| */ | */ | ||||
| ErrorInfo* tokenize(AssemblerState *state) | 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; | ErrorInfo *ei = NULL; | ||||
| ASMLayoutInfo li; | |||||
| ASMSlotInfo si = {.lines = {0}}; | |||||
| ASMDefineTable *deftab = asm_deftable_new(); | |||||
| ASMInstruction dummy_inst = {.next = NULL}, *inst, *prev_inst = &dummy_inst; | ASMInstruction dummy_inst = {.next = NULL}, *inst, *prev_inst = &dummy_inst; | ||||
| ASMData dummy_data = {.next = NULL}, *data, *prev_data = &dummy_data; | ASMData dummy_data = {.next = NULL}, *data, *prev_data = &dummy_data; | ||||
| const ASMLine *line = state->lines; | const ASMLine *line = state->lines; | ||||
| size_t offset = 0; | 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); | memset(si.slots, -1, MMU_NUM_ROM_BANKS); | ||||
| while (line) { | while (line) { | ||||
| @@ -385,7 +408,13 @@ ErrorInfo* tokenize(AssemblerState *state) | |||||
| goto cleanup; | goto cleanup; | ||||
| } | } | ||||
| else if (IS_LOCAL_DIRECTIVE(line)) { | 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))) | if ((ei = handle_origin_directive(line, &offset))) | ||||
| goto cleanup; | goto cleanup; | ||||
| @@ -412,7 +441,7 @@ ErrorInfo* tokenize(AssemblerState *state) | |||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| if ((ei = parse_instruction(line, &inst, offset))) | |||||
| if ((ei = parse_instruction(line, &inst, offset, deftab))) | |||||
| goto cleanup; | goto cleanup; | ||||
| offset += inst->loc.length; | offset += inst->loc.length; | ||||
| @@ -428,7 +457,7 @@ ErrorInfo* tokenize(AssemblerState *state) | |||||
| cleanup: | cleanup: | ||||
| state->instructions = dummy_inst.next; | state->instructions = dummy_inst.next; | ||||
| state->data = dummy_data.next; | state->data = dummy_data.next; | ||||
| free(li.overlap_table); | |||||
| free(li.overlap_origins); | |||||
| free_layout_info(&li); | |||||
| asm_deftable_free(deftab); | |||||
| return ei; | return ei; | ||||
| } | } | ||||