| e8555c89 Andy McCall |
1 |
#include "Dasm.hh" |
| 2 |
|
| 3 |
#include "DasmTables.hh" |
| 4 |
#include "MSXCPUInterface.hh" |
| 5 |
|
| 6 |
#include "narrow.hh" |
| 7 |
#include "strCat.hh" |
| 8 |
|
| 9 |
namespace openmsx { |
| 10 |
|
| 11 |
using namespace std::literals; |
| 12 |
|
| 13 |
static constexpr char sign(uint8_t a) |
| 14 |
{ |
| 15 |
return (a & 128) ? '-' : '+'; |
| 16 |
} |
| 17 |
|
| 18 |
static constexpr int abs(uint8_t a) |
| 19 |
{ |
| 20 |
return (a & 128) ? (256 - a) : a; |
| 21 |
} |
| 22 |
|
| 23 |
void appendAddrAsHex(std::string& output, uint16_t addr) |
| 24 |
{ |
| 25 |
strAppend(output, '#', hex_string<4>(addr)); |
| 26 |
} |
| 27 |
|
| 28 |
std::optional<unsigned> instructionLength(std::span<const uint8_t> bin) |
| 29 |
{ |
| 30 |
if (bin.empty()) return {}; |
| 31 |
auto t = instr_len_tab[bin[0]]; |
| 32 |
if (t < 4) return t; |
| 33 |
|
| 34 |
if (bin.size() < 2) return {}; |
| 35 |
t = instr_len_tab[64 * t + bin[1]]; |
| 36 |
if (t > bin.size()) return {}; |
| 37 |
return t; |
| 38 |
} |
| 39 |
|
| 40 |
unsigned dasm(std::span<const uint8_t> opcode, uint16_t pc, std::string& dest, |
| 41 |
function_ref<void(std::string&, uint16_t)> appendAddr) |
| 42 |
{ |
| 43 |
std::string_view r; |
| 44 |
|
| 45 |
auto [s, i] = [&] -> std::pair<const char*, unsigned> { |
| 46 |
switch (opcode[0]) { |
| 47 |
case 0xCB: |
| 48 |
return {mnemonic_cb[opcode[1]], 2}; |
| 49 |
case 0xED: |
| 50 |
return {mnemonic_ed[opcode[1]], 2}; |
| 51 |
case 0xDD: |
| 52 |
case 0xFD: |
| 53 |
r = (opcode[0] == 0xDD) ? "ix"sv : "iy"sv; |
| 54 |
if (opcode[1] != 0xCB) { |
| 55 |
return {mnemonic_xx[opcode[1]], 2}; |
| 56 |
} else { |
| 57 |
return {mnemonic_xx_cb[opcode[3]], 4}; |
| 58 |
} |
| 59 |
default: |
| 60 |
return {mnemonic_main[opcode[0]], 1}; |
| 61 |
} |
| 62 |
}(); |
| 63 |
|
| 64 |
for (int j = 0; s[j]; ++j) { |
| 65 |
switch (s[j]) { |
| 66 |
case 'B': |
| 67 |
strAppend(dest, '#', hex_string<2>( |
| 68 |
static_cast<uint16_t>(opcode[i]))); |
| 69 |
i += 1; |
| 70 |
break; |
| 71 |
case 'R': |
| 72 |
appendAddr(dest, uint16_t(pc + 2 + static_cast<int8_t>(opcode[i]))); |
| 73 |
i += 1; |
| 74 |
break; |
| 75 |
case 'A': |
| 76 |
case 'W': |
| 77 |
appendAddr(dest, opcode[i] + opcode[i + 1] * 256); |
| 78 |
i += 2; |
| 79 |
break; |
| 80 |
case 'X': |
| 81 |
strAppend(dest, '(', r, sign(opcode[i]), '#', |
| 82 |
hex_string<2>(abs(opcode[i])), ')'); |
| 83 |
i += 1; |
| 84 |
break; |
| 85 |
case 'Y': |
| 86 |
strAppend(dest, '(', r, sign(opcode[2]), '#', |
| 87 |
hex_string<2>(abs(opcode[2])), ')'); |
| 88 |
break; |
| 89 |
case 'I': |
| 90 |
dest += r; |
| 91 |
break; |
| 92 |
case '!': |
| 93 |
case '@': |
| 94 |
case '#': |
| 95 |
strAppend(dest, "db"); |
| 96 |
for (auto k : xrange(i)) { |
| 97 |
strAppend(dest, ((k == 0) ? ' ' : ','), '#', hex_string<2>(opcode[k])); |
| 98 |
} |
| 99 |
break; |
| 100 |
case ' ': { |
| 101 |
dest.resize(7, ' '); |
| 102 |
break; |
| 103 |
} |
| 104 |
default: |
| 105 |
dest += s[j]; |
| 106 |
break; |
| 107 |
} |
| 108 |
} |
| 109 |
return i; |
| 110 |
} |
| 111 |
|
| 112 |
std::span<uint8_t> fetchInstruction(const MSXCPUInterface& interface, uint16_t addr, |
| 113 |
std::span<uint8_t, 4> buffer, EmuTime time) |
| 114 |
{ |
| 115 |
uint16_t idx = 0; |
| 116 |
buffer[idx++] = interface.peekMem(addr, time); |
| 117 |
auto len = instr_len_tab[buffer[0]]; |
| 118 |
if (len >= 4) { |
| 119 |
buffer[idx++] = interface.peekMem(addr + 1, time); |
| 120 |
len = instr_len_tab[64 * len + buffer[1]]; |
| 121 |
assert(len <= 4); |
| 122 |
} |
| 123 |
for (; idx < len; ++idx) { |
| 124 |
buffer[idx] = interface.peekMem(addr + idx, time); |
| 125 |
} |
| 126 |
return buffer.subspan(0, len); |
| 127 |
} |
| 128 |
|
| 129 |
unsigned dasm(const MSXCPUInterface& interface, uint16_t pc, std::span<uint8_t, 4> buf, |
| 130 |
std::string& dest, EmuTime time, |
| 131 |
function_ref<void(std::string&, uint16_t)> appendAddr) |
| 132 |
{ |
| 133 |
auto opcodes = fetchInstruction(interface, pc, buf, time); |
| 134 |
dasm(opcodes, pc, dest, appendAddr); |
| 135 |
return narrow_cast<unsigned>(opcodes.size()); |
| 136 |
} |
| 137 |
|
| 138 |
static unsigned instructionLength(const MSXCPUInterface& interface, uint16_t pc, |
| 139 |
EmuTime time) |
| 140 |
{ |
| 141 |
auto op0 = interface.peekMem(pc, time); |
| 142 |
auto t = instr_len_tab[op0]; |
| 143 |
if (t < 4) return t; |
| 144 |
|
| 145 |
auto op1 = interface.peekMem(pc + 1, time); |
| 146 |
return instr_len_tab[64 * t + op1]; |
| 147 |
} |
| 148 |
|
| 149 |
|
| 150 |
|
| 151 |
|
| 152 |
|
| 153 |
static std::pair<uint16_t, unsigned> findGuaranteedBoundary(const MSXCPUInterface& interface, uint16_t addr, EmuTime time) |
| 154 |
{ |
| 155 |
if (addr < 3) { |
| 156 |
|
| 157 |
return {0, instructionLength(interface, 0, time)}; |
| 158 |
} |
| 159 |
std::array<unsigned, 4> length; |
| 160 |
for (auto i : xrange(4)) { |
| 161 |
length[i] = instructionLength(interface, uint16_t(addr - i), time); |
| 162 |
} |
| 163 |
|
| 164 |
while (true) { |
| 165 |
|
| 166 |
|
| 167 |
|
| 168 |
|
| 169 |
|
| 170 |
|
| 171 |
if ((length[1] == 1) && (length[2] <= 2) && (length[3] <= 3)) { |
| 172 |
return {addr, length[0]}; |
| 173 |
} |
| 174 |
if (addr == 3) { |
| 175 |
|
| 176 |
return {0, length[3]}; |
| 177 |
} |
| 178 |
--addr; |
| 179 |
length[0] = length[1]; |
| 180 |
length[1] = length[2]; |
| 181 |
length[2] = length[3]; |
| 182 |
length[3] = instructionLength(interface, addr - 3, time); |
| 183 |
} |
| 184 |
} |
| 185 |
|
| 186 |
static std::pair<uint16_t, unsigned> instructionBoundaryAndLength( |
| 187 |
const MSXCPUInterface& interface, uint16_t addr, EmuTime time) |
| 188 |
{ |
| 189 |
|
| 190 |
auto [candidate, len] = findGuaranteedBoundary(interface, addr, time); |
| 191 |
|
| 192 |
while (true) { |
| 193 |
if ((candidate + len) > addr) return {candidate, len}; |
| 194 |
candidate += len; |
| 195 |
len = instructionLength(interface, candidate, time); |
| 196 |
} |
| 197 |
} |
| 198 |
|
| 199 |
uint16_t instructionBoundary(const MSXCPUInterface& interface, uint16_t addr, |
| 200 |
EmuTime time) |
| 201 |
{ |
| 202 |
auto [result, len] = instructionBoundaryAndLength(interface, addr, time); |
| 203 |
return result; |
| 204 |
} |
| 205 |
|
| 206 |
uint16_t nInstructionsBefore(const MSXCPUInterface& interface, uint16_t addr, |
| 207 |
EmuTime time, int n) |
| 208 |
{ |
| 209 |
auto start = uint16_t(std::max(0, int(addr - 4 * n))); |
| 210 |
auto [tmp, len] = instructionBoundaryAndLength(interface, start, time); |
| 211 |
|
| 212 |
std::vector<uint16_t> addresses; |
| 213 |
while ((tmp + len) <= addr) { |
| 214 |
addresses.push_back(tmp); |
| 215 |
tmp += len; |
| 216 |
len = instructionLength(interface, tmp, time); |
| 217 |
} |
| 218 |
addresses.push_back(tmp); |
| 219 |
|
| 220 |
return addresses[std::max(0, narrow<int>(addresses.size()) - 1 - n)]; |
| 221 |
} |
| 222 |
|
| 223 |
} |