diff --git a/CHANGES b/CHANGES index cb2f9ebbf..dd855f024 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,7 @@ Features: - Sprite viewer - Debugging console - Improved memory viewer + - GB: LR35902/GB-Z80 disassembler Bugfixes: - LR35902: Fix core never exiting with certain event patterns - GB Timer: Improve DIV reset behavior diff --git a/include/mgba/internal/lr35902/decoder.h b/include/mgba/internal/lr35902/decoder.h new file mode 100644 index 000000000..101a104c3 --- /dev/null +++ b/include/mgba/internal/lr35902/decoder.h @@ -0,0 +1,111 @@ +/* Copyright (c) 2013-2017 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef LR35902_DECODER_H +#define LR35902_DECODER_H + +#include + +CXX_GUARD_START + +enum LR35902Condition { + LR35902_COND_NONE = 0x0, + LR35902_COND_C = 0x1, + LR35902_COND_Z = 0x2, + LR35902_COND_NC = 0x3, + LR35902_COND_NZ = 0x4 +}; + +enum LR35902Mnemonic { + LR35902_MN_ILL = 0, + LR35902_MN_ADC, + LR35902_MN_ADD, + LR35902_MN_AND, + LR35902_MN_BIT, + LR35902_MN_CALL, + LR35902_MN_CCF, + LR35902_MN_CP, + LR35902_MN_CPL, + LR35902_MN_DAA, + LR35902_MN_DEC, + LR35902_MN_DI, + LR35902_MN_EI, + LR35902_MN_HALT, + LR35902_MN_INC, + LR35902_MN_JP, + LR35902_MN_JR, + LR35902_MN_LD, + LR35902_MN_NOP, + LR35902_MN_OR, + LR35902_MN_POP, + LR35902_MN_PUSH, + LR35902_MN_RES, + LR35902_MN_RET, + LR35902_MN_RETI, + LR35902_MN_RL, + LR35902_MN_RLC, + LR35902_MN_RR, + LR35902_MN_RRC, + LR35902_MN_RST, + LR35902_MN_SBC, + LR35902_MN_SCF, + LR35902_MN_SET, + LR35902_MN_SLA, + LR35902_MN_SRA, + LR35902_MN_SRL, + LR35902_MN_STOP, + LR35902_MN_SUB, + LR35902_MN_SWAP, + LR35902_MN_XOR, + + LR35902_MN_MAX +}; + +enum LR35902Register { + LR35902_REG_B = 1, + LR35902_REG_C, + LR35902_REG_D, + LR35902_REG_E, + LR35902_REG_H, + LR35902_REG_L, + LR35902_REG_A, + LR35902_REG_F, + LR35902_REG_BC, + LR35902_REG_DE, + LR35902_REG_HL, + LR35902_REG_AF, + + LR35902_REG_SP, + LR35902_REG_PC +}; + +enum { + LR35902_OP_FLAG_IMPLICIT = 1, + LR35902_OP_FLAG_MEMORY = 2, + LR35902_OP_FLAG_INCREMENT = 4, + LR35902_OP_FLAG_DECREMENT = 8, +}; + +struct LR35902Operand { + uint8_t reg; + uint8_t flags; + uint16_t immediate; +}; + +struct LR35902InstructionInfo { + uint8_t opcode[3]; + uint8_t opcodeSize; + struct LR35902Operand op1; + struct LR35902Operand op2; + unsigned mnemonic; + unsigned condition; +}; + +size_t LR35902Decode(uint8_t opcode, struct LR35902InstructionInfo* info); +int LR35902Disassemble(struct LR35902InstructionInfo* info, char* buffer, int blen); + +CXX_GUARD_END + +#endif diff --git a/src/lr35902/debugger/cli-debugger.c b/src/lr35902/debugger/cli-debugger.c index 65d65b0b7..967faf75e 100644 --- a/src/lr35902/debugger/cli-debugger.c +++ b/src/lr35902/debugger/cli-debugger.c @@ -7,10 +7,14 @@ #include #include +#include #include static void _printStatus(struct CLIDebuggerSystem*); +static void _disassemble(struct CLIDebuggerSystem* debugger, struct CLIDebugVector* dv); +static uint32_t _printLine(struct CLIDebugger* debugger, uint16_t address, int segment); + static struct CLIDebuggerCommandSummary _lr35902Commands[] = { { 0, 0, 0, 0 } }; @@ -23,6 +27,51 @@ static inline void _printFlags(struct CLIDebuggerBackend* be, union FlagRegister f.c ? 'C' : '-'); } +static void _disassemble(struct CLIDebuggerSystem* debugger, struct CLIDebugVector* dv) { + struct LR35902Core* cpu = debugger->p->d.core->cpu; + + uint16_t address; + size_t size; + if (!dv || dv->type != CLIDV_INT_TYPE) { + address = cpu->pc; + } else { + address = dv->intValue; + dv = dv->next; + } + + if (!dv || dv->type != CLIDV_INT_TYPE) { + size = 1; + } else { + size = dv->intValue; + dv = dv->next; // TODO: Check for excess args + } + + size_t i; + for (i = 0; i < size; ++i) { + address += _printLine(debugger->p, address, -1); + } +} + +static inline uint32_t _printLine(struct CLIDebugger* debugger, uint16_t address, int segment) { + struct CLIDebuggerBackend* be = debugger->backend; + struct LR35902InstructionInfo info = {}; + char disassembly[48]; + char* disPtr = disassembly; + uint8_t instruction; + size_t bytesRemaining = 1; + for (bytesRemaining = 1; bytesRemaining; --bytesRemaining) { + instruction = debugger->d.core->rawRead8(debugger->d.core, address, segment); + disPtr += snprintf(disPtr, sizeof(disassembly) - (disPtr - disassembly), "%02X", instruction); + ++address; + bytesRemaining += LR35902Decode(instruction, &info); + }; + disPtr[0] = '\t'; + ++disPtr; + LR35902Disassemble(&info, disPtr, sizeof(disassembly) - (disPtr - disassembly)); + be->printf(be, "%s\n", disassembly); + return address; +} + static void _printStatus(struct CLIDebuggerSystem* debugger) { struct CLIDebuggerBackend* be = debugger->p->backend; struct LR35902Core* cpu = debugger->p->d.core->cpu; @@ -32,6 +81,7 @@ static void _printStatus(struct CLIDebuggerSystem* debugger) { be->printf(be, "H: %02X L: %02X (HL: %04X)\n", cpu->h, cpu->l, cpu->hl); be->printf(be, "PC: %04X SP: %04X\n", cpu->pc, cpu->sp); _printFlags(be, cpu->f); + _printLine(debugger->p, cpu->pc, -1); } static uint32_t _lookupPlatformIdentifier(struct CLIDebuggerSystem* debugger, const char* name, struct CLIDebugVector* dv) { @@ -84,7 +134,7 @@ static uint32_t _lookupPlatformIdentifier(struct CLIDebuggerSystem* debugger, co void LR35902CLIDebuggerCreate(struct CLIDebuggerSystem* debugger) { debugger->printStatus = _printStatus; - debugger->disassemble = NULL; + debugger->disassemble = _disassemble; debugger->lookupPlatformIdentifier = _lookupPlatformIdentifier; debugger->platformName = "GB-Z80"; debugger->platformCommands = _lr35902Commands; diff --git a/src/lr35902/decoder.c b/src/lr35902/decoder.c new file mode 100644 index 000000000..641af7804 --- /dev/null +++ b/src/lr35902/decoder.c @@ -0,0 +1,563 @@ +/* Copyright (c) 2013-2017 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include + +#include +#include + +typedef size_t (*LR35902Decoder)(uint8_t opcode, struct LR35902InstructionInfo* info); + +#define DEFINE_DECODER_LR35902(NAME, BODY) \ + static size_t _LR35902Decode ## NAME (uint8_t opcode, struct LR35902InstructionInfo* info) { \ + UNUSED(opcode); \ + info->mnemonic = LR35902_MN_RST; \ + BODY; \ + return 0; \ + } + +DEFINE_DECODER_LR35902(NOP, info->mnemonic = LR35902_MN_NOP;) + +#define DEFINE_LD_DECODER_LR35902_NOHL(NAME) \ + DEFINE_DECODER_LR35902(LD ## NAME ## _A, \ + info->mnemonic = LR35902_MN_LD; \ + info->op1.reg = LR35902_REG_ ## NAME; \ + info->op2.reg = LR35902_REG_A) \ + DEFINE_DECODER_LR35902(LD ## NAME ## _B, \ + info->mnemonic = LR35902_MN_LD; \ + info->op1.reg = LR35902_REG_ ## NAME; \ + info->op2.reg = LR35902_REG_B) \ + DEFINE_DECODER_LR35902(LD ## NAME ## _C, \ + info->mnemonic = LR35902_MN_LD; \ + info->op1.reg = LR35902_REG_ ## NAME; \ + info->op2.reg = LR35902_REG_C) \ + DEFINE_DECODER_LR35902(LD ## NAME ## _D, \ + info->mnemonic = LR35902_MN_LD; \ + info->op1.reg = LR35902_REG_ ## NAME; \ + info->op2.reg = LR35902_REG_D) \ + DEFINE_DECODER_LR35902(LD ## NAME ## _E, \ + info->mnemonic = LR35902_MN_LD; \ + info->op1.reg = LR35902_REG_ ## NAME; \ + info->op2.reg = LR35902_REG_E) \ + DEFINE_DECODER_LR35902(LD ## NAME ## _H, \ + info->mnemonic = LR35902_MN_LD; \ + info->op1.reg = LR35902_REG_ ## NAME; \ + info->op2.reg = LR35902_REG_H) \ + DEFINE_DECODER_LR35902(LD ## NAME ## _L, \ + info->mnemonic = LR35902_MN_LD; \ + info->op1.reg = LR35902_REG_ ## NAME; \ + info->op2.reg = LR35902_REG_L) + +#define DEFINE_LD_DECODER_LR35902_MEM(NAME, REG) \ + DEFINE_DECODER_LR35902(LD ## NAME ## _ ## REG, info->mnemonic = LR35902_MN_LD; \ + info->op1.reg = LR35902_REG_ ## A; \ + info->op2.reg = LR35902_REG_ ## REG; \ + info->op2.flags = LR35902_OP_FLAG_MEMORY;) + +#define DEFINE_LD_DECODER_LR35902_MEM_2(NAME, REG) \ + DEFINE_DECODER_LR35902(LD ## REG ## _ ## NAME, info->mnemonic = LR35902_MN_LD; \ + info->op1.reg = LR35902_REG_ ## REG; \ + info->op1.flags = LR35902_OP_FLAG_MEMORY; \ + info->op2.reg = LR35902_REG_ ## NAME;) + +#define DEFINE_LD_DECODER_LR35902(NAME) \ + DEFINE_LD_DECODER_LR35902_MEM(NAME, HL) \ + DEFINE_LD_DECODER_LR35902_MEM_2(NAME, HL) \ + DEFINE_DECODER_LR35902(LD ## NAME ## _, info->mnemonic = LR35902_MN_LD; \ + info->op1.reg = LR35902_REG_A; \ + info->op1.flags = LR35902_OP_FLAG_IMPLICIT; \ + return 1;) \ + DEFINE_LD_DECODER_LR35902_NOHL(NAME) + +#define DEFINE_LD_2_DECODER_LR35902(NAME) \ + DEFINE_DECODER_LR35902(LD ## NAME, info->mnemonic = LR35902_MN_LD; \ + info->op1.reg = LR35902_REG_ ## NAME; \ + return 2;) + +DEFINE_LD_DECODER_LR35902(B); +DEFINE_LD_DECODER_LR35902(C); +DEFINE_LD_DECODER_LR35902(D); +DEFINE_LD_DECODER_LR35902(E); +DEFINE_LD_DECODER_LR35902(H); +DEFINE_LD_DECODER_LR35902(L); +DEFINE_LD_DECODER_LR35902(A); +DEFINE_LD_DECODER_LR35902_MEM(A, BC); +DEFINE_LD_DECODER_LR35902_MEM(A, DE); + +DEFINE_LD_2_DECODER_LR35902(BC); +DEFINE_LD_2_DECODER_LR35902(DE); +DEFINE_LD_2_DECODER_LR35902(HL); +DEFINE_LD_2_DECODER_LR35902(SP); + +DEFINE_DECODER_LR35902(LDHL_, \ + info->mnemonic = LR35902_MN_LD; \ + info->op1.reg = LR35902_REG_HL; \ + info->op1.flags = LR35902_OP_FLAG_MEMORY; \ + return 1;) + +DEFINE_DECODER_LR35902(LDHL_SP, \ + info->mnemonic = LR35902_MN_LD; \ + info->op1.reg = LR35902_REG_HL; \ + info->op2.reg = LR35902_REG_SP; \ + return 1;) + +DEFINE_DECODER_LR35902(LDSP_HL, \ + info->mnemonic = LR35902_MN_LD; \ + info->op1.reg = LR35902_REG_SP; \ + info->op2.reg = LR35902_REG_HL;) + +DEFINE_DECODER_LR35902(LDAIOC, \ + info->mnemonic = LR35902_MN_LD; \ + info->op1.reg = LR35902_REG_A; \ + info->op2.reg = LR35902_REG_C; \ + info->op2.immediate = 0xFF00; \ + info->op2.flags = LR35902_OP_FLAG_MEMORY;) + +DEFINE_DECODER_LR35902(LDIOCA, \ + info->mnemonic = LR35902_MN_LD; \ + info->op1.reg = LR35902_REG_C; \ + info->op1.immediate = 0xFF00; \ + info->op1.flags = LR35902_OP_FLAG_MEMORY; \ + info->op2.reg = LR35902_REG_A;) + +DEFINE_DECODER_LR35902(LDAIO, \ + info->mnemonic = LR35902_MN_LD; \ + info->op1.reg = LR35902_REG_A; \ + info->op2.immediate = 0xFF00; \ + info->op2.flags = LR35902_OP_FLAG_MEMORY; \ + return 1;) + +DEFINE_DECODER_LR35902(LDIOA, \ + info->mnemonic = LR35902_MN_LD; \ + info->op1.immediate = 0xFF00; \ + info->op1.flags = LR35902_OP_FLAG_MEMORY; \ + info->op2.reg = LR35902_REG_A; \ + return 1;) + +#define DEFINE_ALU_DECODER_LR35902_NOHL(NAME) \ + DEFINE_DECODER_LR35902(NAME ## A, info->mnemonic = LR35902_MN_ ## NAME; info->op1.reg = LR35902_REG_A) \ + DEFINE_DECODER_LR35902(NAME ## B, info->mnemonic = LR35902_MN_ ## NAME; info->op1.reg = LR35902_REG_B) \ + DEFINE_DECODER_LR35902(NAME ## C, info->mnemonic = LR35902_MN_ ## NAME; info->op1.reg = LR35902_REG_C) \ + DEFINE_DECODER_LR35902(NAME ## D, info->mnemonic = LR35902_MN_ ## NAME; info->op1.reg = LR35902_REG_D) \ + DEFINE_DECODER_LR35902(NAME ## E, info->mnemonic = LR35902_MN_ ## NAME; info->op1.reg = LR35902_REG_E) \ + DEFINE_DECODER_LR35902(NAME ## H, info->mnemonic = LR35902_MN_ ## NAME; info->op1.reg = LR35902_REG_H) \ + DEFINE_DECODER_LR35902(NAME ## L, info->mnemonic = LR35902_MN_ ## NAME; info->op1.reg = LR35902_REG_L) + +#define DEFINE_ALU_DECODER_LR35902_MEM(NAME, REG) \ + DEFINE_DECODER_LR35902(NAME ## REG, info->mnemonic = LR35902_MN_ ## NAME; \ + info->op1.reg = LR35902_REG_HL; \ + info->op1.flags = LR35902_OP_FLAG_MEMORY;) + +#define DEFINE_ALU_DECODER_LR35902(NAME) \ + DEFINE_ALU_DECODER_LR35902_MEM(NAME, HL) \ + DEFINE_DECODER_LR35902(NAME, info->mnemonic = LR35902_MN_ ## NAME; \ + info->op1.reg = LR35902_REG_A; \ + info->op1.flags = LR35902_OP_FLAG_IMPLICIT; \ + return 1;) \ + DEFINE_ALU_DECODER_LR35902_NOHL(NAME) + +DEFINE_ALU_DECODER_LR35902_NOHL(INC); +DEFINE_ALU_DECODER_LR35902_NOHL(DEC); +DEFINE_ALU_DECODER_LR35902(AND); +DEFINE_ALU_DECODER_LR35902(XOR); +DEFINE_ALU_DECODER_LR35902(OR); +DEFINE_ALU_DECODER_LR35902(CP); +DEFINE_ALU_DECODER_LR35902(ADD); +DEFINE_ALU_DECODER_LR35902(ADC); +DEFINE_ALU_DECODER_LR35902(SUB); +DEFINE_ALU_DECODER_LR35902(SBC); + +#define DEFINE_ALU_DECODER_LR35902_ADD_HL(REG) \ + DEFINE_DECODER_LR35902(ADDHL_ ## REG, info->mnemonic = LR35902_MN_ADD; \ + info->op1.reg = LR35902_REG_HL; \ + info->op2.reg = LR35902_REG_ ## REG;) + +DEFINE_ALU_DECODER_LR35902_ADD_HL(BC) +DEFINE_ALU_DECODER_LR35902_ADD_HL(DE) +DEFINE_ALU_DECODER_LR35902_ADD_HL(HL) +DEFINE_ALU_DECODER_LR35902_ADD_HL(SP) + +DEFINE_DECODER_LR35902(ADDSP, info->mnemonic = LR35902_MN_ADD; \ + info->op1.reg = LR35902_REG_SP; \ + return 1;) + +#define DEFINE_CONDITIONAL_DECODER_LR35902(NAME) \ + DEFINE_ ## NAME ## _INSTRUCTION_LR35902(, 0) \ + DEFINE_ ## NAME ## _INSTRUCTION_LR35902(C, LR35902_COND_C) \ + DEFINE_ ## NAME ## _INSTRUCTION_LR35902(Z, LR35902_COND_Z) \ + DEFINE_ ## NAME ## _INSTRUCTION_LR35902(NC, LR35902_COND_NC) \ + DEFINE_ ## NAME ## _INSTRUCTION_LR35902(NZ, LR35902_COND_NZ) + +#define DEFINE_JP_INSTRUCTION_LR35902(CONDITION_NAME, CONDITION) \ + DEFINE_DECODER_LR35902(JP ## CONDITION_NAME, \ + info->mnemonic = LR35902_MN_JP; \ + info->condition = CONDITION; \ + return 2;) + +#define DEFINE_JR_INSTRUCTION_LR35902(CONDITION_NAME, CONDITION) \ + DEFINE_DECODER_LR35902(JR ## CONDITION_NAME, \ + info->mnemonic = LR35902_MN_JR; \ + info->condition = CONDITION; \ + return 1;) + +#define DEFINE_CALL_INSTRUCTION_LR35902(CONDITION_NAME, CONDITION) \ + DEFINE_DECODER_LR35902(CALL ## CONDITION_NAME, \ + info->mnemonic = LR35902_MN_CALL; \ + info->condition = CONDITION; \ + return 2;) + +#define DEFINE_RET_INSTRUCTION_LR35902(CONDITION_NAME, CONDITION) \ + DEFINE_DECODER_LR35902(RET ## CONDITION_NAME, \ + info->mnemonic = LR35902_MN_RET; \ + info->condition = CONDITION;) + +DEFINE_CONDITIONAL_DECODER_LR35902(JP); +DEFINE_CONDITIONAL_DECODER_LR35902(JR); +DEFINE_CONDITIONAL_DECODER_LR35902(CALL); +DEFINE_CONDITIONAL_DECODER_LR35902(RET); + +DEFINE_DECODER_LR35902(JPHL, \ + info->mnemonic = LR35902_MN_JP; \ + info->op1.reg = LR35902_REG_HL) + +DEFINE_DECODER_LR35902(RETI, info->mnemonic = LR35902_MN_RETI) + +DEFINE_DECODER_LR35902(LDBC_A, \ + info->mnemonic = LR35902_MN_LD; \ + info->op1.reg = LR35902_REG_BC; \ + info->op1.flags = LR35902_OP_FLAG_MEMORY; \ + info->op2.reg = LR35902_REG_A;) + +DEFINE_DECODER_LR35902(LDDE_A, \ + info->mnemonic = LR35902_MN_LD; \ + info->op1.reg = LR35902_REG_DE; \ + info->op1.flags = LR35902_OP_FLAG_MEMORY; \ + info->op2.reg = LR35902_REG_A;) + +DEFINE_DECODER_LR35902(LDIA, \ + info->mnemonic = LR35902_MN_LD; \ + info->op1.flags = LR35902_OP_FLAG_MEMORY; \ + info->op2.reg = LR35902_REG_A; \ + return 2;) + +DEFINE_DECODER_LR35902(LDAI, \ + info->mnemonic = LR35902_MN_LD; \ + info->op1.reg = LR35902_REG_A; \ + info->op2.flags = LR35902_OP_FLAG_MEMORY; \ + return 2;) + +DEFINE_DECODER_LR35902(LDISP, \ + info->mnemonic = LR35902_MN_LD; \ + info->op1.flags = LR35902_OP_FLAG_MEMORY; \ + info->op2.reg = LR35902_REG_SP; \ + return 2;) + +DEFINE_DECODER_LR35902(LDIHLA, \ + info->mnemonic = LR35902_MN_LD; \ + info->op1.reg = LR35902_REG_HL; \ + info->op1.flags = LR35902_OP_FLAG_INCREMENT | LR35902_OP_FLAG_MEMORY; \ + info->op2.reg = LR35902_REG_A;) + +DEFINE_DECODER_LR35902(LDDHLA, \ + info->mnemonic = LR35902_MN_LD; \ + info->op1.reg = LR35902_REG_HL; \ + info->op1.flags = LR35902_OP_FLAG_DECREMENT | LR35902_OP_FLAG_MEMORY; \ + info->op2.reg = LR35902_REG_A;) + +DEFINE_DECODER_LR35902(LDA_IHL, \ + info->mnemonic = LR35902_MN_LD; \ + info->op1.reg = LR35902_REG_A; \ + info->op2.reg = LR35902_REG_HL; \ + info->op2.flags = LR35902_OP_FLAG_INCREMENT | LR35902_OP_FLAG_MEMORY;) + +DEFINE_DECODER_LR35902(LDA_DHL, \ + info->mnemonic = LR35902_MN_LD; \ + info->op1.reg = LR35902_REG_A; \ + info->op2.reg = LR35902_REG_HL; \ + info->op2.flags = LR35902_OP_FLAG_DECREMENT | LR35902_OP_FLAG_MEMORY;) + +#define DEFINE_INCDEC_WIDE_INSTRUCTION_LR35902(REG) \ + DEFINE_DECODER_LR35902(INC ## REG, info->mnemonic = LR35902_MN_INC; info->op1.reg = LR35902_REG_ ## REG) \ + DEFINE_DECODER_LR35902(DEC ## REG, info->mnemonic = LR35902_MN_DEC; info->op1.reg = LR35902_REG_ ## REG) + +DEFINE_INCDEC_WIDE_INSTRUCTION_LR35902(BC); +DEFINE_INCDEC_WIDE_INSTRUCTION_LR35902(DE); +DEFINE_INCDEC_WIDE_INSTRUCTION_LR35902(HL); +DEFINE_INCDEC_WIDE_INSTRUCTION_LR35902(SP); + +DEFINE_DECODER_LR35902(INC_HL, + info->mnemonic = LR35902_MN_INC; + info->op1.reg = LR35902_REG_HL; + info->op1.flags = LR35902_OP_FLAG_MEMORY;) + +DEFINE_DECODER_LR35902(DEC_HL, + info->mnemonic = LR35902_MN_DEC; + info->op1.reg = LR35902_REG_HL; + info->op1.flags = LR35902_OP_FLAG_MEMORY;) + +DEFINE_DECODER_LR35902(SCF, info->mnemonic = LR35902_MN_SCF) +DEFINE_DECODER_LR35902(CCF, info->mnemonic = LR35902_MN_CCF) +DEFINE_DECODER_LR35902(CPL_, info->mnemonic = LR35902_MN_CPL) +DEFINE_DECODER_LR35902(DAA, info->mnemonic = LR35902_MN_DAA) + +#define DEFINE_POPPUSH_DECODER_LR35902(REG) \ + DEFINE_DECODER_LR35902(POP ## REG, \ + info->mnemonic = LR35902_MN_POP; \ + info->op1.reg = LR35902_REG_ ## REG;) \ + DEFINE_DECODER_LR35902(PUSH ## REG, \ + info->mnemonic = LR35902_MN_PUSH; \ + info->op1.reg = LR35902_REG_ ## REG;) \ + +DEFINE_POPPUSH_DECODER_LR35902(BC); +DEFINE_POPPUSH_DECODER_LR35902(DE); +DEFINE_POPPUSH_DECODER_LR35902(HL); +DEFINE_POPPUSH_DECODER_LR35902(AF); + +#define DEFINE_CB_2_DECODER_LR35902(NAME, BODY) \ + DEFINE_DECODER_LR35902(NAME ## B, info->op1.reg = LR35902_REG_B; BODY) \ + DEFINE_DECODER_LR35902(NAME ## C, info->op1.reg = LR35902_REG_C; BODY) \ + DEFINE_DECODER_LR35902(NAME ## D, info->op1.reg = LR35902_REG_D; BODY) \ + DEFINE_DECODER_LR35902(NAME ## E, info->op1.reg = LR35902_REG_E; BODY) \ + DEFINE_DECODER_LR35902(NAME ## H, info->op1.reg = LR35902_REG_H; BODY) \ + DEFINE_DECODER_LR35902(NAME ## L, info->op1.reg = LR35902_REG_L; BODY) \ + DEFINE_DECODER_LR35902(NAME ## HL, info->op1.reg = LR35902_REG_HL; BODY) \ + DEFINE_DECODER_LR35902(NAME ## A, info->op1.reg = LR35902_REG_A; BODY) + +#define DEFINE_CB_DECODER_LR35902(NAME, BODY) \ + DEFINE_CB_2_DECODER_LR35902(NAME ## 0, info->op2.immediate = 1; BODY) \ + DEFINE_CB_2_DECODER_LR35902(NAME ## 1, info->op2.immediate = 2; BODY) \ + DEFINE_CB_2_DECODER_LR35902(NAME ## 2, info->op2.immediate = 4; BODY) \ + DEFINE_CB_2_DECODER_LR35902(NAME ## 3, info->op2.immediate = 8; BODY) \ + DEFINE_CB_2_DECODER_LR35902(NAME ## 4, info->op2.immediate = 16; BODY) \ + DEFINE_CB_2_DECODER_LR35902(NAME ## 5, info->op2.immediate = 32; BODY) \ + DEFINE_CB_2_DECODER_LR35902(NAME ## 6, info->op2.immediate = 64; BODY) \ + DEFINE_CB_2_DECODER_LR35902(NAME ## 7, info->op2.immediate = 128; BODY) + +DEFINE_CB_DECODER_LR35902(BIT, info->mnemonic = LR35902_MN_BIT) +DEFINE_CB_DECODER_LR35902(RES, info->mnemonic = LR35902_MN_RES) +DEFINE_CB_DECODER_LR35902(SET, info->mnemonic = LR35902_MN_SET) + +#define DEFINE_CB_X_DECODER_LR35902(NAME) \ + DEFINE_CB_2_DECODER_LR35902(NAME, info->mnemonic = LR35902_MN_ ## NAME) \ + DEFINE_DECODER_LR35902(NAME ## A_, info->mnemonic = LR35902_MN_ ## NAME; info->op1.reg = LR35902_REG_A) + +DEFINE_CB_X_DECODER_LR35902(RL) +DEFINE_CB_X_DECODER_LR35902(RLC) +DEFINE_CB_X_DECODER_LR35902(RR) +DEFINE_CB_X_DECODER_LR35902(RRC) +DEFINE_CB_2_DECODER_LR35902(SLA, info->mnemonic = LR35902_MN_SLA) +DEFINE_CB_2_DECODER_LR35902(SRA, info->mnemonic = LR35902_MN_SRA) +DEFINE_CB_2_DECODER_LR35902(SRL, info->mnemonic = LR35902_MN_SRL) +DEFINE_CB_2_DECODER_LR35902(SWAP, info->mnemonic = LR35902_MN_SWAP) + +DEFINE_DECODER_LR35902(DI, info->mnemonic = LR35902_MN_DI) +DEFINE_DECODER_LR35902(EI, info->mnemonic = LR35902_MN_EI) +DEFINE_DECODER_LR35902(HALT, info->mnemonic = LR35902_MN_HALT) +DEFINE_DECODER_LR35902(ILL, info->mnemonic = LR35902_MN_ILL) +DEFINE_DECODER_LR35902(STOP, info->mnemonic = LR35902_MN_STOP; return 1) + +#define DEFINE_RST_DECODER_LR35902(VEC) \ + DEFINE_DECODER_LR35902(RST ## VEC, info->op1.immediate = 0x ## VEC;) + +DEFINE_RST_DECODER_LR35902(00); +DEFINE_RST_DECODER_LR35902(08); +DEFINE_RST_DECODER_LR35902(10); +DEFINE_RST_DECODER_LR35902(18); +DEFINE_RST_DECODER_LR35902(20); +DEFINE_RST_DECODER_LR35902(28); +DEFINE_RST_DECODER_LR35902(30); +DEFINE_RST_DECODER_LR35902(38); + +DEFINE_DECODER_LR35902(CB, return 1) + +const LR35902Decoder _lr35902DecoderTable[0x100] = { + DECLARE_LR35902_EMITTER_BLOCK(_LR35902Decode) +}; + +const LR35902Decoder _lr35902CBDecoderTable[0x100] = { + DECLARE_LR35902_CB_EMITTER_BLOCK(_LR35902Decode) +}; + +size_t LR35902Decode(uint8_t opcode, struct LR35902InstructionInfo* info) { + if (info->opcodeSize == sizeof(info->opcode)) { + return 0; + } + info->opcode[info->opcodeSize] = opcode; + LR35902Decoder decoder; + switch (info->opcodeSize) { + case 0: + decoder = _lr35902DecoderTable[opcode]; + break; + case 1: + if (info->opcode[0] == 0xCB) { + decoder = _lr35902CBDecoderTable[opcode]; + break; + } + // Fall through + case 2: + ++info->opcodeSize; + if (info->op1.reg) { + info->op2.immediate |= opcode << ((info->opcodeSize - 2) * 8); + } else { + info->op1.immediate |= opcode << ((info->opcodeSize - 2) * 8); + } + return 0; + } + ++info->opcodeSize; + return decoder(opcode, info); +} + +#define ADVANCE(AMOUNT) \ + if (AMOUNT > blen) { \ + buffer[blen - 1] = '\0'; \ + return total; \ + } \ + total += AMOUNT; \ + buffer += AMOUNT; \ + blen -= AMOUNT; + +static const char* _lr35902Conditions[] = { + NULL, + "c", + "z", + "nc", + "nz", +}; + +static const char* _lr35902Registers[] = { + "", + "b", + "c", + "d", + "e", + "h", + "l", + "a", + "f", + "bc", + "de", + "hl", + "af", + "sp", + "pc", +}; + +static const char* _lr35902MnemonicStrings[] = { + "--", + "adc", + "add", + "and", + "bit", + "call", + "ccf", + "cp", + "cpl", + "daa", + "dec", + "di", + "ei", + "halt", + "inc", + "jp", + "jr", + "ld", + "nop", + "or", + "pop", + "push", + "res", + "ret", + "reti", + "rl", + "rlc", + "rr", + "rrc", + "rst", + "sbc", + "scf", + "set", + "sla", + "sra", + "srl", + "stop", + "sub", + "swap", + "xor", + + "ill" +}; + + +static int _decodeOperand(struct LR35902Operand op, char* buffer, int blen) { + int total = 0; + if (op.flags & LR35902_OP_FLAG_IMPLICIT) { + return 0; + } + + if (op.flags & LR35902_OP_FLAG_MEMORY) { + strncpy(buffer, "(", blen - 1); + ADVANCE(1); + } + if (op.immediate) { + int written = snprintf(buffer, blen - 1, "$%02X", op.immediate); + ADVANCE(written); + if (op.reg) { + strncpy(buffer, "+", blen - 1); + ADVANCE(1); + } + } + if (op.reg) { + int written = snprintf(buffer, blen - 1, "%s", _lr35902Registers[op.reg]); + ADVANCE(written); + } + if (op.flags & LR35902_OP_FLAG_INCREMENT) { + strncpy(buffer, "+", blen - 1); + ADVANCE(1); + } + if (op.flags & LR35902_OP_FLAG_DECREMENT) { + strncpy(buffer, "-", blen - 1); + ADVANCE(1); + } + if (op.flags & LR35902_OP_FLAG_MEMORY) { + strncpy(buffer, ")", blen - 1); + ADVANCE(1); + } + return total; +} + +int LR35902Disassemble(struct LR35902InstructionInfo* info, char* buffer, int blen) { + const char* mnemonic = _lr35902MnemonicStrings[info->mnemonic]; + int written; + int total = 0; + const char* cond = _lr35902Conditions[info->condition]; + + written = snprintf(buffer, blen - 1, "%s ", mnemonic); + ADVANCE(written); + + if (cond) { + written = snprintf(buffer, blen - 1, "%s", cond); + ADVANCE(written); + + if (info->op1.reg || info->op1.immediate || info->op2.reg || info->op2.immediate) { + strncpy(buffer, ", ", blen - 1); + ADVANCE(2); + } + } + + written = _decodeOperand(info->op1, buffer, blen); + ADVANCE(written); + + if (info->op2.reg || info->op2.immediate) { + if (written) { + strncpy(buffer, ", ", blen - 1); + ADVANCE(2); + } + written = _decodeOperand(info->op2, buffer, blen); + ADVANCE(written); + } + + buffer[blen - 1] = '\0'; + return total; +}