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