OXYGENHUB
◆ main openmsx / src / cpu / Dasm.cc
5.9 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
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 '#': // invalid z80 instruction
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; // only used by unittest
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
// Calculates an address smaller or equal to the given 'addr' where there is for
150
// sure a boundary. Though this is not (yet) necessarily the largest address
151
// with this property.
152
// In addition return the length of the instruction at the resulting address.
153
static std::pair<uint16_t, unsigned> findGuaranteedBoundary(const MSXCPUInterface& interface, uint16_t addr, EmuTime time)
154
{
155
	if (addr < 3) {
156
		// address 0 (the top) is a boundary
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
		// Criteria:
166
		//   * Assuming a maximum instruction length of 4:
167
		//   * IF  at 'addr - 1' starts an instruction with length 1
168
		//   * and at 'addr - 2' starts an instruction with length 1 or 2
169
		//   * and at 'addr - 3' starts an instruction with length 1 or 2 or 3
170
		//   * THEN 'addr' must be the start of an instruction.
171
		if ((length[1] == 1) && (length[2] <= 2) && (length[3] <= 3)) {
172
			return {addr, length[0]};
173
		}
174
		if (addr == 3) {
175
			// address 0 (the top) is a boundary
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
	// scan backwards for a guaranteed boundary
190
	auto [candidate, len] = findGuaranteedBoundary(interface, addr, time);
191
	// scan forwards for the boundary with the largest address
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))); // for sure small enough
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
} // namespace openmsx