openMSX
MSXCPU.cc
Go to the documentation of this file.
1 #include "MSXCPU.hh"
2 #include "MSXCPUInterface.hh"
3 #include "MSXMotherBoard.hh"
4 #include "Debugger.hh"
5 #include "Scheduler.hh"
6 #include "IntegerSetting.hh"
7 #include "CPUCore.hh"
8 #include "Z80.hh"
9 #include "R800.hh"
10 #include "TclObject.hh"
11 #include "outer.hh"
12 #include "ranges.hh"
13 #include "serialize.hh"
14 #include "unreachable.hh"
15 #include "xrange.hh"
16 #include <cassert>
17 #include <memory>
18 
19 using std::string;
20 using std::vector;
21 
22 namespace openmsx {
23 
25  : motherboard(motherboard_)
26  , traceSetting(
27  motherboard.getCommandController(), "cputrace",
28  "CPU tracing on/off", false, Setting::DONT_SAVE)
29  , diHaltCallback(
30  motherboard.getCommandController(), "di_halt_callback",
31  "Tcl proc called when the CPU executed a DI/HALT sequence")
32  , z80(std::make_unique<CPUCore<Z80TYPE>>(
33  motherboard, "z80", traceSetting,
34  diHaltCallback, EmuTime::zero()))
35  , r800(motherboard.isTurboR()
36  ? std::make_unique<CPUCore<R800TYPE>>(
37  motherboard, "r800", traceSetting,
38  diHaltCallback, EmuTime::zero())
39  : nullptr)
40  , timeInfo(motherboard.getMachineInfoCommand())
41  , z80FreqInfo(motherboard.getMachineInfoCommand(), "z80_freq", *z80)
42  , r800FreqInfo(r800
43  ? std::make_unique<CPUFreqInfoTopic>(
44  motherboard.getMachineInfoCommand(), "r800_freq", *r800)
45  : nullptr)
46  , debuggable(motherboard_)
47  , reference(EmuTime::zero())
48 {
49  z80Active = true; // setActiveCPU(CPU_Z80);
50  newZ80Active = z80Active;
51 
52  motherboard.getDebugger().setCPU(this);
53  motherboard.getScheduler().setCPU(this);
54  traceSetting.attach(*this);
55 
56  z80->freqLocked.attach(*this);
57  z80->freqValue.attach(*this);
58  if (r800) {
59  r800->freqLocked.attach(*this);
60  r800->freqValue.attach(*this);
61  }
62  invalidateMemCacheSlot();
63 }
64 
66 {
67  traceSetting.detach(*this);
68  z80->freqLocked.detach(*this);
69  z80->freqValue.detach(*this);
70  if (r800) {
71  r800->freqLocked.detach(*this);
72  r800->freqValue.detach(*this);
73  }
74  motherboard.getScheduler().setCPU(nullptr);
75  motherboard.getDebugger() .setCPU(nullptr);
76 }
77 
79 {
80  interface = interface_;
81  z80 ->setInterface(interface);
82  if (r800) r800->setInterface(interface);
83 }
84 
85 void MSXCPU::doReset(EmuTime::param time)
86 {
87  z80 ->doReset(time);
88  if (r800) r800->doReset(time);
89 
90  invalidateAllSlotsRWCache(0x0000, 0x10000);
91 
92  reference = time;
93 }
94 
96 {
97  if (cpu == CPU_R800) assert(r800);
98 
99  bool tmp = cpu == CPU_Z80;
100  if (tmp != z80Active) {
101  exitCPULoopSync();
102  newZ80Active = tmp;
103  }
104 }
105 
106 void MSXCPU::setDRAMmode(bool dram)
107 {
108  assert(r800);
109  r800->setDRAMmode(dram);
110 }
111 
112 void MSXCPU::execute(bool fastForward)
113 {
114  if (z80Active != newZ80Active) {
115  EmuTime time = getCurrentTime();
116  z80Active = newZ80Active;
117  z80Active ? z80 ->warp(time)
118  : r800->warp(time);
119  // copy Z80/R800 cache lines
120  auto zCache = z80 ->getCacheLines();
121  auto rCache = r800->getCacheLines();
122  auto from = z80Active ? rCache : zCache;
123  auto to = z80Active ? zCache : rCache;
124  std::copy_n(from.first, CacheLine::NUM, to.first );
125  std::copy_n(from.second, CacheLine::NUM, to.second);
126  }
127  z80Active ? z80 ->execute(fastForward)
128  : r800->execute(fastForward);
129 }
130 
132 {
133  z80Active ? z80 ->exitCPULoopSync()
134  : r800->exitCPULoopSync();
135 }
137 {
138  z80Active ? z80 ->exitCPULoopAsync()
139  : r800->exitCPULoopAsync();
140 }
141 
142 EmuTime::param MSXCPU::getCurrentTime() const
143 {
144  return z80Active ? z80 ->getCurrentTime()
145  : r800->getCurrentTime();
146 }
147 
148 void MSXCPU::setNextSyncPoint(EmuTime::param time)
149 {
150  z80Active ? z80 ->setNextSyncPoint(time)
151  : r800->setNextSyncPoint(time);
152 }
153 
154 void MSXCPU::invalidateMemCacheSlot()
155 {
156  ranges::fill(slots, 0);
157 
158  // nullptr: means not a valid entry and not yet attempted to fill this entry
159  for (auto i : xrange(16)) {
160  ranges::fill(slotReadLines[i], nullptr);
161  ranges::fill(slotWriteLines[i], nullptr);
162  }
163 }
164 
165 void MSXCPU::updateVisiblePage(byte page, byte primarySlot, byte secondarySlot)
166 {
167  byte from = slots[page];
168  byte to = 4 * primarySlot + secondarySlot;
169  slots[page] = to;
170 
171  auto [cpuReadLines, cpuWriteLines] = z80Active ? z80->getCacheLines() : r800->getCacheLines();
172 
173  unsigned first = page * (0x4000 / CacheLine::SIZE);
174  unsigned num = 0x4000 / CacheLine::SIZE;
175  std::copy_n(&cpuReadLines [first], num, &slotReadLines [from][first]);
176  std::copy_n(&slotReadLines [to][first], num, &cpuReadLines [first]);
177  std::copy_n(&cpuWriteLines [first], num, &slotWriteLines[from][first]);
178  std::copy_n(&slotWriteLines[to][first], num, &cpuWriteLines [first]);
179 
180  if (r800) r800->updateVisiblePage(page, primarySlot, secondarySlot);
181 }
182 
184 {
185  if (interface) interface->tick(CacheLineCounters::InvalidateAllSlots);
186  auto [cpuReadLines, cpuWriteLines] = z80Active ? z80->getCacheLines() : r800->getCacheLines();
187 
188  unsigned first = start / CacheLine::SIZE;
189  unsigned num = (size + CacheLine::SIZE - 1) / CacheLine::SIZE;
190  std::fill_n(cpuReadLines + first, num, nullptr); // nullptr: means not a valid entry and not
191  std::fill_n(cpuWriteLines + first, num, nullptr); // yet attempted to fill this entry
192 
193  for (auto i : xrange(16)) {
194  std::fill_n(slotReadLines [i] + first, num, nullptr);
195  std::fill_n(slotWriteLines[i] + first, num, nullptr);
196  }
197 }
198 
199 template<bool READ, bool WRITE, bool SUB_START>
200 void MSXCPU::setRWCache(unsigned start, unsigned size, const byte* rData, byte* wData, int ps, int ss,
201  const byte* disallowRead, const byte* disallowWrite)
202 {
203  if constexpr (!SUB_START) {
204  assert(rData == nullptr);
205  assert(wData == nullptr);
206  }
207 
208  // aligned on cache lines
209  assert((start & CacheLine::LOW) == 0);
210  assert((size & CacheLine::LOW) == 0);
211 
212  int slot = 4 * ps + ss;
213  unsigned page = start >> 14;
214  assert(((start + size - 1) >> 14) == page); // all in same page
215  if constexpr (SUB_START && READ) rData -= start;
216  if constexpr (SUB_START && WRITE) wData -= start;
217 
218  // select between 'active' or 'shadow' cache lines
219  auto [readLines, writeLines] = [&] {
220  if (slot == slots[page]) {
221  return z80Active ? z80->getCacheLines() : r800->getCacheLines();
222  } else {
223  return std::pair{slotReadLines [slot],
224  slotWriteLines[slot]};
225  }
226  }();
227 
228  unsigned first = start / CacheLine::SIZE;
229  readLines += first;
230  writeLines += first;
231  disallowRead += first;
232  disallowWrite += first;
233  unsigned num = size / CacheLine::SIZE;
234 
235  static auto* const NON_CACHEABLE = reinterpret_cast<byte*>(1);
236  for (auto i : xrange(num)) {
237  if constexpr (READ) readLines [i] = disallowRead [i] ? NON_CACHEABLE : rData;
238  if constexpr (WRITE) writeLines[i] = disallowWrite[i] ? NON_CACHEABLE : wData;
239  }
240 }
241 
242 static constexpr void extendForAlignment(unsigned& start, unsigned& size)
243 {
244  constexpr unsigned MASK = ~(CacheLine::LOW); // not CacheLine::HIGH because 0x0000ff00 != 0xffffff00
245 
246  auto end = start + size;
247  start &= MASK; // round down to cacheline
248  end = (end + CacheLine::SIZE - 1) & MASK; // round up to cacheline
249  size = end - start;
250 }
251 
252 void MSXCPU::invalidateRWCache(unsigned start, unsigned size, int ps, int ss,
253  const byte* disallowRead, const byte* disallowWrite)
254 {
255  // unaligned [start, start+size) is OK for invalidate, then simply invalidate a little more.
256  extendForAlignment(start, size);
257  setRWCache<true, true, false>(start, size, nullptr, nullptr, ps, ss, disallowRead, disallowWrite);
258 }
259 void MSXCPU::invalidateRCache(unsigned start, unsigned size, int ps, int ss,
260  const byte* disallowRead)
261 {
262  extendForAlignment(start, size);
263  setRWCache<true, false, false>(start, size, nullptr, nullptr, ps, ss, disallowRead, nullptr);
264 }
265 void MSXCPU::invalidateWCache(unsigned start, unsigned size, int ps, int ss,
266  const byte* disallowWrite)
267 {
268  extendForAlignment(start, size);
269  setRWCache<false, true, false>(start, size, nullptr, nullptr, ps, ss, nullptr, disallowWrite);
270 }
271 
272 void MSXCPU::fillRWCache(unsigned start, unsigned size, const byte* rData, byte* wData, int ps, int ss,
273  const byte* disallowRead, const byte* disallowWrite)
274 {
275  setRWCache<true, true, true>(start, size, rData, wData, ps, ss, disallowRead, disallowWrite);
276 }
277 void MSXCPU::fillRCache(unsigned start, unsigned size, const byte* rData, int ps, int ss,
278  const byte* disallowRead)
279 {
280  setRWCache<true, false, true>(start, size, rData, nullptr, ps, ss, disallowRead, nullptr);
281 }
282 void MSXCPU::fillWCache(unsigned start, unsigned size, byte* wData, int ps, int ss,
283  const byte* disallowWrite)
284 {
285  setRWCache<false, true, true>(start, size, nullptr, wData, ps, ss, nullptr, disallowWrite);
286 }
287 
289 {
290  z80 ->raiseIRQ();
291  if (r800) r800->raiseIRQ();
292 }
294 {
295  z80 ->lowerIRQ();
296  if (r800) r800->lowerIRQ();
297 }
299 {
300  z80 ->raiseNMI();
301  if (r800) r800->raiseNMI();
302 }
304 {
305  z80 ->lowerNMI();
306  if (r800) r800->lowerNMI();
307 }
308 
309 bool MSXCPU::isM1Cycle(unsigned address) const
310 {
311  return z80Active ? z80 ->isM1Cycle(address)
312  : r800->isM1Cycle(address);
313 }
314 
315 void MSXCPU::setZ80Freq(unsigned freq)
316 {
317  z80->setFreq(freq);
318 }
319 
320 void MSXCPU::wait(EmuTime::param time)
321 {
322  z80Active ? z80 ->wait(time)
323  : r800->wait(time);
324 }
325 
326 EmuTime MSXCPU::waitCyclesZ80(EmuTime::param time, unsigned cycles)
327 {
328  return z80Active ? z80 ->waitCycles(time, cycles)
329  : time;
330 }
331 
332 EmuTime MSXCPU::waitCyclesR800(EmuTime::param time, unsigned cycles)
333 {
334  return z80Active ? time
335  : r800->waitCycles(time, cycles);
336 }
337 
339 {
340  if (z80Active) {
341  return *z80;
342  } else {
343  return *r800;
344  }
345 }
346 
347 void MSXCPU::update(const Setting& setting) noexcept
348 {
349  z80 ->update(setting);
350  if (r800) r800->update(setting);
351  exitCPULoopSync();
352 }
353 
354 // Command
355 
357  Interpreter& interp, span<const TclObject> tokens,
358  TclObject& result) const
359 {
360  z80Active ? z80 ->disasmCommand(interp, tokens, result)
361  : r800->disasmCommand(interp, tokens, result);
362 }
363 
364 void MSXCPU::setPaused(bool paused)
365 {
366  if (z80Active) {
367  z80 ->setExtHALT(paused);
368  z80 ->exitCPULoopSync();
369  } else {
370  r800->setExtHALT(paused);
371  r800->exitCPULoopSync();
372  }
373 }
374 
375 
376 // class TimeInfoTopic
377 
378 MSXCPU::TimeInfoTopic::TimeInfoTopic(InfoCommand& machineInfoCommand)
379  : InfoTopic(machineInfoCommand, "time")
380 {
381 }
382 
383 void MSXCPU::TimeInfoTopic::execute(
384  span<const TclObject> /*tokens*/, TclObject& result) const
385 {
386  auto& cpu = OUTER(MSXCPU, timeInfo);
387  EmuDuration dur = cpu.getCurrentTime() - cpu.reference;
388  result = dur.toDouble();
389 }
390 
391 string MSXCPU::TimeInfoTopic::help(const vector<string>& /*tokens*/) const
392 {
393  return "Prints the time in seconds that the MSX is powered on\n";
394 }
395 
396 
397 // class CPUFreqInfoTopic
398 
399 MSXCPU::CPUFreqInfoTopic::CPUFreqInfoTopic(
400  InfoCommand& machineInfoCommand,
401  const string& name_, CPUClock& clock_)
402  : InfoTopic(machineInfoCommand, name_)
403  , clock(clock_)
404 {
405 }
406 
407 void MSXCPU::CPUFreqInfoTopic::execute(
408  span<const TclObject> /*tokens*/, TclObject& result) const
409 {
410  result = clock.getFreq();
411 }
412 
413 string MSXCPU::CPUFreqInfoTopic::help(const vector<string>& /*tokens*/) const
414 {
415  return "Returns the actual frequency of this CPU.\n"
416  "This frequency can vary because:\n"
417  " - the user has overridden the freq via the '{z80,r800}_freq' setting\n"
418  " - (only on some MSX machines) the MSX software can switch the Z80 between 2 frequencies\n"
419  "See also the '{z80,r800}_freq_locked' setting.\n";
420 }
421 
422 
423 // class Debuggable
424 
426  "Registers of the active CPU (Z80 or R800).\n"
427  "Each byte in this debuggable represents one 8 bit register:\n"
428  " 0 -> A 1 -> F 2 -> B 3 -> C\n"
429  " 4 -> D 5 -> E 6 -> H 7 -> L\n"
430  " 8 -> A' 9 -> F' 10 -> B' 11 -> C'\n"
431  " 12 -> D' 13 -> E' 14 -> H' 15 -> L'\n"
432  " 16 -> IXH 17 -> IXL 18 -> IYH 19 -> IYL\n"
433  " 20 -> PCH 21 -> PCL 22 -> SPH 23 -> SPL\n"
434  " 24 -> I 25 -> R 26 -> IM 27 -> IFF1/2\n"
435  "The last position (27) contains the IFF1 and IFF2 flags in respectively\n"
436  "bit 0 and 1. Bit 2 contains 'IFF1 AND last-instruction-was-not-EI', so\n"
437  "this effectively indicates that the CPU could accept an interrupt at\n"
438  "the start of the current instruction.\n";
439 
440 MSXCPU::Debuggable::Debuggable(MSXMotherBoard& motherboard_)
441  : SimpleDebuggable(motherboard_, "CPU regs", CPU_REGS_DESC, 28)
442 {
443 }
444 
445 byte MSXCPU::Debuggable::read(unsigned address)
446 {
447  auto& cpu = OUTER(MSXCPU, debuggable);
448  const CPURegs& regs = cpu.getRegisters();
449  switch (address) {
450  case 0: return regs.getA();
451  case 1: return regs.getF();
452  case 2: return regs.getB();
453  case 3: return regs.getC();
454  case 4: return regs.getD();
455  case 5: return regs.getE();
456  case 6: return regs.getH();
457  case 7: return regs.getL();
458  case 8: return regs.getA2();
459  case 9: return regs.getF2();
460  case 10: return regs.getB2();
461  case 11: return regs.getC2();
462  case 12: return regs.getD2();
463  case 13: return regs.getE2();
464  case 14: return regs.getH2();
465  case 15: return regs.getL2();
466  case 16: return regs.getIXh();
467  case 17: return regs.getIXl();
468  case 18: return regs.getIYh();
469  case 19: return regs.getIYl();
470  case 20: return regs.getPCh();
471  case 21: return regs.getPCl();
472  case 22: return regs.getSPh();
473  case 23: return regs.getSPl();
474  case 24: return regs.getI();
475  case 25: return regs.getR();
476  case 26: return regs.getIM();
477  case 27: return 1 * regs.getIFF1() +
478  2 * regs.getIFF2() +
479  4 * (regs.getIFF1() && !regs.prevWasEI());
480  default: UNREACHABLE; return 0;
481  }
482 }
483 
484 void MSXCPU::Debuggable::write(unsigned address, byte value)
485 {
486  auto& cpu = OUTER(MSXCPU, debuggable);
487  CPURegs& regs = cpu.getRegisters();
488  switch (address) {
489  case 0: regs.setA(value); break;
490  case 1: regs.setF(value); break;
491  case 2: regs.setB(value); break;
492  case 3: regs.setC(value); break;
493  case 4: regs.setD(value); break;
494  case 5: regs.setE(value); break;
495  case 6: regs.setH(value); break;
496  case 7: regs.setL(value); break;
497  case 8: regs.setA2(value); break;
498  case 9: regs.setF2(value); break;
499  case 10: regs.setB2(value); break;
500  case 11: regs.setC2(value); break;
501  case 12: regs.setD2(value); break;
502  case 13: regs.setE2(value); break;
503  case 14: regs.setH2(value); break;
504  case 15: regs.setL2(value); break;
505  case 16: regs.setIXh(value); break;
506  case 17: regs.setIXl(value); break;
507  case 18: regs.setIYh(value); break;
508  case 19: regs.setIYl(value); break;
509  case 20: regs.setPCh(value); break;
510  case 21: regs.setPCl(value); break;
511  case 22: regs.setSPh(value); break;
512  case 23: regs.setSPl(value); break;
513  case 24: regs.setI(value); break;
514  case 25: regs.setR(value); break;
515  case 26:
516  if (value < 3) regs.setIM(value);
517  break;
518  case 27:
519  regs.setIFF1((value & 0x01) != 0);
520  regs.setIFF2((value & 0x02) != 0);
521  // can't change afterEI
522  break;
523  default:
524  UNREACHABLE;
525  }
526 }
527 
528 // version 1: initial version
529 // version 2: activeCPU,newCPU -> z80Active,newZ80Active
530 template<typename Archive>
531 void MSXCPU::serialize(Archive& ar, unsigned version)
532 {
533  if (ar.versionAtLeast(version, 2)) {
534  ar.serialize("z80", *z80);
535  if (r800) ar.serialize("r800", *r800);
536  ar.serialize("z80Active", z80Active,
537  "newZ80Active", newZ80Active);
538  } else {
539  // backwards-compatibility
540  assert(Archive::IS_LOADER);
541 
542  ar.serializeWithID("z80", *z80);
543  if (r800) ar.serializeWithID("r800", *r800);
544  CPUBase* activeCPU = nullptr;
545  CPUBase* newCPU = nullptr;
546  ar.serializePointerID("activeCPU", activeCPU);
547  ar.serializePointerID("newCPU", newCPU);
548  z80Active = activeCPU == z80.get();
549  if (newCPU) {
550  newZ80Active = newCPU == z80.get();
551  } else {
552  newZ80Active = z80Active;
553  }
554  }
555  ar.serialize("resetTime", reference);
556 
557  if constexpr (Archive::IS_LOADER) {
558  invalidateMemCacheSlot();
559  invalidateAllSlotsRWCache(0x0000, 0x10000);
560  }
561 }
563 
564 } // namespace openmsx
void tick(ENUM e) const
void setCPU(MSXCPU *cpu_)
Definition: Debugger.hh:40
void setZ80Freq(unsigned freq)
Switch the Z80 clock freq.
Definition: MSXCPU.cc:315
void fillWCache(unsigned start, unsigned size, byte *wData, int ps, int ss, const byte *disallowWrite)
Definition: MSXCPU.cc:282
bool isM1Cycle(unsigned address) const
Should only be used from within a MSXDevice::readMem() method.
Definition: MSXCPU.cc:309
void lowerNMI()
This methods lowers the non-maskable interrupt again.
Definition: MSXCPU.cc:303
void fillRWCache(unsigned start, unsigned size, const byte *rData, byte *wData, int ps, int ss, const byte *disallowRead, const byte *disallowWrite)
Fill the read and write cache lines for a specific slot with the specified value.
Definition: MSXCPU.cc:272
void setActiveCPU(CPUType cpu)
Switch between Z80/R800.
Definition: MSXCPU.cc:95
void disasmCommand(Interpreter &interp, span< const TclObject > tokens, TclObject &result) const
Definition: MSXCPU.cc:356
void invalidateAllSlotsRWCache(word start, unsigned size)
Invalidate the CPU its cache for the interval [start, start + size) For example MSXMemoryMapper and M...
Definition: MSXCPU.cc:183
void fillRCache(unsigned start, unsigned size, const byte *rData, int ps, int ss, const byte *disallowRead)
Definition: MSXCPU.cc:277
void updateVisiblePage(byte page, byte primarySlot, byte secondarySlot)
Inform CPU of bank switch.
Definition: MSXCPU.cc:165
void invalidateRWCache(unsigned start, unsigned size, int ps, int ss, const byte *disallowRead, const byte *disallowWrite)
Similar to the method above, but only invalidates one specific slot.
Definition: MSXCPU.cc:252
CPURegs & getRegisters()
Definition: MSXCPU.cc:338
EmuTime waitCyclesR800(EmuTime::param time, unsigned cycles)
Definition: MSXCPU.cc:332
void exitCPULoopSync()
See CPUCore::exitCPULoopsync()
Definition: MSXCPU.cc:131
void setNextSyncPoint(EmuTime::param time)
Definition: MSXCPU.cc:148
EmuTime waitCyclesZ80(EmuTime::param time, unsigned cycles)
Definition: MSXCPU.cc:326
void raiseIRQ()
This method raises a maskable interrupt.
Definition: MSXCPU.cc:288
void raiseNMI()
This method raises a non-maskable interrupt.
Definition: MSXCPU.cc:298
void wait(EmuTime::param time)
Definition: MSXCPU.cc:320
void invalidateRCache(unsigned start, unsigned size, int ps, int ss, const byte *disallowRead)
Definition: MSXCPU.cc:259
void doReset(EmuTime::param time)
Reset CPU.
Definition: MSXCPU.cc:85
void setInterface(MSXCPUInterface *interf)
Definition: MSXCPU.cc:78
void setPaused(bool paused)
(un)pause CPU.
Definition: MSXCPU.cc:364
void exitCPULoopAsync()
See CPUCore::exitCPULoopAsync()
Definition: MSXCPU.cc:136
void setDRAMmode(bool dram)
Sets DRAM or ROM mode (influences memory access speed for R800).
Definition: MSXCPU.cc:106
void lowerIRQ()
This methods lowers the maskable interrupt again.
Definition: MSXCPU.cc:293
MSXCPU(MSXMotherBoard &motherboard)
Definition: MSXCPU.cc:24
void invalidateWCache(unsigned start, unsigned size, int ps, int ss, const byte *disallowWrite)
Definition: MSXCPU.cc:265
void setCPU(MSXCPU *cpu_)
Definition: Scheduler.hh:41
void detach(Observer< T > &observer)
Definition: Subject.hh:56
void attach(Observer< T > &observer)
Definition: Subject.hh:50
Definition: span.hh:126
static_string_view
constexpr unsigned NUM
Definition: CacheLine.hh:8
constexpr unsigned LOW
Definition: CacheLine.hh:9
constexpr unsigned SIZE
Definition: CacheLine.hh:7
This file implemented 3 utility functions:
Definition: Autofire.cc:9
@ InvalidateAllSlots
constexpr static_string_view CPU_REGS_DESC
Definition: MSXCPU.cc:425
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
void serialize(Archive &ar, T &t, unsigned version)
void fill(ForwardRange &&range, const T &value)
Definition: ranges.hh:197
size_t size(std::string_view utf8)
#define OUTER(type, member)
Definition: outer.hh:41
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:983
#define UNREACHABLE
Definition: unreachable.hh:38
constexpr auto xrange(T e)
Definition: xrange.hh:155
constexpr auto end(const zstring_view &x)
Definition: zstring_view.hh:84