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:
284
src/gbasm/opcodes.c
Normal file
284
src/gbasm/opcodes.c
Normal file
@@ -0,0 +1,284 @@
|
||||
#include "gbasm/gb_types.h"
|
||||
#include "gbasm/opcodes.h"
|
||||
#include "gbasm/errors.h"
|
||||
|
||||
#include "tri.h"
|
||||
#include "common.h"
|
||||
|
||||
#define MAX_OPCODE_LEN 10 /*TODO: Check that's enough */
|
||||
|
||||
static bool opcodes_initted = false;
|
||||
static struct tri opcode_tri;
|
||||
|
||||
bool gbasm_argtype_in_set(uint32_t argtype_set, uint32_t argtype)
|
||||
{
|
||||
return !!(argtype & argtype_set);
|
||||
}
|
||||
|
||||
static int check_no_args(const struct gbasm_parsed_inst *inst,
|
||||
const struct gbasm_op_info *op_info)
|
||||
{
|
||||
if (inst->num_operands > 0) {
|
||||
gbasm_too_many_args_error(inst, op_info);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t length_one_byte(
|
||||
const struct gbasm_parsed_inst *inst,
|
||||
const struct gbasm_op_info *info)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define GEN_FIXED_EMITFN(_name, _data_array) \
|
||||
size_t _name(struct emitter *emitter, \
|
||||
const struct gbasm_parsed_inst *inst) \
|
||||
{ \
|
||||
const uint8_t raw[] = _data_array; \
|
||||
emit(emitter, raw, sizeof(raw)); \
|
||||
return sizeof(raw); \
|
||||
}
|
||||
|
||||
GEN_FIXED_EMITFN(nop_emit, {0x00})
|
||||
GEN_FIXED_EMITFN(halt_emit, {0x76})
|
||||
GEN_FIXED_EMITFN(stop_emit, {0x10})
|
||||
GEN_FIXED_EMITFN(di_emit, {0xf3})
|
||||
GEN_FIXED_EMITFN(ei_emit, {0xfb})
|
||||
GEN_FIXED_EMITFN(rla_emit, {0x17})
|
||||
GEN_FIXED_EMITFN(rra_emit, {0x1f})
|
||||
GEN_FIXED_EMITFN(rlca_emit, {0x07})
|
||||
GEN_FIXED_EMITFN(rrca_emit, {0x0f})
|
||||
GEN_FIXED_EMITFN(daa_emit, {0x27})
|
||||
GEN_FIXED_EMITFN(scf_emit, {0x37})
|
||||
GEN_FIXED_EMITFN(reti_emit, {0xd9})
|
||||
GEN_FIXED_EMITFN(cpl_emit, {0x2f})
|
||||
GEN_FIXED_EMITFN(ccf_emit, {0x3f})
|
||||
|
||||
int inc_dec_check(const struct gbasm_parsed_inst *inst,
|
||||
const struct gbasm_op_info *op_info)
|
||||
{
|
||||
if (inst->num_operands > 1) {
|
||||
gbasm_too_many_args_error(inst, op_info);
|
||||
}
|
||||
|
||||
if (inst->num_operands < 1) {
|
||||
gbasm_too_few_args_error(inst, op_info);
|
||||
}
|
||||
|
||||
if (!gbasm_argtype_in_set(op_info->operand_types[0], inst->operands[0].type)) {
|
||||
gbasm_arg_wrong_type_error(inst, op_info);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t inc_emit(struct emitter *emitter,
|
||||
const struct gbasm_parsed_inst *inst)
|
||||
{
|
||||
uint8_t opcode = 0x00;
|
||||
|
||||
switch (inst->operands[0].type) {
|
||||
case GBASM_OPERAND_REG_8:
|
||||
switch (inst->operands[0].r8.type) {
|
||||
case GBASM_OPERAND_R8_A: opcode = 0x3c; break;
|
||||
case GBASM_OPERAND_R8_B: opcode = 0x04; break;
|
||||
case GBASM_OPERAND_R8_C: opcode = 0x0c; break;
|
||||
case GBASM_OPERAND_R8_D: opcode = 0x14; break;
|
||||
case GBASM_OPERAND_R8_E: opcode = 0x1c; break;
|
||||
case GBASM_OPERAND_R8_H: opcode = 0x24; break;
|
||||
case GBASM_OPERAND_R8_L: opcode = 0x2c; break;
|
||||
}
|
||||
break;
|
||||
case GBASM_OPERAND_REG_16:
|
||||
switch (inst->operands[0].r16.type) {
|
||||
case GBASM_OPERAND_R16_BC: opcode = 0x03; break;
|
||||
case GBASM_OPERAND_R16_DE: opcode = 0x13; break;
|
||||
case GBASM_OPERAND_R16_HL: opcode = 0x23; break;
|
||||
case GBASM_OPERAND_R16_SP: opcode = 0x33; break;
|
||||
case GBASM_OPERAND_R16_HL_DEREF: opcode = 0x34; break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ASSERT(0);
|
||||
}
|
||||
|
||||
emit(emitter, &opcode, 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t dec_emit(struct emitter *emitter,
|
||||
const struct gbasm_parsed_inst *inst)
|
||||
{
|
||||
uint8_t opcode = 0x00;
|
||||
|
||||
switch (inst->operands[0].type) {
|
||||
case GBASM_OPERAND_REG_8:
|
||||
switch (inst->operands[0].r8.type) {
|
||||
case GBASM_OPERAND_R8_A: opcode = 0x3d; break;
|
||||
case GBASM_OPERAND_R8_B: opcode = 0x05; break;
|
||||
case GBASM_OPERAND_R8_C: opcode = 0x0d; break;
|
||||
case GBASM_OPERAND_R8_D: opcode = 0x15; break;
|
||||
case GBASM_OPERAND_R8_E: opcode = 0x1d; break;
|
||||
case GBASM_OPERAND_R8_H: opcode = 0x25; break;
|
||||
case GBASM_OPERAND_R8_L: opcode = 0x2d; break;
|
||||
}
|
||||
break;
|
||||
case GBASM_OPERAND_REG_16:
|
||||
switch (inst->operands[0].r16.type) {
|
||||
case GBASM_OPERAND_R16_BC: opcode = 0x0b; break;
|
||||
case GBASM_OPERAND_R16_DE: opcode = 0x1b; break;
|
||||
case GBASM_OPERAND_R16_HL: opcode = 0x2b; break;
|
||||
case GBASM_OPERAND_R16_SP: opcode = 0x3b; break;
|
||||
case GBASM_OPERAND_R16_HL_DEREF: opcode = 0x35; break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ASSERT(0);
|
||||
}
|
||||
|
||||
emit(emitter, &opcode, 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
struct gbasm_op_info gbasm_op_infos[] = {
|
||||
{
|
||||
.opcode = "nop",
|
||||
.operand_types = {},
|
||||
.check = check_no_args,
|
||||
.length = length_one_byte,
|
||||
.emit = nop_emit,
|
||||
},
|
||||
{
|
||||
.opcode = "halt",
|
||||
.operand_types = {},
|
||||
.check = check_no_args,
|
||||
.length = length_one_byte,
|
||||
.emit = halt_emit,
|
||||
},
|
||||
{
|
||||
.opcode = "stop",
|
||||
.operand_types = {},
|
||||
.check = check_no_args,
|
||||
.length = length_one_byte,
|
||||
.emit = stop_emit,
|
||||
},
|
||||
{
|
||||
.opcode = "di",
|
||||
.operand_types = {},
|
||||
.check = check_no_args,
|
||||
.length = length_one_byte,
|
||||
.emit = di_emit,
|
||||
},
|
||||
{
|
||||
.opcode = "ei",
|
||||
.operand_types = {},
|
||||
.check = check_no_args,
|
||||
.length = length_one_byte,
|
||||
.emit = ei_emit,
|
||||
},
|
||||
{
|
||||
.opcode = "rla",
|
||||
.operand_types = {},
|
||||
.check = check_no_args,
|
||||
.length = length_one_byte,
|
||||
.emit = rla_emit,
|
||||
},
|
||||
{
|
||||
.opcode = "rra",
|
||||
.operand_types = {},
|
||||
.check = check_no_args,
|
||||
.length = length_one_byte,
|
||||
.emit = rra_emit,
|
||||
},
|
||||
{
|
||||
.opcode = "rlca",
|
||||
.operand_types = {},
|
||||
.check = check_no_args,
|
||||
.length = length_one_byte,
|
||||
.emit = rlca_emit,
|
||||
},
|
||||
{
|
||||
.opcode = "rrca",
|
||||
.operand_types = {},
|
||||
.check = check_no_args,
|
||||
.length = length_one_byte,
|
||||
.emit = rrca_emit,
|
||||
},
|
||||
{
|
||||
.opcode = "daa",
|
||||
.operand_types = {},
|
||||
.check = check_no_args,
|
||||
.length = length_one_byte,
|
||||
.emit = daa_emit,
|
||||
},
|
||||
{
|
||||
.opcode = "scf",
|
||||
.operand_types = {},
|
||||
.check = check_no_args,
|
||||
.length = length_one_byte,
|
||||
.emit = scf_emit,
|
||||
},
|
||||
{
|
||||
.opcode = "reti",
|
||||
.operand_types = {},
|
||||
.check = check_no_args,
|
||||
.length = length_one_byte,
|
||||
.emit = reti_emit,
|
||||
},
|
||||
{
|
||||
.opcode = "cpl",
|
||||
.operand_types = {},
|
||||
.check = check_no_args,
|
||||
.length = length_one_byte,
|
||||
.emit = cpl_emit,
|
||||
},
|
||||
{
|
||||
.opcode = "ccf",
|
||||
.operand_types = {},
|
||||
.check = check_no_args,
|
||||
.length = length_one_byte,
|
||||
.emit = ccf_emit,
|
||||
},
|
||||
{
|
||||
.opcode = "inc",
|
||||
/* TODO: support inc (HL) */
|
||||
.operand_types = { GBASM_OPERAND_REG_8 | GBASM_OPERAND_REG_16 },
|
||||
.check = inc_dec_check,
|
||||
.length = length_one_byte,
|
||||
.emit = inc_emit,
|
||||
},
|
||||
{
|
||||
.opcode = "dec",
|
||||
/* TODO: support inc (HL) */
|
||||
.operand_types = { GBASM_OPERAND_REG_8 | GBASM_OPERAND_REG_16 },
|
||||
.check = inc_dec_check,
|
||||
.length = length_one_byte,
|
||||
.emit = dec_emit,
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
static void init_opcode_tri()
|
||||
{
|
||||
int i;
|
||||
tri_init(&opcode_tri);
|
||||
opcodes_initted = true;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(gbasm_op_infos); i++) {
|
||||
tri_add_string(&opcode_tri, gbasm_op_infos[i].opcode, &gbasm_op_infos[i]);
|
||||
}
|
||||
}
|
||||
|
||||
const struct gbasm_op_info *gbasm_get_opcode_info(const char *opcode)
|
||||
{
|
||||
if (!opcodes_initted) {
|
||||
init_opcode_tri();
|
||||
}
|
||||
|
||||
return tri_get_string(&opcode_tri, opcode);
|
||||
}
|
||||
Reference in New Issue
Block a user