OXYGENHUB
◆ main openmsx / src / MSXMatsushita.cc
5.3 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#include "MSXMatsushita.hh"
2
3
#include "MSXCPU.hh"
4
#include "MSXCPUInterface.hh"
5
#include "MSXCliComm.hh"
6
#include "MSXException.hh"
7
#include "SRAM.hh"
8
#include "VDP.hh"
9
#include "serialize.hh"
10
11
#include "xrange.hh"
12
13
namespace openmsx {
14
15
static constexpr uint8_t ID = 0x08;
16
17
MSXMatsushita::MSXMatsushita(const DeviceConfig& config)
18
	: MSXDevice(config)
19
	, MSXSwitchedDevice(getMotherBoard(), ID)
20
	, cpu(getCPU()) // used frequently, so cache it
21
	, firmwareSwitch(config)
22
	, sram(config.findChild("sramname") ? std::make_unique<SRAM>(getName() + " SRAM", 0x800, config) : nullptr)
23
	, turboAvailable(config.getChildDataAsBool("hasturbo", false))
24
{
25
	// TODO find out what ports 0x41 0x45 0x46 are used for
26
}
27
28
void MSXMatsushita::init()
29
{
30
	MSXDevice::init();
31
32
	const auto& refs = getReferences();
33
	vdp = !refs.empty() ? dynamic_cast<VDP*>(refs[0]) : nullptr;
34
	if (!vdp) {
35
		// No (valid) reference to the VDP found. We do allow this to
36
		// model MSX machines where the Matsushita device does not
37
		// influence the VDP timing. It's not know whether such
38
		// machines actually exist.
39
		return;
40
	}
41
42
	// Wrap the VDP ports.
43
	auto& cpuInterface = getCPUInterface();
44
	bool error = false;
45
	for (auto i : xrange(2)) {
46
		error |= !cpuInterface.replace_IO_In (uint8_t(0x98 + i), vdp, this);
47
	}
48
	for (auto i : xrange(4)) {
49
		error |= !cpuInterface.replace_IO_Out(uint8_t(0x98 + i), vdp, this);
50
	}
51
	if (error) {
52
		unwrap();
53
		throw MSXException(
54
			"Invalid Matsushita configuration: "
55
			"VDP not on IO-ports 0x98-0x9B.");
56
	}
57
	reset(EmuTime::dummy());
58
}
59
60
MSXMatsushita::~MSXMatsushita()
61
{
62
	if (!vdp) return;
63
	unwrap();
64
}
65
66
void MSXMatsushita::unwrap()
67
{
68
	// Unwrap the VDP ports.
69
	auto& cpuInterface = getCPUInterface();
70
	for (auto i : xrange(2)) {
71
		cpuInterface.replace_IO_In (uint8_t(0x98 + i), this, vdp);
72
	}
73
	for (auto i : xrange(4)) {
74
		cpuInterface.replace_IO_Out(uint8_t(0x98 + i), this, vdp);
75
	}
76
}
77
78
void MSXMatsushita::reset(EmuTime /*time*/)
79
{
80
	// TODO check this
81
	color1 = color2 = 0;
82
	pattern = 0;
83
	address = 0;
84
}
85
86
uint8_t MSXMatsushita::readSwitchedIO(uint16_t port, EmuTime time)
87
{
88
	// TODO: Port 7 and 8 can be read as well.
89
	uint8_t result = peekSwitchedIO(port, time);
90
	switch (port & 0x0F) {
91
	case 3:
92
		pattern = uint8_t((pattern << 2) | (pattern >> 6));
93
		break;
94
	case 9:
95
		address = (address + 1) & 0x1FFF;
96
		break;
97
	}
98
	return result;
99
}
100
101
uint8_t MSXMatsushita::peekSwitchedIO(uint16_t port, EmuTime /*time*/) const
102
{
103
	switch (port & 0x0F) {
104
	case 0:
105
		return uint8_t(~ID);
106
	case 1: {
107
		uint8_t result = firmwareSwitch.getStatus() ? 0x7F : 0xFF;
108
		// bit 0: turbo status, 0=on
109
		if (turboEnabled) {
110
			result &= ~0x01;
111
		}
112
		// bit 2: 0 = turbo feature available
113
		if (turboAvailable) {
114
			result &= ~0x04;
115
		}
116
		return result;
117
	}
118
	case 3:
119
		return uint8_t((((pattern & 0x80) ? color2 : color1) << 4) |
120
		               (((pattern & 0x40) ? color2 : color1) << 0));
121
	case 9:
122
		if (address < 0x800 && sram) {
123
			return (*sram)[address];
124
		} else {
125
			return 0xFF;
126
		}
127
	default:
128
		return 0xFF;
129
	}
130
}
131
132
void MSXMatsushita::writeSwitchedIO(uint16_t port, uint8_t value, EmuTime /*time*/)
133
{
134
	switch (port & 0x0F) {
135
	case 1:
136
		// the turboEnabled flag works even though no turbo is available
137
		if (value & 1) {
138
			// bit0 = 1 -> 3.5MHz
139
			if (turboAvailable) {
140
				getCPU().setZ80Freq(3579545);
141
			}
142
			turboEnabled = false;
143
		} else {
144
			// bit0 = 0 -> 5.3MHz
145
			if (turboAvailable) {
146
				getCPU().setZ80Freq(5369318); // 3579545 * 3/2
147
			}
148
			turboEnabled = true;
149
		}
150
		break;
151
	case 3:
152
		color2 = (value & 0xF0) >> 4;
153
		color1 =  value & 0x0F;
154
		break;
155
	case 4:
156
		pattern = value;
157
		break;
158
	case 7:
159
		// set address (low)
160
		address = (address & 0xFF00) | value;
161
		break;
162
	case 8:
163
		// set address (high)
164
		address = uint16_t((address & 0x00FF) | ((value & 0x1F) << 8));
165
		break;
166
	case 9:
167
		// write sram
168
		if (address < 0x800 && sram) {
169
			sram->write(address, value);
170
		}
171
		address = (address + 1) & 0x1FFF;
172
		break;
173
	}
174
}
175
176
uint8_t MSXMatsushita::readIO(uint16_t port, EmuTime time)
177
{
178
	// TODO also delay on read?
179
	assert(vdp);
180
	return vdp->readIO(port, time);
181
}
182
183
uint8_t MSXMatsushita::peekIO(uint16_t port, EmuTime time) const
184
{
185
	assert(vdp);
186
	return vdp->peekIO(port, time);
187
}
188
189
void MSXMatsushita::writeIO(uint16_t port, uint8_t value, EmuTime time)
190
{
191
	assert(vdp);
192
	delay(time);
193
	vdp->writeIO(port, value, lastTime.getTime());
194
}
195
196
void MSXMatsushita::delay(EmuTime time)
197
{
198
	if (turboAvailable && turboEnabled) {
199
		lastTime += 46; // 8us, like in S1990
200
		if (time < lastTime.getTime()) {
201
			cpu.wait(lastTime.getTime());
202
			return;
203
		}
204
	}
205
	lastTime.reset(time);
206
}
207
208
template<typename Archive>
209
void MSXMatsushita::serialize(Archive& ar, unsigned version)
210
{
211
	ar.template serializeBase<MSXDevice>(*this);
212
	// no need to serialize MSXSwitchedDevice base class
213
214
	if (sram) ar.serialize("SRAM", *sram);
215
	ar.serialize("address", address,
216
	             "color1", color1,
217
	             "color2", color2,
218
	             "pattern", pattern);
219
220
	if (ar.versionAtLeast(version, 2)) {
221
		ar.serialize("lastTime", lastTime,
222
		             "turboEnabled", turboEnabled);
223
	} else {
224
		// keep 'lastTime == zero'
225
		// keep 'turboEnabled == false'
226
		getCliComm().printWarning(
227
			"Loading an old savestate: the timing of the CPU-VDP "
228
			"communication emulation has changed. This may cause "
229
			"synchronization problems in replay.");
230
	}
231
}
232
233
INSTANTIATE_SERIALIZE_METHODS(MSXMatsushita);
234
REGISTER_MSXDEVICE(MSXMatsushita, "Matsushita");
235
236
} // namespace openmsx