Browse Source

Refactor; abstract out HashTable; start support for .DEFINEs.

master
Ben Kurtovic 9 years ago
parent
commit
7745a9cbac
7 changed files with 254 additions and 65 deletions
  1. +7
    -4
      src/assembler/directives.h
  2. +104
    -0
      src/assembler/hash_table.c
  3. +29
    -0
      src/assembler/hash_table.h
  4. +1
    -1
      src/assembler/inst_args.h
  5. +51
    -37
      src/assembler/state.c
  6. +16
    -6
      src/assembler/state.h
  7. +46
    -17
      src/assembler/tokenizer.c

+ 7
- 4
src/assembler/directives.h View File

@@ -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) \


+ 104
- 0
src/assembler/hash_table.c View File

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

+ 29
- 0
src/assembler/hash_table.h View File

@@ -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*);

+ 1
- 1
src/assembler/inst_args.h View File

@@ -5,7 +5,7 @@

#include <stdint.h>

#include "state.h"
#define MAX_SYMBOL_SIZE 256

typedef enum {
AT_REGISTER,


+ 51
- 37
src/assembler/state.c View File

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


+ 16
- 6
src/assembler/state.h View File

@@ -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*);


+ 46
- 17
src/assembler/tokenizer.c View File

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

Loading…
Cancel
Save