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