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 "CliComm.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 (0x98 + i, vdp, this);
47 }
48 for (auto i : xrange(4)) {
49 error |= !cpuInterface.replace_IO_Out(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 (0x98 + i, this, vdp);
71 }
72 for (auto i : xrange(4)) {
73 cpuInterface.replace_IO_Out(0x98 + i, this, vdp);
74 }
75}
76
77void MSXMatsushita::reset(EmuTime::param /*time*/)
78{
79 color1 = color2 = pattern = address = 0; // TODO check this
80}
81
82byte MSXMatsushita::readSwitchedIO(word port, EmuTime::param time)
83{
84 // TODO: Port 7 and 8 can be read as well.
85 byte result = peekSwitchedIO(port, time);
86 switch (port & 0x0F) {
87 case 3:
88 pattern = (pattern << 2) | (pattern >> 6);
89 break;
90 case 9:
91 address = (address + 1) & 0x1FFF;
92 break;
93 }
94 return result;
95}
96
97byte MSXMatsushita::peekSwitchedIO(word port, EmuTime::param /*time*/) const
98{
99 switch (port & 0x0F) {
100 case 0:
101 return byte(~ID);
102 case 1: {
103 byte result = firmwareSwitch.getStatus() ? 0x7F : 0xFF;
104 // bit 0: turbo status, 0=on
105 if (turboEnabled) {
106 result &= ~0x01;
107 }
108 // bit 2: 0 = turbo feature available
109 if (turboAvailable) {
110 result &= ~0x04;
111 }
112 return result;
113 }
114 case 3:
115 return (((pattern & 0x80) ? color2 : color1) << 4)
116 | ((pattern & 0x40) ? color2 : color1);
117 case 9:
118 if (address < 0x800 && sram) {
119 return (*sram)[address];
120 } else {
121 return 0xFF;
122 }
123 default:
124 return 0xFF;
125 }
126}
127
128void MSXMatsushita::writeSwitchedIO(word port, byte value, EmuTime::param /*time*/)
129{
130 switch (port & 0x0F) {
131 case 1:
132 // the turboEnabled flag works even though no turbo is available
133 if (value & 1) {
134 // bit0 = 1 -> 3.5MHz
135 if (turboAvailable) {
136 getCPU().setZ80Freq(3579545);
137 }
138 turboEnabled = false;
139 } else {
140 // bit0 = 0 -> 5.3MHz
141 if (turboAvailable) {
142 getCPU().setZ80Freq(5369318); // 3579545 * 3/2
143 }
144 turboEnabled = true;
145 }
146 break;
147 case 3:
148 color2 = (value & 0xF0) >> 4;
149 color1 = value & 0x0F;
150 break;
151 case 4:
152 pattern = value;
153 break;
154 case 7:
155 // set address (low)
156 address = (address & 0xFF00) | value;
157 break;
158 case 8:
159 // set address (high)
160 address = (address & 0x00FF) | ((value & 0x1F) << 8);
161 break;
162 case 9:
163 // write sram
164 if (address < 0x800 && sram) {
165 sram->write(address, value);
166 }
167 address = (address + 1) & 0x1FFF;
168 break;
169 }
170}
171
172byte MSXMatsushita::readIO(word port, EmuTime::param time)
173{
174 // TODO also delay on read?
175 assert(vdp);
176 return vdp->readIO(port, time);
177}
178
179byte MSXMatsushita::peekIO(word port, EmuTime::param time) const
180{
181 assert(vdp);
182 return vdp->peekIO(port, time);
183}
184
185void MSXMatsushita::writeIO(word port, byte value, EmuTime::param time)
186{
187 assert(vdp);
188 delay(time);
189 vdp->writeIO(port, value, lastTime.getTime());
190}
191
192void MSXMatsushita::delay(EmuTime::param time)
193{
194 if (turboAvailable && turboEnabled) {
195 lastTime += 46; // 8us, like in S1990
196 if (time < lastTime.getTime()) {
197 cpu.wait(lastTime.getTime());
198 return;
199 }
200 }
201 lastTime.reset(time);
202}
203
204template<typename Archive>
205void MSXMatsushita::serialize(Archive& ar, unsigned version)
206{
207 ar.template serializeBase<MSXDevice>(*this);
208 // no need to serialize MSXSwitchedDevice base class
209
210 if (sram) ar.serialize("SRAM", *sram);
211 ar.serialize("address", address,
212 "color1", color1,
213 "color2", color2,
214 "pattern", pattern);
215
216 if (ar.versionAtLeast(version, 2)) {
217 ar.serialize("lastTime", lastTime,
218 "turboEnabled", turboEnabled);
219 } else {
220 // keep 'lastTime == zero'
221 // keep 'turboEnabled == false'
223 "Loading an old savestate: the timing of the CPU-VDP "
224 "communication emulation has changed. This may cause "
225 "synchronization problems in replay.");
226 }
227}
228
231
232} // namespace openmsx
void printWarning(std::string_view message)
Definition: CliComm.cc:10
constexpr void reset(EmuTime::param e)
Reset the clock to start ticking at the given time.
Definition: Clock.hh:102
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:313
void wait(EmuTime::param time)
Definition: MSXCPU.cc:318
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX.
Definition: MSXDevice.hh:34
virtual void init()
Definition: MSXDevice.cc:44
CliComm & getCliComm() const
Definition: MSXDevice.cc:141
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
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)
void init() override
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:64
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:642
byte peekIO(word port, EmuTime::param time) const override
Read a byte from a given IO port.
Definition: VDP.cc:985
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:965
std::string getName(KeyCode keyCode)
Translate key code to key name.
Definition: Keys.cc:730
This file implemented 3 utility functions:
Definition: Autofire.cc:9
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
REGISTER_MSXDEVICE(ChakkariCopy, "ChakkariCopy")
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
STL namespace.
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:1021
constexpr auto xrange(T e)
Definition: xrange.hh:133