summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/x86log.cpp
blob: f118cdb3e7ea1287f5ee05e8c4fa4090596ce461 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
// license:BSD-3-Clause
// copyright-holders:Aaron Giles
/***************************************************************************

    x86log.c

    x86/x64 code logging helpers.

***************************************************************************/

#include "emu.h"
#include "x86log.h"

#include "cpu/i386/i386dasm.h"

#include <sstream>


namespace {

class x86_buf : public util::disasm_interface::data_buffer
{
public:
	x86_buf(offs_t pc, const u8 *b) : base_pc(pc), buf(b) { }
	~x86_buf() = default;

	// We know we're on a x86, so we can go short
	virtual u8  r8 (offs_t pc) const override { return *(u8  *)(buf + pc - base_pc); }
	virtual u16 r16(offs_t pc) const override { return *(u16 *)(buf + pc - base_pc); }
	virtual u32 r32(offs_t pc) const override { return *(u32 *)(buf + pc - base_pc); }
	virtual u64 r64(offs_t pc) const override { return *(u64 *)(buf + pc - base_pc); }

private:
	offs_t base_pc;
	const u8 *buf;
};

class x86_config : public i386_disassembler::config
{
public:
	~x86_config() = default;
	virtual int get_mode() const override { return sizeof(void *) * 8; };
};

} // anonymous namespace



/***************************************************************************
    EXTERNAL INTERFACES
***************************************************************************/

/*-------------------------------------------------
    x86log_context::create- create a new context
-------------------------------------------------*/

x86log_context::ptr x86log_context::create(std::string_view filename)
{
	try
	{
		// allocate the log
		auto log = std::make_unique<x86log_context>();
		log->data_range.reserve(MAX_DATA_RANGES);
		log->comment_list.reserve(MAX_COMMENTS);

		// allocate the filename
		log->filename = filename;

		// reset things
		log->reset_log();
		return log;
	}
	catch (std::bad_alloc const &)
	{
		return nullptr;
	}
}


/*-------------------------------------------------
    ~x86log_context - release a context
-------------------------------------------------*/

x86log_context::~x86log_context()
{
	// close any open files
	if (file)
		std::fclose(file);
}


/*-------------------------------------------------
    x86log_context::mark_as_data - mark a given
    range as data for logging purposes
-------------------------------------------------*/

void x86log_context::mark_as_data(x86code const *base, x86code const *end, int size) noexcept
{
	assert(end >= base);
	assert((size == 1) || (size == 2) || (size == 4) || (size == 8));

	// we assume data ranges are registered in order; enforce this
	assert(data_range.empty() || (base > data_range.back().end));

	// fill in the new range
	try
	{
		data_range.emplace_back(data_range_t{ base, end, size });
	}
	catch (std::bad_alloc const &)
	{
	}
}


/*-------------------------------------------------
    x86log_context::disasm_code_range - disassemble
    a range of code and reset accumulated
    information
-------------------------------------------------*/

void x86log_context::disasm_code_range(const char *label, x86code const *start, x86code const *stop)
{
	auto const lastcomment = comment_list.cend();
	auto curcomment = comment_list.cbegin();
	auto const lastdata = data_range.cend();
	auto curdata = data_range.cbegin();
	x86code const *cur = start;

	// print the optional label
	if (label)
		printf("\n%s\n", label);

	// loop from the start until the cache top
	x86_config conf;
	i386_disassembler dis(&conf);
	std::stringstream strbuffer;
	while (cur < stop)
	{
		strbuffer.str("");
		int bytes;

		// skip past any past data ranges
		while ((curdata != lastdata) && (cur > curdata->end))
			++curdata;

		// skip past any past comments
		while ((curcomment != lastcomment) && (cur > curcomment->base))
			++curcomment;

		if ((cur >= curdata->base) && (cur <= curdata->end))
		{
			// if we're in a data range, output the next chunk and continue
			bytes = curdata->size;
			switch (curdata->size)
			{
				default:
				case 1: util::stream_format(strbuffer, "db      %02X", *cur); break;
				case 2: util::stream_format(strbuffer, "dw      %04X", *reinterpret_cast<uint16_t const *>(cur)); break;
				case 4: util::stream_format(strbuffer, "dd      %08X", *reinterpret_cast<uint32_t const *>(cur)); break;
				case 8: util::stream_format(strbuffer, "dq      %016X", *reinterpret_cast<uint64_t const *>(cur)); break;
			}
		}
		else if (*cur == 0xcc)
		{
			// if we're not in the data range, skip filler opcodes
			cur++;
			continue;
		}
		else
		{
			// otherwise, do a disassembly of the current instruction
			offs_t pc = uintptr_t(cur);
			x86_buf buf(pc, cur);
			bytes = dis.disassemble(strbuffer, pc, buf, buf) & util::disasm_interface::LENGTHMASK;
		}

		if ((curcomment != lastcomment) && (cur == curcomment->base))
		{
			// if we have a matching comment, output it
			// if we have additional matching comments at the same address, output them first
			for ( ; ((curcomment + 1) != lastcomment) && (cur == curcomment[1].base); curcomment++)
				printf("%p: %-50s; %s\n", cur, "", curcomment->string);
			printf("%p: %-50s; %s\n", cur, std::move(strbuffer).str().c_str(), curcomment->string);
		}
		else
		{
			// if we don't, just print the disassembly and move on
			printf("%p: %s\n", cur, std::move(strbuffer).str().c_str());
		}

		// advance past this instruction
		cur += bytes;
	}

	// reset our state
	reset_log();
}



/*-------------------------------------------------
    reset_log - reset the state of the log
-------------------------------------------------*/

void x86log_context::reset_log() noexcept
{
	data_range.clear();
	comment_list.clear();
	comment_pool_next = comment_pool;
}