Selaa lähdekoodia

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

master
Ben Kurtovic 9 vuotta sitten
vanhempi
commit
7745a9cbac
7 muutettua tiedostoa jossa 254 lisäystä ja 65 poistoa
  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 Näytä tiedosto

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


+ 104
- 0
src/assembler/hash_table.c Näytä tiedosto

@@ -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 Näytä tiedosto

@@ -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 Näytä tiedosto

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


+ 51
- 37
src/assembler/state.c Näytä tiedosto

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


+ 16
- 6
src/assembler/state.h Näytä tiedosto

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


+ 46
- 17
src/assembler/tokenizer.c Näytä tiedosto

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

Ladataan…
Peruuta
Tallenna