22 : motherboard(motherboard_)
24 motherboard.getCommandController(),
"cputrace",
25 "CPU tracing on/off", false,
Setting::DONT_SAVE)
27 motherboard.getCommandController(),
"di_halt_callback",
28 "Tcl proc called when the CPU executed a DI/HALT sequence",
29 "default_di_halt_callback",
32 motherboard,
"z80", traceSetting,
33 diHaltCallback, EmuTime::zero()))
34 , r800(motherboard.isTurboR()
36 motherboard,
"r800", traceSetting,
37 diHaltCallback, EmuTime::zero())
39 , timeInfo(motherboard.getMachineInfoCommand())
40 , z80FreqInfo(motherboard.getMachineInfoCommand(),
"z80_freq", *z80)
42 ?
std::make_unique<CPUFreqInfoTopic>(
43 motherboard.getMachineInfoCommand(),
"r800_freq", *r800)
45 , debuggable(motherboard_)
49 traceSetting.
attach(*
this);
51 z80->freqLocked.attach(*
this);
52 z80->freqValue.attach(*
this);
54 r800->freqLocked.attach(*
this);
55 r800->freqValue.attach(*
this);
57 invalidateMemCacheSlot();
62 traceSetting.
detach(*
this);
63 z80->freqLocked.detach(*
this);
64 z80->freqValue.detach(*
this);
66 r800->freqLocked.detach(*
this);
67 r800->freqValue.detach(*
this);
75 interface = interface_;
76 z80 ->setInterface(interface);
77 if (r800) r800->setInterface(interface);
83 if (r800) r800->doReset(time);
95 if (tmp != z80Active) {
104 r800->setDRAMmode(dram);
107void MSXCPU::execute(
bool fastForward)
109 if (z80Active != newZ80Active) {
110 EmuTime time = getCurrentTime();
111 z80Active = newZ80Active;
112 z80Active ? z80 ->warp(time)
115 auto zCache = z80 ->getCacheLines();
116 auto rCache = r800->getCacheLines();
117 auto from = z80Active ? rCache : zCache;
118 auto to = z80Active ? zCache : rCache;
122 z80Active ? z80 ->execute(fastForward)
123 : r800->execute(fastForward);
128 z80Active ? z80 ->exitCPULoopSync()
129 : r800->exitCPULoopSync();
133 z80Active ? z80 ->exitCPULoopAsync()
134 : r800->exitCPULoopAsync();
137EmuTime::param MSXCPU::getCurrentTime()
const
139 return z80Active ? z80 ->getCurrentTime()
140 : r800->getCurrentTime();
145 z80Active ? z80 ->setNextSyncPoint(time)
146 : r800->setNextSyncPoint(time);
149void MSXCPU::invalidateMemCacheSlot()
154 for (
auto i :
xrange(16)) {
162 assert(primarySlot < 4);
163 assert(secondarySlot < 4);
165 byte from = slots[page];
166 byte to = narrow<byte>(4 * primarySlot + secondarySlot);
169 auto [cpuReadLines, cpuWriteLines] = z80Active ? z80->getCacheLines() : r800->getCacheLines();
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]);
178 if (r800) r800->updateVisiblePage(page, primarySlot, secondarySlot);
184 auto [cpuReadLines, cpuWriteLines] = z80Active ? z80->getCacheLines() : r800->getCacheLines();
191 for (
auto i :
xrange(16)) {
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)
202 if constexpr (!SUB_START) {
203 assert(rData ==
nullptr);
204 assert(wData ==
nullptr);
211 int slot = 4 * ps + ss;
212 unsigned page = start >> 14;
213 assert(((start +
size - 1) >> 14) == page);
214 if constexpr (SUB_START && READ) rData -= start;
215 if constexpr (SUB_START && WRITE) wData -= start;
218 auto [readLines, writeLines] = [&] {
219 if (slot == slots[page]) {
220 return z80Active ? z80->getCacheLines() : r800->getCacheLines();
222 return CacheLines{slotReadLines [slot],
223 slotWriteLines[slot]};
230 static auto*
const NON_CACHEABLE =
reinterpret_cast<byte*
>(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;
237static constexpr void extendForAlignment(
unsigned& start,
unsigned&
size)
248 std::span<const byte, 256> disallowRead,
249 std::span<const byte, 256> disallowWrite)
252 extendForAlignment(start,
size);
253 setRWCache<true, true, false>(start,
size,
nullptr,
nullptr, ps, ss, disallowRead, disallowWrite);
256 std::span<const byte, 256> disallowRead,
257 std::span<const byte, 256> disallowWrite)
259 extendForAlignment(start,
size);
260 setRWCache<true, false, false>(start,
size,
nullptr,
nullptr, ps, ss, disallowRead, disallowWrite);
263 std::span<const byte, 256> disallowRead,
264 std::span<const byte, 256> disallowWrite)
266 extendForAlignment(start,
size);
267 setRWCache<false, true, false>(start,
size,
nullptr,
nullptr, ps, ss, disallowRead, disallowWrite);
271 std::span<const byte, 256> disallowRead,
272 std::span<const byte, 256> disallowWrite)
274 setRWCache<true, true, true>(start,
size, rData, wData, ps, ss, disallowRead, disallowWrite);
277 std::span<const byte, 256> disallowRead,
278 std::span<const byte, 256> disallowWrite)
280 setRWCache<true, false, true>(start,
size, rData,
nullptr, ps, ss, disallowRead, disallowWrite);
283 std::span<const byte, 256> disallowRead,
284 std::span<const byte, 256> disallowWrite)
286 setRWCache<false, true, true>(start,
size,
nullptr, wData, ps, ss, disallowRead, disallowWrite);
292 if (r800) r800->raiseIRQ();
297 if (r800) r800->lowerIRQ();
302 if (r800) r800->raiseNMI();
307 if (r800) r800->lowerNMI();
312 return z80Active ? z80 ->isM1Cycle(address)
313 : r800->isM1Cycle(address);
323 z80Active ? z80 ->wait(time)
329 return z80Active ? z80 ->waitCycles(time, cycles)
335 return z80Active ? time
336 : r800->waitCycles(time, cycles);
351 if (r800) r800->update(
setting);
358 Interpreter& interp, std::span<const TclObject> tokens,
361 z80Active ? z80 ->disasmCommand(interp, tokens, result)
362 : r800->disasmCommand(interp, tokens, result);
368 z80 ->setExtHALT(paused);
369 z80 ->exitCPULoopSync();
371 r800->setExtHALT(paused);
372 r800->exitCPULoopSync();
379MSXCPU::TimeInfoTopic::TimeInfoTopic(
InfoCommand& machineInfoCommand)
384void MSXCPU::TimeInfoTopic::execute(
385 std::span<const TclObject> , TclObject& result)
const
388 EmuDuration dur = cpu.getCurrentTime() - cpu.reference;
389 result = dur.toDouble();
392std::string MSXCPU::TimeInfoTopic::help(std::span<const TclObject> )
const
394 return "Prints the time in seconds that the MSX is powered on\n";
400MSXCPU::CPUFreqInfoTopic::CPUFreqInfoTopic(
401 InfoCommand& machineInfoCommand,
402 const std::string& name_,
CPUClock& clock_)
403 : InfoTopic(machineInfoCommand, name_)
408void MSXCPU::CPUFreqInfoTopic::execute(
409 std::span<const TclObject> , TclObject& result)
const
411 result = clock.getFreq();
414std::string MSXCPU::CPUFreqInfoTopic::help(std::span<const TclObject> )
const
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";
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";
441MSXCPU::Debuggable::Debuggable(MSXMotherBoard& motherboard_)
442 : SimpleDebuggable(motherboard_,
"CPU regs", CPU_REGS_DESC, 28)
446byte MSXCPU::Debuggable::read(
unsigned address)
449 const CPURegs& regs = cpu.getRegisters();
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() +
480 4 * (regs.getIFF1() && !regs.prevWasEI()));
485void MSXCPU::Debuggable::write(
unsigned address,
byte value)
488 CPURegs& regs = cpu.getRegisters();
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;
517 if (value < 3) regs.setIM(value);
520 regs.setIFF1((value & 0x01) != 0);
521 regs.setIFF2((value & 0x02) != 0);
531template<
typename Archive>
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);
541 assert(Archive::IS_LOADER);
543 ar.serializeWithID(
"z80", *z80);
544 if (r800) ar.serializeWithID(
"r800", *r800);
547 ar.serializePointerID(
"activeCPU", activeCPU);
548 ar.serializePointerID(
"newCPU", newCPU);
549 z80Active = activeCPU == z80.get();
551 newZ80Active = newCPU == z80.get();
553 newZ80Active = z80Active;
556 ar.serialize(
"resetTime", reference);
558 if constexpr (Archive::IS_LOADER) {
559 invalidateMemCacheSlot();
void setCPU(MSXCPU *cpu_)
void setZ80Freq(unsigned freq)
Switch the Z80 clock freq.
bool isM1Cycle(unsigned address) const
Should only be used from within a MSXDevice::readMem() method.
void lowerNMI()
This methods lowers the non-maskable interrupt again.
void serialize(Archive &ar, unsigned version)
void setActiveCPU(CPUType cpu)
Switch between Z80/R800.
void invalidateAllSlotsRWCache(word start, unsigned size)
Invalidate the CPU its cache for the interval [start, start + size) For example MSXMemoryMapper and M...
void fillWCache(unsigned start, unsigned size, byte *wData, int ps, int ss, std::span< const byte, 256 > disallowRead, std::span< const byte, 256 > disallowWrite)
void updateVisiblePage(byte page, byte primarySlot, byte secondarySlot)
Inform CPU of bank switch.
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)
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.
void invalidateRCache(unsigned start, unsigned size, int ps, int ss, std::span< const byte, 256 > disallowRead, std::span< const byte, 256 > disallowWrite)
EmuTime waitCyclesR800(EmuTime::param time, unsigned cycles)
void exitCPULoopSync()
See CPUCore::exitCPULoopSync()
void disasmCommand(Interpreter &interp, std::span< const TclObject > tokens, TclObject &result) const
void setNextSyncPoint(EmuTime::param time)
EmuTime waitCyclesZ80(EmuTime::param time, unsigned cycles)
void raiseIRQ()
This method raises a maskable interrupt.
void raiseNMI()
This method raises a non-maskable interrupt.
void wait(EmuTime::param time)
void doReset(EmuTime::param time)
Reset CPU.
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.
void setPaused(bool paused)
(un)pause CPU.
void setInterface(MSXCPUInterface *interface)
void exitCPULoopAsync()
See CPUCore::exitCPULoopAsync()
void setDRAMmode(bool dram)
Sets DRAM or ROM mode (influences memory access speed for R800).
void lowerIRQ()
This methods lowers the maskable interrupt again.
MSXCPU(MSXMotherBoard &motherboard)
void invalidateWCache(unsigned start, unsigned size, int ps, int ss, std::span< const byte, 256 > disallowRead, std::span< const byte, 256 > disallowWrite)
Scheduler & getScheduler()
void setCPU(MSXCPU *cpu_)
void detach(Observer< T > &observer)
void attach(Observer< T > &observer)
This file implemented 3 utility functions:
uint8_t byte
8 bit unsigned integer
uint16_t word
16 bit unsigned integer
constexpr void fill(ForwardRange &&range, const T &value)
auto copy(InputRange &&range, OutputIter out)
size_t size(std::string_view utf8)
#define OUTER(type, member)
constexpr auto subspan(Range &&range, size_t offset, size_t count=std::dynamic_extent)
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
constexpr auto xrange(T e)
constexpr auto end(const zstring_view &x)