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