Files
gb-emu/src/gbasm/opcodes.c

357 lines
8.0 KiB
C

#include "gbasm/gb_types.h"
#include "gbasm/opcodes.h"
#include "gbasm/errors.h"
#include "common/tri.h"
#include "common/common.h"
#define MAX_OPCODE_LEN 10 /*TODO: Check that's enough */
static bool opcodes_initted = false;
static struct tri opcode_tri;
static bool gbasm_argtype_in_set(uint32_t argtype_set, uint32_t argtype)
{
return !!(argtype & argtype_set);
}
static bool imm_is_8_bit(int val)
{
/* Allow signed or unsigned representation */
return (val <= 255) && (val >= -128);
}
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) \
static 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})
static 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;
}
static int ld_check(const struct gbasm_parsed_inst *inst,
const struct gbasm_op_info *op_info)
{
if (inst->num_operands < 2) {
gbasm_too_many_args_error(inst, op_info);
}
if (inst->num_operands > 2) {
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);
}
if (inst->operands[0].type == GBASM_OPERAND_REG_8) {
if (!imm_is_8_bit(inst->operands[1].imm.value)) {
DEBUG_LOG("inst->operands[1].imm.value = %d",
inst->operands[1].imm.value);
gbasm_invalid_imm_error(inst, op_info);
}
}
return 0;
}
static 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;
case GBASM_OPERAND_R8_HL_DEREF: opcode = 0x34; 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;
}
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;
case GBASM_OPERAND_R8_HL_DEREF: opcode = 0x35; 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;
}
break;
default:
ASSERT(0);
}
emit(emitter, &opcode, 1);
return 1;
}
size_t ld_emit(struct emitter *emitter,
const struct gbasm_parsed_inst *inst)
{
uint8_t opcode;
uint8_t imm8;
if (inst->operands[0].type == GBASM_OPERAND_REG_8) {
switch (inst->operands[1].type) {
case GBASM_OPERAND_REG_8:
opcode = 0x40;
opcode += inst->operands[0].r8.type * 8;
opcode += inst->operands[1].r8.type;
emit(emitter, &opcode, 1);
return 1;
case GBASM_OPERAND_IMM:
imm8 = inst->operands[1].imm.value;
opcode = 0x06;
opcode += inst->operands[0].r8.type * 8;
emit(emitter, &opcode, 1);
emit(emitter, &imm8, 1);
return 2;
default:
break;
}
}
return -1;
}
struct gbasm_op_info gbasm_op_infos[] = {
{
.opcode = "nop",
.operand_types = {0},
.check = check_no_args,
.length = length_one_byte,
.emit = nop_emit,
},
{
.opcode = "halt",
.operand_types = {0},
.check = check_no_args,
.length = length_one_byte,
.emit = halt_emit,
},
{
.opcode = "stop",
.operand_types = {0},
.check = check_no_args,
.length = length_one_byte,
.emit = stop_emit,
},
{
.opcode = "di",
.operand_types = {0},
.check = check_no_args,
.length = length_one_byte,
.emit = di_emit,
},
{
.opcode = "ei",
.operand_types = {0},
.check = check_no_args,
.length = length_one_byte,
.emit = ei_emit,
},
{
.opcode = "rla",
.operand_types = {0},
.check = check_no_args,
.length = length_one_byte,
.emit = rla_emit,
},
{
.opcode = "rra",
.operand_types = {0},
.check = check_no_args,
.length = length_one_byte,
.emit = rra_emit,
},
{
.opcode = "rlca",
.operand_types = {0},
.check = check_no_args,
.length = length_one_byte,
.emit = rlca_emit,
},
{
.opcode = "rrca",
.operand_types = {0},
.check = check_no_args,
.length = length_one_byte,
.emit = rrca_emit,
},
{
.opcode = "daa",
.operand_types = {0},
.check = check_no_args,
.length = length_one_byte,
.emit = daa_emit,
},
{
.opcode = "scf",
.operand_types = {0},
.check = check_no_args,
.length = length_one_byte,
.emit = scf_emit,
},
{
.opcode = "reti",
.operand_types = {0},
.check = check_no_args,
.length = length_one_byte,
.emit = reti_emit,
},
{
.opcode = "cpl",
.operand_types = {0},
.check = check_no_args,
.length = length_one_byte,
.emit = cpl_emit,
},
{
.opcode = "ccf",
.operand_types = {0},
.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,
},
{
.opcode = "ld",
/* support all of the other operands */
.operand_types = { GBASM_OPERAND_REG_8,
GBASM_OPERAND_REG_8 |
GBASM_OPERAND_IMM },
.check = ld_check,
.length = length_one_byte,
.emit = ld_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);
}