#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); }