diff options
author | Oxore <oxore@protonmail.com> | 2023-05-08 22:28:05 +0300 |
---|---|---|
committer | Oxore <oxore@protonmail.com> | 2023-05-08 22:28:05 +0300 |
commit | 944d1d40ae18d4cc4ad78102676d866ca76c66cc (patch) | |
tree | 8118ab1e3f67a154b8032cddadeea7e1cd1829db | |
parent | c5b2c3eed16b586b0e488ede2a69bf85fd91397e (diff) |
Impl CMP and CMPA
-rw-r--r-- | disasm.cpp | 145 | ||||
-rw-r--r-- | test.bash | 23 |
2 files changed, 106 insertions, 62 deletions
@@ -1577,56 +1577,7 @@ static void disasm_divu_divs_sbcd_or( return disasm_or_and(node, instr, code, s, opsize, "or"); } -static void disasm_chunk_b(DisasmNode &n, uint16_t i, const DataBuffer &c, const Settings &s) -{ - // TODO Implement - return disasm_verbatim(n, i, c, s); -} - -static inline void disasm_exg(DisasmNode &node, uint16_t instr) -{ - assert((instr & 0x130) == 0x100); - const int m1 = (instr >> 3) & 1; - const int m2 = (instr >> 6) & 3; - assert(m2 != 0); // Therefore m == 0 and m == 1 are impossible - assert(m2 != 3); // Therefore m == 6 and m == 7 are impossible - const int m = (m2 << 1) | m1; - assert(m != 4); // Only m == 2, m == 3 and m == 5 values are allowed - const int xn = instr & 7; - const int xi = (instr >> 9) & 7; - const auto src = (m == 3) ? AddrModeArg::An(xi) : AddrModeArg::Dn(xi); - const auto dst = (m == 2) ? AddrModeArg::Dn(xn) : AddrModeArg::An(xn); - char src_str[32]{}; - char dst_str[32]{}; - src.SNPrint(src_str, sizeof(src_str)); - dst.SNPrint(dst_str, sizeof(dst_str)); - snprintf(node.mnemonic, kMnemonicBufferSize, "exg"); - snprintf(node.arguments, kArgsBufferSize, "%s,%s", src_str, dst_str); - node.size = kInstructionSizeStepBytes + src.Size() + dst.Size(); -} - -static void disasm_chunk_c( - DisasmNode &node, uint16_t instr, const DataBuffer &code, const Settings &s) -{ - if ((instr & 0x1f0) == 0x100) { - // XXX GNU AS does not know ABCD.B, it only knows ABCD, but happily - // consumes SBCD.B and others. That's why `skip_suffix` flag is needed, - // specifically for ABCD mnemonic. It is probably a bug in GNU AS. - const bool skip_size_suffix = true; - return disasm_addx_subx_abcd_sbcd(node, instr, "abcd", "", skip_size_suffix); - } - const OpSize opsize = static_cast<OpSize>((instr >> 6) & 3); - if (opsize == OpSize::kInvalid) { - return disasm_divu_divs_mulu_muls(node, instr, code, s, "mul"); - } - const unsigned m_split = instr & 0x1f8; - if (m_split == 0x188 || m_split == 0x148 || m_split == 0x140) { - return disasm_exg(node, instr); - } - return disasm_or_and(node, instr, code, s, opsize, "and"); -} - -static inline void disasm_adda_suba( +static inline void disasm_adda_suba_cmpa( DisasmNode &node, const uint16_t instr, const DataBuffer &code, const Settings &s, const char *mnemonic) { const OpSize opsize = static_cast<OpSize>(((instr >> 8) & 1) + 1); @@ -1662,18 +1613,15 @@ static inline void disasm_adda_suba( node.size = kInstructionSizeStepBytes + src.Size() + dst.Size(); } -static void disasm_add_sub_x_a( - DisasmNode &node, const uint16_t instr, const DataBuffer &code, const Settings &s, const char *mnemonic) +static void disasm_add_sub_cmp( + DisasmNode &node, + const uint16_t instr, + const DataBuffer &code, + const Settings &s, + const char *mnemonic, + const OpSize opsize, + const bool dir_to_addr) { - const OpSize opsize = static_cast<OpSize>((instr >> 6) & 3); - if (opsize == OpSize::kInvalid) { - return disasm_adda_suba(node, instr, code, s, mnemonic); - } - const bool dir_to_addr = (instr >> 8) & 1; - const unsigned m = (instr >> 3) & 7; - if (dir_to_addr && (m == 0 || m == 1)) { - return disasm_addx_subx_abcd_sbcd(node, instr, mnemonic, "x"); - } const char suffix = suffix_from_opsize(opsize); const auto addr = AddrModeArg::Fetch( node.offset + kInstructionSizeStepBytes, code, instr, suffix); @@ -1734,6 +1682,79 @@ static void disasm_add_sub_x_a( node.size = kInstructionSizeStepBytes + addr.Size() + reg.Size(); } +static void disasm_eor_cmpm_cmp_cmpa( + DisasmNode &node, const uint16_t instr, const DataBuffer &code, const Settings &s) +{ + const OpSize opsize = static_cast<OpSize>((instr >> 6) & 3); + if (opsize == OpSize::kInvalid) { + return disasm_adda_suba_cmpa(node, instr, code, s, "cmp"); + } + const bool dir_to_addr = ((instr >> 8) & 1); + if (!dir_to_addr) { + return disasm_add_sub_cmp(node, instr, code, s, "cmp", opsize, dir_to_addr); + } + // TODO Implement + return disasm_verbatim(node, instr, code, s); +} + +static inline void disasm_exg(DisasmNode &node, uint16_t instr) +{ + assert((instr & 0x130) == 0x100); + const int m1 = (instr >> 3) & 1; + const int m2 = (instr >> 6) & 3; + assert(m2 != 0); // Therefore m == 0 and m == 1 are impossible + assert(m2 != 3); // Therefore m == 6 and m == 7 are impossible + const int m = (m2 << 1) | m1; + assert(m != 4); // Only m == 2, m == 3 and m == 5 values are allowed + const int xn = instr & 7; + const int xi = (instr >> 9) & 7; + const auto src = (m == 3) ? AddrModeArg::An(xi) : AddrModeArg::Dn(xi); + const auto dst = (m == 2) ? AddrModeArg::Dn(xn) : AddrModeArg::An(xn); + char src_str[32]{}; + char dst_str[32]{}; + src.SNPrint(src_str, sizeof(src_str)); + dst.SNPrint(dst_str, sizeof(dst_str)); + snprintf(node.mnemonic, kMnemonicBufferSize, "exg"); + snprintf(node.arguments, kArgsBufferSize, "%s,%s", src_str, dst_str); + node.size = kInstructionSizeStepBytes + src.Size() + dst.Size(); +} + +static void disasm_chunk_c( + DisasmNode &node, uint16_t instr, const DataBuffer &code, const Settings &s) +{ + if ((instr & 0x1f0) == 0x100) { + // XXX GNU AS does not know ABCD.B, it only knows ABCD, but happily + // consumes SBCD.B and others. That's why `skip_suffix` flag is needed, + // specifically for ABCD mnemonic. It is probably a bug in GNU AS. + const bool skip_size_suffix = true; + return disasm_addx_subx_abcd_sbcd(node, instr, "abcd", "", skip_size_suffix); + } + const OpSize opsize = static_cast<OpSize>((instr >> 6) & 3); + if (opsize == OpSize::kInvalid) { + return disasm_divu_divs_mulu_muls(node, instr, code, s, "mul"); + } + const unsigned m_split = instr & 0x1f8; + if (m_split == 0x188 || m_split == 0x148 || m_split == 0x140) { + return disasm_exg(node, instr); + } + return disasm_or_and(node, instr, code, s, opsize, "and"); +} + +static void disasm_add_sub_x_a( + DisasmNode &node, const uint16_t instr, const DataBuffer &code, const Settings &s, const char *mnemonic) +{ + const OpSize opsize = static_cast<OpSize>((instr >> 6) & 3); + if (opsize == OpSize::kInvalid) { + return disasm_adda_suba_cmpa(node, instr, code, s, mnemonic); + } + const bool dir_to_addr = (instr >> 8) & 1; + const unsigned m = (instr >> 3) & 7; + if (dir_to_addr && (m == 0 || m == 1)) { + return disasm_addx_subx_abcd_sbcd(node, instr, mnemonic, "x"); + } + return disasm_add_sub_cmp(node, instr, code, s, mnemonic, opsize, dir_to_addr); +} + static inline const char *ShiftKindToMnemonic(const ShiftKind k) { switch (k) { @@ -1836,7 +1857,7 @@ static void m68k_disasm(DisasmNode &n, uint16_t i, const DataBuffer &c, const Se // Does not exist return disasm_verbatim(n, i, c, s); case 0xb: - return disasm_chunk_b(n, i, c, s); + return disasm_eor_cmpm_cmp_cmpa(n, i, c, s); case 0xc: return disasm_chunk_c(n, i, c, s); case 0xd: @@ -93,6 +93,28 @@ run_test_iterative() { done } +# bxxx cmp +# +run_test_simple "cmpb Dn, Dn" "\xb4\x01" +run_test_expect_short "cmpb An, Dn" "\xb4\x09" +run_test_simple "cmpw An, Dn" "\xb4\x49" +run_test_simple "cmpb (An), Dn" "\xb4\x11" +run_test_simple "cmpb (An)+, Dn" "\xb4\x19" +run_test_simple "cmpb -(An), Dn" "\xb4\x21" +run_test_simple "cmpl (d8,PC,An), Dn" "\xb0\xbb\x88\xff" +run_test_simple "cmpw (xxx).W, Dn" "\xb0\x78\x88\xff" +# GNU AS would emit CMPI for "cmp #imm,Xn", so we diassemble it as short +run_test_expect_short "cmpl #imm, D6" "\xb6\xbc\x44\xd1\xe6\xe9" + +# bxxx cmpa +# +run_test_simple "cmpaw Dn, An" "\xb4\xc1" +run_test_simple "cmpal An, An" "\xbb\xca" +run_test_simple "cmpaw (An)+, An" "\xba\xda" +run_test_simple "cmpal (xxx).L, An" "\xbb\xf9\x80\x00\x00\x00" +run_test_simple "cmpaw #imm, An" "\xba\xfc\x01\x00" +run_test_simple "cmpal #imm, An" "\xbb\xfc\x80\x00\x00\x00" + # cxxx divu divs # run_test_simple "divuw Dn, Dn" "\x82\xc6" @@ -293,6 +315,7 @@ run_test_expect_short "btstb large immediate in (xxx).L" "\x08\x39\x10\x21\xff\x # 0xxx immediate ops # +run_test_simple "orib #0, D0" "\x00\x00\x00\x00" run_test_simple "orib zero to CCR" "\x00\x3c\x00\x00" run_test_simple "orib positive to CCR" "\x00\x3c\x00\x01" run_test_simple "orib positive to CCR" "\x00\x3c\x00\x7f" |