OXYGENHUB
◆ main openmsx / src / YamahaSKW01.cc
7.1 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#include "YamahaSKW01.hh"
2
3
#include "CacheLine.hh"
4
#include "DummyPrinterPortDevice.hh"
5
#include "MSXException.hh"
6
#include "serialize.hh"
7
8
#include "checked_cast.hh"
9
10
// Implementation based on reverse-engineering of 'acet'/'uniabis', see:
11
//   https://www.msx.org/forum/msx-talk/hardware/trying-to-dump-yamaha-skw-01
12
//   https://github.com/uniabis/msxrawl/tree/main/dumpskw1
13
// The printer port is assumed to work the same as a standard MSX printer port.
14
// This seems to be OK, because when printing with the SKW-01 software and
15
// selecting the "HR-5X" printer and an emulated MSX Printer plugged in, sane
16
// output is produced.
17
// As the emulation is purely based on reverse engineering based on the built
18
// in software, there may be plenty of details wrong.
19
// Also note that the delay that is apparently present when writing the font
20
// address registers (other wise the READY bit wouldn't need to be read out) is
21
// not emulated. Because of that, the READY bit will always just be enabled.
22
23
// Addendum:
24
// It looks like the SKW-05-like device built in the Yamaha YIS-805/256 is very
25
// similar, although it does not have a printer port of course and also no font
26
// ROM. And the main ROM is 48kB instead. We don't know about the real SKW-05,
27
// so let's not make any assumptions on that, but the stuff in the YIS-805/256
28
// we do know about and that part is emulated, to a similar level as the
29
// SKW-01.
30
31
namespace openmsx {
32
33
// Printer port part of the SKW-01, as it needs to be optional in that class for the National CF-SM003
34
35
YamahaSKW01PrinterPort::YamahaSKW01PrinterPort(PluggingController& pluggingController_, const std::string& name_)
36
	: Connector(pluggingController_, name_, std::make_unique<DummyPrinterPortDevice>())
37
{
38
	reset(EmuTime::dummy());
39
}
40
41
void YamahaSKW01PrinterPort::reset(EmuTime time)
42
{
43
	writeData(0, time);    // TODO check this, see MSXPrinterPort
44
	setStrobe(true, time); // TODO check this, see MSXPrinterPort
45
}
46
47
bool YamahaSKW01PrinterPort::getStatus(EmuTime time) const
48
{
49
	return getPluggedPrintDev().getStatus(time);
50
}
51
52
void YamahaSKW01PrinterPort::setStrobe(bool newStrobe, EmuTime time)
53
{
54
	if (newStrobe != strobe) {
55
		strobe = newStrobe;
56
		getPluggedPrintDev().setStrobe(strobe, time);
57
	}
58
}
59
60
void YamahaSKW01PrinterPort::writeData(uint8_t newData, EmuTime time)
61
{
62
	if (newData != data) {
63
		data = newData;
64
		getPluggedPrintDev().writeData(data, time);
65
	}
66
}
67
68
zstring_view YamahaSKW01PrinterPort::getDescription() const
69
{
70
	return "Yamaha SKW-01 printer port";
71
}
72
73
zstring_view YamahaSKW01PrinterPort::getClass() const
74
{
75
	return "Printer Port";
76
}
77
78
void YamahaSKW01PrinterPort::plug(Pluggable& dev, EmuTime time)
79
{
80
	Connector::plug(dev, time);
81
	getPluggedPrintDev().writeData(data, time);
82
	getPluggedPrintDev().setStrobe(strobe, time);
83
}
84
85
PrinterPortDevice& YamahaSKW01PrinterPort::getPluggedPrintDev() const
86
{
87
	return *checked_cast<PrinterPortDevice*>(&getPlugged());
88
}
89
90
template<typename Archive>
91
void YamahaSKW01PrinterPort::serialize(Archive& ar, unsigned /*version*/)
92
{
93
	ar.template serializeBase<Connector>(*this);
94
	ar.serialize("strobe"     , strobe,
95
	             "data"       , data);
96
	// TODO force writing data to port?? (See MSXPrinterPort)
97
}
98
99
YamahaSKW01::YamahaSKW01(DeviceConfig& config)
100
	: MSXDevice(config)
101
	, mainRom(MSXDevice::getName() + " main"     , "rom", config, "main")
102
	, fontRom(config.getXML()->findChildWithAttribute("rom", "id", "kanjifont") ?
103
		 std::make_optional<Rom>(MSXDevice::getName() + " kanjifont", "rom", config, "kanjifont") : std::nullopt)
104
	, dataRom(MSXDevice::getName() + " data"     , "rom", config, "data")
105
	, sram(MSXDevice::getName() + " SRAM", 0x800, config)
106
{
107
	if (config.getChildDataAsBool("hasprinterport", true)) {
108
		printerPort.emplace(getPluggingController(), getName() + " printerport");
109
	}
110
111
	if (mainRom.size() != one_of(32 * 1024u, 48 * 1024u)) {
112
		throw MSXException("Main ROM must be exactly 32kB or 48kB in size.");
113
	}
114
	if (fontRom && fontRom->size() != 128 * 1024) {
115
		throw MSXException("Font ROM must be exactly 128kB in size.");
116
	}
117
	if (dataRom.size() != 32 * 1024) {
118
		throw MSXException("Data ROM must be exactly 32kB in size.");
119
	}
120
121
	reset(EmuTime::dummy());
122
}
123
124
void YamahaSKW01::reset(EmuTime time)
125
{
126
	fontAddress = {0, 0, 0, 0};
127
	dataAddress = 0;
128
129
	if (printerPort) printerPort->reset(time);
130
}
131
132
byte YamahaSKW01::readMem(uint16_t address, EmuTime time)
133
{
134
	return peekMem(address, time);
135
}
136
137
byte YamahaSKW01::peekMem(uint16_t address, EmuTime time) const
138
{
139
	if (address == one_of(0x7FC0, 0x7FC2, 0x7FC4, 0x7FC6)) {
140
		return fontRom ? 0x01 /* for now, always READY to read */ : 0x00;
141
	} else if (address == one_of(0x7FC1, 0x7FC3, 0x7FC5, 0x7FC7)) {
142
		if (!fontRom) return 0x00;
143
		unsigned group = (address - 0x7FC1) / 2;
144
		unsigned base = 0x8000 * group;
145
		unsigned offset = fontAddress[group] & 0x7FFF;
146
		return (*fontRom)[base + offset];
147
	} else if (address == 0x7FC8 || address == 0x7FC9) {
148
		return 0xFF;
149
	} else if (address == 0x7FCA || address == 0x7FCB) {
150
		if ((dataAddress & (1 << 15)) == 0) {
151
			return dataRom[dataAddress & 0x7FFF];
152
		} else {
153
			return sram[dataAddress & 0x7FF];
154
		}
155
	} else if (address == 0x7FCC && printerPort) {
156
		// bit 1 = status / other bits always 1
157
		return printerPort->getStatus(time)
158
		       ? 0xFF : 0xFD;
159
	} else if (address < mainRom.size()) {
160
		return mainRom[address];
161
	} else {
162
		return 0xFF;
163
	}
164
}
165
166
void YamahaSKW01::writeMem(uint16_t address, byte value, EmuTime time)
167
{
168
	if (0x7FC0 <= address && address <= 0x7FC7) {
169
		unsigned group = (address - 0x7FC0) / 2;
170
		if ((address & 1) == 0) {
171
			// LSB address
172
			fontAddress[group] = (fontAddress[group] & 0xFF00) | uint16_t(value << 0);
173
		} else {
174
			// MSB address
175
			fontAddress[group] = (fontAddress[group] & 0x00FF) | uint16_t(value << 8);
176
		}
177
	} else if (address == 0x7FC8) {
178
		dataAddress = (dataAddress & 0xFF00) | uint16_t(value << 0);
179
	} else if (address == 0x7FC9) {
180
		dataAddress = (dataAddress & 0x00FF) | uint16_t(value << 8);
181
	} else if (address == 0x7FCA || address == 0x7FCB) {
182
		if ((dataAddress & (1 << 15)) != 0) {
183
			sram.write(dataAddress & 0x7FF, value);
184
		}
185
	} else if (address == 0x7FCC && printerPort) {
186
		printerPort->setStrobe(value & 1, time);
187
	} else if (address == 0x7FCE && printerPort) {
188
		printerPort->writeData(value, time);
189
	}
190
}
191
192
const byte* YamahaSKW01::getReadCacheLine(uint16_t start) const
193
{
194
	if ((start & CacheLine::HIGH) == (0x7FC0 & CacheLine::HIGH)) {
195
		// 0x7FC0-0x7FCF memory mapped registers
196
		return nullptr; // not cacheable
197
	} else if (start < mainRom.size()) {
198
		return &mainRom[start];
199
	} else {
200
		return unmappedRead.data();
201
	}
202
}
203
204
byte* YamahaSKW01::getWriteCacheLine(uint16_t start)
205
{
206
	if ((start & CacheLine::HIGH) == (0x7FC0 & CacheLine::HIGH)) {
207
		// 0x7FC0-0x7FCF memory mapped registers
208
		return nullptr; // not cacheable
209
	} else {
210
		return unmappedWrite.data();
211
	}
212
}
213
214
template<typename Archive>
215
void YamahaSKW01::serialize(Archive& ar, unsigned version)
216
{
217
	ar.template serializeBase<MSXDevice>(*this);
218
	ar.serialize("fontAddress", fontAddress,
219
	             "dataAddress", dataAddress,
220
	             "SRAM"       , sram);
221
	if (printerPort) {
222
	             printerPort->serialize(ar, version);
223
	}
224
}
225
INSTANTIATE_SERIALIZE_METHODS(YamahaSKW01);
226
REGISTER_MSXDEVICE(YamahaSKW01, "YamahaSKW01");
227
228
} // namespace openmsx