Add a mostly non-functional Gameboy CPU and the skeleton of a Gameboy assembler intended for unit tests.
213 lines
3.8 KiB
C
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;
|
|
}
|