@@ -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; | ||||
} | } |