openMSX
CPUCore.cc
Go to the documentation of this file.
1// MEMORY EMULATION
2// ----------------
3//
4// Memory access emulation is a very important part of the CPU emulation.
5// Because they happen so frequently they really need to be executed as fast as
6// possible otherwise they will completely bring down the speed of the CPU
7// emulation.
8//
9// A very fast way to emulate memory accesses is by simply reading/writing to a
10// 64kb array (for a 16-bit address space). Unfortunately this doesn't allow
11// for memory mapped IO (MMIO). These are memory regions where read/writes
12// trigger side effects, so where we need to execute device-specific code on
13// read or writes. An alternative that does work with MMIO is for every access
14// execute a virtual method call, (this is the approach taken by most current
15// MSX emulators). Unfortunately this is also a lot slower.
16//
17// It is possible to combine the speed of array accesses with the flexibility
18// of virtual methods. In openMSX it's implemented as follows: the 64kb address
19// space is divided in 256 regions of 256 bytes (called cacheLines in the code
20// below). For each such region we store a pointer, if this pointer is nullptr
21// then we have to use the slow way (=virtual method call). If it is not nullptr,
22// the pointer points to a block of memory that can be directly accessed. In
23// some contexts accesses via the pointer are known as backdoor accesses while
24// the accesses directly to the device are known as front-door accesses.
25//
26// We keep different pointers for read and write accesses. This allows to also
27// implement ROMs efficiently: read is handled as regular RAM, but writes end
28// up in some dummy memory region. This region is called 'unmappedWrite' in the
29// code. There is also a special region 'unmappedRead', this region is filled
30// with 0xFF and can be used to model (parts of) a device that don't react to
31// reads (so reads return 0xFF).
32//
33// Because of bank switching (the MSX slot select mechanism, but also e.g.
34// MegaROM back switching) the memory map as seen by the Z80 is not static. This
35// means that the cacheLine pointers also need to change during runtime. To
36// solve this we made the bank switch code also responsible for invalidating the
37// cacheLines of the switched region. These pointers are filled-in again in a
38// lazy way: the first read or write to a cache line will first get this
39// pointer (see getReadCacheLine() and getWriteCacheLine() in the code below),
40// from then on this pointer is used for all further accesses to this region,
41// until the cache is invalidated again.
42//
43//
44// INSTRUCTION EMULATION
45// ---------------------
46//
47// UPDATE: the 'threaded interpreter model' is not enabled by default
48// main reason is the huge memory requirement while compiling
49// and that it doesn't work on non-gcc compilers
50//
51// The current implementation is based on a 'threaded interpreter model'. In
52// the text below I'll call the older implementation the 'traditional
53// interpreter model'. From a very high level these two models look like this:
54//
55// Traditional model:
56// while (!needExit()) {
57// byte opcode = fetch(PC++);
58// switch (opcode) {
59// case 0x00: nop(); break;
60// case 0x01: ld_bc_nn(); break;
61// ...
62// }
63// }
64//
65// Threaded model:
66// byte opcode = fetch(PC++); //
67// goto *(table[opcode]); // fetch-and-dispatch
68// // note: the goto * syntax is a gcc extension called computed-gotos
69//
70// op00: nop(); if (!needExit()) [fetch-and-dispatch];
71// op01: ld_bc_nn(); if (!needExit()) [fetch-and-dispatch];
72// ...
73//
74// In the first model there is a central place in the code that fetches (the
75// first byte of) the instruction and based on this byte jumps to the
76// appropriate routine. In the second model, this fetch-and-dispatch logic is
77// duplicated at the end of each instruction.
78//
79// Typically the 'dispatch' part in above paragraph is implemented (either by
80// the compiler or manually using computed goto's) via a jump table. Thus on
81// assembler level via an indirect jump. For the host CPU it's hard to predict
82// the destination address of such an indirect jump, certainly if there's only
83// one such jump for all dispatching (the traditional model). If each
84// instruction has its own indirect jump instruction (the threaded model), it
85// becomes a bit easier, because often one particular z80 instructions is
86// followed by a specific other z80 instruction (or one from a small subset).
87// For example a z80 'cp' instruction is most likely followed by a 'conditional
88// jump' z80 instruction. Modern CPUs are quite sensitive to
89// branch-(mis)predictions, so the above optimization helps quite a lot. I
90// measured a speedup of more than 10%!
91//
92// There is another advantage to the threaded model. Because also the
93// needExit() test is duplicated for each instruction, it becomes possible to
94// tweak it for individual instructions. But first let me explain this
95// exit-test in more detail.
96//
97// These are the main reasons why the emulator should stop emulating CPU
98// instructions:
99// 1) When other devices than the CPU must be emulated (e.g. video frame
100// rendering). In openMSX this is handled by the Scheduler class and
101// actually we don't exit the CPU loop (anymore) for this. Instead we
102// simply execute the device code as a subroutine. Each time right before
103// we access an IO port or do a front-door memory access, there is a check
104// whether we should emulate device code (search for schedule() in the code
105// below).
106// 2) To keep the inner CPU loop as fast as possible we don't check for IRQ,
107// NMI or HALT status in this loop. Instead this condition is checked only
108// once at the beginning outside of the loop (if there wasn't a pending IRQ
109// on the first instruction there also won't be one on the second
110// instruction, if all we did was emulating cpu instructions). Now when one
111// of these conditions changes, we must exit the inner loop and re-evaluate
112// them. For example after an EI instruction we must check the IRQ status
113// again.
114// 3) Various reasons like:
115// * Z80/R800 switch
116// * executing a Tcl command (could be a cpu-register debug read)
117// * exit the emulator
118// 4) 'once-in-a-while': To avoid threading problems and race conditions,
119// several threads in openMSX only 'schedule' work that will later be
120// executed by the main emulation thread. The main thread checks for such
121// task outside of the cpu emulation loop. So once-in-a-while we need to
122// exit the loop. The exact timing doesn't matter here because anyway the
123// relative timing between threads is undefined.
124// So for 1) we don't need to do anything (we don't actually exit). For 2) and
125// 3) we need to exit the loop as soon as possible (right after the current
126// instruction is finished). For 4) it's OK to exit 'eventually' (a few hundred
127// z80 instructions late is still OK).
128//
129// Condition 2) is implemented with the 'slowInstructions' mechanism. Condition
130// 3) via exitCPULoopSync() (may only get called by the main emulation thread)
131// and condition 4) is implemented via exitCPULoopAsync() (can be called from
132// any thread).
133//
134// Now back to the exit-test optimization: in the threaded model each
135// instruction ends with:
136//
137// if (needExit()) return
138// byte opcode = fetch(PC++);
139// goto *(table[opcode]);
140//
141// And if we look in more detail at fetch():
142//
143// if (canDoBackdoor(addr)) {
144// doBackdoorAccess(addr);
145// } else {
146// doFrontdoorAccess(addr);
147// }
148//
149// So there are in fact two checks per instruction. This can be reduced to only
150// one check with the following trick:
151//
152// !!!WRONG!!!
153// In the past we optimized this to only check canDoBackdoor() (and make sure
154// canDoBackdoor() returned false when needExit() would return true). This
155// worked rather well, except for one case: when we exit the CPU loop we also
156// check for pending Syncronization points. It is possible such a SyncPoint
157// raises the IRQ line. So it is important to check for exit after every
158// instruction, otherwise we would enter the IRQ routine a couple of
159// instructions too late.
160
161#include "CPUCore.hh"
162#include "MSXCPUInterface.hh"
163#include "Scheduler.hh"
164#include "MSXMotherBoard.hh"
165#include "CliComm.hh"
166#include "TclCallback.hh"
167#include "Dasm.hh"
168#include "Z80.hh"
169#include "R800.hh"
170#include "Thread.hh"
171#include "endian.hh"
172#include "inline.hh"
173#include "narrow.hh"
174#include "unreachable.hh"
175#include "xrange.hh"
176#include <array>
177#include <cassert>
178#include <iostream>
179#include <type_traits>
180
181
182//
183// #define USE_COMPUTED_GOTO
184//
185// Computed goto's are not enabled by default:
186// - Computed goto's are a gcc extension, it's not part of the official c++
187// standard. So this will only work if you use gcc as your compiler (it
188// won't work with visual c++ for example)
189// - This is only beneficial on CPUs with branch prediction for indirect jumps
190// and a reasonable amount of cache. For example it is very beneficial for a
191// intel core2 cpu (10% faster), but not for a ARM920 (a few percent slower)
192// - Compiling src/cpu/CPUCore.cc with computed goto's enabled is very demanding
193// on the compiler. On older gcc versions it requires up to 1.5GB of memory.
194// But even on more recent gcc versions it still requires around 700MB.
195//
196// Probably the easiest way to enable this, is to pass the -DUSE_COMPUTED_GOTO
197// flag to the compiler. This is for example done in the super-opt flavour.
198// See build/flavour-super-opt.mk
199
200#ifndef _MSC_VER
201 // [[maybe_unused]] on a label is not (yet?) officially part of c++
202 // Gcc/clang do support it (as an extension), but visual studio complains
203 // about it. Hence the different implementation for both.
204 #define MAYBE_UNUSED_LABEL [[maybe_unused]]
205#else
206 #pragma warning(disable : 4102) // unreferenced label
207 #define MAYBE_UNUSED_LABEL
208#endif
209
210
211namespace openmsx {
212
213enum Reg8 : int { A, F, B, C, D, E, H, L, IXH, IXL, IYH, IYL, REG_I, REG_R, DUMMY };
214enum Reg16 : int { AF, BC, DE, HL, IX, IY, SP };
215
216// flag positions
217static constexpr byte S_FLAG = 0x80;
218static constexpr byte Z_FLAG = 0x40;
219static constexpr byte Y_FLAG = 0x20;
220static constexpr byte H_FLAG = 0x10;
221static constexpr byte X_FLAG = 0x08;
222static constexpr byte V_FLAG = 0x04;
223static constexpr byte P_FLAG = V_FLAG;
224static constexpr byte N_FLAG = 0x02;
225static constexpr byte C_FLAG = 0x01;
226
227// flag-register lookup tables
228struct Table {
229 std::array<byte, 256> ZS;
230 std::array<byte, 256> ZSXY;
231 std::array<byte, 256> ZSP;
232 std::array<byte, 256> ZSPXY;
233 std::array<byte, 256> ZSPH;
234};
235
236static constexpr byte ZS0 = Z_FLAG;
237static constexpr byte ZSXY0 = Z_FLAG;
238static constexpr byte ZSP0 = Z_FLAG | V_FLAG;
239static constexpr byte ZSPXY0 = Z_FLAG | V_FLAG;
240static constexpr byte ZS255 = S_FLAG;
241static constexpr byte ZSXY255 = S_FLAG | X_FLAG | Y_FLAG;
242
243static constexpr Table initTables()
244{
245 Table table = {};
246
247 for (auto i_ : xrange(256)) {
248 auto i = narrow_cast<byte>(i_);
249 byte zFlag = (i == 0) ? Z_FLAG : 0;
250 byte sFlag = i & S_FLAG;
251 byte xFlag = i & X_FLAG;
252 byte yFlag = i & Y_FLAG;
253 byte vFlag = V_FLAG;
254 for (int v = 128; v != 0; v >>= 1) {
255 if (i & v) vFlag ^= V_FLAG;
256 }
257 table.ZS [i] = zFlag | sFlag;
258 table.ZSXY [i] = zFlag | sFlag | xFlag | yFlag;
259 table.ZSP [i] = zFlag | sFlag | vFlag;
260 table.ZSPXY[i] = zFlag | sFlag | xFlag | yFlag | vFlag;
261 table.ZSPH [i] = zFlag | sFlag | vFlag | H_FLAG;
262 }
263 assert(table.ZS [ 0] == ZS0);
264 assert(table.ZSXY [ 0] == ZSXY0);
265 assert(table.ZSP [ 0] == ZSP0);
266 assert(table.ZSPXY[ 0] == ZSPXY0);
267 assert(table.ZS [255] == ZS255);
268 assert(table.ZSXY [255] == ZSXY255);
269
270 return table;
271}
272
273static constexpr Table table = initTables();
274
275// Global variable, because it should be shared between Z80 and R800.
276// It must not be shared between the CPUs of different MSX machines, but
277// the (logical) lifetime of this variable cannot overlap between execution
278// of two MSX machines.
279static word start_pc;
280
281// conditions
282struct CondC { bool operator()(byte f) const { return (f & C_FLAG) != 0; } };
283struct CondNC { bool operator()(byte f) const { return !(f & C_FLAG); } };
284struct CondZ { bool operator()(byte f) const { return (f & Z_FLAG) != 0; } };
285struct CondNZ { bool operator()(byte f) const { return !(f & Z_FLAG); } };
286struct CondM { bool operator()(byte f) const { return (f & S_FLAG) != 0; } };
287struct CondP { bool operator()(byte f) const { return !(f & S_FLAG); } };
288struct CondPE { bool operator()(byte f) const { return (f & V_FLAG) != 0; } };
289struct CondPO { bool operator()(byte f) const { return !(f & V_FLAG); } };
290struct CondTrue { bool operator()(byte /*f*/) const { return true; } };
291
292template<typename T> CPUCore<T>::CPUCore(
293 MSXMotherBoard& motherboard_, const std::string& name,
294 const BooleanSetting& traceSetting_,
295 TclCallback& diHaltCallback_, EmuTime::param time)
296 : CPURegs(T::IS_R800)
297 , T(time, motherboard_.getScheduler())
298 , motherboard(motherboard_)
299 , scheduler(motherboard.getScheduler())
300 , traceSetting(traceSetting_)
301 , diHaltCallback(diHaltCallback_)
302 , IRQStatus(motherboard.getDebugger(), name + ".pendingIRQ",
303 "Non-zero if there are pending IRQs (thus CPU would enter "
304 "interrupt routine in EI mode).",
305 0)
306 , IRQAccept(motherboard.getDebugger(), name + ".acceptIRQ",
307 "This probe is only useful to set a breakpoint on (the value "
308 "return by read is meaningless). The breakpoint gets triggered "
309 "right after the CPU accepted an IRQ.")
310 , freqLocked(
311 motherboard.getCommandController(), tmpStrCat(name, "_freq_locked"),
312 "real (locked) or custom (unlocked) CPU frequency",
313 true)
314 , freqValue(
315 motherboard.getCommandController(), tmpStrCat(name, "_freq"),
316 "custom CPU frequency (only valid when unlocked)",
317 T::CLOCK_FREQ, 1000000, 1000000000)
318 , freq(T::CLOCK_FREQ)
319 , tracingEnabled(traceSetting.getBoolean())
320 , isCMOS(motherboard.hasToshibaEngine()) // Toshiba MSX-ENGINEs embed a CMOS Z80
321{
322 static_assert(!std::is_polymorphic_v<CPUCore<T>>,
323 "keep CPUCore non-virtual to keep PC at offset 0");
324 doSetFreq();
325 doReset(time);
326}
327
328template<typename T> void CPUCore<T>::warp(EmuTime::param time)
329{
330 assert(T::getTimeFast() <= time);
331 T::setTime(time);
332}
333
334template<typename T> EmuTime::param CPUCore<T>::getCurrentTime() const
335{
336 return T::getTime();
337}
338
339template<typename T> void CPUCore<T>::doReset(EmuTime::param time)
340{
341 // AF and SP are 0xFFFF
342 // PC, R, IFF1, IFF2, HALT and IM are 0x0
343 // all others are random
344 setAF(0xFFFF);
345 setBC(0xFFFF);
346 setDE(0xFFFF);
347 setHL(0xFFFF);
348 setIX(0xFFFF);
349 setIY(0xFFFF);
350 setPC(0x0000);
351 setSP(0xFFFF);
352 setAF2(0xFFFF);
353 setBC2(0xFFFF);
354 setDE2(0xFFFF);
355 setHL2(0xFFFF);
356 setIFF1(false);
357 setIFF2(false);
358 setHALT(false);
359 setExtHALT(false);
360 setIM(0);
361 setI(0x00);
362 setR(0x00);
363 T::setMemPtr(0xFFFF);
364 clearPrevious();
365
366 // We expect this assert to be valid
367 // assert(T::getTimeFast() <= time); // time shouldn't go backwards
368 // But it's disabled for the following reason:
369 // 'motion' (IRC nickname) managed to create a replay file that
370 // contains a reset command that falls in the middle of a Z80
371 // instruction. Replayed commands go via the Scheduler, and are
372 // (typically) executed right after a complete CPU instruction. So
373 // the CPU is (slightly) ahead in time of the about to be executed
374 // reset command.
375 // Normally this situation should never occur: console commands,
376 // hotkeys, commands over cliComm, ... are all handled via the global
377 // event mechanism. Such global events are scheduled between CPU
378 // instructions, so also in a replay they should fall between CPU
379 // instructions.
380 // However if for some reason the timing of the emulation changed
381 // (improved emulation accuracy or a bug so that emulation isn't
382 // deterministic or the replay file was edited, ...), then the above
383 // reasoning no longer holds and the assert can trigger.
384 // We need to be robust against loading older replays (when emulation
385 // timing has changed). So in that respect disabling the assert is
386 // good. Though in the example above (motion's replay) it's not clear
387 // whether the assert is really triggered by mixing an old replay
388 // with a newer openMSX version. In any case so far we haven't been
389 // able to reproduce this assert by recording and replaying using a
390 // single openMSX version.
391 T::setTime(time);
392
393 assert(NMIStatus == 0); // other devices must reset their NMI source
394 assert(IRQStatus == 0); // other devices must reset their IRQ source
395}
396
397// I believe the following two methods are thread safe even without any
398// locking. The worst that can happen is that we occasionally needlessly
399// exit the CPU loop, but that's harmless
400// TODO thread issues are always tricky, can someone confirm this really
401// is thread safe
402template<typename T> void CPUCore<T>::exitCPULoopAsync()
403{
404 // can get called from non-main threads
405 exitLoop = true;
406}
407template<typename T> void CPUCore<T>::exitCPULoopSync()
408{
409 assert(Thread::isMainThread());
410 exitLoop = true;
411 T::disableLimit();
412}
413template<typename T> inline bool CPUCore<T>::needExitCPULoop()
414{
415 // always executed in main thread
416 if (exitLoop) [[unlikely]] {
417 // Note: The test-and-set is _not_ atomic! But that's fine.
418 // An atomic implementation is trivial (see below), but
419 // this version (at least on x86) avoids the more expensive
420 // instructions on the likely path.
421 exitLoop = false;
422 return true;
423 }
424 return false;
425
426 // Alternative implementation:
427 // atomically set to false and return the old value
428 //return exitLoop.exchange(false);
429}
430
431template<typename T> void CPUCore<T>::setSlowInstructions()
432{
433 slowInstructions = 2;
434 T::disableLimit();
435}
436
437template<typename T> void CPUCore<T>::raiseIRQ()
438{
439 assert(IRQStatus >= 0);
440 if (IRQStatus == 0) {
441 setSlowInstructions();
442 }
443 IRQStatus = IRQStatus + 1;
444}
445
446template<typename T> void CPUCore<T>::lowerIRQ()
447{
448 IRQStatus = IRQStatus - 1;
449 assert(IRQStatus >= 0);
450}
451
452template<typename T> void CPUCore<T>::raiseNMI()
453{
454 assert(NMIStatus >= 0);
455 if (NMIStatus == 0) {
456 nmiEdge = true;
457 setSlowInstructions();
458 }
459 NMIStatus++;
460}
461
462template<typename T> void CPUCore<T>::lowerNMI()
463{
464 NMIStatus--;
465 assert(NMIStatus >= 0);
466}
467
468template<typename T> bool CPUCore<T>::isM1Cycle(unsigned address) const
469{
470 // This method should only be called from within a MSXDevice::readMem()
471 // method. It can be used to check whether the current read action has
472 // the M1 pin active. The 'address' parameter that is give to readMem()
473 // should be passed (unchanged) to this method.
474 //
475 // This simple implementation works because the rest of the CPUCore
476 // code is careful to only update the PC register on M1 cycles. In
477 // practice that means that the PC is (only) updated at the very end of
478 // every instruction, even if is a multi-byte instruction. Or for
479 // prefix-instructions the PC is also updated after the prefix is
480 // fetched (because such instructions activate M1 twice).
481 return address == getPC();
482}
483
484template<typename T> void CPUCore<T>::wait(EmuTime::param time)
485{
486 assert(time >= getCurrentTime());
487 scheduler.schedule(time);
488 T::advanceTime(time);
489}
490
491template<typename T> EmuTime CPUCore<T>::waitCycles(EmuTime::param time, unsigned cycles)
492{
493 T::add(cycles);
494 EmuTime time2 = T::calcTime(time, cycles);
495 // note: time2 is not necessarily equal to T::getTime() because of the
496 // way how WRITE_PORT() is implemented.
497 scheduler.schedule(time2);
498 return time2;
499}
500
501template<typename T> void CPUCore<T>::setNextSyncPoint(EmuTime::param time)
502{
503 T::setLimit(time);
504}
505
506
507static constexpr char toHex(byte x)
508{
509 return narrow<char>((x < 10) ? (x + '0') : (x - 10 + 'A'));
510}
511static constexpr void toHex(byte x, std::span<char, 3> buf)
512{
513 buf[0] = toHex(x / 16);
514 buf[1] = toHex(x & 15);
515}
516
517template<typename T> void CPUCore<T>::disasmCommand(
518 Interpreter& interp, std::span<const TclObject> tokens, TclObject& result) const
519{
520 word address = (tokens.size() < 3) ? getPC() : word(tokens[2].getInt(interp));
521 std::array<byte, 4> outBuf;
522 std::string dasmOutput;
523 unsigned len = dasm(*interface, address, outBuf, dasmOutput,
524 T::getTimeFast());
525 result.addListElement(dasmOutput);
526 std::array<char, 3> tmp; tmp[2] = 0;
527 for (auto i : xrange(len)) {
528 toHex(outBuf[i], tmp);
529 result.addListElement(tmp.data());
530 }
531}
532
533template<typename T> void CPUCore<T>::update(const Setting& setting) noexcept
534{
535 if (&setting == &freqLocked) {
536 doSetFreq();
537 } else if (&setting == &freqValue) {
538 doSetFreq();
539 } else if (&setting == &traceSetting) {
540 tracingEnabled = traceSetting.getBoolean();
541 }
542}
543
544template<typename T> void CPUCore<T>::setFreq(unsigned freq_)
545{
546 freq = freq_;
547 doSetFreq();
548}
549
550template<typename T> void CPUCore<T>::doSetFreq()
551{
552 if (freqLocked.getBoolean()) {
553 // locked, use value set via setFreq()
554 T::setFreq(freq);
555 } else {
556 // unlocked, use value set by user
557 T::setFreq(freqValue.getInt());
558 }
559}
560
561
562template<typename T> inline byte CPUCore<T>::READ_PORT(word port, unsigned cc)
563{
564 EmuTime time = T::getTimeFast(cc);
565 scheduler.schedule(time);
566 byte result = interface->readIO(port, time);
567 // note: no forced page-break after IO
568 return result;
569}
570
571template<typename T> inline void CPUCore<T>::WRITE_PORT(word port, byte value, unsigned cc)
572{
573 EmuTime time = T::getTimeFast(cc);
574 scheduler.schedule(time);
575 interface->writeIO(port, value, time);
576 // note: no forced page-break after IO
577}
578
579template<typename T> template<bool PRE_PB, bool POST_PB>
580NEVER_INLINE byte CPUCore<T>::RDMEMslow(unsigned address, unsigned cc)
581{
582 interface->tick(CacheLineCounters::NonCachedRead);
583 // not cached
584 unsigned high = address >> CacheLine::BITS;
585 if (readCacheLine[high] == nullptr) {
586 // try to cache now (not a valid entry, and not yet tried)
587 auto addrBase = narrow_cast<word>(address & CacheLine::HIGH);
588 if (const byte* line = interface->getReadCacheLine(addrBase)) {
589 // cached ok
590 T::template PRE_MEM<PRE_PB, POST_PB>(address);
591 T::template POST_MEM< POST_PB>(address);
592 readCacheLine[high] = line - addrBase;
593 return readCacheLine[high][address];
594 }
595 }
596 // uncacheable
597 readCacheLine[high] = reinterpret_cast<const byte*>(1);
598 T::template PRE_MEM<PRE_PB, POST_PB>(address);
599 EmuTime time = T::getTimeFast(cc);
600 scheduler.schedule(time);
601 byte result = interface->readMem(narrow_cast<word>(address), time);
602 T::template POST_MEM<POST_PB>(address);
603 return result;
604}
605template<typename T> template<bool PRE_PB, bool POST_PB>
606ALWAYS_INLINE byte CPUCore<T>::RDMEM_impl2(unsigned address, unsigned cc)
607{
608 const byte* line = readCacheLine[address >> CacheLine::BITS];
609 if (uintptr_t(line) > 1) [[likely]] {
610 // cached, fast path
611 T::template PRE_MEM<PRE_PB, POST_PB>(address);
612 T::template POST_MEM< POST_PB>(address);
613 return line[address];
614 } else {
615 return RDMEMslow<PRE_PB, POST_PB>(address, cc); // not inlined
616 }
617}
618template<typename T> template<bool PRE_PB, bool POST_PB>
619ALWAYS_INLINE byte CPUCore<T>::RDMEM_impl(unsigned address, unsigned cc)
620{
621 constexpr bool PRE = T::template Normalize<PRE_PB >::value;
622 constexpr bool POST = T::template Normalize<POST_PB>::value;
623 return RDMEM_impl2<PRE, POST>(address, cc);
624}
625template<typename T> template<unsigned PC_OFFSET> ALWAYS_INLINE byte CPUCore<T>::RDMEM_OPCODE(unsigned cc)
626{
627 // Real Z80 would update the PC register now. In this implementation
628 // we've chosen to instead update PC only once at the end of the
629 // instruction. (Of course we made sure this difference is not
630 // noticeable by the program).
631 //
632 // See the comments in isM1Cycle() for the motivation for this
633 // deviation. Apart from that functional aspect it also turns out to be
634 // faster to only update PC once per instruction instead of after each
635 // fetch.
636 unsigned address = narrow_cast<word>(getPC() + PC_OFFSET);
637 return RDMEM_impl<false, false>(address, cc);
638}
639template<typename T> ALWAYS_INLINE byte CPUCore<T>::RDMEM(unsigned address, unsigned cc)
640{
641 return RDMEM_impl<true, true>(address, cc);
642}
643
644template<typename T> template<bool PRE_PB, bool POST_PB>
645NEVER_INLINE word CPUCore<T>::RD_WORD_slow(unsigned address, unsigned cc)
646{
647 auto res = word(RDMEM_impl<PRE_PB, false>(address, cc));
648 res |= word(RDMEM_impl<false, POST_PB>(narrow_cast<word>(address + 1), cc + T::CC_RDMEM) << 8);
649 return res;
650}
651template<typename T> template<bool PRE_PB, bool POST_PB>
652ALWAYS_INLINE word CPUCore<T>::RD_WORD_impl2(unsigned address, unsigned cc)
653{
654 const byte* line = readCacheLine[address >> CacheLine::BITS];
655 if (((address & CacheLine::LOW) != CacheLine::LOW) && (uintptr_t(line) > 1)) [[likely]] {
656 // fast path: cached and two bytes in same cache line
657 T::template PRE_WORD<PRE_PB, POST_PB>(address);
658 T::template POST_WORD< POST_PB>(address);
659 return Endian::read_UA_L16(&line[address]);
660 } else {
661 // slow path, not inline
662 return RD_WORD_slow<PRE_PB, POST_PB>(address, cc);
663 }
664}
665template<typename T> template<bool PRE_PB, bool POST_PB>
666ALWAYS_INLINE word CPUCore<T>::RD_WORD_impl(unsigned address, unsigned cc)
667{
668 constexpr bool PRE = T::template Normalize<PRE_PB >::value;
669 constexpr bool POST = T::template Normalize<POST_PB>::value;
670 return RD_WORD_impl2<PRE, POST>(address, cc);
671}
672template<typename T> template<unsigned PC_OFFSET> ALWAYS_INLINE word CPUCore<T>::RD_WORD_PC(unsigned cc)
673{
674 unsigned addr = narrow_cast<word>(getPC() + PC_OFFSET);
675 return RD_WORD_impl<false, false>(addr, cc);
676}
677template<typename T> ALWAYS_INLINE word CPUCore<T>::RD_WORD(
678 unsigned address, unsigned cc)
679{
680 return RD_WORD_impl<true, true>(address, cc);
681}
682
683template<typename T> template<bool PRE_PB, bool POST_PB>
684NEVER_INLINE void CPUCore<T>::WRMEMslow(unsigned address, byte value, unsigned cc)
685{
686 interface->tick(CacheLineCounters::NonCachedWrite);
687 // not cached
688 unsigned high = address >> CacheLine::BITS;
689 if (writeCacheLine[high] == nullptr) {
690 // try to cache now
691 auto addrBase = narrow_cast<word>(address & CacheLine::HIGH);
692 if (byte* line = interface->getWriteCacheLine(addrBase)) {
693 // cached ok
694 T::template PRE_MEM<PRE_PB, POST_PB>(address);
695 T::template POST_MEM< POST_PB>(address);
696 writeCacheLine[high] = line - addrBase;
697 writeCacheLine[high][address] = value;
698 return;
699 }
700 }
701 // uncacheable
702 writeCacheLine[high] = reinterpret_cast<byte*>(1);
703 T::template PRE_MEM<PRE_PB, POST_PB>(address);
704 EmuTime time = T::getTimeFast(cc);
705 scheduler.schedule(time);
706 interface->writeMem(narrow_cast<word>(address), value, time);
707 T::template POST_MEM<POST_PB>(address);
708}
709template<typename T> template<bool PRE_PB, bool POST_PB>
711 unsigned address, byte value, unsigned cc)
712{
713 byte* line = writeCacheLine[address >> CacheLine::BITS];
714 if (uintptr_t(line) > 1) [[likely]] {
715 // cached, fast path
716 T::template PRE_MEM<PRE_PB, POST_PB>(address);
717 T::template POST_MEM< POST_PB>(address);
718 line[address] = value;
719 } else {
720 WRMEMslow<PRE_PB, POST_PB>(address, value, cc); // not inlined
721 }
722}
723template<typename T> template<bool PRE_PB, bool POST_PB>
725 unsigned address, byte value, unsigned cc)
726{
727 constexpr bool PRE = T::template Normalize<PRE_PB >::value;
728 constexpr bool POST = T::template Normalize<POST_PB>::value;
729 WRMEM_impl2<PRE, POST>(address, value, cc);
730}
731template<typename T> ALWAYS_INLINE void CPUCore<T>::WRMEM(
732 unsigned address, byte value, unsigned cc)
733{
734 WRMEM_impl<true, true>(address, value, cc);
735}
736
737template<typename T> NEVER_INLINE void CPUCore<T>::WR_WORD_slow(
738 unsigned address, word value, unsigned cc)
739{
740 WRMEM_impl<true, false>( address, byte(value & 255), cc);
741 WRMEM_impl<false, true>(narrow_cast<word>(address + 1), byte(value >> 8), cc + T::CC_WRMEM);
742}
743template<typename T> ALWAYS_INLINE void CPUCore<T>::WR_WORD(
744 unsigned address, word value, unsigned cc)
745{
746 byte* line = writeCacheLine[address >> CacheLine::BITS];
747 if (((address & CacheLine::LOW) != CacheLine::LOW) && (uintptr_t(line) > 1)) [[likely]] {
748 // fast path: cached and two bytes in same cache line
749 T::template PRE_WORD<true, true>(address);
750 T::template POST_WORD< true>(address);
751 Endian::write_UA_L16(&line[address], value);
752 } else {
753 // slow path, not inline
754 WR_WORD_slow(address, value, cc);
755 }
756}
757
758// same as WR_WORD, but writes high byte first
759template<typename T> template<bool PRE_PB, bool POST_PB>
761 unsigned address, word value, unsigned cc)
762{
763 WRMEM_impl<PRE_PB, false>(narrow_cast<word>(address + 1), byte(value >> 8), cc);
764 WRMEM_impl<false, POST_PB>( address, byte(value & 255), cc + T::CC_WRMEM);
765}
766template<typename T> template<bool PRE_PB, bool POST_PB>
768 unsigned address, word value, unsigned cc)
769{
770 byte* line = writeCacheLine[address >> CacheLine::BITS];
771 if (((address & CacheLine::LOW) != CacheLine::LOW) && (uintptr_t(line) > 1)) [[likely]] {
772 // fast path: cached and two bytes in same cache line
773 T::template PRE_WORD<PRE_PB, POST_PB>(address);
774 T::template POST_WORD< POST_PB>(address);
775 Endian::write_UA_L16(&line[address], value);
776 } else {
777 // slow path, not inline
778 WR_WORD_rev_slow<PRE_PB, POST_PB>(address, value, cc);
779 }
780}
781template<typename T> template<bool PRE_PB, bool POST_PB>
783 unsigned address, word value, unsigned cc)
784{
785 constexpr bool PRE = T::template Normalize<PRE_PB >::value;
786 constexpr bool POST = T::template Normalize<POST_PB>::value;
787 WR_WORD_rev2<PRE, POST>(address, value, cc);
788}
789
790
791// NMI interrupt
792template<typename T> inline void CPUCore<T>::nmi()
793{
794 incR(1);
795 setHALT(false);
796 setIFF1(false);
797 PUSH<T::EE_NMI_1>(getPC());
798 setPC(0x0066);
799 T::add(T::CC_NMI);
800}
801
802// IM0 interrupt
803template<typename T> inline void CPUCore<T>::irq0()
804{
805 // TODO current implementation only works for 1-byte instructions
806 // ok for MSX
807 assert(interface->readIRQVector() == 0xFF);
808 incR(1);
809 setHALT(false);
810 setIFF1(false);
811 setIFF2(false);
812 PUSH<T::EE_IRQ0_1>(getPC());
813 setPC(0x0038);
814 T::setMemPtr(getPC());
815 T::add(T::CC_IRQ0);
816}
817
818// IM1 interrupt
819template<typename T> inline void CPUCore<T>::irq1()
820{
821 incR(1);
822 setHALT(false);
823 setIFF1(false);
824 setIFF2(false);
825 PUSH<T::EE_IRQ1_1>(getPC());
826 setPC(0x0038);
827 T::setMemPtr(getPC());
828 T::add(T::CC_IRQ1);
829}
830
831// IM2 interrupt
832template<typename T> inline void CPUCore<T>::irq2()
833{
834 incR(1);
835 setHALT(false);
836 setIFF1(false);
837 setIFF2(false);
838 PUSH<T::EE_IRQ2_1>(getPC());
839 unsigned x = interface->readIRQVector() | (getI() << 8);
840 setPC(RD_WORD(x, T::CC_IRQ2_2));
841 T::setMemPtr(getPC());
842 T::add(T::CC_IRQ2);
843}
844
845template<typename T>
846void CPUCore<T>::executeInstructions()
847{
848 checkNoCurrentFlags();
849#ifdef USE_COMPUTED_GOTO
850 // Addresses of all main-opcode routines,
851 // Note that 40/49/53/5B/64/6D/7F is replaced by 00 (ld r,r == nop)
852 static void* opcodeTable[256] = {
853 &&op00, &&op01, &&op02, &&op03, &&op04, &&op05, &&op06, &&op07,
854 &&op08, &&op09, &&op0A, &&op0B, &&op0C, &&op0D, &&op0E, &&op0F,
855 &&op10, &&op11, &&op12, &&op13, &&op14, &&op15, &&op16, &&op17,
856 &&op18, &&op19, &&op1A, &&op1B, &&op1C, &&op1D, &&op1E, &&op1F,
857 &&op20, &&op21, &&op22, &&op23, &&op24, &&op25, &&op26, &&op27,
858 &&op28, &&op29, &&op2A, &&op2B, &&op2C, &&op2D, &&op2E, &&op2F,
859 &&op30, &&op31, &&op32, &&op33, &&op34, &&op35, &&op36, &&op37,
860 &&op38, &&op39, &&op3A, &&op3B, &&op3C, &&op3D, &&op3E, &&op3F,
861 &&op00, &&op41, &&op42, &&op43, &&op44, &&op45, &&op46, &&op47,
862 &&op48, &&op00, &&op4A, &&op4B, &&op4C, &&op4D, &&op4E, &&op4F,
863 &&op50, &&op51, &&op00, &&op53, &&op54, &&op55, &&op56, &&op57,
864 &&op58, &&op59, &&op5A, &&op00, &&op5C, &&op5D, &&op5E, &&op5F,
865 &&op60, &&op61, &&op62, &&op63, &&op00, &&op65, &&op66, &&op67,
866 &&op68, &&op69, &&op6A, &&op6B, &&op6C, &&op00, &&op6E, &&op6F,
867 &&op70, &&op71, &&op72, &&op73, &&op74, &&op75, &&op76, &&op77,
868 &&op78, &&op79, &&op7A, &&op7B, &&op7C, &&op7D, &&op7E, &&op00,
869 &&op80, &&op81, &&op82, &&op83, &&op84, &&op85, &&op86, &&op87,
870 &&op88, &&op89, &&op8A, &&op8B, &&op8C, &&op8D, &&op8E, &&op8F,
871 &&op90, &&op91, &&op92, &&op93, &&op94, &&op95, &&op96, &&op97,
872 &&op98, &&op99, &&op9A, &&op9B, &&op9C, &&op9D, &&op9E, &&op9F,
873 &&opA0, &&opA1, &&opA2, &&opA3, &&opA4, &&opA5, &&opA6, &&opA7,
874 &&opA8, &&opA9, &&opAA, &&opAB, &&opAC, &&opAD, &&opAE, &&opAF,
875 &&opB0, &&opB1, &&opB2, &&opB3, &&opB4, &&opB5, &&opB6, &&opB7,
876 &&opB8, &&opB9, &&opBA, &&opBB, &&opBC, &&opBD, &&opBE, &&opBF,
877 &&opC0, &&opC1, &&opC2, &&opC3, &&opC4, &&opC5, &&opC6, &&opC7,
878 &&opC8, &&opC9, &&opCA, &&opCB, &&opCC, &&opCD, &&opCE, &&opCF,
879 &&opD0, &&opD1, &&opD2, &&opD3, &&opD4, &&opD5, &&opD6, &&opD7,
880 &&opD8, &&opD9, &&opDA, &&opDB, &&opDC, &&opDD, &&opDE, &&opDF,
881 &&opE0, &&opE1, &&opE2, &&opE3, &&opE4, &&opE5, &&opE6, &&opE7,
882 &&opE8, &&opE9, &&opEA, &&opEB, &&opEC, &&opED, &&opEE, &&opEF,
883 &&opF0, &&opF1, &&opF2, &&opF3, &&opF4, &&opF5, &&opF6, &&opF7,
884 &&opF8, &&opF9, &&opFA, &&opFB, &&opFC, &&opFD, &&opFE, &&opFF,
885 };
886
887// Check T::limitReached(). If it's OK to continue,
888// fetch and execute next instruction.
889#define NEXT \
890 setPC(getPC() + ii.length); \
891 T::add(ii.cycles); \
892 T::R800Refresh(*this); \
893 if (!T::limitReached()) [[likely]] { \
894 incR(1); \
895 unsigned address = getPC(); \
896 const byte* line = readCacheLine[address >> CacheLine::BITS]; \
897 if (uintptr_t(line) > 1) [[likely]] { \
898 T::template PRE_MEM<false, false>(address); \
899 T::template POST_MEM< false>(address); \
900 byte op = line[address]; \
901 goto *(opcodeTable[op]); \
902 } else { \
903 goto fetchSlow; \
904 } \
905 } \
906 return;
907
908// After some instructions we must always exit the CPU loop (ei, halt, retn)
909#define NEXT_STOP \
910 setPC(getPC() + ii.length); \
911 T::add(ii.cycles); \
912 T::R800Refresh(*this); \
913 assert(T::limitReached()); \
914 return;
915
916#define NEXT_EI \
917 setPC(getPC() + ii.length); \
918 T::add(ii.cycles); \
919 /* !! NO T::R800Refresh(*this); !! */ \
920 assert(T::limitReached()); \
921 return;
922
923// Define a label (instead of case in a switch statement)
924#define CASE(X) op##X:
925
926#else // USE_COMPUTED_GOTO
927
928#define NEXT \
929 setPC(getPC() + ii.length); \
930 T::add(ii.cycles); \
931 T::R800Refresh(*this); \
932 if (!T::limitReached()) [[likely]] { \
933 goto start; \
934 } \
935 return;
936
937#define NEXT_STOP \
938 setPC(getPC() + ii.length); \
939 T::add(ii.cycles); \
940 T::R800Refresh(*this); \
941 assert(T::limitReached()); \
942 return;
943
944#define NEXT_EI \
945 setPC(getPC() + ii.length); \
946 T::add(ii.cycles); \
947 /* !! NO T::R800Refresh(*this); !! */ \
948 assert(T::limitReached()); \
949 return;
950
951#define CASE(X) case 0x##X:
952
953#endif // USE_COMPUTED_GOTO
954
955#ifndef USE_COMPUTED_GOTO
956start:
957#endif
958 unsigned ixy; // for dd_cb/fd_cb
959 byte opcodeMain = RDMEM_OPCODE<0>(T::CC_MAIN);
960 incR(1);
961#ifdef USE_COMPUTED_GOTO
962 goto *(opcodeTable[opcodeMain]);
963
964fetchSlow: {
965 unsigned address = getPC();
966 byte opcodeSlow = RDMEMslow<false, false>(address, T::CC_MAIN);
967 goto *(opcodeTable[opcodeSlow]);
968}
969#endif
970
971#ifndef USE_COMPUTED_GOTO
972MAYBE_UNUSED_LABEL switchOpcode:
973 switch (opcodeMain) {
974CASE(40) // ld b,b
975CASE(49) // ld c,c
976CASE(52) // ld d,d
977CASE(5B) // ld e,e
978CASE(64) // ld h,h
979CASE(6D) // ld l,l
980CASE(7F) // ld a,a
981#endif
982CASE(00) { II ii = nop(); NEXT; }
983CASE(07) { II ii = rlca(); NEXT; }
984CASE(0F) { II ii = rrca(); NEXT; }
985CASE(17) { II ii = rla(); NEXT; }
986CASE(1F) { II ii = rra(); NEXT; }
987CASE(08) { II ii = ex_af_af(); NEXT; }
988CASE(27) { II ii = daa(); NEXT; }
989CASE(2F) { II ii = cpl(); NEXT; }
990CASE(37) { II ii = scf(); NEXT; }
991CASE(3F) { II ii = ccf(); NEXT; }
992CASE(20) { II ii = jr(CondNZ()); NEXT; }
993CASE(28) { II ii = jr(CondZ ()); NEXT; }
994CASE(30) { II ii = jr(CondNC()); NEXT; }
995CASE(38) { II ii = jr(CondC ()); NEXT; }
996CASE(18) { II ii = jr(CondTrue()); NEXT; }
997CASE(10) { II ii = djnz(); NEXT; }
998CASE(32) { II ii = ld_xbyte_a(); NEXT; }
999CASE(3A) { II ii = ld_a_xbyte(); NEXT; }
1000CASE(22) { II ii = ld_xword_SS<HL,0>(); NEXT; }
1001CASE(2A) { II ii = ld_SS_xword<HL,0>(); NEXT; }
1002CASE(02) { II ii = ld_SS_a<BC>(); NEXT; }
1003CASE(12) { II ii = ld_SS_a<DE>(); NEXT; }
1004CASE(1A) { II ii = ld_a_SS<DE>(); NEXT; }
1005CASE(0A) { II ii = ld_a_SS<BC>(); NEXT; }
1006CASE(03) { II ii = inc_SS<BC,0>(); NEXT; }
1007CASE(13) { II ii = inc_SS<DE,0>(); NEXT; }
1008CASE(23) { II ii = inc_SS<HL,0>(); NEXT; }
1009CASE(33) { II ii = inc_SS<SP,0>(); NEXT; }
1010CASE(0B) { II ii = dec_SS<BC,0>(); NEXT; }
1011CASE(1B) { II ii = dec_SS<DE,0>(); NEXT; }
1012CASE(2B) { II ii = dec_SS<HL,0>(); NEXT; }
1013CASE(3B) { II ii = dec_SS<SP,0>(); NEXT; }
1014CASE(09) { II ii = add_SS_TT<HL,BC,0>(); NEXT; }
1015CASE(19) { II ii = add_SS_TT<HL,DE,0>(); NEXT; }
1016CASE(29) { II ii = add_SS_SS<HL ,0>(); NEXT; }
1017CASE(39) { II ii = add_SS_TT<HL,SP,0>(); NEXT; }
1018CASE(01) { II ii = ld_SS_word<BC,0>(); NEXT; }
1019CASE(11) { II ii = ld_SS_word<DE,0>(); NEXT; }
1020CASE(21) { II ii = ld_SS_word<HL,0>(); NEXT; }
1021CASE(31) { II ii = ld_SS_word<SP,0>(); NEXT; }
1022CASE(04) { II ii = inc_R<B,0>(); NEXT; }
1023CASE(0C) { II ii = inc_R<C,0>(); NEXT; }
1024CASE(14) { II ii = inc_R<D,0>(); NEXT; }
1025CASE(1C) { II ii = inc_R<E,0>(); NEXT; }
1026CASE(24) { II ii = inc_R<H,0>(); NEXT; }
1027CASE(2C) { II ii = inc_R<L,0>(); NEXT; }
1028CASE(3C) { II ii = inc_R<A,0>(); NEXT; }
1029CASE(34) { II ii = inc_xhl(); NEXT; }
1030CASE(05) { II ii = dec_R<B,0>(); NEXT; }
1031CASE(0D) { II ii = dec_R<C,0>(); NEXT; }
1032CASE(15) { II ii = dec_R<D,0>(); NEXT; }
1033CASE(1D) { II ii = dec_R<E,0>(); NEXT; }
1034CASE(25) { II ii = dec_R<H,0>(); NEXT; }
1035CASE(2D) { II ii = dec_R<L,0>(); NEXT; }
1036CASE(3D) { II ii = dec_R<A,0>(); NEXT; }
1037CASE(35) { II ii = dec_xhl(); NEXT; }
1038CASE(06) { II ii = ld_R_byte<B,0>(); NEXT; }
1039CASE(0E) { II ii = ld_R_byte<C,0>(); NEXT; }
1040CASE(16) { II ii = ld_R_byte<D,0>(); NEXT; }
1041CASE(1E) { II ii = ld_R_byte<E,0>(); NEXT; }
1042CASE(26) { II ii = ld_R_byte<H,0>(); NEXT; }
1043CASE(2E) { II ii = ld_R_byte<L,0>(); NEXT; }
1044CASE(3E) { II ii = ld_R_byte<A,0>(); NEXT; }
1045CASE(36) { II ii = ld_xhl_byte(); NEXT; }
1046
1047CASE(41) { II ii = ld_R_R<B,C,0>(); NEXT; }
1048CASE(42) { II ii = ld_R_R<B,D,0>(); NEXT; }
1049CASE(43) { II ii = ld_R_R<B,E,0>(); NEXT; }
1050CASE(44) { II ii = ld_R_R<B,H,0>(); NEXT; }
1051CASE(45) { II ii = ld_R_R<B,L,0>(); NEXT; }
1052CASE(47) { II ii = ld_R_R<B,A,0>(); NEXT; }
1053CASE(48) { II ii = ld_R_R<C,B,0>(); NEXT; }
1054CASE(4A) { II ii = ld_R_R<C,D,0>(); NEXT; }
1055CASE(4B) { II ii = ld_R_R<C,E,0>(); NEXT; }
1056CASE(4C) { II ii = ld_R_R<C,H,0>(); NEXT; }
1057CASE(4D) { II ii = ld_R_R<C,L,0>(); NEXT; }
1058CASE(4F) { II ii = ld_R_R<C,A,0>(); NEXT; }
1059CASE(50) { II ii = ld_R_R<D,B,0>(); NEXT; }
1060CASE(51) { II ii = ld_R_R<D,C,0>(); NEXT; }
1061CASE(53) { II ii = ld_R_R<D,E,0>(); NEXT; }
1062CASE(54) { II ii = ld_R_R<D,H,0>(); NEXT; }
1063CASE(55) { II ii = ld_R_R<D,L,0>(); NEXT; }
1064CASE(57) { II ii = ld_R_R<D,A,0>(); NEXT; }
1065CASE(58) { II ii = ld_R_R<E,B,0>(); NEXT; }
1066CASE(59) { II ii = ld_R_R<E,C,0>(); NEXT; }
1067CASE(5A) { II ii = ld_R_R<E,D,0>(); NEXT; }
1068CASE(5C) { II ii = ld_R_R<E,H,0>(); NEXT; }
1069CASE(5D) { II ii = ld_R_R<E,L,0>(); NEXT; }
1070CASE(5F) { II ii = ld_R_R<E,A,0>(); NEXT; }
1071CASE(60) { II ii = ld_R_R<H,B,0>(); NEXT; }
1072CASE(61) { II ii = ld_R_R<H,C,0>(); NEXT; }
1073CASE(62) { II ii = ld_R_R<H,D,0>(); NEXT; }
1074CASE(63) { II ii = ld_R_R<H,E,0>(); NEXT; }
1075CASE(65) { II ii = ld_R_R<H,L,0>(); NEXT; }
1076CASE(67) { II ii = ld_R_R<H,A,0>(); NEXT; }
1077CASE(68) { II ii = ld_R_R<L,B,0>(); NEXT; }
1078CASE(69) { II ii = ld_R_R<L,C,0>(); NEXT; }
1079CASE(6A) { II ii = ld_R_R<L,D,0>(); NEXT; }
1080CASE(6B) { II ii = ld_R_R<L,E,0>(); NEXT; }
1081CASE(6C) { II ii = ld_R_R<L,H,0>(); NEXT; }
1082CASE(6F) { II ii = ld_R_R<L,A,0>(); NEXT; }
1083CASE(78) { II ii = ld_R_R<A,B,0>(); NEXT; }
1084CASE(79) { II ii = ld_R_R<A,C,0>(); NEXT; }
1085CASE(7A) { II ii = ld_R_R<A,D,0>(); NEXT; }
1086CASE(7B) { II ii = ld_R_R<A,E,0>(); NEXT; }
1087CASE(7C) { II ii = ld_R_R<A,H,0>(); NEXT; }
1088CASE(7D) { II ii = ld_R_R<A,L,0>(); NEXT; }
1089CASE(70) { II ii = ld_xhl_R<B>(); NEXT; }
1090CASE(71) { II ii = ld_xhl_R<C>(); NEXT; }
1091CASE(72) { II ii = ld_xhl_R<D>(); NEXT; }
1092CASE(73) { II ii = ld_xhl_R<E>(); NEXT; }
1093CASE(74) { II ii = ld_xhl_R<H>(); NEXT; }
1094CASE(75) { II ii = ld_xhl_R<L>(); NEXT; }
1095CASE(77) { II ii = ld_xhl_R<A>(); NEXT; }
1096CASE(46) { II ii = ld_R_xhl<B>(); NEXT; }
1097CASE(4E) { II ii = ld_R_xhl<C>(); NEXT; }
1098CASE(56) { II ii = ld_R_xhl<D>(); NEXT; }
1099CASE(5E) { II ii = ld_R_xhl<E>(); NEXT; }
1100CASE(66) { II ii = ld_R_xhl<H>(); NEXT; }
1101CASE(6E) { II ii = ld_R_xhl<L>(); NEXT; }
1102CASE(7E) { II ii = ld_R_xhl<A>(); NEXT; }
1103CASE(76) { II ii = halt(); NEXT_STOP; }
1104
1105CASE(80) { II ii = add_a_R<B,0>(); NEXT; }
1106CASE(81) { II ii = add_a_R<C,0>(); NEXT; }
1107CASE(82) { II ii = add_a_R<D,0>(); NEXT; }
1108CASE(83) { II ii = add_a_R<E,0>(); NEXT; }
1109CASE(84) { II ii = add_a_R<H,0>(); NEXT; }
1110CASE(85) { II ii = add_a_R<L,0>(); NEXT; }
1111CASE(86) { II ii = add_a_xhl(); NEXT; }
1112CASE(87) { II ii = add_a_a(); NEXT; }
1113CASE(88) { II ii = adc_a_R<B,0>(); NEXT; }
1114CASE(89) { II ii = adc_a_R<C,0>(); NEXT; }
1115CASE(8A) { II ii = adc_a_R<D,0>(); NEXT; }
1116CASE(8B) { II ii = adc_a_R<E,0>(); NEXT; }
1117CASE(8C) { II ii = adc_a_R<H,0>(); NEXT; }
1118CASE(8D) { II ii = adc_a_R<L,0>(); NEXT; }
1119CASE(8E) { II ii = adc_a_xhl(); NEXT; }
1120CASE(8F) { II ii = adc_a_a(); NEXT; }
1121CASE(90) { II ii = sub_R<B,0>(); NEXT; }
1122CASE(91) { II ii = sub_R<C,0>(); NEXT; }
1123CASE(92) { II ii = sub_R<D,0>(); NEXT; }
1124CASE(93) { II ii = sub_R<E,0>(); NEXT; }
1125CASE(94) { II ii = sub_R<H,0>(); NEXT; }
1126CASE(95) { II ii = sub_R<L,0>(); NEXT; }
1127CASE(96) { II ii = sub_xhl(); NEXT; }
1128CASE(97) { II ii = sub_a(); NEXT; }
1129CASE(98) { II ii = sbc_a_R<B,0>(); NEXT; }
1130CASE(99) { II ii = sbc_a_R<C,0>(); NEXT; }
1131CASE(9A) { II ii = sbc_a_R<D,0>(); NEXT; }
1132CASE(9B) { II ii = sbc_a_R<E,0>(); NEXT; }
1133CASE(9C) { II ii = sbc_a_R<H,0>(); NEXT; }
1134CASE(9D) { II ii = sbc_a_R<L,0>(); NEXT; }
1135CASE(9E) { II ii = sbc_a_xhl(); NEXT; }
1136CASE(9F) { II ii = sbc_a_a(); NEXT; }
1137CASE(A0) { II ii = and_R<B,0>(); NEXT; }
1138CASE(A1) { II ii = and_R<C,0>(); NEXT; }
1139CASE(A2) { II ii = and_R<D,0>(); NEXT; }
1140CASE(A3) { II ii = and_R<E,0>(); NEXT; }
1141CASE(A4) { II ii = and_R<H,0>(); NEXT; }
1142CASE(A5) { II ii = and_R<L,0>(); NEXT; }
1143CASE(A6) { II ii = and_xhl(); NEXT; }
1144CASE(A7) { II ii = and_a(); NEXT; }
1145CASE(A8) { II ii = xor_R<B,0>(); NEXT; }
1146CASE(A9) { II ii = xor_R<C,0>(); NEXT; }
1147CASE(AA) { II ii = xor_R<D,0>(); NEXT; }
1148CASE(AB) { II ii = xor_R<E,0>(); NEXT; }
1149CASE(AC) { II ii = xor_R<H,0>(); NEXT; }
1150CASE(AD) { II ii = xor_R<L,0>(); NEXT; }
1151CASE(AE) { II ii = xor_xhl(); NEXT; }
1152CASE(AF) { II ii = xor_a(); NEXT; }
1153CASE(B0) { II ii = or_R<B,0>(); NEXT; }
1154CASE(B1) { II ii = or_R<C,0>(); NEXT; }
1155CASE(B2) { II ii = or_R<D,0>(); NEXT; }
1156CASE(B3) { II ii = or_R<E,0>(); NEXT; }
1157CASE(B4) { II ii = or_R<H,0>(); NEXT; }
1158CASE(B5) { II ii = or_R<L,0>(); NEXT; }
1159CASE(B6) { II ii = or_xhl(); NEXT; }
1160CASE(B7) { II ii = or_a(); NEXT; }
1161CASE(B8) { II ii = cp_R<B,0>(); NEXT; }
1162CASE(B9) { II ii = cp_R<C,0>(); NEXT; }
1163CASE(BA) { II ii = cp_R<D,0>(); NEXT; }
1164CASE(BB) { II ii = cp_R<E,0>(); NEXT; }
1165CASE(BC) { II ii = cp_R<H,0>(); NEXT; }
1166CASE(BD) { II ii = cp_R<L,0>(); NEXT; }
1167CASE(BE) { II ii = cp_xhl(); NEXT; }
1168CASE(BF) { II ii = cp_a(); NEXT; }
1169
1170CASE(D3) { II ii = out_byte_a(); NEXT; }
1171CASE(DB) { II ii = in_a_byte(); NEXT; }
1172CASE(D9) { II ii = exx(); NEXT; }
1173CASE(E3) { II ii = ex_xsp_SS<HL,0>(); NEXT; }
1174CASE(EB) { II ii = ex_de_hl(); NEXT; }
1175CASE(E9) { II ii = jp_SS<HL,0>(); NEXT; }
1176CASE(F9) { II ii = ld_sp_SS<HL,0>(); NEXT; }
1177CASE(F3) { II ii = di(); NEXT; }
1178CASE(FB) { II ii = ei(); NEXT_EI; }
1179CASE(C6) { II ii = add_a_byte(); NEXT; }
1180CASE(CE) { II ii = adc_a_byte(); NEXT; }
1181CASE(D6) { II ii = sub_byte(); NEXT; }
1182CASE(DE) { II ii = sbc_a_byte(); NEXT; }
1183CASE(E6) { II ii = and_byte(); NEXT; }
1184CASE(EE) { II ii = xor_byte(); NEXT; }
1185CASE(F6) { II ii = or_byte(); NEXT; }
1186CASE(FE) { II ii = cp_byte(); NEXT; }
1187CASE(C0) { II ii = ret(CondNZ()); NEXT; }
1188CASE(C8) { II ii = ret(CondZ ()); NEXT; }
1189CASE(D0) { II ii = ret(CondNC()); NEXT; }
1190CASE(D8) { II ii = ret(CondC ()); NEXT; }
1191CASE(E0) { II ii = ret(CondPO()); NEXT; }
1192CASE(E8) { II ii = ret(CondPE()); NEXT; }
1193CASE(F0) { II ii = ret(CondP ()); NEXT; }
1194CASE(F8) { II ii = ret(CondM ()); NEXT; }
1195CASE(C9) { II ii = ret(); NEXT; }
1196CASE(C2) { II ii = jp(CondNZ()); NEXT; }
1197CASE(CA) { II ii = jp(CondZ ()); NEXT; }
1198CASE(D2) { II ii = jp(CondNC()); NEXT; }
1199CASE(DA) { II ii = jp(CondC ()); NEXT; }
1200CASE(E2) { II ii = jp(CondPO()); NEXT; }
1201CASE(EA) { II ii = jp(CondPE()); NEXT; }
1202CASE(F2) { II ii = jp(CondP ()); NEXT; }
1203CASE(FA) { II ii = jp(CondM ()); NEXT; }
1204CASE(C3) { II ii = jp(CondTrue()); NEXT; }
1205CASE(C4) { II ii = call(CondNZ()); NEXT; }
1206CASE(CC) { II ii = call(CondZ ()); NEXT; }
1207CASE(D4) { II ii = call(CondNC()); NEXT; }
1208CASE(DC) { II ii = call(CondC ()); NEXT; }
1209CASE(E4) { II ii = call(CondPO()); NEXT; }
1210CASE(EC) { II ii = call(CondPE()); NEXT; }
1211CASE(F4) { II ii = call(CondP ()); NEXT; }
1212CASE(FC) { II ii = call(CondM ()); NEXT; }
1213CASE(CD) { II ii = call(CondTrue()); NEXT; }
1214CASE(C1) { II ii = pop_SS <BC,0>(); NEXT; }
1215CASE(D1) { II ii = pop_SS <DE,0>(); NEXT; }
1216CASE(E1) { II ii = pop_SS <HL,0>(); NEXT; }
1217CASE(F1) { II ii = pop_SS <AF,0>(); NEXT; }
1218CASE(C5) { II ii = push_SS<BC,0>(); NEXT; }
1219CASE(D5) { II ii = push_SS<DE,0>(); NEXT; }
1220CASE(E5) { II ii = push_SS<HL,0>(); NEXT; }
1221CASE(F5) { II ii = push_SS<AF,0>(); NEXT; }
1222CASE(C7) { II ii = rst<0x00>(); NEXT; }
1223CASE(CF) { II ii = rst<0x08>(); NEXT; }
1224CASE(D7) { II ii = rst<0x10>(); NEXT; }
1225CASE(DF) { II ii = rst<0x18>(); NEXT; }
1226CASE(E7) { II ii = rst<0x20>(); NEXT; }
1227CASE(EF) { II ii = rst<0x28>(); NEXT; }
1228CASE(F7) { II ii = rst<0x30>(); NEXT; }
1229CASE(FF) { II ii = rst<0x38>(); NEXT; }
1230CASE(CB) {
1231 setPC(getPC() + 1); // M1 cycle at this point
1232 byte cb_opcode = RDMEM_OPCODE<0>(T::CC_PREFIX);
1233 incR(1);
1234 switch (cb_opcode) {
1235 case 0x00: { II ii = rlc_R<B>(); NEXT; }
1236 case 0x01: { II ii = rlc_R<C>(); NEXT; }
1237 case 0x02: { II ii = rlc_R<D>(); NEXT; }
1238 case 0x03: { II ii = rlc_R<E>(); NEXT; }
1239 case 0x04: { II ii = rlc_R<H>(); NEXT; }
1240 case 0x05: { II ii = rlc_R<L>(); NEXT; }
1241 case 0x07: { II ii = rlc_R<A>(); NEXT; }
1242 case 0x06: { II ii = rlc_xhl(); NEXT; }
1243 case 0x08: { II ii = rrc_R<B>(); NEXT; }
1244 case 0x09: { II ii = rrc_R<C>(); NEXT; }
1245 case 0x0a: { II ii = rrc_R<D>(); NEXT; }
1246 case 0x0b: { II ii = rrc_R<E>(); NEXT; }
1247 case 0x0c: { II ii = rrc_R<H>(); NEXT; }
1248 case 0x0d: { II ii = rrc_R<L>(); NEXT; }
1249 case 0x0f: { II ii = rrc_R<A>(); NEXT; }
1250 case 0x0e: { II ii = rrc_xhl(); NEXT; }
1251 case 0x10: { II ii = rl_R<B>(); NEXT; }
1252 case 0x11: { II ii = rl_R<C>(); NEXT; }
1253 case 0x12: { II ii = rl_R<D>(); NEXT; }
1254 case 0x13: { II ii = rl_R<E>(); NEXT; }
1255 case 0x14: { II ii = rl_R<H>(); NEXT; }
1256 case 0x15: { II ii = rl_R<L>(); NEXT; }
1257 case 0x17: { II ii = rl_R<A>(); NEXT; }
1258 case 0x16: { II ii = rl_xhl(); NEXT; }
1259 case 0x18: { II ii = rr_R<B>(); NEXT; }
1260 case 0x19: { II ii = rr_R<C>(); NEXT; }
1261 case 0x1a: { II ii = rr_R<D>(); NEXT; }
1262 case 0x1b: { II ii = rr_R<E>(); NEXT; }
1263 case 0x1c: { II ii = rr_R<H>(); NEXT; }
1264 case 0x1d: { II ii = rr_R<L>(); NEXT; }
1265 case 0x1f: { II ii = rr_R<A>(); NEXT; }
1266 case 0x1e: { II ii = rr_xhl(); NEXT; }
1267 case 0x20: { II ii = sla_R<B>(); NEXT; }
1268 case 0x21: { II ii = sla_R<C>(); NEXT; }
1269 case 0x22: { II ii = sla_R<D>(); NEXT; }
1270 case 0x23: { II ii = sla_R<E>(); NEXT; }
1271 case 0x24: { II ii = sla_R<H>(); NEXT; }
1272 case 0x25: { II ii = sla_R<L>(); NEXT; }
1273 case 0x27: { II ii = sla_R<A>(); NEXT; }
1274 case 0x26: { II ii = sla_xhl(); NEXT; }
1275 case 0x28: { II ii = sra_R<B>(); NEXT; }
1276 case 0x29: { II ii = sra_R<C>(); NEXT; }
1277 case 0x2a: { II ii = sra_R<D>(); NEXT; }
1278 case 0x2b: { II ii = sra_R<E>(); NEXT; }
1279 case 0x2c: { II ii = sra_R<H>(); NEXT; }
1280 case 0x2d: { II ii = sra_R<L>(); NEXT; }
1281 case 0x2f: { II ii = sra_R<A>(); NEXT; }
1282 case 0x2e: { II ii = sra_xhl(); NEXT; }
1283 case 0x30: { II ii = T::IS_R800 ? sla_R<B>() : sll_R<B>(); NEXT; }
1284 case 0x31: { II ii = T::IS_R800 ? sla_R<C>() : sll_R<C>(); NEXT; }
1285 case 0x32: { II ii = T::IS_R800 ? sla_R<D>() : sll_R<D>(); NEXT; }
1286 case 0x33: { II ii = T::IS_R800 ? sla_R<E>() : sll_R<E>(); NEXT; }
1287 case 0x34: { II ii = T::IS_R800 ? sla_R<H>() : sll_R<H>(); NEXT; }
1288 case 0x35: { II ii = T::IS_R800 ? sla_R<L>() : sll_R<L>(); NEXT; }
1289 case 0x37: { II ii = T::IS_R800 ? sla_R<A>() : sll_R<A>(); NEXT; }
1290 case 0x36: { II ii = T::IS_R800 ? sla_xhl() : sll_xhl(); NEXT; }
1291 case 0x38: { II ii = srl_R<B>(); NEXT; }
1292 case 0x39: { II ii = srl_R<C>(); NEXT; }
1293 case 0x3a: { II ii = srl_R<D>(); NEXT; }
1294 case 0x3b: { II ii = srl_R<E>(); NEXT; }
1295 case 0x3c: { II ii = srl_R<H>(); NEXT; }
1296 case 0x3d: { II ii = srl_R<L>(); NEXT; }
1297 case 0x3f: { II ii = srl_R<A>(); NEXT; }
1298 case 0x3e: { II ii = srl_xhl(); NEXT; }
1299
1300 case 0x40: { II ii = bit_N_R<0,B>(); NEXT; }
1301 case 0x41: { II ii = bit_N_R<0,C>(); NEXT; }
1302 case 0x42: { II ii = bit_N_R<0,D>(); NEXT; }
1303 case 0x43: { II ii = bit_N_R<0,E>(); NEXT; }
1304 case 0x44: { II ii = bit_N_R<0,H>(); NEXT; }
1305 case 0x45: { II ii = bit_N_R<0,L>(); NEXT; }
1306 case 0x47: { II ii = bit_N_R<0,A>(); NEXT; }
1307 case 0x48: { II ii = bit_N_R<1,B>(); NEXT; }
1308 case 0x49: { II ii = bit_N_R<1,C>(); NEXT; }
1309 case 0x4a: { II ii = bit_N_R<1,D>(); NEXT; }
1310 case 0x4b: { II ii = bit_N_R<1,E>(); NEXT; }
1311 case 0x4c: { II ii = bit_N_R<1,H>(); NEXT; }
1312 case 0x4d: { II ii = bit_N_R<1,L>(); NEXT; }
1313 case 0x4f: { II ii = bit_N_R<1,A>(); NEXT; }
1314 case 0x50: { II ii = bit_N_R<2,B>(); NEXT; }
1315 case 0x51: { II ii = bit_N_R<2,C>(); NEXT; }
1316 case 0x52: { II ii = bit_N_R<2,D>(); NEXT; }
1317 case 0x53: { II ii = bit_N_R<2,E>(); NEXT; }
1318 case 0x54: { II ii = bit_N_R<2,H>(); NEXT; }
1319 case 0x55: { II ii = bit_N_R<2,L>(); NEXT; }
1320 case 0x57: { II ii = bit_N_R<2,A>(); NEXT; }
1321 case 0x58: { II ii = bit_N_R<3,B>(); NEXT; }
1322 case 0x59: { II ii = bit_N_R<3,C>(); NEXT; }
1323 case 0x5a: { II ii = bit_N_R<3,D>(); NEXT; }
1324 case 0x5b: { II ii = bit_N_R<3,E>(); NEXT; }
1325 case 0x5c: { II ii = bit_N_R<3,H>(); NEXT; }
1326 case 0x5d: { II ii = bit_N_R<3,L>(); NEXT; }
1327 case 0x5f: { II ii = bit_N_R<3,A>(); NEXT; }
1328 case 0x60: { II ii = bit_N_R<4,B>(); NEXT; }
1329 case 0x61: { II ii = bit_N_R<4,C>(); NEXT; }
1330 case 0x62: { II ii = bit_N_R<4,D>(); NEXT; }
1331 case 0x63: { II ii = bit_N_R<4,E>(); NEXT; }
1332 case 0x64: { II ii = bit_N_R<4,H>(); NEXT; }
1333 case 0x65: { II ii = bit_N_R<4,L>(); NEXT; }
1334 case 0x67: { II ii = bit_N_R<4,A>(); NEXT; }
1335 case 0x68: { II ii = bit_N_R<5,B>(); NEXT; }
1336 case 0x69: { II ii = bit_N_R<5,C>(); NEXT; }
1337 case 0x6a: { II ii = bit_N_R<5,D>(); NEXT; }
1338 case 0x6b: { II ii = bit_N_R<5,E>(); NEXT; }
1339 case 0x6c: { II ii = bit_N_R<5,H>(); NEXT; }
1340 case 0x6d: { II ii = bit_N_R<5,L>(); NEXT; }
1341 case 0x6f: { II ii = bit_N_R<5,A>(); NEXT; }
1342 case 0x70: { II ii = bit_N_R<6,B>(); NEXT; }
1343 case 0x71: { II ii = bit_N_R<6,C>(); NEXT; }
1344 case 0x72: { II ii = bit_N_R<6,D>(); NEXT; }
1345 case 0x73: { II ii = bit_N_R<6,E>(); NEXT; }
1346 case 0x74: { II ii = bit_N_R<6,H>(); NEXT; }
1347 case 0x75: { II ii = bit_N_R<6,L>(); NEXT; }
1348 case 0x77: { II ii = bit_N_R<6,A>(); NEXT; }
1349 case 0x78: { II ii = bit_N_R<7,B>(); NEXT; }
1350 case 0x79: { II ii = bit_N_R<7,C>(); NEXT; }
1351 case 0x7a: { II ii = bit_N_R<7,D>(); NEXT; }
1352 case 0x7b: { II ii = bit_N_R<7,E>(); NEXT; }
1353 case 0x7c: { II ii = bit_N_R<7,H>(); NEXT; }
1354 case 0x7d: { II ii = bit_N_R<7,L>(); NEXT; }
1355 case 0x7f: { II ii = bit_N_R<7,A>(); NEXT; }
1356 case 0x46: { II ii = bit_N_xhl<0>(); NEXT; }
1357 case 0x4e: { II ii = bit_N_xhl<1>(); NEXT; }
1358 case 0x56: { II ii = bit_N_xhl<2>(); NEXT; }
1359 case 0x5e: { II ii = bit_N_xhl<3>(); NEXT; }
1360 case 0x66: { II ii = bit_N_xhl<4>(); NEXT; }
1361 case 0x6e: { II ii = bit_N_xhl<5>(); NEXT; }
1362 case 0x76: { II ii = bit_N_xhl<6>(); NEXT; }
1363 case 0x7e: { II ii = bit_N_xhl<7>(); NEXT; }
1364
1365 case 0x80: { II ii = res_N_R<0,B>(); NEXT; }
1366 case 0x81: { II ii = res_N_R<0,C>(); NEXT; }
1367 case 0x82: { II ii = res_N_R<0,D>(); NEXT; }
1368 case 0x83: { II ii = res_N_R<0,E>(); NEXT; }
1369 case 0x84: { II ii = res_N_R<0,H>(); NEXT; }
1370 case 0x85: { II ii = res_N_R<0,L>(); NEXT; }
1371 case 0x87: { II ii = res_N_R<0,A>(); NEXT; }
1372 case 0x88: { II ii = res_N_R<1,B>(); NEXT; }
1373 case 0x89: { II ii = res_N_R<1,C>(); NEXT; }
1374 case 0x8a: { II ii = res_N_R<1,D>(); NEXT; }
1375 case 0x8b: { II ii = res_N_R<1,E>(); NEXT; }
1376 case 0x8c: { II ii = res_N_R<1,H>(); NEXT; }
1377 case 0x8d: { II ii = res_N_R<1,L>(); NEXT; }
1378 case 0x8f: { II ii = res_N_R<1,A>(); NEXT; }
1379 case 0x90: { II ii = res_N_R<2,B>(); NEXT; }
1380 case 0x91: { II ii = res_N_R<2,C>(); NEXT; }
1381 case 0x92: { II ii = res_N_R<2,D>(); NEXT; }
1382 case 0x93: { II ii = res_N_R<2,E>(); NEXT; }
1383 case 0x94: { II ii = res_N_R<2,H>(); NEXT; }
1384 case 0x95: { II ii = res_N_R<2,L>(); NEXT; }
1385 case 0x97: { II ii = res_N_R<2,A>(); NEXT; }
1386 case 0x98: { II ii = res_N_R<3,B>(); NEXT; }
1387 case 0x99: { II ii = res_N_R<3,C>(); NEXT; }
1388 case 0x9a: { II ii = res_N_R<3,D>(); NEXT; }
1389 case 0x9b: { II ii = res_N_R<3,E>(); NEXT; }
1390 case 0x9c: { II ii = res_N_R<3,H>(); NEXT; }
1391 case 0x9d: { II ii = res_N_R<3,L>(); NEXT; }
1392 case 0x9f: { II ii = res_N_R<3,A>(); NEXT; }
1393 case 0xa0: { II ii = res_N_R<4,B>(); NEXT; }
1394 case 0xa1: { II ii = res_N_R<4,C>(); NEXT; }
1395 case 0xa2: { II ii = res_N_R<4,D>(); NEXT; }
1396 case 0xa3: { II ii = res_N_R<4,E>(); NEXT; }
1397 case 0xa4: { II ii = res_N_R<4,H>(); NEXT; }
1398 case 0xa5: { II ii = res_N_R<4,L>(); NEXT; }
1399 case 0xa7: { II ii = res_N_R<4,A>(); NEXT; }
1400 case 0xa8: { II ii = res_N_R<5,B>(); NEXT; }
1401 case 0xa9: { II ii = res_N_R<5,C>(); NEXT; }
1402 case 0xaa: { II ii = res_N_R<5,D>(); NEXT; }
1403 case 0xab: { II ii = res_N_R<5,E>(); NEXT; }
1404 case 0xac: { II ii = res_N_R<5,H>(); NEXT; }
1405 case 0xad: { II ii = res_N_R<5,L>(); NEXT; }
1406 case 0xaf: { II ii = res_N_R<5,A>(); NEXT; }
1407 case 0xb0: { II ii = res_N_R<6,B>(); NEXT; }
1408 case 0xb1: { II ii = res_N_R<6,C>(); NEXT; }
1409 case 0xb2: { II ii = res_N_R<6,D>(); NEXT; }
1410 case 0xb3: { II ii = res_N_R<6,E>(); NEXT; }
1411 case 0xb4: { II ii = res_N_R<6,H>(); NEXT; }
1412 case 0xb5: { II ii = res_N_R<6,L>(); NEXT; }
1413 case 0xb7: { II ii = res_N_R<6,A>(); NEXT; }
1414 case 0xb8: { II ii = res_N_R<7,B>(); NEXT; }
1415 case 0xb9: { II ii = res_N_R<7,C>(); NEXT; }
1416 case 0xba: { II ii = res_N_R<7,D>(); NEXT; }
1417 case 0xbb: { II ii = res_N_R<7,E>(); NEXT; }
1418 case 0xbc: { II ii = res_N_R<7,H>(); NEXT; }
1419 case 0xbd: { II ii = res_N_R<7,L>(); NEXT; }
1420 case 0xbf: { II ii = res_N_R<7,A>(); NEXT; }
1421 case 0x86: { II ii = res_N_xhl<0>(); NEXT; }
1422 case 0x8e: { II ii = res_N_xhl<1>(); NEXT; }
1423 case 0x96: { II ii = res_N_xhl<2>(); NEXT; }
1424 case 0x9e: { II ii = res_N_xhl<3>(); NEXT; }
1425 case 0xa6: { II ii = res_N_xhl<4>(); NEXT; }
1426 case 0xae: { II ii = res_N_xhl<5>(); NEXT; }
1427 case 0xb6: { II ii = res_N_xhl<6>(); NEXT; }
1428 case 0xbe: { II ii = res_N_xhl<7>(); NEXT; }
1429
1430 case 0xc0: { II ii = set_N_R<0,B>(); NEXT; }
1431 case 0xc1: { II ii = set_N_R<0,C>(); NEXT; }
1432 case 0xc2: { II ii = set_N_R<0,D>(); NEXT; }
1433 case 0xc3: { II ii = set_N_R<0,E>(); NEXT; }
1434 case 0xc4: { II ii = set_N_R<0,H>(); NEXT; }
1435 case 0xc5: { II ii = set_N_R<0,L>(); NEXT; }
1436 case 0xc7: { II ii = set_N_R<0,A>(); NEXT; }
1437 case 0xc8: { II ii = set_N_R<1,B>(); NEXT; }
1438 case 0xc9: { II ii = set_N_R<1,C>(); NEXT; }
1439 case 0xca: { II ii = set_N_R<1,D>(); NEXT; }
1440 case 0xcb: { II ii = set_N_R<1,E>(); NEXT; }
1441 case 0xcc: { II ii = set_N_R<1,H>(); NEXT; }
1442 case 0xcd: { II ii = set_N_R<1,L>(); NEXT; }
1443 case 0xcf: { II ii = set_N_R<1,A>(); NEXT; }
1444 case 0xd0: { II ii = set_N_R<2,B>(); NEXT; }
1445 case 0xd1: { II ii = set_N_R<2,C>(); NEXT; }
1446 case 0xd2: { II ii = set_N_R<2,D>(); NEXT; }
1447 case 0xd3: { II ii = set_N_R<2,E>(); NEXT; }
1448 case 0xd4: { II ii = set_N_R<2,H>(); NEXT; }
1449 case 0xd5: { II ii = set_N_R<2,L>(); NEXT; }
1450 case 0xd7: { II ii = set_N_R<2,A>(); NEXT; }
1451 case 0xd8: { II ii = set_N_R<3,B>(); NEXT; }
1452 case 0xd9: { II ii = set_N_R<3,C>(); NEXT; }
1453 case 0xda: { II ii = set_N_R<3,D>(); NEXT; }
1454 case 0xdb: { II ii = set_N_R<3,E>(); NEXT; }
1455 case 0xdc: { II ii = set_N_R<3,H>(); NEXT; }
1456 case 0xdd: { II ii = set_N_R<3,L>(); NEXT; }
1457 case 0xdf: { II ii = set_N_R<3,A>(); NEXT; }
1458 case 0xe0: { II ii = set_N_R<4,B>(); NEXT; }
1459 case 0xe1: { II ii = set_N_R<4,C>(); NEXT; }
1460 case 0xe2: { II ii = set_N_R<4,D>(); NEXT; }
1461 case 0xe3: { II ii = set_N_R<4,E>(); NEXT; }
1462 case 0xe4: { II ii = set_N_R<4,H>(); NEXT; }
1463 case 0xe5: { II ii = set_N_R<4,L>(); NEXT; }
1464 case 0xe7: { II ii = set_N_R<4,A>(); NEXT; }
1465 case 0xe8: { II ii = set_N_R<5,B>(); NEXT; }
1466 case 0xe9: { II ii = set_N_R<5,C>(); NEXT; }
1467 case 0xea: { II ii = set_N_R<5,D>(); NEXT; }
1468 case 0xeb: { II ii = set_N_R<5,E>(); NEXT; }
1469 case 0xec: { II ii = set_N_R<5,H>(); NEXT; }
1470 case 0xed: { II ii = set_N_R<5,L>(); NEXT; }
1471 case 0xef: { II ii = set_N_R<5,A>(); NEXT; }
1472 case 0xf0: { II ii = set_N_R<6,B>(); NEXT; }
1473 case 0xf1: { II ii = set_N_R<6,C>(); NEXT; }
1474 case 0xf2: { II ii = set_N_R<6,D>(); NEXT; }
1475 case 0xf3: { II ii = set_N_R<6,E>(); NEXT; }
1476 case 0xf4: { II ii = set_N_R<6,H>(); NEXT; }
1477 case 0xf5: { II ii = set_N_R<6,L>(); NEXT; }
1478 case 0xf7: { II ii = set_N_R<6,A>(); NEXT; }
1479 case 0xf8: { II ii = set_N_R<7,B>(); NEXT; }
1480 case 0xf9: { II ii = set_N_R<7,C>(); NEXT; }
1481 case 0xfa: { II ii = set_N_R<7,D>(); NEXT; }
1482 case 0xfb: { II ii = set_N_R<7,E>(); NEXT; }
1483 case 0xfc: { II ii = set_N_R<7,H>(); NEXT; }
1484 case 0xfd: { II ii = set_N_R<7,L>(); NEXT; }
1485 case 0xff: { II ii = set_N_R<7,A>(); NEXT; }
1486 case 0xc6: { II ii = set_N_xhl<0>(); NEXT; }
1487 case 0xce: { II ii = set_N_xhl<1>(); NEXT; }
1488 case 0xd6: { II ii = set_N_xhl<2>(); NEXT; }
1489 case 0xde: { II ii = set_N_xhl<3>(); NEXT; }
1490 case 0xe6: { II ii = set_N_xhl<4>(); NEXT; }
1491 case 0xee: { II ii = set_N_xhl<5>(); NEXT; }
1492 case 0xf6: { II ii = set_N_xhl<6>(); NEXT; }
1493 case 0xfe: { II ii = set_N_xhl<7>(); NEXT; }
1494 default: UNREACHABLE; return;
1495 }
1496}
1497CASE(ED) {
1498 setPC(getPC() + 1); // M1 cycle at this point
1499 byte ed_opcode = RDMEM_OPCODE<0>(T::CC_PREFIX);
1500 incR(1);
1501 switch (ed_opcode) {
1502 case 0x00: case 0x01: case 0x02: case 0x03:
1503 case 0x04: case 0x05: case 0x06: case 0x07:
1504 case 0x08: case 0x09: case 0x0a: case 0x0b:
1505 case 0x0c: case 0x0d: case 0x0e: case 0x0f:
1506 case 0x10: case 0x11: case 0x12: case 0x13:
1507 case 0x14: case 0x15: case 0x16: case 0x17:
1508 case 0x18: case 0x19: case 0x1a: case 0x1b:
1509 case 0x1c: case 0x1d: case 0x1e: case 0x1f:
1510 case 0x20: case 0x21: case 0x22: case 0x23:
1511 case 0x24: case 0x25: case 0x26: case 0x27:
1512 case 0x28: case 0x29: case 0x2a: case 0x2b:
1513 case 0x2c: case 0x2d: case 0x2e: case 0x2f:
1514 case 0x30: case 0x31: case 0x32: case 0x33:
1515 case 0x34: case 0x35: case 0x36: case 0x37:
1516 case 0x38: case 0x39: case 0x3a: case 0x3b:
1517 case 0x3c: case 0x3d: case 0x3e: case 0x3f:
1518
1519 case 0x77: case 0x7f:
1520
1521 case 0x80: case 0x81: case 0x82: case 0x83:
1522 case 0x84: case 0x85: case 0x86: case 0x87:
1523 case 0x88: case 0x89: case 0x8a: case 0x8b:
1524 case 0x8c: case 0x8d: case 0x8e: case 0x8f:
1525 case 0x90: case 0x91: case 0x92: case 0x93:
1526 case 0x94: case 0x95: case 0x96: case 0x97:
1527 case 0x98: case 0x99: case 0x9a: case 0x9b:
1528 case 0x9c: case 0x9d: case 0x9e: case 0x9f:
1529 case 0xa4: case 0xa5: case 0xa6: case 0xa7:
1530 case 0xac: case 0xad: case 0xae: case 0xaf:
1531 case 0xb4: case 0xb5: case 0xb6: case 0xb7:
1532 case 0xbc: case 0xbd: case 0xbe: case 0xbf:
1533
1534 case 0xc0: case 0xc2:
1535 case 0xc4: case 0xc5: case 0xc6: case 0xc7:
1536 case 0xc8: case 0xca: case 0xcb:
1537 case 0xcc: case 0xcd: case 0xce: case 0xcf:
1538 case 0xd0: case 0xd2: case 0xd3:
1539 case 0xd4: case 0xd5: case 0xd6: case 0xd7:
1540 case 0xd8: case 0xda: case 0xdb:
1541 case 0xdc: case 0xdd: case 0xde: case 0xdf:
1542 case 0xe0: case 0xe1: case 0xe2: case 0xe3:
1543 case 0xe4: case 0xe5: case 0xe6: case 0xe7:
1544 case 0xe8: case 0xe9: case 0xea: case 0xeb:
1545 case 0xec: case 0xed: case 0xee: case 0xef:
1546 case 0xf0: case 0xf1: case 0xf2:
1547 case 0xf4: case 0xf5: case 0xf6: case 0xf7:
1548 case 0xf8: case 0xf9: case 0xfa: case 0xfb:
1549 case 0xfc: case 0xfd: case 0xfe: case 0xff:
1550 { II ii = nop(); NEXT; }
1551
1552 case 0x40: { II ii = in_R_c<B>(); NEXT; }
1553 case 0x48: { II ii = in_R_c<C>(); NEXT; }
1554 case 0x50: { II ii = in_R_c<D>(); NEXT; }
1555 case 0x58: { II ii = in_R_c<E>(); NEXT; }
1556 case 0x60: { II ii = in_R_c<H>(); NEXT; }
1557 case 0x68: { II ii = in_R_c<L>(); NEXT; }
1558 case 0x70: { II ii = in_R_c<DUMMY>(); NEXT; }
1559 case 0x78: { II ii = in_R_c<A>(); NEXT; }
1560
1561 case 0x41: { II ii = out_c_R<B>(); NEXT; }
1562 case 0x49: { II ii = out_c_R<C>(); NEXT; }
1563 case 0x51: { II ii = out_c_R<D>(); NEXT; }
1564 case 0x59: { II ii = out_c_R<E>(); NEXT; }
1565 case 0x61: { II ii = out_c_R<H>(); NEXT; }
1566 case 0x69: { II ii = out_c_R<L>(); NEXT; }
1567 case 0x71: { II ii = out_c_0(); NEXT; }
1568 case 0x79: { II ii = out_c_R<A>(); NEXT; }
1569
1570 case 0x42: { II ii = sbc_hl_SS<BC>(); NEXT; }
1571 case 0x52: { II ii = sbc_hl_SS<DE>(); NEXT; }
1572 case 0x62: { II ii = sbc_hl_hl (); NEXT; }
1573 case 0x72: { II ii = sbc_hl_SS<SP>(); NEXT; }
1574
1575 case 0x4a: { II ii = adc_hl_SS<BC>(); NEXT; }
1576 case 0x5a: { II ii = adc_hl_SS<DE>(); NEXT; }
1577 case 0x6a: { II ii = adc_hl_hl (); NEXT; }
1578 case 0x7a: { II ii = adc_hl_SS<SP>(); NEXT; }
1579
1580 case 0x43: { II ii = ld_xword_SS_ED<BC>(); NEXT; }
1581 case 0x53: { II ii = ld_xword_SS_ED<DE>(); NEXT; }
1582 case 0x63: { II ii = ld_xword_SS_ED<HL>(); NEXT; }
1583 case 0x73: { II ii = ld_xword_SS_ED<SP>(); NEXT; }
1584
1585 case 0x4b: { II ii = ld_SS_xword_ED<BC>(); NEXT; }
1586 case 0x5b: { II ii = ld_SS_xword_ED<DE>(); NEXT; }
1587 case 0x6b: { II ii = ld_SS_xword_ED<HL>(); NEXT; }
1588 case 0x7b: { II ii = ld_SS_xword_ED<SP>(); NEXT; }
1589
1590 case 0x47: { II ii = ld_i_a(); NEXT; }
1591 case 0x4f: { II ii = ld_r_a(); NEXT; }
1592 case 0x57: { II ii = ld_a_IR<REG_I>(); if (T::IS_R800) { NEXT; } else { NEXT_STOP; }}
1593 case 0x5f: { II ii = ld_a_IR<REG_R>(); if (T::IS_R800) { NEXT; } else { NEXT_STOP; }}
1594
1595 case 0x67: { II ii = rrd(); NEXT; }
1596 case 0x6f: { II ii = rld(); NEXT; }
1597
1598 case 0x45: case 0x4d: case 0x55: case 0x5d:
1599 case 0x65: case 0x6d: case 0x75: case 0x7d:
1600 { II ii = retn(); NEXT_STOP; }
1601 case 0x46: case 0x4e: case 0x66: case 0x6e:
1602 { II ii = im_N<0>(); NEXT; }
1603 case 0x56: case 0x76:
1604 { II ii = im_N<1>(); NEXT; }
1605 case 0x5e: case 0x7e:
1606 { II ii = im_N<2>(); NEXT; }
1607 case 0x44: case 0x4c: case 0x54: case 0x5c:
1608 case 0x64: case 0x6c: case 0x74: case 0x7c:
1609 { II ii = neg(); NEXT; }
1610
1611 case 0xa0: { II ii = ldi(); NEXT; }
1612 case 0xa1: { II ii = cpi(); NEXT; }
1613 case 0xa2: { II ii = ini(); NEXT; }
1614 case 0xa3: { II ii = outi(); NEXT; }
1615 case 0xa8: { II ii = ldd(); NEXT; }
1616 case 0xa9: { II ii = cpd(); NEXT; }
1617 case 0xaa: { II ii = ind(); NEXT; }
1618 case 0xab: { II ii = outd(); NEXT; }
1619 case 0xb0: { II ii = ldir(); NEXT; }
1620 case 0xb1: { II ii = cpir(); NEXT; }
1621 case 0xb2: { II ii = inir(); NEXT; }
1622 case 0xb3: { II ii = otir(); NEXT; }
1623 case 0xb8: { II ii = lddr(); NEXT; }
1624 case 0xb9: { II ii = cpdr(); NEXT; }
1625 case 0xba: { II ii = indr(); NEXT; }
1626 case 0xbb: { II ii = otdr(); NEXT; }
1627
1628 case 0xc1: { II ii = T::IS_R800 ? mulub_a_R<B>() : nop(); NEXT; }
1629 case 0xc9: { II ii = T::IS_R800 ? mulub_a_R<C>() : nop(); NEXT; }
1630 case 0xd1: { II ii = T::IS_R800 ? mulub_a_R<D>() : nop(); NEXT; }
1631 case 0xd9: { II ii = T::IS_R800 ? mulub_a_R<E>() : nop(); NEXT; }
1632 case 0xc3: { II ii = T::IS_R800 ? muluw_hl_SS<BC>() : nop(); NEXT; }
1633 case 0xf3: { II ii = T::IS_R800 ? muluw_hl_SS<SP>() : nop(); NEXT; }
1634 default: UNREACHABLE; return;
1635 }
1636}
1637MAYBE_UNUSED_LABEL opDD_2:
1638CASE(DD) {
1639 setPC(getPC() + 1); // M1 cycle at this point
1640 byte opcodeDD = RDMEM_OPCODE<0>(T::CC_DD + T::CC_MAIN);
1641 incR(1);
1642 switch (opcodeDD) {
1643 case 0x00: // nop();
1644 case 0x01: // ld_bc_word();
1645 case 0x02: // ld_xbc_a();
1646 case 0x03: // inc_bc();
1647 case 0x04: // inc_b();
1648 case 0x05: // dec_b();
1649 case 0x06: // ld_b_byte();
1650 case 0x07: // rlca();
1651 case 0x08: // ex_af_af();
1652 case 0x0a: // ld_a_xbc();
1653 case 0x0b: // dec_bc();
1654 case 0x0c: // inc_c();
1655 case 0x0d: // dec_c();
1656 case 0x0e: // ld_c_byte();
1657 case 0x0f: // rrca();
1658 case 0x10: // djnz();
1659 case 0x11: // ld_de_word();
1660 case 0x12: // ld_xde_a();
1661 case 0x13: // inc_de();
1662 case 0x14: // inc_d();
1663 case 0x15: // dec_d();
1664 case 0x16: // ld_d_byte();
1665 case 0x17: // rla();
1666 case 0x18: // jr();
1667 case 0x1a: // ld_a_xde();
1668 case 0x1b: // dec_de();
1669 case 0x1c: // inc_e();
1670 case 0x1d: // dec_e();
1671 case 0x1e: // ld_e_byte();
1672 case 0x1f: // rra();
1673 case 0x20: // jr_nz();
1674 case 0x27: // daa();
1675 case 0x28: // jr_z();
1676 case 0x2f: // cpl();
1677 case 0x30: // jr_nc();
1678 case 0x31: // ld_sp_word();
1679 case 0x32: // ld_xbyte_a();
1680 case 0x33: // inc_sp();
1681 case 0x37: // scf();
1682 case 0x38: // jr_c();
1683 case 0x3a: // ld_a_xbyte();
1684 case 0x3b: // dec_sp();
1685 case 0x3c: // inc_a();
1686 case 0x3d: // dec_a();
1687 case 0x3e: // ld_a_byte();
1688 case 0x3f: // ccf();
1689
1690 case 0x40: // ld_b_b();
1691 case 0x41: // ld_b_c();
1692 case 0x42: // ld_b_d();
1693 case 0x43: // ld_b_e();
1694 case 0x47: // ld_b_a();
1695 case 0x48: // ld_c_b();
1696 case 0x49: // ld_c_c();
1697 case 0x4a: // ld_c_d();
1698 case 0x4b: // ld_c_e();
1699 case 0x4f: // ld_c_a();
1700 case 0x50: // ld_d_b();
1701 case 0x51: // ld_d_c();
1702 case 0x52: // ld_d_d();
1703 case 0x53: // ld_d_e();
1704 case 0x57: // ld_d_a();
1705 case 0x58: // ld_e_b();
1706 case 0x59: // ld_e_c();
1707 case 0x5a: // ld_e_d();
1708 case 0x5b: // ld_e_e();
1709 case 0x5f: // ld_e_a();
1710 case 0x64: // ld_ixh_ixh(); == nop
1711 case 0x6d: // ld_ixl_ixl(); == nop
1712 case 0x76: // halt();
1713 case 0x78: // ld_a_b();
1714 case 0x79: // ld_a_c();
1715 case 0x7a: // ld_a_d();
1716 case 0x7b: // ld_a_e();
1717 case 0x7f: // ld_a_a();
1718
1719 case 0x80: // add_a_b();
1720 case 0x81: // add_a_c();
1721 case 0x82: // add_a_d();
1722 case 0x83: // add_a_e();
1723 case 0x87: // add_a_a();
1724 case 0x88: // adc_a_b();
1725 case 0x89: // adc_a_c();
1726 case 0x8a: // adc_a_d();
1727 case 0x8b: // adc_a_e();
1728 case 0x8f: // adc_a_a();
1729 case 0x90: // sub_b();
1730 case 0x91: // sub_c();
1731 case 0x92: // sub_d();
1732 case 0x93: // sub_e();
1733 case 0x97: // sub_a();
1734 case 0x98: // sbc_a_b();
1735 case 0x99: // sbc_a_c();
1736 case 0x9a: // sbc_a_d();
1737 case 0x9b: // sbc_a_e();
1738 case 0x9f: // sbc_a_a();
1739 case 0xa0: // and_b();
1740 case 0xa1: // and_c();
1741 case 0xa2: // and_d();
1742 case 0xa3: // and_e();
1743 case 0xa7: // and_a();
1744 case 0xa8: // xor_b();
1745 case 0xa9: // xor_c();
1746 case 0xaa: // xor_d();
1747 case 0xab: // xor_e();
1748 case 0xaf: // xor_a();
1749 case 0xb0: // or_b();
1750 case 0xb1: // or_c();
1751 case 0xb2: // or_d();
1752 case 0xb3: // or_e();
1753 case 0xb7: // or_a();
1754 case 0xb8: // cp_b();
1755 case 0xb9: // cp_c();
1756 case 0xba: // cp_d();
1757 case 0xbb: // cp_e();
1758 case 0xbf: // cp_a();
1759
1760 case 0xc0: // ret_nz();
1761 case 0xc1: // pop_bc();
1762 case 0xc2: // jp_nz();
1763 case 0xc3: // jp();
1764 case 0xc4: // call_nz();
1765 case 0xc5: // push_bc();
1766 case 0xc6: // add_a_byte();
1767 case 0xc7: // rst_00();
1768 case 0xc8: // ret_z();
1769 case 0xc9: // ret();
1770 case 0xca: // jp_z();
1771 case 0xcc: // call_z();
1772 case 0xcd: // call();
1773 case 0xce: // adc_a_byte();
1774 case 0xcf: // rst_08();
1775 case 0xd0: // ret_nc();
1776 case 0xd1: // pop_de();
1777 case 0xd2: // jp_nc();
1778 case 0xd3: // out_byte_a();
1779 case 0xd4: // call_nc();
1780 case 0xd5: // push_de();
1781 case 0xd6: // sub_byte();
1782 case 0xd7: // rst_10();
1783 case 0xd8: // ret_c();
1784 case 0xd9: // exx();
1785 case 0xda: // jp_c();
1786 case 0xdb: // in_a_byte();
1787 case 0xdc: // call_c();
1788 case 0xde: // sbc_a_byte();
1789 case 0xdf: // rst_18();
1790 case 0xe0: // ret_po();
1791 case 0xe2: // jp_po();
1792 case 0xe4: // call_po();
1793 case 0xe6: // and_byte();
1794 case 0xe7: // rst_20();
1795 case 0xe8: // ret_pe();
1796 case 0xea: // jp_pe();
1797 case 0xeb: // ex_de_hl();
1798 case 0xec: // call_pe();
1799 case 0xed: // ed();
1800 case 0xee: // xor_byte();
1801 case 0xef: // rst_28();
1802 case 0xf0: // ret_p();
1803 case 0xf1: // pop_af();
1804 case 0xf2: // jp_p();
1805 case 0xf3: // di();
1806 case 0xf4: // call_p();
1807 case 0xf5: // push_af();
1808 case 0xf6: // or_byte();
1809 case 0xf7: // rst_30();
1810 case 0xf8: // ret_m();
1811 case 0xfa: // jp_m();
1812 case 0xfb: // ei();
1813 case 0xfc: // call_m();
1814 case 0xfe: // cp_byte();
1815 case 0xff: // rst_38();
1816 if constexpr (T::IS_R800) {
1817 II ii = nop<T::CC_DD>(); NEXT;
1818 } else {
1819 T::add(T::CC_DD);
1820 #ifdef USE_COMPUTED_GOTO
1821 goto *(opcodeTable[opcodeDD]);
1822 #else
1823 opcodeMain = opcodeDD;
1824 goto switchOpcode;
1825 #endif
1826 }
1827
1828 case 0x09: { II ii = add_SS_TT<IX,BC,T::CC_DD>(); NEXT; }
1829 case 0x19: { II ii = add_SS_TT<IX,DE,T::CC_DD>(); NEXT; }
1830 case 0x29: { II ii = add_SS_SS<IX ,T::CC_DD>(); NEXT; }
1831 case 0x39: { II ii = add_SS_TT<IX,SP,T::CC_DD>(); NEXT; }
1832 case 0x21: { II ii = ld_SS_word<IX,T::CC_DD>(); NEXT; }
1833 case 0x22: { II ii = ld_xword_SS<IX,T::CC_DD>(); NEXT; }
1834 case 0x2a: { II ii = ld_SS_xword<IX,T::CC_DD>(); NEXT; }
1835 case 0x23: { II ii = inc_SS<IX,T::CC_DD>(); NEXT; }
1836 case 0x2b: { II ii = dec_SS<IX,T::CC_DD>(); NEXT; }
1837 case 0x24: { II ii = inc_R<IXH,T::CC_DD>(); NEXT; }
1838 case 0x2c: { II ii = inc_R<IXL,T::CC_DD>(); NEXT; }
1839 case 0x25: { II ii = dec_R<IXH,T::CC_DD>(); NEXT; }
1840 case 0x2d: { II ii = dec_R<IXL,T::CC_DD>(); NEXT; }
1841 case 0x26: { II ii = ld_R_byte<IXH,T::CC_DD>(); NEXT; }
1842 case 0x2e: { II ii = ld_R_byte<IXL,T::CC_DD>(); NEXT; }
1843 case 0x34: { II ii = inc_xix<IX>(); NEXT; }
1844 case 0x35: { II ii = dec_xix<IX>(); NEXT; }
1845 case 0x36: { II ii = ld_xix_byte<IX>(); NEXT; }
1846
1847 case 0x44: { II ii = ld_R_R<B,IXH,T::CC_DD>(); NEXT; }
1848 case 0x45: { II ii = ld_R_R<B,IXL,T::CC_DD>(); NEXT; }
1849 case 0x4c: { II ii = ld_R_R<C,IXH,T::CC_DD>(); NEXT; }
1850 case 0x4d: { II ii = ld_R_R<C,IXL,T::CC_DD>(); NEXT; }
1851 case 0x54: { II ii = ld_R_R<D,IXH,T::CC_DD>(); NEXT; }
1852 case 0x55: { II ii = ld_R_R<D,IXL,T::CC_DD>(); NEXT; }
1853 case 0x5c: { II ii = ld_R_R<E,IXH,T::CC_DD>(); NEXT; }
1854 case 0x5d: { II ii = ld_R_R<E,IXL,T::CC_DD>(); NEXT; }
1855 case 0x7c: { II ii = ld_R_R<A,IXH,T::CC_DD>(); NEXT; }
1856 case 0x7d: { II ii = ld_R_R<A,IXL,T::CC_DD>(); NEXT; }
1857 case 0x60: { II ii = ld_R_R<IXH,B,T::CC_DD>(); NEXT; }
1858 case 0x61: { II ii = ld_R_R<IXH,C,T::CC_DD>(); NEXT; }
1859 case 0x62: { II ii = ld_R_R<IXH,D,T::CC_DD>(); NEXT; }
1860 case 0x63: { II ii = ld_R_R<IXH,E,T::CC_DD>(); NEXT; }
1861 case 0x65: { II ii = ld_R_R<IXH,IXL,T::CC_DD>(); NEXT; }
1862 case 0x67: { II ii = ld_R_R<IXH,A,T::CC_DD>(); NEXT; }
1863 case 0x68: { II ii = ld_R_R<IXL,B,T::CC_DD>(); NEXT; }
1864 case 0x69: { II ii = ld_R_R<IXL,C,T::CC_DD>(); NEXT; }
1865 case 0x6a: { II ii = ld_R_R<IXL,D,T::CC_DD>(); NEXT; }
1866 case 0x6b: { II ii = ld_R_R<IXL,E,T::CC_DD>(); NEXT; }
1867 case 0x6c: { II ii = ld_R_R<IXL,IXH,T::CC_DD>(); NEXT; }
1868 case 0x6f: { II ii = ld_R_R<IXL,A,T::CC_DD>(); NEXT; }
1869 case 0x70: { II ii = ld_xix_R<IX,B>(); NEXT; }
1870 case 0x71: { II ii = ld_xix_R<IX,C>(); NEXT; }
1871 case 0x72: { II ii = ld_xix_R<IX,D>(); NEXT; }
1872 case 0x73: { II ii = ld_xix_R<IX,E>(); NEXT; }
1873 case 0x74: { II ii = ld_xix_R<IX,H>(); NEXT; }
1874 case 0x75: { II ii = ld_xix_R<IX,L>(); NEXT; }
1875 case 0x77: { II ii = ld_xix_R<IX,A>(); NEXT; }
1876 case 0x46: { II ii = ld_R_xix<B,IX>(); NEXT; }
1877 case 0x4e: { II ii = ld_R_xix<C,IX>(); NEXT; }
1878 case 0x56: { II ii = ld_R_xix<D,IX>(); NEXT; }
1879 case 0x5e: { II ii = ld_R_xix<E,IX>(); NEXT; }
1880 case 0x66: { II ii = ld_R_xix<H,IX>(); NEXT; }
1881 case 0x6e: { II ii = ld_R_xix<L,IX>(); NEXT; }
1882 case 0x7e: { II ii = ld_R_xix<A,IX>(); NEXT; }
1883
1884 case 0x84: { II ii = add_a_R<IXH,T::CC_DD>(); NEXT; }
1885 case 0x85: { II ii = add_a_R<IXL,T::CC_DD>(); NEXT; }
1886 case 0x86: { II ii = add_a_xix<IX>(); NEXT; }
1887 case 0x8c: { II ii = adc_a_R<IXH,T::CC_DD>(); NEXT; }
1888 case 0x8d: { II ii = adc_a_R<IXL,T::CC_DD>(); NEXT; }
1889 case 0x8e: { II ii = adc_a_xix<IX>(); NEXT; }
1890 case 0x94: { II ii = sub_R<IXH,T::CC_DD>(); NEXT; }
1891 case 0x95: { II ii = sub_R<IXL,T::CC_DD>(); NEXT; }
1892 case 0x96: { II ii = sub_xix<IX>(); NEXT; }
1893 case 0x9c: { II ii = sbc_a_R<IXH,T::CC_DD>(); NEXT; }
1894 case 0x9d: { II ii = sbc_a_R<IXL,T::CC_DD>(); NEXT; }
1895 case 0x9e: { II ii = sbc_a_xix<IX>(); NEXT; }
1896 case 0xa4: { II ii = and_R<IXH,T::CC_DD>(); NEXT; }
1897 case 0xa5: { II ii = and_R<IXL,T::CC_DD>(); NEXT; }
1898 case 0xa6: { II ii = and_xix<IX>(); NEXT; }
1899 case 0xac: { II ii = xor_R<IXH,T::CC_DD>(); NEXT; }
1900 case 0xad: { II ii = xor_R<IXL,T::CC_DD>(); NEXT; }
1901 case 0xae: { II ii = xor_xix<IX>(); NEXT; }
1902 case 0xb4: { II ii = or_R<IXH,T::CC_DD>(); NEXT; }
1903 case 0xb5: { II ii = or_R<IXL,T::CC_DD>(); NEXT; }
1904 case 0xb6: { II ii = or_xix<IX>(); NEXT; }
1905 case 0xbc: { II ii = cp_R<IXH,T::CC_DD>(); NEXT; }
1906 case 0xbd: { II ii = cp_R<IXL,T::CC_DD>(); NEXT; }
1907 case 0xbe: { II ii = cp_xix<IX>(); NEXT; }
1908
1909 case 0xe1: { II ii = pop_SS <IX,T::CC_DD>(); NEXT; }
1910 case 0xe5: { II ii = push_SS<IX,T::CC_DD>(); NEXT; }
1911 case 0xe3: { II ii = ex_xsp_SS<IX,T::CC_DD>(); NEXT; }
1912 case 0xe9: { II ii = jp_SS<IX,T::CC_DD>(); NEXT; }
1913 case 0xf9: { II ii = ld_sp_SS<IX,T::CC_DD>(); NEXT; }
1914 case 0xcb: ixy = getIX(); goto xx_cb;
1915 case 0xdd:
1916 if constexpr (T::IS_R800) {
1917 II ii = nop<T::CC_DD>(); NEXT;
1918 } else {
1919 T::add(T::CC_DD); goto opDD_2;
1920 }
1921 case 0xfd:
1922 if constexpr (T::IS_R800) {
1923 II ii = nop<T::CC_DD>(); NEXT;
1924 } else {
1925 T::add(T::CC_DD); goto opFD_2;
1926 }
1927 default: UNREACHABLE; return;
1928 }
1929}
1930MAYBE_UNUSED_LABEL opFD_2:
1931CASE(FD) {
1932 setPC(getPC() + 1); // M1 cycle at this point
1933 byte opcodeFD = RDMEM_OPCODE<0>(T::CC_DD + T::CC_MAIN);
1934 incR(1);
1935 switch (opcodeFD) {
1936 case 0x00: // nop();
1937 case 0x01: // ld_bc_word();
1938 case 0x02: // ld_xbc_a();
1939 case 0x03: // inc_bc();
1940 case 0x04: // inc_b();
1941 case 0x05: // dec_b();
1942 case 0x06: // ld_b_byte();
1943 case 0x07: // rlca();
1944 case 0x08: // ex_af_af();
1945 case 0x0a: // ld_a_xbc();
1946 case 0x0b: // dec_bc();
1947 case 0x0c: // inc_c();
1948 case 0x0d: // dec_c();
1949 case 0x0e: // ld_c_byte();
1950 case 0x0f: // rrca();
1951 case 0x10: // djnz();
1952 case 0x11: // ld_de_word();
1953 case 0x12: // ld_xde_a();
1954 case 0x13: // inc_de();
1955 case 0x14: // inc_d();
1956 case 0x15: // dec_d();
1957 case 0x16: // ld_d_byte();
1958 case 0x17: // rla();
1959 case 0x18: // jr();
1960 case 0x1a: // ld_a_xde();
1961 case 0x1b: // dec_de();
1962 case 0x1c: // inc_e();
1963 case 0x1d: // dec_e();
1964 case 0x1e: // ld_e_byte();
1965 case 0x1f: // rra();
1966 case 0x20: // jr_nz();
1967 case 0x27: // daa();
1968 case 0x28: // jr_z();
1969 case 0x2f: // cpl();
1970 case 0x30: // jr_nc();
1971 case 0x31: // ld_sp_word();
1972 case 0x32: // ld_xbyte_a();
1973 case 0x33: // inc_sp();
1974 case 0x37: // scf();
1975 case 0x38: // jr_c();
1976 case 0x3a: // ld_a_xbyte();
1977 case 0x3b: // dec_sp();
1978 case 0x3c: // inc_a();
1979 case 0x3d: // dec_a();
1980 case 0x3e: // ld_a_byte();
1981 case 0x3f: // ccf();
1982
1983 case 0x40: // ld_b_b();
1984 case 0x41: // ld_b_c();
1985 case 0x42: // ld_b_d();
1986 case 0x43: // ld_b_e();
1987 case 0x47: // ld_b_a();
1988 case 0x48: // ld_c_b();
1989 case 0x49: // ld_c_c();
1990 case 0x4a: // ld_c_d();
1991 case 0x4b: // ld_c_e();
1992 case 0x4f: // ld_c_a();
1993 case 0x50: // ld_d_b();
1994 case 0x51: // ld_d_c();
1995 case 0x52: // ld_d_d();
1996 case 0x53: // ld_d_e();
1997 case 0x57: // ld_d_a();
1998 case 0x58: // ld_e_b();
1999 case 0x59: // ld_e_c();
2000 case 0x5a: // ld_e_d();
2001 case 0x5b: // ld_e_e();
2002 case 0x5f: // ld_e_a();
2003 case 0x64: // ld_ixh_ixh(); == nop
2004 case 0x6d: // ld_ixl_ixl(); == nop
2005 case 0x76: // halt();
2006 case 0x78: // ld_a_b();
2007 case 0x79: // ld_a_c();
2008 case 0x7a: // ld_a_d();
2009 case 0x7b: // ld_a_e();
2010 case 0x7f: // ld_a_a();
2011
2012 case 0x80: // add_a_b();
2013 case 0x81: // add_a_c();
2014 case 0x82: // add_a_d();
2015 case 0x83: // add_a_e();
2016 case 0x87: // add_a_a();
2017 case 0x88: // adc_a_b();
2018 case 0x89: // adc_a_c();
2019 case 0x8a: // adc_a_d();
2020 case 0x8b: // adc_a_e();
2021 case 0x8f: // adc_a_a();
2022 case 0x90: // sub_b();
2023 case 0x91: // sub_c();
2024 case 0x92: // sub_d();
2025 case 0x93: // sub_e();
2026 case 0x97: // sub_a();
2027 case 0x98: // sbc_a_b();
2028 case 0x99: // sbc_a_c();
2029 case 0x9a: // sbc_a_d();
2030 case 0x9b: // sbc_a_e();
2031 case 0x9f: // sbc_a_a();
2032 case 0xa0: // and_b();
2033 case 0xa1: // and_c();
2034 case 0xa2: // and_d();
2035 case 0xa3: // and_e();
2036 case 0xa7: // and_a();
2037 case 0xa8: // xor_b();
2038 case 0xa9: // xor_c();
2039 case 0xaa: // xor_d();
2040 case 0xab: // xor_e();
2041 case 0xaf: // xor_a();
2042 case 0xb0: // or_b();
2043 case 0xb1: // or_c();
2044 case 0xb2: // or_d();
2045 case 0xb3: // or_e();
2046 case 0xb7: // or_a();
2047 case 0xb8: // cp_b();
2048 case 0xb9: // cp_c();
2049 case 0xba: // cp_d();
2050 case 0xbb: // cp_e();
2051 case 0xbf: // cp_a();
2052
2053 case 0xc0: // ret_nz();
2054 case 0xc1: // pop_bc();
2055 case 0xc2: // jp_nz();
2056 case 0xc3: // jp();
2057 case 0xc4: // call_nz();
2058 case 0xc5: // push_bc();
2059 case 0xc6: // add_a_byte();
2060 case 0xc7: // rst_00();
2061 case 0xc8: // ret_z();
2062 case 0xc9: // ret();
2063 case 0xca: // jp_z();
2064 case 0xcc: // call_z();
2065 case 0xcd: // call();
2066 case 0xce: // adc_a_byte();
2067 case 0xcf: // rst_08();
2068 case 0xd0: // ret_nc();
2069 case 0xd1: // pop_de();
2070 case 0xd2: // jp_nc();
2071 case 0xd3: // out_byte_a();
2072 case 0xd4: // call_nc();
2073 case 0xd5: // push_de();
2074 case 0xd6: // sub_byte();
2075 case 0xd7: // rst_10();
2076 case 0xd8: // ret_c();
2077 case 0xd9: // exx();
2078 case 0xda: // jp_c();
2079 case 0xdb: // in_a_byte();
2080 case 0xdc: // call_c();
2081 case 0xde: // sbc_a_byte();
2082 case 0xdf: // rst_18();
2083 case 0xe0: // ret_po();
2084 case 0xe2: // jp_po();
2085 case 0xe4: // call_po();
2086 case 0xe6: // and_byte();
2087 case 0xe7: // rst_20();
2088 case 0xe8: // ret_pe();
2089 case 0xea: // jp_pe();
2090 case 0xeb: // ex_de_hl();
2091 case 0xec: // call_pe();
2092 case 0xed: // ed();
2093 case 0xee: // xor_byte();
2094 case 0xef: // rst_28();
2095 case 0xf0: // ret_p();
2096 case 0xf1: // pop_af();
2097 case 0xf2: // jp_p();
2098 case 0xf3: // di();
2099 case 0xf4: // call_p();
2100 case 0xf5: // push_af();
2101 case 0xf6: // or_byte();
2102 case 0xf7: // rst_30();
2103 case 0xf8: // ret_m();
2104 case 0xfa: // jp_m();
2105 case 0xfb: // ei();
2106 case 0xfc: // call_m();
2107 case 0xfe: // cp_byte();
2108 case 0xff: // rst_38();
2109 if constexpr (T::IS_R800) {
2110 II ii = nop<T::CC_DD>(); NEXT;
2111 } else {
2112 T::add(T::CC_DD);
2113 #ifdef USE_COMPUTED_GOTO
2114 goto *(opcodeTable[opcodeFD]);
2115 #else
2116 opcodeMain = opcodeFD;
2117 goto switchOpcode;
2118 #endif
2119 }
2120
2121 case 0x09: { II ii = add_SS_TT<IY,BC,T::CC_DD>(); NEXT; }
2122 case 0x19: { II ii = add_SS_TT<IY,DE,T::CC_DD>(); NEXT; }
2123 case 0x29: { II ii = add_SS_SS<IY ,T::CC_DD>(); NEXT; }
2124 case 0x39: { II ii = add_SS_TT<IY,SP,T::CC_DD>(); NEXT; }
2125 case 0x21: { II ii = ld_SS_word<IY,T::CC_DD>(); NEXT; }
2126 case 0x22: { II ii = ld_xword_SS<IY,T::CC_DD>(); NEXT; }
2127 case 0x2a: { II ii = ld_SS_xword<IY,T::CC_DD>(); NEXT; }
2128 case 0x23: { II ii = inc_SS<IY,T::CC_DD>(); NEXT; }
2129 case 0x2b: { II ii = dec_SS<IY,T::CC_DD>(); NEXT; }
2130 case 0x24: { II ii = inc_R<IYH,T::CC_DD>(); NEXT; }
2131 case 0x2c: { II ii = inc_R<IYL,T::CC_DD>(); NEXT; }
2132 case 0x25: { II ii = dec_R<IYH,T::CC_DD>(); NEXT; }
2133 case 0x2d: { II ii = dec_R<IYL,T::CC_DD>(); NEXT; }
2134 case 0x26: { II ii = ld_R_byte<IYH,T::CC_DD>(); NEXT; }
2135 case 0x2e: { II ii = ld_R_byte<IYL,T::CC_DD>(); NEXT; }
2136 case 0x34: { II ii = inc_xix<IY>(); NEXT; }
2137 case 0x35: { II ii = dec_xix<IY>(); NEXT; }
2138 case 0x36: { II ii = ld_xix_byte<IY>(); NEXT; }
2139
2140 case 0x44: { II ii = ld_R_R<B,IYH,T::CC_DD>(); NEXT; }
2141 case 0x45: { II ii = ld_R_R<B,IYL,T::CC_DD>(); NEXT; }
2142 case 0x4c: { II ii = ld_R_R<C,IYH,T::CC_DD>(); NEXT; }
2143 case 0x4d: { II ii = ld_R_R<C,IYL,T::CC_DD>(); NEXT; }
2144 case 0x54: { II ii = ld_R_R<D,IYH,T::CC_DD>(); NEXT; }
2145 case 0x55: { II ii = ld_R_R<D,IYL,T::CC_DD>(); NEXT; }
2146 case 0x5c: { II ii = ld_R_R<E,IYH,T::CC_DD>(); NEXT; }
2147 case 0x5d: { II ii = ld_R_R<E,IYL,T::CC_DD>(); NEXT; }
2148 case 0x7c: { II ii = ld_R_R<A,IYH,T::CC_DD>(); NEXT; }
2149 case 0x7d: { II ii = ld_R_R<A,IYL,T::CC_DD>(); NEXT; }
2150 case 0x60: { II ii = ld_R_R<IYH,B,T::CC_DD>(); NEXT; }
2151 case 0x61: { II ii = ld_R_R<IYH,C,T::CC_DD>(); NEXT; }
2152 case 0x62: { II ii = ld_R_R<IYH,D,T::CC_DD>(); NEXT; }
2153 case 0x63: { II ii = ld_R_R<IYH,E,T::CC_DD>(); NEXT; }
2154 case 0x65: { II ii = ld_R_R<IYH,IYL,T::CC_DD>(); NEXT; }
2155 case 0x67: { II ii = ld_R_R<IYH,A,T::CC_DD>(); NEXT; }
2156 case 0x68: { II ii = ld_R_R<IYL,B,T::CC_DD>(); NEXT; }
2157 case 0x69: { II ii = ld_R_R<IYL,C,T::CC_DD>(); NEXT; }
2158 case 0x6a: { II ii = ld_R_R<IYL,D,T::CC_DD>(); NEXT; }
2159 case 0x6b: { II ii = ld_R_R<IYL,E,T::CC_DD>(); NEXT; }
2160 case 0x6c: { II ii = ld_R_R<IYL,IYH,T::CC_DD>(); NEXT; }
2161 case 0x6f: { II ii = ld_R_R<IYL,A,T::CC_DD>(); NEXT; }
2162 case 0x70: { II ii = ld_xix_R<IY,B>(); NEXT; }
2163 case 0x71: { II ii = ld_xix_R<IY,C>(); NEXT; }
2164 case 0x72: { II ii = ld_xix_R<IY,D>(); NEXT; }
2165 case 0x73: { II ii = ld_xix_R<IY,E>(); NEXT; }
2166 case 0x74: { II ii = ld_xix_R<IY,H>(); NEXT; }
2167 case 0x75: { II ii = ld_xix_R<IY,L>(); NEXT; }
2168 case 0x77: { II ii = ld_xix_R<IY,A>(); NEXT; }
2169 case 0x46: { II ii = ld_R_xix<B,IY>(); NEXT; }
2170 case 0x4e: { II ii = ld_R_xix<C,IY>(); NEXT; }
2171 case 0x56: { II ii = ld_R_xix<D,IY>(); NEXT; }
2172 case 0x5e: { II ii = ld_R_xix<E,IY>(); NEXT; }
2173 case 0x66: { II ii = ld_R_xix<H,IY>(); NEXT; }
2174 case 0x6e: { II ii = ld_R_xix<L,IY>(); NEXT; }
2175 case 0x7e: { II ii = ld_R_xix<A,IY>(); NEXT; }
2176
2177 case 0x84: { II ii = add_a_R<IYH,T::CC_DD>(); NEXT; }
2178 case 0x85: { II ii = add_a_R<IYL,T::CC_DD>(); NEXT; }
2179 case 0x86: { II ii = add_a_xix<IY>(); NEXT; }
2180 case 0x8c: { II ii = adc_a_R<IYH,T::CC_DD>(); NEXT; }
2181 case 0x8d: { II ii = adc_a_R<IYL,T::CC_DD>(); NEXT; }
2182 case 0x8e: { II ii = adc_a_xix<IY>(); NEXT; }
2183 case 0x94: { II ii = sub_R<IYH,T::CC_DD>(); NEXT; }
2184 case 0x95: { II ii = sub_R<IYL,T::CC_DD>(); NEXT; }
2185 case 0x96: { II ii = sub_xix<IY>(); NEXT; }
2186 case 0x9c: { II ii = sbc_a_R<IYH,T::CC_DD>(); NEXT; }
2187 case 0x9d: { II ii = sbc_a_R<IYL,T::CC_DD>(); NEXT; }
2188 case 0x9e: { II ii = sbc_a_xix<IY>(); NEXT; }
2189 case 0xa4: { II ii = and_R<IYH,T::CC_DD>(); NEXT; }
2190 case 0xa5: { II ii = and_R<IYL,T::CC_DD>(); NEXT; }
2191 case 0xa6: { II ii = and_xix<IY>(); NEXT; }
2192 case 0xac: { II ii = xor_R<IYH,T::CC_DD>(); NEXT; }
2193 case 0xad: { II ii = xor_R<IYL,T::CC_DD>(); NEXT; }
2194 case 0xae: { II ii = xor_xix<IY>(); NEXT; }
2195 case 0xb4: { II ii = or_R<IYH,T::CC_DD>(); NEXT; }
2196 case 0xb5: { II ii = or_R<IYL,T::CC_DD>(); NEXT; }
2197 case 0xb6: { II ii = or_xix<IY>(); NEXT; }
2198 case 0xbc: { II ii = cp_R<IYH,T::CC_DD>(); NEXT; }
2199 case 0xbd: { II ii = cp_R<IYL,T::CC_DD>(); NEXT; }
2200 case 0xbe: { II ii = cp_xix<IY>(); NEXT; }
2201
2202 case 0xe1: { II ii = pop_SS <IY,T::CC_DD>(); NEXT; }
2203 case 0xe5: { II ii = push_SS<IY,T::CC_DD>(); NEXT; }
2204 case 0xe3: { II ii = ex_xsp_SS<IY,T::CC_DD>(); NEXT; }
2205 case 0xe9: { II ii = jp_SS<IY,T::CC_DD>(); NEXT; }
2206 case 0xf9: { II ii = ld_sp_SS<IY,T::CC_DD>(); NEXT; }
2207 case 0xcb: ixy = getIY(); goto xx_cb;
2208 case 0xdd:
2209 if constexpr (T::IS_R800) {
2210 II ii = nop<T::CC_DD>(); NEXT;
2211 } else {
2212 T::add(T::CC_DD); goto opDD_2;
2213 }
2214 case 0xfd:
2215 if constexpr (T::IS_R800) {
2216 II ii = nop<T::CC_DD>(); NEXT;
2217 } else {
2218 T::add(T::CC_DD); goto opFD_2;
2219 }
2220 default: UNREACHABLE; return;
2221 }
2222}
2223#ifndef USE_COMPUTED_GOTO
2224 default: UNREACHABLE; return;
2225}
2226#endif
2227
2228xx_cb: {
2229 unsigned tmp = RD_WORD_PC<1>(T::CC_DD + T::CC_DD_CB);
2230 auto ofst = narrow_cast<int8_t>(tmp & 0xFF);
2231 unsigned addr = narrow_cast<word>(ixy + ofst);
2232 auto xxcb_opcode = narrow_cast<byte>(tmp >> 8);
2233 switch (xxcb_opcode) {
2234 case 0x00: { II ii = rlc_xix_R<B>(addr); NEXT; }
2235 case 0x01: { II ii = rlc_xix_R<C>(addr); NEXT; }
2236 case 0x02: { II ii = rlc_xix_R<D>(addr); NEXT; }
2237 case 0x03: { II ii = rlc_xix_R<E>(addr); NEXT; }
2238 case 0x04: { II ii = rlc_xix_R<H>(addr); NEXT; }
2239 case 0x05: { II ii = rlc_xix_R<L>(addr); NEXT; }
2240 case 0x06: { II ii = rlc_xix_R<DUMMY>(addr); NEXT; }
2241 case 0x07: { II ii = rlc_xix_R<A>(addr); NEXT; }
2242 case 0x08: { II ii = rrc_xix_R<B>(addr); NEXT; }
2243 case 0x09: { II ii = rrc_xix_R<C>(addr); NEXT; }
2244 case 0x0a: { II ii = rrc_xix_R<D>(addr); NEXT; }
2245 case 0x0b: { II ii = rrc_xix_R<E>(addr); NEXT; }
2246 case 0x0c: { II ii = rrc_xix_R<H>(addr); NEXT; }
2247 case 0x0d: { II ii = rrc_xix_R<L>(addr); NEXT; }
2248 case 0x0e: { II ii = rrc_xix_R<DUMMY>(addr); NEXT; }
2249 case 0x0f: { II ii = rrc_xix_R<A>(addr); NEXT; }
2250 case 0x10: { II ii = rl_xix_R<B>(addr); NEXT; }
2251 case 0x11: { II ii = rl_xix_R<C>(addr); NEXT; }
2252 case 0x12: { II ii = rl_xix_R<D>(addr); NEXT; }
2253 case 0x13: { II ii = rl_xix_R<E>(addr); NEXT; }
2254 case 0x14: { II ii = rl_xix_R<H>(addr); NEXT; }
2255 case 0x15: { II ii = rl_xix_R<L>(addr); NEXT; }
2256 case 0x16: { II ii = rl_xix_R<DUMMY>(addr); NEXT; }
2257 case 0x17: { II ii = rl_xix_R<A>(addr); NEXT; }
2258 case 0x18: { II ii = rr_xix_R<B>(addr); NEXT; }
2259 case 0x19: { II ii = rr_xix_R<C>(addr); NEXT; }
2260 case 0x1a: { II ii = rr_xix_R<D>(addr); NEXT; }
2261 case 0x1b: { II ii = rr_xix_R<E>(addr); NEXT; }
2262 case 0x1c: { II ii = rr_xix_R<H>(addr); NEXT; }
2263 case 0x1d: { II ii = rr_xix_R<L>(addr); NEXT; }
2264 case 0x1e: { II ii = rr_xix_R<DUMMY>(addr); NEXT; }
2265 case 0x1f: { II ii = rr_xix_R<A>(addr); NEXT; }
2266 case 0x20: { II ii = sla_xix_R<B>(addr); NEXT; }
2267 case 0x21: { II ii = sla_xix_R<C>(addr); NEXT; }
2268 case 0x22: { II ii = sla_xix_R<D>(addr); NEXT; }
2269 case 0x23: { II ii = sla_xix_R<E>(addr); NEXT; }
2270 case 0x24: { II ii = sla_xix_R<H>(addr); NEXT; }
2271 case 0x25: { II ii = sla_xix_R<L>(addr); NEXT; }
2272 case 0x26: { II ii = sla_xix_R<DUMMY>(addr); NEXT; }
2273 case 0x27: { II ii = sla_xix_R<A>(addr); NEXT; }
2274 case 0x28: { II ii = sra_xix_R<B>(addr); NEXT; }
2275 case 0x29: { II ii = sra_xix_R<C>(addr); NEXT; }
2276 case 0x2a: { II ii = sra_xix_R<D>(addr); NEXT; }
2277 case 0x2b: { II ii = sra_xix_R<E>(addr); NEXT; }
2278 case 0x2c: { II ii = sra_xix_R<H>(addr); NEXT; }
2279 case 0x2d: { II ii = sra_xix_R<L>(addr); NEXT; }
2280 case 0x2e: { II ii = sra_xix_R<DUMMY>(addr); NEXT; }
2281 case 0x2f: { II ii = sra_xix_R<A>(addr); NEXT; }
2282 case 0x30: { II ii = T::IS_R800 ? sll2() : sll_xix_R<B>(addr); NEXT; }
2283 case 0x31: { II ii = T::IS_R800 ? sll2() : sll_xix_R<C>(addr); NEXT; }
2284 case 0x32: { II ii = T::IS_R800 ? sll2() : sll_xix_R<D>(addr); NEXT; }
2285 case 0x33: { II ii = T::IS_R800 ? sll2() : sll_xix_R<E>(addr); NEXT; }
2286 case 0x34: { II ii = T::IS_R800 ? sll2() : sll_xix_R<H>(addr); NEXT; }
2287 case 0x35: { II ii = T::IS_R800 ? sll2() : sll_xix_R<L>(addr); NEXT; }
2288 case 0x36: { II ii = T::IS_R800 ? sll2() : sll_xix_R<DUMMY>(addr); NEXT; }
2289 case 0x37: { II ii = T::IS_R800 ? sll2() : sll_xix_R<A>(addr); NEXT; }
2290 case 0x38: { II ii = srl_xix_R<B>(addr); NEXT; }
2291 case 0x39: { II ii = srl_xix_R<C>(addr); NEXT; }
2292 case 0x3a: { II ii = srl_xix_R<D>(addr); NEXT; }
2293 case 0x3b: { II ii = srl_xix_R<E>(addr); NEXT; }
2294 case 0x3c: { II ii = srl_xix_R<H>(addr); NEXT; }
2295 case 0x3d: { II ii = srl_xix_R<L>(addr); NEXT; }
2296 case 0x3e: { II ii = srl_xix_R<DUMMY>(addr); NEXT; }
2297 case 0x3f: { II ii = srl_xix_R<A>(addr); NEXT; }
2298
2299 case 0x40: case 0x41: case 0x42: case 0x43:
2300 case 0x44: case 0x45: case 0x46: case 0x47:
2301 { II ii = bit_N_xix<0>(addr); NEXT; }
2302 case 0x48: case 0x49: case 0x4a: case 0x4b:
2303 case 0x4c: case 0x4d: case 0x4e: case 0x4f:
2304 { II ii = bit_N_xix<1>(addr); NEXT; }
2305 case 0x50: case 0x51: case 0x52: case 0x53:
2306 case 0x54: case 0x55: case 0x56: case 0x57:
2307 { II ii = bit_N_xix<2>(addr); NEXT; }
2308 case 0x58: case 0x59: case 0x5a: case 0x5b:
2309 case 0x5c: case 0x5d: case 0x5e: case 0x5f:
2310 { II ii = bit_N_xix<3>(addr); NEXT; }
2311 case 0x60: case 0x61: case 0x62: case 0x63:
2312 case 0x64: case 0x65: case 0x66: case 0x67:
2313 { II ii = bit_N_xix<4>(addr); NEXT; }
2314 case 0x68: case 0x69: case 0x6a: case 0x6b:
2315 case 0x6c: case 0x6d: case 0x6e: case 0x6f:
2316 { II ii = bit_N_xix<5>(addr); NEXT; }
2317 case 0x70: case 0x71: case 0x72: case 0x73:
2318 case 0x74: case 0x75: case 0x76: case 0x77:
2319 { II ii = bit_N_xix<6>(addr); NEXT; }
2320 case 0x78: case 0x79: case 0x7a: case 0x7b:
2321 case 0x7c: case 0x7d: case 0x7e: case 0x7f:
2322 { II ii = bit_N_xix<7>(addr); NEXT; }
2323
2324 case 0x80: { II ii = res_N_xix_R<0,B>(addr); NEXT; }
2325 case 0x81: { II ii = res_N_xix_R<0,C>(addr); NEXT; }
2326 case 0x82: { II ii = res_N_xix_R<0,D>(addr); NEXT; }
2327 case 0x83: { II ii = res_N_xix_R<0,E>(addr); NEXT; }
2328 case 0x84: { II ii = res_N_xix_R<0,H>(addr); NEXT; }
2329 case 0x85: { II ii = res_N_xix_R<0,L>(addr); NEXT; }
2330 case 0x87: { II ii = res_N_xix_R<0,A>(addr); NEXT; }
2331 case 0x88: { II ii = res_N_xix_R<1,B>(addr); NEXT; }
2332 case 0x89: { II ii = res_N_xix_R<1,C>(addr); NEXT; }
2333 case 0x8a: { II ii = res_N_xix_R<1,D>(addr); NEXT; }
2334 case 0x8b: { II ii = res_N_xix_R<1,E>(addr); NEXT; }
2335 case 0x8c: { II ii = res_N_xix_R<1,H>(addr); NEXT; }
2336 case 0x8d: { II ii = res_N_xix_R<1,L>(addr); NEXT; }
2337 case 0x8f: { II ii = res_N_xix_R<1,A>(addr); NEXT; }
2338 case 0x90: { II ii = res_N_xix_R<2,B>(addr); NEXT; }
2339 case 0x91: { II ii = res_N_xix_R<2,C>(addr); NEXT; }
2340 case 0x92: { II ii = res_N_xix_R<2,D>(addr); NEXT; }
2341 case 0x93: { II ii = res_N_xix_R<2,E>(addr); NEXT; }
2342 case 0x94: { II ii = res_N_xix_R<2,H>(addr); NEXT; }
2343 case 0x95: { II ii = res_N_xix_R<2,L>(addr); NEXT; }
2344 case 0x97: { II ii = res_N_xix_R<2,A>(addr); NEXT; }
2345 case 0x98: { II ii = res_N_xix_R<3,B>(addr); NEXT; }
2346 case 0x99: { II ii = res_N_xix_R<3,C>(addr); NEXT; }
2347 case 0x9a: { II ii = res_N_xix_R<3,D>(addr); NEXT; }
2348 case 0x9b: { II ii = res_N_xix_R<3,E>(addr); NEXT; }
2349 case 0x9c: { II ii = res_N_xix_R<3,H>(addr); NEXT; }
2350 case 0x9d: { II ii = res_N_xix_R<3,L>(addr); NEXT; }
2351 case 0x9f: { II ii = res_N_xix_R<3,A>(addr); NEXT; }
2352 case 0xa0: { II ii = res_N_xix_R<4,B>(addr); NEXT; }
2353 case 0xa1: { II ii = res_N_xix_R<4,C>(addr); NEXT; }
2354 case 0xa2: { II ii = res_N_xix_R<4,D>(addr); NEXT; }
2355 case 0xa3: { II ii = res_N_xix_R<4,E>(addr); NEXT; }
2356 case 0xa4: { II ii = res_N_xix_R<4,H>(addr); NEXT; }
2357 case 0xa5: { II ii = res_N_xix_R<4,L>(addr); NEXT; }
2358 case 0xa7: { II ii = res_N_xix_R<4,A>(addr); NEXT; }
2359 case 0xa8: { II ii = res_N_xix_R<5,B>(addr); NEXT; }
2360 case 0xa9: { II ii = res_N_xix_R<5,C>(addr); NEXT; }
2361 case 0xaa: { II ii = res_N_xix_R<5,D>(addr); NEXT; }
2362 case 0xab: { II ii = res_N_xix_R<5,E>(addr); NEXT; }
2363 case 0xac: { II ii = res_N_xix_R<5,H>(addr); NEXT; }
2364 case 0xad: { II ii = res_N_xix_R<5,L>(addr); NEXT; }
2365 case 0xaf: { II ii = res_N_xix_R<5,A>(addr); NEXT; }
2366 case 0xb0: { II ii = res_N_xix_R<6,B>(addr); NEXT; }
2367 case 0xb1: { II ii = res_N_xix_R<6,C>(addr); NEXT; }
2368 case 0xb2: { II ii = res_N_xix_R<6,D>(addr); NEXT; }
2369 case 0xb3: { II ii = res_N_xix_R<6,E>(addr); NEXT; }
2370 case 0xb4: { II ii = res_N_xix_R<6,H>(addr); NEXT; }
2371 case 0xb5: { II ii = res_N_xix_R<6,L>(addr); NEXT; }
2372 case 0xb7: { II ii = res_N_xix_R<6,A>(addr); NEXT; }
2373 case 0xb8: { II ii = res_N_xix_R<7,B>(addr); NEXT; }
2374 case 0xb9: { II ii = res_N_xix_R<7,C>(addr); NEXT; }
2375 case 0xba: { II ii = res_N_xix_R<7,D>(addr); NEXT; }
2376 case 0xbb: { II ii = res_N_xix_R<7,E>(addr); NEXT; }
2377 case 0xbc: { II ii = res_N_xix_R<7,H>(addr); NEXT; }
2378 case 0xbd: { II ii = res_N_xix_R<7,L>(addr); NEXT; }
2379 case 0xbf: { II ii = res_N_xix_R<7,A>(addr); NEXT; }
2380 case 0x86: { II ii = res_N_xix_R<0,DUMMY>(addr); NEXT; }
2381 case 0x8e: { II ii = res_N_xix_R<1,DUMMY>(addr); NEXT; }
2382 case 0x96: { II ii = res_N_xix_R<2,DUMMY>(addr); NEXT; }
2383 case 0x9e: { II ii = res_N_xix_R<3,DUMMY>(addr); NEXT; }
2384 case 0xa6: { II ii = res_N_xix_R<4,DUMMY>(addr); NEXT; }
2385 case 0xae: { II ii = res_N_xix_R<5,DUMMY>(addr); NEXT; }
2386 case 0xb6: { II ii = res_N_xix_R<6,DUMMY>(addr); NEXT; }
2387 case 0xbe: { II ii = res_N_xix_R<7,DUMMY>(addr); NEXT; }
2388
2389 case 0xc0: { II ii = set_N_xix_R<0,B>(addr); NEXT; }
2390 case 0xc1: { II ii = set_N_xix_R<0,C>(addr); NEXT; }
2391 case 0xc2: { II ii = set_N_xix_R<0,D>(addr); NEXT; }
2392 case 0xc3: { II ii = set_N_xix_R<0,E>(addr); NEXT; }
2393 case 0xc4: { II ii = set_N_xix_R<0,H>(addr); NEXT; }
2394 case 0xc5: { II ii = set_N_xix_R<0,L>(addr); NEXT; }
2395 case 0xc7: { II ii = set_N_xix_R<0,A>(addr); NEXT; }
2396 case 0xc8: { II ii = set_N_xix_R<1,B>(addr); NEXT; }
2397 case 0xc9: { II ii = set_N_xix_R<1,C>(addr); NEXT; }
2398 case 0xca: { II ii = set_N_xix_R<1,D>(addr); NEXT; }
2399 case 0xcb: { II ii = set_N_xix_R<1,E>(addr); NEXT; }
2400 case 0xcc: { II ii = set_N_xix_R<1,H>(addr); NEXT; }
2401 case 0xcd: { II ii = set_N_xix_R<1,L>(addr); NEXT; }
2402 case 0xcf: { II ii = set_N_xix_R<1,A>(addr); NEXT; }
2403 case 0xd0: { II ii = set_N_xix_R<2,B>(addr); NEXT; }
2404 case 0xd1: { II ii = set_N_xix_R<2,C>(addr); NEXT; }
2405 case 0xd2: { II ii = set_N_xix_R<2,D>(addr); NEXT; }
2406 case 0xd3: { II ii = set_N_xix_R<2,E>(addr); NEXT; }
2407 case 0xd4: { II ii = set_N_xix_R<2,H>(addr); NEXT; }
2408 case 0xd5: { II ii = set_N_xix_R<2,L>(addr); NEXT; }
2409 case 0xd7: { II ii = set_N_xix_R<2,A>(addr); NEXT; }
2410 case 0xd8: { II ii = set_N_xix_R<3,B>(addr); NEXT; }
2411 case 0xd9: { II ii = set_N_xix_R<3,C>(addr); NEXT; }
2412 case 0xda: { II ii = set_N_xix_R<3,D>(addr); NEXT; }
2413 case 0xdb: { II ii = set_N_xix_R<3,E>(addr); NEXT; }
2414 case 0xdc: { II ii = set_N_xix_R<3,H>(addr); NEXT; }
2415 case 0xdd: { II ii = set_N_xix_R<3,L>(addr); NEXT; }
2416 case 0xdf: { II ii = set_N_xix_R<3,A>(addr); NEXT; }
2417 case 0xe0: { II ii = set_N_xix_R<4,B>(addr); NEXT; }
2418 case 0xe1: { II ii = set_N_xix_R<4,C>(addr); NEXT; }
2419 case 0xe2: { II ii = set_N_xix_R<4,D>(addr); NEXT; }
2420 case 0xe3: { II ii = set_N_xix_R<4,E>(addr); NEXT; }
2421 case 0xe4: { II ii = set_N_xix_R<4,H>(addr); NEXT; }
2422 case 0xe5: { II ii = set_N_xix_R<4,L>(addr); NEXT; }
2423 case 0xe7: { II ii = set_N_xix_R<4,A>(addr); NEXT; }
2424 case 0xe8: { II ii = set_N_xix_R<5,B>(addr); NEXT; }
2425 case 0xe9: { II ii = set_N_xix_R<5,C>(addr); NEXT; }
2426 case 0xea: { II ii = set_N_xix_R<5,D>(addr); NEXT; }
2427 case 0xeb: { II ii = set_N_xix_R<5,E>(addr); NEXT; }
2428 case 0xec: { II ii = set_N_xix_R<5,H>(addr); NEXT; }
2429 case 0xed: { II ii = set_N_xix_R<5,L>(addr); NEXT; }
2430 case 0xef: { II ii = set_N_xix_R<5,A>(addr); NEXT; }
2431 case 0xf0: { II ii = set_N_xix_R<6,B>(addr); NEXT; }
2432 case 0xf1: { II ii = set_N_xix_R<6,C>(addr); NEXT; }
2433 case 0xf2: { II ii = set_N_xix_R<6,D>(addr); NEXT; }
2434 case 0xf3: { II ii = set_N_xix_R<6,E>(addr); NEXT; }
2435 case 0xf4: { II ii = set_N_xix_R<6,H>(addr); NEXT; }
2436 case 0xf5: { II ii = set_N_xix_R<6,L>(addr); NEXT; }
2437 case 0xf7: { II ii = set_N_xix_R<6,A>(addr); NEXT; }
2438 case 0xf8: { II ii = set_N_xix_R<7,B>(addr); NEXT; }
2439 case 0xf9: { II ii = set_N_xix_R<7,C>(addr); NEXT; }
2440 case 0xfa: { II ii = set_N_xix_R<7,D>(addr); NEXT; }
2441 case 0xfb: { II ii = set_N_xix_R<7,E>(addr); NEXT; }
2442 case 0xfc: { II ii = set_N_xix_R<7,H>(addr); NEXT; }
2443 case 0xfd: { II ii = set_N_xix_R<7,L>(addr); NEXT; }
2444 case 0xff: { II ii = set_N_xix_R<7,A>(addr); NEXT; }
2445 case 0xc6: { II ii = set_N_xix_R<0,DUMMY>(addr); NEXT; }
2446 case 0xce: { II ii = set_N_xix_R<1,DUMMY>(addr); NEXT; }
2447 case 0xd6: { II ii = set_N_xix_R<2,DUMMY>(addr); NEXT; }
2448 case 0xde: { II ii = set_N_xix_R<3,DUMMY>(addr); NEXT; }
2449 case 0xe6: { II ii = set_N_xix_R<4,DUMMY>(addr); NEXT; }
2450 case 0xee: { II ii = set_N_xix_R<5,DUMMY>(addr); NEXT; }
2451 case 0xf6: { II ii = set_N_xix_R<6,DUMMY>(addr); NEXT; }
2452 case 0xfe: { II ii = set_N_xix_R<7,DUMMY>(addr); NEXT; }
2453 default: UNREACHABLE;
2454 }
2455 }
2456}
2457
2458template<typename T> inline void CPUCore<T>::cpuTracePre()
2459{
2460 start_pc = getPC();
2461}
2462template<typename T> inline void CPUCore<T>::cpuTracePost()
2463{
2464 if (tracingEnabled) [[unlikely]] {
2465 cpuTracePost_slow();
2466 }
2467}
2468template<typename T> void CPUCore<T>::cpuTracePost_slow()
2469{
2470 std::array<byte, 4> opBuf;
2471 std::string dasmOutput;
2472 dasm(*interface, start_pc, opBuf, dasmOutput, T::getTimeFast());
2473 std::cout << strCat(hex_string<4>(start_pc),
2474 " : ", dasmOutput,
2475 " AF=", hex_string<4>(getAF()),
2476 " BC=", hex_string<4>(getBC()),
2477 " DE=", hex_string<4>(getDE()),
2478 " HL=", hex_string<4>(getHL()),
2479 " IX=", hex_string<4>(getIX()),
2480 " IY=", hex_string<4>(getIY()),
2481 " SP=", hex_string<4>(getSP()),
2482 '\n')
2483 << std::flush;
2484}
2485
2486template<typename T> ExecIRQ CPUCore<T>::getExecIRQ() const
2487{
2488 if (nmiEdge) [[unlikely]] return ExecIRQ::NMI;
2489 if (IRQStatus && getIFF1() && !prevWasEI()) [[unlikely]] return ExecIRQ::IRQ;
2490 return ExecIRQ::NONE;
2491}
2492
2493template<typename T> void CPUCore<T>::executeSlow(ExecIRQ execIRQ)
2494{
2495 if (execIRQ == ExecIRQ::NMI) [[unlikely]] {
2496 nmiEdge = false;
2497 nmi(); // NMI occurred
2498 } else if (execIRQ == ExecIRQ::IRQ) [[unlikely]] {
2499 // normal interrupt
2500 if (prevWasLDAI()) [[unlikely]] {
2501 // HACK!!!
2502 // The 'ld a,i' or 'ld a,r' instruction copies the IFF2
2503 // bit to the V flag. Though when the Z80 accepts an
2504 // IRQ directly after this instruction, the V flag is 0
2505 // (instead of the expected value 1). This can probably
2506 // be explained if you look at the pipeline of the Z80.
2507 // But for speed reasons we implement it here as a
2508 // fix-up (a hack) in the IRQ routine. This behaviour
2509 // is actually a bug in the Z80.
2510 // Thanks to n_n for reporting this behaviour. I think
2511 // this was discovered by GuyveR800. Also thanks to
2512 // n_n for writing a test program that demonstrates
2513 // this quirk.
2514 // I also wrote a test program that demonstrates this
2515 // behaviour is the same whether 'ld a,i' is preceded
2516 // by a 'ei' instruction or not (so it's not caused by
2517 // the 'delayed IRQ acceptance of ei').
2518 assert(getF() & V_FLAG);
2519 setF(getF() & ~V_FLAG);
2520 }
2521 IRQAccept.signal();
2522 switch (getIM()) {
2523 case 0: irq0();
2524 break;
2525 case 1: irq1();
2526 break;
2527 case 2: irq2();
2528 break;
2529 default:
2531 }
2532 } else if (getHALT()) [[unlikely]] {
2533 // in halt mode
2534 incR(narrow_cast<byte>(T::advanceHalt(T::HALT_STATES, scheduler.getNext())));
2535 setSlowInstructions();
2536 } else {
2537 cpuTracePre();
2538 assert(T::limitReached()); // we want only one instruction
2539 executeInstructions();
2540 endInstruction();
2541
2542 if constexpr (T::IS_R800) {
2543 if (/*unlikely*/(prev2WasCall()) && /*likely*/(!prevWasPopRet())) [[unlikely]] {
2544 // On R800 a CALL or RST instruction not _immediately_
2545 // followed by a (single-byte) POP or RET instruction
2546 // causes an extra cycle in that following instruction.
2547 // No idea why yet. See doc/internal/r800-call.txt
2548 // for more information.
2549 //
2550 // TODO this implementation adds the extra cycle at
2551 // the end of the instruction POP/RET. It is not known
2552 // where in the instruction the real R800 adds this cycle.
2553 T::add(1);
2554 }
2555 }
2556 cpuTracePost();
2557 }
2558}
2559
2560template<typename T> void CPUCore<T>::execute(bool fastForward)
2561{
2562 // In fast-forward mode, breakpoints, watchpoints or debug conditions
2563 // won't trigger. It is possible we already are in break mode, but
2564 // break is ignored in fast-forward mode.
2565 assert(fastForward || !interface->isBreaked());
2566 if (fastForward) {
2567 interface->setFastForward(true);
2568 }
2569 execute2(fastForward);
2570 interface->setFastForward(false);
2571}
2572
2573template<typename T> void CPUCore<T>::execute2(bool fastForward)
2574{
2575 // note: Don't use getTimeFast() here, because 'once in a while' we
2576 // need to CPUClock::sync() to avoid overflow.
2577 // Should be done at least once per second (approx). So only
2578 // once in this method is enough.
2579 scheduler.schedule(T::getTime());
2580 setSlowInstructions();
2581
2582 // Note: we call scheduler _after_ executing the instruction and before
2583 // deciding between executeFast() and executeSlow() (because a
2584 // SyncPoint could set an IRQ and then we must choose executeSlow())
2585 if (fastForward ||
2586 (!interface->anyBreakPoints() && !tracingEnabled)) {
2587 // fast path, no breakpoints, no tracing
2588 do {
2589 if (slowInstructions) {
2590 --slowInstructions;
2591 executeSlow(getExecIRQ());
2592 scheduler.schedule(T::getTimeFast());
2593 } else {
2594 while (slowInstructions == 0) {
2595 T::enableLimit(); // does CPUClock::sync()
2596 if (!T::limitReached()) [[likely]] {
2597 // multiple instructions
2598 executeInstructions();
2599 // note: pipeline only shifted one
2600 // step for multiple instructions
2601 endInstruction();
2602 }
2603 scheduler.schedule(T::getTimeFast());
2604 if (needExitCPULoop()) return;
2605 }
2606 }
2607 } while (!needExitCPULoop());
2608 } else {
2609 do {
2610 if (slowInstructions == 0) {
2611 cpuTracePre();
2612 assert(T::limitReached()); // only one instruction
2613 executeInstructions();
2614 endInstruction();
2615 cpuTracePost();
2616 } else {
2617 --slowInstructions;
2618 executeSlow(getExecIRQ());
2619 }
2620 // Don't use getTimeFast() here, we need a call to
2621 // CPUClock::sync() 'once in a while'. (During a
2622 // reverse fast-forward this wasn't always the case).
2623 scheduler.schedule(T::getTime());
2624
2625 // Only check for breakpoints when we're not about to jump to an IRQ handler.
2626 //
2627 // This fixes the following problem reported by Grauw:
2628 //
2629 // I found a breakpoints bug: sometimes a breakpoint gets hit twice even
2630 // though the code is executed once. This manifests itself in my profiler
2631 // as an imbalance between section begin- and end-calls.
2632 //
2633 // Turns out this occurs when an interrupt occurs exactly on the line of
2634 // the breakpoint, then the breakpoint gets hit before immediately going
2635 // to the ISR, as well as when returning from the ISR.
2636 //
2637 // The IRQ is handled by the Z80 at the end of an instruction. So it
2638 // should change the PC before the next instruction is fetched and the
2639 // breakpoints should be evaluated during instruction fetch.
2640 //
2641 // I think Grauw's analysis is correct. Though for performance reasons we
2642 // don't emulate the Z80 like that: we don't check for IRQs at the end of
2643 // every instruction. In the openMSX emulation model, we can only enter an
2644 // ISR:
2645 // - (One instruction after) switching from DI to EI mode.
2646 // - After emulating device code. This can be:
2647 // * When the Z80 communicated with the device (IO or memory mapped IO).
2648 // * The device had set a synchronization point.
2649 // In all cases disableLimit() gets called which will cause
2650 // limitReached() to return true (and possibly slowInstructions to be > 0).
2651 // So after most emulated Z80 instructions there can't be a pending IRQ, so
2652 // checking for it is wasteful. Also synchronization points are handled
2653 // between emulated Z80 instructions, that means me must check for pending
2654 // IRQs at the start (instead of end) of an instruction.
2655 //
2656 auto execIRQ = getExecIRQ();
2657 if ((execIRQ == ExecIRQ::NONE) &&
2658 interface->checkBreakPoints(getPC())) {
2659 assert(interface->isBreaked());
2660 break;
2661 }
2662 } while (!needExitCPULoop());
2663 }
2664}
2665
2666template<typename T> template<Reg8 R8> ALWAYS_INLINE byte CPUCore<T>::get8() const {
2667 if constexpr (R8 == A) { return getA(); }
2668 else if constexpr (R8 == F) { return getF(); }
2669 else if constexpr (R8 == B) { return getB(); }
2670 else if constexpr (R8 == C) { return getC(); }
2671 else if constexpr (R8 == D) { return getD(); }
2672 else if constexpr (R8 == E) { return getE(); }
2673 else if constexpr (R8 == H) { return getH(); }
2674 else if constexpr (R8 == L) { return getL(); }
2675 else if constexpr (R8 == IXH) { return getIXh(); }
2676 else if constexpr (R8 == IXL) { return getIXl(); }
2677 else if constexpr (R8 == IYH) { return getIYh(); }
2678 else if constexpr (R8 == IYL) { return getIYl(); }
2679 else if constexpr (R8 == REG_I) { return getI(); }
2680 else if constexpr (R8 == REG_R) { return getR(); }
2681 else if constexpr (R8 == DUMMY) { return 0; }
2682 else { UNREACHABLE; return 0; }
2683}
2684template<typename T> template<Reg16 R16> ALWAYS_INLINE word CPUCore<T>::get16() const {
2685 if constexpr (R16 == AF) { return getAF(); }
2686 else if constexpr (R16 == BC) { return getBC(); }
2687 else if constexpr (R16 == DE) { return getDE(); }
2688 else if constexpr (R16 == HL) { return getHL(); }
2689 else if constexpr (R16 == IX) { return getIX(); }
2690 else if constexpr (R16 == IY) { return getIY(); }
2691 else if constexpr (R16 == SP) { return getSP(); }
2692 else { UNREACHABLE; return 0; }
2693}
2694template<typename T> template<Reg8 R8> ALWAYS_INLINE void CPUCore<T>::set8(byte x) {
2695 if constexpr (R8 == A) { setA(x); }
2696 else if constexpr (R8 == F) { setF(x); }
2697 else if constexpr (R8 == B) { setB(x); }
2698 else if constexpr (R8 == C) { setC(x); }
2699 else if constexpr (R8 == D) { setD(x); }
2700 else if constexpr (R8 == E) { setE(x); }
2701 else if constexpr (R8 == H) { setH(x); }
2702 else if constexpr (R8 == L) { setL(x); }
2703 else if constexpr (R8 == IXH) { setIXh(x); }
2704 else if constexpr (R8 == IXL) { setIXl(x); }
2705 else if constexpr (R8 == IYH) { setIYh(x); }
2706 else if constexpr (R8 == IYL) { setIYl(x); }
2707 else if constexpr (R8 == REG_I) { setI(x); }
2708 else if constexpr (R8 == REG_R) { setR(x); }
2709 else if constexpr (R8 == DUMMY) { /* nothing */ }
2710 else { UNREACHABLE; }
2711}
2712template<typename T> template<Reg16 R16> ALWAYS_INLINE void CPUCore<T>::set16(word x) {
2713 if constexpr (R16 == AF) { setAF(x); }
2714 else if constexpr (R16 == BC) { setBC(x); }
2715 else if constexpr (R16 == DE) { setDE(x); }
2716 else if constexpr (R16 == HL) { setHL(x); }
2717 else if constexpr (R16 == IX) { setIX(x); }
2718 else if constexpr (R16 == IY) { setIY(x); }
2719 else if constexpr (R16 == SP) { setSP(x); }
2720 else { UNREACHABLE; }
2721}
2722
2723// LD r,r
2724template<typename T> template<Reg8 DST, Reg8 SRC, int EE> II CPUCore<T>::ld_R_R() {
2725 set8<DST>(get8<SRC>()); return {1, T::CC_LD_R_R + EE};
2726}
2727
2728// LD SP,ss
2729template<typename T> template<Reg16 REG, int EE> II CPUCore<T>::ld_sp_SS() {
2730 setSP(get16<REG>()); return {1, T::CC_LD_SP_HL + EE};
2731}
2732
2733// LD (ss),a
2734template<typename T> template<Reg16 REG> II CPUCore<T>::ld_SS_a() {
2735 T::setMemPtr((getA() << 8) | ((get16<REG>() + 1) & 0xFF));
2736 WRMEM(get16<REG>(), getA(), T::CC_LD_SS_A_1);
2737 return {1, T::CC_LD_SS_A};
2738}
2739
2740// LD (HL),r
2741template<typename T> template<Reg8 SRC> II CPUCore<T>::ld_xhl_R() {
2742 WRMEM(getHL(), get8<SRC>(), T::CC_LD_HL_R_1);
2743 return {1, T::CC_LD_HL_R};
2744}
2745
2746// LD (IXY+e),r
2747template<typename T> template<Reg16 IXY, Reg8 SRC> II CPUCore<T>::ld_xix_R() {
2748 int8_t ofst = RDMEM_OPCODE<1>(T::CC_DD + T::CC_LD_XIX_R_1);
2749 unsigned addr = narrow_cast<word>(get16<IXY>() + ofst);
2750 T::setMemPtr(addr);
2751 WRMEM(addr, get8<SRC>(), T::CC_DD + T::CC_LD_XIX_R_2);
2752 return {2, T::CC_DD + T::CC_LD_XIX_R};
2753}
2754
2755// LD (HL),n
2756template<typename T> II CPUCore<T>::ld_xhl_byte() {
2757 byte val = RDMEM_OPCODE<1>(T::CC_LD_HL_N_1);
2758 WRMEM(getHL(), val, T::CC_LD_HL_N_2);
2759 return {2, T::CC_LD_HL_N};
2760}
2761
2762// LD (IXY+e),n
2763template<typename T> template<Reg16 IXY> II CPUCore<T>::ld_xix_byte() {
2764 unsigned tmp = RD_WORD_PC<1>(T::CC_DD + T::CC_LD_XIX_N_1);
2765 auto ofst = narrow_cast<int8_t>(tmp & 0xFF);
2766 auto val = narrow_cast<byte>(tmp >> 8);
2767 unsigned addr = narrow_cast<word>(get16<IXY>() + ofst);
2768 T::setMemPtr(addr);
2769 WRMEM(addr, val, T::CC_DD + T::CC_LD_XIX_N_2);
2770 return {3, T::CC_DD + T::CC_LD_XIX_N};
2771}
2772
2773// LD (nn),A
2774template<typename T> II CPUCore<T>::ld_xbyte_a() {
2775 unsigned x = RD_WORD_PC<1>(T::CC_LD_NN_A_1);
2776 T::setMemPtr((getA() << 8) | ((x + 1) & 0xFF));
2777 WRMEM(x, getA(), T::CC_LD_NN_A_2);
2778 return {3, T::CC_LD_NN_A};
2779}
2780
2781// LD (nn),ss
2782template<typename T> template<int EE> inline II CPUCore<T>::WR_NN_Y(word reg) {
2783 unsigned addr = RD_WORD_PC<1>(T::CC_LD_XX_HL_1 + EE);
2784 T::setMemPtr(addr + 1);
2785 WR_WORD(addr, reg, T::CC_LD_XX_HL_2 + EE);
2786 return {3, T::CC_LD_XX_HL + EE};
2787}
2788template<typename T> template<Reg16 REG, int EE> II CPUCore<T>::ld_xword_SS() {
2789 return WR_NN_Y<EE >(get16<REG>());
2790}
2791template<typename T> template<Reg16 REG> II CPUCore<T>::ld_xword_SS_ED() {
2792 return WR_NN_Y<T::EE_ED>(get16<REG>());
2793}
2794
2795// LD A,(ss)
2796template<typename T> template<Reg16 REG> II CPUCore<T>::ld_a_SS() {
2797 T::setMemPtr(get16<REG>() + 1);
2798 setA(RDMEM(get16<REG>(), T::CC_LD_A_SS_1));
2799 return {1, T::CC_LD_A_SS};
2800}
2801
2802// LD A,(nn)
2803template<typename T> II CPUCore<T>::ld_a_xbyte() {
2804 unsigned addr = RD_WORD_PC<1>(T::CC_LD_A_NN_1);
2805 T::setMemPtr(addr + 1);
2806 setA(RDMEM(addr, T::CC_LD_A_NN_2));
2807 return {3, T::CC_LD_A_NN};
2808}
2809
2810// LD r,n
2811template<typename T> template<Reg8 DST, int EE> II CPUCore<T>::ld_R_byte() {
2812 set8<DST>(RDMEM_OPCODE<1>(T::CC_LD_R_N_1 + EE)); return {2, T::CC_LD_R_N + EE};
2813}
2814
2815// LD r,(hl)
2816template<typename T> template<Reg8 DST> II CPUCore<T>::ld_R_xhl() {
2817 set8<DST>(RDMEM(getHL(), T::CC_LD_R_HL_1)); return {1, T::CC_LD_R_HL};
2818}
2819
2820// LD r,(IXY+e)
2821template<typename T> template<Reg8 DST, Reg16 IXY> II CPUCore<T>::ld_R_xix() {
2822 int8_t ofst = RDMEM_OPCODE<1>(T::CC_DD + T::CC_LD_R_XIX_1);
2823 unsigned addr = narrow_cast<word>(get16<IXY>() + ofst);
2824 T::setMemPtr(addr);
2825 set8<DST>(RDMEM(addr, T::CC_DD + T::CC_LD_R_XIX_2));
2826 return {2, T::CC_DD + T::CC_LD_R_XIX};
2827}
2828
2829// LD ss,(nn)
2830template<typename T> template<int EE> inline word CPUCore<T>::RD_P_XX() {
2831 unsigned addr = RD_WORD_PC<1>(T::CC_LD_HL_XX_1 + EE);
2832 T::setMemPtr(addr + 1);
2833 return RD_WORD(addr, T::CC_LD_HL_XX_2 + EE);
2834}
2835template<typename T> template<Reg16 REG, int EE> II CPUCore<T>::ld_SS_xword() {
2836 set16<REG>(RD_P_XX<EE>()); return {3, T::CC_LD_HL_XX + EE};
2837}
2838template<typename T> template<Reg16 REG> II CPUCore<T>::ld_SS_xword_ED() {
2839 set16<REG>(RD_P_XX<T::EE_ED>()); return {3, T::CC_LD_HL_XX + T::EE_ED};
2840}
2841
2842// LD ss,nn
2843template<typename T> template<Reg16 REG, int EE> II CPUCore<T>::ld_SS_word() {
2844 set16<REG>(RD_WORD_PC<1>(T::CC_LD_SS_NN_1 + EE)); return {3, T::CC_LD_SS_NN + EE};
2845}
2846
2847
2848// ADC A,r
2849template<typename T> inline void CPUCore<T>::ADC(byte reg) {
2850 unsigned res = getA() + reg + ((getF() & C_FLAG) ? 1 : 0);
2851 byte f = ((res & 0x100) ? C_FLAG : 0) |
2852 ((getA() ^ res ^ reg) & H_FLAG) |
2853 (((getA() ^ res) & (reg ^ res) & 0x80) >> 5) | // V_FLAG
2854 0; // N_FLAG
2855 if constexpr (T::IS_R800) {
2856 f |= table.ZS[res & 0xFF];
2857 f |= byte(getF() & (X_FLAG | Y_FLAG));
2858 } else {
2859 f |= table.ZSXY[res & 0xFF];
2860 }
2861 setF(f);
2862 setA(narrow_cast<byte>(res));
2863}
2864template<typename T> inline II CPUCore<T>::adc_a_a() {
2865 unsigned res = 2 * getA() + ((getF() & C_FLAG) ? 1 : 0);
2866 byte f = ((res & 0x100) ? C_FLAG : 0) |
2867 (res & H_FLAG) |
2868 (((getA() ^ res) & 0x80) >> 5) | // V_FLAG
2869 0; // N_FLAG
2870 if constexpr (T::IS_R800) {
2871 f |= table.ZS[res & 0xFF];
2872 f |= byte(getF() & (X_FLAG | Y_FLAG));
2873 } else {
2874 f |= table.ZSXY[res & 0xFF];
2875 }
2876 setF(f);
2877 setA(narrow_cast<byte>(res));
2878 return {1, T::CC_CP_R};
2879}
2880template<typename T> template<Reg8 SRC, int EE> II CPUCore<T>::adc_a_R() {
2881 ADC(get8<SRC>()); return {1, T::CC_CP_R + EE};
2882}
2883template<typename T> II CPUCore<T>::adc_a_byte() {
2884 ADC(RDMEM_OPCODE<1>(T::CC_CP_N_1)); return {2, T::CC_CP_N};
2885}
2886template<typename T> II CPUCore<T>::adc_a_xhl() {
2887 ADC(RDMEM(getHL(), T::CC_CP_XHL_1)); return {1, T::CC_CP_XHL};
2888}
2889template<typename T> template<Reg16 IXY> II CPUCore<T>::adc_a_xix() {
2890 int8_t ofst = RDMEM_OPCODE<1>(T::CC_DD + T::CC_CP_XIX_1);
2891 unsigned addr = narrow_cast<word>(get16<IXY>() + ofst);
2892 T::setMemPtr(addr);
2893 ADC(RDMEM(addr, T::CC_DD + T::CC_CP_XIX_2));
2894 return {2, T::CC_DD + T::CC_CP_XIX};
2895}
2896
2897// ADD A,r
2898template<typename T> inline void CPUCore<T>::ADD(byte reg) {
2899 unsigned res = getA() + reg;
2900 byte f = ((res & 0x100) ? C_FLAG : 0) |
2901 ((getA() ^ res ^ reg) & H_FLAG) |
2902 (((getA() ^ res) & (reg ^ res) & 0x80) >> 5) | // V_FLAG
2903 0; // N_FLAG
2904 if constexpr (T::IS_R800) {
2905 f |= table.ZS[res & 0xFF];
2906 f |= byte(getF() & (X_FLAG | Y_FLAG));
2907 } else {
2908 f |= table.ZSXY[res & 0xFF];
2909 }
2910 setF(f);
2911 setA(narrow_cast<byte>(res));
2912}
2913template<typename T> inline II CPUCore<T>::add_a_a() {
2914 unsigned res = 2 * getA();
2915 byte f = ((res & 0x100) ? C_FLAG : 0) |
2916 (res & H_FLAG) |
2917 (((getA() ^ res) & 0x80) >> 5) | // V_FLAG
2918 0; // N_FLAG
2919 if constexpr (T::IS_R800) {
2920 f |= table.ZS[res & 0xFF];
2921 f |= byte(getF() & (X_FLAG | Y_FLAG));
2922 } else {
2923 f |= table.ZSXY[res & 0xFF];
2924 }
2925 setF(f);
2926 setA(narrow_cast<byte>(res));
2927 return {1, T::CC_CP_R};
2928}
2929template<typename T> template<Reg8 SRC, int EE> II CPUCore<T>::add_a_R() {
2930 ADD(get8<SRC>()); return {1, T::CC_CP_R + EE};
2931}
2932template<typename T> II CPUCore<T>::add_a_byte() {
2933 ADD(RDMEM_OPCODE<1>(T::CC_CP_N_1)); return {2, T::CC_CP_N};
2934}
2935template<typename T> II CPUCore<T>::add_a_xhl() {
2936 ADD(RDMEM(getHL(), T::CC_CP_XHL_1)); return {1, T::CC_CP_XHL};
2937}
2938template<typename T> template<Reg16 IXY> II CPUCore<T>::add_a_xix() {
2939 int8_t ofst = RDMEM_OPCODE<1>(T::CC_DD + T::CC_CP_XIX_1);
2940 unsigned addr = narrow_cast<word>(get16<IXY>() + ofst);
2941 T::setMemPtr(addr);
2942 ADD(RDMEM(addr, T::CC_DD + T::CC_CP_XIX_2));
2943 return {2, T::CC_DD + T::CC_CP_XIX};
2944}
2945
2946// AND r
2947template<typename T> inline void CPUCore<T>::AND(byte reg) {
2948 setA(getA() & reg);
2949 byte f = 0;
2950 if constexpr (T::IS_R800) {
2951 f |= table.ZSPH[getA()];
2952 f |= byte(getF() & (X_FLAG | Y_FLAG));
2953 } else {
2954 f |= table.ZSPXY[getA()];
2955 f |= H_FLAG;
2956 }
2957 setF(f);
2958}
2959template<typename T> II CPUCore<T>::and_a() {
2960 byte f = 0;
2961 if constexpr (T::IS_R800) {
2962 f |= table.ZSPH[getA()];
2963 f |= byte(getF() & (X_FLAG | Y_FLAG));
2964 } else {
2965 f |= table.ZSPXY[getA()];
2966 f |= H_FLAG;
2967 }
2968 setF(f);
2969 return {1, T::CC_CP_R};
2970}
2971template<typename T> template<Reg8 SRC, int EE> II CPUCore<T>::and_R() {
2972 AND(get8<SRC>()); return {1, T::CC_CP_R + EE};
2973}
2974template<typename T> II CPUCore<T>::and_byte() {
2975 AND(RDMEM_OPCODE<1>(T::CC_CP_N_1)); return {2, T::CC_CP_N};
2976}
2977template<typename T> II CPUCore<T>::and_xhl() {
2978 AND(RDMEM(getHL(), T::CC_CP_XHL_1)); return {1, T::CC_CP_XHL};
2979}
2980template<typename T> template<Reg16 IXY> II CPUCore<T>::and_xix() {
2981 int8_t ofst = RDMEM_OPCODE<1>(T::CC_DD + T::CC_CP_XIX_1);
2982 unsigned addr = narrow_cast<word>(get16<IXY>() + ofst);
2983 T::setMemPtr(addr);
2984 AND(RDMEM(addr, T::CC_DD + T::CC_CP_XIX_2));
2985 return {2, T::CC_DD + T::CC_CP_XIX};
2986}
2987
2988// CP r
2989template<typename T> inline void CPUCore<T>::CP(byte reg) {
2990 unsigned q = getA() - reg;
2991 byte f = table.ZS[q & 0xFF] |
2992 ((q & 0x100) ? C_FLAG : 0) |
2993 N_FLAG |
2994 ((getA() ^ byte(q) ^ reg) & H_FLAG) |
2995 (((reg ^ getA()) & (getA() ^ byte(q)) & 0x80) >> 5); // V_FLAG
2996 if constexpr (T::IS_R800) {
2997 f |= byte(getF() & (X_FLAG | Y_FLAG));
2998 } else {
2999 f |= byte(reg & (X_FLAG | Y_FLAG)); // XY from operand, not from result
3000 }
3001 setF(f);
3002}
3003template<typename T> II CPUCore<T>::cp_a() {
3004 byte f = ZS0 | N_FLAG;
3005 if constexpr (T::IS_R800) {
3006 f |= byte(getF() & (X_FLAG | Y_FLAG));
3007 } else {
3008 f |= byte(getA() & (X_FLAG | Y_FLAG)); // XY from operand, not from result
3009 }
3010 setF(f);
3011 return {1, T::CC_CP_R};
3012}
3013template<typename T> template<Reg8 SRC, int EE> II CPUCore<T>::cp_R() {
3014 CP(get8<SRC>()); return {1, T::CC_CP_R + EE};
3015}
3016template<typename T> II CPUCore<T>::cp_byte() {
3017 CP(RDMEM_OPCODE<1>(T::CC_CP_N_1)); return {2, T::CC_CP_N};
3018}
3019template<typename T> II CPUCore<T>::cp_xhl() {
3020 CP(RDMEM(getHL(), T::CC_CP_XHL_1)); return {1, T::CC_CP_XHL};
3021}
3022template<typename T> template<Reg16 IXY> II CPUCore<T>::cp_xix() {
3023 int8_t ofst = RDMEM_OPCODE<1>(T::CC_DD + T::CC_CP_XIX_1);
3024 unsigned addr = narrow_cast<word>(get16<IXY>() + ofst);
3025 T::setMemPtr(addr);
3026 CP(RDMEM(addr, T::CC_DD + T::CC_CP_XIX_2));
3027 return {2, T::CC_DD + T::CC_CP_XIX};
3028}
3029
3030// OR r
3031template<typename T> inline void CPUCore<T>::OR(byte reg) {
3032 setA(getA() | reg);
3033 byte f = 0;
3034 if constexpr (T::IS_R800) {
3035 f |= table.ZSP[getA()];
3036 f |= byte(getF() & (X_FLAG | Y_FLAG));
3037 } else {
3038 f |= table.ZSPXY[getA()];
3039 }
3040 setF(f);
3041}
3042template<typename T> II CPUCore<T>::or_a() {
3043 byte f = 0;
3044 if constexpr (T::IS_R800) {
3045 f |= table.ZSP[getA()];
3046 f |= byte(getF() & (X_FLAG | Y_FLAG));
3047 } else {
3048 f |= table.ZSPXY[getA()];
3049 }
3050 setF(f);
3051 return {1, T::CC_CP_R};
3052}
3053template<typename T> template<Reg8 SRC, int EE> II CPUCore<T>::or_R() {
3054 OR(get8<SRC>()); return {1, T::CC_CP_R + EE};
3055}
3056template<typename T> II CPUCore<T>::or_byte() {
3057 OR(RDMEM_OPCODE<1>(T::CC_CP_N_1)); return {2, T::CC_CP_N};
3058}
3059template<typename T> II CPUCore<T>::or_xhl() {
3060 OR(RDMEM(getHL(), T::CC_CP_XHL_1)); return {1, T::CC_CP_XHL};
3061}
3062template<typename T> template<Reg16 IXY> II CPUCore<T>::or_xix() {
3063 int8_t ofst = RDMEM_OPCODE<1>(T::CC_DD + T::CC_CP_XIX_1);
3064 unsigned addr = narrow_cast<word>(get16<IXY>() + ofst);
3065 T::setMemPtr(addr);
3066 OR(RDMEM(addr, T::CC_DD + T::CC_CP_XIX_2));
3067 return {2, T::CC_DD + T::CC_CP_XIX};
3068}
3069
3070// SBC A,r
3071template<typename T> inline void CPUCore<T>::SBC(byte reg) {
3072 unsigned res = getA() - reg - ((getF() & C_FLAG) ? 1 : 0);
3073 byte f = ((res & 0x100) ? C_FLAG : 0) |
3074 N_FLAG |
3075 ((getA() ^ res ^ reg) & H_FLAG) |
3076 (((reg ^ getA()) & (getA() ^ res) & 0x80) >> 5); // V_FLAG
3077 if constexpr (T::IS_R800) {
3078 f |= table.ZS[res & 0xFF];
3079 f |= byte(getF() & (X_FLAG | Y_FLAG));
3080 } else {
3081 f |= table.ZSXY[res & 0xFF];
3082 }
3083 setF(f);
3084 setA(narrow_cast<byte>(res));
3085}
3086template<typename T> II CPUCore<T>::sbc_a_a() {
3087 if constexpr (T::IS_R800) {
3088 word t = (getF() & C_FLAG)
3089 ? (255 * 256 | ZS255 | C_FLAG | H_FLAG | N_FLAG)
3090 : ( 0 * 256 | ZS0 | N_FLAG);
3091 setAF(t | (getF() & (X_FLAG | Y_FLAG)));
3092 } else {
3093 setAF((getF() & C_FLAG) ?
3094 (255 * 256 | ZSXY255 | C_FLAG | H_FLAG | N_FLAG) :
3095 ( 0 * 256 | ZSXY0 | N_FLAG));
3096 }
3097 return {1, T::CC_CP_R};
3098}
3099template<typename T> template<Reg8 SRC, int EE> II CPUCore<T>::sbc_a_R() {
3100 SBC(get8<SRC>()); return {1, T::CC_CP_R + EE};
3101}
3102template<typename T> II CPUCore<T>::sbc_a_byte() {
3103 SBC(RDMEM_OPCODE<1>(T::CC_CP_N_1)); return {2, T::CC_CP_N};
3104}
3105template<typename T> II CPUCore<T>::sbc_a_xhl() {
3106 SBC(RDMEM(getHL(), T::CC_CP_XHL_1)); return {1, T::CC_CP_XHL};
3107}
3108template<typename T> template<Reg16 IXY> II CPUCore<T>::sbc_a_xix() {
3109 int8_t ofst = RDMEM_OPCODE<1>(T::CC_DD + T::CC_CP_XIX_1);
3110 unsigned addr = narrow_cast<word>(get16<IXY>() + ofst);
3111 T::setMemPtr(addr);
3112 SBC(RDMEM(addr, T::CC_DD + T::CC_CP_XIX_2));
3113 return {2, T::CC_DD + T::CC_CP_XIX};
3114}
3115
3116// SUB r
3117template<typename T> inline void CPUCore<T>::SUB(byte reg) {
3118 unsigned res = getA() - reg;
3119 byte f = ((res & 0x100) ? C_FLAG : 0) |
3120 N_FLAG |
3121 ((getA() ^ res ^ reg) & H_FLAG) |
3122 (((reg ^ getA()) & (getA() ^ res) & 0x80) >> 5); // V_FLAG
3123 if constexpr (T::IS_R800) {
3124 f |= table.ZS[res & 0xFF];
3125 f |= byte(getF() & (X_FLAG | Y_FLAG));
3126 } else {
3127 f |= table.ZSXY[res & 0xFF];
3128 }
3129 setF(f);
3130 setA(narrow_cast<byte>(res));
3131}
3132template<typename T> II CPUCore<T>::sub_a() {
3133 if constexpr (T::IS_R800) {
3134 word t = 0 * 256 | ZS0 | N_FLAG;
3135 setAF(t | (getF() & (X_FLAG | Y_FLAG)));
3136 } else {
3137 setAF(0 * 256 | ZSXY0 | N_FLAG);
3138 }
3139 return {1, T::CC_CP_R};
3140}
3141template<typename T> template<Reg8 SRC, int EE> II CPUCore<T>::sub_R() {
3142 SUB(get8<SRC>()); return {1, T::CC_CP_R + EE};
3143}
3144template<typename T> II CPUCore<T>::sub_byte() {
3145 SUB(RDMEM_OPCODE<1>(T::CC_CP_N_1)); return {2, T::CC_CP_N};
3146}
3147template<typename T> II CPUCore<T>::sub_xhl() {
3148 SUB(RDMEM(getHL(), T::CC_CP_XHL_1)); return {1, T::CC_CP_XHL};
3149}
3150template<typename T> template<Reg16 IXY> II CPUCore<T>::sub_xix() {
3151 int8_t ofst = RDMEM_OPCODE<1>(T::CC_DD + T::CC_CP_XIX_1);
3152 unsigned addr = narrow_cast<word>(get16<IXY>() + ofst);
3153 T::setMemPtr(addr);
3154 SUB(RDMEM(addr, T::CC_DD + T::CC_CP_XIX_2));
3155 return {2, T::CC_DD + T::CC_CP_XIX};
3156}
3157
3158// XOR r
3159template<typename T> inline void CPUCore<T>::XOR(byte reg) {
3160 setA(getA() ^ reg);
3161 byte f = 0;
3162 if constexpr (T::IS_R800) {
3163 f |= table.ZSP[getA()];
3164 f |= byte(getF() & (X_FLAG | Y_FLAG));
3165 } else {
3166 f |= table.ZSPXY[getA()];
3167 }
3168 setF(f);
3169}
3170template<typename T> II CPUCore<T>::xor_a() {
3171 if constexpr (T::IS_R800) {
3172 word t = 0 * 256 + ZSP0;
3173 setAF(t | (getF() & (X_FLAG | Y_FLAG)));
3174 } else {
3175 setAF(0 * 256 + ZSPXY0);
3176 }
3177 return {1, T::CC_CP_R};
3178}
3179template<typename T> template<Reg8 SRC, int EE> II CPUCore<T>::xor_R() {
3180 XOR(get8<SRC>()); return {1, T::CC_CP_R + EE};
3181}
3182template<typename T> II CPUCore<T>::xor_byte() {
3183 XOR(RDMEM_OPCODE<1>(T::CC_CP_N_1)); return {2, T::CC_CP_N};
3184}
3185template<typename T> II CPUCore<T>::xor_xhl() {
3186 XOR(RDMEM(getHL(), T::CC_CP_XHL_1)); return {1, T::CC_CP_XHL};
3187}
3188template<typename T> template<Reg16 IXY> II CPUCore<T>::xor_xix() {
3189 int8_t ofst = RDMEM_OPCODE<1>(T::CC_DD + T::CC_CP_XIX_1);
3190 unsigned addr = narrow_cast<word>(get16<IXY>() + ofst);
3191 T::setMemPtr(addr);
3192 XOR(RDMEM(addr, T::CC_DD + T::CC_CP_XIX_2));
3193 return {2, T::CC_DD + T::CC_CP_XIX};
3194}
3195
3196
3197// DEC r
3198template<typename T> inline byte CPUCore<T>::DEC(byte reg) {
3199 byte res = reg - 1;
3200 byte f = ((reg & ~res & 0x80) >> 5) | // V_FLAG
3201 (((res & 0x0F) + 1) & H_FLAG) |
3202 N_FLAG;
3203 if constexpr (T::IS_R800) {
3204 f |= byte(getF() & (C_FLAG | X_FLAG | Y_FLAG));
3205 f |= table.ZS[res];
3206 } else {
3207 f |= byte(getF() & C_FLAG);
3208 f |= table.ZSXY[res];
3209 }
3210 setF(f);
3211 return res;
3212}
3213template<typename T> template<Reg8 REG, int EE> II CPUCore<T>::dec_R() {
3214 set8<REG>(DEC(get8<REG>())); return {1, T::CC_INC_R + EE};
3215}
3216template<typename T> template<int EE> inline void CPUCore<T>::DEC_X(unsigned x) {
3217 byte val = DEC(RDMEM(x, T::CC_INC_XHL_1 + EE));
3218 WRMEM(x, val, T::CC_INC_XHL_2 + EE);
3219}
3220template<typename T> II CPUCore<T>::dec_xhl() {
3221 DEC_X<0>(getHL());
3222 return {1, T::CC_INC_XHL};
3223}
3224template<typename T> template<Reg16 IXY> II CPUCore<T>::dec_xix() {
3225 int8_t ofst = RDMEM_OPCODE<1>(T::CC_DD + T::CC_INC_XIX_1);
3226 unsigned addr = narrow_cast<word>(get16<IXY>() + ofst);
3227 T::setMemPtr(addr);
3228 DEC_X<T::CC_DD + T::EE_INC_XIX>(addr);
3229 return {2, T::CC_INC_XHL + T::CC_DD + T::EE_INC_XIX};
3230}
3231
3232// INC r
3233template<typename T> inline byte CPUCore<T>::INC(byte reg) {
3234 reg++;
3235 byte f = ((reg & -reg & 0x80) >> 5) | // V_FLAG
3236 (((reg & 0x0F) - 1) & H_FLAG) |
3237 0; // N_FLAG
3238 if constexpr (T::IS_R800) {
3239 f |= byte(getF() & (C_FLAG | X_FLAG | Y_FLAG));
3240 f |= table.ZS[reg];
3241 } else {
3242 f |= byte(getF() & C_FLAG);
3243 f |= table.ZSXY[reg];
3244 }
3245 setF(f);
3246 return reg;
3247}
3248template<typename T> template<Reg8 REG, int EE> II CPUCore<T>::inc_R() {
3249 set8<REG>(INC(get8<REG>())); return {1, T::CC_INC_R + EE};
3250}
3251template<typename T> template<int EE> inline void CPUCore<T>::INC_X(unsigned x) {
3252 byte val = INC(RDMEM(x, T::CC_INC_XHL_1 + EE));
3253 WRMEM(x, val, T::CC_INC_XHL_2 + EE);
3254}
3255template<typename T> II CPUCore<T>::inc_xhl() {
3256 INC_X<0>(getHL());
3257 return {1, T::CC_INC_XHL};
3258}
3259template<typename T> template<Reg16 IXY> II CPUCore<T>::inc_xix() {
3260 int8_t ofst = RDMEM_OPCODE<1>(T::CC_DD + T::CC_INC_XIX_1);
3261 unsigned addr = narrow_cast<word>(get16<IXY>() + ofst);
3262 T::setMemPtr(addr);
3263 INC_X<T::CC_DD + T::EE_INC_XIX>(addr);
3264 return {2, T::CC_INC_XHL + T::CC_DD + T::EE_INC_XIX};
3265}
3266
3267
3268// ADC HL,ss
3269template<typename T> template<Reg16 REG> inline II CPUCore<T>::adc_hl_SS() {
3270 unsigned reg = get16<REG>();
3271 T::setMemPtr(getHL() + 1);
3272 unsigned res = getHL() + reg + ((getF() & C_FLAG) ? 1 : 0);
3273 byte f = byte(res >> 16) | // C_FLAG
3274 0; // N_FLAG
3275 if constexpr (T::IS_R800) {
3276 f |= byte(getF() & (X_FLAG | Y_FLAG));
3277 }
3278 if (res & 0xFFFF) {
3279 f |= byte(((getHL() ^ res ^ reg) >> 8) & H_FLAG);
3280 f |= 0; // Z_FLAG
3281 f |= byte(((getHL() ^ res) & (reg ^ res) & 0x8000) >> 13); // V_FLAG
3282 if constexpr (T::IS_R800) {
3283 f |= (res >> 8) & S_FLAG;
3284 } else {
3285 f |= (res >> 8) & (S_FLAG | X_FLAG | Y_FLAG);
3286 }
3287 } else {
3288 f |= byte(((getHL() ^ reg) >> 8) & H_FLAG);
3289 f |= Z_FLAG;
3290 f |= byte((getHL() & reg & 0x8000) >> 13); // V_FLAG
3291 f |= 0; // S_FLAG (X_FLAG Y_FLAG)
3292 }
3293 setF(f);
3294 setHL(narrow_cast<word>(res));
3295 return {1, T::CC_ADC_HL_SS};
3296}
3297template<typename T> II CPUCore<T>::adc_hl_hl() {
3298 T::setMemPtr(getHL() + 1);
3299 unsigned res = 2 * getHL() + ((getF() & C_FLAG) ? 1 : 0);
3300 byte f = byte(res >> 16) | // C_FLAG
3301 0; // N_FLAG
3302 if constexpr (T::IS_R800) {
3303 f |= byte(getF() & (X_FLAG | Y_FLAG));
3304 }
3305 if (res & 0xFFFF) {
3306 f |= 0; // Z_FLAG
3307 f |= byte(((getHL() ^ res) & 0x8000) >> 13); // V_FLAG
3308 if constexpr (T::IS_R800) {
3309 f |= byte((res >> 8) & (H_FLAG | S_FLAG));
3310 } else {
3311 f |= byte((res >> 8) & (H_FLAG | S_FLAG | X_FLAG | Y_FLAG));
3312 }
3313 } else {
3314 f |= Z_FLAG;
3315 f |= byte((getHL() & 0x8000) >> 13); // V_FLAG
3316 f |= 0; // H_FLAG S_FLAG (X_FLAG Y_FLAG)
3317 }
3318 setF(f);
3319 setHL(narrow_cast<word>(res));
3320 return {1, T::CC_ADC_HL_SS};
3321}
3322
3323// ADD HL/IX/IY,ss
3324template<typename T> template<Reg16 REG1, Reg16 REG2, int EE> II CPUCore<T>::add_SS_TT() {
3325 unsigned reg1 = get16<REG1>();
3326 unsigned reg2 = get16<REG2>();
3327 T::setMemPtr(reg1 + 1);
3328 unsigned res = reg1 + reg2;
3329 byte f = byte(((reg1 ^ res ^ reg2) >> 8) & H_FLAG) |
3330 byte(res >> 16) | // C_FLAG
3331 0; // N_FLAG
3332 if constexpr (T::IS_R800) {
3333 f |= byte(getF() & (S_FLAG | Z_FLAG | V_FLAG | X_FLAG | Y_FLAG));
3334 } else {
3335 f |= byte(getF() & (S_FLAG | Z_FLAG | V_FLAG));
3336 f |= byte((res >> 8) & (X_FLAG | Y_FLAG));
3337 }
3338 setF(f);
3339 set16<REG1>(narrow_cast<word>(res));
3340 return {1, T::CC_ADD_HL_SS + EE};
3341}
3342template<typename T> template<Reg16 REG, int EE> II CPUCore<T>::add_SS_SS() {
3343 unsigned reg = get16<REG>();
3344 T::setMemPtr(reg + 1);
3345 unsigned res = 2 * reg;
3346 byte f = byte(res >> 16) | // C_FLAG
3347 0; // N_FLAG
3348 if constexpr (T::IS_R800) {
3349 f |= byte(getF() & (S_FLAG | Z_FLAG | V_FLAG | X_FLAG | Y_FLAG));
3350 f |= byte((res >> 8) & H_FLAG);
3351 } else {
3352 f |= byte(getF() & (S_FLAG | Z_FLAG | V_FLAG));
3353 f |= byte((res >> 8) & (H_FLAG | X_FLAG | Y_FLAG));
3354 }
3355 setF(f);
3356 set16<REG>(narrow_cast<word>(res));
3357 return {1, T::CC_ADD_HL_SS + EE};
3358}
3359
3360// SBC HL,ss
3361template<typename T> template<Reg16 REG> inline II CPUCore<T>::sbc_hl_SS() {
3362 unsigned reg = get16<REG>();
3363 T::setMemPtr(getHL() + 1);
3364 unsigned res = getHL() - reg - ((getF() & C_FLAG) ? 1 : 0);
3365 byte f = ((res & 0x10000) ? C_FLAG : 0) |
3366 N_FLAG;
3367 if constexpr (T::IS_R800) {
3368 f |= byte(getF() & (X_FLAG | Y_FLAG));
3369 }
3370 if (res & 0xFFFF) {
3371 f |= byte(((getHL() ^ res ^ reg) >> 8) & H_FLAG);
3372 f |= 0; // Z_FLAG
3373 f |= byte(((reg ^ getHL()) & (getHL() ^ res) & 0x8000) >> 13); // V_FLAG
3374 if constexpr (T::IS_R800) {
3375 f |= byte((res >> 8) & S_FLAG);
3376 } else {
3377 f |= byte((res >> 8) & (S_FLAG | X_FLAG | Y_FLAG));
3378 }
3379 } else {
3380 f |= byte(((getHL() ^ reg) >> 8) & H_FLAG);
3381 f |= Z_FLAG;
3382 f |= byte(((reg ^ getHL()) & getHL() & 0x8000) >> 13); // V_FLAG
3383 f |= 0; // S_FLAG (X_FLAG Y_FLAG)
3384 }
3385 setF(f);
3386 setHL(narrow_cast<word>(res));
3387 return {1, T::CC_ADC_HL_SS};
3388}
3389template<typename T> II CPUCore<T>::sbc_hl_hl() {
3390 T::setMemPtr(getHL() + 1);
3391 byte f = T::IS_R800 ? (getF() & (X_FLAG | Y_FLAG)) : 0;
3392 if (getF() & C_FLAG) {
3393 f |= C_FLAG | H_FLAG | S_FLAG | N_FLAG;
3394 if constexpr (!T::IS_R800) {
3395 f |= X_FLAG | Y_FLAG;
3396 }
3397 setHL(0xFFFF);
3398 } else {
3399 f |= Z_FLAG | N_FLAG;
3400 setHL(0);
3401 }
3402 setF(f);
3403 return {1, T::CC_ADC_HL_SS};
3404}
3405
3406// DEC ss
3407template<typename T> template<Reg16 REG, int EE> II CPUCore<T>::dec_SS() {
3408 set16<REG>(narrow_cast<word>(get16<REG>() - 1)); return {1, T::CC_INC_SS + EE};
3409}
3410
3411// INC ss
3412template<typename T> template<Reg16 REG, int EE> II CPUCore<T>::inc_SS() {
3413 set16<REG>(narrow_cast<word>(get16<REG>() + 1)); return {1, T::CC_INC_SS + EE};
3414}
3415
3416
3417// BIT n,r
3418template<typename T> template<unsigned N, Reg8 REG> II CPUCore<T>::bit_N_R() {
3419 byte reg = get8<REG>();
3420 byte f = 0; // N_FLAG
3421 if constexpr (T::IS_R800) {
3422 // this is very different from Z80 (not only XY flags)
3423 f |= byte(getF() & (S_FLAG | V_FLAG | C_FLAG | X_FLAG | Y_FLAG));
3424 f |= H_FLAG;
3425 f |= (reg & (1 << N)) ? 0 : Z_FLAG;
3426 } else {
3427 f |= table.ZSPH[reg & (1 << N)];
3428 f |= byte(getF() & C_FLAG);
3429 f |= byte(reg & (X_FLAG | Y_FLAG));
3430 }
3431 setF(f);
3432 return {1, T::CC_BIT_R};
3433}
3434template<typename T> template<unsigned N> inline II CPUCore<T>::bit_N_xhl() {
3435 byte m = RDMEM(getHL(), T::CC_BIT_XHL_1) & (1 << N);
3436 byte f = 0; // N_FLAG
3437 if constexpr (T::IS_R800) {
3438 f |= byte(getF() & (S_FLAG | V_FLAG | C_FLAG | X_FLAG | Y_FLAG));
3439 f |= H_FLAG;
3440 f |= m ? 0 : Z_FLAG;
3441 } else {
3442 f |= table.ZSPH[m];
3443 f |= byte(getF() & C_FLAG);
3444 f |= byte((T::getMemPtr() >> 8) & (X_FLAG | Y_FLAG));
3445 }
3446 setF(f);
3447 return {1, T::CC_BIT_XHL};
3448}
3449template<typename T> template<unsigned N> inline II CPUCore<T>::bit_N_xix(unsigned addr) {
3450 T::setMemPtr(addr);
3451 byte m = RDMEM(addr, T::CC_DD + T::CC_BIT_XIX_1) & (1 << N);
3452 byte f = 0; // N_FLAG
3453 if constexpr (T::IS_R800) {
3454 f |= byte(getF() & (S_FLAG | V_FLAG | C_FLAG | X_FLAG | Y_FLAG));
3455 f |= H_FLAG;
3456 f |= m ? 0 : Z_FLAG;
3457 } else {
3458 f |= table.ZSPH[m];
3459 f |= byte(getF() & C_FLAG);
3460 f |= byte((addr >> 8) & (X_FLAG | Y_FLAG));
3461 }
3462 setF(f);
3463 return {3, T::CC_DD + T::CC_BIT_XIX};
3464}
3465
3466// RES n,r
3467static constexpr byte RES(unsigned b, byte reg) {
3468 return reg & byte(~(1 << b));
3469}
3470template<typename T> template<unsigned N, Reg8 REG> II CPUCore<T>::res_N_R() {
3471 set8<REG>(RES(N, get8<REG>())); return {1, T::CC_SET_R};
3472}
3473template<typename T> template<int EE> inline byte CPUCore<T>::RES_X(unsigned bit, unsigned addr) {
3474 byte res = RES(bit, RDMEM(addr, T::CC_SET_XHL_1 + EE));
3475 WRMEM(addr, res, T::CC_SET_XHL_2 + EE);
3476 return res;
3477}
3478template<typename T> template<unsigned N> II CPUCore<T>::res_N_xhl() {
3479 RES_X<0>(N, getHL()); return {1, T::CC_SET_XHL};
3480}
3481template<typename T> template<unsigned N, Reg8 REG> II CPUCore<T>::res_N_xix_R(unsigned a) {
3482 T::setMemPtr(a);
3483 set8<REG>(RES_X<T::CC_DD + T::EE_SET_XIX>(N, a));
3484 return {3, T::CC_DD + T::CC_SET_XIX};
3485}
3486
3487// SET n,r
3488static constexpr byte SET(unsigned b, byte reg) {
3489 return reg | byte(1 << b);
3490}
3491template<typename T> template<unsigned N, Reg8 REG> II CPUCore<T>::set_N_R() {
3492 set8<REG>(SET(N, get8<REG>())); return {1, T::CC_SET_R};
3493}
3494template<typename T> template<int EE> inline byte CPUCore<T>::SET_X(unsigned bit, unsigned addr) {
3495 byte res = SET(bit, RDMEM(addr, T::CC_SET_XHL_1 + EE));
3496 WRMEM(addr, res, T::CC_SET_XHL_2 + EE);
3497 return res;
3498}
3499template<typename T> template<unsigned N> II CPUCore<T>::set_N_xhl() {
3500 SET_X<0>(N, getHL()); return {1, T::CC_SET_XHL};
3501}
3502template<typename T> template<unsigned N, Reg8 REG> II CPUCore<T>::set_N_xix_R(unsigned a) {
3503 T::setMemPtr(a);
3504 set8<REG>(SET_X<T::CC_DD + T::EE_SET_XIX>(N, a));
3505 return {3, T::CC_DD + T::CC_SET_XIX};
3506}
3507
3508// RL r
3509template<typename T> inline byte CPUCore<T>::RL(byte reg) {
3510 byte c = reg >> 7;
3511 reg = narrow_cast<byte>((reg << 1) | ((getF() & C_FLAG) ? 0x01 : 0));
3512 byte f = c ? C_FLAG : 0;
3513 if constexpr (T::IS_R800) {
3514 f |= table.ZSP[reg];
3515 f |= byte(getF() & (X_FLAG | Y_FLAG));
3516 } else {
3517 f |= table.ZSPXY[reg];
3518 }
3519 setF(f);
3520 return reg;
3521}
3522template<typename T> template<int EE> inline byte CPUCore<T>::RL_X(unsigned x) {
3523 byte res = RL(RDMEM(x, T::CC_SET_XHL_1 + EE));
3524 WRMEM(x, res, T::CC_SET_XHL_2 + EE);
3525 return res;
3526}
3527template<typename T> template<Reg8 REG> II CPUCore<T>::rl_R() {
3528 set8<REG>(RL(get8<REG>())); return {1, T::CC_SET_R};
3529}
3530template<typename T> II CPUCore<T>::rl_xhl() {
3531 RL_X<0>(getHL()); return {1, T::CC_SET_XHL};
3532}
3533template<typename T> template<Reg8 REG> II CPUCore<T>::rl_xix_R(unsigned a) {
3534 T::setMemPtr(a);
3535 set8<REG>(RL_X<T::CC_DD + T::EE_SET_XIX>(a));
3536 return {3, T::CC_DD + T::CC_SET_XIX};
3537}
3538
3539// RLC r
3540template<typename T> inline byte CPUCore<T>::RLC(byte reg) {
3541 byte c = reg >> 7;
3542 reg = narrow_cast<byte>((reg << 1) | c);
3543 byte f = c ? C_FLAG : 0;
3544 if constexpr (T::IS_R800) {
3545 f |= table.ZSP[reg];
3546 f |= byte(getF() & (X_FLAG | Y_FLAG));
3547 } else {
3548 f |= table.ZSPXY[reg];
3549 }
3550 setF(f);
3551 return reg;
3552}
3553template<typename T> template<int EE> inline byte CPUCore<T>::RLC_X(unsigned x) {
3554 byte res = RLC(RDMEM(x, T::CC_SET_XHL_1 + EE));
3555 WRMEM(x, res, T::CC_SET_XHL_2 + EE);
3556 return res;
3557}
3558template<typename T> template<Reg8 REG> II CPUCore<T>::rlc_R() {
3559 set8<REG>(RLC(get8<REG>())); return {1, T::CC_SET_R};
3560}
3561template<typename T> II CPUCore<T>::rlc_xhl() {
3562 RLC_X<0>(getHL()); return {1, T::CC_SET_XHL};
3563}
3564template<typename T> template<Reg8 REG> II CPUCore<T>::rlc_xix_R(unsigned a) {
3565 T::setMemPtr(a);
3566 set8<REG>(RLC_X<T::CC_DD + T::EE_SET_XIX>(a));
3567 return {3, T::CC_DD + T::CC_SET_XIX};
3568}
3569
3570// RR r
3571template<typename T> inline byte CPUCore<T>::RR(byte reg) {
3572 byte c = reg & 1;
3573 reg = narrow_cast<byte>((reg >> 1) | ((getF() & C_FLAG) << 7));
3574 byte f = c ? C_FLAG : 0;
3575 if constexpr (T::IS_R800) {
3576 f |= table.ZSP[reg];
3577 f |= byte(getF() & (X_FLAG | Y_FLAG));
3578 } else {
3579 f |= table.ZSPXY[reg];
3580 }
3581 setF(f);
3582 return reg;
3583}
3584template<typename T> template<int EE> inline byte CPUCore<T>::RR_X(unsigned x) {
3585 byte res = RR(RDMEM(x, T::CC_SET_XHL_1 + EE));
3586 WRMEM(x, res, T::CC_SET_XHL_2 + EE);
3587 return res;
3588}
3589template<typename T> template<Reg8 REG> II CPUCore<T>::rr_R() {
3590 set8<REG>(RR(get8<REG>())); return {1, T::CC_SET_R};
3591}
3592template<typename T> II CPUCore<T>::rr_xhl() {
3593 RR_X<0>(getHL()); return {1, T::CC_SET_XHL};
3594}
3595template<typename T> template<Reg8 REG> II CPUCore<T>::rr_xix_R(unsigned a) {
3596 T::setMemPtr(a);
3597 set8<REG>(RR_X<T::CC_DD + T::EE_SET_XIX>(a));
3598 return {3, T::CC_DD + T::CC_SET_XIX};
3599}
3600
3601// RRC r
3602template<typename T> inline byte CPUCore<T>::RRC(byte reg) {
3603 byte c = reg & 1;
3604 reg = narrow_cast<byte>((reg >> 1) | (c << 7));
3605 byte f = c ? C_FLAG : 0;
3606 if constexpr (T::IS_R800) {
3607 f |= table.ZSP[reg];
3608 f |= byte(getF() & (X_FLAG | Y_FLAG));
3609 } else {
3610 f |= table.ZSPXY[reg];
3611 }
3612 setF(f);
3613 return reg;
3614}
3615template<typename T> template<int EE> inline byte CPUCore<T>::RRC_X(unsigned x) {
3616 byte res = RRC(RDMEM(x, T::CC_SET_XHL_1 + EE));
3617 WRMEM(x, res, T::CC_SET_XHL_2 + EE);
3618 return res;
3619}
3620template<typename T> template<Reg8 REG> II CPUCore<T>::rrc_R() {
3621 set8<REG>(RRC(get8<REG>())); return {1, T::CC_SET_R};
3622}
3623template<typename T> II CPUCore<T>::rrc_xhl() {
3624 RRC_X<0>(getHL()); return {1, T::CC_SET_XHL};
3625}
3626template<typename T> template<Reg8 REG> II CPUCore<T>::rrc_xix_R(unsigned a) {
3627 T::setMemPtr(a);
3628 set8<REG>(RRC_X<T::CC_DD + T::EE_SET_XIX>(a));
3629 return {3, T::CC_DD + T::CC_SET_XIX};
3630}
3631
3632// SLA r
3633template<typename T> inline byte CPUCore<T>::SLA(byte reg) {
3634 byte c = reg >> 7;
3635 reg <<= 1;
3636 byte f = c ? C_FLAG : 0;
3637 if constexpr (T::IS_R800) {
3638 f |= table.ZSP[reg];
3639 f |= byte(getF() & (X_FLAG | Y_FLAG));
3640 } else {
3641 f |= table.ZSPXY[reg];
3642 }
3643 setF(f);
3644 return reg;
3645}
3646template<typename T> template<int EE> inline byte CPUCore<T>::SLA_X(unsigned x) {
3647 byte res = SLA(RDMEM(x, T::CC_SET_XHL_1 + EE));
3648 WRMEM(x, res, T::CC_SET_XHL_2 + EE);
3649 return res;
3650}
3651template<typename T> template<Reg8 REG> II CPUCore<T>::sla_R() {
3652 set8<REG>(SLA(get8<REG>())); return {1, T::CC_SET_R};
3653}
3654template<typename T> II CPUCore<T>::sla_xhl() {
3655 SLA_X<0>(getHL()); return {1, T::CC_SET_XHL};
3656}
3657template<typename T> template<Reg8 REG> II CPUCore<T>::sla_xix_R(unsigned a) {
3658 T::setMemPtr(a);
3659 set8<REG>(SLA_X<T::CC_DD + T::EE_SET_XIX>(a));
3660 return {3, T::CC_DD + T::CC_SET_XIX};
3661}
3662
3663// SLL r
3664template<typename T> inline byte CPUCore<T>::SLL(byte reg) {
3665 assert(!T::IS_R800); // this instruction is Z80-only
3666 byte c = reg >> 7;
3667 reg = narrow_cast<byte>((reg << 1) | 1);
3668 byte f = c ? C_FLAG : 0;
3669 f |= table.ZSPXY[reg];
3670 setF(f);
3671 return reg;
3672}
3673template<typename T> template<int EE> inline byte CPUCore<T>::SLL_X(unsigned x) {
3674 byte res = SLL(RDMEM(x, T::CC_SET_XHL_1 + EE));
3675 WRMEM(x, res, T::CC_SET_XHL_2 + EE);
3676 return res;
3677}
3678template<typename T> template<Reg8 REG> II CPUCore<T>::sll_R() {
3679 set8<REG>(SLL(get8<REG>())); return {1, T::CC_SET_R};
3680}
3681template<typename T> II CPUCore<T>::sll_xhl() {
3682 SLL_X<0>(getHL()); return {1, T::CC_SET_XHL};
3683}
3684template<typename T> template<Reg8 REG> II CPUCore<T>::sll_xix_R(unsigned a) {
3685 T::setMemPtr(a);
3686 set8<REG>(SLL_X<T::CC_DD + T::EE_SET_XIX>(a));
3687 return {3, T::CC_DD + T::CC_SET_XIX};
3688}
3689template<typename T> II CPUCore<T>::sll2() {
3690 assert(T::IS_R800); // this instruction is R800-only
3691 byte f = (getF() & (X_FLAG | Y_FLAG)) |
3692 (getA() >> 7) | // C_FLAG
3693 0; // all other flags zero
3694 setF(f);
3695 return {3, T::CC_DD + T::CC_SET_XIX}; // TODO
3696}
3697
3698// SRA r
3699template<typename T> inline byte CPUCore<T>::SRA(byte reg) {
3700 byte c = reg & 1;
3701 reg = (reg >> 1) | (reg & 0x80);
3702 byte f = c ? C_FLAG : 0;
3703 if constexpr (T::IS_R800) {
3704 f |= table.ZSP[reg];
3705 f |= byte(getF() & (X_FLAG | Y_FLAG));
3706 } else {
3707 f |= table.ZSPXY[reg];
3708 }
3709 setF(f);
3710 return reg;
3711}
3712template<typename T> template<int EE> inline byte CPUCore<T>::SRA_X(unsigned x) {
3713 byte res = SRA(RDMEM(x, T::CC_SET_XHL_1 + EE));
3714 WRMEM(x, res, T::CC_SET_XHL_2 + EE);
3715 return res;
3716}
3717template<typename T> template<Reg8 REG> II CPUCore<T>::sra_R() {
3718 set8<REG>(SRA(get8<REG>())); return {1, T::CC_SET_R};
3719}
3720template<typename T> II CPUCore<T>::sra_xhl() {
3721 SRA_X<0>(getHL()); return {1, T::CC_SET_XHL};
3722}
3723template<typename T> template<Reg8 REG> II CPUCore<T>::sra_xix_R(unsigned a) {
3724 T::setMemPtr(a);
3725 set8<REG>(SRA_X<T::CC_DD + T::EE_SET_XIX>(a));
3726 return {3, T::CC_DD + T::CC_SET_XIX};
3727}
3728
3729// SRL R
3730template<typename T> inline byte CPUCore<T>::SRL(byte reg) {
3731 byte c = reg & 1;
3732 reg >>= 1;
3733 byte f = c ? C_FLAG : 0;
3734 if constexpr (T::IS_R800) {
3735 f |= table.ZSP[reg];
3736 f |= byte(getF() & (X_FLAG | Y_FLAG));
3737 } else {
3738 f |= table.ZSPXY[reg];
3739 }
3740 setF(f);
3741 return reg;
3742}
3743template<typename T> template<int EE> inline byte CPUCore<T>::SRL_X(unsigned x) {
3744 byte res = SRL(RDMEM(x, T::CC_SET_XHL_1 + EE));
3745 WRMEM(x, res, T::CC_SET_XHL_2 + EE);
3746 return res;
3747}
3748template<typename T> template<Reg8 REG> II CPUCore<T>::srl_R() {
3749 set8<REG>(SRL(get8<REG>())); return {1, T::CC_SET_R};
3750}
3751template<typename T> II CPUCore<T>::srl_xhl() {
3752 SRL_X<0>(getHL()); return {1, T::CC_SET_XHL};
3753}
3754template<typename T> template<Reg8 REG> II CPUCore<T>::srl_xix_R(unsigned a) {
3755 T::setMemPtr(a);
3756 set8<REG>(SRL_X<T::CC_DD + T::EE_SET_XIX>(a));
3757 return {3, T::CC_DD + T::CC_SET_XIX};
3758}
3759
3760// RLA RLCA RRA RRCA
3761template<typename T> II CPUCore<T>::rla() {
3762 byte c = getF() & C_FLAG;
3763 byte f = (getA() & 0x80) ? C_FLAG : 0;
3764 if constexpr (T::IS_R800) {
3765 f |= byte(getF() & (S_FLAG | Z_FLAG | P_FLAG | X_FLAG | Y_FLAG));
3766 } else {
3767 f |= byte(getF() & (S_FLAG | Z_FLAG | P_FLAG));
3768 }
3769 setA(narrow_cast<byte>((getA() << 1) | (c ? 1 : 0)));
3770 if constexpr (!T::IS_R800) {
3771 f |= byte(getA() & (X_FLAG | Y_FLAG));
3772 }
3773 setF(f);
3774 return {1, T::CC_RLA};
3775}
3776template<typename T> II CPUCore<T>::rlca() {
3777 setA(narrow_cast<byte>((getA() << 1) | (getA() >> 7)));
3778 byte f = 0;
3779 if constexpr (T::IS_R800) {
3780 f |= byte(getF() & (S_FLAG | Z_FLAG | P_FLAG | X_FLAG | Y_FLAG));
3781 f |= byte(getA() & C_FLAG);
3782 } else {
3783 f |= byte(getF() & (S_FLAG | Z_FLAG | P_FLAG));
3784 f |= byte(getA() & (Y_FLAG | X_FLAG | C_FLAG));
3785 }
3786 setF(f);
3787 return {1, T::CC_RLA};
3788}
3789template<typename T> II CPUCore<T>::rra() {
3790 auto c = byte((getF() & C_FLAG) << 7);
3791 byte f = (getA() & 0x01) ? C_FLAG : 0;
3792 if constexpr (T::IS_R800) {
3793 f |= byte(getF() & (S_FLAG | Z_FLAG | P_FLAG | X_FLAG | Y_FLAG));
3794 } else {
3795 f |= byte(getF() & (S_FLAG | Z_FLAG | P_FLAG));
3796 }
3797 setA((getA() >> 1) | c);
3798 if constexpr (!T::IS_R800) {
3799 f |= byte(getA() & (X_FLAG | Y_FLAG));
3800 }
3801 setF(f);
3802 return {1, T::CC_RLA};
3803}
3804template<typename T> II CPUCore<T>::rrca() {
3805 byte f = getA() & C_FLAG;
3806 if constexpr (T::IS_R800) {
3807 f |= byte(getF() & (S_FLAG | Z_FLAG | P_FLAG | X_FLAG | Y_FLAG));
3808 } else {
3809 f |= byte(getF() & (S_FLAG | Z_FLAG | P_FLAG));
3810 }
3811 setA(narrow_cast<byte>((getA() >> 1) | (getA() << 7)));
3812 if constexpr (!T::IS_R800) {
3813 f |= byte(getA() & (X_FLAG | Y_FLAG));
3814 }
3815 setF(f);
3816 return {1, T::CC_RLA};
3817}
3818
3819
3820// RLD
3821template<typename T> II CPUCore<T>::rld() {
3822 byte val = RDMEM(getHL(), T::CC_RLD_1);
3823 T::setMemPtr(getHL() + 1);
3824 WRMEM(getHL(), narrow_cast<byte>((val << 4) | (getA() & 0x0F)), T::CC_RLD_2);
3825 setA((getA() & 0xF0) | (val >> 4));
3826 byte f = 0;
3827 if constexpr (T::IS_R800) {
3828 f |= byte(getF() & (C_FLAG | X_FLAG | Y_FLAG));
3829 f |= table.ZSP[getA()];
3830 } else {
3831 f |= byte(getF() & C_FLAG);
3832 f |= table.ZSPXY[getA()];
3833 }
3834 setF(f);
3835 return {1, T::CC_RLD};
3836}
3837
3838// RRD
3839template<typename T> II CPUCore<T>::rrd() {
3840 byte val = RDMEM(getHL(), T::CC_RLD_1);
3841 T::setMemPtr(getHL() + 1);
3842 WRMEM(getHL(), narrow_cast<byte>((val >> 4) | (getA() << 4)), T::CC_RLD_2);
3843 setA((getA() & 0xF0) | (val & 0x0F));
3844 byte f = 0;
3845 if constexpr (T::IS_R800) {
3846 f |= byte(getF() & (C_FLAG | X_FLAG | Y_FLAG));
3847 f |= table.ZSP[getA()];
3848 } else {
3849 f |= byte(getF() & C_FLAG);
3850 f |= table.ZSPXY[getA()];
3851 }
3852 setF(f);
3853 return {1, T::CC_RLD};
3854}
3855
3856
3857// PUSH ss
3858template<typename T> template<int EE> inline void CPUCore<T>::PUSH(word reg) {
3859 setSP(getSP() - 2);
3860 WR_WORD_rev<true, true>(getSP(), reg, T::CC_PUSH_1 + EE);
3861}
3862template<typename T> template<Reg16 REG, int EE> II CPUCore<T>::push_SS() {
3863 PUSH<EE>(get16<REG>()); return {1, T::CC_PUSH + EE};
3864}
3865
3866// POP ss
3867template<typename T> template<int EE> inline word CPUCore<T>::POP() {
3868 word addr = getSP();
3869 setSP(addr + 2);
3870 if constexpr (T::IS_R800) {
3871 // handles both POP and RET instructions (RET with condition = true)
3872 if constexpr (EE == 0) { // not reti/retn, not pop ix/iy
3873 setCurrentPopRet();
3874 // No need for setSlowInstructions()
3875 // -> this only matters directly after a CALL
3876 // instruction and in that case we're still
3877 // executing slow instructions.
3878 }
3879 }
3880 return RD_WORD(addr, T::CC_POP_1 + EE);
3881}
3882template<typename T> template<Reg16 REG, int EE> II CPUCore<T>::pop_SS() {
3883 set16<REG>(POP<EE>()); return {1, T::CC_POP + EE};
3884}
3885
3886
3887// CALL nn / CALL cc,nn
3888template<typename T> template<typename COND> II CPUCore<T>::call(COND cond) {
3889 word addr = RD_WORD_PC<1>(T::CC_CALL_1);
3890 T::setMemPtr(addr);
3891 if (cond(getF())) {
3892 PUSH<T::EE_CALL>(getPC() + 3);
3893 setPC(addr);
3894 if constexpr (T::IS_R800) {
3895 setCurrentCall();
3896 setSlowInstructions();
3897 }
3898 return {0/*3*/, T::CC_CALL_A};
3899 } else {
3900 return {3, T::CC_CALL_B};
3901 }
3902}
3903
3904
3905// RST n
3906template<typename T> template<unsigned ADDR> II CPUCore<T>::rst() {
3907 PUSH<0>(getPC() + 1);
3908 T::setMemPtr(ADDR);
3909 setPC(ADDR);
3910 if constexpr (T::IS_R800) {
3911 setCurrentCall();
3912 setSlowInstructions();
3913 }
3914 return {0/*1*/, T::CC_RST};
3915}
3916
3917
3918// RET
3919template<typename T> template<int EE, typename COND> inline II CPUCore<T>::RET(COND cond) {
3920 if (cond(getF())) {
3921 auto addr = POP<EE>();
3922 T::setMemPtr(addr);
3923 setPC(addr);
3924 return {0/*1*/, T::CC_RET_A + EE};
3925 } else {
3926 return {1, T::CC_RET_B + EE};
3927 }
3928}
3929template<typename T> template<typename COND> II CPUCore<T>::ret(COND cond) {
3930 return RET<T::EE_RET_C>(cond);
3931}
3932template<typename T> II CPUCore<T>::ret() {
3933 return RET<0>(CondTrue());
3934}
3935template<typename T> II CPUCore<T>::retn() { // also reti
3936 setIFF1(getIFF2());
3937 setSlowInstructions();
3938 return RET<T::EE_RETN>(CondTrue());
3939}
3940
3941
3942// JP ss
3943template<typename T> template<Reg16 REG, int EE> II CPUCore<T>::jp_SS() {
3944 setPC(get16<REG>()); T::R800ForcePageBreak(); return {0/*1*/, T::CC_JP_HL + EE};
3945}
3946
3947// JP nn / JP cc,nn
3948template<typename T> template<typename COND> II CPUCore<T>::jp(COND cond) {
3949 word addr = RD_WORD_PC<1>(T::CC_JP_1);
3950 T::setMemPtr(addr);
3951 if (cond(getF())) {
3952 setPC(addr);
3953 T::R800ForcePageBreak();
3954 return {0/*3*/, T::CC_JP_A};
3955 } else {
3956 return {3, T::CC_JP_B};
3957 }
3958}
3959
3960// JR e
3961template<typename T> template<typename COND> II CPUCore<T>::jr(COND cond) {
3962 int8_t ofst = RDMEM_OPCODE<1>(T::CC_JR_1);
3963 if (cond(getF())) {
3964 if (((getPC() + 2) & 0xFF) == 0) {
3965 // On R800, when this instruction is located in the
3966 // last two byte of a page (a page is a 256-byte
3967 // (aligned) memory block) and even if we jump back,
3968 // thus fetching the next opcode byte does not cause a
3969 // page-break, there still is one cycle overhead. It's
3970 // as-if there is a page-break.
3971 //
3972 // This could be explained by some (very limited)
3973 // pipeline behaviour in R800: it seems that the
3974 // decision to cause a page-break on the next
3975 // instruction is already made before the jump
3976 // destination address for the current instruction is
3977 // calculated (though a destination address in another
3978 // page is also a reason for a page-break).
3979 //
3980 // It's likely all instructions behave like this, but I
3981 // think we can get away with only explicitly emulating
3982 // this behaviour in the djnz and the jr (conditional
3983 // or not) instructions: all other instructions that
3984 // cause the PC to change in a non-incremental way do
3985 // already force a page-break for another reason, so
3986 // this effect is masked. Examples of such instructions
3987 // are: JP, RET, CALL, RST, all repeated block
3988 // instructions, accepting an IRQ, (are there more
3989 // instructions or events that change PC?)
3990 //
3991 // See doc/r800-djnz.txt for more details.
3992 T::R800ForcePageBreak();
3993 }
3994 setPC(narrow_cast<word>(getPC() + 2 + ofst));
3995 T::setMemPtr(getPC());
3996 return {0/*2*/, T::CC_JR_A};
3997 } else {
3998 return {2, T::CC_JR_B};
3999 }
4000}
4001
4002// DJNZ e
4003template<typename T> II CPUCore<T>::djnz() {
4004 byte b = getB() - 1;
4005 setB(b);
4006 int8_t ofst = RDMEM_OPCODE<1>(T::CC_JR_1 + T::EE_DJNZ);
4007 if (b) {
4008 if (((getPC() + 2) & 0xFF) == 0) {
4009 // See comment in jr()
4010 T::R800ForcePageBreak();
4011 }
4012 setPC(narrow_cast<word>(getPC() + 2 + ofst));
4013 T::setMemPtr(getPC());
4014 return {0/*2*/, T::CC_JR_A + T::EE_DJNZ};
4015 } else {
4016 return {2, T::CC_JR_B + T::EE_DJNZ};
4017 }
4018}
4019
4020// EX (SP),ss
4021template<typename T> template<Reg16 REG, int EE> II CPUCore<T>::ex_xsp_SS() {
4022 word res = RD_WORD_impl<true, false>(getSP(), T::CC_EX_SP_HL_1 + EE);
4023 T::setMemPtr(res);
4024 WR_WORD_rev<false, true>(getSP(), get16<REG>(), T::CC_EX_SP_HL_2 + EE);
4025 set16<REG>(res);
4026 return {1, T::CC_EX_SP_HL + EE};
4027}
4028
4029// IN r,(c)
4030template<typename T> template<Reg8 REG> II CPUCore<T>::in_R_c() {
4031 if constexpr (T::IS_R800) T::waitForEvenCycle(T::CC_IN_R_C_1);
4032 T::setMemPtr(getBC() + 1);
4033 byte res = READ_PORT(getBC(), T::CC_IN_R_C_1);
4034 byte f = 0;
4035 if constexpr (T::IS_R800) {
4036 f |= byte(getF() & (C_FLAG | X_FLAG | Y_FLAG));
4037 f |= table.ZSP[res];
4038 } else {
4039 f |= byte(getF() & C_FLAG);
4040 f |= table.ZSPXY[res];
4041 }
4042 setF(f);
4043 set8<REG>(res);
4044 return {1, T::CC_IN_R_C};
4045}
4046
4047// IN a,(n)
4048template<typename T> II CPUCore<T>::in_a_byte() {
4049 unsigned y = RDMEM_OPCODE<1>(T::CC_IN_A_N_1) + 256 * getA();
4050 T::setMemPtr(y + 1);
4051 if constexpr (T::IS_R800) T::waitForEvenCycle(T::CC_IN_A_N_2);
4052 setA(READ_PORT(narrow_cast<word>(y), T::CC_IN_A_N_2));
4053 return {2, T::CC_IN_A_N};
4054}
4055
4056// OUT (c),r
4057template<typename T> template<Reg8 REG> II CPUCore<T>::out_c_R() {
4058 if constexpr (T::IS_R800) T::waitForEvenCycle(T::CC_OUT_C_R_1);
4059 T::setMemPtr(getBC() + 1);
4060 WRITE_PORT(getBC(), get8<REG>(), T::CC_OUT_C_R_1);
4061 return {1, T::CC_OUT_C_R};
4062}
4063template<typename T> II CPUCore<T>::out_c_0() {
4064 // TODO not on R800
4065 if constexpr (T::IS_R800) T::waitForEvenCycle(T::CC_OUT_C_R_1);
4066 T::setMemPtr(getBC() + 1);
4067 byte out_c_x = isCMOS ? 255 : 0;
4068 WRITE_PORT(getBC(), out_c_x, T::CC_OUT_C_R_1);
4069 return {1, T::CC_OUT_C_R};
4070}
4071
4072// OUT (n),a
4073template<typename T> II CPUCore<T>::out_byte_a() {
4074 byte port = RDMEM_OPCODE<1>(T::CC_OUT_N_A_1);
4075 auto y = narrow_cast<word>((getA() << 8) | port);
4076 T::setMemPtr((getA() << 8) | ((port + 1) & 255));
4077 if constexpr (T::IS_R800) T::waitForEvenCycle(T::CC_OUT_N_A_2);
4078 WRITE_PORT(y, getA(), T::CC_OUT_N_A_2);
4079 return {2, T::CC_OUT_N_A};
4080}
4081
4082
4083// block CP
4084template<typename T> inline II CPUCore<T>::BLOCK_CP(int increase, bool repeat) {
4085 T::setMemPtr(T::getMemPtr() + increase);
4086 byte val = RDMEM(getHL(), T::CC_CPI_1);
4087 byte res = getA() - val;
4088 setHL(narrow_cast<word>(getHL() + increase));
4089 setBC(getBC() - 1);
4090 byte f = ((getA() ^ val ^ res) & H_FLAG) |
4091 table.ZS[res] |
4092 N_FLAG |
4093 (getBC() ? V_FLAG : 0);
4094 if constexpr (T::IS_R800) {
4095 f |= byte(getF() & (C_FLAG | X_FLAG | Y_FLAG));
4096 } else {
4097 f |= byte(getF() & C_FLAG);
4098 unsigned k = res - ((f & H_FLAG) >> 4);
4099 f |= (k << 4) & Y_FLAG; // bit 1 -> flag 5
4100 f |= k & X_FLAG; // bit 3 -> flag 3
4101 }
4102 setF(f);
4103 if (repeat && getBC() && res) {
4104 //setPC(getPC() - 2);
4105 T::setMemPtr(getPC() + 1);
4106 return {word(-1)/*1*/, T::CC_CPIR};
4107 } else {
4108 return {1, T::CC_CPI};
4109 }
4110}
4111template<typename T> II CPUCore<T>::cpd() { return BLOCK_CP(-1, false); }
4112template<typename T> II CPUCore<T>::cpi() { return BLOCK_CP( 1, false); }
4113template<typename T> II CPUCore<T>::cpdr() { return BLOCK_CP(-1, true ); }
4114template<typename T> II CPUCore<T>::cpir() { return BLOCK_CP( 1, true ); }
4115
4116
4117// block LD
4118template<typename T> inline II CPUCore<T>::BLOCK_LD(int increase, bool repeat) {
4119 byte val = RDMEM(getHL(), T::CC_LDI_1);
4120 WRMEM(getDE(), val, T::CC_LDI_2);
4121 setHL(narrow_cast<word>(getHL() + increase));
4122 setDE(narrow_cast<word>(getDE() + increase));
4123 setBC(getBC() - 1);
4124 byte f = getBC() ? V_FLAG : 0;
4125 if constexpr (T::IS_R800) {
4126 f |= byte(getF() & (S_FLAG | Z_FLAG | C_FLAG | X_FLAG | Y_FLAG));
4127 } else {
4128 f |= byte(getF() & (S_FLAG | Z_FLAG | C_FLAG));
4129 f |= byte(((getA() + val) << 4) & Y_FLAG); // bit 1 -> flag 5
4130 f |= byte((getA() + val) & X_FLAG); // bit 3 -> flag 3
4131 }
4132 setF(f);
4133 if (repeat && getBC()) {
4134 //setPC(getPC() - 2);
4135 T::setMemPtr(getPC() + 1);
4136 return {word(-1)/*1*/, T::CC_LDIR};
4137 } else {
4138 return {1, T::CC_LDI};
4139 }
4140}
4141template<typename T> II CPUCore<T>::ldd() { return BLOCK_LD(-1, false); }
4142template<typename T> II CPUCore<T>::ldi() { return BLOCK_LD( 1, false); }
4143template<typename T> II CPUCore<T>::lddr() { return BLOCK_LD(-1, true ); }
4144template<typename T> II CPUCore<T>::ldir() { return BLOCK_LD( 1, true ); }
4145
4146
4147// block IN
4148template<typename T> inline II CPUCore<T>::BLOCK_IN(int increase, bool repeat) {
4149 if constexpr (T::IS_R800) T::waitForEvenCycle(T::CC_INI_1);
4150 T::setMemPtr(getBC() + increase);
4151 setBC(getBC() - 0x100); // decr before use
4152 byte val = READ_PORT(getBC(), T::CC_INI_1);
4153 WRMEM(getHL(), val, T::CC_INI_2);
4154 setHL(narrow_cast<word>(getHL() + increase));
4155 unsigned k = val + ((getC() + increase) & 0xFF);
4156 byte b = getB();
4157 if constexpr (T::IS_R800) {
4158 setF((getF() & ~Z_FLAG) | (b ? 0 : Z_FLAG) | N_FLAG);
4159 } else {
4160 setF(((val & S_FLAG) >> 6) | // N_FLAG
4161 ((k & 0x100) ? (H_FLAG | C_FLAG) : 0) |
4162 table.ZSXY[b] |
4163 (table.ZSPXY[(k & 0x07) ^ b] & P_FLAG));
4164 }
4165 if (repeat && b) {
4166 //setPC(getPC() - 2);
4167 return {word(-1)/*1*/, T::CC_INIR};
4168 } else {
4169 return {1, T::CC_INI};
4170 }
4171}
4172template<typename T> II CPUCore<T>::ind() { return BLOCK_IN(-1, false); }
4173template<typename T> II CPUCore<T>::ini() { return BLOCK_IN( 1, false); }
4174template<typename T> II CPUCore<T>::indr() { return BLOCK_IN(-1, true ); }
4175template<typename T> II CPUCore<T>::inir() { return BLOCK_IN( 1, true ); }
4176
4177
4178// block OUT
4179template<typename T> inline II CPUCore<T>::BLOCK_OUT(int increase, bool repeat) {
4180 byte val = RDMEM(getHL(), T::CC_OUTI_1);
4181 setHL(narrow_cast<word>(getHL() + increase));
4182 if constexpr (T::IS_R800) T::waitForEvenCycle(T::CC_OUTI_2);
4183 WRITE_PORT(getBC(), val, T::CC_OUTI_2);
4184 setBC(getBC() - 0x100); // decr after use
4185 T::setMemPtr(getBC() + increase);
4186 unsigned k = val + getL();
4187 byte b = getB();
4188 if constexpr (T::IS_R800) {
4189 setF((getF() & ~Z_FLAG) | (b ? 0 : Z_FLAG) | N_FLAG);
4190 } else {
4191 setF(((val & S_FLAG) >> 6) | // N_FLAG
4192 ((k & 0x100) ? (H_FLAG | C_FLAG) : 0) |
4193 table.ZSXY[b] |
4194 (table.ZSPXY[(k & 0x07) ^ b] & P_FLAG));
4195 }
4196 if (repeat && b) {
4197 //setPC(getPC() - 2);
4198 return {word(-1)/*1*/, T::CC_OTIR};
4199 } else {
4200 return {1, T::CC_OUTI};
4201 }
4202}
4203template<typename T> II CPUCore<T>::outd() { return BLOCK_OUT(-1, false); }
4204template<typename T> II CPUCore<T>::outi() { return BLOCK_OUT( 1, false); }
4205template<typename T> II CPUCore<T>::otdr() { return BLOCK_OUT(-1, true ); }
4206template<typename T> II CPUCore<T>::otir() { return BLOCK_OUT( 1, true ); }
4207
4208
4209// various
4210template<typename T> template<int EE> II CPUCore<T>::nop() { return {1, T::CC_NOP + EE}; }
4211template<typename T> II CPUCore<T>::ccf() {
4212 byte f = 0;
4213 if constexpr (T::IS_R800) {
4214 // H flag is different from Z80 (and as always XY flags as well)
4215 f |= byte(getF() & (S_FLAG | Z_FLAG | P_FLAG | C_FLAG | X_FLAG | Y_FLAG | H_FLAG));
4216 } else {
4217 f |= byte((getF() & C_FLAG) << 4); // H_FLAG
4218 // only set X(Y) flag (don't reset if already set)
4219 if (isCMOS) {
4220 // Y flag is not changed on a CMOS Z80
4221 f |= byte(getF() & (S_FLAG | Z_FLAG | P_FLAG | C_FLAG | Y_FLAG));
4222 f |= byte((getF() | getA()) & X_FLAG);
4223 } else {
4224 f |= byte(getF() & (S_FLAG | Z_FLAG | P_FLAG | C_FLAG));
4225 f |= byte((getF() | getA()) & (X_FLAG | Y_FLAG));
4226 }
4227 }
4228 f ^= C_FLAG;
4229 setF(f);
4230 return {1, T::CC_CCF};
4231}
4232template<typename T> II CPUCore<T>::cpl() {
4233 setA(getA() ^ 0xFF);
4234 byte f = H_FLAG | N_FLAG;
4235 if constexpr (T::IS_R800) {
4236 f |= getF();
4237 } else {
4238 f |= byte(getF() & (S_FLAG | Z_FLAG | P_FLAG | C_FLAG));
4239 f |= byte(getA() & (X_FLAG | Y_FLAG));
4240 }
4241 setF(f);
4242 return {1, T::CC_CPL};
4243}
4244template<typename T> II CPUCore<T>::daa() {
4245 byte a = getA();
4246 byte f = getF();
4247 byte adjust = 0;
4248 if ((f & H_FLAG) || ((getA() & 0xf) > 9)) adjust += 6;
4249 if ((f & C_FLAG) || (getA() > 0x99)) adjust += 0x60;
4250 if (f & N_FLAG) a -= adjust; else a += adjust;
4251 if constexpr (T::IS_R800) {
4252 f &= C_FLAG | N_FLAG | X_FLAG | Y_FLAG;
4253 f |= table.ZSP[a];
4254 } else {
4255 f &= C_FLAG | N_FLAG;
4256 f |= table.ZSPXY[a];
4257 }
4258 f |= byte((getA() > 0x99) | ((getA() ^ a) & H_FLAG));
4259 setA(a);
4260 setF(f);
4261 return {1, T::CC_DAA};
4262}
4263template<typename T> II CPUCore<T>::neg() {
4264 // alternative: LUT word negTable[256]
4265 unsigned a = getA();
4266 unsigned res = -signed(a);
4267 byte f = ((res & 0x100) ? C_FLAG : 0) |
4268 N_FLAG |
4269 ((res ^ a) & H_FLAG) |
4270 ((a & res & 0x80) >> 5); // V_FLAG
4271 if constexpr (T::IS_R800) {
4272 f |= table.ZS[res & 0xFF];
4273 f |= byte(getF() & (X_FLAG | Y_FLAG));
4274 } else {
4275 f |= table.ZSXY[res & 0xFF];
4276 }
4277 setF(f);
4278 setA(narrow_cast<byte>(res));
4279 return {1, T::CC_NEG};
4280}
4281template<typename T> II CPUCore<T>::scf() {
4282 byte f = C_FLAG;
4283 if constexpr (T::IS_R800) {
4284 f |= byte(getF() & (S_FLAG | Z_FLAG | P_FLAG | X_FLAG | Y_FLAG));
4285 } else {
4286 // only set X(Y) flag (don't reset if already set)
4287 if (isCMOS) {
4288 // Y flag is not changed on a CMOS Z80
4289 f |= byte(getF() & (S_FLAG | Z_FLAG | P_FLAG | Y_FLAG));
4290 f |= byte((getF() | getA()) & X_FLAG);
4291 } else {
4292 f |= byte(getF() & (S_FLAG | Z_FLAG | P_FLAG));
4293 f |= byte((getF() | getA()) & (X_FLAG | Y_FLAG));
4294 }
4295 }
4296 setF(f);
4297 return {1, T::CC_SCF};
4298}
4299
4300template<typename T> II CPUCore<T>::ex_af_af() {
4301 auto t = getAF2(); setAF2(getAF()); setAF(t);
4302 return {1, T::CC_EX};
4303}
4304template<typename T> II CPUCore<T>::ex_de_hl() {
4305 auto t = getDE(); setDE(getHL()); setHL(t);
4306 return {1, T::CC_EX};
4307}
4308template<typename T> II CPUCore<T>::exx() {
4309 auto t1 = getBC2(); setBC2(getBC()); setBC(t1);
4310 auto t2 = getDE2(); setDE2(getDE()); setDE(t2);
4311 auto t3 = getHL2(); setHL2(getHL()); setHL(t3);
4312 return {1, T::CC_EX};
4313}
4314
4315template<typename T> II CPUCore<T>::di() {
4316 setIFF1(false);
4317 setIFF2(false);
4318 return {1, T::CC_DI};
4319}
4320template<typename T> II CPUCore<T>::ei() {
4321 setIFF1(true);
4322 setIFF2(true);
4323 setCurrentEI(); // no ints directly after this instr
4324 setSlowInstructions();
4325 return {1, T::CC_EI};
4326}
4327template<typename T> II CPUCore<T>::halt() {
4328 setHALT(true);
4329 setSlowInstructions();
4330
4331 if (!(getIFF1() || getIFF2())) {
4332 diHaltCallback.execute();
4333 }
4334 return {1, T::CC_HALT};
4335}
4336template<typename T> template<unsigned N> II CPUCore<T>::im_N() {
4337 setIM(N); return {1, T::CC_IM};
4338}
4339
4340// LD A,I/R
4341template<typename T> template<Reg8 REG> II CPUCore<T>::ld_a_IR() {
4342 setA(get8<REG>());
4343 byte f = getIFF2() ? V_FLAG : 0;
4344 if constexpr (T::IS_R800) {
4345 f |= byte(getF() & (C_FLAG | X_FLAG | Y_FLAG));
4346 f |= table.ZS[getA()];
4347 } else {
4348 f |= byte(getF() & C_FLAG);
4349 f |= table.ZSXY[getA()];
4350 // see comment in the IRQ acceptance part of executeSlow().
4351 setCurrentLDAI(); // only Z80 (not R800) has this quirk
4352 setSlowInstructions();
4353 }
4354 setF(f);
4355 return {1, T::CC_LD_A_I};
4356}
4357
4358// LD I/R,A
4359template<typename T> II CPUCore<T>::ld_r_a() {
4360 // This code sequence:
4361 // XOR A / LD R,A / LD A,R
4362 // gives A=2 for Z80, but A=1 for R800. The difference can possibly be
4363 // explained by a difference in the relative time between writing the
4364 // new value to the R register and increasing the R register per M1
4365 // cycle. Here we implemented the R800 behaviour by storing 'A-1' into
4366 // R, that's good enough for now.
4367 byte val = getA();
4368 if constexpr (T::IS_R800) val -= 1;
4369 setR(val);
4370 return {1, T::CC_LD_A_I};
4371}
4372template<typename T> II CPUCore<T>::ld_i_a() {
4373 setI(getA());
4374 return {1, T::CC_LD_A_I};
4375}
4376
4377// MULUB A,r
4378template<typename T> template<Reg8 REG> II CPUCore<T>::mulub_a_R() {
4379 assert(T::IS_R800); // this instruction is R800-only
4380 // Verified on real R800:
4381 // YHXN flags are unchanged
4382 // SV flags are reset
4383 // Z flag is set when result is zero
4384 // C flag is set when result doesn't fit in 8-bit
4385 setHL(word(getA()) * word(get8<REG>()));
4386 setF((getF() & (N_FLAG | H_FLAG | X_FLAG | Y_FLAG)) |
4387 0 | // S_FLAG V_FLAG
4388 (getHL() ? 0 : Z_FLAG) |
4389 ((getHL() & 0xFF00) ? C_FLAG : 0));
4390 return {1, T::CC_MULUB};
4391}
4392
4393// MULUW HL,ss
4394template<typename T> template<Reg16 REG> II CPUCore<T>::muluw_hl_SS() {
4395 assert(T::IS_R800); // this instruction is R800-only
4396 // Verified on real R800:
4397 // YHXN flags are unchanged
4398 // SV flags are reset
4399 // Z flag is set when result is zero
4400 // C flag is set when result doesn't fit in 16-bit
4401 uint32_t res = uint32_t(getHL()) * get16<REG>();
4402 setDE(narrow_cast<word>(res >> 16));
4403 setHL(narrow_cast<word>(res >> 0));
4404 setF((getF() & (N_FLAG | H_FLAG | X_FLAG | Y_FLAG)) |
4405 0 | // S_FLAG V_FLAG
4406 (res ? 0 : Z_FLAG) |
4407 ((res & 0xFFFF0000) ? C_FLAG : 0));
4408 return {1, T::CC_MULUW};
4409}
4410
4411
4412// versions:
4413// 1 -> initial version
4414// 2 -> moved memptr from here to Z80TYPE (and not to R800TYPE)
4415// 3 -> timing of the emulation changed (no changes in serialization)
4416// 4 -> timing of the emulation changed again (see doc/internal/r800-call.txt)
4417// 5 -> added serialization of nmiEdge
4418template<typename T> template<typename Archive>
4419void CPUCore<T>::serialize(Archive& ar, unsigned version)
4420{
4421 T::serialize(ar, version);
4422 ar.serialize("regs", static_cast<CPURegs&>(*this));
4423 if (ar.versionBelow(version, 2)) {
4424 unsigned mPtr = 0; // dummy value (avoid warning)
4425 ar.serialize("memptr", mPtr);
4426 T::setMemPtr(mPtr);
4427 }
4428
4429 if (ar.versionBelow(version, 5)) {
4430 // NMI is unused on MSX and even on systems where it is used nmiEdge
4431 // is true only between the moment the NMI request comes in and the
4432 // moment the Z80 jumps to the NMI handler, so defaulting to false
4433 // is pretty safe.
4434 nmiEdge = false;
4435 } else {
4436 // CPU is deserialized after devices, so nmiEdge is restored to the
4437 // saved version even if IRQHelpers set it on deserialization.
4438 ar.serialize("nmiEdge", nmiEdge);
4439 }
4440
4441 // Don't serialize:
4442 // - IRQStatus, NMIStatus:
4443 // the IRQHelper deserialization makes sure these get the right value
4444 // - slowInstructions, exitLoop:
4445 // serialization happens outside the CPU emulation loop
4446
4447 if constexpr (T::IS_R800) {
4448 if (ar.versionBelow(version, 4)) {
4449 motherboard.getMSXCliComm().printWarning(
4450 "Loading an old savestate: the timing of the R800 "
4451 "emulation has changed. This may cause synchronization "
4452 "problems in replay.");
4453 }
4454 }
4455}
4456
4457// Force template instantiation
4458template class CPUCore<Z80TYPE>;
4459template class CPUCore<R800TYPE>;
4460
4463
4464} // namespace openmsx
#define MAYBE_UNUSED_LABEL
Definition: CPUCore.cc:204
#define NEXT
#define NEXT_EI
#define CASE(X)
#define NEXT_STOP
BaseSetting * setting
Definition: Interpreter.cc:28
TclObject t
void lowerIRQ()
Lowers the maskable interrupt count.
Definition: CPUCore.cc:446
void setNextSyncPoint(EmuTime::param time)
Definition: CPUCore.cc:501
void disasmCommand(Interpreter &interp, std::span< const TclObject > tokens, TclObject &result) const
Definition: CPUCore.cc:517
void setFreq(unsigned freq)
Change the clock freq.
Definition: CPUCore.cc:544
void execute(bool fastForward)
Definition: CPUCore.cc:2560
void warp(EmuTime::param time)
Definition: CPUCore.cc:328
CPUCore(MSXMotherBoard &motherboard, const std::string &name, const BooleanSetting &traceSetting, TclCallback &diHaltCallback, EmuTime::param time)
Definition: CPUCore.cc:292
void lowerNMI()
Lowers the non-maskable interrupt count.
Definition: CPUCore.cc:462
void exitCPULoopSync()
Request to exit the main CPU emulation loop.
Definition: CPUCore.cc:407
EmuTime::param getCurrentTime() const
Definition: CPUCore.cc:334
void exitCPULoopAsync()
Similar to exitCPULoopSync(), but this method may be called from any thread.
Definition: CPUCore.cc:402
void raiseNMI()
Raises the non-maskable interrupt count.
Definition: CPUCore.cc:452
void serialize(Archive &ar, unsigned version)
Definition: CPUCore.cc:4419
void doReset(EmuTime::param time)
Reset the CPU.
Definition: CPUCore.cc:339
void wait(EmuTime::param time)
Definition: CPUCore.cc:484
EmuTime waitCycles(EmuTime::param time, unsigned cycles)
Definition: CPUCore.cc:491
bool isM1Cycle(unsigned address) const
Definition: CPUCore.cc:468
void raiseIRQ()
Raises the maskable interrupt count.
Definition: CPUCore.cc:437
void addListElement(const T &t)
Definition: TclObject.hh:128
#define NEVER_INLINE
Definition: inline.hh:17
#define ALWAYS_INLINE
Definition: inline.hh:16
ALWAYS_INLINE uint16_t read_UA_L16(const void *p)
Definition: endian.hh:229
ALWAYS_INLINE void write_UA_L16(void *p, uint16_t x)
Definition: endian.hh:189
constexpr unsigned LOW
Definition: CacheLine.hh:9
constexpr unsigned HIGH
Definition: CacheLine.hh:10
constexpr unsigned BITS
Definition: CacheLine.hh:6
bool isMainThread()
Returns true when called from the main thread.
Definition: Thread.cc:15
This file implemented 3 utility functions:
Definition: Autofire.cc:9
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
unsigned dasm(const MSXCPUInterface &interface, word pc, std::span< byte, 4 > buf, std::string &dest, EmuTime::param time)
Disassemble.
Definition: Dasm.cc:19
@ REG_I
Definition: CPUCore.cc:213
@ REG_R
Definition: CPUCore.cc:213
@ DUMMY
Definition: CPUCore.cc:213
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
void serialize(Archive &ar, T &t, unsigned version)
std::array< const EDStorage, 4 > A
std::array< const A, 3 > A2
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:1021
TemporaryString tmpStrCat(Ts &&... ts)
Definition: strCat.hh:610
Definition: view_test.cc:239
bool operator()(byte f) const
Definition: CPUCore.cc:282
bool operator()(byte f) const
Definition: CPUCore.cc:286
bool operator()(byte f) const
Definition: CPUCore.cc:283
bool operator()(byte f) const
Definition: CPUCore.cc:285
bool operator()(byte f) const
Definition: CPUCore.cc:288
bool operator()(byte f) const
Definition: CPUCore.cc:289
bool operator()(byte f) const
Definition: CPUCore.cc:287
bool operator()(byte) const
Definition: CPUCore.cc:290
bool operator()(byte f) const
Definition: CPUCore.cc:284
std::array< byte, 256 > ZSPXY
Definition: CPUCore.cc:232
std::array< byte, 256 > ZSP
Definition: CPUCore.cc:231
std::array< byte, 256 > ZSPH
Definition: CPUCore.cc:233
std::array< byte, 256 > ZSXY
Definition: CPUCore.cc:230
std::array< byte, 256 > ZS
Definition: CPUCore.cc:229
#define UNREACHABLE
Definition: unreachable.hh:38
constexpr void repeat(T n, Op op)
Repeat the given operation 'op' 'n' times.
Definition: xrange.hh:147
constexpr auto xrange(T e)
Definition: xrange.hh:132