-
Notifications
You must be signed in to change notification settings - Fork 98
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial implementation of Yamaha SKW-01.
This is the Kanji Word Processing Unit, which has some unique features: - non-standard kanji font ROM, accessible via non-standard method - 32kB main program ROM - 32kB data ROM - 2kB battery backed SRAM - printer port, with non-standard I/O mapping Thanks to several users on MRC, the device could be reverse-engineered in such a way that it is clear how the happy flow should work. And that was used to implement the emulation. See the references in the .cc file for the credits. Thanks to Wouter for reviewing and his suggestions.
- Loading branch information
1 parent
5f8a95d
commit 715361e
Showing
6 changed files
with
282 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
<?xml version="1.0" ?> | ||
<!DOCTYPE msxconfig SYSTEM 'msxconfig2.dtd'> | ||
<msxconfig> | ||
<info> | ||
<!-- Note: the switch to disable SRAM is not emulated! --> | ||
<name>Kanji Word Processor Unit</name> | ||
<manufacturer>Yamaha</manufacturer> | ||
<code>SKW-01</code> | ||
<release_year>1985</release_year> | ||
<description>Japanese Word Processor with Kanji ROM and printer port.</description> | ||
<type>kanji font expansion</type> | ||
</info> | ||
<devices> | ||
<primary slot="any"> | ||
<secondary slot="any"> | ||
<YamahaSKW01 id="Kanji Word Processor Unit"> | ||
<mem base="0x0000" size="0x8000"/> | ||
<rom id="main"> | ||
<sha1>6a7f41d89f9d50f4ff648233670660cfb07a41ee</sha1> | ||
<filename>skw-01_main.rom</filename> | ||
</rom> | ||
<rom id="kanjifont"> | ||
<sha1>f0901a63de6c95a42ce6ad82dd5e6bfba4bcf180</sha1> | ||
<filename>skw-01_kanjifont.rom</filename> | ||
</rom> | ||
<rom id="data"> | ||
<sha1>82f2ecd9f3135a8602d54cc6322216b3b15ae8b3</sha1> | ||
<filename>skw-01_data.rom</filename> | ||
</rom> | ||
<sramname>skw-01.sram</sramname> | ||
</YamahaSKW01> | ||
</secondary> | ||
</primary> | ||
|
||
</devices> | ||
</msxconfig> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
#include "YamahaSKW01.hh" | ||
#include "DummyPrinterPortDevice.hh" | ||
#include "CacheLine.hh" | ||
#include "MSXException.hh" | ||
#include "checked_cast.hh" | ||
#include "serialize.hh" | ||
|
||
// Implementation based on reverse-engineering of 'acet'/'uniabis', see: | ||
// https://www.msx.org/forum/msx-talk/hardware/trying-to-dump-yamaha-skw-01 | ||
// https://github.com/uniabis/msxrawl/tree/main/dumpskw1 | ||
// The printer port is assumed to work the same as a standard MSX printer port, | ||
// but when testing with the built in software, it doesn't seem to work | ||
// correctly, as the output is not what I would expect (when connecting an | ||
// emulated MSX Printer). | ||
// As the emulation is purely based on reverse engineering based on the built | ||
// in software, there may be plenty of details wrong. | ||
// Also note that the delay that is apparently present when writing the font | ||
// address registers (other wise the READY bit wouldn't need to be read out) is | ||
// not emulated. Because of that, the READY bit will always just be enabled. | ||
|
||
namespace openmsx { | ||
|
||
YamahaSKW01::YamahaSKW01(const DeviceConfig& config) | ||
: MSXDevice(config) | ||
, Connector(MSXDevice::getPluggingController(), MSXDevice::getName() + " printerport", | ||
std::make_unique<DummyPrinterPortDevice>()) | ||
, mainRom(MSXDevice::getName() + " main" , "rom", config, "main") | ||
, fontRom(MSXDevice::getName() + " kanjifont", "rom", config, "kanjifont") | ||
, dataRom(MSXDevice::getName() + " data" , "rom", config, "data") | ||
, sram(MSXDevice::getName() + " SRAM", 0x800, config) | ||
{ | ||
if (mainRom.size() != 32 * 1024) { | ||
throw MSXException("Main ROM must be exactly 32kB in size."); | ||
} | ||
if (fontRom.size() != 128 * 1024) { | ||
throw MSXException("Font ROM must be exactly 128kB in size."); | ||
} | ||
if (dataRom.size() != 32 * 1024) { | ||
throw MSXException("Data ROM must be exactly 32kB in size."); | ||
} | ||
|
||
reset(EmuTime::dummy()); | ||
} | ||
|
||
void YamahaSKW01::reset(EmuTime::param time) | ||
{ | ||
fontAddress = {0, 0, 0, 0}; | ||
dataAddress = 0; | ||
|
||
writeData(0, time); // TODO check this, see MSXPrinterPort | ||
setStrobe(true, time); // TODO check this, see MSXPrinterPort | ||
} | ||
|
||
byte YamahaSKW01::readMem(word address, EmuTime::param time) | ||
{ | ||
return peekMem(address, time); | ||
} | ||
|
||
void YamahaSKW01::writeMem(word address, byte value, EmuTime::param time) | ||
{ | ||
if (0x7FC0 <= address && address <= 0x7FC9) { | ||
unsigned group = (address - 0x7FC0) / 2; | ||
if ((address & 1) == 0) { | ||
// LSB address | ||
fontAddress[group] = (fontAddress[group] & 0xFF00) | (value << 0); | ||
} else { | ||
// MSB address | ||
fontAddress[group] = (fontAddress[group] & 0x00FF) | (value << 8); | ||
} | ||
} else if (address == 0x7FCA || address == 0x7FCB) { | ||
if ((dataAddress & (1 << 15)) != 0) { | ||
sram.write(dataAddress & 0x7FF, value); | ||
} | ||
} else if (address == 0x7FCC) { | ||
setStrobe(value & 1, time); | ||
} else if (address == 0x7FCE) { | ||
writeData(value, time); | ||
} | ||
} | ||
|
||
const byte* YamahaSKW01::getReadCacheLine(word start) const | ||
{ | ||
if ((start & CacheLine::HIGH) == (0x7FC0 & CacheLine::HIGH)) { | ||
// 0x7FC0-0x7FCF memory mapped registers | ||
return nullptr; // not cacheable | ||
} else if (start < 0x8000) { | ||
return &mainRom[start]; | ||
} else { | ||
return unmappedRead.data(); | ||
} | ||
} | ||
|
||
byte* YamahaSKW01::getWriteCacheLine(word start) const | ||
{ | ||
if ((start & CacheLine::HIGH) == (0x7FC0 & CacheLine::HIGH)) { | ||
// 0x7FC0-0x7FCF memory mapped registers | ||
return nullptr; // not cacheable | ||
} else { | ||
return unmappedWrite.data(); | ||
} | ||
} | ||
|
||
byte YamahaSKW01::peekMem(word address, EmuTime::param time) const | ||
{ | ||
if (address == one_of(0x7FC0, 0x7FC2, 0x7FC4, 0x7FC6)) { | ||
return 0x01; // for now, always READY to read | ||
} else if (address == one_of(0x7FC1, 0x7FC3, 0x7FC5, 0x7FC7)) { | ||
unsigned group = (address - 0x7FC1) / 2; | ||
unsigned base = 0x8000 * group; | ||
unsigned offset = fontAddress[group] & 0x7FFF; | ||
return fontRom[base + offset]; | ||
} else if (address == 0x7FCA || address == 0x7FCB) { | ||
if ((dataAddress & (1 << 15)) == 0) { | ||
return dataRom[dataAddress & 0x7FFF]; | ||
} else { | ||
return sram[dataAddress & 0x7FF]; | ||
} | ||
} else if (address == 0x7FCC) { | ||
// bit 1 = status / other bits always 1 | ||
return getPluggedPrintDev().getStatus(time) | ||
? 0xFF : 0xFD; | ||
} else if (address < 0x8000) { | ||
return mainRom[address]; | ||
} else { | ||
return 0xFF; | ||
} | ||
} | ||
|
||
void YamahaSKW01::setStrobe(bool newStrobe, EmuTime::param time) | ||
{ | ||
if (newStrobe != strobe) { | ||
strobe = newStrobe; | ||
getPluggedPrintDev().setStrobe(strobe, time); | ||
} | ||
} | ||
|
||
void YamahaSKW01::writeData(uint8_t newData, EmuTime::param time) | ||
{ | ||
if (newData != data) { | ||
data = newData; | ||
getPluggedPrintDev().writeData(data, time); | ||
} | ||
} | ||
|
||
std::string_view YamahaSKW01::getDescription() const | ||
{ | ||
return MSXDevice::getName() + " printer port"; | ||
} | ||
|
||
std::string_view YamahaSKW01::getClass() const | ||
{ | ||
return "Printer Port"; | ||
} | ||
|
||
void YamahaSKW01::plug(Pluggable& dev, EmuTime::param time) | ||
{ | ||
Connector::plug(dev, time); | ||
getPluggedPrintDev().writeData(data, time); | ||
getPluggedPrintDev().setStrobe(strobe, time); | ||
} | ||
|
||
PrinterPortDevice& YamahaSKW01::getPluggedPrintDev() const | ||
{ | ||
return *checked_cast<PrinterPortDevice*>(&getPlugged()); | ||
} | ||
|
||
template<typename Archive> | ||
void YamahaSKW01::serialize(Archive& ar, unsigned /*version*/) | ||
{ | ||
ar.template serializeBase<MSXDevice>(*this); | ||
ar.serialize("fontAddress", fontAddress, | ||
"dataAddress", dataAddress, | ||
"SRAM" , sram, | ||
"strobe" , strobe, | ||
"data" , data); | ||
// TODO force writing data to port?? (See MSXPrinterPort) | ||
} | ||
INSTANTIATE_SERIALIZE_METHODS(YamahaSKW01); | ||
REGISTER_MSXDEVICE(YamahaSKW01, "YamahaSKW01"); | ||
|
||
} // namespace openmsx |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
#ifndef YAMAHASKW01_HH | ||
#define YAMAHASKW01_HH | ||
|
||
#include "MSXDevice.hh" | ||
#include "Connector.hh" | ||
#include "SRAM.hh" | ||
#include "Rom.hh" | ||
#include <array> | ||
|
||
namespace openmsx { | ||
|
||
class PrinterPortDevice; | ||
|
||
class YamahaSKW01 final : public MSXDevice, public Connector | ||
{ | ||
public: | ||
explicit YamahaSKW01(const DeviceConfig& config); | ||
|
||
// MSXDevice | ||
void reset(EmuTime::param time) override; | ||
|
||
[[nodiscard]] byte readMem(word address, EmuTime::param time) override; | ||
void writeMem(word address, byte value, EmuTime::param time) override; | ||
[[nodiscard]] const byte* getReadCacheLine(word start) const override; | ||
[[nodiscard]] byte* getWriteCacheLine(word start) const override; | ||
[[nodiscard]] virtual byte peekMem(word address, EmuTime::param time) const override; | ||
|
||
// Connector | ||
[[nodiscard]] std::string_view getDescription() const override; | ||
[[nodiscard]] std::string_view getClass() const override; | ||
void plug(Pluggable& dev, EmuTime::param time) override; | ||
|
||
template<typename Archive> | ||
void serialize(Archive& ar, unsigned version); | ||
|
||
private: | ||
Rom mainRom; | ||
Rom fontRom; | ||
Rom dataRom; | ||
SRAM sram; | ||
std::array<uint16_t, 4> fontAddress; | ||
uint16_t dataAddress; | ||
|
||
// printer port stuff | ||
bool strobe = false; // != true | ||
uint8_t data = 255; // != 0 | ||
|
||
private: | ||
// printer port stuff | ||
[[nodiscard]] PrinterPortDevice& getPluggedPrintDev() const; | ||
|
||
void setStrobe(bool newStrobe, EmuTime::param time); | ||
void writeData(uint8_t newData, EmuTime::param time); | ||
}; | ||
|
||
} // namespace openmsx | ||
|
||
#endif |