gb-emu: initial commit
Add a mostly non-functional Gameboy CPU and the skeleton of a Gameboy assembler intended for unit tests.
This commit is contained in:
212
src/gbasm/operands.c
Normal file
212
src/gbasm/operands.c
Normal file
@@ -0,0 +1,212 @@
|
||||
#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;
|
||||
}
|
||||
Reference in New Issue
Block a user