openMSX
MSXCPU.cc
Go to the documentation of this file.
1 #include "MSXCPU.hh"
2 #include "MSXMotherBoard.hh"
3 #include "Debugger.hh"
4 #include "Scheduler.hh"
5 #include "IntegerSetting.hh"
6 #include "CPUCore.hh"
7 #include "Z80.hh"
8 #include "R800.hh"
9 #include "TclObject.hh"
10 #include "memory.hh"
11 #include "outer.hh"
12 #include "serialize.hh"
13 #include "unreachable.hh"
14 #include <cassert>
15 
16 using std::string;
17 using std::vector;
18 
19 namespace openmsx {
20 
22  : motherboard(motherboard_)
23  , traceSetting(
24  motherboard.getCommandController(), "cputrace",
25  "CPU tracing on/off", false, Setting::DONT_SAVE)
26  , diHaltCallback(
27  motherboard.getCommandController(), "di_halt_callback",
28  "Tcl proc called when the CPU executed a DI/HALT sequence")
29  , z80(make_unique<CPUCore<Z80TYPE>>(
30  motherboard, "z80", traceSetting,
31  diHaltCallback, EmuTime::zero))
32  , r800(motherboard.isTurboR()
34  motherboard, "r800", traceSetting,
35  diHaltCallback, EmuTime::zero)
36  : nullptr)
37  , timeInfo(motherboard.getMachineInfoCommand())
38  , z80FreqInfo(motherboard.getMachineInfoCommand(), "z80_freq", *z80)
39  , r800FreqInfo(r800
40  ? make_unique<CPUFreqInfoTopic>(
41  motherboard.getMachineInfoCommand(), "r800_freq", *r800)
42  : nullptr)
43  , debuggable(motherboard_)
44  , reference(EmuTime::zero)
45 {
46  z80Active = true; // setActiveCPU(CPU_Z80);
47  newZ80Active = z80Active;
48 
49  motherboard.getDebugger().setCPU(this);
50  motherboard.getScheduler().setCPU(this);
51  traceSetting.attach(*this);
52 
53  z80->freqLocked.attach(*this);
54  z80->freqValue.attach(*this);
55  if (r800) {
56  r800->freqLocked.attach(*this);
57  r800->freqValue.attach(*this);
58  }
59 }
60 
62 {
63  traceSetting.detach(*this);
64  z80->freqLocked.detach(*this);
65  z80->freqValue.detach(*this);
66  if (r800) {
67  r800->freqLocked.detach(*this);
68  r800->freqValue.detach(*this);
69  }
70  motherboard.getScheduler().setCPU(nullptr);
71  motherboard.getDebugger() .setCPU(nullptr);
72 }
73 
75 {
76  z80 ->setInterface(interface);
77  if (r800) r800->setInterface(interface);
78 }
79 
81 {
82  z80 ->doReset(time);
83  if (r800) r800->doReset(time);
84 
85  reference = time;
86 }
87 
89 {
90  if (cpu == CPU_R800) assert(r800);
91 
92  bool tmp = cpu == CPU_Z80;
93  if (tmp != z80Active) {
95  newZ80Active = tmp;
96  }
97 }
98 
99 void MSXCPU::setDRAMmode(bool dram)
100 {
101  assert(r800);
102  r800->setDRAMmode(dram);
103 }
104 
105 void MSXCPU::execute(bool fastForward)
106 {
107  if (z80Active != newZ80Active) {
108  EmuTime time = getCurrentTime();
109  z80Active = newZ80Active;
110  z80Active ? z80 ->warp(time)
111  : r800->warp(time);
112  invalidateMemCache(0x0000, 0x10000);
113  }
114  z80Active ? z80 ->execute(fastForward)
115  : r800->execute(fastForward);
116 }
117 
119 {
120  z80Active ? z80 ->exitCPULoopSync()
121  : r800->exitCPULoopSync();
122 }
124 {
125  z80Active ? z80 ->exitCPULoopAsync()
126  : r800->exitCPULoopAsync();
127 }
128 
129 EmuTime::param MSXCPU::getCurrentTime() const
130 {
131  return z80Active ? z80 ->getCurrentTime()
132  : r800->getCurrentTime();
133 }
134 
136 {
137  z80Active ? z80 ->setNextSyncPoint(time)
138  : r800->setNextSyncPoint(time);
139 }
140 
141 void MSXCPU::updateVisiblePage(byte page, byte primarySlot, byte secondarySlot)
142 {
143  invalidateMemCache(page * 0x4000, 0x4000);
144  if (r800) r800->updateVisiblePage(page, primarySlot, secondarySlot);
145 }
146 
147 void MSXCPU::invalidateMemCache(word start, unsigned size)
148 {
149  z80Active ? z80 ->invalidateMemCache(start, size)
150  : r800->invalidateMemCache(start, size);
151 }
152 
154 {
155  z80 ->raiseIRQ();
156  if (r800) r800->raiseIRQ();
157 }
159 {
160  z80 ->lowerIRQ();
161  if (r800) r800->lowerIRQ();
162 }
164 {
165  z80 ->raiseNMI();
166  if (r800) r800->raiseNMI();
167 }
169 {
170  z80 ->lowerNMI();
171  if (r800) r800->lowerNMI();
172 }
173 
174 bool MSXCPU::isM1Cycle(unsigned address) const
175 {
176  return z80Active ? z80 ->isM1Cycle(address)
177  : r800->isM1Cycle(address);
178 }
179 
180 void MSXCPU::setZ80Freq(unsigned freq)
181 {
182  z80->setFreq(freq);
183 }
184 
186 {
187  z80Active ? z80 ->wait(time)
188  : r800->wait(time);
189 }
190 
192 {
193  return z80Active ? z80 ->waitCycles(time, cycles)
194  : r800->waitCycles(time, cycles);
195 }
196 
198 {
199  return z80Active ? time
200  : r800->waitCycles(time, cycles);
201 }
202 
204 {
205  if (z80Active) {
206  return *z80;
207  } else {
208  return *r800;
209  }
210 }
211 
212 void MSXCPU::update(const Setting& setting)
213 {
214  z80 ->update(setting);
215  if (r800) r800->update(setting);
216  exitCPULoopSync();
217 }
218 
219 // Command
220 
222  Interpreter& interp, array_ref<TclObject> tokens,
223  TclObject& result) const
224 {
225  z80Active ? z80 ->disasmCommand(interp, tokens, result)
226  : r800->disasmCommand(interp, tokens, result);
227 }
228 
229 void MSXCPU::setPaused(bool paused)
230 {
231  if (z80Active) {
232  z80 ->setExtHALT(paused);
233  z80 ->exitCPULoopSync();
234  } else {
235  r800->setExtHALT(paused);
236  r800->exitCPULoopSync();
237  }
238 }
239 
240 
241 // class TimeInfoTopic
242 
243 MSXCPU::TimeInfoTopic::TimeInfoTopic(InfoCommand& machineInfoCommand)
244  : InfoTopic(machineInfoCommand, "time")
245 {
246 }
247 
248 void MSXCPU::TimeInfoTopic::execute(
249  array_ref<TclObject> /*tokens*/, TclObject& result) const
250 {
251  auto& cpu = OUTER(MSXCPU, timeInfo);
252  EmuDuration dur = cpu.getCurrentTime() - cpu.reference;
253  result.setDouble(dur.toDouble());
254 }
255 
256 string MSXCPU::TimeInfoTopic::help(const vector<string>& /*tokens*/) const
257 {
258  return "Prints the time in seconds that the MSX is powered on\n";
259 }
260 
261 
262 // class CPUFreqInfoTopic
263 
264 MSXCPU::CPUFreqInfoTopic::CPUFreqInfoTopic(
265  InfoCommand& machineInfoCommand,
266  const string& name_, CPUClock& clock_)
267  : InfoTopic(machineInfoCommand, name_)
268  , clock(clock_)
269 {
270 }
271 
272 void MSXCPU::CPUFreqInfoTopic::execute(
273  array_ref<TclObject> /*tokens*/, TclObject& result) const
274 {
275  result.setInt(clock.getFreq());
276 }
277 
278 string MSXCPU::CPUFreqInfoTopic::help(const vector<string>& /*tokens*/) const
279 {
280  return "Returns the actual frequency of this CPU.\n"
281  "This frequency can vary because:\n"
282  " - the user has overridden the freq via the '{z80,r800}_freq' setting\n"
283  " - (only on some MSX machines) the MSX software can switch the Z80 between 2 frequencies\n"
284  "See also the '{z80,r800}_freq_locked' setting.\n";
285 }
286 
287 
288 // class Debuggable
289 
290 static const char* const CPU_REGS_DESC =
291  "Registers of the active CPU (Z80 or R800).\n"
292  "Each byte in this debuggable represents one 8 bit register:\n"
293  " 0 -> A 1 -> F 2 -> B 3 -> C\n"
294  " 4 -> D 5 -> E 6 -> H 7 -> L\n"
295  " 8 -> A' 9 -> F' 10 -> B' 11 -> C'\n"
296  " 12 -> D' 13 -> E' 14 -> H' 15 -> L'\n"
297  " 16 -> IXH 17 -> IXL 18 -> IYH 19 -> IYL\n"
298  " 20 -> PCH 21 -> PCL 22 -> SPH 23 -> SPL\n"
299  " 24 -> I 25 -> R 26 -> IM 27 -> IFF1/2\n"
300  "The last position (27) contains the IFF1 and IFF2 flags in respectively\n"
301  "bit 0 and 1. Bit 2 contains 'IFF1 AND last-instruction-was-not-EI', so\n"
302  "this effectively indicates that the CPU could accept an interrupt at\n"
303  "the start of the current instruction.\n";
304 
305 MSXCPU::Debuggable::Debuggable(MSXMotherBoard& motherboard_)
306  : SimpleDebuggable(motherboard_, "CPU regs", CPU_REGS_DESC, 28)
307 {
308 }
309 
310 byte MSXCPU::Debuggable::read(unsigned address)
311 {
312  auto& cpu = OUTER(MSXCPU, debuggable);
313  const CPURegs& regs = cpu.getRegisters();
314  switch (address) {
315  case 0: return regs.getA();
316  case 1: return regs.getF();
317  case 2: return regs.getB();
318  case 3: return regs.getC();
319  case 4: return regs.getD();
320  case 5: return regs.getE();
321  case 6: return regs.getH();
322  case 7: return regs.getL();
323  case 8: return regs.getA2();
324  case 9: return regs.getF2();
325  case 10: return regs.getB2();
326  case 11: return regs.getC2();
327  case 12: return regs.getD2();
328  case 13: return regs.getE2();
329  case 14: return regs.getH2();
330  case 15: return regs.getL2();
331  case 16: return regs.getIXh();
332  case 17: return regs.getIXl();
333  case 18: return regs.getIYh();
334  case 19: return regs.getIYl();
335  case 20: return regs.getPCh();
336  case 21: return regs.getPCl();
337  case 22: return regs.getSPh();
338  case 23: return regs.getSPl();
339  case 24: return regs.getI();
340  case 25: return regs.getR();
341  case 26: return regs.getIM();
342  case 27: return 1 * regs.getIFF1() +
343  2 * regs.getIFF2() +
344  4 * (regs.getIFF1() && !regs.prevWasEI());
345  default: UNREACHABLE; return 0;
346  }
347 }
348 
349 void MSXCPU::Debuggable::write(unsigned address, byte value)
350 {
351  auto& cpu = OUTER(MSXCPU, debuggable);
352  CPURegs& regs = cpu.getRegisters();
353  switch (address) {
354  case 0: regs.setA(value); break;
355  case 1: regs.setF(value); break;
356  case 2: regs.setB(value); break;
357  case 3: regs.setC(value); break;
358  case 4: regs.setD(value); break;
359  case 5: regs.setE(value); break;
360  case 6: regs.setH(value); break;
361  case 7: regs.setL(value); break;
362  case 8: regs.setA2(value); break;
363  case 9: regs.setF2(value); break;
364  case 10: regs.setB2(value); break;
365  case 11: regs.setC2(value); break;
366  case 12: regs.setD2(value); break;
367  case 13: regs.setE2(value); break;
368  case 14: regs.setH2(value); break;
369  case 15: regs.setL2(value); break;
370  case 16: regs.setIXh(value); break;
371  case 17: regs.setIXl(value); break;
372  case 18: regs.setIYh(value); break;
373  case 19: regs.setIYl(value); break;
374  case 20: regs.setPCh(value); break;
375  case 21: regs.setPCl(value); break;
376  case 22: regs.setSPh(value); break;
377  case 23: regs.setSPl(value); break;
378  case 24: regs.setI(value); break;
379  case 25: regs.setR(value); break;
380  case 26:
381  if (value < 3) regs.setIM(value);
382  break;
383  case 27:
384  regs.setIFF1((value & 0x01) != 0);
385  regs.setIFF2((value & 0x02) != 0);
386  // can't change afterEI
387  break;
388  default:
389  UNREACHABLE;
390  }
391 }
392 
393 // version 1: initial version
394 // version 2: activeCPU,newCPU -> z80Active,newZ80Active
395 template<typename Archive>
396 void MSXCPU::serialize(Archive& ar, unsigned version)
397 {
398  if (ar.versionAtLeast(version, 2)) {
399  ar.serialize("z80", *z80);
400  if (r800) ar.serialize("r800", *r800);
401  ar.serialize("z80Active", z80Active);
402  ar.serialize("newZ80Active", newZ80Active);
403  } else {
404  // backwards-compatibility
405  assert(ar.isLoader());
406 
407  ar.serializeWithID("z80", *z80);
408  if (r800) ar.serializeWithID("r800", *r800);
409  CPUBase* activeCPU = nullptr;
410  CPUBase* newCPU = nullptr;
411  ar.serializePointerID("activeCPU", activeCPU);
412  ar.serializePointerID("newCPU", newCPU);
413  z80Active = activeCPU == z80.get();
414  if (newCPU) {
415  newZ80Active = newCPU == z80.get();
416  } else {
417  newZ80Active = z80Active;
418  }
419  }
420  ar.serialize("resetTime", reference);
421 }
423 
424 } // namespace openmsx
EmuTime waitCyclesR800(EmuTime::param time, unsigned cycles)
Definition: MSXCPU.cc:197
void setDouble(double value)
Definition: TclObject.cc:47
void raiseNMI()
This method raises a non-maskable interrupt.
Definition: MSXCPU.cc:163
void setI(byte x)
Definition: CPURegs.hh:107
void updateVisiblePage(byte page, byte primarySlot, byte secondarySlot)
Inform CPU of bank switch.
Definition: MSXCPU.cc:141
byte getC() const
Definition: CPURegs.hh:26
void setC(byte x)
Definition: CPURegs.hh:71
byte getL2() const
Definition: CPURegs.hh:38
void setH2(byte x)
Definition: CPURegs.hh:82
void setR(byte x)
Definition: CPURegs.hh:108
void lowerIRQ()
This methods lowers the maskable interrupt again.
Definition: MSXCPU.cc:158
void setNextSyncPoint(EmuTime::param time)
Definition: MSXCPU.cc:135
byte getR() const
Definition: CPURegs.hh:63
void exitCPULoopAsync()
See CPUCore::exitCPULoopAsync()
Definition: MSXCPU.cc:123
bool isM1Cycle(unsigned address) const
Should only be used from within a MSXDevice::readMem() method.
Definition: MSXCPU.cc:174
bool prevWasEI() const
Definition: CPURegs.hh:157
void setB(byte x)
Definition: CPURegs.hh:70
CPURegs & getRegisters()
Definition: MSXCPU.cc:203
byte getSPl() const
Definition: CPURegs.hh:46
void setD2(byte x)
Definition: CPURegs.hh:80
void doReset(EmuTime::param time)
Reset CPU.
Definition: MSXCPU.cc:80
void setIM(byte x)
Definition: CPURegs.hh:106
void setF2(byte x)
Definition: CPURegs.hh:77
void disasmCommand(Interpreter &interp, array_ref< TclObject > tokens, TclObject &result) const
Definition: MSXCPU.cc:221
void setCPU(MSXCPU *cpu_)
Definition: Debugger.hh:40
void setPaused(bool paused)
(un)pause CPU.
Definition: MSXCPU.cc:229
void setE(byte x)
Definition: CPURegs.hh:73
bool getIFF1() const
Definition: CPURegs.hh:64
void setPCh(byte x)
Definition: CPURegs.hh:88
void setIFF1(bool x)
Definition: CPURegs.hh:109
byte getF2() const
Definition: CPURegs.hh:32
void setIYh(byte x)
Definition: CPURegs.hh:86
byte getC2() const
Definition: CPURegs.hh:34
byte getA2() const
Definition: CPURegs.hh:31
MSXCPU(MSXMotherBoard &motherboard)
Definition: MSXCPU.cc:21
void invalidateMemCache(word start, unsigned size)
Invalidate the CPU its cache for the interval [start, start + size) For example MSXMemoryMapper and M...
Definition: MSXCPU.cc:147
byte getPCl() const
Definition: CPURegs.hh:44
byte getIYl() const
Definition: CPURegs.hh:42
void setDRAMmode(bool dram)
Sets DRAM or ROM mode (influences memory access speed for R800).
Definition: MSXCPU.cc:99
void attach(Observer< T > &observer)
Definition: Subject.hh:52
void setA2(byte x)
Definition: CPURegs.hh:76
void setPCl(byte x)
Definition: CPURegs.hh:89
byte getIM() const
Definition: CPURegs.hh:61
void setIYl(byte x)
Definition: CPURegs.hh:87
void wait(EmuTime::param time)
Definition: MSXCPU.cc:185
This class implements a subset of the proposal for std::array_ref (proposed for the next c++ standard...
Definition: array_ref.hh:19
void setCPU(MSXCPU *cpu_)
Definition: Scheduler.hh:42
byte getPCh() const
Definition: CPURegs.hh:43
void setSPl(byte x)
Definition: CPURegs.hh:91
EmuTime waitCycles(EmuTime::param time, unsigned cycles)
Definition: MSXCPU.cc:191
void serialize(Archive &ar, unsigned version)
Definition: MSXCPU.cc:396
double toDouble() const
Definition: EmuDuration.hh:46
void setA(byte x)
Definition: CPURegs.hh:68
void setL(byte x)
Definition: CPURegs.hh:75
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
void setH(byte x)
Definition: CPURegs.hh:74
byte getIYh() const
Definition: CPURegs.hh:41
byte getH() const
Definition: CPURegs.hh:29
unsigned char byte
8 bit unsigned integer
Definition: openmsx.hh:25
void setE2(byte x)
Definition: CPURegs.hh:81
byte getD() const
Definition: CPURegs.hh:27
void setB2(byte x)
Definition: CPURegs.hh:78
void raiseIRQ()
This method raises a maskable interrupt.
Definition: MSXCPU.cc:153
void lowerNMI()
This methods lowers the non-maskable interrupt again.
Definition: MSXCPU.cc:168
byte getB2() const
Definition: CPURegs.hh:33
void setInterface(MSXCPUInterface *interf)
Definition: MSXCPU.cc:74
byte getL() const
Definition: CPURegs.hh:30
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:804
void setL2(byte x)
Definition: CPURegs.hh:83
byte getD2() const
Definition: CPURegs.hh:35
byte getSPh() const
Definition: CPURegs.hh:45
void setIXl(byte x)
Definition: CPURegs.hh:85
void setSPh(byte x)
Definition: CPURegs.hh:90
void setZ80Freq(unsigned freq)
Switch the Z80 clock freq.
Definition: MSXCPU.cc:180
size_t size() const
byte getIXh() const
Definition: CPURegs.hh:39
void setF(byte x)
Definition: CPURegs.hh:69
void detach(Observer< T > &observer)
Definition: Subject.hh:58
void setD(byte x)
Definition: CPURegs.hh:72
byte getF() const
Definition: CPURegs.hh:24
void setInt(int value)
Definition: TclObject.cc:25
byte getB() const
Definition: CPURegs.hh:25
void exitCPULoopSync()
See CPUCore::exitCPULoopsync()
Definition: MSXCPU.cc:118
byte getA() const
Definition: CPURegs.hh:23
byte getE2() const
Definition: CPURegs.hh:36
void setActiveCPU(CPUType cpu)
Switch between Z80/R800.
Definition: MSXCPU.cc:88
byte getH2() const
Definition: CPURegs.hh:37
#define OUTER(type, member)
Definition: outer.hh:38
void setIFF2(bool x)
Definition: CPURegs.hh:110
void setC2(byte x)
Definition: CPURegs.hh:79
byte getE() const
Definition: CPURegs.hh:28
void setIXh(byte x)
Definition: CPURegs.hh:84
byte getIXl() const
Definition: CPURegs.hh:40
std::unique_ptr< T > make_unique()
Definition: memory.hh:27
byte getI() const
Definition: CPURegs.hh:62
bool getIFF2() const
Definition: CPURegs.hh:65
unsigned short word
16 bit unsigned integer
Definition: openmsx.hh:28
#define UNREACHABLE
Definition: unreachable.hh:35