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