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