openMSX
YamahaSKW01.cc
Go to the documentation of this file.
1#include "YamahaSKW01.hh"
3#include "CacheLine.hh"
4#include "MSXException.hh"
5#include "checked_cast.hh"
6#include "serialize.hh"
7
8// Implementation based on reverse-engineering of 'acet'/'uniabis', see:
9// https://www.msx.org/forum/msx-talk/hardware/trying-to-dump-yamaha-skw-01
10// https://github.com/uniabis/msxrawl/tree/main/dumpskw1
11// The printer port is assumed to work the same as a standard MSX printer port.
12// This seems to be OK, because when printing with the SKW-01 software and
13// selecting the "HR-5X" printer and an emulated MSX Printer plugged in, sane
14// output is produced.
15// As the emulation is purely based on reverse engineering based on the built
16// in software, there may be plenty of details wrong.
17// Also note that the delay that is apparently present when writing the font
18// address registers (other wise the READY bit wouldn't need to be read out) is
19// not emulated. Because of that, the READY bit will always just be enabled.
20
21namespace openmsx {
22
23// Printer port part of the SKW-01, as it needs to be optional in that class for the National CF-SM003
24
25YamahaSKW01PrinterPort::YamahaSKW01PrinterPort(PluggingController& pluggingController_, const std::string& name_)
26 : Connector(pluggingController_, name_, std::make_unique<DummyPrinterPortDevice>())
27{
28 reset(EmuTime::dummy());
29}
30
31void YamahaSKW01PrinterPort::reset(EmuTime::param time)
32{
33 writeData(0, time); // TODO check this, see MSXPrinterPort
34 setStrobe(true, time); // TODO check this, see MSXPrinterPort
35}
36
37bool YamahaSKW01PrinterPort::getStatus(EmuTime::param time) const
38{
39 return getPluggedPrintDev().getStatus(time);
40}
41
42void YamahaSKW01PrinterPort::setStrobe(bool newStrobe, EmuTime::param time)
43{
44 if (newStrobe != strobe) {
45 strobe = newStrobe;
46 getPluggedPrintDev().setStrobe(strobe, time);
47 }
48}
49
50void YamahaSKW01PrinterPort::writeData(uint8_t newData, EmuTime::param time)
51{
52 if (newData != data) {
53 data = newData;
54 getPluggedPrintDev().writeData(data, time);
55 }
56}
57
59{
60 return "Yamaha SKW-01 printer port";
61}
62
63std::string_view YamahaSKW01PrinterPort::getClass() const
64{
65 return "Printer Port";
66}
67
68void YamahaSKW01PrinterPort::plug(Pluggable& dev, EmuTime::param time)
69{
70 Connector::plug(dev, time);
71 getPluggedPrintDev().writeData(data, time);
72 getPluggedPrintDev().setStrobe(strobe, time);
73}
74
75PrinterPortDevice& YamahaSKW01PrinterPort::getPluggedPrintDev() const
76{
77 return *checked_cast<PrinterPortDevice*>(&getPlugged());
78}
79
80template<typename Archive>
81void YamahaSKW01PrinterPort::serialize(Archive& ar, unsigned /*version*/)
82{
83 ar.template serializeBase<Connector>(*this);
84 ar.serialize("strobe" , strobe,
85 "data" , data);
86 // TODO force writing data to port?? (See MSXPrinterPort)
87}
88
90 : MSXDevice(config)
91 , mainRom(MSXDevice::getName() + " main" , "rom", config, "main")
92 , fontRom(MSXDevice::getName() + " kanjifont", "rom", config, "kanjifont")
93 , dataRom(MSXDevice::getName() + " data" , "rom", config, "data")
94 , sram(MSXDevice::getName() + " SRAM", 0x800, config)
95{
96 if (config.getChildDataAsBool("hasprinterport", true)) {
97 printerPort.emplace(getPluggingController(), getName() + " printerport");
98 }
99 if (mainRom.size() != 32 * 1024) {
100 throw MSXException("Main ROM must be exactly 32kB in size.");
101 }
102 if (fontRom.size() != 128 * 1024) {
103 throw MSXException("Font ROM must be exactly 128kB in size.");
104 }
105 if (dataRom.size() != 32 * 1024) {
106 throw MSXException("Data ROM must be exactly 32kB in size.");
107 }
108
109 reset(EmuTime::dummy());
110}
111
112void YamahaSKW01::reset(EmuTime::param time)
113{
114 fontAddress = {0, 0, 0, 0};
115 dataAddress = 0;
116
117 if (printerPort) printerPort->reset(time);
118}
119
120byte YamahaSKW01::readMem(word address, EmuTime::param time)
121{
122 return peekMem(address, time);
123}
124
125byte YamahaSKW01::peekMem(word address, EmuTime::param time) const
126{
127 if (address == one_of(0x7FC0, 0x7FC2, 0x7FC4, 0x7FC6)) {
128 return 0x01; // for now, always READY to read
129 } else if (address == one_of(0x7FC1, 0x7FC3, 0x7FC5, 0x7FC7)) {
130 unsigned group = (address - 0x7FC1) / 2;
131 unsigned base = 0x8000 * group;
132 unsigned offset = fontAddress[group] & 0x7FFF;
133 return fontRom[base + offset];
134 } else if (address == 0x7FC8 || address == 0x7FC9) {
135 return 0xFF;
136 } else if (address == 0x7FCA || address == 0x7FCB) {
137 if ((dataAddress & (1 << 15)) == 0) {
138 return dataRom[dataAddress & 0x7FFF];
139 } else {
140 return sram[dataAddress & 0x7FF];
141 }
142 } else if (address == 0x7FCC && printerPort) {
143 // bit 1 = status / other bits always 1
144 return printerPort->getStatus(time)
145 ? 0xFF : 0xFD;
146 } else if (address < 0x8000) {
147 return mainRom[address];
148 } else {
149 return 0xFF;
150 }
151}
152
153void YamahaSKW01::writeMem(word address, byte value, EmuTime::param time)
154{
155 if (0x7FC0 <= address && address <= 0x7FC7) {
156 unsigned group = (address - 0x7FC0) / 2;
157 if ((address & 1) == 0) {
158 // LSB address
159 fontAddress[group] = (fontAddress[group] & 0xFF00) | uint16_t(value << 0);
160 } else {
161 // MSB address
162 fontAddress[group] = (fontAddress[group] & 0x00FF) | uint16_t(value << 8);
163 }
164 } else if (address == 0x7FC8) {
165 dataAddress = (dataAddress & 0xFF00) | uint16_t(value << 0);
166 } else if (address == 0x7FC9) {
167 dataAddress = (dataAddress & 0x00FF) | uint16_t(value << 8);
168 } else if (address == 0x7FCA || address == 0x7FCB) {
169 if ((dataAddress & (1 << 15)) != 0) {
170 sram.write(dataAddress & 0x7FF, value);
171 }
172 } else if (address == 0x7FCC && printerPort) {
173 printerPort->setStrobe(value & 1, time);
174 } else if (address == 0x7FCE && printerPort) {
175 printerPort->writeData(value, time);
176 }
177}
178
179const byte* YamahaSKW01::getReadCacheLine(word start) const
180{
181 if ((start & CacheLine::HIGH) == (0x7FC0 & CacheLine::HIGH)) {
182 // 0x7FC0-0x7FCF memory mapped registers
183 return nullptr; // not cacheable
184 } else if (start < 0x8000) {
185 return &mainRom[start];
186 } else {
187 return unmappedRead.data();
188 }
189}
190
192{
193 if ((start & CacheLine::HIGH) == (0x7FC0 & CacheLine::HIGH)) {
194 // 0x7FC0-0x7FCF memory mapped registers
195 return nullptr; // not cacheable
196 } else {
197 return unmappedWrite.data();
198 }
199}
200
201template<typename Archive>
202void YamahaSKW01::serialize(Archive& ar, unsigned version)
203{
204 ar.template serializeBase<MSXDevice>(*this);
205 ar.serialize("fontAddress", fontAddress,
206 "dataAddress", dataAddress,
207 "SRAM" , sram);
208 if (printerPort) {
209 printerPort->serialize(ar, version);
210 }
211}
214
215} // namespace openmsx
#define REGISTER_MSXDEVICE(CLASS, NAME)
Definition MSXDevice.hh:356
Represents something you can plug devices into.
Definition Connector.hh:21
Pluggable & getPlugged() const
Returns the Pluggable currently plugged in.
Definition Connector.hh:61
virtual void plug(Pluggable &device, EmuTime::param time)
This plugs a Pluggable in this Connector.
Definition Connector.cc:25
bool getChildDataAsBool(std::string_view name, bool defaultValue=false) const
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX.
Definition MSXDevice.hh:36
PluggingController & getPluggingController() const
Definition MSXDevice.cc:157
static std::array< byte, 0x10000 > unmappedRead
Definition MSXDevice.hh:306
virtual const std::string & getName() const
Returns a human-readable name for this device.
Definition MSXDevice.cc:375
static std::array< byte, 0x10000 > unmappedWrite
Definition MSXDevice.hh:307
Central administration of Connectors and Pluggables.
virtual bool getStatus(EmuTime::param time)=0
Returns the STATUS signal: false = low = ready, true = high = not ready.
virtual void writeData(uint8_t data, EmuTime::param time)=0
Sets the data signals.
virtual void setStrobe(bool strobe, EmuTime::param time)=0
Sets the strobe signal: false = low, true = high.
auto size() const
Definition Rom.hh:36
void write(size_t addr, byte value)
Definition SRAM.cc:64
void reset(EmuTime::param time)
void setStrobe(bool newStrobe, EmuTime::param time)
void plug(Pluggable &dev, EmuTime::param time) override
This plugs a Pluggable in this Connector.
void serialize(Archive &ar, unsigned version)
bool getStatus(EmuTime::param time) const
std::string_view getClass() const override
A Connector belong to a certain class.
YamahaSKW01PrinterPort(PluggingController &pluggingController, const std::string &name)
std::string_view getDescription() const override
Get a description for this connector.
void writeData(uint8_t newData, EmuTime::param time)
byte * getWriteCacheLine(word start) override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for writing.
YamahaSKW01(const DeviceConfig &config)
byte peekMem(word address, EmuTime::param time) const override
Read a byte from a given memory location.
void writeMem(word address, byte value, EmuTime::param time) override
Write a given byte to a given location at a certain time to this device.
void reset(EmuTime::param time) override
This method is called on reset.
byte readMem(word address, EmuTime::param time) override
Read a byte from a location at a certain time from this device.
void serialize(Archive &ar, unsigned version)
const byte * getReadCacheLine(word start) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for reading.
constexpr unsigned HIGH
Definition CacheLine.hh:10
This file implemented 3 utility functions:
Definition Autofire.cc:11
uint16_t word
16 bit unsigned integer
Definition openmsx.hh:29
STL namespace.
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)