openMSX
MSXMatsushita.cc
Go to the documentation of this file.
1#include "MSXMatsushita.hh"
2#include "MSXCPU.hh"
3#include "SRAM.hh"
4#include "VDP.hh"
5#include "MSXCPUInterface.hh"
6#include "MSXCliComm.hh"
7#include "MSXException.hh"
8#include "serialize.hh"
9#include "xrange.hh"
10
11namespace openmsx {
12
13static constexpr byte ID = 0x08;
14
16 : MSXDevice(config)
17 , MSXSwitchedDevice(getMotherBoard(), ID)
18 , cpu(getCPU()) // used frequently, so cache it
19 , firmwareSwitch(config)
20 , sram(config.findChild("sramname") ? std::make_unique<SRAM>(getName() + " SRAM", 0x800, config) : nullptr)
21 , turboAvailable(config.getChildDataAsBool("hasturbo", false))
22{
23 // TODO find out what ports 0x41 0x45 0x46 are used for
24
25 reset(EmuTime::dummy());
26}
27
29{
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 (byte(0x98 + i), vdp, this);
47 }
48 for (auto i : xrange(4)) {
49 error |= !cpuInterface.replace_IO_Out(byte(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}
58
60{
61 if (!vdp) return;
62 unwrap();
63}
64
65void MSXMatsushita::unwrap()
66{
67 // Unwrap the VDP ports.
68 auto& cpuInterface = getCPUInterface();
69 for (auto i : xrange(2)) {
70 cpuInterface.replace_IO_In (byte(0x98 + i), this, vdp);
71 }
72 for (auto i : xrange(4)) {
73 cpuInterface.replace_IO_Out(byte(0x98 + i), this, vdp);
74 }
75}
76
77void MSXMatsushita::reset(EmuTime::param /*time*/)
78{
79 // TODO check this
80 color1 = color2 = 0;
81 pattern = 0;
82 address = 0;
83}
84
85byte MSXMatsushita::readSwitchedIO(word port, EmuTime::param time)
86{
87 // TODO: Port 7 and 8 can be read as well.
88 byte result = peekSwitchedIO(port, time);
89 switch (port & 0x0F) {
90 case 3:
91 pattern = byte((pattern << 2) | (pattern >> 6));
92 break;
93 case 9:
94 address = (address + 1) & 0x1FFF;
95 break;
96 }
97 return result;
98}
99
100byte MSXMatsushita::peekSwitchedIO(word port, EmuTime::param /*time*/) const
101{
102 switch (port & 0x0F) {
103 case 0:
104 return byte(~ID);
105 case 1: {
106 byte result = firmwareSwitch.getStatus() ? 0x7F : 0xFF;
107 // bit 0: turbo status, 0=on
108 if (turboEnabled) {
109 result &= ~0x01;
110 }
111 // bit 2: 0 = turbo feature available
112 if (turboAvailable) {
113 result &= ~0x04;
114 }
115 return result;
116 }
117 case 3:
118 return byte((((pattern & 0x80) ? color2 : color1) << 4) |
119 (((pattern & 0x40) ? color2 : color1) << 0));
120 case 9:
121 if (address < 0x800 && sram) {
122 return (*sram)[address];
123 } else {
124 return 0xFF;
125 }
126 default:
127 return 0xFF;
128 }
129}
130
131void MSXMatsushita::writeSwitchedIO(word port, byte value, EmuTime::param /*time*/)
132{
133 switch (port & 0x0F) {
134 case 1:
135 // the turboEnabled flag works even though no turbo is available
136 if (value & 1) {
137 // bit0 = 1 -> 3.5MHz
138 if (turboAvailable) {
139 getCPU().setZ80Freq(3579545);
140 }
141 turboEnabled = false;
142 } else {
143 // bit0 = 0 -> 5.3MHz
144 if (turboAvailable) {
145 getCPU().setZ80Freq(5369318); // 3579545 * 3/2
146 }
147 turboEnabled = true;
148 }
149 break;
150 case 3:
151 color2 = (value & 0xF0) >> 4;
152 color1 = value & 0x0F;
153 break;
154 case 4:
155 pattern = value;
156 break;
157 case 7:
158 // set address (low)
159 address = (address & 0xFF00) | value;
160 break;
161 case 8:
162 // set address (high)
163 address = word((address & 0x00FF) | ((value & 0x1F) << 8));
164 break;
165 case 9:
166 // write sram
167 if (address < 0x800 && sram) {
168 sram->write(address, value);
169 }
170 address = (address + 1) & 0x1FFF;
171 break;
172 }
173}
174
175byte MSXMatsushita::readIO(word port, EmuTime::param time)
176{
177 // TODO also delay on read?
178 assert(vdp);
179 return vdp->readIO(port, time);
180}
181
182byte MSXMatsushita::peekIO(word port, EmuTime::param time) const
183{
184 assert(vdp);
185 return vdp->peekIO(port, time);
186}
187
188void MSXMatsushita::writeIO(word port, byte value, EmuTime::param time)
189{
190 assert(vdp);
191 delay(time);
192 vdp->writeIO(port, value, lastTime.getTime());
193}
194
195void MSXMatsushita::delay(EmuTime::param time)
196{
197 if (turboAvailable && turboEnabled) {
198 lastTime += 46; // 8us, like in S1990
199 if (time < lastTime.getTime()) {
200 cpu.wait(lastTime.getTime());
201 return;
202 }
203 }
204 lastTime.reset(time);
205}
206
207template<typename Archive>
208void MSXMatsushita::serialize(Archive& ar, unsigned version)
209{
210 ar.template serializeBase<MSXDevice>(*this);
211 // no need to serialize MSXSwitchedDevice base class
212
213 if (sram) ar.serialize("SRAM", *sram);
214 ar.serialize("address", address,
215 "color1", color1,
216 "color2", color2,
217 "pattern", pattern);
218
219 if (ar.versionAtLeast(version, 2)) {
220 ar.serialize("lastTime", lastTime,
221 "turboEnabled", turboEnabled);
222 } else {
223 // keep 'lastTime == zero'
224 // keep 'turboEnabled == false'
226 "Loading an old savestate: the timing of the CPU-VDP "
227 "communication emulation has changed. This may cause "
228 "synchronization problems in replay.");
229 }
230}
231
234
235} // namespace openmsx
#define REGISTER_MSXDEVICE(CLASS, NAME)
Definition MSXDevice.hh:356
void printWarning(std::string_view message)
Definition CliComm.cc:12
constexpr void reset(EmuTime::param e)
Reset the clock to start ticking at the given time.
Definition Clock.hh:103
constexpr EmuTime::param getTime() const
Gets the time at which the last clock tick occurred.
Definition Clock.hh:46
void setZ80Freq(unsigned freq)
Switch the Z80 clock freq.
Definition MSXCPU.cc:320
void wait(EmuTime::param time)
Definition MSXCPU.cc:325
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX.
Definition MSXDevice.hh:36
virtual void init()
Definition MSXDevice.cc:44
MSXCPU & getCPU() const
Definition MSXDevice.cc:129
const Devices & getReferences() const
Get the device references that are specified for this device.
Definition MSXDevice.cc:119
MSXCPUInterface & getCPUInterface() const
Definition MSXDevice.cc:133
MSXCliComm & getCliComm() const
Definition MSXDevice.cc:141
void writeIO(word port, byte value, EmuTime::param time) override
Write a byte to a given IO port at a certain time to this device.
byte readIO(word port, EmuTime::param time) override
Read a byte from an IO port at a certain time from this device.
void serialize(Archive &ar, unsigned version)
MSXMatsushita(const DeviceConfig &config)
void reset(EmuTime::param time) override
This method is called on reset.
byte peekSwitchedIO(word port, EmuTime::param time) const override
byte peekIO(word port, EmuTime::param time) const override
Read a byte from a given IO port.
void writeSwitchedIO(word port, byte value, EmuTime::param time) override
byte readSwitchedIO(word port, EmuTime::param time) override
Unified implementation of MSX Video Display Processors (VDPs).
Definition VDP.hh:67
void writeIO(word port, byte value, EmuTime::param time) override
Write a byte to a given IO port at a certain time to this device.
Definition VDP.cc:645
byte peekIO(word port, EmuTime::param time) const override
Read a byte from a given IO port.
Definition VDP.cc:1005
byte readIO(word port, EmuTime::param time) override
Read a byte from an IO port at a certain time from this device.
Definition VDP.cc:985
This file implemented 3 utility functions:
Definition Autofire.cc:11
uint8_t byte
8 bit unsigned integer
Definition openmsx.hh:26
uint16_t word
16 bit unsigned integer
Definition openmsx.hh:29
STL namespace.
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
constexpr auto xrange(T e)
Definition xrange.hh:132