Files
gb-emu/src/gbasm/operands.c
Max Regan 6e2f4096a2 gb-emu: initial commit
Add a mostly non-functional Gameboy CPU and the skeleton
of a Gameboy assembler intended for unit tests.
2017-05-10 22:40:12 -07:00

213 lines
3.8 KiB
C

#include "gbasm/gb_types.h"
#include "gbasm/opcodes.h"
#include "gbasm/errors.h"
#include "tri.h"
#include "common.h"
#include <string.h>
static bool operands_initted = false;
static struct tri operand_tri;
static struct tri prefixes_tri;
static const struct gbasm_operand_info gbasm_operand_infos[] = {
{
.token = "a",
.fixed = true,
.op_info = {
.type = GBASM_OPERAND_REG_8,
.r8 = { .type = GBASM_OPERAND_R8_A },
}
},
{
.token = "b",
.fixed = true,
.op_info = {
.type = GBASM_OPERAND_REG_8,
.r8 = { .type = GBASM_OPERAND_R8_B },
},
},
{
/* TODO: handle C condition code */
.token = "c",
.fixed = true,
.op_info = {
.type = GBASM_OPERAND_REG_8,
.r8 = { .type = GBASM_OPERAND_R8_C },
},
},
{
.token = "d",
.fixed = true,
.op_info = {
.type = GBASM_OPERAND_REG_8,
.r8 = { .type = GBASM_OPERAND_R8_D },
},
},
{
.token = "e",
.fixed = true,
.op_info = {
.type = GBASM_OPERAND_REG_8,
.r8 = { .type = GBASM_OPERAND_R8_E },
},
},
{
.token = "h",
.fixed = true,
.op_info = {
.type = GBASM_OPERAND_REG_8,
.r8 = { .type = GBASM_OPERAND_R8_H },
},
},
{
.token = "l",
.fixed = true,
.op_info = {
.type = GBASM_OPERAND_REG_8,
.r8 = { .type = GBASM_OPERAND_R8_L },
},
},
{
.token = "bc",
.fixed = true,
.op_info = {
.type = GBASM_OPERAND_REG_16,
.r16 = { GBASM_OPERAND_R16_BC },
},
},
{
.token = "de",
.fixed = true,
.op_info = {
.type = GBASM_OPERAND_REG_16,
.r16 = { GBASM_OPERAND_R16_DE },
},
},
{
.token = "hl",
.fixed = true,
.op_info = {
.type = GBASM_OPERAND_REG_16,
.r16 = { GBASM_OPERAND_R16_HL },
},
},
{
.token = "(hl)",
.fixed = true,
.op_info = {
.type = GBASM_OPERAND_REG_16,
.r16 = { GBASM_OPERAND_R16_HL_DEREF },
},
},
{
.token = "sp",
.fixed = true,
.op_info = {
.type = GBASM_OPERAND_REG_16,
.r16 = { GBASM_OPERAND_R16_SP },
},
},
{
.token = "nc",
.fixed = true,
.op_info = {
.type = GBASM_OPERAND_COND,
.cond = { GBASM_OPERAND_COND_NC },
},
},
{
.token = "z",
.fixed = true,
.op_info = {
.type = GBASM_OPERAND_COND,
.cond = { GBASM_OPERAND_COND_Z },
},
},
{
.token = "nz",
.fixed = true,
.op_info = {
.type = GBASM_OPERAND_COND,
.cond = { GBASM_OPERAND_COND_NZ },
},
},
};
static int convert_constant(const char *token, struct gbasm_operand *info)
{
int val = strtol(token + 1, NULL, 0);
if (val <= 0xff || val >= -0xff) {
info->type = GBASM_OPERAND_IMM_8;
info->d8.value = val;
} else if (val <= 0xffff || val >= 0xffff) {
info->type = GBASM_OPERAND_IMM_16;
info->d16.value = val;
} else {
return -1;
}
return 0;
}
static const struct gbasm_operand_prefix prefixes[] = {
{
.prefix = "$",
.convert = convert_constant,
},
};
static void init_opcode_tri()
{
int i;
tri_init(&operand_tri);
tri_init(&prefixes_tri);
for (i = 0; i < ARRAY_SIZE(gbasm_operand_infos); i++) {
tri_add_string(&operand_tri,
gbasm_operand_infos[i].token,
(void *) &gbasm_operand_infos[i]);
}
for (i = 0; i < ARRAY_SIZE(prefixes); i++) {
tri_add_string(&prefixes_tri,
prefixes[i].prefix,
(void *) &prefixes[i]);
}
operands_initted = true;
}
int gbasm_parse_operand(const char *token, struct gbasm_operand *out)
{
struct gbasm_operand_info *info;
struct gbasm_operand_prefix *prefix;
if (!operands_initted) {
init_opcode_tri();
}
info = (struct gbasm_operand_info *) tri_get_string(&operand_tri, token);
if (info != NULL) {
if (info->fixed) {
memcpy(out, &info->op_info, sizeof(*out));
} else {
info->convert(token, out);
}
return 0;
}
prefix = (struct gbasm_operand_prefix *) tri_prefix_match(&prefixes_tri, token);
if (prefix != NULL) {
prefix->convert(token, out);
return 0;
}
return -1;
}