openMSX
Dasm.cc
Go to the documentation of this file.
1#include "Dasm.hh"
2#include "DasmTables.hh"
3#include "MSXCPUInterface.hh"
4#include "narrow.hh"
5#include "strCat.hh"
6
7namespace openmsx {
8
9static constexpr char sign(uint8_t a)
10{
11 return (a & 128) ? '-' : '+';
12}
13
14static constexpr int abs(uint8_t a)
15{
16 return (a & 128) ? (256 - a) : a;
17}
18
19void appendAddrAsHex(std::string& output, uint16_t addr)
20{
21 strAppend(output, '#', hex_string<4>(addr));
22}
23
24unsigned dasm(const MSXCPUInterface& interface, uint16_t pc, std::span<uint8_t, 4> buf,
25 std::string& dest, EmuTime::param time,
26 std::function<void(std::string&, uint16_t)> appendAddr)
27{
28 const char* r = nullptr;
29
30 buf[0] = interface.peekMem(pc, time);
31 auto [s, i] = [&]() -> std::pair<const char*, unsigned> {
32 switch (buf[0]) {
33 case 0xCB:
34 buf[1] = interface.peekMem(pc + 1, time);
35 return {mnemonic_cb[buf[1]], 2};
36 case 0xED:
37 buf[1] = interface.peekMem(pc + 1, time);
38 return {mnemonic_ed[buf[1]], 2};
39 case 0xDD:
40 case 0xFD:
41 r = (buf[0] == 0xDD) ? "ix" : "iy";
42 buf[1] = interface.peekMem(pc + 1, time);
43 if (buf[1] != 0xcb) {
44 return {mnemonic_xx[buf[1]], 2};
45 } else {
46 buf[2] = interface.peekMem(pc + 2, time);
47 buf[3] = interface.peekMem(pc + 3, time);
48 return {mnemonic_xx_cb[buf[3]], 4};
49 }
50 default:
51 return {mnemonic_main[buf[0]], 1};
52 }
53 }();
54
55 for (int j = 0; s[j]; ++j) {
56 switch (s[j]) {
57 case 'B':
58 buf[i] = interface.peekMem(narrow_cast<uint16_t>(pc + i), time);
59 strAppend(dest, '#', hex_string<2>(
60 static_cast<uint16_t>(buf[i])));
61 i += 1;
62 break;
63 case 'R':
64 buf[i] = interface.peekMem(narrow_cast<uint16_t>(pc + i), time);
65 appendAddr(dest, pc + 2 + static_cast<int8_t>(buf[i]));
66 i += 1;
67 break;
68 case 'A':
69 case 'W':
70 buf[i + 0] = interface.peekMem(narrow_cast<uint16_t>(pc + i + 0), time);
71 buf[i + 1] = interface.peekMem(narrow_cast<uint16_t>(pc + i + 1), time);
72 appendAddr(dest, buf[i] + buf[i + 1] * 256);
73 i += 2;
74 break;
75 case 'X':
76 buf[i] = interface.peekMem(narrow_cast<uint16_t>(pc + i), time);
77 strAppend(dest, '(', r, sign(buf[i]), '#',
78 hex_string<2>(abs(buf[i])), ')');
79 i += 1;
80 break;
81 case 'Y':
82 strAppend(dest, r, sign(buf[2]), '#', hex_string<2>(abs(buf[2])));
83 break;
84 case 'I':
85 dest += r;
86 break;
87 case '!':
88 dest = strCat("db #ED,#", hex_string<2>(buf[1]),
89 " ");
90 return 2;
91 case '@':
92 dest = strCat("db #", hex_string<2>(buf[0]),
93 " ");
94 return 1;
95 case '#':
96 dest = strCat("db #", hex_string<2>(buf[0]),
97 ",#CB,#", hex_string<2>(buf[2]),
98 ' ');
99 return 2;
100 case ' ': {
101 dest.resize(7, ' ');
102 break;
103 }
104 default:
105 dest += s[j];
106 break;
107 }
108 }
109 return i;
110}
111
112unsigned instructionLength(const MSXCPUInterface& interface, uint16_t pc,
113 EmuTime::param time)
114{
115 auto op0 = interface.peekMem(pc, time);
116 auto t = instr_len_tab[op0];
117 if (t < 4) return t;
118
119 auto op1 = interface.peekMem(pc + 1, time);
120 return instr_len_tab[64 * t + op1];
121}
122
123// Calculates an address smaller or equal to the given 'addr' where there is for
124// sure a boundary. Though this is not (yet) necessarily the largest address
125// with this property.
126// In addition return the length of the instruction at the resulting address.
127static std::pair<uint16_t, unsigned> findGuaranteedBoundary(const MSXCPUInterface& interface, uint16_t addr, EmuTime::param time)
128{
129 if (addr < 3) {
130 // address 0 (the top) is a boundary
131 return {0, instructionLength(interface, 0, time)};
132 }
133 std::array<unsigned, 4> length;
134 for (auto i : xrange(4)) {
135 length[i] = instructionLength(interface, addr - i, time);
136 }
137
138 while (true) {
139 // Criteria:
140 // * Assuming a maximum instruction length of 4:
141 // * IF at 'addr - 1' starts an instruction with length 1
142 // * and at 'addr - 2' starts an instruction with length 1 or 2
143 // * and at 'addr - 3' starts an instruction with length 1 or 2 or 3
144 // * THEN 'addr' must be the start of an instruction.
145 if ((length[1] == 1) && (length[2] <= 2) && (length[3] <= 3)) {
146 return {addr, length[0]};
147 }
148 if (addr == 3) {
149 // address 0 (the top) is a boundary
150 return {0, length[3]};
151 }
152 --addr;
153 length[0] = length[1];
154 length[1] = length[2];
155 length[2] = length[3];
156 length[3] = instructionLength(interface, addr - 3, time);
157 }
158}
159
160static std::pair<uint16_t, unsigned> instructionBoundaryAndLength(
161 const MSXCPUInterface& interface, uint16_t addr, EmuTime::param time)
162{
163 // scan backwards for a guaranteed boundary
164 auto [candidate, len] = findGuaranteedBoundary(interface, addr, time);
165 // scan forwards for the boundary with the largest address
166 while (true) {
167 if ((candidate + len) > addr) return {candidate, len};
168 candidate += len;
169 len = instructionLength(interface, candidate, time);
170 }
171}
172
173uint16_t instructionBoundary(const MSXCPUInterface& interface, uint16_t addr,
174 EmuTime::param time)
175{
176 auto [result, len] = instructionBoundaryAndLength(interface, addr, time);
177 return result;
178}
179
180uint16_t nInstructionsBefore(const MSXCPUInterface& interface, uint16_t addr,
181 EmuTime::param time, int n)
182{
183 unsigned start = std::max(0, int(addr - 4 * n)); // for sure small enough
184 auto [tmp, len] = instructionBoundaryAndLength(interface, start, time);
185
186 std::vector<uint16_t> addresses;
187 while ((tmp + len) <= addr) {
188 addresses.push_back(tmp);
189 tmp += len;
190 len = instructionLength(interface, tmp, time);
191 }
192 addresses.push_back(tmp);
193
194 return addresses[std::max(0, narrow<int>(addresses.size()) - 1 - n)];
195}
196
197} // namespace openmsx
TclObject t
byte peekMem(word address, EmuTime::param time) const
Peek memory location.
T length(const vecN< N, T > &x)
Definition: gl_vec.hh:341
constexpr vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:285
This file implemented 3 utility functions:
Definition: Autofire.cc:9
const std::array< const char *, 256 > mnemonic_xx
Definition: DasmTables.cc:128
const std::array< const char *, 256 > mnemonic_ed
Definition: DasmTables.cc:92
unsigned dasm(const MSXCPUInterface &interface, uint16_t pc, std::span< uint8_t, 4 > buf, std::string &dest, EmuTime::param time, std::function< void(std::string &, uint16_t)> appendAddr)
Disassemble.
Definition: Dasm.cc:24
const std::array< const char *, 256 > mnemonic_xx_cb
Definition: DasmTables.cc:20
const std::array< const char *, 256 > mnemonic_cb
Definition: DasmTables.cc:56
const std::array< uint8_t, std::size_t(3) *256 > instr_len_tab
Definition: DasmTables.cc:202
const std::array< const char *, 256 > mnemonic_main
Definition: DasmTables.cc:164
uint16_t instructionBoundary(const MSXCPUInterface &interface, uint16_t addr, EmuTime::param time)
This is only an heuristic to display instructions in a debugger disassembly view.
Definition: Dasm.cc:173
uint16_t nInstructionsBefore(const MSXCPUInterface &interface, uint16_t addr, EmuTime::param time, int n)
Get the start address of the 'n'th instruction before the instruction containing the byte at the give...
Definition: Dasm.cc:180
unsigned instructionLength(const MSXCPUInterface &interface, uint16_t pc, EmuTime::param time)
Calculate the length of the instruction at the given address.
Definition: Dasm.cc:112
void appendAddrAsHex(std::string &output, uint16_t addr)
Definition: Dasm.cc:19
std::string strCat(Ts &&...ts)
Definition: strCat.hh:625
void strAppend(std::string &result, Ts &&...ts)
Definition: strCat.hh:703
constexpr auto xrange(T e)
Definition: xrange.hh:132