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