0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Z80エミュレーターで遊ぶ(モニタープログラム)(ESP32-S3)(いろいろ、なぜかUNO Q発売)

Last updated at Posted at 2025-10-07

参考

いろいろ注意

  • 過去ログを見よ!!!
  • end -> @ (not ctrl + z)
  • NOP -> NOP_t

mem[0] = 0xAF;                               // xor a
mem[1] = 0x3C;                               // inc a
mem[2] = 0x32; mem[3] = 0x32; mem[4] = 0x00; // ld(0x0032) a
mem[5] = 0x76;                               // halt


AF 3C 32 32 00

結果


=================================
=================================
*[a]ddress input/outupt start address
[i]nput input dump (stop ctrl + z)
[l]ist output dump
*[n]ew reset ram clear
[r]eset run execution (no ram clear) 256step
*[s]ave save eeprom
*[g]et load eeprom
[b]ye exit system
=>i
!!input

input text
AF 3C 32 32 00
[AF 3C 32 32 00]num=5

=================================
=================================
*[a]ddress input/outupt start address
[i]nput input dump (stop ctrl + z)
[l]ist output dump
*[n]ew reset ram clear
[r]eset run execution (no ram clear) 256step
*[s]ave save eeprom
*[g]et load eeprom
[b]ye exit system
=>l
!!list

input binary
add  +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F
0000 AF 3C 32 32 00 00 00 00 00 00 00 00 00 00 00 00 
0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

=================================
=================================
*[a]ddress input/outupt start address
[i]nput input dump (stop ctrl + z)
[l]ist output dump
*[n]ew reset ram clear
[r]eset run execution (no ram clear) 256step
*[s]ave save eeprom
*[g]et load eeprom
[b]ye exit system
=>r
!!run

=================================
=================================
*[a]ddress input/outupt start address
[i]nput input dump (stop ctrl + z)
[l]ist output dump
*[n]ew reset ram clear
[r]eset run execution (no ram clear) 256step
*[s]ave save eeprom
*[g]et load eeprom
[b]ye exit system
=>l
!!list

input binary
add  +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F
0000 AF 3C 32 32 00 00 00 00 00 00 00 00 00 00 00 00 
0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0030 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 
0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

=================================
=================================
*[a]ddress input/outupt start address
[i]nput input dump (stop ctrl + z)
[l]ist output dump
*[n]ew reset ram clear
[r]eset run execution (no ram clear) 256step
*[s]ave save eeprom
*[g]et load eeprom
[b]ye exit system
=>



プログラム




//Z80_monitor_menu1


//インクルド
#include <Arduino.h>
#include <EEPROM.h>

#include <stdio.h>
#include <string.h>

#include "z80.hpp"


// MMU; Memory Management Unit
// z80.hpp では MMU は提供していないので自前で準備する必要があります。
// 以下、64KB RAM と 256 bytes の I/O を持つシンプルな MMU の実装例です。
class MMU {
  public:
    unsigned char RAM[0x10000];
    unsigned char IO[0x100];

    MMU() {
        memset(&RAM, 0, sizeof(RAM));
        memset(&IO, 0, sizeof(IO));
    }
};

// メモリアドレス(addr)から1バイト読み込む処理
static unsigned char readByte(void* arg, unsigned short addr) {
    return ((MMU*)arg)->RAM[addr];
}

// メモリアドレス(addr) へ 1 byte の値(value)を書き込む処理
static void writeByte(void* arg, unsigned short addr, unsigned char value)
{
    ((MMU*)arg)->RAM[addr] = value;
}

// I/O ポート (port) からの入力処理
static unsigned char inPort(void* arg, unsigned char port)
{
    return ((MMU*)arg)->IO[port];
}

// I/O ポート (port) へ 値(value) を書き込む処理
static void outPort(void* arg, unsigned char port, unsigned char value) {
    ((MMU*)arg)->IO[port] = value;
}


//定義
char str1[256 + 1] = {0,0,0,0};

//定義 メニュー用
char menu1[] = {"\n"
                "=================================\n"
                "=================================\n"
                "*[a]ddress input/outupt start address\n"
                "[i]nput input dump (stop ctrl + z)\n"
                "[l]ist output dump\n"
                "*[n]ew reset ram clear\n"
                "[r]eset run execution (no ram clear) 256step\n"
                "*[s]ave save eeprom\n"
                "*[g]et load eeprom\n"
                "[b]ye exit system\n"
                "=>"
               };
//char menu1[] = ">"; //debug

//0 1 2 3 4 5 6 7 8 9 A B C D E F
char t[] = {
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //00
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //10
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //20
  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, //30

  0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, //40
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //50
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //60
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //70

  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //80
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //90
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //A0
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //B0

  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //C0
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //D0
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //E0
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 //F0
};


int dump_in(unsigned char *p, unsigned char *d) {
  // Your code here!

  int i = 0; //カウンター 入力文字
  int l = 0; //カウンター 出力バイナリ
  int a; //一時領域
  int g; //一時領域

  while (1) { //無限ループ

    //読み飛ばし
    //構造化プログラミング先読み
    a = p[i]; //読み出し

    while ((t[a] == 0) && (a != '0') && (a != 0)) {

      i++;
      a = p[i]; //読み出し

    }  //while

    if (a == 0) break; //終端文字の時は、ループを抜ける

    //-------------------------------------

    //16進の間
    //構造化プログラミング先読み
    a = p[i]; //読み出し

    g = 0;
    while ( ((t[a] != 0) || (a == '0')) && (a != 0)) {

      g = (g << 4) + t[a];

      i++;
      a = p[i]; //読み出し

    }  //while

    d[l] = g;
    l++;

    if (a == 0) break; //終端文字の時は、ループを抜ける

  }  //while 0

  return (l);

}  //dump_in


void dump_out(unsigned char *dump1, int num) {
  // Your code here!
  char *b = "0123456789ABCDEF";  //16進の変換テーブル

  int k = 0;  //アドレスのカウンター

  //ヘッターの表示
  Serial.println("add  +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F");

  if (num == 0) return;

  for (int i = 0; i < (((num - 1) / 16) + 1); i++) { //行のループ

    //アドレスの表示
    Serial.print(b[k >> 12       ]);  //上位4ビットを表示
    Serial.print(b[k >>  8 & 0x0f]);  //12ビット目から4ビット
    Serial.print(b[k >>  4 & 0x0f]);  //8ビット目から4ビット
    Serial.print(b[k       & 0x0f]);  //下位4ビットを表示
    Serial.print(' ');

    for (int j = 0; j < 16; j++) { //列のループ

      //データの表示
      int a = dump1[k++];
      Serial.print(b[a >> 4       ]);  //上位4ビットを表示
      Serial.print(b[a      & 0x0f]);  //下位4ビットを表示
      Serial.print(' ');

    }  //for j

    Serial.println(); //改行

  }  //for i

}  //dump_out


//初期化
void setup() {

  //シリアルポートの初期化
  Serial.begin(9600);

  Serial.print(menu1); //メニューの表示

}  //setup


    MMU mmu;
    Z80 z80(readByte, writeByte, inPort, outPort, &mmu);


//メインループ
void loop() {


  if (Serial.available() > 0) {  // 受信したデータが存在する
    char ch1 = Serial.read();    // 受信データを読み込む
    Serial.println(ch1);         // 受信データを送りかえす

    if (ch1 == 'a') { //add
      Serial.println("!!add");  //debug

    } else if (ch1 == 'i') { //input
      Serial.println("!!input");  //debug

      Serial.println();
      Serial.println("input text");  //debug

      int i = 0;
      while (1) { //無限ループ

        if (Serial.available() > 0) { // 受信したデータが存在する
          char ch = Serial.read(); // 受信データを読み込む
          //if (ch == 26) { //STOP
          if (ch == '@') { //STOP
            str1[i] = 0;
            break;
          }
          str1[i++] = ch;
          Serial.print(ch); // 受信データを送りかえす
        }  //endif

      }  //while

      //16進テキストをバイナリーに変換
      //unsigned char p[]="01-02-0A";
      Serial.println(); //debug
      Serial.print('['); //debug
      Serial.print(str1); //gebug
      Serial.print(']'); //debug
      int num = dump_in((unsigned char *)str1,mmu.RAM);
      Serial.print("num="); //gebug
      Serial.print(num); //gebug
      Serial.println(); //debug

    } else if (ch1 == 'l') { //list
      Serial.println("!!list");  //debug

      Serial.println();
      Serial.println("input binary");  //debug


      dump_out(  mmu.RAM ,256);


    } else if (ch1 == 'n') { //new
      Serial.println("!!new");  //debug

      str1[0] = 0; //文字列の初期化(クリアー)

    } else if (ch1 == 'r') { //run
      Serial.println("!!run");  //debug

    // when executing about 1234Hz
    int actualExecuteClocks = z80.execute(256);

    } else if (ch1 == 's') { //save
      Serial.println("!!save");  //debug

      int i = 0;
      while (str1[i] != 0) {

        EEPROM.write(i, str1[i]); //EEPROMへの書き込み
        i++;

      }  //while
      EEPROM.write(i, 0); //EEPROMへの書き込み

    } else if (ch1 == 'g') { // get
      Serial.println("!!load");  //debug

      char ch;
      int i = 0;
      do {

        ch = EEPROM.read(i); //EEPROMからの読み込み
        //ch = 0; //debug
        str1[i] = ch;
        i++;

      } while (ch != 0); //while


    } else if (ch1 == 'b') { //bye
      Serial.println("!!bye");  //debug

      while (1) {}; //無限ループ

    }  //endif

    Serial.print(menu1); //メニューの表示

  }  //endif


}  //loop



z80.hpp





/**
 * SUZUKI PLAN - Z80 Emulator
 * -----------------------------------------------------------------------------
 * The MIT License (MIT)
 *
 * Copyright (c) 2019 Yoji Suzuki.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * -----------------------------------------------------------------------------
 */
#ifndef INCLUDE_Z80_HPP
#define INCLUDE_Z80_HPP
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#if !defined(Z80_DISABLE_BREAKPOINT) || !defined(Z80_DISABLE_NESTCHECK)
#include <map>
#include <vector>
#endif

#ifndef Z80_NO_FUNCTIONAL
#include <functional>
#endif

#ifndef Z80_NO_EXCEPTION
#include <stdexcept>
#endif

class Z80
{
  public: // Interface data types
    struct WaitClocks {
        int fetch;  // Wait T-cycle (Hz) before fetching instruction (default is 0 = no wait)
        int fetchM; // Wait T-cycle (Hz) before fetching multi-bytes instruction (default is 0 = no wait)
        int read;   // Wait T-cycle (Hz) before to read memory (default is 0 = no wait)
        int write;  // Wait T-cycle (Hz) before to write memory (default is 0 = no wait)
    } wtc;

    struct RegisterPair {
        unsigned char A;
        unsigned char F;
        unsigned char B;
        unsigned char C;
        unsigned char D;
        unsigned char E;
        unsigned char H;
        unsigned char L;
    };

    struct Register {
        struct RegisterPair pair;
        struct RegisterPair back;
        unsigned short PC;
        unsigned short SP;
        unsigned short IX;
        unsigned short IY;
        unsigned short interruptVector; // interrupt vector for IRQ
        unsigned short interruptAddrN;  // interrupt address for NMI
        unsigned short WZ;
        unsigned short reserved16;
        unsigned char R;
        unsigned char I;
        unsigned char IFF;
        unsigned char interrupt; // NI-- --mm (N: NMI, I: IRQ, mm: mode)
        unsigned char consumeClockCounter;
        unsigned char execEI;
        unsigned char reserved8[2];
    } reg;

    inline unsigned char flagS() { return 0b10000000; }
    inline unsigned char flagZ() { return 0b01000000; }
    inline unsigned char flagY() { return 0b00100000; }
    inline unsigned char flagH() { return 0b00010000; }
    inline unsigned char flagX() { return 0b00001000; }
    inline unsigned char flagPV() { return 0b00000100; }
    inline unsigned char flagN() { return 0b00000010; }
    inline unsigned char flagC() { return 0b00000001; }

    inline unsigned char readByte(unsigned short addr, int clock = 4)
    {
#ifndef Z80_DISABLE_BREAKPOINT
        if (clock && wtc.read) consumeClock(wtc.read);
        unsigned char byte = CB.read(CB.arg, addr);
        if (clock) consumeClock(clock);
#else
        consumeClock(wtc.read);
        unsigned char byte = CB.read(CB.arg, addr);
        consumeClock(clock);
#endif
        return byte;
    }

    inline void writeByte(unsigned short addr, unsigned char value, int clock = 4)
    {
        consumeClock(wtc.write);
        CB.write(CB.arg, addr, value);
        consumeClock(clock);
    }

  private: // Internal functions & variables
    // bit table
    const unsigned char bits[8] = {0b00000001, 0b00000010, 0b00000100, 0b00001000, 0b00010000, 0b00100000, 0b01000000, 0b10000000};
    // flag setter
    inline void setFlagS() { reg.pair.F |= flagS(); }
    inline void setFlagZ() { reg.pair.F |= flagZ(); }
    inline void setFlagY() { reg.pair.F |= flagY(); }
    inline void setFlagH() { reg.pair.F |= flagH(); }
    inline void setFlagX() { reg.pair.F |= flagX(); }
    inline void setFlagPV() { reg.pair.F |= flagPV(); }
    inline void setFlagN() { reg.pair.F |= flagN(); }
    inline void setFlagC() { reg.pair.F |= flagC(); }
    inline void resetFlagS() { reg.pair.F &= ~flagS(); }
    inline void resetFlagZ() { reg.pair.F &= ~flagZ(); }
    inline void resetFlagY() { reg.pair.F &= ~flagY(); }
    inline void resetFlagH() { reg.pair.F &= ~flagH(); }
    inline void resetFlagX() { reg.pair.F &= ~flagX(); }
    inline void resetFlagPV() { reg.pair.F &= ~flagPV(); }
    inline void resetFlagN() { reg.pair.F &= ~flagN(); }
    inline void resetFlagC() { reg.pair.F &= ~flagC(); }
    inline void setFlagS(bool on) { on ? setFlagS() : resetFlagS(); }
    inline void setFlagZ(bool on) { on ? setFlagZ() : resetFlagZ(); }
    inline void setFlagY(bool on) { on ? setFlagY() : resetFlagY(); }
    inline void setFlagH(bool on) { on ? setFlagH() : resetFlagH(); }
    inline void setFlagX(bool on) { on ? setFlagX() : resetFlagX(); }
    inline void setFlagPV(bool on) { on ? setFlagPV() : resetFlagPV(); }
    inline void setFlagN(bool on) { on ? setFlagN() : resetFlagN(); }
    inline void setFlagC(bool on) { on ? setFlagC() : resetFlagC(); }

    inline void setFlagXY(unsigned char value)
    {
        setFlagX(value & flagX());
        setFlagY(value & flagY());
    }

    // flag checker
    inline bool isFlagS() { return reg.pair.F & flagS(); }
    inline bool isFlagZ() { return reg.pair.F & flagZ(); }
    inline bool isFlagH() { return reg.pair.F & flagH(); }
    inline bool isFlagPV() { return reg.pair.F & flagPV(); }
    inline bool isFlagN() { return reg.pair.F & flagN(); }
    inline bool isFlagC() { return reg.pair.F & flagC(); }

    enum class Condition {
        Z = 0x40,
        C = 0x01,
        PV = 0x04,
        S = 0x80,
        NZ = 0x4040,
        NC = 0x0101,
        NPV = 0x0404,
        NS = 0x8080,
    };

    inline bool checkConditionFlag(Condition c)
    {
        int ic = (int)c;
        return (ic & reg.pair.F) ^ ((ic & 0xFF00) >> 8);
    }

    inline unsigned char IFF1() { return 0b00000001; }
    inline unsigned char IFF2() { return 0b00000100; }
    inline unsigned char IFF_IRQ() { return 0b00100000; }
    inline unsigned char IFF_NMI() { return 0b01000000; }
    inline unsigned char IFF_HALT() { return 0b10000000; }

#ifndef Z80_DISABLE_BREAKPOINT
    class BreakPoint
    {
      public:
        unsigned short addr;
#ifdef Z80_NO_FUNCTIONAL
        void (*callback)(void*);
        BreakPoint(unsigned short addr_, void (*callback_)(void*))
#else
        std::function<void(void*)> callback;
        BreakPoint(unsigned short addr_, const std::function<void(void*)>& callback_)
#endif
        {
            this->addr = addr_;
            this->callback = callback_;
        }
    };

    class BreakOperand
    {
      public:
        int prefixNumber;
        unsigned char operandNumber;
#ifdef Z80_NO_FUNCTIONAL
        void (*callback)(void*, unsigned char*, int);
        BreakOperand(int prefixNumber_, unsigned char operandNumber_, void (*callback_)(void*, unsigned char*, int))
        {
            this->prefixNumber = prefixNumber_;
            this->operandNumber = operandNumber_;
            this->callback = callback_;
        }
#else
        std::function<void(void*, unsigned char*, int)> callback;
        BreakOperand(int prefixNumber_, unsigned char operandNumber_, const std::function<void(void*, unsigned char*, int)>& callback_)
        {
            this->prefixNumber = prefixNumber_;
            this->operandNumber = operandNumber_;
            this->callback = callback_;
        }
#endif
    };
#endif

#ifndef Z80_DISABLE_NESTCHECK
    class SimpleHandler
    {
      public:
#ifdef Z80_NO_FUNCTIONAL
        void (*callback)(void*);
        SimpleHandler(void (*callback_)(void*))
#else
        std::function<void(void*)> callback;
        SimpleHandler(const std::function<void(void*)>& callback_)
#endif
        {
            this->callback = callback_;
        }
    };

    inline void invokeReturnHandlers()
    {
        for (auto handler : this->CB.returnHandlers) {
            handler->callback(this->CB.arg);
        }
    }

    inline void invokeCallHandlers()
    {
        for (auto handler : this->CB.callHandlers) {
            handler->callback(this->CB.arg);
        }
    }
#endif

    struct Callback {
#ifdef Z80_NO_FUNCTIONAL
        unsigned char (*read)(void*, unsigned short);
        void (*write)(void*, unsigned short, unsigned char);
        unsigned char (*in)(void*, unsigned short);
        void (*out)(void*, unsigned short, unsigned char);
        void (*consumeClock)(void*, int);
#else
        std::function<unsigned char(void*, unsigned short)> read;
        std::function<void(void*, unsigned short, unsigned char)> write;
        std::function<unsigned char(void*, unsigned short)> in;
        std::function<void(void*, unsigned short, unsigned char)> out;
        std::function<void(void*, int)> consumeClock;
#endif

#ifndef Z80_UNSUPPORT_16BIT_PORT
        bool returnPortAs16Bits;
#endif

#ifndef Z80_DISABLE_DEBUG
#ifdef Z80_NO_FUNCTIONAL
        void (*debugMessage)(void*, const char*);
#else
        std::function<void(void*, const char*)> debugMessage;
#endif
        bool debugMessageEnabled;
#endif

#ifndef Z80_DISABLE_BREAKPOINT
        std::map<int, std::vector<BreakPoint*>*> breakPoints;
        std::map<int, std::vector<BreakOperand*>*> breakOperands;
#endif
#ifndef Z80_DISABLE_NESTCHECK
        std::vector<SimpleHandler*> returnHandlers;
        std::vector<SimpleHandler*> callHandlers;
#endif
        bool consumeClockEnabled;
        void* arg;
    } CB;

    bool requestBreakFlag;

#ifndef Z80_DISABLE_BREAKPOINT
    inline void checkBreakPoint()
    {
        auto it = CB.breakPoints.find(reg.PC);
        if (it == CB.breakPoints.end()) return;
        for (auto bp : *CB.breakPoints[reg.PC]) {
            bp->callback(CB.arg);
        }
    }

    inline void readFullOpcode(BreakOperand* operand, unsigned char* opcode, int* opcodeLength)
    {
        *opcodeLength = 0;
        switch (operand->prefixNumber) {
            case 0x00:
                opcode[0] = operand->operandNumber;
                *opcodeLength = opLength1[opcode[0]];
                for (int i = 1; i < *opcodeLength; i++) {
                    opcode[i] = readByte(reg.PC + i - 1, 0); // read without consume clocks
                }
                break;
            case 0xCB:
                opcode[0] = 0xCB;
                opcode[1] = operand->operandNumber;
                *opcodeLength = 2;
                break;
            case 0xED:
                opcode[0] = 0xED;
                opcode[1] = operand->operandNumber;
                *opcodeLength = opLengthED[opcode[1]];
                for (int i = 2; i < *opcodeLength; i++) {
                    opcode[i] = readByte(reg.PC + i - 2, 0); // read without consume clocks
                }
                break;
            case 0xDD:
                opcode[0] = 0xDD;
                opcode[1] = operand->operandNumber;
                *opcodeLength = opLengthIXY[opcode[1]];
                for (int i = 2; i < *opcodeLength; i++) {
                    opcode[i] = readByte(reg.PC + i - 2, 0); // read without consume clocks
                }
                break;
            case 0xFD:
                opcode[0] = 0xFD;
                opcode[1] = operand->operandNumber;
                *opcodeLength = opLengthIXY[opcode[1]];
                for (int i = 2; i < *opcodeLength; i++) {
                    opcode[i] = readByte(reg.PC + i - 2, 0); // read without consume clocks
                }
                break;
            case 0xDDCB:
                opcode[0] = 0xDD;
                opcode[1] = 0xCB;
                opcode[2] = operand->operandNumber;
                opcode[3] = readByte(reg.PC, 0);
                *opcodeLength = 4;
                break;
            case 0xFDCB:
                opcode[0] = 0xFD;
                opcode[1] = 0xCB;
                opcode[2] = operand->operandNumber;
                opcode[3] = readByte(reg.PC, 0);
                *opcodeLength = 4;
                break;
        }
    }

    inline void checkBreakOperand(int operandNumber)
    {
        if (CB.breakOperands.empty()) return;
        auto it = CB.breakOperands.find(operandNumber);
        if (it == CB.breakOperands.end()) return;
        unsigned char opcode[16];
        int opcodeLength = 16;
        bool first = true;
        for (auto bo : *CB.breakOperands[operandNumber]) {
            if (first) {
                readFullOpcode(bo, opcode, &opcodeLength);
                first = false;
            }
            bo->callback(CB.arg, opcode, opcodeLength);
        }
    }

    inline void checkBreakOperandCB(unsigned char operandNumber) { checkBreakOperand(0xCB00 | operandNumber); }
    inline void checkBreakOperandED(unsigned char operandNumber) { checkBreakOperand(0xED00 | operandNumber); }
    inline void checkBreakOperandIX(unsigned char operandNumber) { checkBreakOperand(0xDD00 | operandNumber); }
    inline void checkBreakOperandIY(unsigned char operandNumber) { checkBreakOperand(0xFD00 | operandNumber); }
    inline void checkBreakOperandIX4(unsigned char operandNumber) { checkBreakOperand(0xDDCB00 | operandNumber); }
    inline void checkBreakOperandIY4(unsigned char operandNumber) { checkBreakOperand(0xFDCB00 | operandNumber); }
#endif

#ifndef Z80_DISABLE_DEBUG
    inline void log(const char* format, ...)
    {
        char buf[1024];
        va_list args;
        va_start(args, format);
        vsnprintf(buf, sizeof(buf), format, args);
        va_end(args);
        CB.debugMessage(CB.arg, buf);
    }
#endif

    inline unsigned short getAF()
    {
        return make16BitsFromLE(reg.pair.F, reg.pair.A);
    }
    inline unsigned short getAF2() { return make16BitsFromLE(reg.back.F, reg.back.A); }
    inline unsigned short getBC() { return make16BitsFromLE(reg.pair.C, reg.pair.B); }
    inline unsigned short getBC2() { return make16BitsFromLE(reg.back.C, reg.back.B); }
    inline unsigned short getDE() { return make16BitsFromLE(reg.pair.E, reg.pair.D); }
    inline unsigned short getDE2() { return make16BitsFromLE(reg.back.E, reg.back.D); }
    inline unsigned short getHL() { return make16BitsFromLE(reg.pair.L, reg.pair.H); }
    inline unsigned short getHL2() { return make16BitsFromLE(reg.back.L, reg.back.H); }

    inline void setAF(unsigned short value) { splitTo8BitsPair(value, &reg.pair.A, &reg.pair.F); }
    inline void setAF2(unsigned short value) { splitTo8BitsPair(value, &reg.back.A, &reg.back.F); }
    inline void setBC(unsigned short value) { splitTo8BitsPair(value, &reg.pair.B, &reg.pair.C); }
    inline void setBC2(unsigned short value) { splitTo8BitsPair(value, &reg.back.B, &reg.back.C); }
    inline void setDE(unsigned short value) { splitTo8BitsPair(value, &reg.pair.D, &reg.pair.E); }
    inline void setDE2(unsigned short value) { splitTo8BitsPair(value, &reg.back.D, &reg.back.E); }
    inline void setHL(unsigned short value) { splitTo8BitsPair(value, &reg.pair.H, &reg.pair.L); }
    inline void setHL2(unsigned short value) { splitTo8BitsPair(value, &reg.back.H, &reg.back.L); }

    inline unsigned short getRP(unsigned char rp)
    {
        switch (rp & 0b11) {
            case 0b00: return getBC();
            case 0b01: return getDE();
            case 0b10: return getHL();
            default: return reg.SP;
        }
    }

    inline unsigned short getRPIX(unsigned char rp)
    {
        switch (rp & 0b11) {
            case 0b00: return getBC();
            case 0b01: return getDE();
            case 0b10: return reg.IX;
            default: return reg.SP;
        }
    }

    inline unsigned short getRPIY(unsigned char rp)
    {
        switch (rp & 0b11) {
            case 0b00: return getBC();
            case 0b01: return getDE();
            case 0b10: return reg.IY;
            default: return reg.SP;
        }
    }

    inline void setRP(unsigned char rp, unsigned short value)
    {
        switch (rp & 0b11) {
            case 0b00: setBC(value); break;
            case 0b01: setDE(value); break;
            case 0b10: setHL(value); break;
            default: reg.SP = value;
        }
    }

    inline unsigned char getIXH() { return (reg.IX & 0xFF00) >> 8; }
    inline unsigned char getIXL() { return reg.IX & 0x00FF; }
    inline unsigned char getIYH() { return (reg.IY & 0xFF00) >> 8; }
    inline unsigned char getIYL() { return reg.IY & 0x00FF; }
    inline unsigned char getPCH() { return (reg.PC & 0xFF00) >> 8; }
    inline unsigned char getPCL() { return reg.PC & 0x00FF; }
    inline void setPCH(unsigned char v) { reg.PC = (reg.PC & 0x00FF) + v * 256; }
    inline void setPCL(unsigned char v) { reg.PC = (reg.PC & 0xFF00) + v; }
    inline void setSPH(unsigned char v) { reg.SP = (reg.SP & 0x00FF) + v * 256; }
    inline void setSPL(unsigned char v) { reg.SP = (reg.SP & 0xFF00) + v; }
    inline void setIXH(unsigned char v) { reg.IX = (reg.IX & 0x00FF) + v * 256; }
    inline void setIXL(unsigned char v) { reg.IX = (reg.IX & 0xFF00) + v; }
    inline void setIYH(unsigned char v) { reg.IY = (reg.IY & 0x00FF) + v * 256; }
    inline void setIYL(unsigned char v) { reg.IY = (reg.IY & 0xFF00) + v; }

    inline bool isEvenNumberBits(unsigned char value)
    {
        int on = 0;
        int off = 0;
        value & 0b10000000 ? on++ : off++;
        value & 0b01000000 ? on++ : off++;
        value & 0b00100000 ? on++ : off++;
        value & 0b00010000 ? on++ : off++;
        value & 0b00001000 ? on++ : off++;
        value & 0b00000100 ? on++ : off++;
        value & 0b00000010 ? on++ : off++;
        value & 0b00000001 ? on++ : off++;
        return (on & 1) == 0;
    }

    inline void consumeClock(int hz)
    {
        reg.consumeClockCounter += hz;
#ifndef Z80_CALLBACK_PER_INSTRUCTION
#ifdef Z80_CALLBACK_WITHOUT_CHECK
        CB.consumeClock(CB.arg, hz);
#else
        if (CB.consumeClockEnabled && hz) CB.consumeClock(CB.arg, hz);
#endif
#endif
    }

    inline unsigned short getPort16WithB(unsigned char c) { return make16BitsFromLE(c, reg.pair.B); }
    inline unsigned short getPort16WithA(unsigned char c) { return make16BitsFromLE(c, reg.pair.A); }

    inline unsigned char inPortWithB(unsigned char port, int clock = 4)
    {
#ifdef Z80_UNSUPPORT_16BIT_PORT
        unsigned char byte = CB.in(CB.arg, port);
#else
        unsigned char byte = CB.in(CB.arg, CB.returnPortAs16Bits ? getPort16WithB(port) : port);
#endif
        consumeClock(clock);
        return byte;
    }

    inline unsigned char inPortWithA(unsigned char port, int clock = 4)
    {
#ifdef Z80_UNSUPPORT_16BIT_PORT
        unsigned char byte = CB.in(CB.arg, port);
#else
        unsigned char byte = CB.in(CB.arg, CB.returnPortAs16Bits ? getPort16WithA(port) : port);
#endif
        consumeClock(clock);
        return byte;
    }

    inline void outPortWithB(unsigned char port, unsigned char value, int clock = 4)
    {
#ifdef Z80_UNSUPPORT_16BIT_PORT
        CB.out(CB.arg, port, value);
#else
        CB.out(CB.arg, CB.returnPortAs16Bits ? getPort16WithB(port) : port, value);
#endif
        consumeClock(clock);
    }

    inline void outPortWithA(unsigned char port, unsigned char value, int clock = 4)
    {
#ifdef Z80_UNSUPPORT_16BIT_PORT
        CB.out(CB.arg, port, value);
#else
        CB.out(CB.arg, CB.returnPortAs16Bits ? getPort16WithA(port) : port, value);
#endif
        consumeClock(clock);
    }

    static inline void NOP_t(Z80* ctx)
    {
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] NOP", ctx->reg.PC - 1);
#endif
    }

    static inline void HALT(Z80* ctx)
    {
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] HALT", ctx->reg.PC - 1);
#endif
        ctx->reg.IFF |= ctx->IFF_HALT();
    }

    static inline void DI(Z80* ctx)
    {
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] DI", ctx->reg.PC - 1);
#endif
        ctx->reg.IFF &= ~(ctx->IFF1() | ctx->IFF2());
    }

    static inline void EI(Z80* ctx)
    {
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] EI", ctx->reg.PC - 1);
#endif
        ctx->reg.IFF |= ctx->IFF1() | ctx->IFF2();
        ctx->reg.execEI = 1;
    }

    static inline void IM0(Z80* ctx) { ctx->IM(0); }
    static inline void IM1(Z80* ctx) { ctx->IM(1); }
    static inline void IM2(Z80* ctx) { ctx->IM(2); }
    inline void IM(unsigned char interrptMode)
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] IM %d", reg.PC - 2, interrptMode);
#endif
        reg.interrupt &= 0b11111100;
        reg.interrupt |= interrptMode & 0b11;
    }

    static inline void LD_A_I_(Z80* ctx) { ctx->LD_A_I(); }
    inline void LD_A_I()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD A<$%02X>, I<$%02X>", reg.PC - 2, reg.pair.A, reg.I);
#endif
        reg.pair.A = reg.I;
        setFlagPV(reg.IFF & IFF2());
        consumeClock(1);
    }

    static inline void LD_I_A_(Z80* ctx) { ctx->LD_I_A(); }
    inline void LD_I_A()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD I<$%02X>, A<$%02X>", reg.PC - 2, reg.I, reg.pair.A);
#endif
        reg.I = reg.pair.A;
        consumeClock(1);
    }

    static inline void LD_A_R_(Z80* ctx) { ctx->LD_A_R(); }
    inline void LD_A_R()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD A<$%02X>, R<$%02X>", reg.PC - 2, reg.pair.A, reg.R);
#endif
        reg.pair.A = reg.R;
        setFlagPV(reg.IFF & IFF1());
        consumeClock(1);
    }

    static inline void LD_R_A_(Z80* ctx) { ctx->LD_R_A(); }
    inline void LD_R_A()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD R<$%02X>, A<$%02X>", reg.PC - 2, reg.R, reg.pair.A);
#endif
        reg.R = reg.pair.A;
        consumeClock(1);
    }

    static inline void OP_CB(Z80* ctx)
    {
        unsigned char operandNumber = ctx->fetch(4 + ctx->wtc.fetchM);
#ifndef Z80_DISABLE_BREAKPOINT
        ctx->checkBreakOperandCB(operandNumber);
#endif
        ctx->opSetCB[operandNumber](ctx);
    }

    static inline void OP_ED(Z80* ctx)
    {
        unsigned char operandNumber = ctx->fetch(4 + ctx->wtc.fetchM);
#ifndef Z80_NO_EXCEPTION
        if (!ctx->opSetED[operandNumber]) {
            char buf[80];
            snprintf(buf, sizeof(buf), "detect an unknown operand (ED,%02X)", operandNumber);
            throw std::runtime_error(buf);
        }
#endif
#ifndef Z80_DISABLE_BREAKPOINT
        ctx->checkBreakOperandED(operandNumber);
#endif
        ctx->opSetED[operandNumber](ctx);
    }

    static inline void OP_IX(Z80* ctx)
    {
        unsigned char operandNumber = ctx->fetch(4 + ctx->wtc.fetchM);
#ifndef Z80_NO_EXCEPTION
        if (!ctx->opSetIX[operandNumber]) {
            char buf[80];
            snprintf(buf, sizeof(buf), "detect an unknown operand (DD,%02X)", operandNumber);
            throw std::runtime_error(buf);
        }
#endif
#ifndef Z80_DISABLE_BREAKPOINT
        ctx->checkBreakOperandIX(operandNumber);
#endif
        ctx->opSetIX[operandNumber](ctx);
    }

    static inline void OP_IY(Z80* ctx)
    {
        unsigned char operandNumber = ctx->fetch(4 + ctx->wtc.fetchM);
#ifndef Z80_NO_EXCEPTION
        if (!ctx->opSetIY[operandNumber]) {
            char buf[80];
            snprintf(buf, sizeof(buf), "detect an unknown operand (FD,%02X)", operandNumber);
            throw std::runtime_error(buf);
        }
#endif
#ifndef Z80_DISABLE_BREAKPOINT
        ctx->checkBreakOperandIY(operandNumber);
#endif
        ctx->opSetIY[operandNumber](ctx);
    }

    static inline void OP_IX4(Z80* ctx)
    {
        signed char op3 = (signed char)ctx->fetch(4);
        unsigned char op4 = ctx->fetch(4);
#ifndef Z80_DISABLE_BREAKPOINT
        ctx->checkBreakOperandIX4(op4);
#endif
        ctx->opSetIX4[op4](ctx, op3);
    }

    static inline void OP_IY4(Z80* ctx)
    {
        signed char op3 = (signed char)ctx->fetch(4);
        unsigned char op4 = ctx->fetch(4);
#ifndef Z80_DISABLE_BREAKPOINT
        ctx->checkBreakOperandIY4(op4);
#endif
        ctx->opSetIY4[op4](ctx, op3);
    }

    // Load location (HL) with value n
    static inline void LD_HL_N(Z80* ctx)
    {
        unsigned char n = ctx->fetch(3);
        unsigned short hl = ctx->getHL();
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] LD (HL<$%04X>), $%02X", ctx->reg.PC - 2, hl, n);
#endif
        ctx->writeByte(hl, n, 3);
    }

    // Load Acc. wth location (BC)
    static inline void LD_A_BC(Z80* ctx)
    {
        unsigned short addr = ctx->getBC();
        unsigned char n = ctx->readByte(addr, 3);
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] LD A, (BC<$%02X%02X>) = $%02X", ctx->reg.PC - 1, ctx->reg.pair.B, ctx->reg.pair.C, n);
#endif
        ctx->reg.pair.A = n;
    }

    // Load Acc. wth location (DE)
    static inline void LD_A_DE(Z80* ctx)
    {
        unsigned short addr = ctx->getDE();
        unsigned char n = ctx->readByte(addr, 3);
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] LD A, (DE<$%02X%02X>) = $%02X", ctx->reg.PC - 1, ctx->reg.pair.D, ctx->reg.pair.E, n);
#endif
        ctx->reg.pair.A = n;
    }

    // Load Acc. wth location (nn)
    static inline void LD_A_NN(Z80* ctx)
    {
        unsigned char l = ctx->fetch(3);
        unsigned char h = ctx->fetch(3);
        unsigned short addr = ctx->make16BitsFromLE(l, h);
        unsigned char n = ctx->readByte(addr, 3);
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] LD A, ($%04X) = $%02X", ctx->reg.PC - 3, addr, n);
#endif
        ctx->reg.pair.A = n;
    }

    // Load location (BC) wtih Acc.
    static inline void LD_BC_A(Z80* ctx)
    {
        unsigned short addr = ctx->getBC();
        unsigned char n = ctx->reg.pair.A;
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] LD (BC<$%02X%02X>), A<$%02X>", ctx->reg.PC - 1, ctx->reg.pair.B, ctx->reg.pair.C, n);
#endif
        ctx->writeByte(addr, n, 3);
    }

    // Load location (DE) wtih Acc.
    static inline void LD_DE_A(Z80* ctx)
    {
        unsigned short addr = ctx->getDE();
        unsigned char n = ctx->reg.pair.A;
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] LD (DE<$%02X%02X>), A<$%02X>", ctx->reg.PC - 1, ctx->reg.pair.D, ctx->reg.pair.E, n);
#endif
        ctx->writeByte(addr, n, 3);
    }

    // Load location (nn) with Acc.
    static inline void LD_NN_A(Z80* ctx)
    {
        unsigned char l = ctx->fetch(3);
        unsigned char h = ctx->fetch(3);
        unsigned short addr = ctx->make16BitsFromLE(l, h);
        unsigned char n = ctx->reg.pair.A;
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] LD ($%04X), A<$%02X>", ctx->reg.PC - 3, addr, n);
#endif
        ctx->writeByte(addr, n, 3);
    }

    // Load HL with location (nn).
    static inline void LD_HL_ADDR(Z80* ctx)
    {
        unsigned char l = ctx->fetch(3);
        unsigned char h = ctx->fetch(3);
        unsigned short addr = ctx->make16BitsFromLE(l, h);
#ifndef Z80_DISABLE_DEBUG
        unsigned short hl = ctx->getHL();
#endif
        ctx->reg.pair.L = ctx->readByte(addr, 3);
        ctx->reg.pair.H = ctx->readByte(addr + 1, 3);
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] LD HL<$%04X>, ($%04X) = $%04X", ctx->reg.PC - 3, hl, addr, ctx->getHL());
#endif
    }

    // Load location (nn) with HL.
    static inline void LD_ADDR_HL(Z80* ctx)
    {
        unsigned char l = ctx->fetch(3);
        unsigned char h = ctx->fetch(3);
        unsigned short addr = ctx->make16BitsFromLE(l, h);
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] LD ($%04X), %s", ctx->reg.PC - 3, addr, ctx->registerPairDump(0b10));
#endif
        ctx->writeByte(addr, ctx->reg.pair.L, 3);
        ctx->writeByte(addr + 1, ctx->reg.pair.H, 3);
    }

    // Load SP with HL.
    static inline void LD_SP_HL(Z80* ctx)
    {
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] LD %s, HL<$%04X>", ctx->reg.PC - 1, ctx->registerPairDump(0b11), ctx->getHL());
#endif
        ctx->reg.SP = ctx->getHL();
        ctx->consumeClock(2);
    }

    // Exchange H and L with D and E
    static inline void EX_DE_HL(Z80* ctx)
    {
        unsigned short de = ctx->getDE();
        unsigned short hl = ctx->getHL();
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] EX %s, %s", ctx->reg.PC - 1, ctx->registerPairDump(0b01), ctx->registerPairDump(0b10));
#endif
        ctx->setDE(hl);
        ctx->setHL(de);
    }

    // Exchange A and F with A' and F'
    static inline void EX_AF_AF2(Z80* ctx)
    {
        unsigned short af = ctx->getAF();
        unsigned short af2 = ctx->getAF2();
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] EX AF<$%02X%02X>, AF'<$%02X%02X>", ctx->reg.PC - 1, ctx->reg.pair.A, ctx->reg.pair.F, ctx->reg.back.A, ctx->reg.back.F);
#endif
        ctx->setAF(af2);
        ctx->setAF2(af);
    }

    static inline void EX_SP_HL(Z80* ctx)
    {
#ifndef Z80_DISABLE_DEBUG
        unsigned short sp = ctx->reg.SP;
#endif
        unsigned char l = ctx->pop(4);
        unsigned char h = ctx->pop(4);
#ifndef Z80_DISABLE_DEBUG
        unsigned short hl = ctx->getHL();
        if (ctx->isDebug()) ctx->log("[%04X] EX (SP<$%04X>) = $%02X%02X, HL<$%04X>", ctx->reg.PC - 1, sp, h, l, hl);
#endif
        ctx->push(ctx->reg.pair.H, 4);
        ctx->reg.pair.H = h;
        ctx->push(ctx->reg.pair.L, 3);
        ctx->reg.pair.L = l;
    }

    static inline void EXX(Z80* ctx)
    {
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] EXX", ctx->reg.PC - 1);
#endif
        unsigned short bc = ctx->getBC();
        unsigned short bc2 = ctx->getBC2();
        unsigned short de = ctx->getDE();
        unsigned short de2 = ctx->getDE2();
        unsigned short hl = ctx->getHL();
        unsigned short hl2 = ctx->getHL2();
        ctx->setBC(bc2);
        ctx->setBC2(bc);
        ctx->setDE(de2);
        ctx->setDE2(de);
        ctx->setHL(hl2);
        ctx->setHL2(hl);
    }

    inline void push(unsigned char value, int clocks)
    {
        reg.SP--;
        writeByte(reg.SP, value, clocks);
    }

    inline unsigned char pop(int clocks)
    {
        unsigned char value = readByte(reg.SP, clocks);
        reg.SP++;
        return value;
    }

    static inline void PUSH_AF(Z80* ctx)
    {
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] PUSH AF<$%02X%02X> <SP:$%04X>", ctx->reg.PC - 1, ctx->reg.pair.A, ctx->reg.pair.F, ctx->reg.SP);
#endif
        ctx->push(ctx->reg.pair.A, 4);
        ctx->push(ctx->reg.pair.F, 3);
    }

    static inline void POP_AF(Z80* ctx)
    {
        ctx->reg.pair.F = ctx->pop(3);
        ctx->reg.pair.A = ctx->pop(3);
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] POP AF <SP:$%04X> = $%04X", ctx->reg.PC - 1, ctx->reg.SP - 2, ctx->getAF());
#endif
    }

    unsigned char* registerPointerTable[8] = {&reg.pair.B, &reg.pair.C, &reg.pair.D, &reg.pair.E, &reg.pair.H, &reg.pair.L, &reg.pair.F, &reg.pair.A};
    inline unsigned char* getRegisterPointer(unsigned char r) { return registerPointerTable[r]; }
    inline unsigned char getRegister(unsigned char r) { return *registerPointerTable[r]; }

#ifndef Z80_DISABLE_DEBUG
    inline char* registerDump(unsigned char r)
    {
        static char A[16];
        static char B[16];
        static char C[16];
        static char D[16];
        static char E[16];
        static char H[16];
        static char L[16];
        static char F[16];
        static char unknown[2];
        switch (r & 0b111) {
            case 0b111: snprintf(A, sizeof(A), "A<$%02X>", reg.pair.A); return A;
            case 0b000: snprintf(B, sizeof(B), "B<$%02X>", reg.pair.B); return B;
            case 0b001: snprintf(C, sizeof(C), "C<$%02X>", reg.pair.C); return C;
            case 0b010: snprintf(D, sizeof(D), "D<$%02X>", reg.pair.D); return D;
            case 0b011: snprintf(E, sizeof(E), "E<$%02X>", reg.pair.E); return E;
            case 0b100: snprintf(H, sizeof(H), "H<$%02X>", reg.pair.H); return H;
            case 0b101: snprintf(L, sizeof(L), "L<$%02X>", reg.pair.L); return L;
            case 0b110: snprintf(F, sizeof(F), "F<$%02X>", reg.pair.F); return F;
        }
        unknown[0] = '?';
        unknown[1] = '\0';
        return unknown;
    }

    inline char* conditionDump(Condition c)
    {
        static char CN[4];
        switch (c) {
            case Condition::NZ: strcpy(CN, "NZ"); break;
            case Condition::Z: strcpy(CN, "Z"); break;
            case Condition::NC: strcpy(CN, "NC"); break;
            case Condition::C: strcpy(CN, "C"); break;
            case Condition::NPV: strcpy(CN, "PO"); break;
            case Condition::PV: strcpy(CN, "PE"); break;
            case Condition::NS: strcpy(CN, "P"); break;
            case Condition::S: strcpy(CN, "M"); break;
        }
        return CN;
    }

    inline char* relativeDump(unsigned short pc, signed char e)
    {
        static char buf[80];
        if (e < 0) {
            int ee = -e;
            ee -= 2;
            snprintf(buf, sizeof(buf), "$%04X - %d = $%04X", pc, ee, pc + e + 2);
        } else {
            snprintf(buf, sizeof(buf), "$%04X + %d = $%04X", pc, e + 2, pc + e + 2);
        }
        return buf;
    }

    inline char* registerDump2(unsigned char r)
    {
        static char A[16];
        static char B[16];
        static char C[16];
        static char D[16];
        static char E[16];
        static char H[16];
        static char L[16];
        static char unknown[2] = "?";
        switch (r) {
            case 0b111: snprintf(A, sizeof(A), "A'<$%02X>", reg.back.A); return A;
            case 0b000: snprintf(B, sizeof(B), "B'<$%02X>", reg.back.B); return B;
            case 0b001: snprintf(C, sizeof(C), "C'<$%02X>", reg.back.C); return C;
            case 0b010: snprintf(D, sizeof(D), "D'<$%02X>", reg.back.D); return D;
            case 0b011: snprintf(E, sizeof(E), "E'<$%02X>", reg.back.E); return E;
            case 0b100: snprintf(H, sizeof(H), "H'<$%02X>", reg.back.H); return H;
            case 0b101: snprintf(L, sizeof(L), "L'<$%02X>", reg.back.L); return L;
            default: return unknown;
        }
    }

    inline char* registerPairDump(unsigned char ptn)
    {
        static char BC[16];
        static char DE[16];
        static char HL[16];
        static char SP[16];
        static char unknown[2] = "?";
        switch (ptn & 0b11) {
            case 0b00: snprintf(BC, sizeof(BC), "BC<$%02X%02X>", reg.pair.B, reg.pair.C); return BC;
            case 0b01: snprintf(DE, sizeof(DE), "DE<$%02X%02X>", reg.pair.D, reg.pair.E); return DE;
            case 0b10: snprintf(HL, sizeof(HL), "HL<$%02X%02X>", reg.pair.H, reg.pair.L); return HL;
            case 0b11: snprintf(SP, sizeof(SP), "SP<$%04X>", reg.SP); return SP;
            default: return unknown;
        }
    }

    inline char* registerPairDumpIX(unsigned char ptn)
    {
        static char BC[16];
        static char DE[16];
        static char IX[16];
        static char SP[16];
        static char unknown[2] = "?";
        switch (ptn & 0b11) {
            case 0b00: snprintf(BC, sizeof(BC), "BC<$%02X%02X>", reg.pair.B, reg.pair.C); return BC;
            case 0b01: snprintf(DE, sizeof(DE), "DE<$%02X%02X>", reg.pair.D, reg.pair.E); return DE;
            case 0b10: snprintf(IX, sizeof(IX), "IX<$%04X>", reg.IX); return IX;
            case 0b11: snprintf(SP, sizeof(SP), "SP<$%04X>", reg.SP); return SP;
            default: return unknown;
        }
    }

    inline char* registerPairDumpIY(unsigned char ptn)
    {
        static char BC[16];
        static char DE[16];
        static char IY[16];
        static char SP[16];
        static char unknown[2] = "?";
        switch (ptn & 0b11) {
            case 0b00: snprintf(BC, sizeof(BC), "BC<$%02X%02X>", reg.pair.B, reg.pair.C); return BC;
            case 0b01: snprintf(DE, sizeof(DE), "DE<$%02X%02X>", reg.pair.D, reg.pair.E); return DE;
            case 0b10: snprintf(IY, sizeof(IY), "IY<$%04X>", reg.IY); return IY;
            case 0b11: snprintf(SP, sizeof(SP), "SP<$%04X>", reg.SP); return SP;
            default: return unknown;
        }
    }
#endif

    // Load Reg. r1 with Reg. r2
    static inline void LD_B_B(Z80* ctx) { ctx->LD_R1_R2(0b000, 0b000); }
    static inline void LD_B_C(Z80* ctx) { ctx->LD_R1_R2(0b000, 0b001); }
    static inline void LD_B_D(Z80* ctx) { ctx->LD_R1_R2(0b000, 0b010); }
    static inline void LD_B_E(Z80* ctx) { ctx->LD_R1_R2(0b000, 0b011); }
    static inline void LD_B_B_2(Z80* ctx) { ctx->LD_R1_R2(0b000, 0b000, 2); }
    static inline void LD_B_C_2(Z80* ctx) { ctx->LD_R1_R2(0b000, 0b001, 2); }
    static inline void LD_B_D_2(Z80* ctx) { ctx->LD_R1_R2(0b000, 0b010, 2); }
    static inline void LD_B_E_2(Z80* ctx) { ctx->LD_R1_R2(0b000, 0b011, 2); }
    static inline void LD_B_H(Z80* ctx) { ctx->LD_R1_R2(0b000, 0b100); }
    static inline void LD_B_L(Z80* ctx) { ctx->LD_R1_R2(0b000, 0b101); }
    static inline void LD_B_A(Z80* ctx) { ctx->LD_R1_R2(0b000, 0b111); }
    static inline void LD_C_B(Z80* ctx) { ctx->LD_R1_R2(0b001, 0b000); }
    static inline void LD_C_C(Z80* ctx) { ctx->LD_R1_R2(0b001, 0b001); }
    static inline void LD_C_D(Z80* ctx) { ctx->LD_R1_R2(0b001, 0b010); }
    static inline void LD_C_E(Z80* ctx) { ctx->LD_R1_R2(0b001, 0b011); }
    static inline void LD_B_A_2(Z80* ctx) { ctx->LD_R1_R2(0b000, 0b111, 2); }
    static inline void LD_C_B_2(Z80* ctx) { ctx->LD_R1_R2(0b001, 0b000, 2); }
    static inline void LD_C_C_2(Z80* ctx) { ctx->LD_R1_R2(0b001, 0b001, 2); }
    static inline void LD_C_D_2(Z80* ctx) { ctx->LD_R1_R2(0b001, 0b010, 2); }
    static inline void LD_C_E_2(Z80* ctx) { ctx->LD_R1_R2(0b001, 0b011, 2); }
    static inline void LD_C_H(Z80* ctx) { ctx->LD_R1_R2(0b001, 0b100); }
    static inline void LD_C_L(Z80* ctx) { ctx->LD_R1_R2(0b001, 0b101); }
    static inline void LD_C_A(Z80* ctx) { ctx->LD_R1_R2(0b001, 0b111); }
    static inline void LD_D_B(Z80* ctx) { ctx->LD_R1_R2(0b010, 0b000); }
    static inline void LD_D_C(Z80* ctx) { ctx->LD_R1_R2(0b010, 0b001); }
    static inline void LD_D_D(Z80* ctx) { ctx->LD_R1_R2(0b010, 0b010); }
    static inline void LD_D_E(Z80* ctx) { ctx->LD_R1_R2(0b010, 0b011); }
    static inline void LD_C_A_2(Z80* ctx) { ctx->LD_R1_R2(0b001, 0b111, 2); }
    static inline void LD_D_B_2(Z80* ctx) { ctx->LD_R1_R2(0b010, 0b000, 2); }
    static inline void LD_D_C_2(Z80* ctx) { ctx->LD_R1_R2(0b010, 0b001, 2); }
    static inline void LD_D_D_2(Z80* ctx) { ctx->LD_R1_R2(0b010, 0b010, 2); }
    static inline void LD_D_E_2(Z80* ctx) { ctx->LD_R1_R2(0b010, 0b011, 2); }
    static inline void LD_D_H(Z80* ctx) { ctx->LD_R1_R2(0b010, 0b100); }
    static inline void LD_D_L(Z80* ctx) { ctx->LD_R1_R2(0b010, 0b101); }
    static inline void LD_D_A(Z80* ctx) { ctx->LD_R1_R2(0b010, 0b111); }
    static inline void LD_E_B(Z80* ctx) { ctx->LD_R1_R2(0b011, 0b000); }
    static inline void LD_E_C(Z80* ctx) { ctx->LD_R1_R2(0b011, 0b001); }
    static inline void LD_E_D(Z80* ctx) { ctx->LD_R1_R2(0b011, 0b010); }
    static inline void LD_E_E(Z80* ctx) { ctx->LD_R1_R2(0b011, 0b011); }
    static inline void LD_D_A_2(Z80* ctx) { ctx->LD_R1_R2(0b010, 0b111, 2); }
    static inline void LD_E_B_2(Z80* ctx) { ctx->LD_R1_R2(0b011, 0b000, 2); }
    static inline void LD_E_C_2(Z80* ctx) { ctx->LD_R1_R2(0b011, 0b001, 2); }
    static inline void LD_E_D_2(Z80* ctx) { ctx->LD_R1_R2(0b011, 0b010, 2); }
    static inline void LD_E_E_2(Z80* ctx) { ctx->LD_R1_R2(0b011, 0b011, 2); }
    static inline void LD_E_H(Z80* ctx) { ctx->LD_R1_R2(0b011, 0b100); }
    static inline void LD_E_L(Z80* ctx) { ctx->LD_R1_R2(0b011, 0b101); }
    static inline void LD_E_A(Z80* ctx) { ctx->LD_R1_R2(0b011, 0b111); }
    static inline void LD_E_A_2(Z80* ctx) { ctx->LD_R1_R2(0b011, 0b111, 2); }
    static inline void LD_H_B(Z80* ctx) { ctx->LD_R1_R2(0b100, 0b000); }
    static inline void LD_H_C(Z80* ctx) { ctx->LD_R1_R2(0b100, 0b001); }
    static inline void LD_H_D(Z80* ctx) { ctx->LD_R1_R2(0b100, 0b010); }
    static inline void LD_H_E(Z80* ctx) { ctx->LD_R1_R2(0b100, 0b011); }
    static inline void LD_H_H(Z80* ctx) { ctx->LD_R1_R2(0b100, 0b100); }
    static inline void LD_H_L(Z80* ctx) { ctx->LD_R1_R2(0b100, 0b101); }
    static inline void LD_H_A(Z80* ctx) { ctx->LD_R1_R2(0b100, 0b111); }
    static inline void LD_L_B(Z80* ctx) { ctx->LD_R1_R2(0b101, 0b000); }
    static inline void LD_L_C(Z80* ctx) { ctx->LD_R1_R2(0b101, 0b001); }
    static inline void LD_L_D(Z80* ctx) { ctx->LD_R1_R2(0b101, 0b010); }
    static inline void LD_L_E(Z80* ctx) { ctx->LD_R1_R2(0b101, 0b011); }
    static inline void LD_L_H(Z80* ctx) { ctx->LD_R1_R2(0b101, 0b100); }
    static inline void LD_L_L(Z80* ctx) { ctx->LD_R1_R2(0b101, 0b101); }
    static inline void LD_L_A(Z80* ctx) { ctx->LD_R1_R2(0b101, 0b111); }
    static inline void LD_A_B(Z80* ctx) { ctx->LD_R1_R2(0b111, 0b000); }
    static inline void LD_A_C(Z80* ctx) { ctx->LD_R1_R2(0b111, 0b001); }
    static inline void LD_A_D(Z80* ctx) { ctx->LD_R1_R2(0b111, 0b010); }
    static inline void LD_A_E(Z80* ctx) { ctx->LD_R1_R2(0b111, 0b011); }
    static inline void LD_A_B_2(Z80* ctx) { ctx->LD_R1_R2(0b111, 0b000, 2); }
    static inline void LD_A_C_2(Z80* ctx) { ctx->LD_R1_R2(0b111, 0b001, 2); }
    static inline void LD_A_D_2(Z80* ctx) { ctx->LD_R1_R2(0b111, 0b010, 2); }
    static inline void LD_A_E_2(Z80* ctx) { ctx->LD_R1_R2(0b111, 0b011, 2); }
    static inline void LD_A_H(Z80* ctx) { ctx->LD_R1_R2(0b111, 0b100); }
    static inline void LD_A_L(Z80* ctx) { ctx->LD_R1_R2(0b111, 0b101); }
    static inline void LD_A_A(Z80* ctx) { ctx->LD_R1_R2(0b111, 0b111); }
    static inline void LD_A_A_2(Z80* ctx) { ctx->LD_R1_R2(0b111, 0b111, 2); }
    inline void LD_R1_R2(unsigned char r1, unsigned char r2, int counter = 1)
    {
        unsigned char* r1p = getRegisterPointer(r1);
        unsigned char* r2p = getRegisterPointer(r2);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD %s, %s", reg.PC - counter, registerDump(r1), registerDump(r2));
#endif
        if (r1p && r2p) *r1p = *r2p;
    }

    // Load Reg. r with value n
    static inline void LD_A_N(Z80* ctx) { ctx->LD_R_N(0b111); }
    static inline void LD_B_N(Z80* ctx) { ctx->LD_R_N(0b000); }
    static inline void LD_C_N(Z80* ctx) { ctx->LD_R_N(0b001); }
    static inline void LD_D_N(Z80* ctx) { ctx->LD_R_N(0b010); }
    static inline void LD_E_N(Z80* ctx) { ctx->LD_R_N(0b011); }
    static inline void LD_H_N(Z80* ctx) { ctx->LD_R_N(0b100); }
    static inline void LD_L_N(Z80* ctx) { ctx->LD_R_N(0b101); }
    static inline void LD_A_N_3(Z80* ctx) { ctx->LD_R_N(0b111, 3); }
    static inline void LD_B_N_3(Z80* ctx) { ctx->LD_R_N(0b000, 3); }
    static inline void LD_C_N_3(Z80* ctx) { ctx->LD_R_N(0b001, 3); }
    static inline void LD_D_N_3(Z80* ctx) { ctx->LD_R_N(0b010, 3); }
    static inline void LD_E_N_3(Z80* ctx) { ctx->LD_R_N(0b011, 3); }
    inline void LD_R_N(unsigned char r, int pc = 2)
    {
        unsigned char* rp = getRegisterPointer(r);
        unsigned char n = fetch(3);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD %s, $%02X", reg.PC - pc, registerDump(r), n);
#endif
        if (rp) *rp = n;
    }

    // Load Reg. IX(high) with value n
    static inline void LD_IXH_N_(Z80* ctx) { ctx->LD_IXH_N(); }
    inline void LD_IXH_N()
    {
        unsigned char n = fetch(3);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD IXH, $%02X", reg.PC - 3, n);
#endif
        setIXH(n);
    }

    // Load Reg. IX(high) with value Reg.
    static inline void LD_IXH_A(Z80* ctx) { ctx->LD_IXH_R(0b111); }
    static inline void LD_IXH_B(Z80* ctx) { ctx->LD_IXH_R(0b000); }
    static inline void LD_IXH_C(Z80* ctx) { ctx->LD_IXH_R(0b001); }
    static inline void LD_IXH_D(Z80* ctx) { ctx->LD_IXH_R(0b010); }
    static inline void LD_IXH_E(Z80* ctx) { ctx->LD_IXH_R(0b011); }
    inline void LD_IXH_R(unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD IXH, %s", reg.PC - 2, registerDump(r));
#endif
        setIXH(*rp);
    }

    // Load Reg. IX(high) with value IX(high)
    static inline void LD_IXH_IXH_(Z80* ctx) { ctx->LD_IXH_IXH(); }
    inline void LD_IXH_IXH()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD IXH, IXH<$%02X>", reg.PC - 2, getIXH());
#endif
    }

    // Load Reg. IX(high) with value IX(low)
    static inline void LD_IXH_IXL_(Z80* ctx) { ctx->LD_IXH_IXL(); }
    inline void LD_IXH_IXL()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD IXH, IXL<$%02X>", reg.PC - 2, getIXL());
#endif
        setIXH(getIXL());
    }

    // Load Reg. IX(low) with value n
    static inline void LD_IXL_N_(Z80* ctx) { ctx->LD_IXL_N(); }
    inline void LD_IXL_N()
    {
        unsigned char n = fetch(3);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD IXL, $%02X", reg.PC - 3, n);
#endif
        setIXL(n);
    }

    // Load Reg. IX(low) with value Reg.
    static inline void LD_IXL_A(Z80* ctx) { ctx->LD_IXL_R(0b111); }
    static inline void LD_IXL_B(Z80* ctx) { ctx->LD_IXL_R(0b000); }
    static inline void LD_IXL_C(Z80* ctx) { ctx->LD_IXL_R(0b001); }
    static inline void LD_IXL_D(Z80* ctx) { ctx->LD_IXL_R(0b010); }
    static inline void LD_IXL_E(Z80* ctx) { ctx->LD_IXL_R(0b011); }
    inline void LD_IXL_R(unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD IXL, %s", reg.PC - 2, registerDump(r));
#endif
        setIXL(*rp);
    }

    // Load Reg. IX(low) with value IX(high)
    static inline void LD_IXL_IXH_(Z80* ctx) { ctx->LD_IXL_IXH(); }
    inline void LD_IXL_IXH()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD IXL, IXH<$%02X>", reg.PC - 2, getIXH());
#endif
        setIXL(getIXH());
    }

    // Load Reg. IX(low) with value IX(low)
    static inline void LD_IXL_IXL_(Z80* ctx) { ctx->LD_IXL_IXL(); }
    inline void LD_IXL_IXL()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD IXL, IXL<$%02X>", reg.PC - 2, getIXL());
#endif
    }

    // Load Reg. IY(high) with value n
    static inline void LD_IYH_N_(Z80* ctx) { ctx->LD_IYH_N(); }
    inline void LD_IYH_N()
    {
        unsigned char n = fetch(3);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD IYH, $%02X", reg.PC - 3, n);
#endif
        setIYH(n);
    }

    // Load Reg. IY(high) with value Reg.
    static inline void LD_IYH_A(Z80* ctx) { ctx->LD_IYH_R(0b111); }
    static inline void LD_IYH_B(Z80* ctx) { ctx->LD_IYH_R(0b000); }
    static inline void LD_IYH_C(Z80* ctx) { ctx->LD_IYH_R(0b001); }
    static inline void LD_IYH_D(Z80* ctx) { ctx->LD_IYH_R(0b010); }
    static inline void LD_IYH_E(Z80* ctx) { ctx->LD_IYH_R(0b011); }
    inline void LD_IYH_R(unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD IYH, %s", reg.PC - 2, registerDump(r));
#endif
        setIYH(*rp);
    }

    // Load Reg. IY(high) with value IY(high)
    static inline void LD_IYH_IYH_(Z80* ctx) { ctx->LD_IYH_IYH(); }
    inline void LD_IYH_IYH()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD IYH, IYH<$%02X>", reg.PC - 2, getIYH());
#endif
    }

    // Load Reg. IY(high) with value IY(low)
    static inline void LD_IYH_IYL_(Z80* ctx) { ctx->LD_IYH_IYL(); }
    inline void LD_IYH_IYL()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD IYH, IYL<$%02X>", reg.PC - 2, getIYL());
#endif
        setIYH(getIYL());
    }

    // Load Reg. IY(low) with value n
    static inline void LD_IYL_N_(Z80* ctx) { ctx->LD_IYL_N(); }
    inline void LD_IYL_N()
    {
        unsigned char n = fetch(3);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD IYL, $%02X", reg.PC - 3, n);
#endif
        setIYL(n);
    }

    // Load Reg. IY(low) with value Reg.
    static inline void LD_IYL_A(Z80* ctx) { ctx->LD_IYL_R(0b111); }
    static inline void LD_IYL_B(Z80* ctx) { ctx->LD_IYL_R(0b000); }
    static inline void LD_IYL_C(Z80* ctx) { ctx->LD_IYL_R(0b001); }
    static inline void LD_IYL_D(Z80* ctx) { ctx->LD_IYL_R(0b010); }
    static inline void LD_IYL_E(Z80* ctx) { ctx->LD_IYL_R(0b011); }
    inline void LD_IYL_R(unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD IYL, %s", reg.PC - 2, registerDump(r));
#endif
        setIYL(*rp);
    }

    // Load Reg. IY(low) with value IY(high)
    static inline void LD_IYL_IYH_(Z80* ctx) { ctx->LD_IYL_IYH(); }
    inline void LD_IYL_IYH()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD IYL, IYH<$%02X>", reg.PC - 2, getIYH());
#endif
        setIYL(getIYH());
    }

    // Load Reg. IY(low) with value IY(low)
    static inline void LD_IYL_IYL_(Z80* ctx) { ctx->LD_IYL_IYL(); }
    inline void LD_IYL_IYL()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD IYL, IYL<$%02X>", reg.PC - 2, getIYL());
#endif
    }

    // Load Reg. r with location (HL)
    static inline void LD_B_HL(Z80* ctx) { ctx->LD_R_HL(0b000); }
    static inline void LD_C_HL(Z80* ctx) { ctx->LD_R_HL(0b001); }
    static inline void LD_D_HL(Z80* ctx) { ctx->LD_R_HL(0b010); }
    static inline void LD_E_HL(Z80* ctx) { ctx->LD_R_HL(0b011); }
    static inline void LD_H_HL(Z80* ctx) { ctx->LD_R_HL(0b100); }
    static inline void LD_L_HL(Z80* ctx) { ctx->LD_R_HL(0b101); }
    static inline void LD_A_HL(Z80* ctx) { ctx->LD_R_HL(0b111); }
    inline void LD_R_HL(unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
        unsigned char n = readByte(getHL(), 3);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD %s, (%s) = $%02X", reg.PC - 1, registerDump(r), registerPairDump(0b10), n);
#endif
        if (rp) *rp = n;
    }

    // Load Reg. r with location (IX+d)
    static inline void LD_A_IX(Z80* ctx) { ctx->LD_R_IX(0b111); }
    static inline void LD_B_IX(Z80* ctx) { ctx->LD_R_IX(0b000); }
    static inline void LD_C_IX(Z80* ctx) { ctx->LD_R_IX(0b001); }
    static inline void LD_D_IX(Z80* ctx) { ctx->LD_R_IX(0b010); }
    static inline void LD_E_IX(Z80* ctx) { ctx->LD_R_IX(0b011); }
    static inline void LD_H_IX(Z80* ctx) { ctx->LD_R_IX(0b100); }
    static inline void LD_L_IX(Z80* ctx) { ctx->LD_R_IX(0b101); }
    inline void LD_R_IX(unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
        signed char d = (signed char)fetch(4);
        unsigned char n = readByte((reg.IX + d) & 0xFFFF);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD %s, (IX<$%04X>+$%02X) = $%02X", reg.PC - 3, registerDump(r), reg.IX, d, n);
#endif
        if (rp) *rp = n;
        consumeClock(3);
    }

    // Load Reg. r with IXH
    static inline void LD_A_IXH(Z80* ctx) { ctx->LD_R_IXH(0b111); }
    static inline void LD_B_IXH(Z80* ctx) { ctx->LD_R_IXH(0b000); }
    static inline void LD_C_IXH(Z80* ctx) { ctx->LD_R_IXH(0b001); }
    static inline void LD_D_IXH(Z80* ctx) { ctx->LD_R_IXH(0b010); }
    static inline void LD_E_IXH(Z80* ctx) { ctx->LD_R_IXH(0b011); }
    inline void LD_R_IXH(unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD %s, IXH<$%02X>", reg.PC - 2, registerDump(r), getIXH());
#endif
        if (rp) *rp = getIXH();
    }

    // Load Reg. r with IXL
    static inline void LD_A_IXL(Z80* ctx) { ctx->LD_R_IXL(0b111); }
    static inline void LD_B_IXL(Z80* ctx) { ctx->LD_R_IXL(0b000); }
    static inline void LD_C_IXL(Z80* ctx) { ctx->LD_R_IXL(0b001); }
    static inline void LD_D_IXL(Z80* ctx) { ctx->LD_R_IXL(0b010); }
    static inline void LD_E_IXL(Z80* ctx) { ctx->LD_R_IXL(0b011); }
    inline void LD_R_IXL(unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD %s, IXL<$%02X>", reg.PC - 2, registerDump(r), getIXL());
#endif
        if (rp) *rp = getIXL();
    }

    // Load Reg. r with location (IY+d)
    static inline void LD_A_IY(Z80* ctx) { ctx->LD_R_IY(0b111); }
    static inline void LD_B_IY(Z80* ctx) { ctx->LD_R_IY(0b000); }
    static inline void LD_C_IY(Z80* ctx) { ctx->LD_R_IY(0b001); }
    static inline void LD_D_IY(Z80* ctx) { ctx->LD_R_IY(0b010); }
    static inline void LD_E_IY(Z80* ctx) { ctx->LD_R_IY(0b011); }
    static inline void LD_H_IY(Z80* ctx) { ctx->LD_R_IY(0b100); }
    static inline void LD_L_IY(Z80* ctx) { ctx->LD_R_IY(0b101); }
    inline void LD_R_IY(unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
        signed char d = (signed char)fetch(4);
        unsigned char n = readByte((reg.IY + d) & 0xFFFF);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD %s, (IY<$%04X>+$%02X) = $%02X", reg.PC - 3, registerDump(r), reg.IY, d, n);
#endif
        if (rp) *rp = n;
        consumeClock(3);
    }

    // Load Reg. r with IYH
    static inline void LD_A_IYH(Z80* ctx) { ctx->LD_R_IYH(0b111); }
    static inline void LD_B_IYH(Z80* ctx) { ctx->LD_R_IYH(0b000); }
    static inline void LD_C_IYH(Z80* ctx) { ctx->LD_R_IYH(0b001); }
    static inline void LD_D_IYH(Z80* ctx) { ctx->LD_R_IYH(0b010); }
    static inline void LD_E_IYH(Z80* ctx) { ctx->LD_R_IYH(0b011); }
    inline void LD_R_IYH(unsigned char r)
    {
        unsigned char iyh = getIYH();
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD %s, IYH<$%02X>", reg.PC - 2, registerDump(r), iyh);
#endif
        if (rp) *rp = iyh;
    }

    // Load Reg. r with IYL
    static inline void LD_A_IYL(Z80* ctx) { ctx->LD_R_IYL(0b111); }
    static inline void LD_B_IYL(Z80* ctx) { ctx->LD_R_IYL(0b000); }
    static inline void LD_C_IYL(Z80* ctx) { ctx->LD_R_IYL(0b001); }
    static inline void LD_D_IYL(Z80* ctx) { ctx->LD_R_IYL(0b010); }
    static inline void LD_E_IYL(Z80* ctx) { ctx->LD_R_IYL(0b011); }
    inline void LD_R_IYL(unsigned char r)
    {
        unsigned char iyl = getIYL();
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD %s, IYL<$%02X>", reg.PC - 2, registerDump(r), iyl);
#endif
        if (rp) *rp = iyl;
    }

    // Load location (HL) with Reg. r
    static inline void LD_HL_B(Z80* ctx) { ctx->LD_HL_R(0b000); }
    static inline void LD_HL_C(Z80* ctx) { ctx->LD_HL_R(0b001); }
    static inline void LD_HL_D(Z80* ctx) { ctx->LD_HL_R(0b010); }
    static inline void LD_HL_E(Z80* ctx) { ctx->LD_HL_R(0b011); }
    static inline void LD_HL_H(Z80* ctx) { ctx->LD_HL_R(0b100); }
    static inline void LD_HL_L(Z80* ctx) { ctx->LD_HL_R(0b101); }
    static inline void LD_HL_A(Z80* ctx) { ctx->LD_HL_R(0b111); }
    inline void LD_HL_R(unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
        unsigned short addr = getHL();
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD (%s), %s", reg.PC - 1, registerPairDump(0b10), registerDump(r));
#endif
        writeByte(addr, *rp, 3);
    }

    // 	Load location (IX+d) with Reg. r
    static inline void LD_IX_A(Z80* ctx) { ctx->LD_IX_R(0b111); }
    static inline void LD_IX_B(Z80* ctx) { ctx->LD_IX_R(0b000); }
    static inline void LD_IX_C(Z80* ctx) { ctx->LD_IX_R(0b001); }
    static inline void LD_IX_D(Z80* ctx) { ctx->LD_IX_R(0b010); }
    static inline void LD_IX_E(Z80* ctx) { ctx->LD_IX_R(0b011); }
    static inline void LD_IX_H(Z80* ctx) { ctx->LD_IX_R(0b100); }
    static inline void LD_IX_L(Z80* ctx) { ctx->LD_IX_R(0b101); }
    inline void LD_IX_R(unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
        signed char d = (signed char)fetch(4);
        unsigned short addr = (unsigned short)(reg.IX + d);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD (IX<$%04X>+$%02X), %s", reg.PC - 3, reg.IX, d, registerDump(r));
#endif
        if (rp) writeByte(addr, *rp);
        consumeClock(3);
    }

    // 	Load location (IY+d) with Reg. r
    static inline void LD_IY_A(Z80* ctx) { ctx->LD_IY_R(0b111); }
    static inline void LD_IY_B(Z80* ctx) { ctx->LD_IY_R(0b000); }
    static inline void LD_IY_C(Z80* ctx) { ctx->LD_IY_R(0b001); }
    static inline void LD_IY_D(Z80* ctx) { ctx->LD_IY_R(0b010); }
    static inline void LD_IY_E(Z80* ctx) { ctx->LD_IY_R(0b011); }
    static inline void LD_IY_H(Z80* ctx) { ctx->LD_IY_R(0b100); }
    static inline void LD_IY_L(Z80* ctx) { ctx->LD_IY_R(0b101); }
    inline void LD_IY_R(unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
        signed char d = (signed char)fetch(4);
        unsigned short addr = (unsigned short)(reg.IY + d);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD (IY<$%04X>+$%02X), %s", reg.PC - 3, reg.IY, d, registerDump(r));
#endif
        if (rp) writeByte(addr, *rp);
        consumeClock(3);
    }

    // Load location (IX+d) with value n
    static inline void LD_IX_N_(Z80* ctx) { ctx->LD_IX_N(); }
    inline void LD_IX_N()
    {
        signed char d = (signed char)fetch(4);
        unsigned char n = fetch(4);
        unsigned short addr = (unsigned short)(reg.IX + d);
        writeByte(addr, n, 3);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD (IX<$%04X>+$%02X), $%02X", reg.PC - 4, reg.IX, d, n);
#endif
    }

    // Load location (IY+d) with value n
    static inline void LD_IY_N_(Z80* ctx) { ctx->LD_IY_N(); }
    inline void LD_IY_N()
    {
        signed char d = (signed char)fetch(4);
        unsigned char n = fetch(4);
        unsigned short addr = (unsigned short)(reg.IY + d);
        writeByte(addr, n, 3);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD (IY<$%04X>+$%02X), $%02X", reg.PC - 4, reg.IY, d, n);
#endif
    }

    // Load Reg. pair rp with value nn.
    static inline void LD_BC_NN(Z80* ctx) { ctx->LD_RP_NN(0b00); }
    static inline void LD_DE_NN(Z80* ctx) { ctx->LD_RP_NN(0b01); }
    static inline void LD_HL_NN(Z80* ctx) { ctx->LD_RP_NN(0b10); }
    static inline void LD_SP_NN(Z80* ctx) { ctx->LD_RP_NN(0b11); }
    inline void LD_RP_NN(unsigned char rp)
    {
        unsigned char* rH;
        unsigned char* rL;
        switch (rp) {
            case 0b00:
                rH = &reg.pair.B;
                rL = &reg.pair.C;
                break;
            case 0b01:
                rH = &reg.pair.D;
                rL = &reg.pair.E;
                break;
            case 0b10:
                rH = &reg.pair.H;
                rL = &reg.pair.L;
                break;
            case 0b11: {
                // SP is not managed in pair structure, so calculate directly
#ifndef Z80_DISABLE_DEBUG
                unsigned short sp = reg.SP;
#endif
                setSPL(fetch(3));
                setSPH(fetch(3));
#ifndef Z80_DISABLE_DEBUG
                if (isDebug()) log("[%04X] LD SP<$%04X>, $%04X", reg.PC - 3, sp, reg.SP);
#endif
                return;
            }
            default:
#ifndef Z80_DISABLE_DEBUG
                if (isDebug()) log("invalid register pair has specified: $%02X", rp);
#endif
#ifndef Z80_NO_EXCEPTION
                throw std::runtime_error("invalid register pair has specified");
#endif
                return;
        }
#ifndef Z80_DISABLE_DEBUG
        const char* dump = isDebug() ? registerPairDump(rp) : "";
#endif
        unsigned char nL = fetch(3);
        *rL = nL;
        unsigned char nH = fetch(3);
        *rH = nH;
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD %s, $%02X%02X", reg.PC - 3, dump, nH, nL);
#endif
    }

    static inline void LD_IX_NN_(Z80* ctx) { ctx->LD_IX_NN(); }
    inline void LD_IX_NN()
    {
        setIXL(fetch(3));
        setIXH(fetch(3));
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD IX, $%04X", reg.PC - 4, reg.IX);
#endif
    }

    static inline void LD_IY_NN_(Z80* ctx) { ctx->LD_IY_NN(); }
    inline void LD_IY_NN()
    {
        setIYL(fetch(3));
        setIYH(fetch(3));
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD IY, $%04X", reg.PC - 4, reg.IY);
#endif
    }

    // Load Reg. pair rp with location (nn)
    static inline void LD_RP_ADDR_BC(Z80* ctx) { ctx->LD_RP_ADDR(0b00); }
    static inline void LD_RP_ADDR_DE(Z80* ctx) { ctx->LD_RP_ADDR(0b01); }
    static inline void LD_RP_ADDR_HL(Z80* ctx) { ctx->LD_RP_ADDR(0b10); }
    static inline void LD_RP_ADDR_SP(Z80* ctx) { ctx->LD_RP_ADDR(0b11); }
    inline void LD_RP_ADDR(unsigned char rp)
    {
        unsigned char l = fetch(3);
        unsigned char h = fetch(3);
        unsigned short addr = make16BitsFromLE(l, h);
        unsigned char* rL;
        unsigned char* rH;
        switch (rp) {
            case 0:
                rH = &reg.pair.B;
                rL = &reg.pair.C;
                break;
            case 1:
                rH = &reg.pair.D;
                rL = &reg.pair.E;
                break;
            case 2:
                rH = &reg.pair.H;
                rL = &reg.pair.L;
                break;
            case 3: {
#ifndef Z80_DISABLE_DEBUG
                const char* dump = isDebug() ? registerPairDump(rp) : "";
#endif
                setSPL(readByte(addr, 3));
                setSPH(readByte(addr + 1, 3));
                reg.WZ = addr + 1;
#ifndef Z80_DISABLE_DEBUG
                if (isDebug()) log("[%04X] LD %s, ($%04X) = $%04X", reg.PC - 4, dump, addr, reg.SP);
#endif
                return;
            }
            default:
#ifndef Z80_DISABLE_DEBUG
                if (isDebug()) log("invalid register pair has specified: $%02X", rp);
#endif
#ifndef Z80_NO_EXCEPTION
                throw std::runtime_error("invalid register pair has specified");
#endif
                return;
        }
#ifndef Z80_DISABLE_DEBUG
        const char* dump = isDebug() ? registerPairDump(rp) : "";
#endif
        *rL = readByte(addr, 3);
        *rH = readByte(addr + 1, 3);
        reg.WZ = addr + 1;
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD %s, ($%04X) = $%04X", reg.PC - 4, dump, addr, make16BitsFromLE(*rL, *rH));
#endif
    }

    // Load location (nn) with Reg. pair rp.
    static inline void LD_ADDR_RP_BC(Z80* ctx) { ctx->LD_ADDR_RP(0b00); }
    static inline void LD_ADDR_RP_DE(Z80* ctx) { ctx->LD_ADDR_RP(0b01); }
    static inline void LD_ADDR_RP_HL(Z80* ctx) { ctx->LD_ADDR_RP(0b10); }
    static inline void LD_ADDR_RP_SP(Z80* ctx) { ctx->LD_ADDR_RP(0b11); }
    inline void LD_ADDR_RP(unsigned char rp)
    {
        unsigned char l = fetch(3);
        unsigned char h = fetch(3);
        unsigned short addr = make16BitsFromLE(l, h);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD ($%04X), %s", reg.PC - 4, addr, registerPairDump(rp));
#endif
        switch (rp) {
            case 0b00:
                h = reg.pair.B;
                l = reg.pair.C;
                break;
            case 0b01:
                h = reg.pair.D;
                l = reg.pair.E;
                break;
            case 0b10:
                h = reg.pair.H;
                l = reg.pair.L;
                break;
            case 0b11:
                splitTo8BitsPair(reg.SP, &h, &l);
                break;
            default:
#ifndef Z80_DISABLE_DEBUG
                if (isDebug()) log("invalid register pair has specified: $%02X", rp);
#endif
#ifndef Z80_NO_EXCEPTION
                throw std::runtime_error("invalid register pair has specified");
#endif
                return;
        }
        writeByte(addr, l, 3);
        writeByte(addr + 1, h, 3);
        reg.WZ = addr + 1;
    }

    // Load IX with location (nn)
    static inline void LD_IX_ADDR_(Z80* ctx) { ctx->LD_IX_ADDR(); }
    inline void LD_IX_ADDR()
    {
        unsigned char l = fetch(3);
        unsigned char h = fetch(3);
        unsigned short addr = make16BitsFromLE(l, h);
#ifndef Z80_DISABLE_DEBUG
        unsigned short ix = reg.IX;
#endif
        setIXL(readByte(addr, 3));
        setIXH(readByte(addr + 1, 3));
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD IX<$%04X>, ($%04X) = $%04X", reg.PC - 4, ix, addr, reg.IX);
#endif
    }

    // Load IY with location (nn)
    static inline void LD_IY_ADDR_(Z80* ctx) { ctx->LD_IY_ADDR(); }
    inline void LD_IY_ADDR()
    {
        unsigned char l = fetch(3);
        unsigned char h = fetch(3);
        unsigned short addr = make16BitsFromLE(l, h);
#ifndef Z80_DISABLE_DEBUG
        unsigned short iy = reg.IY;
#endif
        setIYL(readByte(addr, 3));
        setIYH(readByte(addr + 1, 3));
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD IY<$%04X>, ($%04X) = $%04X", reg.PC - 4, iy, addr, reg.IY);
#endif
    }

    static inline void LD_ADDR_IX_(Z80* ctx) { ctx->LD_ADDR_IX(); }
    inline void LD_ADDR_IX()
    {
        unsigned char l = fetch(3);
        unsigned char h = fetch(3);
        unsigned short addr = make16BitsFromLE(l, h);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD ($%04X), IX<$%04X>", reg.PC - 4, addr, reg.IX);
#endif
        writeByte(addr, getIXL(), 3);
        writeByte(addr + 1, getIXH(), 3);
    }

    static inline void LD_ADDR_IY_(Z80* ctx) { ctx->LD_ADDR_IY(); }
    inline void LD_ADDR_IY()
    {
        unsigned char l = fetch(3);
        unsigned char h = fetch(3);
        unsigned short addr = make16BitsFromLE(l, h);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD ($%04X), IY<$%04X>", reg.PC - 4, addr, reg.IY);
#endif
        writeByte(addr, getIYL(), 3);
        writeByte(addr + 1, getIYH(), 3);
    }

    // Load SP with IX.
    static inline void LD_SP_IX_(Z80* ctx) { ctx->LD_SP_IX(); }
    inline void LD_SP_IX()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD %s, IX<$%04X>", reg.PC - 2, registerPairDump(0b11), reg.IX);
#endif
        reg.SP = reg.IX;
        consumeClock(2);
    }

    // Load SP with IY.
    static inline void LD_SP_IY_(Z80* ctx) { ctx->LD_SP_IY(); }
    inline void LD_SP_IY()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] LD %s, IY<$%04X>", reg.PC - 2, registerPairDump(0b11), reg.IY);
#endif
        reg.SP = reg.IY;
        consumeClock(2);
    }

    // Load location (DE) with Loacation (HL), increment/decrement DE, HL, decrement BC
    inline void repeatLD(bool isIncDEHL, bool isRepeat)
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) {
            if (isIncDEHL) {
                if (isDebug()) log("[%04X] %s ... %s, %s, %s", reg.PC - 2, isRepeat ? "LDIR" : "LDI", registerPairDump(0b00), registerPairDump(0b01), registerPairDump(0b10));
            } else {
                if (isDebug()) log("[%04X] %s ... %s, %s, %s", reg.PC - 2, isRepeat ? "LDDR" : "LDD", registerPairDump(0b00), registerPairDump(0b01), registerPairDump(0b10));
            }
        }
#endif
        unsigned short bc = getBC();
        unsigned short de = getDE();
        unsigned short hl = getHL();
        unsigned char n = readByte(hl);
        writeByte(de, n);
        if (isIncDEHL) {
            de++;
            hl++;
        } else {
            de--;
            hl--;
        }
        bc--;
        setBC(bc);
        setDE(de);
        setHL(hl);
        resetFlagH();
        setFlagPV(bc != 0);
        resetFlagN();
        unsigned char an = reg.pair.A + n;
        setFlagY(an & 0b00000010);
        setFlagX(an & 0b00001000);
        if (isRepeat && 0 != bc) {
            reg.PC -= 2;
            consumeClock(5);
        }
    }
    static inline void LDI(Z80* ctx) { ctx->repeatLD(true, false); }
    static inline void LDIR(Z80* ctx) { ctx->repeatLD(true, true); }
    static inline void LDD(Z80* ctx) { ctx->repeatLD(false, false); }
    static inline void LDDR(Z80* ctx) { ctx->repeatLD(false, true); }

    // Exchange stack top with IX
    static inline void EX_SP_IX_(Z80* ctx) { ctx->EX_SP_IX(); }
    inline void EX_SP_IX()
    {
#ifndef Z80_DISABLE_DEBUG
        unsigned short sp = reg.SP;
#endif
        unsigned char l = pop(4);
        unsigned char h = pop(4);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] EX (SP<$%04X>) = $%02X%02X, IX<$%04X>", reg.PC - 2, sp, h, l, reg.IX);
#endif
        push(getIXH(), 4);
        setIXH(h);
        push(getIXL(), 3);
        setIXL(l);
    }

    // Exchange stack top with IY
    static inline void EX_SP_IY_(Z80* ctx) { ctx->EX_SP_IY(); }
    inline void EX_SP_IY()
    {
#ifndef Z80_DISABLE_DEBUG
        unsigned short sp = reg.SP;
#endif
        unsigned char l = pop(4);
        unsigned char h = pop(4);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] EX (SP<$%04X>) = $%02X%02X, IY<$%04X>", reg.PC - 2, sp, h, l, reg.IY);
#endif
        push(getIYH(), 4);
        setIYH(h);
        push(getIYL(), 3);
        setIYL(l);
    }

    // Push Reg. on Stack.
    static inline void PUSH_BC(Z80* ctx) { ctx->PUSH_RP(0b00); }
    static inline void PUSH_DE(Z80* ctx) { ctx->PUSH_RP(0b01); }
    static inline void PUSH_HL(Z80* ctx) { ctx->PUSH_RP(0b10); }
    inline void PUSH_RP(unsigned char rp)
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] PUSH %s <SP:$%04X>", reg.PC - 1, registerPairDump(rp), reg.SP);
#endif
        switch (rp) {
            case 0b00:
                push(reg.pair.B, 4);
                push(reg.pair.C, 3);
                break;
            case 0b01:
                push(reg.pair.D, 4);
                push(reg.pair.E, 3);
                break;
            case 0b10:
                push(reg.pair.H, 4);
                push(reg.pair.L, 3);
                break;
            default:
#ifndef Z80_DISABLE_DEBUG
                if (isDebug()) log("invalid register pair has specified: $%02X", rp);
#endif
#ifdef Z80_NO_EXCEPTION
                ;
#else
                throw std::runtime_error("invalid register pair has specified");
#endif
        }
    }

    // Push Reg. on Stack.
    static inline void POP_BC(Z80* ctx) { ctx->POP_RP(0b00); }
    static inline void POP_DE(Z80* ctx) { ctx->POP_RP(0b01); }
    static inline void POP_HL(Z80* ctx) { ctx->POP_RP(0b10); }
    inline void POP_RP(unsigned char rp)
    {
#ifndef Z80_DISABLE_DEBUG
        unsigned short sp = reg.SP;
        const char* dump = isDebug() ? registerPairDump(rp) : "";
        unsigned short after;
#endif
        switch (rp) {
            case 0b00:
                reg.pair.C = pop(3);
                reg.pair.B = pop(3);
#ifndef Z80_DISABLE_DEBUG
                after = getBC();
#endif
                break;
            case 0b01:
                reg.pair.E = pop(3);
                reg.pair.D = pop(3);
#ifndef Z80_DISABLE_DEBUG
                after = getDE();
#endif
                break;
            case 0b10:
                reg.pair.L = pop(3);
                reg.pair.H = pop(3);
#ifndef Z80_DISABLE_DEBUG
                after = getHL();
#endif
                break;
            default:
#ifndef Z80_DISABLE_DEBUG
                if (isDebug()) log("invalid register pair has specified: $%02X", rp);
#endif
#ifndef Z80_NO_EXCEPTION
                throw std::runtime_error("invalid register pair has specified");
#endif
                return;
        }
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] POP %s <SP:$%04X> = $%04X", reg.PC - 1, dump, sp, after);
#endif
    }

    // Push Reg. IX on Stack.
    static inline void PUSH_IX_(Z80* ctx) { ctx->PUSH_IX(); }
    inline void PUSH_IX()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] PUSH IX<$%04X> <SP:$%04X>", reg.PC - 2, reg.IX, reg.SP);
#endif
        push(getIXH(), 4);
        push(getIXL(), 3);
    }

    // Pop Reg. IX from Stack.
    static inline void POP_IX_(Z80* ctx) { ctx->POP_IX(); }
    inline void POP_IX()
    {
#ifndef Z80_DISABLE_DEBUG
        unsigned short sp = reg.SP;
#endif
        setIXL(pop(3));
        setIXH(pop(3));
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] POP IX <SP:$%04X> = $%04X", reg.PC - 2, sp, reg.IX);
#endif
    }

    // Push Reg. IY on Stack.
    static inline void PUSH_IY_(Z80* ctx) { ctx->PUSH_IY(); }
    inline void PUSH_IY()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] PUSH IY<$%04X> <SP:$%04X>", reg.PC - 2, reg.IY, reg.SP);
#endif
        push(getIYH(), 4);
        push(getIYL(), 3);
    }

    // Pop Reg. IY from Stack.
    static inline void POP_IY_(Z80* ctx) { ctx->POP_IY(); }
    inline void POP_IY()
    {
#ifndef Z80_DISABLE_DEBUG
        unsigned short sp = reg.SP;
#endif
        setIYL(pop(3));
        setIYH(pop(3));
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] POP IY <SP:$%04X> = $%04X", reg.PC - 2, sp, reg.IY);
#endif
    }

    inline void setFlagByRotate(unsigned char n, bool carry, bool isA = false)
    {
        setFlagC(carry);
        resetFlagH();
        resetFlagN();
        setFlagXY(n);
        if (!isA) {
            setFlagS(n & 0x80);
            setFlagZ(0 == n);
            setFlagPV(isEvenNumberBits(n));
        }
    }

    inline unsigned char SLL(unsigned char n)
    {
        unsigned char c = n & 0x80;
        n &= 0b01111111;
        n <<= 1;
        n |= 1; // differ with SLA
        setFlagByRotate(n, c);
        return n;
    }

    inline unsigned char SLA(unsigned char n)
    {
        unsigned char c = (n & 0x80) >> 7;
        n &= 0b01111111;
        n <<= 1;
        setFlagByRotate(n, c);
        return n;
    }

    inline unsigned char SRL(unsigned char n)
    {
        unsigned char n0 = n & 0x01;
        n &= 0b11111110;
        n >>= 1;
        setFlagByRotate(n, n0);
        return n;
    }

    inline unsigned char SRA(unsigned char n)
    {
        unsigned char n0 = n & 0x01;
        unsigned char n7 = n & 0x80;
        n &= 0b11111110;
        n >>= 1;
        n = n7 ? n | 0x80 : n & 0x7F;
        setFlagByRotate(n, n0);
        return n;
    }

    inline unsigned char RLC(unsigned char n, bool isA = false)
    {
        unsigned char c = (n & 0x80) >> 7;
        n &= 0b01111111;
        n <<= 1;
        n |= c; // differ with RL
        setFlagByRotate(n, c, isA);
        return n;
    }

    inline unsigned char RL(unsigned char n, bool isA = false)
    {
        unsigned char c = (n & 0x80) >> 7;
        n &= 0b01111111;
        n <<= 1;
        n |= isFlagC() ? 1 : 0; // differ with RLC
        setFlagByRotate(n, c, isA);
        return n;
    }

    inline unsigned char RRC(unsigned char n, bool isA = false)
    {
        unsigned char c = (n & 1) << 7;
        n &= 0b11111110;
        n >>= 1;
        n |= c; // differ with RR
        setFlagByRotate(n, c, isA);
        return n;
    }

    inline unsigned char RR(unsigned char n, bool isA = false)
    {
        unsigned char c = (n & 1) << 7;
        n &= 0b11111110;
        n >>= 1;
        n |= isFlagC() ? 0x80 : 0; // differ with RR
        setFlagByRotate(n, c, isA);
        return n;
    }

    static inline void RLCA(Z80* ctx)
    {
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] RLCA <A:$%02X, C:%s>", ctx->reg.PC - 1, ctx->reg.pair.A, ctx->isFlagC() ? "ON" : "OFF");
#endif
        ctx->reg.pair.A = ctx->RLC(ctx->reg.pair.A, true);
    }

    static inline void RRCA(Z80* ctx)
    {
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] RRCA <A:$%02X, C:%s>", ctx->reg.PC - 1, ctx->reg.pair.A, ctx->isFlagC() ? "ON" : "OFF");
#endif
        ctx->reg.pair.A = ctx->RRC(ctx->reg.pair.A, true);
    }

    static inline void RLA(Z80* ctx)
    {
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] RLA <A:$%02X, C:%s>", ctx->reg.PC - 1, ctx->reg.pair.A, ctx->isFlagC() ? "ON" : "OFF");
#endif
        ctx->reg.pair.A = ctx->RL(ctx->reg.pair.A, true);
    }

    static inline void RRA(Z80* ctx)
    {
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] RRA <A:$%02X, C:%s>", ctx->reg.PC - 1, ctx->reg.pair.A, ctx->isFlagC() ? "ON" : "OFF");
#endif
        ctx->reg.pair.A = ctx->RR(ctx->reg.pair.A, true);
    }

    // Rotate register Left Circular
    static inline void RLC_B(Z80* ctx) { ctx->RLC_R(0b000); }
    static inline void RLC_C(Z80* ctx) { ctx->RLC_R(0b001); }
    static inline void RLC_D(Z80* ctx) { ctx->RLC_R(0b010); }
    static inline void RLC_E(Z80* ctx) { ctx->RLC_R(0b011); }
    static inline void RLC_H(Z80* ctx) { ctx->RLC_R(0b100); }
    static inline void RLC_L(Z80* ctx) { ctx->RLC_R(0b101); }
    static inline void RLC_A(Z80* ctx) { ctx->RLC_R(0b111); }
    inline void RLC_R(unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] RLC %s", reg.PC - 2, registerDump(r));
#endif
        *rp = RLC(*rp);
    }

    // Rotate Left register
    static inline void RL_B(Z80* ctx) { ctx->RL_R(0b000); }
    static inline void RL_C(Z80* ctx) { ctx->RL_R(0b001); }
    static inline void RL_D(Z80* ctx) { ctx->RL_R(0b010); }
    static inline void RL_E(Z80* ctx) { ctx->RL_R(0b011); }
    static inline void RL_H(Z80* ctx) { ctx->RL_R(0b100); }
    static inline void RL_L(Z80* ctx) { ctx->RL_R(0b101); }
    static inline void RL_A(Z80* ctx) { ctx->RL_R(0b111); }
    inline void RL_R(unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] RL %s <C:%s>", reg.PC - 2, registerDump(r), isFlagC() ? "ON" : "OFF");
#endif
        *rp = RL(*rp);
    }

    // Shift operand register left Arithmetic
    static inline void SLA_B(Z80* ctx) { ctx->SLA_R(0b000); }
    static inline void SLA_C(Z80* ctx) { ctx->SLA_R(0b001); }
    static inline void SLA_D(Z80* ctx) { ctx->SLA_R(0b010); }
    static inline void SLA_E(Z80* ctx) { ctx->SLA_R(0b011); }
    static inline void SLA_H(Z80* ctx) { ctx->SLA_R(0b100); }
    static inline void SLA_L(Z80* ctx) { ctx->SLA_R(0b101); }
    static inline void SLA_A(Z80* ctx) { ctx->SLA_R(0b111); }
    inline void SLA_R(unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SLA %s", reg.PC - 2, registerDump(r));
#endif
        *rp = SLA(*rp);
    }

    // Rotate register Right Circular
    static inline void RRC_B(Z80* ctx) { ctx->RRC_R(0b000); }
    static inline void RRC_C(Z80* ctx) { ctx->RRC_R(0b001); }
    static inline void RRC_D(Z80* ctx) { ctx->RRC_R(0b010); }
    static inline void RRC_E(Z80* ctx) { ctx->RRC_R(0b011); }
    static inline void RRC_H(Z80* ctx) { ctx->RRC_R(0b100); }
    static inline void RRC_L(Z80* ctx) { ctx->RRC_R(0b101); }
    static inline void RRC_A(Z80* ctx) { ctx->RRC_R(0b111); }
    inline void RRC_R(unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] RRC %s", reg.PC - 2, registerDump(r));
#endif
        *rp = RRC(*rp);
    }

    // Rotate Right register
    static inline void RR_B(Z80* ctx) { ctx->RR_R(0b000); }
    static inline void RR_C(Z80* ctx) { ctx->RR_R(0b001); }
    static inline void RR_D(Z80* ctx) { ctx->RR_R(0b010); }
    static inline void RR_E(Z80* ctx) { ctx->RR_R(0b011); }
    static inline void RR_H(Z80* ctx) { ctx->RR_R(0b100); }
    static inline void RR_L(Z80* ctx) { ctx->RR_R(0b101); }
    static inline void RR_A(Z80* ctx) { ctx->RR_R(0b111); }
    inline void RR_R(unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] RR %s <C:%s>", reg.PC - 2, registerDump(r), isFlagC() ? "ON" : "OFF");
#endif
        *rp = RR(*rp);
    }

    // Shift operand register Right Arithmetic
    static inline void SRA_B(Z80* ctx) { ctx->SRA_R(0b000); }
    static inline void SRA_C(Z80* ctx) { ctx->SRA_R(0b001); }
    static inline void SRA_D(Z80* ctx) { ctx->SRA_R(0b010); }
    static inline void SRA_E(Z80* ctx) { ctx->SRA_R(0b011); }
    static inline void SRA_H(Z80* ctx) { ctx->SRA_R(0b100); }
    static inline void SRA_L(Z80* ctx) { ctx->SRA_R(0b101); }
    static inline void SRA_A(Z80* ctx) { ctx->SRA_R(0b111); }
    inline void SRA_R(unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SRA %s", reg.PC - 2, registerDump(r));
#endif
        *rp = SRA(*rp);
    }

    // Shift operand register Right Logical
    static inline void SRL_B(Z80* ctx) { ctx->SRL_R(0b000); }
    static inline void SRL_C(Z80* ctx) { ctx->SRL_R(0b001); }
    static inline void SRL_D(Z80* ctx) { ctx->SRL_R(0b010); }
    static inline void SRL_E(Z80* ctx) { ctx->SRL_R(0b011); }
    static inline void SRL_H(Z80* ctx) { ctx->SRL_R(0b100); }
    static inline void SRL_L(Z80* ctx) { ctx->SRL_R(0b101); }
    static inline void SRL_A(Z80* ctx) { ctx->SRL_R(0b111); }
    inline void SRL_R(unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SRL %s", reg.PC - 2, registerDump(r));
#endif
        *rp = SRL(*rp);
    }

    // Shift operand register Left Logical
    static inline void SLL_B(Z80* ctx) { ctx->SLL_R(0b000); }
    static inline void SLL_C(Z80* ctx) { ctx->SLL_R(0b001); }
    static inline void SLL_D(Z80* ctx) { ctx->SLL_R(0b010); }
    static inline void SLL_E(Z80* ctx) { ctx->SLL_R(0b011); }
    static inline void SLL_H(Z80* ctx) { ctx->SLL_R(0b100); }
    static inline void SLL_L(Z80* ctx) { ctx->SLL_R(0b101); }
    static inline void SLL_A(Z80* ctx) { ctx->SLL_R(0b111); }
    inline void SLL_R(unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SLL %s", reg.PC - 2, registerDump(r));
#endif
        *rp = SLL(*rp);
    }

    // Rotate memory (HL) Left Circular
    static inline void RLC_HL_(Z80* ctx) { ctx->RLC_HL(); }
    inline void RLC_HL()
    {
        unsigned short addr = getHL();
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] RLC (HL<$%04X>) = $%02X", reg.PC - 2, addr, n);
#endif
        writeByte(addr, RLC(n), 3);
    }

    // Rotate Left memory
    static inline void RL_HL_(Z80* ctx) { ctx->RL_HL(); }
    inline void RL_HL()
    {
        unsigned short addr = getHL();
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] RL (HL<$%04X>) = $%02X <C:%s>", reg.PC - 2, addr, n, isFlagC() ? "ON" : "OFF");
#endif
        writeByte(addr, RL(n), 3);
    }

    // Shift operand location (HL) left Arithmetic
    static inline void SLA_HL_(Z80* ctx) { ctx->SLA_HL(); }
    inline void SLA_HL()
    {
        unsigned short addr = getHL();
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SLA (HL<$%04X>) = $%02X", reg.PC - 2, addr, n);
#endif
        writeByte(addr, SLA(n), 3);
    }

    // Rotate memory (HL) Right Circular
    static inline void RRC_HL_(Z80* ctx) { ctx->RRC_HL(); }
    inline void RRC_HL()
    {
        unsigned short addr = getHL();
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] RRC (HL<$%04X>) = $%02X", reg.PC - 2, addr, n);
#endif
        writeByte(addr, RRC(n), 3);
    }

    // Rotate Right memory
    static inline void RR_HL_(Z80* ctx) { ctx->RR_HL(); }
    inline void RR_HL()
    {
        unsigned short addr = getHL();
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] RR (HL<$%04X>) = $%02X <C:%s>", reg.PC - 2, addr, n, isFlagC() ? "ON" : "OFF");
#endif
        writeByte(addr, RR(n), 3);
    }

    // Shift operand location (HL) Right Arithmetic
    static inline void SRA_HL_(Z80* ctx) { ctx->SRA_HL(); }
    inline void SRA_HL()
    {
        unsigned short addr = getHL();
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SRA (HL<$%04X>) = $%02X", reg.PC - 2, addr, n);
#endif
        writeByte(addr, SRA(n), 3);
    }

    // Shift operand location (HL) Right Logical
    static inline void SRL_HL_(Z80* ctx) { ctx->SRL_HL(); }
    inline void SRL_HL()
    {
        unsigned short addr = getHL();
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SRL (HL<$%04X>) = $%02X", reg.PC - 2, addr, n);
#endif
        writeByte(addr, SRL(n), 3);
    }

    // Shift operand location (HL) Left Logical
    static inline void SLL_HL_(Z80* ctx) { ctx->SLL_HL(); }
    inline void SLL_HL()
    {
        unsigned short addr = getHL();
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SLL (HL<$%04X>) = $%02X", reg.PC - 2, addr, n);
#endif
        writeByte(addr, SLL(n), 3);
    }

    // Rotate memory (IX+d) Left Circular
    static inline void RLC_IX_(Z80* ctx, signed char d) { ctx->RLC_IX(d); }
    inline void RLC_IX(signed char d, unsigned char* rp = nullptr, const char* extraLog = nullptr)
    {
        unsigned short addr = (unsigned short)(reg.IX + d);
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] RLC (IX+d<$%04X>) = $%02X%s", reg.PC - 4, addr, n, extraLog ? extraLog : "");
#endif
        unsigned char result = RLC(n);
        if (rp) *rp = result;
        writeByte(addr, result, 3);
    }

    // Rotate memory (IX+d) Left Circular with load to Reg A/B/C/D/E/H/L/F
    static inline void RLC_IX_with_LD_B(Z80* ctx, signed char d) { ctx->RLC_IX_with_LD(d, 0b000); }
    static inline void RLC_IX_with_LD_C(Z80* ctx, signed char d) { ctx->RLC_IX_with_LD(d, 0b001); }
    static inline void RLC_IX_with_LD_D(Z80* ctx, signed char d) { ctx->RLC_IX_with_LD(d, 0b010); }
    static inline void RLC_IX_with_LD_E(Z80* ctx, signed char d) { ctx->RLC_IX_with_LD(d, 0b011); }
    static inline void RLC_IX_with_LD_H(Z80* ctx, signed char d) { ctx->RLC_IX_with_LD(d, 0b100); }
    static inline void RLC_IX_with_LD_L(Z80* ctx, signed char d) { ctx->RLC_IX_with_LD(d, 0b101); }
    static inline void RLC_IX_with_LD_A(Z80* ctx, signed char d) { ctx->RLC_IX_with_LD(d, 0b111); }
    inline void RLC_IX_with_LD(signed char d, unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        char buf[80];
        if (isDebug()) {
            snprintf(buf, sizeof(buf), " --> %s", registerDump(r));
        } else {
            buf[0] = '\0';
        }
        RLC_IX(d, rp, buf);
#else
        RLC_IX(d, rp);
#endif
    }

    // Rotate memory (IY+d) Left Circular with load to Reg A/B/C/D/E/H/L/F
    static inline void RLC_IY_with_LD_B(Z80* ctx, signed char d) { ctx->RLC_IY_with_LD(d, 0b000); }
    static inline void RLC_IY_with_LD_C(Z80* ctx, signed char d) { ctx->RLC_IY_with_LD(d, 0b001); }
    static inline void RLC_IY_with_LD_D(Z80* ctx, signed char d) { ctx->RLC_IY_with_LD(d, 0b010); }
    static inline void RLC_IY_with_LD_E(Z80* ctx, signed char d) { ctx->RLC_IY_with_LD(d, 0b011); }
    static inline void RLC_IY_with_LD_H(Z80* ctx, signed char d) { ctx->RLC_IY_with_LD(d, 0b100); }
    static inline void RLC_IY_with_LD_L(Z80* ctx, signed char d) { ctx->RLC_IY_with_LD(d, 0b101); }
    static inline void RLC_IY_with_LD_A(Z80* ctx, signed char d) { ctx->RLC_IY_with_LD(d, 0b111); }
    inline void RLC_IY_with_LD(signed char d, unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        char buf[80];
        if (isDebug()) {
            snprintf(buf, sizeof(buf), " --> %s", registerDump(r));
        } else {
            buf[0] = '\0';
        }
        RLC_IY(d, rp, buf);
#else
        RLC_IY(d, rp);
#endif
    }

    // Rotate memory (IX+d) Right Circular
    static inline void RRC_IX_(Z80* ctx, signed char d) { ctx->RRC_IX(d); }
    inline void RRC_IX(signed char d, unsigned char* rp = nullptr, const char* extraLog = nullptr)
    {
        unsigned short addr = (unsigned short)(reg.IX + d);
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] RRC (IX+d<$%04X>) = $%02X%s", reg.PC - 4, addr, n, extraLog ? extraLog : "");
#endif
        unsigned char result = RRC(n);
        if (rp) *rp = result;
        writeByte(addr, result, 3);
    }

    // Rotate memory (IX+d) Right Circular with load to Reg A/B/C/D/E/H/L/F
    static inline void RRC_IX_with_LD_B(Z80* ctx, signed char d) { ctx->RRC_IX_with_LD(d, 0b000); }
    static inline void RRC_IX_with_LD_C(Z80* ctx, signed char d) { ctx->RRC_IX_with_LD(d, 0b001); }
    static inline void RRC_IX_with_LD_D(Z80* ctx, signed char d) { ctx->RRC_IX_with_LD(d, 0b010); }
    static inline void RRC_IX_with_LD_E(Z80* ctx, signed char d) { ctx->RRC_IX_with_LD(d, 0b011); }
    static inline void RRC_IX_with_LD_H(Z80* ctx, signed char d) { ctx->RRC_IX_with_LD(d, 0b100); }
    static inline void RRC_IX_with_LD_L(Z80* ctx, signed char d) { ctx->RRC_IX_with_LD(d, 0b101); }
    static inline void RRC_IX_with_LD_A(Z80* ctx, signed char d) { ctx->RRC_IX_with_LD(d, 0b111); }
    inline void RRC_IX_with_LD(signed char d, unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        char buf[80];
        if (isDebug()) {
            snprintf(buf, sizeof(buf), " --> %s", registerDump(r));
        } else {
            buf[0] = '\0';
        }
        RRC_IX(d, rp, buf);
#else
        RRC_IX(d, rp);
#endif
    }

    // Rotate memory (IY+d) Right Circular with load to Reg A/B/C/D/E/H/L/F
    static inline void RRC_IY_with_LD_B(Z80* ctx, signed char d) { ctx->RRC_IY_with_LD(d, 0b000); }
    static inline void RRC_IY_with_LD_C(Z80* ctx, signed char d) { ctx->RRC_IY_with_LD(d, 0b001); }
    static inline void RRC_IY_with_LD_D(Z80* ctx, signed char d) { ctx->RRC_IY_with_LD(d, 0b010); }
    static inline void RRC_IY_with_LD_E(Z80* ctx, signed char d) { ctx->RRC_IY_with_LD(d, 0b011); }
    static inline void RRC_IY_with_LD_H(Z80* ctx, signed char d) { ctx->RRC_IY_with_LD(d, 0b100); }
    static inline void RRC_IY_with_LD_L(Z80* ctx, signed char d) { ctx->RRC_IY_with_LD(d, 0b101); }
    static inline void RRC_IY_with_LD_A(Z80* ctx, signed char d) { ctx->RRC_IY_with_LD(d, 0b111); }
    inline void RRC_IY_with_LD(signed char d, unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        char buf[80];
        if (isDebug()) {
            snprintf(buf, sizeof(buf), " --> %s", registerDump(r));
        } else {
            buf[0] = '\0';
        }
        RRC_IY(d, rp, buf);
#else
        RRC_IY(d, rp);
#endif
    }

    // Rotate Left memory
    static inline void RL_IX_(Z80* ctx, signed char d) { ctx->RL_IX(d); }
    inline void RL_IX(signed char d, unsigned char* rp = nullptr, const char* extraLog = nullptr)
    {
        unsigned short addr = (unsigned short)(reg.IX + d);
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] RL (IX+d<$%04X>) = $%02X <C:%s>%s", reg.PC - 4, addr, n, isFlagC() ? "ON" : "OFF", extraLog ? extraLog : "");
#endif
        unsigned char result = RL(n);
        if (rp) *rp = result;
        writeByte(addr, result, 3);
    }

    // Rotate Left memory with load Reg.
    static inline void RL_IX_with_LD_B(Z80* ctx, signed char d) { ctx->RL_IX_with_LD(d, 0b000); }
    static inline void RL_IX_with_LD_C(Z80* ctx, signed char d) { ctx->RL_IX_with_LD(d, 0b001); }
    static inline void RL_IX_with_LD_D(Z80* ctx, signed char d) { ctx->RL_IX_with_LD(d, 0b010); }
    static inline void RL_IX_with_LD_E(Z80* ctx, signed char d) { ctx->RL_IX_with_LD(d, 0b011); }
    static inline void RL_IX_with_LD_H(Z80* ctx, signed char d) { ctx->RL_IX_with_LD(d, 0b100); }
    static inline void RL_IX_with_LD_L(Z80* ctx, signed char d) { ctx->RL_IX_with_LD(d, 0b101); }
    static inline void RL_IX_with_LD_A(Z80* ctx, signed char d) { ctx->RL_IX_with_LD(d, 0b111); }
    inline void RL_IX_with_LD(signed char d, unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        char buf[80];
        if (isDebug()) {
            snprintf(buf, sizeof(buf), " --> %s", registerDump(r));
        } else {
            buf[0] = '\0';
        }
        RL_IX(d, rp, buf);
#else
        RL_IX(d, rp);
#endif
    }

    // Rotate Left memory with load Reg.
    static inline void RL_IY_with_LD_B(Z80* ctx, signed char d) { ctx->RL_IY_with_LD(d, 0b000); }
    static inline void RL_IY_with_LD_C(Z80* ctx, signed char d) { ctx->RL_IY_with_LD(d, 0b001); }
    static inline void RL_IY_with_LD_D(Z80* ctx, signed char d) { ctx->RL_IY_with_LD(d, 0b010); }
    static inline void RL_IY_with_LD_E(Z80* ctx, signed char d) { ctx->RL_IY_with_LD(d, 0b011); }
    static inline void RL_IY_with_LD_H(Z80* ctx, signed char d) { ctx->RL_IY_with_LD(d, 0b100); }
    static inline void RL_IY_with_LD_L(Z80* ctx, signed char d) { ctx->RL_IY_with_LD(d, 0b101); }
    static inline void RL_IY_with_LD_A(Z80* ctx, signed char d) { ctx->RL_IY_with_LD(d, 0b111); }
    inline void RL_IY_with_LD(signed char d, unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        char buf[80];
        if (isDebug()) {
            snprintf(buf, sizeof(buf), " --> %s", registerDump(r));
        } else {
            buf[0] = '\0';
        }
        RL_IY(d, rp, buf);
#else
        RL_IY(d, rp);
#endif
    }

    // Rotate Right memory
    static inline void RR_IX_(Z80* ctx, signed char d) { ctx->RR_IX(d); }
    inline void RR_IX(signed char d, unsigned char* rp = nullptr, const char* extraLog = nullptr)
    {
        unsigned short addr = (unsigned short)(reg.IX + d);
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] RR (IX+d<$%04X>) = $%02X <C:%s>%s", reg.PC - 4, addr, n, isFlagC() ? "ON" : "OFF", extraLog ? extraLog : "");
#endif
        unsigned char result = RR(n);
        if (rp) *rp = result;
        writeByte(addr, result, 3);
    }

    // Rotate Right memory with load Reg.
    static inline void RR_IX_with_LD_B(Z80* ctx, signed char d) { ctx->RR_IX_with_LD(d, 0b000); }
    static inline void RR_IX_with_LD_C(Z80* ctx, signed char d) { ctx->RR_IX_with_LD(d, 0b001); }
    static inline void RR_IX_with_LD_D(Z80* ctx, signed char d) { ctx->RR_IX_with_LD(d, 0b010); }
    static inline void RR_IX_with_LD_E(Z80* ctx, signed char d) { ctx->RR_IX_with_LD(d, 0b011); }
    static inline void RR_IX_with_LD_H(Z80* ctx, signed char d) { ctx->RR_IX_with_LD(d, 0b100); }
    static inline void RR_IX_with_LD_L(Z80* ctx, signed char d) { ctx->RR_IX_with_LD(d, 0b101); }
    static inline void RR_IX_with_LD_A(Z80* ctx, signed char d) { ctx->RR_IX_with_LD(d, 0b111); }
    inline void RR_IX_with_LD(signed char d, unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        char buf[80];
        if (isDebug()) {
            snprintf(buf, sizeof(buf), " --> %s", registerDump(r));
        } else {
            buf[0] = '\0';
        }
        RR_IX(d, rp, buf);
#else
        RR_IX(d, rp);
#endif
    }

    // Rotate Right memory with load Reg.
    static inline void RR_IY_with_LD_B(Z80* ctx, signed char d) { ctx->RR_IY_with_LD(d, 0b000); }
    static inline void RR_IY_with_LD_C(Z80* ctx, signed char d) { ctx->RR_IY_with_LD(d, 0b001); }
    static inline void RR_IY_with_LD_D(Z80* ctx, signed char d) { ctx->RR_IY_with_LD(d, 0b010); }
    static inline void RR_IY_with_LD_E(Z80* ctx, signed char d) { ctx->RR_IY_with_LD(d, 0b011); }
    static inline void RR_IY_with_LD_H(Z80* ctx, signed char d) { ctx->RR_IY_with_LD(d, 0b100); }
    static inline void RR_IY_with_LD_L(Z80* ctx, signed char d) { ctx->RR_IY_with_LD(d, 0b101); }
    static inline void RR_IY_with_LD_A(Z80* ctx, signed char d) { ctx->RR_IY_with_LD(d, 0b111); }
    inline void RR_IY_with_LD(signed char d, unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        char buf[80];
        if (isDebug()) {
            snprintf(buf, sizeof(buf), " --> %s", registerDump(r));
        } else {
            buf[0] = '\0';
        }
        RR_IY(d, rp, buf);
#else
        RR_IY(d, rp);
#endif
    }

    // Shift operand location (IX+d) left Arithmetic
    static inline void SLA_IX_(Z80* ctx, signed char d) { ctx->SLA_IX(d); }
    inline void SLA_IX(signed char d, unsigned char* rp = nullptr, const char* extraLog = nullptr)
    {
        unsigned short addr = (unsigned short)(reg.IX + d);
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SLA (IX+d<$%04X>) = $%02X%s", reg.PC - 4, addr, n, extraLog ? extraLog : "");
#endif
        unsigned char result = SLA(n);
        if (rp) *rp = result;
        writeByte(addr, result, 3);
    }

    // Shift operand location (IX+d) left Arithmetic with load Reg.
    static inline void SLA_IX_with_LD_B(Z80* ctx, signed char d) { ctx->SLA_IX_with_LD(d, 0b000); }
    static inline void SLA_IX_with_LD_C(Z80* ctx, signed char d) { ctx->SLA_IX_with_LD(d, 0b001); }
    static inline void SLA_IX_with_LD_D(Z80* ctx, signed char d) { ctx->SLA_IX_with_LD(d, 0b010); }
    static inline void SLA_IX_with_LD_E(Z80* ctx, signed char d) { ctx->SLA_IX_with_LD(d, 0b011); }
    static inline void SLA_IX_with_LD_H(Z80* ctx, signed char d) { ctx->SLA_IX_with_LD(d, 0b100); }
    static inline void SLA_IX_with_LD_L(Z80* ctx, signed char d) { ctx->SLA_IX_with_LD(d, 0b101); }
    static inline void SLA_IX_with_LD_A(Z80* ctx, signed char d) { ctx->SLA_IX_with_LD(d, 0b111); }
    inline void SLA_IX_with_LD(signed char d, unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        char buf[80];
        if (isDebug()) {
            snprintf(buf, sizeof(buf), " --> %s", registerDump(r));
        } else {
            buf[0] = '\0';
        }
        SLA_IX(d, rp, buf);
#else
        SLA_IX(d, rp);
#endif
    }

    // Shift operand location (IY+d) left Arithmetic with load Reg.
    static inline void SLA_IY_with_LD_B(Z80* ctx, signed char d) { ctx->SLA_IY_with_LD(d, 0b000); }
    static inline void SLA_IY_with_LD_C(Z80* ctx, signed char d) { ctx->SLA_IY_with_LD(d, 0b001); }
    static inline void SLA_IY_with_LD_D(Z80* ctx, signed char d) { ctx->SLA_IY_with_LD(d, 0b010); }
    static inline void SLA_IY_with_LD_E(Z80* ctx, signed char d) { ctx->SLA_IY_with_LD(d, 0b011); }
    static inline void SLA_IY_with_LD_H(Z80* ctx, signed char d) { ctx->SLA_IY_with_LD(d, 0b100); }
    static inline void SLA_IY_with_LD_L(Z80* ctx, signed char d) { ctx->SLA_IY_with_LD(d, 0b101); }
    static inline void SLA_IY_with_LD_A(Z80* ctx, signed char d) { ctx->SLA_IY_with_LD(d, 0b111); }
    inline void SLA_IY_with_LD(signed char d, unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        char buf[80];
        if (isDebug()) {
            snprintf(buf, sizeof(buf), " --> %s", registerDump(r));
        } else {
            buf[0] = '\0';
        }
        SLA_IY(d, rp, buf);
#else
        SLA_IY(d, rp);
#endif
    }

    // Shift operand location (IX+d) Right Arithmetic
    static inline void SRA_IX_(Z80* ctx, signed char d) { ctx->SRA_IX(d); }
    inline void SRA_IX(signed char d, unsigned char* rp = nullptr, const char* extraLog = nullptr)
    {
        unsigned short addr = (unsigned short)(reg.IX + d);
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SRA (IX+d<$%04X>) = $%02X%s", reg.PC - 4, addr, n, extraLog ? extraLog : "");
#endif
        unsigned char result = SRA(n);
        if (rp) *rp = result;
        writeByte(addr, result, 3);
    }

    // Shift operand location (IX+d) right Arithmetic with load Reg.
    static inline void SRA_IX_with_LD_B(Z80* ctx, signed char d) { ctx->SRA_IX_with_LD(d, 0b000); }
    static inline void SRA_IX_with_LD_C(Z80* ctx, signed char d) { ctx->SRA_IX_with_LD(d, 0b001); }
    static inline void SRA_IX_with_LD_D(Z80* ctx, signed char d) { ctx->SRA_IX_with_LD(d, 0b010); }
    static inline void SRA_IX_with_LD_E(Z80* ctx, signed char d) { ctx->SRA_IX_with_LD(d, 0b011); }
    static inline void SRA_IX_with_LD_H(Z80* ctx, signed char d) { ctx->SRA_IX_with_LD(d, 0b100); }
    static inline void SRA_IX_with_LD_L(Z80* ctx, signed char d) { ctx->SRA_IX_with_LD(d, 0b101); }
    static inline void SRA_IX_with_LD_A(Z80* ctx, signed char d) { ctx->SRA_IX_with_LD(d, 0b111); }
    inline void SRA_IX_with_LD(signed char d, unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        char buf[80];
        if (isDebug()) {
            snprintf(buf, sizeof(buf), " --> %s", registerDump(r));
        } else {
            buf[0] = '\0';
        }
        SRA_IX(d, rp, buf);
#else
        SRA_IX(d, rp);
#endif
    }

    // Shift operand location (IY+d) right Arithmetic with load Reg.
    static inline void SRA_IY_with_LD_B(Z80* ctx, signed char d) { ctx->SRA_IY_with_LD(d, 0b000); }
    static inline void SRA_IY_with_LD_C(Z80* ctx, signed char d) { ctx->SRA_IY_with_LD(d, 0b001); }
    static inline void SRA_IY_with_LD_D(Z80* ctx, signed char d) { ctx->SRA_IY_with_LD(d, 0b010); }
    static inline void SRA_IY_with_LD_E(Z80* ctx, signed char d) { ctx->SRA_IY_with_LD(d, 0b011); }
    static inline void SRA_IY_with_LD_H(Z80* ctx, signed char d) { ctx->SRA_IY_with_LD(d, 0b100); }
    static inline void SRA_IY_with_LD_L(Z80* ctx, signed char d) { ctx->SRA_IY_with_LD(d, 0b101); }
    static inline void SRA_IY_with_LD_A(Z80* ctx, signed char d) { ctx->SRA_IY_with_LD(d, 0b111); }
    inline void SRA_IY_with_LD(signed char d, unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        char buf[80];
        if (isDebug()) {
            snprintf(buf, sizeof(buf), " --> %s", registerDump(r));
        } else {
            buf[0] = '\0';
        }
        SRA_IY(d, rp, buf);
#else
        SRA_IY(d, rp);
#endif
    }

    // Shift operand location (IX+d) Right Logical
    static inline void SRL_IX_(Z80* ctx, signed char d) { ctx->SRL_IX(d); }
    inline void SRL_IX(signed char d, unsigned char* rp = nullptr, const char* extraLog = nullptr)
    {
        unsigned short addr = (unsigned short)(reg.IX + d);
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SRL (IX+d<$%04X>) = $%02X%s", reg.PC - 4, addr, n, extraLog ? extraLog : "");
#endif
        unsigned char result = SRL(n);
        if (rp) *rp = result;
        writeByte(addr, result, 3);
    }

    // Shift operand location (IX+d) Right Logical with load Reg.
    static inline void SRL_IX_with_LD_B(Z80* ctx, signed char d) { ctx->SRL_IX_with_LD(d, 0b000); }
    static inline void SRL_IX_with_LD_C(Z80* ctx, signed char d) { ctx->SRL_IX_with_LD(d, 0b001); }
    static inline void SRL_IX_with_LD_D(Z80* ctx, signed char d) { ctx->SRL_IX_with_LD(d, 0b010); }
    static inline void SRL_IX_with_LD_E(Z80* ctx, signed char d) { ctx->SRL_IX_with_LD(d, 0b011); }
    static inline void SRL_IX_with_LD_H(Z80* ctx, signed char d) { ctx->SRL_IX_with_LD(d, 0b100); }
    static inline void SRL_IX_with_LD_L(Z80* ctx, signed char d) { ctx->SRL_IX_with_LD(d, 0b101); }
    static inline void SRL_IX_with_LD_A(Z80* ctx, signed char d) { ctx->SRL_IX_with_LD(d, 0b111); }
    inline void SRL_IX_with_LD(signed char d, unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        char buf[80];
        if (isDebug()) {
            snprintf(buf, sizeof(buf), " --> %s", registerDump(r));
        } else {
            buf[0] = '\0';
        }
        SRL_IX(d, rp, buf);
#else
        SRL_IX(d, rp);
#endif
    }

    // Shift operand location (IY+d) Right Logical with load Reg.
    static inline void SRL_IY_with_LD_B(Z80* ctx, signed char d) { ctx->SRL_IY_with_LD(d, 0b000); }
    static inline void SRL_IY_with_LD_C(Z80* ctx, signed char d) { ctx->SRL_IY_with_LD(d, 0b001); }
    static inline void SRL_IY_with_LD_D(Z80* ctx, signed char d) { ctx->SRL_IY_with_LD(d, 0b010); }
    static inline void SRL_IY_with_LD_E(Z80* ctx, signed char d) { ctx->SRL_IY_with_LD(d, 0b011); }
    static inline void SRL_IY_with_LD_H(Z80* ctx, signed char d) { ctx->SRL_IY_with_LD(d, 0b100); }
    static inline void SRL_IY_with_LD_L(Z80* ctx, signed char d) { ctx->SRL_IY_with_LD(d, 0b101); }
    static inline void SRL_IY_with_LD_A(Z80* ctx, signed char d) { ctx->SRL_IY_with_LD(d, 0b111); }
    inline void SRL_IY_with_LD(signed char d, unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        char buf[80];
        if (isDebug()) {
            snprintf(buf, sizeof(buf), " --> %s", registerDump(r));
        } else {
            buf[0] = '\0';
        }
        SRL_IY(d, rp, buf);
#else
        SRL_IY(d, rp);
#endif
    }

    // Shift operand location (IX+d) Left Logical
    // NOTE: this function is only for SLL_IX_with_LD
    static inline void SLL_IX_(Z80* ctx, signed char d) { ctx->SLL_IX(d); }
    inline void SLL_IX(signed char d, unsigned char* rp = nullptr, const char* extraLog = nullptr)
    {
        unsigned short addr = (unsigned short)(reg.IX + d);
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SLL (IX+d<$%04X>) = $%02X%s", reg.PC - 4, addr, n, extraLog ? extraLog : "");
#endif
        unsigned char result = SLL(n);
        writeByte(addr, result, 3);
        if (rp) *rp = result;
    }

    // Shift operand location (IX+d) Left Logical with load Reg.
    static inline void SLL_IX_with_LD_B(Z80* ctx, signed char d) { ctx->SLL_IX_with_LD(d, 0b000); }
    static inline void SLL_IX_with_LD_C(Z80* ctx, signed char d) { ctx->SLL_IX_with_LD(d, 0b001); }
    static inline void SLL_IX_with_LD_D(Z80* ctx, signed char d) { ctx->SLL_IX_with_LD(d, 0b010); }
    static inline void SLL_IX_with_LD_E(Z80* ctx, signed char d) { ctx->SLL_IX_with_LD(d, 0b011); }
    static inline void SLL_IX_with_LD_H(Z80* ctx, signed char d) { ctx->SLL_IX_with_LD(d, 0b100); }
    static inline void SLL_IX_with_LD_L(Z80* ctx, signed char d) { ctx->SLL_IX_with_LD(d, 0b101); }
    static inline void SLL_IX_with_LD_A(Z80* ctx, signed char d) { ctx->SLL_IX_with_LD(d, 0b111); }
    inline void SLL_IX_with_LD(signed char d, unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        char buf[80];
        if (isDebug()) {
            snprintf(buf, sizeof(buf), " --> %s", registerDump(r));
        } else {
            buf[0] = '\0';
        }
        SLL_IX(d, rp, buf);
#else
        SLL_IX(d, rp);
#endif
    }

    // Shift operand location (IY+d) Left Logical
    // NOTE: this function is only for SLL_IY_with_LD
    static inline void SLL_IY_(Z80* ctx, signed char d) { ctx->SLL_IY(d); }
    inline void SLL_IY(signed char d, unsigned char* rp = nullptr, const char* extraLog = nullptr)
    {
        unsigned short addr = (unsigned short)(reg.IY + d);
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SLL (IY+d<$%04X>) = $%02X%s", reg.PC - 4, addr, n, extraLog ? extraLog : "");
#endif
        unsigned char result = SLL(n);
        writeByte(addr, result, 3);
        if (rp) *rp = result;
    }

    // Shift operand location (IY+d) Left Logical with load Reg.
    static inline void SLL_IY_with_LD_B(Z80* ctx, signed char d) { ctx->SLL_IY_with_LD(d, 0b000); }
    static inline void SLL_IY_with_LD_C(Z80* ctx, signed char d) { ctx->SLL_IY_with_LD(d, 0b001); }
    static inline void SLL_IY_with_LD_D(Z80* ctx, signed char d) { ctx->SLL_IY_with_LD(d, 0b010); }
    static inline void SLL_IY_with_LD_E(Z80* ctx, signed char d) { ctx->SLL_IY_with_LD(d, 0b011); }
    static inline void SLL_IY_with_LD_H(Z80* ctx, signed char d) { ctx->SLL_IY_with_LD(d, 0b100); }
    static inline void SLL_IY_with_LD_L(Z80* ctx, signed char d) { ctx->SLL_IY_with_LD(d, 0b101); }
    static inline void SLL_IY_with_LD_A(Z80* ctx, signed char d) { ctx->SLL_IY_with_LD(d, 0b111); }
    inline void SLL_IY_with_LD(signed char d, unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        char buf[80];
        if (isDebug()) {
            snprintf(buf, sizeof(buf), " --> %s", registerDump(r));
        } else {
            buf[0] = '\0';
        }
        SLL_IY(d, rp, buf);
#else
        SLL_IY(d, rp);
#endif
    }

    // Rotate memory (IY+d) Left Circular
    static inline void RLC_IY_(Z80* ctx, signed char d) { ctx->RLC_IY(d); }
    inline void RLC_IY(signed char d, unsigned char* rp = nullptr, const char* extraLog = nullptr)
    {
        unsigned short addr = (unsigned short)(reg.IY + d);
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] RLC (IY+d<$%04X>) = $%02X%s", reg.PC - 4, addr, n, extraLog ? extraLog : "");
#endif
        unsigned char result = RLC(n);
        if (rp) *rp = result;
        writeByte(addr, result, 3);
    }

    // Rotate memory (IY+d) Right Circular
    static inline void RRC_IY_(Z80* ctx, signed char d) { ctx->RRC_IY(d); }
    inline void RRC_IY(signed char d, unsigned char* rp = nullptr, const char* extraLog = nullptr)
    {
        unsigned short addr = (unsigned short)(reg.IY + d);
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] RRC (IY+d<$%04X>) = $%02X%s", reg.PC - 4, addr, n, extraLog ? extraLog : "");
#endif
        unsigned char result = RRC(n);
        if (rp) *rp = result;
        writeByte(addr, result, 3);
    }

    // Rotate Left memory
    static inline void RL_IY_(Z80* ctx, signed char d) { ctx->RL_IY(d); }
    inline void RL_IY(signed char d, unsigned char* rp = nullptr, const char* extraLog = nullptr)
    {
        unsigned short addr = (unsigned short)(reg.IY + d);
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] RL (IY+d<$%04X>) = $%02X <C:%s>%s", reg.PC - 4, addr, n, isFlagC() ? "ON" : "OFF", extraLog ? extraLog : "");
#endif
        unsigned char result = RL(n);
        if (rp) *rp = result;
        writeByte(addr, result, 3);
    }

    // Shift operand location (IY+d) left Arithmetic
    static inline void SLA_IY_(Z80* ctx, signed char d) { ctx->SLA_IY(d); }
    inline void SLA_IY(signed char d, unsigned char* rp = nullptr, const char* extraLog = nullptr)
    {
        unsigned short addr = (unsigned short)(reg.IY + d);
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SLA (IY+d<$%04X>) = $%02X%s", reg.PC - 4, addr, n, extraLog ? extraLog : "");
#endif
        unsigned char result = SLA(n);
        if (rp) *rp = result;
        writeByte(addr, result, 3);
    }

    // Rotate Right memory
    static inline void RR_IY_(Z80* ctx, signed char d) { ctx->RR_IY(d); }
    inline void RR_IY(signed char d, unsigned char* rp = nullptr, const char* extraLog = nullptr)
    {
        unsigned short addr = (unsigned short)(reg.IY + d);
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] RR (IY+d<$%04X>) = $%02X <C:%s>%s", reg.PC - 4, addr, n, isFlagC() ? "ON" : "OFF", extraLog ? extraLog : "");
#endif
        unsigned char result = RR(n);
        if (rp) *rp = result;
        writeByte(addr, result, 3);
    }

    // Shift operand location (IY+d) Right Arithmetic
    static inline void SRA_IY_(Z80* ctx, signed char d) { ctx->SRA_IY(d); }
    inline void SRA_IY(signed char d, unsigned char* rp = nullptr, const char* extraLog = nullptr)
    {
        unsigned short addr = (unsigned short)(reg.IY + d);
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SRA (IY+d<$%04X>) = $%02X%s", reg.PC - 4, addr, n, extraLog ? extraLog : "");
#endif
        unsigned char result = SRA(n);
        if (rp) *rp = result;
        writeByte(addr, result, 3);
    }

    // Shift operand location (IY+d) Right Logical
    static inline void SRL_IY_(Z80* ctx, signed char d) { ctx->SRL_IY(d); }
    inline void SRL_IY(signed char d, unsigned char* rp = nullptr, const char* extraLog = nullptr)
    {
        unsigned short addr = (unsigned short)(reg.IY + d);
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SRL (IY+d<$%04X>) = $%02X%s", reg.PC - 4, addr, n, extraLog ? extraLog : "");
#endif
        unsigned char result = SRL(n);
        if (rp) *rp = result;
        writeByte(addr, result, 3);
    }

    inline void addition8(int addition, int carry) { arithmetic8(false, addition, carry, true, true); }
    inline void subtract8(int subtract, int carry, bool setCarry = true, bool setResult = true) { arithmetic8(true, subtract, carry, setCarry, setResult); }
    inline void arithmetic8(bool negative, int addition, int carry, bool setCarry, bool setResult)
    {
        int before = reg.pair.A;
        int result = before + (negative ? -addition - carry : addition + carry);
        int carryX = before ^ addition ^ result;
        unsigned char finalResult = result & 0xFF;
        setFlagZ(0 == finalResult);
        setFlagN(negative);
        setFlagS(0x80 & finalResult);
        setFlagH(carryX & 0x10);
        setFlagPV(((carryX << 1) ^ carryX) & 0x100);
        if (setCarry) setFlagC(carryX & 0x100);
        if (setResult) {
            reg.pair.A = finalResult;
            setFlagXY(reg.pair.A);
        } else {
            setFlagXY(addition);
        }
    }

    inline void setFlagByIncrement(unsigned char before)
    {
        unsigned char finalResult = before + 1;
        resetFlagN();
        setFlagZ(0 == finalResult);
        setFlagS(0x80 & finalResult);
        setFlagH((finalResult & 0x0F) == 0x00);
        setFlagPV(finalResult == 0x80);
        setFlagXY(finalResult);
    }

    inline void setFlagByDecrement(unsigned char before)
    {
        unsigned char finalResult = before - 1;
        setFlagN();
        setFlagZ(0 == finalResult);
        setFlagS(0x80 & finalResult);
        setFlagH((finalResult & 0x0F) == 0x0F);
        setFlagPV(finalResult == 0x7F);
        setFlagXY(finalResult);
    }

    // Add Reg. r to Acc.
    static inline void ADD_B(Z80* ctx) { ctx->ADD_R(0b000); }
    static inline void ADD_C(Z80* ctx) { ctx->ADD_R(0b001); }
    static inline void ADD_D(Z80* ctx) { ctx->ADD_R(0b010); }
    static inline void ADD_E(Z80* ctx) { ctx->ADD_R(0b011); }
    static inline void ADD_H(Z80* ctx) { ctx->ADD_R(0b100); }
    static inline void ADD_L(Z80* ctx) { ctx->ADD_R(0b101); }
    static inline void ADD_A(Z80* ctx) { ctx->ADD_R(0b111); }
    static inline void ADD_B_2(Z80* ctx) { ctx->ADD_R(0b000, 2); }
    static inline void ADD_C_2(Z80* ctx) { ctx->ADD_R(0b001, 2); }
    static inline void ADD_D_2(Z80* ctx) { ctx->ADD_R(0b010, 2); }
    static inline void ADD_E_2(Z80* ctx) { ctx->ADD_R(0b011, 2); }
    static inline void ADD_A_2(Z80* ctx) { ctx->ADD_R(0b111, 2); }
    inline void ADD_R(unsigned char r, int pc = 1)
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] ADD %s, %s", reg.PC - pc, registerDump(0b111), registerDump(r));
#endif
        unsigned char* rp = getRegisterPointer(r);
        addition8(*rp, 0);
    }

    // Add IXH to Acc.
    static inline void ADD_IXH_(Z80* ctx) { ctx->ADD_IXH(); }
    inline void ADD_IXH()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] ADD %s, IXH<$%02X>", reg.PC - 2, registerDump(0b111), getIXH());
#endif
        addition8(getIXH(), 0);
    }

    // Add IXL to Acc.
    static inline void ADD_IXL_(Z80* ctx) { ctx->ADD_IXL(); }
    inline void ADD_IXL()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] ADD %s, IXL<$%02X>", reg.PC - 2, registerDump(0b111), getIXL());
#endif
        addition8(getIXL(), 0);
    }

    // Add IYH to Acc.
    static inline void ADD_IYH_(Z80* ctx) { ctx->ADD_IYH(); }
    inline void ADD_IYH()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] ADD %s, IYH<$%02X>", reg.PC - 2, registerDump(0b111), getIYH());
#endif
        addition8(getIYH(), 0);
    }

    // Add IYL to Acc.
    static inline void ADD_IYL_(Z80* ctx) { ctx->ADD_IYL(); }
    inline void ADD_IYL()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] ADD %s, IYL<$%02X>", reg.PC - 2, registerDump(0b111), getIYL());
#endif
        addition8(getIYL(), 0);
    }

    // Add value n to Acc.
    static inline void ADD_N(Z80* ctx)
    {
        unsigned char n = ctx->fetch(3);
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] ADD %s, $%02X", ctx->reg.PC - 2, ctx->registerDump(0b111), n);
#endif
        ctx->addition8(n, 0);
    }

    // Add location (HL) to Acc.
    static inline void ADD_HL(Z80* ctx)
    {
        unsigned short addr = ctx->getHL();
        unsigned char n = ctx->readByte(addr, 3);
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] ADD %s, (%s) = $%02X", ctx->reg.PC - 1, ctx->registerDump(0b111), ctx->registerPairDump(0b10), n);
#endif
        ctx->addition8(n, 0);
    }

    // Add location (IX+d) to Acc.
    static inline void ADD_IX_(Z80* ctx) { ctx->ADD_IX(); }
    inline void ADD_IX()
    {
        signed char d = (signed char)fetch(4);
        unsigned short addr = (unsigned short)(reg.IX + d);
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] ADD %s, (IX+d<$%04X>) = $%02X", reg.PC - 3, registerDump(0b111), addr, n);
#endif
        addition8(n, 0);
        consumeClock(3);
    }

    // Add location (IY+d) to Acc.
    static inline void ADD_IY_(Z80* ctx) { ctx->ADD_IY(); }
    inline void ADD_IY()
    {
        signed char d = (signed char)fetch(4);
        unsigned short addr = (unsigned short)(reg.IY + d);
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] ADD %s, (IY+d<$%04X>) = $%02X", reg.PC - 3, registerDump(0b111), addr, n);
#endif
        addition8(n, 0);
        consumeClock(3);
    }

    // Add Resister with carry
    static inline void ADC_B(Z80* ctx) { ctx->ADC_R(0b000); }
    static inline void ADC_C(Z80* ctx) { ctx->ADC_R(0b001); }
    static inline void ADC_D(Z80* ctx) { ctx->ADC_R(0b010); }
    static inline void ADC_E(Z80* ctx) { ctx->ADC_R(0b011); }
    static inline void ADC_H(Z80* ctx) { ctx->ADC_R(0b100); }
    static inline void ADC_L(Z80* ctx) { ctx->ADC_R(0b101); }
    static inline void ADC_A(Z80* ctx) { ctx->ADC_R(0b111); }
    static inline void ADC_B_2(Z80* ctx) { ctx->ADC_R(0b000, 2); }
    static inline void ADC_C_2(Z80* ctx) { ctx->ADC_R(0b001, 2); }
    static inline void ADC_D_2(Z80* ctx) { ctx->ADC_R(0b010, 2); }
    static inline void ADC_E_2(Z80* ctx) { ctx->ADC_R(0b011, 2); }
    static inline void ADC_A_2(Z80* ctx) { ctx->ADC_R(0b111, 2); }
    inline void ADC_R(unsigned char r, int pc = 1)
    {
        unsigned char* rp = getRegisterPointer(r);
        unsigned char c = isFlagC() ? 1 : 0;
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] ADC %s, %s <C:%s>", reg.PC - pc, registerDump(0b111), registerDump(r), c ? "ON" : "OFF");
#endif
        addition8(*rp, c);
    }

    // Add IXH to Acc.
    static inline void ADC_IXH_(Z80* ctx) { ctx->ADC_IXH(); }
    inline void ADC_IXH()
    {
        unsigned char c = isFlagC() ? 1 : 0;
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] ADC %s, IXH<$%02X> <C:%s>", reg.PC - 2, registerDump(0b111), getIXH(), c ? "ON" : "OFF");
#endif
        addition8(getIXH(), c);
    }

    // Add IXL to Acc.
    static inline void ADC_IXL_(Z80* ctx) { ctx->ADC_IXL(); }
    inline void ADC_IXL()
    {
        unsigned char c = isFlagC() ? 1 : 0;
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] ADC %s, IXL<$%02X> <C:%s>", reg.PC - 2, registerDump(0b111), getIXL(), c ? "ON" : "OFF");
#endif
        addition8(getIXL(), c);
    }

    // Add IYH to Acc.
    static inline void ADC_IYH_(Z80* ctx) { ctx->ADC_IYH(); }
    inline void ADC_IYH()
    {
        unsigned char c = isFlagC() ? 1 : 0;
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] ADC %s, IYH<$%02X> <C:%s>", reg.PC - 2, registerDump(0b111), getIYH(), c ? "ON" : "OFF");
#endif
        addition8(getIYH(), c);
    }

    // Add IYL to Acc.
    static inline void ADC_IYL_(Z80* ctx) { ctx->ADC_IYL(); }
    inline void ADC_IYL()
    {
        unsigned char c = isFlagC() ? 1 : 0;
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] ADC %s, IYL<$%02X> <C:%s>", reg.PC - 2, registerDump(0b111), getIYL(), c ? "ON" : "OFF");
#endif
        addition8(getIYL(), c);
    }

    // Add immediate with carry
    static inline void ADC_N(Z80* ctx)
    {
        unsigned char n = ctx->fetch(3);
        unsigned char c = ctx->isFlagC() ? 1 : 0;
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] ADC %s, $%02X <C:%s>", ctx->reg.PC - 2, ctx->registerDump(0b111), n, c ? "ON" : "OFF");
#endif
        ctx->addition8(n, c);
    }

    // Add memory with carry
    static inline void ADC_HL(Z80* ctx)
    {
        unsigned short addr = ctx->getHL();
        unsigned char n = ctx->readByte(addr, 3);
        unsigned char c = ctx->isFlagC() ? 1 : 0;
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] ADC %s, (%s) = $%02X <C:%s>", ctx->reg.PC - 1, ctx->registerDump(0b111), ctx->registerPairDump(0b10), n, c ? "ON" : "OFF");
#endif
        ctx->addition8(n, c);
    }

    // Add memory with carry
    static inline void ADC_IX_(Z80* ctx) { ctx->ADC_IX(); }
    inline void ADC_IX()
    {
        signed char d = (signed char)fetch(4);
        unsigned short addr = (unsigned short)(reg.IX + d);
        unsigned char n = readByte(addr);
        unsigned char c = isFlagC() ? 1 : 0;
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] ADC %s, (IX+d<$%04X>) = $%02X <C:%s>", reg.PC - 3, registerDump(0b111), addr, n, c ? "ON" : "OFF");
#endif
        addition8(n, c);
        consumeClock(3);
    }

    // Add memory with carry
    static inline void ADC_IY_(Z80* ctx) { ctx->ADC_IY(); }
    inline void ADC_IY()
    {
        signed char d = (signed char)fetch(4);
        unsigned short addr = (unsigned short)(reg.IY + d);
        unsigned char n = readByte(addr);
        unsigned char c = isFlagC() ? 1 : 0;
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] ADC %s, (IY+d<$%04X>) = $%02X <C:%s>", reg.PC - 3, registerDump(0b111), addr, n, c ? "ON" : "OFF");
#endif
        addition8(n, c);
        consumeClock(3);
    }

    // Increment Register
    static inline void INC_B(Z80* ctx) { ctx->INC_R(0b000); }
    static inline void INC_C(Z80* ctx) { ctx->INC_R(0b001); }
    static inline void INC_D(Z80* ctx) { ctx->INC_R(0b010); }
    static inline void INC_E(Z80* ctx) { ctx->INC_R(0b011); }
    static inline void INC_H(Z80* ctx) { ctx->INC_R(0b100); }
    static inline void INC_L(Z80* ctx) { ctx->INC_R(0b101); }
    static inline void INC_A(Z80* ctx) { ctx->INC_R(0b111); }
    static inline void INC_B_2(Z80* ctx) { ctx->INC_R(0b000, 2); }
    static inline void INC_C_2(Z80* ctx) { ctx->INC_R(0b001, 2); }
    static inline void INC_D_2(Z80* ctx) { ctx->INC_R(0b010, 2); }
    static inline void INC_E_2(Z80* ctx) { ctx->INC_R(0b011, 2); }
    static inline void INC_A_2(Z80* ctx) { ctx->INC_R(0b111, 2); }
    inline void INC_R(unsigned char r, int pc = 1)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] INC %s", reg.PC - pc, registerDump(r));
#endif
        setFlagByIncrement(*rp);
        (*rp)++;
    }

    // Increment location (HL)
    static inline void INC_HL(Z80* ctx)
    {
        unsigned short addr = ctx->getHL();
        unsigned char n = ctx->readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] INC (%s) = $%02X", ctx->reg.PC - 1, ctx->registerPairDump(0b10), n);
#endif
        ctx->setFlagByIncrement(n);
        ctx->writeByte(addr, n + 1, 3);
    }

    // Increment location (IX+d)
    static inline void INC_IX_(Z80* ctx) { ctx->INC_IX(); }
    inline void INC_IX()
    {
        signed char d = (signed char)fetch(4);
        unsigned short addr = (unsigned short)(reg.IX + d);
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] INC (IX+d<$%04X>) = $%02X", reg.PC - 3, addr, n);
#endif
        setFlagByIncrement(n);
        writeByte(addr, n + 1);
        consumeClock(3);
    }

    // Increment register high 8 bits of IX
    static inline void INC_IXH_(Z80* ctx) { ctx->INC_IXH(); }
    inline void INC_IXH()
    {
        unsigned char ixh = getIXH();
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] INC IXH<$%02X>", reg.PC - 2, ixh);
#endif
        setFlagByIncrement(ixh++);
        setIXH(ixh);
    }

    // Increment register low 8 bits of IX
    static inline void INC_IXL_(Z80* ctx) { ctx->INC_IXL(); }
    inline void INC_IXL()
    {
        unsigned char ixl = getIXL();
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] INC IXL<$%02X>", reg.PC - 2, ixl);
#endif
        setFlagByIncrement(ixl++);
        setIXL(ixl);
    }

    // Increment location (IY+d)
    static inline void INC_IY_(Z80* ctx) { ctx->INC_IY(); }
    inline void INC_IY()
    {
        signed char d = (signed char)fetch(4);
        unsigned short addr = (unsigned short)(reg.IY + d);
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] INC (IY+d<$%04X>) = $%02X", reg.PC - 3, addr, n);
#endif
        setFlagByIncrement(n);
        writeByte(addr, n + 1);
        consumeClock(3);
    }

    // Increment register high 8 bits of IY
    static inline void INC_IYH_(Z80* ctx) { ctx->INC_IYH(); }
    inline void INC_IYH()
    {
        unsigned char iyh = getIYH();
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] INC IYH<$%02X>", reg.PC - 2, iyh);
#endif
        setFlagByIncrement(iyh++);
        setIYH(iyh);
    }

    // Increment register low 8 bits of IY
    static inline void INC_IYL_(Z80* ctx) { ctx->INC_IYL(); }
    inline void INC_IYL()
    {
        unsigned char iyl = getIYL();
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] INC IYL<$%02X>", reg.PC - 2, iyl);
#endif
        setFlagByIncrement(iyl++);
        setIYL(iyl);
    }

    // Subtract Register
    static inline void SUB_B(Z80* ctx) { ctx->SUB_R(0b000); }
    static inline void SUB_C(Z80* ctx) { ctx->SUB_R(0b001); }
    static inline void SUB_D(Z80* ctx) { ctx->SUB_R(0b010); }
    static inline void SUB_E(Z80* ctx) { ctx->SUB_R(0b011); }
    static inline void SUB_H(Z80* ctx) { ctx->SUB_R(0b100); }
    static inline void SUB_L(Z80* ctx) { ctx->SUB_R(0b101); }
    static inline void SUB_A(Z80* ctx) { ctx->SUB_R(0b111); }
    static inline void SUB_B_2(Z80* ctx) { ctx->SUB_R(0b000, 2); }
    static inline void SUB_C_2(Z80* ctx) { ctx->SUB_R(0b001, 2); }
    static inline void SUB_D_2(Z80* ctx) { ctx->SUB_R(0b010, 2); }
    static inline void SUB_E_2(Z80* ctx) { ctx->SUB_R(0b011, 2); }
    static inline void SUB_A_2(Z80* ctx) { ctx->SUB_R(0b111, 2); }
    inline void SUB_R(unsigned char r, int pc = 1)
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SUB %s, %s", reg.PC - pc, registerDump(0b111), registerDump(r));
#endif
        unsigned char* rp = getRegisterPointer(r);
        subtract8(*rp, 0);
    }

    // Subtract IXH to Acc.
    static inline void SUB_IXH_(Z80* ctx) { ctx->SUB_IXH(); }
    inline void SUB_IXH()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SUB %s, IXH<$%02X>", reg.PC - 2, registerDump(0b111), getIXH());
#endif
        subtract8(getIXH(), 0);
    }

    // Subtract IXL to Acc.
    static inline void SUB_IXL_(Z80* ctx) { ctx->SUB_IXL(); }
    inline void SUB_IXL()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SUB %s, IXL<$%02X>", reg.PC - 2, registerDump(0b111), getIXL());
#endif
        subtract8(getIXL(), 0);
    }

    // Subtract IYH to Acc.
    static inline void SUB_IYH_(Z80* ctx) { ctx->SUB_IYH(); }
    inline void SUB_IYH()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SUB %s, IYH<$%02X>", reg.PC - 2, registerDump(0b111), getIYH());
#endif
        subtract8(getIYH(), 0);
    }

    // Subtract IYL to Acc.
    static inline void SUB_IYL_(Z80* ctx) { ctx->SUB_IYL(); }
    inline void SUB_IYL()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SUB %s, IYL<$%02X>", reg.PC - 2, registerDump(0b111), getIYL());
#endif
        subtract8(getIYL(), 0);
    }

    // Subtract immediate
    static inline void SUB_N(Z80* ctx)
    {
        unsigned char n = ctx->fetch(3);
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] SUB %s, $%02X", ctx->reg.PC - 2, ctx->registerDump(0b111), n);
#endif
        ctx->subtract8(n, 0);
    }

    // Subtract memory
    static inline void SUB_HL(Z80* ctx)
    {
        unsigned short addr = ctx->getHL();
        unsigned char n = ctx->readByte(addr, 3);
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] SUB %s, (%s) = $%02X", ctx->reg.PC - 1, ctx->registerDump(0b111), ctx->registerPairDump(0b10), n);
#endif
        ctx->subtract8(n, 0);
    }

    // Subtract memory
    static inline void SUB_IX_(Z80* ctx) { ctx->SUB_IX(); }
    inline void SUB_IX()
    {
        signed char d = (signed char)fetch(4);
        unsigned short addr = (unsigned short)(reg.IX + d);
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SUB %s, (IX+d<$%04X>) = $%02X", reg.PC - 3, registerDump(0b111), addr, n);
#endif
        subtract8(n, 0);
        consumeClock(3);
    }

    // Subtract memory
    static inline void SUB_IY_(Z80* ctx) { ctx->SUB_IY(); }
    inline void SUB_IY()
    {
        signed char d = (signed char)fetch(4);
        unsigned short addr = (unsigned short)(reg.IY + d);
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SUB %s, (IY+d<$%04X>) = $%02X", reg.PC - 3, registerDump(0b111), addr, n);
#endif
        subtract8(n, 0);
        consumeClock(3);
    }

    // Subtract Resister with carry
    static inline void SBC_B(Z80* ctx) { ctx->SBC_R(0b000); }
    static inline void SBC_C(Z80* ctx) { ctx->SBC_R(0b001); }
    static inline void SBC_D(Z80* ctx) { ctx->SBC_R(0b010); }
    static inline void SBC_E(Z80* ctx) { ctx->SBC_R(0b011); }
    static inline void SBC_H(Z80* ctx) { ctx->SBC_R(0b100); }
    static inline void SBC_L(Z80* ctx) { ctx->SBC_R(0b101); }
    static inline void SBC_A(Z80* ctx) { ctx->SBC_R(0b111); }
    static inline void SBC_B_2(Z80* ctx) { ctx->SBC_R(0b000, 2); }
    static inline void SBC_C_2(Z80* ctx) { ctx->SBC_R(0b001, 2); }
    static inline void SBC_D_2(Z80* ctx) { ctx->SBC_R(0b010, 2); }
    static inline void SBC_E_2(Z80* ctx) { ctx->SBC_R(0b011, 2); }
    static inline void SBC_A_2(Z80* ctx) { ctx->SBC_R(0b111, 2); }
    inline void SBC_R(unsigned char r, int pc = 1)
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SBC %s, %s <C:%s>", reg.PC - pc, registerDump(0b111), registerDump(r), isFlagC() ? "ON" : "OFF");
#endif
        subtract8(getRegister(r), isFlagC() ? 1 : 0);
    }

    // Subtract IXH to Acc. with carry
    static inline void SBC_IXH_(Z80* ctx) { ctx->SBC_IXH(); }
    inline void SBC_IXH()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SBC %s, IXH<$%02X> <C:%s>", reg.PC - 2, registerDump(0b111), getIXH(), isFlagC() ? "ON" : "OFF");
#endif
        subtract8(getIXH(), isFlagC() ? 1 : 0);
    }

    // Subtract IXL to Acc. with carry
    static inline void SBC_IXL_(Z80* ctx) { ctx->SBC_IXL(); }
    inline void SBC_IXL()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SBC %s, IXL<$%02X> <C:%s>", reg.PC - 2, registerDump(0b111), getIXL(), isFlagC() ? "ON" : "OFF");
#endif
        subtract8(getIXL(), isFlagC() ? 1 : 0);
    }

    // Subtract IYH to Acc. with carry
    static inline void SBC_IYH_(Z80* ctx) { ctx->SBC_IYH(); }
    inline void SBC_IYH()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SBC %s, IYH<$%02X> <C:%s>", reg.PC - 2, registerDump(0b111), getIYH(), isFlagC() ? "ON" : "OFF");
#endif
        subtract8(getIYH(), isFlagC() ? 1 : 0);
    }

    // Subtract IYL to Acc. with carry
    static inline void SBC_IYL_(Z80* ctx) { ctx->SBC_IYL(); }
    inline void SBC_IYL()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SBC %s, IYL<$%02X> <C:%s>", reg.PC - 2, registerDump(0b111), getIYL(), isFlagC() ? "ON" : "OFF");
#endif
        subtract8(getIYL(), isFlagC() ? 1 : 0);
    }

    // Subtract immediate with carry
    static inline void SBC_N(Z80* ctx)
    {
        unsigned char n = ctx->fetch(3);
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] SBC %s, $%02X <C:%s>", ctx->reg.PC - 2, ctx->registerDump(0b111), n, ctx->isFlagC() ? "ON" : "OFF");
#endif
        ctx->subtract8(n, ctx->isFlagC() ? 1 : 0);
    }

    // Subtract memory with carry
    static inline void SBC_HL(Z80* ctx)
    {
        unsigned char n = ctx->readByte(ctx->getHL(), 3);
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] SBC %s, (%s) = $%02X <C:%s>", ctx->reg.PC - 1, ctx->registerDump(0b111), ctx->registerPairDump(0b10), n, ctx->isFlagC() ? "ON" : "OFF");
#endif
        ctx->subtract8(n, ctx->isFlagC() ? 1 : 0);
    }

    // Subtract memory with carry
    static inline void SBC_IX_(Z80* ctx) { ctx->SBC_IX(); }
    inline void SBC_IX()
    {
        signed char d = (signed char)fetch(4);
        unsigned char n = readByte((unsigned short)(reg.IX + d));
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SBC %s, (IX+d<$%04X>) = $%02X <C:%s>", reg.PC - 3, registerDump(0b111), (unsigned short)(reg.IX + d), n, isFlagC() ? "ON" : "OFF");
#endif
        subtract8(n, isFlagC() ? 1 : 0);
        consumeClock(3);
    }

    // Subtract memory with carry
    static inline void SBC_IY_(Z80* ctx) { ctx->SBC_IY(); }
    inline void SBC_IY()
    {
        signed char d = (signed char)fetch(4);
        unsigned char n = readByte((unsigned short)(reg.IY + d));
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SBC %s, (IY+d<$%04X>) = $%02X <C:%s>", reg.PC - 3, registerDump(0b111), (unsigned short)(reg.IY + d), n, isFlagC() ? "ON" : "OFF");
#endif
        subtract8(n, isFlagC() ? 1 : 0);
        consumeClock(3);
    }

    // Decrement Register
    static inline void DEC_B(Z80* ctx) { ctx->DEC_R(0b000); }
    static inline void DEC_C(Z80* ctx) { ctx->DEC_R(0b001); }
    static inline void DEC_D(Z80* ctx) { ctx->DEC_R(0b010); }
    static inline void DEC_E(Z80* ctx) { ctx->DEC_R(0b011); }
    static inline void DEC_H(Z80* ctx) { ctx->DEC_R(0b100); }
    static inline void DEC_L(Z80* ctx) { ctx->DEC_R(0b101); }
    static inline void DEC_A(Z80* ctx) { ctx->DEC_R(0b111); }
    static inline void DEC_B_2(Z80* ctx) { ctx->DEC_R(0b000, 2); }
    static inline void DEC_C_2(Z80* ctx) { ctx->DEC_R(0b001, 2); }
    static inline void DEC_D_2(Z80* ctx) { ctx->DEC_R(0b010, 2); }
    static inline void DEC_E_2(Z80* ctx) { ctx->DEC_R(0b011, 2); }
    static inline void DEC_A_2(Z80* ctx) { ctx->DEC_R(0b111, 2); }
    inline void DEC_R(unsigned char r, int pc = 1)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] DEC %s", reg.PC - pc, registerDump(r));
#endif
        setFlagByDecrement(*rp);
        (*rp)--;
    }

    // Decrement location (HL)
    static inline void DEC_HL(Z80* ctx)
    {
        unsigned short addr = ctx->getHL();
        unsigned char n = ctx->readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] DEC (%s) = $%02X", ctx->reg.PC - 1, ctx->registerPairDump(0b10), n);
#endif
        ctx->setFlagByDecrement(n);
        ctx->writeByte(addr, n - 1, 3);
    }

    // Decrement location (IX+d)
    static inline void DEC_IX_(Z80* ctx) { ctx->DEC_IX(); }
    inline void DEC_IX()
    {
        signed char d = (signed char)fetch(4);
        unsigned short addr = (unsigned short)(reg.IX + d);
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] DEC (IX+d<$%04X>) = $%02X", reg.PC - 3, addr, n);
#endif
        setFlagByDecrement(n);
        writeByte(addr, n - 1);
        consumeClock(3);
    }

    // Decrement high 8 bits of IX
    static inline void DEC_IXH_(Z80* ctx) { ctx->DEC_IXH(); }
    inline void DEC_IXH()
    {
        unsigned char ixh = getIXH();
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] DEC IXH<$%02X>", reg.PC - 2, ixh);
#endif
        setFlagByDecrement(ixh--);
        setIXH(ixh);
    }

    // Decrement low 8 bits of IX
    static inline void DEC_IXL_(Z80* ctx) { ctx->DEC_IXL(); }
    inline void DEC_IXL()
    {
        unsigned char ixl = getIXL();
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] DEC IXL<$%02X>", reg.PC - 2, ixl);
#endif
        setFlagByDecrement(ixl--);
        setIXL(ixl);
    }

    // Decrement location (IY+d)
    static inline void DEC_IY_(Z80* ctx) { ctx->DEC_IY(); }
    inline void DEC_IY()
    {
        signed char d = (signed char)fetch(4);
        unsigned short addr = (unsigned short)(reg.IY + d);
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] DEC (IY+d<$%04X>) = $%02X", reg.PC - 3, addr, n);
#endif
        setFlagByDecrement(n);
        writeByte(addr, n - 1);
        consumeClock(3);
    }

    // Decrement high 8 bits of IY
    static inline void DEC_IYH_(Z80* ctx) { ctx->DEC_IYH(); }
    inline void DEC_IYH()
    {
        unsigned char iyh = getIYH();
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] DEC IYH<$%02X>", reg.PC - 2, iyh);
#endif
        setFlagByDecrement(iyh--);
        setIYH(iyh);
    }

    // Decrement low 8 bits of IY
    static inline void DEC_IYL_(Z80* ctx) { ctx->DEC_IYL(); }
    inline void DEC_IYL()
    {
        unsigned char iyl = getIYL();
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] DEC IYL<$%02X>", reg.PC - 2, iyl);
#endif
        setFlagByDecrement(iyl--);
        setIYL(iyl);
    }

    inline void setFlagByAdd16(unsigned short before, unsigned short addition)
    {
        int result = before + addition;
        int carrybits = before ^ addition ^ result;
        resetFlagN();
        setFlagXY((result & 0xFF00) >> 8);
        setFlagC((carrybits & 0x10000) != 0);
        setFlagH((carrybits & 0x1000) != 0);
    }

    inline void setFlagByAdc16(unsigned short before, unsigned short addition)
    {
        int result = before + addition;
        int carrybits = before ^ addition ^ result;
        unsigned short finalResult = (unsigned short)(result);
        // same as ADD
        resetFlagN();
        setFlagXY((finalResult & 0xFF00) >> 8);
        setFlagC((carrybits & 0x10000) != 0);
        setFlagH((carrybits & 0x1000) != 0);
        // only ADC
        setFlagS(finalResult & 0x8000);
        setFlagZ(0 == finalResult);
        setFlagPV((((carrybits << 1) ^ carrybits) & 0x10000) != 0);
    }

    // Add register pair to H and L
    static inline void ADD_HL_BC(Z80* ctx) { ctx->ADD_HL_RP(0b00); }
    static inline void ADD_HL_DE(Z80* ctx) { ctx->ADD_HL_RP(0b01); }
    static inline void ADD_HL_HL(Z80* ctx) { ctx->ADD_HL_RP(0b10); }
    static inline void ADD_HL_SP(Z80* ctx) { ctx->ADD_HL_RP(0b11); }
    inline void ADD_HL_RP(unsigned char rp)
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] ADD %s, %s", reg.PC - 1, registerPairDump(0b10), registerPairDump(rp));
#endif
        unsigned short hl = getHL();
        unsigned short nn = getRP(rp);
        reg.WZ = nn + 1;
        setFlagByAdd16(hl, nn);
        setHL(hl + nn);
        consumeClock(7);
    }

    // Add with carry register pair to HL
    static inline void ADC_HL_BC(Z80* ctx) { ctx->ADC_HL_RP(0b00); }
    static inline void ADC_HL_DE(Z80* ctx) { ctx->ADC_HL_RP(0b01); }
    static inline void ADC_HL_HL(Z80* ctx) { ctx->ADC_HL_RP(0b10); }
    static inline void ADC_HL_SP(Z80* ctx) { ctx->ADC_HL_RP(0b11); }
    inline void ADC_HL_RP(unsigned char rp)
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] ADC %s, %s <C:%s>", reg.PC - 2, registerPairDump(0b10), registerPairDump(rp), isFlagC() ? "ON" : "OFF");
#endif
        unsigned short hl = getHL();
        unsigned short nn = getRP(rp);
        unsigned char c = isFlagC() ? 1 : 0;
        reg.WZ = hl + 1;
        setFlagByAdc16(hl, c + nn);
        setHL(hl + c + nn);
        consumeClock(7);
    }

    // Add register pair to IX
    static inline void ADD_IX_BC(Z80* ctx) { ctx->ADD_IX_RP(0b00); }
    static inline void ADD_IX_DE(Z80* ctx) { ctx->ADD_IX_RP(0b01); }
    static inline void ADD_IX_IX(Z80* ctx) { ctx->ADD_IX_RP(0b10); }
    static inline void ADD_IX_SP(Z80* ctx) { ctx->ADD_IX_RP(0b11); }
    inline void ADD_IX_RP(unsigned char rp)
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] ADD IX<$%04X>, %s", reg.PC - 2, reg.IX, registerPairDumpIX(rp));
#endif
        unsigned short nn = getRPIX(rp);
        setFlagByAdd16(reg.IX, nn);
        reg.IX += nn;
        consumeClock(7);
    }

    // Add register pair to IY
    static inline void ADD_IY_BC(Z80* ctx) { ctx->ADD_IY_RP(0b00); }
    static inline void ADD_IY_DE(Z80* ctx) { ctx->ADD_IY_RP(0b01); }
    static inline void ADD_IY_IY(Z80* ctx) { ctx->ADD_IY_RP(0b10); }
    static inline void ADD_IY_SP(Z80* ctx) { ctx->ADD_IY_RP(0b11); }
    inline void ADD_IY_RP(unsigned char rp)
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] ADD IY<$%04X>, %s", reg.PC - 2, reg.IY, registerPairDumpIY(rp));
#endif
        unsigned short nn = getRPIY(rp);
        setFlagByAdd16(reg.IY, nn);
        reg.IY += nn;
        consumeClock(7);
    }

    // Increment register pair
    static inline void INC_RP_BC(Z80* ctx) { ctx->INC_RP(0b00); }
    static inline void INC_RP_DE(Z80* ctx) { ctx->INC_RP(0b01); }
    static inline void INC_RP_HL(Z80* ctx) { ctx->INC_RP(0b10); }
    static inline void INC_RP_SP(Z80* ctx) { ctx->INC_RP(0b11); }
    inline void INC_RP(unsigned char rp)
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] INC %s", reg.PC - 1, registerPairDump(rp));
#endif
        setRP(rp, getRP(rp) + 1);
        consumeClock(2);
    }

    // Increment IX
    static inline void INC_IX_reg_(Z80* ctx) { ctx->INC_IX_reg(); }
    inline void INC_IX_reg()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] INC IX<$%04X>", reg.PC - 2, reg.IX);
#endif
        reg.IX++;
        consumeClock(2);
    }

    // Increment IY
    static inline void INC_IY_reg_(Z80* ctx) { ctx->INC_IY_reg(); }
    inline void INC_IY_reg()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] INC IY<$%04X>", reg.PC - 2, reg.IY);
#endif
        reg.IY++;
        consumeClock(2);
    }

    // Decrement register pair
    static inline void DEC_RP_BC(Z80* ctx) { ctx->DEC_RP(0b00); }
    static inline void DEC_RP_DE(Z80* ctx) { ctx->DEC_RP(0b01); }
    static inline void DEC_RP_HL(Z80* ctx) { ctx->DEC_RP(0b10); }
    static inline void DEC_RP_SP(Z80* ctx) { ctx->DEC_RP(0b11); }
    inline void DEC_RP(unsigned char rp)
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] DEC %s", reg.PC - 1, registerPairDump(rp));
#endif
        setRP(rp, getRP(rp) - 1);
        consumeClock(2);
    }

    // Decrement IX
    static inline void DEC_IX_reg_(Z80* ctx) { ctx->DEC_IX_reg(); }
    inline void DEC_IX_reg()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] DEC IX<$%04X>", reg.PC - 2, reg.IX);
#endif
        reg.IX--;
        consumeClock(2);
    }

    // Decrement IY
    static inline void DEC_IY_reg_(Z80* ctx) { ctx->DEC_IY_reg(); }
    inline void DEC_IY_reg()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] DEC IY<$%04X>", reg.PC - 2, reg.IY);
#endif
        reg.IY--;
        consumeClock(2);
    }

    inline void setFlagBySbc16(unsigned short before, unsigned short subtract)
    {
        int result = before - subtract;
        int carrybits = before ^ subtract ^ result;
        unsigned short finalResult = (unsigned short)result;
        setFlagN();
        setFlagXY((finalResult & 0xFF00) >> 8);
        setFlagC((carrybits & 0x10000) != 0);
        setFlagH((carrybits & 0x1000) != 0);
        setFlagS(finalResult & 0x8000);
        setFlagZ(0 == finalResult);
        setFlagPV((((carrybits << 1) ^ carrybits) & 0x10000) != 0);
    }

    // Subtract register pair from HL with carry
    static inline void SBC_HL_BC(Z80* ctx) { ctx->SBC_HL_RP(0b00); }
    static inline void SBC_HL_DE(Z80* ctx) { ctx->SBC_HL_RP(0b01); }
    static inline void SBC_HL_HL(Z80* ctx) { ctx->SBC_HL_RP(0b10); }
    static inline void SBC_HL_SP(Z80* ctx) { ctx->SBC_HL_RP(0b11); }
    inline void SBC_HL_RP(unsigned char rp)
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SBC %s, %s <C:%s>", reg.PC - 2, registerPairDump(0b10), registerPairDump(rp), isFlagC() ? "ON" : "OFF");
#endif
        unsigned short hl = getHL();
        unsigned short nn = getRP(rp);
        unsigned char c = isFlagC() ? 1 : 0;
        reg.WZ = hl + 1;
        setFlagBySbc16(hl, c + nn);
        setHL(hl - c - nn);
        consumeClock(7);
    }

    inline void setFlagByLogical()
    {
        setFlagS(reg.pair.A & 0x80);
        setFlagZ(reg.pair.A == 0);
        setFlagXY(reg.pair.A);
        setFlagPV(isEvenNumberBits(reg.pair.A));
        resetFlagN();
        resetFlagC();
    }

    inline void and8(unsigned char n)
    {
        reg.pair.A &= n;
        setFlagByLogical();
        setFlagH();
    }

    inline void or8(unsigned char n)
    {
        reg.pair.A |= n;
        setFlagByLogical();
        resetFlagH();
    }

    inline void xor8(unsigned char n)
    {
        reg.pair.A ^= n;
        setFlagByLogical();
        resetFlagH();
    }

    // AND Register
    static inline void AND_B(Z80* ctx) { ctx->AND_R(0b000); }
    static inline void AND_C(Z80* ctx) { ctx->AND_R(0b001); }
    static inline void AND_D(Z80* ctx) { ctx->AND_R(0b010); }
    static inline void AND_E(Z80* ctx) { ctx->AND_R(0b011); }
    static inline void AND_H(Z80* ctx) { ctx->AND_R(0b100); }
    static inline void AND_L(Z80* ctx) { ctx->AND_R(0b101); }
    static inline void AND_A(Z80* ctx) { ctx->AND_R(0b111); }
    static inline void AND_B_2(Z80* ctx) { ctx->AND_R(0b000, 2); }
    static inline void AND_C_2(Z80* ctx) { ctx->AND_R(0b001, 2); }
    static inline void AND_D_2(Z80* ctx) { ctx->AND_R(0b010, 2); }
    static inline void AND_E_2(Z80* ctx) { ctx->AND_R(0b011, 2); }
    static inline void AND_A_2(Z80* ctx) { ctx->AND_R(0b111, 2); }
    inline void AND_R(unsigned char r, int pc = 1)
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] AND %s, %s", reg.PC - pc, registerDump(0b111), registerDump(r));
#endif
        and8(getRegister(r));
    }

    // AND with register IXH
    static inline void AND_IXH_(Z80* ctx) { ctx->AND_IXH(); }
    inline void AND_IXH()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] AND %s, IXH<$%02X>", reg.PC - 2, registerDump(0b111), getIXH());
#endif
        and8(getIXH());
    }

    // AND with register IXL
    static inline void AND_IXL_(Z80* ctx) { ctx->AND_IXL(); }
    inline void AND_IXL()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] AND %s, IXL<$%02X>", reg.PC - 2, registerDump(0b111), getIXL());
#endif
        and8(getIXL());
    }

    // AND with register IYH
    static inline void AND_IYH_(Z80* ctx) { ctx->AND_IYH(); }
    inline void AND_IYH()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] AND %s, IYH<$%02X>", reg.PC - 2, registerDump(0b111), getIYH());
#endif
        and8(getIYH());
    }

    // AND with register IYL
    static inline void AND_IYL_(Z80* ctx) { ctx->AND_IYL(); }
    inline void AND_IYL()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] AND %s, IYL<$%02X>", reg.PC - 2, registerDump(0b111), getIYL());
#endif
        and8(getIYL());
    }

    // AND immediate
    static inline void AND_N(Z80* ctx)
    {
        unsigned char n = ctx->fetch(3);
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] AND %s, $%02X", ctx->reg.PC - 2, ctx->registerDump(0b111), n);
#endif
        ctx->and8(n);
    }

    // AND Memory
    static inline void AND_HL(Z80* ctx)
    {
        unsigned short addr = ctx->getHL();
        unsigned char n = ctx->readByte(addr, 3);
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] AND %s, (%s) = $%02X", ctx->reg.PC - 1, ctx->registerDump(0b111), ctx->registerPairDump(0b10), n);
#endif
        ctx->and8(n);
    }

    // AND Memory
    static inline void AND_IX_(Z80* ctx) { ctx->AND_IX(); }
    inline void AND_IX()
    {
        signed char d = (signed char)fetch(4);
        unsigned char n = readByte((unsigned short)(reg.IX + d));
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] AND %s, (IX+d<$%04X>) = $%02X", reg.PC - 3, registerDump(0b111), (unsigned short)(reg.IX + d), reg.pair.A & n);
#endif
        and8(n);
        consumeClock(3);
    }

    // AND Memory
    static inline void AND_IY_(Z80* ctx) { ctx->AND_IY(); }
    inline void AND_IY()
    {
        signed char d = (signed char)fetch(4);
        unsigned char n = readByte((unsigned short)(reg.IY + d));
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] AND %s, (IY+d<$%04X>) = $%02X", reg.PC - 3, registerDump(0b111), (unsigned short)(reg.IY + d), reg.pair.A & n);
#endif
        and8(n);
        consumeClock(3);
    }

    // OR Register
    static inline void OR_B(Z80* ctx) { ctx->OR_R(0b000); }
    static inline void OR_C(Z80* ctx) { ctx->OR_R(0b001); }
    static inline void OR_D(Z80* ctx) { ctx->OR_R(0b010); }
    static inline void OR_E(Z80* ctx) { ctx->OR_R(0b011); }
    static inline void OR_H(Z80* ctx) { ctx->OR_R(0b100); }
    static inline void OR_L(Z80* ctx) { ctx->OR_R(0b101); }
    static inline void OR_A(Z80* ctx) { ctx->OR_R(0b111); }
    static inline void OR_B_2(Z80* ctx) { ctx->OR_R(0b000, 2); }
    static inline void OR_C_2(Z80* ctx) { ctx->OR_R(0b001, 2); }
    static inline void OR_D_2(Z80* ctx) { ctx->OR_R(0b010, 2); }
    static inline void OR_E_2(Z80* ctx) { ctx->OR_R(0b011, 2); }
    static inline void OR_A_2(Z80* ctx) { ctx->OR_R(0b111, 2); }
    inline void OR_R(unsigned char r, int pc = 1)
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] OR %s, %s", reg.PC - pc, registerDump(0b111), registerDump(r));
#endif
        or8(getRegister(r));
    }

    // OR with register IXH
    static inline void OR_IXH_(Z80* ctx) { ctx->OR_IXH(); }
    inline void OR_IXH()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] OR %s, IXH<$%02X>", reg.PC - 2, registerDump(0b111), getIXH());
#endif
        or8(getIXH());
    }

    // OR with register IXL
    static inline void OR_IXL_(Z80* ctx) { ctx->OR_IXL(); }
    inline void OR_IXL()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] OR %s, IXL<$%02X>", reg.PC - 2, registerDump(0b111), getIXL());
#endif
        or8(getIXL());
    }

    // OR with register IYH
    static inline void OR_IYH_(Z80* ctx) { ctx->OR_IYH(); }
    inline void OR_IYH()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] OR %s, IYH<$%02X>", reg.PC - 2, registerDump(0b111), getIYH());
#endif
        or8(getIYH());
    }

    // OR with register IYL
    static inline void OR_IYL_(Z80* ctx) { ctx->OR_IYL(); }
    inline void OR_IYL()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] OR %s, IYL<$%02X>", reg.PC - 2, registerDump(0b111), getIYL());
#endif
        or8(getIYL());
    }

    // OR immediate
    static inline void OR_N(Z80* ctx)
    {
        unsigned char n = ctx->fetch(3);
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] OR %s, $%02X", ctx->reg.PC - 2, ctx->registerDump(0b111), n);
#endif
        ctx->or8(n);
    }

    // OR Memory
    static inline void OR_HL(Z80* ctx)
    {
        unsigned char n = ctx->readByte(ctx->getHL(), 3);
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] OR %s, (%s) = $%02X", ctx->reg.PC - 1, ctx->registerDump(0b111), ctx->registerPairDump(0b10), ctx->reg.pair.A | n);
#endif
        ctx->or8(n);
    }

    // OR Memory
    static inline void OR_IX_(Z80* ctx) { ctx->OR_IX(); }
    inline void OR_IX()
    {
        signed char d = (signed char)fetch(4);
        unsigned char n = readByte((unsigned short)(reg.IX + d));
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] OR %s, (IX+d<$%04X>) = $%02X", reg.PC - 3, registerDump(0b111), (unsigned short)(reg.IX + d), reg.pair.A | n);
#endif
        or8(n);
        consumeClock(3);
    }

    // OR Memory
    static inline void OR_IY_(Z80* ctx) { ctx->OR_IY(); }
    inline void OR_IY()
    {
        signed char d = (signed char)fetch(4);
        unsigned char n = readByte((unsigned short)(reg.IY + d));
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] OR %s, (IY+d<$%04X>) = $%02X", reg.PC - 3, registerDump(0b111), (unsigned short)(reg.IY + d), reg.pair.A | n);
#endif
        or8(n);
        consumeClock(3);
    }

    // XOR Reigster
    static inline void XOR_B(Z80* ctx) { ctx->XOR_R(0b000); }
    static inline void XOR_C(Z80* ctx) { ctx->XOR_R(0b001); }
    static inline void XOR_D(Z80* ctx) { ctx->XOR_R(0b010); }
    static inline void XOR_E(Z80* ctx) { ctx->XOR_R(0b011); }
    static inline void XOR_H(Z80* ctx) { ctx->XOR_R(0b100); }
    static inline void XOR_L(Z80* ctx) { ctx->XOR_R(0b101); }
    static inline void XOR_A(Z80* ctx) { ctx->XOR_R(0b111); }
    static inline void XOR_B_2(Z80* ctx) { ctx->XOR_R(0b000, 2); }
    static inline void XOR_C_2(Z80* ctx) { ctx->XOR_R(0b001, 2); }
    static inline void XOR_D_2(Z80* ctx) { ctx->XOR_R(0b010, 2); }
    static inline void XOR_E_2(Z80* ctx) { ctx->XOR_R(0b011, 2); }
    static inline void XOR_A_2(Z80* ctx) { ctx->XOR_R(0b111, 2); }
    inline void XOR_R(unsigned char r, int pc = 1)
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] XOR %s, %s", reg.PC - pc, registerDump(0b111), registerDump(r));
#endif
        xor8(getRegister(r));
    }

    // XOR with register IXH
    static inline void XOR_IXH_(Z80* ctx) { ctx->XOR_IXH(); }
    inline void XOR_IXH()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] XOR %s, IXH<$%02X>", reg.PC - 2, registerDump(0b111), getIXH());
#endif
        xor8(getIXH());
    }

    // XOR with register IXL
    static inline void XOR_IXL_(Z80* ctx) { ctx->XOR_IXL(); }
    inline void XOR_IXL()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] XOR %s, IXL<$%02X>", reg.PC - 2, registerDump(0b111), getIXL());
#endif
        xor8(getIXL());
    }

    // XOR with register IYH
    static inline void XOR_IYH_(Z80* ctx) { ctx->XOR_IYH(); }
    inline void XOR_IYH()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] XOR %s, IYH<$%02X>", reg.PC - 2, registerDump(0b111), getIYH());
#endif
        xor8(getIYH());
    }

    // XOR with register IYL
    static inline void XOR_IYL_(Z80* ctx) { ctx->XOR_IYL(); }
    inline void XOR_IYL()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] XOR %s, IYL<$%02X>", reg.PC - 2, registerDump(0b111), getIYL());
#endif
        xor8(getIYL());
    }

    // XOR immediate
    static inline void XOR_N(Z80* ctx)
    {
        unsigned char n = ctx->fetch(3);
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] XOR %s, $%02X", ctx->reg.PC - 2, ctx->registerDump(0b111), n);
#endif
        ctx->xor8(n);
    }

    // XOR Memory
    static inline void XOR_HL(Z80* ctx)
    {
        unsigned char n = ctx->readByte(ctx->getHL(), 3);
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] XOR %s, (%s) = $%02X", ctx->reg.PC - 1, ctx->registerDump(0b111), ctx->registerPairDump(0b10), ctx->reg.pair.A ^ n);
#endif
        ctx->xor8(n);
    }

    // XOR Memory
    static inline void XOR_IX_(Z80* ctx) { ctx->XOR_IX(); }
    inline void XOR_IX()
    {
        signed char d = (signed char)fetch(4);
        unsigned char n = readByte((unsigned short)(reg.IX + d));
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] XOR %s, (IX+d<$%04X>) = $%02X", reg.PC - 3, registerDump(0b111), (unsigned short)(reg.IX + d), reg.pair.A ^ n);
#endif
        xor8(n);
        consumeClock(3);
    }

    // XOR Memory
    static inline void XOR_IY_(Z80* ctx) { ctx->XOR_IY(); }
    inline void XOR_IY()
    {
        signed char d = (signed char)fetch(4);
        unsigned char n = readByte((unsigned short)(reg.IY + d));
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] XOR %s, (IY+d<$%04X>) = $%02X", reg.PC - 3, registerDump(0b111), (unsigned short)(reg.IY + d), reg.pair.A ^ n);
#endif
        xor8(n);
        consumeClock(3);
    }

    // Complement acc. (1's Comp.)
    static inline void CPL(Z80* ctx)
    {
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] CPL %s", ctx->reg.PC - 1, ctx->registerDump(0b111));
#endif
        ctx->reg.pair.A = ~ctx->reg.pair.A;
        ctx->setFlagH();
        ctx->setFlagN();
        ctx->setFlagXY(ctx->reg.pair.A);
    }

    // Negate Acc. (2's Comp.)
    static inline void NEG_(Z80* ctx) { ctx->NEG(); }
    inline void NEG()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] NEG %s", reg.PC - 2, registerDump(0b111));
#endif
        unsigned char a = reg.pair.A;
        reg.pair.A = 0;
        subtract8(a, 0);
    }

    //  Complement Carry Flag
    static inline void CCF(Z80* ctx)
    {
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] CCF <C:%s -> %s>", ctx->reg.PC - 1, ctx->isFlagC() ? "ON" : "OFF", !ctx->isFlagC() ? "ON" : "OFF");
#endif
        ctx->setFlagH(ctx->isFlagC());
        ctx->resetFlagN();
        ctx->setFlagC(!ctx->isFlagC());
        ctx->setFlagXY(ctx->reg.pair.A);
    }

    // Set Carry Flag
    static inline void SCF(Z80* ctx)
    {
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] SCF <C:%s -> ON>", ctx->reg.PC - 1, ctx->isFlagC() ? "ON" : "OFF");
#endif
        ctx->resetFlagH();
        ctx->resetFlagN();
        ctx->setFlagC();
        ctx->setFlagXY(ctx->reg.pair.A);
    }

    // Test BIT b of register r
    static inline void BIT_B_0(Z80* ctx) { ctx->BIT_R(0b000, 0); }
    static inline void BIT_B_1(Z80* ctx) { ctx->BIT_R(0b000, 1); }
    static inline void BIT_B_2(Z80* ctx) { ctx->BIT_R(0b000, 2); }
    static inline void BIT_B_3(Z80* ctx) { ctx->BIT_R(0b000, 3); }
    static inline void BIT_B_4(Z80* ctx) { ctx->BIT_R(0b000, 4); }
    static inline void BIT_B_5(Z80* ctx) { ctx->BIT_R(0b000, 5); }
    static inline void BIT_B_6(Z80* ctx) { ctx->BIT_R(0b000, 6); }
    static inline void BIT_B_7(Z80* ctx) { ctx->BIT_R(0b000, 7); }
    static inline void BIT_C_0(Z80* ctx) { ctx->BIT_R(0b001, 0); }
    static inline void BIT_C_1(Z80* ctx) { ctx->BIT_R(0b001, 1); }
    static inline void BIT_C_2(Z80* ctx) { ctx->BIT_R(0b001, 2); }
    static inline void BIT_C_3(Z80* ctx) { ctx->BIT_R(0b001, 3); }
    static inline void BIT_C_4(Z80* ctx) { ctx->BIT_R(0b001, 4); }
    static inline void BIT_C_5(Z80* ctx) { ctx->BIT_R(0b001, 5); }
    static inline void BIT_C_6(Z80* ctx) { ctx->BIT_R(0b001, 6); }
    static inline void BIT_C_7(Z80* ctx) { ctx->BIT_R(0b001, 7); }
    static inline void BIT_D_0(Z80* ctx) { ctx->BIT_R(0b010, 0); }
    static inline void BIT_D_1(Z80* ctx) { ctx->BIT_R(0b010, 1); }
    static inline void BIT_D_2(Z80* ctx) { ctx->BIT_R(0b010, 2); }
    static inline void BIT_D_3(Z80* ctx) { ctx->BIT_R(0b010, 3); }
    static inline void BIT_D_4(Z80* ctx) { ctx->BIT_R(0b010, 4); }
    static inline void BIT_D_5(Z80* ctx) { ctx->BIT_R(0b010, 5); }
    static inline void BIT_D_6(Z80* ctx) { ctx->BIT_R(0b010, 6); }
    static inline void BIT_D_7(Z80* ctx) { ctx->BIT_R(0b010, 7); }
    static inline void BIT_E_0(Z80* ctx) { ctx->BIT_R(0b011, 0); }
    static inline void BIT_E_1(Z80* ctx) { ctx->BIT_R(0b011, 1); }
    static inline void BIT_E_2(Z80* ctx) { ctx->BIT_R(0b011, 2); }
    static inline void BIT_E_3(Z80* ctx) { ctx->BIT_R(0b011, 3); }
    static inline void BIT_E_4(Z80* ctx) { ctx->BIT_R(0b011, 4); }
    static inline void BIT_E_5(Z80* ctx) { ctx->BIT_R(0b011, 5); }
    static inline void BIT_E_6(Z80* ctx) { ctx->BIT_R(0b011, 6); }
    static inline void BIT_E_7(Z80* ctx) { ctx->BIT_R(0b011, 7); }
    static inline void BIT_H_0(Z80* ctx) { ctx->BIT_R(0b100, 0); }
    static inline void BIT_H_1(Z80* ctx) { ctx->BIT_R(0b100, 1); }
    static inline void BIT_H_2(Z80* ctx) { ctx->BIT_R(0b100, 2); }
    static inline void BIT_H_3(Z80* ctx) { ctx->BIT_R(0b100, 3); }
    static inline void BIT_H_4(Z80* ctx) { ctx->BIT_R(0b100, 4); }
    static inline void BIT_H_5(Z80* ctx) { ctx->BIT_R(0b100, 5); }
    static inline void BIT_H_6(Z80* ctx) { ctx->BIT_R(0b100, 6); }
    static inline void BIT_H_7(Z80* ctx) { ctx->BIT_R(0b100, 7); }
    static inline void BIT_L_0(Z80* ctx) { ctx->BIT_R(0b101, 0); }
    static inline void BIT_L_1(Z80* ctx) { ctx->BIT_R(0b101, 1); }
    static inline void BIT_L_2(Z80* ctx) { ctx->BIT_R(0b101, 2); }
    static inline void BIT_L_3(Z80* ctx) { ctx->BIT_R(0b101, 3); }
    static inline void BIT_L_4(Z80* ctx) { ctx->BIT_R(0b101, 4); }
    static inline void BIT_L_5(Z80* ctx) { ctx->BIT_R(0b101, 5); }
    static inline void BIT_L_6(Z80* ctx) { ctx->BIT_R(0b101, 6); }
    static inline void BIT_L_7(Z80* ctx) { ctx->BIT_R(0b101, 7); }
    static inline void BIT_A_0(Z80* ctx) { ctx->BIT_R(0b111, 0); }
    static inline void BIT_A_1(Z80* ctx) { ctx->BIT_R(0b111, 1); }
    static inline void BIT_A_2(Z80* ctx) { ctx->BIT_R(0b111, 2); }
    static inline void BIT_A_3(Z80* ctx) { ctx->BIT_R(0b111, 3); }
    static inline void BIT_A_4(Z80* ctx) { ctx->BIT_R(0b111, 4); }
    static inline void BIT_A_5(Z80* ctx) { ctx->BIT_R(0b111, 5); }
    static inline void BIT_A_6(Z80* ctx) { ctx->BIT_R(0b111, 6); }
    static inline void BIT_A_7(Z80* ctx) { ctx->BIT_R(0b111, 7); }
    inline void BIT_R(unsigned char r, unsigned char bit)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] BIT %s of bit-%d", reg.PC - 2, registerDump(r), bit);
#endif
        unsigned char n = 0;
        n = *rp & bits[bit];
        setFlagZ(n ? false : true);
        setFlagPV(isFlagZ());
        setFlagS(!isFlagZ() && 7 == bit);
        setFlagH();
        resetFlagN();
        setFlagXY(*rp);
    }

    // Test BIT b of location (HL)
    static inline void BIT_HL_0(Z80* ctx) { ctx->BIT_HL(0); }
    static inline void BIT_HL_1(Z80* ctx) { ctx->BIT_HL(1); }
    static inline void BIT_HL_2(Z80* ctx) { ctx->BIT_HL(2); }
    static inline void BIT_HL_3(Z80* ctx) { ctx->BIT_HL(3); }
    static inline void BIT_HL_4(Z80* ctx) { ctx->BIT_HL(4); }
    static inline void BIT_HL_5(Z80* ctx) { ctx->BIT_HL(5); }
    static inline void BIT_HL_6(Z80* ctx) { ctx->BIT_HL(6); }
    static inline void BIT_HL_7(Z80* ctx) { ctx->BIT_HL(7); }
    inline void BIT_HL(unsigned char bit)
    {
        unsigned char n = readByte(getHL());
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] BIT (%s) = $%02X of bit-%d", reg.PC - 2, registerPairDump(0b10), n, bit);
#endif
        n &= bits[bit];
        setFlagZ(!n);
        setFlagPV(isFlagZ());
        setFlagS(!isFlagZ() && 7 == bit);
        setFlagH();
        resetFlagN();
        setFlagXY((reg.WZ & 0xFF00) >> 8);
    }

    // Test BIT b of location (IX+d)
    static inline void BIT_IX_0(Z80* ctx, signed char d) { ctx->BIT_IX(d, 0); }
    static inline void BIT_IX_1(Z80* ctx, signed char d) { ctx->BIT_IX(d, 1); }
    static inline void BIT_IX_2(Z80* ctx, signed char d) { ctx->BIT_IX(d, 2); }
    static inline void BIT_IX_3(Z80* ctx, signed char d) { ctx->BIT_IX(d, 3); }
    static inline void BIT_IX_4(Z80* ctx, signed char d) { ctx->BIT_IX(d, 4); }
    static inline void BIT_IX_5(Z80* ctx, signed char d) { ctx->BIT_IX(d, 5); }
    static inline void BIT_IX_6(Z80* ctx, signed char d) { ctx->BIT_IX(d, 6); }
    static inline void BIT_IX_7(Z80* ctx, signed char d) { ctx->BIT_IX(d, 7); }
    inline void BIT_IX(signed char d, unsigned char bit)
    {
        unsigned char n = readByte((unsigned short)(reg.IX + d));
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] BIT (IX+d<$%04X>) = $%02X of bit-%d", reg.PC - 4, (unsigned short)(reg.IX + d), n, bit);
#endif
        n &= bits[bit];
        setFlagZ(!n);
        setFlagPV(isFlagZ());
        setFlagS(!isFlagZ() && 7 == bit);
        setFlagH();
        resetFlagN();
        setFlagXY((reg.WZ & 0xFF00) >> 8);
    }

    // Test BIT b of location (IY+d)
    static inline void BIT_IY_0(Z80* ctx, signed char d) { ctx->BIT_IY(d, 0); }
    static inline void BIT_IY_1(Z80* ctx, signed char d) { ctx->BIT_IY(d, 1); }
    static inline void BIT_IY_2(Z80* ctx, signed char d) { ctx->BIT_IY(d, 2); }
    static inline void BIT_IY_3(Z80* ctx, signed char d) { ctx->BIT_IY(d, 3); }
    static inline void BIT_IY_4(Z80* ctx, signed char d) { ctx->BIT_IY(d, 4); }
    static inline void BIT_IY_5(Z80* ctx, signed char d) { ctx->BIT_IY(d, 5); }
    static inline void BIT_IY_6(Z80* ctx, signed char d) { ctx->BIT_IY(d, 6); }
    static inline void BIT_IY_7(Z80* ctx, signed char d) { ctx->BIT_IY(d, 7); }
    inline void BIT_IY(signed char d, unsigned char bit)
    {
        unsigned char n = readByte((unsigned short)(reg.IY + d));
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] BIT (IY+d<$%04X>) = $%02X of bit-%d", reg.PC - 4, (unsigned short)(reg.IY + d), n, bit);
#endif
        n &= bits[bit];
        setFlagZ(!n);
        setFlagPV(isFlagZ());
        setFlagS(!isFlagZ() && 7 == bit);
        setFlagH();
        resetFlagN();
        setFlagXY((reg.WZ & 0xFF00) >> 8);
    }

    // SET bit b of register r
    static inline void SET_B_0(Z80* ctx) { ctx->SET_R(0b000, 0); }
    static inline void SET_B_1(Z80* ctx) { ctx->SET_R(0b000, 1); }
    static inline void SET_B_2(Z80* ctx) { ctx->SET_R(0b000, 2); }
    static inline void SET_B_3(Z80* ctx) { ctx->SET_R(0b000, 3); }
    static inline void SET_B_4(Z80* ctx) { ctx->SET_R(0b000, 4); }
    static inline void SET_B_5(Z80* ctx) { ctx->SET_R(0b000, 5); }
    static inline void SET_B_6(Z80* ctx) { ctx->SET_R(0b000, 6); }
    static inline void SET_B_7(Z80* ctx) { ctx->SET_R(0b000, 7); }
    static inline void SET_C_0(Z80* ctx) { ctx->SET_R(0b001, 0); }
    static inline void SET_C_1(Z80* ctx) { ctx->SET_R(0b001, 1); }
    static inline void SET_C_2(Z80* ctx) { ctx->SET_R(0b001, 2); }
    static inline void SET_C_3(Z80* ctx) { ctx->SET_R(0b001, 3); }
    static inline void SET_C_4(Z80* ctx) { ctx->SET_R(0b001, 4); }
    static inline void SET_C_5(Z80* ctx) { ctx->SET_R(0b001, 5); }
    static inline void SET_C_6(Z80* ctx) { ctx->SET_R(0b001, 6); }
    static inline void SET_C_7(Z80* ctx) { ctx->SET_R(0b001, 7); }
    static inline void SET_D_0(Z80* ctx) { ctx->SET_R(0b010, 0); }
    static inline void SET_D_1(Z80* ctx) { ctx->SET_R(0b010, 1); }
    static inline void SET_D_2(Z80* ctx) { ctx->SET_R(0b010, 2); }
    static inline void SET_D_3(Z80* ctx) { ctx->SET_R(0b010, 3); }
    static inline void SET_D_4(Z80* ctx) { ctx->SET_R(0b010, 4); }
    static inline void SET_D_5(Z80* ctx) { ctx->SET_R(0b010, 5); }
    static inline void SET_D_6(Z80* ctx) { ctx->SET_R(0b010, 6); }
    static inline void SET_D_7(Z80* ctx) { ctx->SET_R(0b010, 7); }
    static inline void SET_E_0(Z80* ctx) { ctx->SET_R(0b011, 0); }
    static inline void SET_E_1(Z80* ctx) { ctx->SET_R(0b011, 1); }
    static inline void SET_E_2(Z80* ctx) { ctx->SET_R(0b011, 2); }
    static inline void SET_E_3(Z80* ctx) { ctx->SET_R(0b011, 3); }
    static inline void SET_E_4(Z80* ctx) { ctx->SET_R(0b011, 4); }
    static inline void SET_E_5(Z80* ctx) { ctx->SET_R(0b011, 5); }
    static inline void SET_E_6(Z80* ctx) { ctx->SET_R(0b011, 6); }
    static inline void SET_E_7(Z80* ctx) { ctx->SET_R(0b011, 7); }
    static inline void SET_H_0(Z80* ctx) { ctx->SET_R(0b100, 0); }
    static inline void SET_H_1(Z80* ctx) { ctx->SET_R(0b100, 1); }
    static inline void SET_H_2(Z80* ctx) { ctx->SET_R(0b100, 2); }
    static inline void SET_H_3(Z80* ctx) { ctx->SET_R(0b100, 3); }
    static inline void SET_H_4(Z80* ctx) { ctx->SET_R(0b100, 4); }
    static inline void SET_H_5(Z80* ctx) { ctx->SET_R(0b100, 5); }
    static inline void SET_H_6(Z80* ctx) { ctx->SET_R(0b100, 6); }
    static inline void SET_H_7(Z80* ctx) { ctx->SET_R(0b100, 7); }
    static inline void SET_L_0(Z80* ctx) { ctx->SET_R(0b101, 0); }
    static inline void SET_L_1(Z80* ctx) { ctx->SET_R(0b101, 1); }
    static inline void SET_L_2(Z80* ctx) { ctx->SET_R(0b101, 2); }
    static inline void SET_L_3(Z80* ctx) { ctx->SET_R(0b101, 3); }
    static inline void SET_L_4(Z80* ctx) { ctx->SET_R(0b101, 4); }
    static inline void SET_L_5(Z80* ctx) { ctx->SET_R(0b101, 5); }
    static inline void SET_L_6(Z80* ctx) { ctx->SET_R(0b101, 6); }
    static inline void SET_L_7(Z80* ctx) { ctx->SET_R(0b101, 7); }
    static inline void SET_A_0(Z80* ctx) { ctx->SET_R(0b111, 0); }
    static inline void SET_A_1(Z80* ctx) { ctx->SET_R(0b111, 1); }
    static inline void SET_A_2(Z80* ctx) { ctx->SET_R(0b111, 2); }
    static inline void SET_A_3(Z80* ctx) { ctx->SET_R(0b111, 3); }
    static inline void SET_A_4(Z80* ctx) { ctx->SET_R(0b111, 4); }
    static inline void SET_A_5(Z80* ctx) { ctx->SET_R(0b111, 5); }
    static inline void SET_A_6(Z80* ctx) { ctx->SET_R(0b111, 6); }
    static inline void SET_A_7(Z80* ctx) { ctx->SET_R(0b111, 7); }
    inline void SET_R(unsigned char r, unsigned char bit)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SET %s of bit-%d", reg.PC - 2, registerDump(r), bit);
#endif
        *rp |= bits[bit];
    }

    // SET bit b of location (HL)
    static inline void SET_HL_0(Z80* ctx) { ctx->SET_HL(0); }
    static inline void SET_HL_1(Z80* ctx) { ctx->SET_HL(1); }
    static inline void SET_HL_2(Z80* ctx) { ctx->SET_HL(2); }
    static inline void SET_HL_3(Z80* ctx) { ctx->SET_HL(3); }
    static inline void SET_HL_4(Z80* ctx) { ctx->SET_HL(4); }
    static inline void SET_HL_5(Z80* ctx) { ctx->SET_HL(5); }
    static inline void SET_HL_6(Z80* ctx) { ctx->SET_HL(6); }
    static inline void SET_HL_7(Z80* ctx) { ctx->SET_HL(7); }
    inline void SET_HL(unsigned char bit)
    {
        unsigned short addr = getHL();
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SET (%s) = $%02X of bit-%d", reg.PC - 2, registerPairDump(0b10), n, bit);
#endif
        n |= bits[bit];
        writeByte(addr, n, 3);
    }

    // SET bit b of location (IX+d)
    static inline void SET_IX_0(Z80* ctx, signed char d) { ctx->SET_IX(d, 0); }
    static inline void SET_IX_1(Z80* ctx, signed char d) { ctx->SET_IX(d, 1); }
    static inline void SET_IX_2(Z80* ctx, signed char d) { ctx->SET_IX(d, 2); }
    static inline void SET_IX_3(Z80* ctx, signed char d) { ctx->SET_IX(d, 3); }
    static inline void SET_IX_4(Z80* ctx, signed char d) { ctx->SET_IX(d, 4); }
    static inline void SET_IX_5(Z80* ctx, signed char d) { ctx->SET_IX(d, 5); }
    static inline void SET_IX_6(Z80* ctx, signed char d) { ctx->SET_IX(d, 6); }
    static inline void SET_IX_7(Z80* ctx, signed char d) { ctx->SET_IX(d, 7); }
    inline void SET_IX(signed char d, unsigned char bit, unsigned char* rp = nullptr, const char* extraLog = nullptr)
    {
        unsigned short addr = (unsigned short)(reg.IX + d);
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SET (IX+d<$%04X>) = $%02X of bit-%d%s", reg.PC - 4, addr, n, bit, extraLog ? extraLog : "");
#endif
        n |= bits[bit];
        if (rp) *rp = n;
        writeByte(addr, n, 3);
    }

    // SET bit b of location (IX+d) with load Reg.
    static inline void SET_IX_0_with_LD_B(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 0, 0b000); }
    static inline void SET_IX_1_with_LD_B(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 1, 0b000); }
    static inline void SET_IX_2_with_LD_B(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 2, 0b000); }
    static inline void SET_IX_3_with_LD_B(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 3, 0b000); }
    static inline void SET_IX_4_with_LD_B(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 4, 0b000); }
    static inline void SET_IX_5_with_LD_B(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 5, 0b000); }
    static inline void SET_IX_6_with_LD_B(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 6, 0b000); }
    static inline void SET_IX_7_with_LD_B(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 7, 0b000); }
    static inline void SET_IX_0_with_LD_C(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 0, 0b001); }
    static inline void SET_IX_1_with_LD_C(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 1, 0b001); }
    static inline void SET_IX_2_with_LD_C(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 2, 0b001); }
    static inline void SET_IX_3_with_LD_C(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 3, 0b001); }
    static inline void SET_IX_4_with_LD_C(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 4, 0b001); }
    static inline void SET_IX_5_with_LD_C(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 5, 0b001); }
    static inline void SET_IX_6_with_LD_C(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 6, 0b001); }
    static inline void SET_IX_7_with_LD_C(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 7, 0b001); }
    static inline void SET_IX_0_with_LD_D(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 0, 0b010); }
    static inline void SET_IX_1_with_LD_D(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 1, 0b010); }
    static inline void SET_IX_2_with_LD_D(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 2, 0b010); }
    static inline void SET_IX_3_with_LD_D(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 3, 0b010); }
    static inline void SET_IX_4_with_LD_D(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 4, 0b010); }
    static inline void SET_IX_5_with_LD_D(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 5, 0b010); }
    static inline void SET_IX_6_with_LD_D(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 6, 0b010); }
    static inline void SET_IX_7_with_LD_D(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 7, 0b010); }
    static inline void SET_IX_0_with_LD_E(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 0, 0b011); }
    static inline void SET_IX_1_with_LD_E(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 1, 0b011); }
    static inline void SET_IX_2_with_LD_E(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 2, 0b011); }
    static inline void SET_IX_3_with_LD_E(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 3, 0b011); }
    static inline void SET_IX_4_with_LD_E(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 4, 0b011); }
    static inline void SET_IX_5_with_LD_E(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 5, 0b011); }
    static inline void SET_IX_6_with_LD_E(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 6, 0b011); }
    static inline void SET_IX_7_with_LD_E(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 7, 0b011); }
    static inline void SET_IX_0_with_LD_H(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 0, 0b100); }
    static inline void SET_IX_1_with_LD_H(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 1, 0b100); }
    static inline void SET_IX_2_with_LD_H(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 2, 0b100); }
    static inline void SET_IX_3_with_LD_H(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 3, 0b100); }
    static inline void SET_IX_4_with_LD_H(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 4, 0b100); }
    static inline void SET_IX_5_with_LD_H(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 5, 0b100); }
    static inline void SET_IX_6_with_LD_H(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 6, 0b100); }
    static inline void SET_IX_7_with_LD_H(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 7, 0b100); }
    static inline void SET_IX_0_with_LD_L(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 0, 0b101); }
    static inline void SET_IX_1_with_LD_L(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 1, 0b101); }
    static inline void SET_IX_2_with_LD_L(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 2, 0b101); }
    static inline void SET_IX_3_with_LD_L(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 3, 0b101); }
    static inline void SET_IX_4_with_LD_L(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 4, 0b101); }
    static inline void SET_IX_5_with_LD_L(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 5, 0b101); }
    static inline void SET_IX_6_with_LD_L(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 6, 0b101); }
    static inline void SET_IX_7_with_LD_L(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 7, 0b101); }
    static inline void SET_IX_0_with_LD_A(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 0, 0b111); }
    static inline void SET_IX_1_with_LD_A(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 1, 0b111); }
    static inline void SET_IX_2_with_LD_A(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 2, 0b111); }
    static inline void SET_IX_3_with_LD_A(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 3, 0b111); }
    static inline void SET_IX_4_with_LD_A(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 4, 0b111); }
    static inline void SET_IX_5_with_LD_A(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 5, 0b111); }
    static inline void SET_IX_6_with_LD_A(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 6, 0b111); }
    static inline void SET_IX_7_with_LD_A(Z80* ctx, signed char d) { ctx->SET_IX_with_LD(d, 7, 0b111); }
    inline void SET_IX_with_LD(signed char d, unsigned char bit, unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        char buf[80];
        if (isDebug()) {
            snprintf(buf, sizeof(buf), " --> %s", registerDump(r));
        } else {
            buf[0] = '\0';
        }
        SET_IX(d, bit, rp, buf);
#else
        SET_IX(d, bit, rp);
#endif
    }

    // SET bit b of location (IY+d)
    static inline void SET_IY_0(Z80* ctx, signed char d) { ctx->SET_IY(d, 0); }
    static inline void SET_IY_1(Z80* ctx, signed char d) { ctx->SET_IY(d, 1); }
    static inline void SET_IY_2(Z80* ctx, signed char d) { ctx->SET_IY(d, 2); }
    static inline void SET_IY_3(Z80* ctx, signed char d) { ctx->SET_IY(d, 3); }
    static inline void SET_IY_4(Z80* ctx, signed char d) { ctx->SET_IY(d, 4); }
    static inline void SET_IY_5(Z80* ctx, signed char d) { ctx->SET_IY(d, 5); }
    static inline void SET_IY_6(Z80* ctx, signed char d) { ctx->SET_IY(d, 6); }
    static inline void SET_IY_7(Z80* ctx, signed char d) { ctx->SET_IY(d, 7); }
    inline void SET_IY(signed char d, unsigned char bit, unsigned char* rp = nullptr, const char* extraLog = nullptr)
    {
        unsigned short addr = (unsigned short)(reg.IY + d);
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] SET (IY+d<$%04X>) = $%02X of bit-%d%s", reg.PC - 4, addr, n, bit, extraLog ? extraLog : "");
#endif
        n |= bits[bit];
        if (rp) *rp = n;
        writeByte(addr, n, 3);
    }

    // SET bit b of location (IY+d) with load Reg.
    static inline void SET_IY_0_with_LD_B(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 0, 0b000); }
    static inline void SET_IY_1_with_LD_B(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 1, 0b000); }
    static inline void SET_IY_2_with_LD_B(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 2, 0b000); }
    static inline void SET_IY_3_with_LD_B(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 3, 0b000); }
    static inline void SET_IY_4_with_LD_B(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 4, 0b000); }
    static inline void SET_IY_5_with_LD_B(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 5, 0b000); }
    static inline void SET_IY_6_with_LD_B(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 6, 0b000); }
    static inline void SET_IY_7_with_LD_B(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 7, 0b000); }
    static inline void SET_IY_0_with_LD_C(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 0, 0b001); }
    static inline void SET_IY_1_with_LD_C(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 1, 0b001); }
    static inline void SET_IY_2_with_LD_C(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 2, 0b001); }
    static inline void SET_IY_3_with_LD_C(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 3, 0b001); }
    static inline void SET_IY_4_with_LD_C(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 4, 0b001); }
    static inline void SET_IY_5_with_LD_C(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 5, 0b001); }
    static inline void SET_IY_6_with_LD_C(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 6, 0b001); }
    static inline void SET_IY_7_with_LD_C(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 7, 0b001); }
    static inline void SET_IY_0_with_LD_D(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 0, 0b010); }
    static inline void SET_IY_1_with_LD_D(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 1, 0b010); }
    static inline void SET_IY_2_with_LD_D(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 2, 0b010); }
    static inline void SET_IY_3_with_LD_D(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 3, 0b010); }
    static inline void SET_IY_4_with_LD_D(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 4, 0b010); }
    static inline void SET_IY_5_with_LD_D(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 5, 0b010); }
    static inline void SET_IY_6_with_LD_D(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 6, 0b010); }
    static inline void SET_IY_7_with_LD_D(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 7, 0b010); }
    static inline void SET_IY_0_with_LD_E(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 0, 0b011); }
    static inline void SET_IY_1_with_LD_E(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 1, 0b011); }
    static inline void SET_IY_2_with_LD_E(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 2, 0b011); }
    static inline void SET_IY_3_with_LD_E(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 3, 0b011); }
    static inline void SET_IY_4_with_LD_E(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 4, 0b011); }
    static inline void SET_IY_5_with_LD_E(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 5, 0b011); }
    static inline void SET_IY_6_with_LD_E(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 6, 0b011); }
    static inline void SET_IY_7_with_LD_E(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 7, 0b011); }
    static inline void SET_IY_0_with_LD_H(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 0, 0b100); }
    static inline void SET_IY_1_with_LD_H(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 1, 0b100); }
    static inline void SET_IY_2_with_LD_H(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 2, 0b100); }
    static inline void SET_IY_3_with_LD_H(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 3, 0b100); }
    static inline void SET_IY_4_with_LD_H(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 4, 0b100); }
    static inline void SET_IY_5_with_LD_H(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 5, 0b100); }
    static inline void SET_IY_6_with_LD_H(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 6, 0b100); }
    static inline void SET_IY_7_with_LD_H(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 7, 0b100); }
    static inline void SET_IY_0_with_LD_L(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 0, 0b101); }
    static inline void SET_IY_1_with_LD_L(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 1, 0b101); }
    static inline void SET_IY_2_with_LD_L(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 2, 0b101); }
    static inline void SET_IY_3_with_LD_L(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 3, 0b101); }
    static inline void SET_IY_4_with_LD_L(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 4, 0b101); }
    static inline void SET_IY_5_with_LD_L(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 5, 0b101); }
    static inline void SET_IY_6_with_LD_L(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 6, 0b101); }
    static inline void SET_IY_7_with_LD_L(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 7, 0b101); }
    static inline void SET_IY_0_with_LD_A(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 0, 0b111); }
    static inline void SET_IY_1_with_LD_A(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 1, 0b111); }
    static inline void SET_IY_2_with_LD_A(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 2, 0b111); }
    static inline void SET_IY_3_with_LD_A(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 3, 0b111); }
    static inline void SET_IY_4_with_LD_A(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 4, 0b111); }
    static inline void SET_IY_5_with_LD_A(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 5, 0b111); }
    static inline void SET_IY_6_with_LD_A(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 6, 0b111); }
    static inline void SET_IY_7_with_LD_A(Z80* ctx, signed char d) { ctx->SET_IY_with_LD(d, 7, 0b111); }
    inline void SET_IY_with_LD(signed char d, unsigned char bit, unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        char buf[80];
        if (isDebug()) {
            snprintf(buf, sizeof(buf), " --> %s", registerDump(r));
        } else {
            buf[0] = '\0';
        }
        SET_IY(d, bit, rp, buf);
#else
        SET_IY(d, bit, rp);
#endif
    }

    // RESET bit b of register r
    static inline void RES_B_0(Z80* ctx) { ctx->RES_R(0b000, 0); }
    static inline void RES_B_1(Z80* ctx) { ctx->RES_R(0b000, 1); }
    static inline void RES_B_2(Z80* ctx) { ctx->RES_R(0b000, 2); }
    static inline void RES_B_3(Z80* ctx) { ctx->RES_R(0b000, 3); }
    static inline void RES_B_4(Z80* ctx) { ctx->RES_R(0b000, 4); }
    static inline void RES_B_5(Z80* ctx) { ctx->RES_R(0b000, 5); }
    static inline void RES_B_6(Z80* ctx) { ctx->RES_R(0b000, 6); }
    static inline void RES_B_7(Z80* ctx) { ctx->RES_R(0b000, 7); }
    static inline void RES_C_0(Z80* ctx) { ctx->RES_R(0b001, 0); }
    static inline void RES_C_1(Z80* ctx) { ctx->RES_R(0b001, 1); }
    static inline void RES_C_2(Z80* ctx) { ctx->RES_R(0b001, 2); }
    static inline void RES_C_3(Z80* ctx) { ctx->RES_R(0b001, 3); }
    static inline void RES_C_4(Z80* ctx) { ctx->RES_R(0b001, 4); }
    static inline void RES_C_5(Z80* ctx) { ctx->RES_R(0b001, 5); }
    static inline void RES_C_6(Z80* ctx) { ctx->RES_R(0b001, 6); }
    static inline void RES_C_7(Z80* ctx) { ctx->RES_R(0b001, 7); }
    static inline void RES_D_0(Z80* ctx) { ctx->RES_R(0b010, 0); }
    static inline void RES_D_1(Z80* ctx) { ctx->RES_R(0b010, 1); }
    static inline void RES_D_2(Z80* ctx) { ctx->RES_R(0b010, 2); }
    static inline void RES_D_3(Z80* ctx) { ctx->RES_R(0b010, 3); }
    static inline void RES_D_4(Z80* ctx) { ctx->RES_R(0b010, 4); }
    static inline void RES_D_5(Z80* ctx) { ctx->RES_R(0b010, 5); }
    static inline void RES_D_6(Z80* ctx) { ctx->RES_R(0b010, 6); }
    static inline void RES_D_7(Z80* ctx) { ctx->RES_R(0b010, 7); }
    static inline void RES_E_0(Z80* ctx) { ctx->RES_R(0b011, 0); }
    static inline void RES_E_1(Z80* ctx) { ctx->RES_R(0b011, 1); }
    static inline void RES_E_2(Z80* ctx) { ctx->RES_R(0b011, 2); }
    static inline void RES_E_3(Z80* ctx) { ctx->RES_R(0b011, 3); }
    static inline void RES_E_4(Z80* ctx) { ctx->RES_R(0b011, 4); }
    static inline void RES_E_5(Z80* ctx) { ctx->RES_R(0b011, 5); }
    static inline void RES_E_6(Z80* ctx) { ctx->RES_R(0b011, 6); }
    static inline void RES_E_7(Z80* ctx) { ctx->RES_R(0b011, 7); }
    static inline void RES_H_0(Z80* ctx) { ctx->RES_R(0b100, 0); }
    static inline void RES_H_1(Z80* ctx) { ctx->RES_R(0b100, 1); }
    static inline void RES_H_2(Z80* ctx) { ctx->RES_R(0b100, 2); }
    static inline void RES_H_3(Z80* ctx) { ctx->RES_R(0b100, 3); }
    static inline void RES_H_4(Z80* ctx) { ctx->RES_R(0b100, 4); }
    static inline void RES_H_5(Z80* ctx) { ctx->RES_R(0b100, 5); }
    static inline void RES_H_6(Z80* ctx) { ctx->RES_R(0b100, 6); }
    static inline void RES_H_7(Z80* ctx) { ctx->RES_R(0b100, 7); }
    static inline void RES_L_0(Z80* ctx) { ctx->RES_R(0b101, 0); }
    static inline void RES_L_1(Z80* ctx) { ctx->RES_R(0b101, 1); }
    static inline void RES_L_2(Z80* ctx) { ctx->RES_R(0b101, 2); }
    static inline void RES_L_3(Z80* ctx) { ctx->RES_R(0b101, 3); }
    static inline void RES_L_4(Z80* ctx) { ctx->RES_R(0b101, 4); }
    static inline void RES_L_5(Z80* ctx) { ctx->RES_R(0b101, 5); }
    static inline void RES_L_6(Z80* ctx) { ctx->RES_R(0b101, 6); }
    static inline void RES_L_7(Z80* ctx) { ctx->RES_R(0b101, 7); }
    static inline void RES_A_0(Z80* ctx) { ctx->RES_R(0b111, 0); }
    static inline void RES_A_1(Z80* ctx) { ctx->RES_R(0b111, 1); }
    static inline void RES_A_2(Z80* ctx) { ctx->RES_R(0b111, 2); }
    static inline void RES_A_3(Z80* ctx) { ctx->RES_R(0b111, 3); }
    static inline void RES_A_4(Z80* ctx) { ctx->RES_R(0b111, 4); }
    static inline void RES_A_5(Z80* ctx) { ctx->RES_R(0b111, 5); }
    static inline void RES_A_6(Z80* ctx) { ctx->RES_R(0b111, 6); }
    static inline void RES_A_7(Z80* ctx) { ctx->RES_R(0b111, 7); }
    inline void RES_R(unsigned char r, unsigned char bit)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] RES %s of bit-%d", reg.PC - 2, registerDump(r), bit);
#endif
        switch (bit) {
            case 0: *rp &= 0b11111110; break;
            case 1: *rp &= 0b11111101; break;
            case 2: *rp &= 0b11111011; break;
            case 3: *rp &= 0b11110111; break;
            case 4: *rp &= 0b11101111; break;
            case 5: *rp &= 0b11011111; break;
            case 6: *rp &= 0b10111111; break;
            case 7: *rp &= 0b01111111; break;
        }
    }

    // RESET bit b of location (HL)
    static inline void RES_HL_0(Z80* ctx) { ctx->RES_HL(0); }
    static inline void RES_HL_1(Z80* ctx) { ctx->RES_HL(1); }
    static inline void RES_HL_2(Z80* ctx) { ctx->RES_HL(2); }
    static inline void RES_HL_3(Z80* ctx) { ctx->RES_HL(3); }
    static inline void RES_HL_4(Z80* ctx) { ctx->RES_HL(4); }
    static inline void RES_HL_5(Z80* ctx) { ctx->RES_HL(5); }
    static inline void RES_HL_6(Z80* ctx) { ctx->RES_HL(6); }
    static inline void RES_HL_7(Z80* ctx) { ctx->RES_HL(7); }
    inline void RES_HL(unsigned char bit)
    {
        unsigned short addr = getHL();
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] RES (%s) = $%02X of bit-%d", reg.PC - 2, registerPairDump(0b10), n, bit);
#endif
        switch (bit) {
            case 0: n &= 0b11111110; break;
            case 1: n &= 0b11111101; break;
            case 2: n &= 0b11111011; break;
            case 3: n &= 0b11110111; break;
            case 4: n &= 0b11101111; break;
            case 5: n &= 0b11011111; break;
            case 6: n &= 0b10111111; break;
            case 7: n &= 0b01111111; break;
        }
        writeByte(addr, n, 3);
    }

    // RESET bit b of location (IX+d)
    static inline void RES_IX_0(Z80* ctx, signed char d) { ctx->RES_IX(d, 0); }
    static inline void RES_IX_1(Z80* ctx, signed char d) { ctx->RES_IX(d, 1); }
    static inline void RES_IX_2(Z80* ctx, signed char d) { ctx->RES_IX(d, 2); }
    static inline void RES_IX_3(Z80* ctx, signed char d) { ctx->RES_IX(d, 3); }
    static inline void RES_IX_4(Z80* ctx, signed char d) { ctx->RES_IX(d, 4); }
    static inline void RES_IX_5(Z80* ctx, signed char d) { ctx->RES_IX(d, 5); }
    static inline void RES_IX_6(Z80* ctx, signed char d) { ctx->RES_IX(d, 6); }
    static inline void RES_IX_7(Z80* ctx, signed char d) { ctx->RES_IX(d, 7); }
    inline void RES_IX(signed char d, unsigned char bit, unsigned char* rp = nullptr, const char* extraLog = nullptr)
    {
        unsigned short addr = (unsigned short)(reg.IX + d);
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] RES (IX+d<$%04X>) = $%02X of bit-%d%s", reg.PC - 4, addr, n, bit, extraLog ? extraLog : "");
#endif
        switch (bit) {
            case 0: n &= 0b11111110; break;
            case 1: n &= 0b11111101; break;
            case 2: n &= 0b11111011; break;
            case 3: n &= 0b11110111; break;
            case 4: n &= 0b11101111; break;
            case 5: n &= 0b11011111; break;
            case 6: n &= 0b10111111; break;
            case 7: n &= 0b01111111; break;
        }
        if (rp) *rp = n;
        writeByte(addr, n, 3);
    }

    // RESET bit b of location (IX+d) with load Reg.
    static inline void RES_IX_0_with_LD_B(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 0, 0b000); }
    static inline void RES_IX_1_with_LD_B(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 1, 0b000); }
    static inline void RES_IX_2_with_LD_B(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 2, 0b000); }
    static inline void RES_IX_3_with_LD_B(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 3, 0b000); }
    static inline void RES_IX_4_with_LD_B(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 4, 0b000); }
    static inline void RES_IX_5_with_LD_B(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 5, 0b000); }
    static inline void RES_IX_6_with_LD_B(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 6, 0b000); }
    static inline void RES_IX_7_with_LD_B(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 7, 0b000); }
    static inline void RES_IX_0_with_LD_C(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 0, 0b001); }
    static inline void RES_IX_1_with_LD_C(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 1, 0b001); }
    static inline void RES_IX_2_with_LD_C(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 2, 0b001); }
    static inline void RES_IX_3_with_LD_C(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 3, 0b001); }
    static inline void RES_IX_4_with_LD_C(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 4, 0b001); }
    static inline void RES_IX_5_with_LD_C(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 5, 0b001); }
    static inline void RES_IX_6_with_LD_C(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 6, 0b001); }
    static inline void RES_IX_7_with_LD_C(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 7, 0b001); }
    static inline void RES_IX_0_with_LD_D(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 0, 0b010); }
    static inline void RES_IX_1_with_LD_D(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 1, 0b010); }
    static inline void RES_IX_2_with_LD_D(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 2, 0b010); }
    static inline void RES_IX_3_with_LD_D(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 3, 0b010); }
    static inline void RES_IX_4_with_LD_D(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 4, 0b010); }
    static inline void RES_IX_5_with_LD_D(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 5, 0b010); }
    static inline void RES_IX_6_with_LD_D(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 6, 0b010); }
    static inline void RES_IX_7_with_LD_D(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 7, 0b010); }
    static inline void RES_IX_0_with_LD_E(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 0, 0b011); }
    static inline void RES_IX_1_with_LD_E(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 1, 0b011); }
    static inline void RES_IX_2_with_LD_E(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 2, 0b011); }
    static inline void RES_IX_3_with_LD_E(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 3, 0b011); }
    static inline void RES_IX_4_with_LD_E(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 4, 0b011); }
    static inline void RES_IX_5_with_LD_E(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 5, 0b011); }
    static inline void RES_IX_6_with_LD_E(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 6, 0b011); }
    static inline void RES_IX_7_with_LD_E(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 7, 0b011); }
    static inline void RES_IX_0_with_LD_H(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 0, 0b100); }
    static inline void RES_IX_1_with_LD_H(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 1, 0b100); }
    static inline void RES_IX_2_with_LD_H(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 2, 0b100); }
    static inline void RES_IX_3_with_LD_H(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 3, 0b100); }
    static inline void RES_IX_4_with_LD_H(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 4, 0b100); }
    static inline void RES_IX_5_with_LD_H(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 5, 0b100); }
    static inline void RES_IX_6_with_LD_H(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 6, 0b100); }
    static inline void RES_IX_7_with_LD_H(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 7, 0b100); }
    static inline void RES_IX_0_with_LD_L(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 0, 0b101); }
    static inline void RES_IX_1_with_LD_L(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 1, 0b101); }
    static inline void RES_IX_2_with_LD_L(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 2, 0b101); }
    static inline void RES_IX_3_with_LD_L(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 3, 0b101); }
    static inline void RES_IX_4_with_LD_L(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 4, 0b101); }
    static inline void RES_IX_5_with_LD_L(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 5, 0b101); }
    static inline void RES_IX_6_with_LD_L(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 6, 0b101); }
    static inline void RES_IX_7_with_LD_L(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 7, 0b101); }
    static inline void RES_IX_0_with_LD_A(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 0, 0b111); }
    static inline void RES_IX_1_with_LD_A(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 1, 0b111); }
    static inline void RES_IX_2_with_LD_A(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 2, 0b111); }
    static inline void RES_IX_3_with_LD_A(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 3, 0b111); }
    static inline void RES_IX_4_with_LD_A(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 4, 0b111); }
    static inline void RES_IX_5_with_LD_A(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 5, 0b111); }
    static inline void RES_IX_6_with_LD_A(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 6, 0b111); }
    static inline void RES_IX_7_with_LD_A(Z80* ctx, signed char d) { ctx->RES_IX_with_LD(d, 7, 0b111); }
    inline void RES_IX_with_LD(signed char d, unsigned char bit, unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        char buf[80];
        if (isDebug()) {
            snprintf(buf, sizeof(buf), " --> %s", registerDump(r));
        } else {
            buf[0] = '\0';
        }
        RES_IX(d, bit, rp, buf);
#else
        RES_IX(d, bit, rp);
#endif
    }

    // RESET bit b of location (IY+d) with load Reg.
    static inline void RES_IY_0_with_LD_B(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 0, 0b000); }
    static inline void RES_IY_1_with_LD_B(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 1, 0b000); }
    static inline void RES_IY_2_with_LD_B(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 2, 0b000); }
    static inline void RES_IY_3_with_LD_B(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 3, 0b000); }
    static inline void RES_IY_4_with_LD_B(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 4, 0b000); }
    static inline void RES_IY_5_with_LD_B(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 5, 0b000); }
    static inline void RES_IY_6_with_LD_B(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 6, 0b000); }
    static inline void RES_IY_7_with_LD_B(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 7, 0b000); }
    static inline void RES_IY_0_with_LD_C(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 0, 0b001); }
    static inline void RES_IY_1_with_LD_C(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 1, 0b001); }
    static inline void RES_IY_2_with_LD_C(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 2, 0b001); }
    static inline void RES_IY_3_with_LD_C(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 3, 0b001); }
    static inline void RES_IY_4_with_LD_C(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 4, 0b001); }
    static inline void RES_IY_5_with_LD_C(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 5, 0b001); }
    static inline void RES_IY_6_with_LD_C(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 6, 0b001); }
    static inline void RES_IY_7_with_LD_C(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 7, 0b001); }
    static inline void RES_IY_0_with_LD_D(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 0, 0b010); }
    static inline void RES_IY_1_with_LD_D(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 1, 0b010); }
    static inline void RES_IY_2_with_LD_D(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 2, 0b010); }
    static inline void RES_IY_3_with_LD_D(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 3, 0b010); }
    static inline void RES_IY_4_with_LD_D(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 4, 0b010); }
    static inline void RES_IY_5_with_LD_D(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 5, 0b010); }
    static inline void RES_IY_6_with_LD_D(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 6, 0b010); }
    static inline void RES_IY_7_with_LD_D(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 7, 0b010); }
    static inline void RES_IY_0_with_LD_E(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 0, 0b011); }
    static inline void RES_IY_1_with_LD_E(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 1, 0b011); }
    static inline void RES_IY_2_with_LD_E(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 2, 0b011); }
    static inline void RES_IY_3_with_LD_E(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 3, 0b011); }
    static inline void RES_IY_4_with_LD_E(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 4, 0b011); }
    static inline void RES_IY_5_with_LD_E(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 5, 0b011); }
    static inline void RES_IY_6_with_LD_E(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 6, 0b011); }
    static inline void RES_IY_7_with_LD_E(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 7, 0b011); }
    static inline void RES_IY_0_with_LD_H(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 0, 0b100); }
    static inline void RES_IY_1_with_LD_H(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 1, 0b100); }
    static inline void RES_IY_2_with_LD_H(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 2, 0b100); }
    static inline void RES_IY_3_with_LD_H(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 3, 0b100); }
    static inline void RES_IY_4_with_LD_H(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 4, 0b100); }
    static inline void RES_IY_5_with_LD_H(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 5, 0b100); }
    static inline void RES_IY_6_with_LD_H(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 6, 0b100); }
    static inline void RES_IY_7_with_LD_H(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 7, 0b100); }
    static inline void RES_IY_0_with_LD_L(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 0, 0b101); }
    static inline void RES_IY_1_with_LD_L(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 1, 0b101); }
    static inline void RES_IY_2_with_LD_L(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 2, 0b101); }
    static inline void RES_IY_3_with_LD_L(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 3, 0b101); }
    static inline void RES_IY_4_with_LD_L(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 4, 0b101); }
    static inline void RES_IY_5_with_LD_L(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 5, 0b101); }
    static inline void RES_IY_6_with_LD_L(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 6, 0b101); }
    static inline void RES_IY_7_with_LD_L(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 7, 0b101); }
    static inline void RES_IY_0_with_LD_A(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 0, 0b111); }
    static inline void RES_IY_1_with_LD_A(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 1, 0b111); }
    static inline void RES_IY_2_with_LD_A(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 2, 0b111); }
    static inline void RES_IY_3_with_LD_A(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 3, 0b111); }
    static inline void RES_IY_4_with_LD_A(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 4, 0b111); }
    static inline void RES_IY_5_with_LD_A(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 5, 0b111); }
    static inline void RES_IY_6_with_LD_A(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 6, 0b111); }
    static inline void RES_IY_7_with_LD_A(Z80* ctx, signed char d) { ctx->RES_IY_with_LD(d, 7, 0b111); }
    inline void RES_IY_with_LD(signed char d, unsigned char bit, unsigned char r)
    {
        unsigned char* rp = getRegisterPointer(r);
#ifndef Z80_DISABLE_DEBUG
        char buf[80];
        if (isDebug()) {
            snprintf(buf, sizeof(buf), " --> %s", registerDump(r));
        } else {
            buf[0] = '\0';
        }
        RES_IY(d, bit, rp, buf);
#else
        RES_IY(d, bit, rp);
#endif
    }

    // RESET bit b of location (IY+d)
    static inline void RES_IY_0(Z80* ctx, signed char d) { ctx->RES_IY(d, 0); }
    static inline void RES_IY_1(Z80* ctx, signed char d) { ctx->RES_IY(d, 1); }
    static inline void RES_IY_2(Z80* ctx, signed char d) { ctx->RES_IY(d, 2); }
    static inline void RES_IY_3(Z80* ctx, signed char d) { ctx->RES_IY(d, 3); }
    static inline void RES_IY_4(Z80* ctx, signed char d) { ctx->RES_IY(d, 4); }
    static inline void RES_IY_5(Z80* ctx, signed char d) { ctx->RES_IY(d, 5); }
    static inline void RES_IY_6(Z80* ctx, signed char d) { ctx->RES_IY(d, 6); }
    static inline void RES_IY_7(Z80* ctx, signed char d) { ctx->RES_IY(d, 7); }
    inline void RES_IY(signed char d, unsigned char bit, unsigned char* rp = nullptr, const char* extraLog = nullptr)
    {
        unsigned short addr = (unsigned short)(reg.IY + d);
        unsigned char n = readByte(addr);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] RES (IY+d<$%04X>) = $%02X of bit-%d%s", reg.PC - 4, addr, n, bit, extraLog ? extraLog : "");
#endif
        switch (bit) {
            case 0: n &= 0b11111110; break;
            case 1: n &= 0b11111101; break;
            case 2: n &= 0b11111011; break;
            case 3: n &= 0b11110111; break;
            case 4: n &= 0b11101111; break;
            case 5: n &= 0b11011111; break;
            case 6: n &= 0b10111111; break;
            case 7: n &= 0b01111111; break;
        }
        if (rp) *rp = n;
        writeByte(addr, n, 3);
    }

    // Compare location (HL) and A, increment/decrement HL and decrement BC
    inline void repeatCP(bool isIncHL, bool isRepeat)
    {
        unsigned short hl = getHL();
        unsigned short bc = getBC();
        unsigned char n = readByte(hl);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) {
            if (isIncHL) {
                log("[%04X] %s ... %s, %s = $%02X, %s", reg.PC - 2, isRepeat ? "CPIR" : "CPI", registerDump(0b111), registerPairDump(0b10), n, registerPairDump(0b00));
            } else {
                log("[%04X] %s ... %s, %s = $%02X, %s", reg.PC - 2, isRepeat ? "CPDR" : "CPD", registerDump(0b111), registerPairDump(0b10), n, registerPairDump(0b00));
            }
        }
#endif
        subtract8(n, 0, false, false);
        int nn = reg.pair.A;
        nn -= n;
        nn -= isFlagH() ? 1 : 0;
        setFlagY(nn & 0b00000010);
        setFlagX(nn & 0b00001000);
        setHL((unsigned short)(hl + (isIncHL ? 1 : -1)));
        bc--;
        setBC(bc);
        setFlagPV(0 != bc);
        consumeClock(4);
        if (isRepeat && !isFlagZ() && 0 != getBC()) {
            reg.PC -= 2;
            consumeClock(5);
        }
        reg.WZ += isIncHL ? 1 : -1;
    }
    static inline void CPI(Z80* ctx) { ctx->repeatCP(true, false); }
    static inline void CPIR(Z80* ctx) { ctx->repeatCP(true, true); }
    static inline void CPD(Z80* ctx) { ctx->repeatCP(false, false); }
    static inline void CPDR(Z80* ctx) { ctx->repeatCP(false, true); }

    // Compare Register
    static inline void CP_B(Z80* ctx) { ctx->CP_R(0b000); }
    static inline void CP_C(Z80* ctx) { ctx->CP_R(0b001); }
    static inline void CP_D(Z80* ctx) { ctx->CP_R(0b010); }
    static inline void CP_E(Z80* ctx) { ctx->CP_R(0b011); }
    static inline void CP_H(Z80* ctx) { ctx->CP_R(0b100); }
    static inline void CP_L(Z80* ctx) { ctx->CP_R(0b101); }
    static inline void CP_A(Z80* ctx) { ctx->CP_R(0b111); }
    static inline void CP_B_2(Z80* ctx) { ctx->CP_R(0b000, 2); }
    static inline void CP_C_2(Z80* ctx) { ctx->CP_R(0b001, 2); }
    static inline void CP_D_2(Z80* ctx) { ctx->CP_R(0b010, 2); }
    static inline void CP_E_2(Z80* ctx) { ctx->CP_R(0b011, 2); }
    static inline void CP_A_2(Z80* ctx) { ctx->CP_R(0b111, 2); }
    inline void CP_R(unsigned char r, int pc = 1)
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] CP %s, %s", reg.PC - pc, registerDump(0b111), registerDump(r));
#endif
        unsigned char* rp = getRegisterPointer(r);
        subtract8(*rp, 0, true, false);
    }

    // Compare Register IXH
    static inline void CP_IXH_(Z80* ctx) { ctx->CP_IXH(); }
    inline void CP_IXH()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] CP %s, IXH<$%02X>", reg.PC - 2, registerDump(0b111), getIXH());
#endif
        subtract8(getIXH(), 0, true, false);
    }

    // Compare Register IXL
    static inline void CP_IXL_(Z80* ctx) { ctx->CP_IXL(); }
    inline void CP_IXL()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] CP %s, IXL<$%02X>", reg.PC - 2, registerDump(0b111), getIXL());
#endif
        subtract8(getIXL(), 0, true, false);
    }

    // Compare Register IYH
    static inline void CP_IYH_(Z80* ctx) { ctx->CP_IYH(); }
    inline void CP_IYH()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] CP %s, IYH<$%02X>", reg.PC - 2, registerDump(0b111), getIYH());
#endif
        subtract8(getIYH(), 0, true, false);
    }

    // Compare Register IYL
    static inline void CP_IYL_(Z80* ctx) { ctx->CP_IYL(); }
    inline void CP_IYL()
    {
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] CP %s, IYL<$%02X>", reg.PC - 2, registerDump(0b111), getIYL());
#endif
        subtract8(getIYL(), 0, true, false);
    }

    // Compare immediate
    static inline void CP_N(Z80* ctx)
    {
        unsigned char n = ctx->fetch(3);
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] CP %s, $%02X", ctx->reg.PC - 2, ctx->registerDump(0b111), n);
#endif
        ctx->subtract8(n, 0, true, false);
    }

    // Compare memory
    static inline void CP_HL(Z80* ctx)
    {
        unsigned char n = ctx->readByte(ctx->getHL(), 3);
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] CP %s, (%s) = $%02X", ctx->reg.PC - 1, ctx->registerDump(0b111), ctx->registerPairDump(0b10), n);
#endif
        ctx->subtract8(n, 0, true, false);
    }

    // Compare memory
    static inline void CP_IX_(Z80* ctx) { ctx->CP_IX(); }
    inline void CP_IX()
    {
        signed char d = (signed char)fetch(4);
        unsigned char n = readByte((unsigned short)(reg.IX + d));
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] CP %s, (IX+d<$%04X>) = $%02X", reg.PC - 3, registerDump(0b111), (unsigned short)(reg.IX + d), n);
#endif
        subtract8(n, 0, true, false);
        consumeClock(3);
    }

    // Compare memory
    static inline void CP_IY_(Z80* ctx) { ctx->CP_IY(); }
    inline void CP_IY()
    {
        signed char d = (signed char)fetch(4);
        unsigned char n = readByte((unsigned short)(reg.IY + d));
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] CP %s, (IY+d<$%04X>) = $%02X", reg.PC - 3, registerDump(0b111), (unsigned short)(reg.IY + d), n);
#endif
        subtract8(n, 0, true, false);
        consumeClock(3);
    }

    // Jump
    static inline void JP_NN(Z80* ctx)
    {
        unsigned char l = ctx->fetch(3);
        unsigned char h = ctx->fetch(3);
        unsigned short addr = ctx->make16BitsFromLE(l, h);
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] JP $%04X", ctx->reg.PC - 3, addr);
#endif
        ctx->reg.PC = addr;
        ctx->reg.WZ = addr;
    }

    // Conditional Jump
    static inline void JP_C0_NN(Z80* ctx) { ctx->JP_C_NN(Condition::NZ); }
    static inline void JP_C1_NN(Z80* ctx) { ctx->JP_C_NN(Condition::Z); }
    static inline void JP_C2_NN(Z80* ctx) { ctx->JP_C_NN(Condition::NC); }
    static inline void JP_C3_NN(Z80* ctx) { ctx->JP_C_NN(Condition::C); }
    static inline void JP_C4_NN(Z80* ctx) { ctx->JP_C_NN(Condition::NPV); }
    static inline void JP_C5_NN(Z80* ctx) { ctx->JP_C_NN(Condition::PV); }
    static inline void JP_C6_NN(Z80* ctx) { ctx->JP_C_NN(Condition::NS); }
    static inline void JP_C7_NN(Z80* ctx) { ctx->JP_C_NN(Condition::S); }
    inline void JP_C_NN(Condition c)
    {
        unsigned char l = fetch(3);
        unsigned char h = fetch(3);
        unsigned short addr = make16BitsFromLE(l, h);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] JP %s, $%04X", reg.PC - 3, conditionDump(c), addr);
#endif
        if (checkConditionFlag(c)) reg.PC = addr;
        reg.WZ = addr;
    }

    // Jump Relative to PC+e
    static inline void JR_E(Z80* ctx)
    {
        signed char e = (signed char)ctx->fetch(3);
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] JR %s", ctx->reg.PC - 2, ctx->relativeDump(ctx->reg.PC - 2, e));
#endif
        ctx->reg.PC += e;
        ctx->consumeClock(5);
    }

    // Jump Relative to PC+e, if condition
    static inline void JR_NZ_E(Z80* ctx) { ctx->JR_CND_E(Condition::NZ); }
    static inline void JR_Z_E(Z80* ctx) { ctx->JR_CND_E(Condition::Z); }
    static inline void JR_NC_E(Z80* ctx) { ctx->JR_CND_E(Condition::NC); }
    static inline void JR_C_E(Z80* ctx) { ctx->JR_CND_E(Condition::C); }
    inline void JR_CND_E(Condition cnd)
    {
        signed char e = (signed char)fetch(3);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] JR %s, %s <%s>", reg.PC - 2, conditionDump(cnd), relativeDump(reg.PC - 2, e), checkConditionFlag(cnd) ? "YES" : "NO");
#endif
        if (checkConditionFlag(cnd)) {
            reg.PC += e;
            consumeClock(5);
        }
    }

    // Jump to HL
    static inline void JP_HL(Z80* ctx)
    {
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] JP %s", ctx->reg.PC - 1, ctx->registerPairDump(0b10));
#endif
        ctx->reg.PC = ctx->getHL();
    }

    // Jump to IX
    static inline void JP_IX(Z80* ctx)
    {
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] JP IX<$%04X>", ctx->reg.PC - 2, ctx->reg.IX);
#endif
        ctx->reg.PC = ctx->reg.IX;
    }

    // Jump to IY
    static inline void JP_IY(Z80* ctx)
    {
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] JP IY<$%04X>", ctx->reg.PC - 2, ctx->reg.IY);
#endif
        ctx->reg.PC = ctx->reg.IY;
    }

    // 	Decrement B and Jump relative if B=0
    static inline void DJNZ_E(Z80* ctx)
    {
        signed char e = (signed char)ctx->fetch(4);
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] DJNZ %s (%s)", ctx->reg.PC - 2, ctx->relativeDump(ctx->reg.PC - 2, e), ctx->registerDump(0b000));
#endif
        ctx->reg.pair.B--;
        if (ctx->reg.pair.B) {
            ctx->reg.PC += e;
            ctx->consumeClock(5);
        }
    }

    // Call
    static inline void CALL_NN(Z80* ctx)
    {
        unsigned short addrL = ctx->fetch(4);
        unsigned short addrH = ctx->fetch(3);
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] CALL $%04X (%s)", ctx->reg.PC - 3, ctx->make16BitsFromLE(addrL, addrH), ctx->registerPairDump(0b11));
#endif
        ctx->push(ctx->getPCH(), 3);
        ctx->setPCH(addrH);
        ctx->push(ctx->getPCL(), 3);
        ctx->setPCL(addrL);
        ctx->reg.WZ = ctx->reg.PC;
#ifndef Z80_DISABLE_NESTCHECK
        ctx->invokeCallHandlers();
#endif
    }

    // Return
    static inline void RET(Z80* ctx)
    {
#ifndef Z80_DISABLE_NESTCHECK
        ctx->invokeReturnHandlers();
#endif
#ifndef Z80_DISABLE_DEBUG
        unsigned short pc = ctx->reg.PC - 1;
        const char* dump = ctx->isDebug() ? ctx->registerPairDump(0b11) : "";
#endif
        ctx->setPCL(ctx->pop(3));
        ctx->setPCH(ctx->pop(3));
        ctx->reg.WZ = ctx->reg.PC;
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] RET to $%04X (%s)", pc, ctx->reg.PC, dump);
#endif
    }

    // Call with condition
    static inline void CALL_C0_NN(Z80* ctx) { ctx->CALL_C_NN(Condition::NZ); }
    static inline void CALL_C1_NN(Z80* ctx) { ctx->CALL_C_NN(Condition::Z); }
    static inline void CALL_C2_NN(Z80* ctx) { ctx->CALL_C_NN(Condition::NC); }
    static inline void CALL_C3_NN(Z80* ctx) { ctx->CALL_C_NN(Condition::C); }
    static inline void CALL_C4_NN(Z80* ctx) { ctx->CALL_C_NN(Condition::NPV); }
    static inline void CALL_C5_NN(Z80* ctx) { ctx->CALL_C_NN(Condition::PV); }
    static inline void CALL_C6_NN(Z80* ctx) { ctx->CALL_C_NN(Condition::NS); }
    static inline void CALL_C7_NN(Z80* ctx) { ctx->CALL_C_NN(Condition::S); }
    inline void CALL_C_NN(Condition c)
    {
        bool execute = checkConditionFlag(c);
        unsigned char nL = fetch(3);
        unsigned char nH = fetch(3);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] CALL %s, $%04X (%s) <execute:%s>", reg.PC - 3, conditionDump(c), make16BitsFromLE(nL, nH), registerPairDump(0b11), execute ? "YES" : "NO");
#endif
        if (execute) {
            push(getPCH(), 4);
            setPCH(nH);
            push(getPCL(), 3);
            setPCL(nL);
#ifndef Z80_DISABLE_NESTCHECK
            invokeCallHandlers();
#endif
        }
        reg.WZ = reg.PC;
    }

    //  with condition
    static inline void RET_C0(Z80* ctx) { ctx->RET_C(Condition::NZ); }
    static inline void RET_C1(Z80* ctx) { ctx->RET_C(Condition::Z); }
    static inline void RET_C2(Z80* ctx) { ctx->RET_C(Condition::NC); }
    static inline void RET_C3(Z80* ctx) { ctx->RET_C(Condition::C); }
    static inline void RET_C4(Z80* ctx) { ctx->RET_C(Condition::NPV); }
    static inline void RET_C5(Z80* ctx) { ctx->RET_C(Condition::PV); }
    static inline void RET_C6(Z80* ctx) { ctx->RET_C(Condition::NS); }
    static inline void RET_C7(Z80* ctx) { ctx->RET_C(Condition::S); }
    inline void RET_C(Condition c)
    {
        if (!checkConditionFlag(c)) {
#ifndef Z80_DISABLE_DEBUG
            if (isDebug()) log("[%04X] RET %s <execute:NO>", reg.PC - 1, conditionDump(c));
#endif
            consumeClock(1);
            return;
        }
#ifndef Z80_DISABLE_NESTCHECK
        invokeReturnHandlers();
#endif
#ifndef Z80_DISABLE_DEBUG
        unsigned short pc = reg.PC;
        unsigned short sp = reg.SP;
#endif
        setPCL(pop(4));
        setPCH(pop(3));
        reg.WZ = reg.PC;
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] RET %s to $%04X (SP<$%04X>) <execute:YES>", pc - 1, conditionDump(c), reg.PC, sp);
#endif
    }

    // Return from interrupt
    static inline void RETI_(Z80* ctx) { ctx->RETI(); }
    inline void RETI()
    {
#ifndef Z80_DISABLE_NESTCHECK
        invokeReturnHandlers();
#endif
#ifndef Z80_DISABLE_DEBUG
        unsigned short pc = reg.PC;
        unsigned short sp = reg.SP;
#endif
        setPCL(pop(3));
        setPCH(pop(3));
        reg.WZ = reg.PC;
        reg.IFF &= ~IFF_IRQ();
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] RETI to $%04X (SP<$%04X>)", pc - 2, reg.PC, sp);
#endif
    }

    // Return from non maskable interrupt
    static inline void RETN_(Z80* ctx) { ctx->RETN(); }
    inline void RETN()
    {
#ifndef Z80_DISABLE_NESTCHECK
        invokeReturnHandlers();
#endif
#ifndef Z80_DISABLE_DEBUG
        unsigned short pc = reg.PC;
        unsigned short sp = reg.SP;
#endif
        setPCL(pop(3));
        setPCH(pop(3));
        reg.WZ = reg.PC;
        reg.IFF &= ~IFF_NMI();
        if (!((reg.IFF & IFF1()) && (reg.IFF & IFF2()))) {
            reg.IFF |= IFF1();
        } else {
            if (reg.IFF & IFF2()) {
                reg.IFF |= IFF1();
            } else {
                reg.IFF &= ~IFF1();
            }
        }
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] RETN to $%04X (SP<$%04X>)", pc - 2, reg.PC, sp);
#endif
    }

    // Interrupt
    static inline void RST00(Z80* ctx) { ctx->RST(0, true); }
    static inline void RST08(Z80* ctx) { ctx->RST(1, true); }
    static inline void RST10(Z80* ctx) { ctx->RST(2, true); }
    static inline void RST18(Z80* ctx) { ctx->RST(3, true); }
    static inline void RST20(Z80* ctx) { ctx->RST(4, true); }
    static inline void RST28(Z80* ctx) { ctx->RST(5, true); }
    static inline void RST30(Z80* ctx) { ctx->RST(6, true); }
    static inline void RST38(Z80* ctx) { ctx->RST(7, true); }
    inline void RST(unsigned char t, bool isOperand)
    {
        unsigned short addr = t * 8;
#ifndef Z80_DISABLE_DEBUG
        unsigned short sp = reg.SP;
        unsigned short pc = reg.PC;
#endif
        push(getPCH(), 4);
        setPCH(0);
        push(getPCL(), 3);
        reg.PC = addr;
        reg.WZ = addr;
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] RST $%04X (SP<$%04X>)", pc - (isOperand ? 1 : 0), addr, sp);
#endif
#ifndef Z80_DISABLE_NESTCHECK
        invokeCallHandlers();
#endif
    }

    // Input a byte form device n to accu.
    static inline void IN_A_N(Z80* ctx)
    {
        unsigned char n = ctx->fetch(3);
        unsigned char i = ctx->inPortWithA(n);
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] IN %s, ($%02X) = $%02X", ctx->reg.PC - 2, ctx->registerDump(0b111), n, i);
#endif
        ctx->reg.pair.A = i;
    }

    // Input a byte form device (C) to register.
    static inline void IN_B_C(Z80* ctx) { ctx->IN_R_C(0b000); }
    static inline void IN_C_C(Z80* ctx) { ctx->IN_R_C(0b001); }
    static inline void IN_D_C(Z80* ctx) { ctx->IN_R_C(0b010); }
    static inline void IN_E_C(Z80* ctx) { ctx->IN_R_C(0b011); }
    static inline void IN_H_C(Z80* ctx) { ctx->IN_R_C(0b100); }
    static inline void IN_L_C(Z80* ctx) { ctx->IN_R_C(0b101); }
    static inline void IN_C(Z80* ctx) { ctx->IN_R_C(0, false); }
    static inline void IN_A_C(Z80* ctx) { ctx->IN_R_C(0b111); }
    inline void IN_R_C(unsigned char r, bool setRegister = true)
    {
        unsigned char* rp = setRegister ? getRegisterPointer(r) : nullptr;
        unsigned char i = inPortWithB(reg.pair.C);
#ifndef Z80_DISABLE_DEBUG
        if (rp) {
            if (isDebug()) log("[%04X] IN %s, (%s) = $%02X", reg.PC - 2, registerDump(r), registerDump(0b001), i);
            *rp = i;
        } else {
            if (isDebug()) log("[%04X] IN (%s) = $%02X", reg.PC - 2, registerDump(0b001), i);
        }
#else
        if (rp) *rp = i;
#endif
        setFlagS(i & 0x80);
        setFlagZ(i == 0);
        resetFlagH();
        setFlagPV(isEvenNumberBits(i));
        resetFlagN();
        setFlagXY(i);
    }

    inline void decrementB_forRepeatIO()
    {
        reg.pair.B--;
        reg.pair.F = 0;
        setFlagC(isFlagC());
        setFlagN();
        setFlagZ(reg.pair.B == 0);
        setFlagXY(reg.pair.B);
        setFlagS(reg.pair.B & 0x80);
        setFlagH((reg.pair.B & 0x0F) == 0x0F);
        setFlagPV(reg.pair.B == 0x7F);
    }

    // Load location (HL) with input from port (C); or increment/decrement HL and decrement B
    inline void repeatIN(bool isIncHL, bool isRepeat)
    {
        reg.WZ = (unsigned short)(getBC() + (isIncHL ? 1 : -1));
        unsigned char i = inPortWithB(reg.pair.C);
        decrementB_forRepeatIO();
        unsigned short hl = getHL();
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) {
            if (isIncHL) {
                log("[%04X] %s ... (%s) <- p(%s) = $%02X [%s]", reg.PC - 2, isRepeat ? "INIR" : "INI", registerPairDump(0b10), registerDump(0b001), i, registerDump(0b000));
            } else {
                log("[%04X] %s ... (%s) <- p(%s) = $%02X [%s]", reg.PC - 2, isRepeat ? "INDR" : "IND", registerPairDump(0b10), registerDump(0b001), i, registerDump(0b000));
            }
        }
#endif
        writeByte(hl, i);
        hl += isIncHL ? 1 : -1;
        setHL(hl);
        setFlagZ(reg.pair.B == 0);
        setFlagN(i & 0x80);                                               // NOTE: undocumented
        setFlagC(0xFF < i + ((reg.pair.C + 1) & 0xFF));                   // NOTE: undocumented
        setFlagH(isFlagC());                                              // NOTE: undocumented
        setFlagPV((i + (((reg.pair.C + 1) & 0xFF) & 0x07)) ^ reg.pair.B); // NOTE: undocumented
        if (isRepeat && 0 != reg.pair.B) {
            reg.PC -= 2;
            consumeClock(5);
        }
    }
    static inline void INI(Z80* ctx) { ctx->repeatIN(true, false); }
    static inline void INIR(Z80* ctx) { ctx->repeatIN(true, true); }
    static inline void IND(Z80* ctx) { ctx->repeatIN(false, false); }
    static inline void INDR(Z80* ctx) { ctx->repeatIN(false, true); }

    // Load Output port (n) with Acc.
    static inline void OUT_N_A(Z80* ctx)
    {
        unsigned char n = ctx->fetch(3);
#ifndef Z80_DISABLE_DEBUG
        if (ctx->isDebug()) ctx->log("[%04X] OUT ($%02X), %s", ctx->reg.PC - 2, n, ctx->registerDump(0b111));
#endif
        ctx->outPortWithA(n, ctx->reg.pair.A);
    }

    // Output a byte to device (C) form register.
    static inline void OUT_C_B(Z80* ctx) { ctx->OUT_C_R(0b000); }
    static inline void OUT_C_C(Z80* ctx) { ctx->OUT_C_R(0b001); }
    static inline void OUT_C_D(Z80* ctx) { ctx->OUT_C_R(0b010); }
    static inline void OUT_C_E(Z80* ctx) { ctx->OUT_C_R(0b011); }
    static inline void OUT_C_H(Z80* ctx) { ctx->OUT_C_R(0b100); }
    static inline void OUT_C_L(Z80* ctx) { ctx->OUT_C_R(0b101); }
    static inline void OUT_C_0(Z80* ctx) { ctx->OUT_C_R(0, true); }
    static inline void OUT_C_A(Z80* ctx) { ctx->OUT_C_R(0b111); }
    inline void OUT_C_R(unsigned char r, bool zero = false)
    {
        if (zero) {
#ifndef Z80_DISABLE_DEBUG
            if (isDebug()) log("[%04X] OUT (%s), 0", reg.PC - 2, registerDump(0b001));
#endif
            outPortWithB(reg.pair.C, 0);
        } else {
#ifndef Z80_DISABLE_DEBUG
            if (isDebug()) log("[%04X] OUT (%s), %s", reg.PC - 2, registerDump(0b001), registerDump(r));
#endif
            outPortWithB(reg.pair.C, getRegister(r));
        }
    }

    // Load Output port (C) with location (HL), increment/decrement HL and decrement B
    inline void repeatOUT(bool isIncHL, bool isRepeat)
    {
        unsigned char o = readByte(getHL());
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) {
            if (isIncHL) {
                log("[%04X] %s ... p(%s) <- (%s) <$%02x> [%s]", reg.PC - 2, isRepeat ? "OUTIR" : "OUTI", registerDump(0b001), registerPairDump(0b10), o, registerDump(0b000));
            } else {
                log("[%04X] %s ... p(%s) <- (%s) <$%02x> [%s]", reg.PC - 2, isRepeat ? "OUTDR" : "OUTD", registerDump(0b001), registerPairDump(0b10), o, registerDump(0b000));
            }
        }
#endif
        decrementB_forRepeatIO();
        outPortWithB(reg.pair.C, o);
        reg.WZ = (unsigned short)(getBC() + (isIncHL ? 1 : -1));
        setHL((unsigned short)(getHL() + (isIncHL ? 1 : -1)));
        setFlagZ(reg.pair.B == 0);
        setFlagN(o & 0x80);                                // NOTE: ACTUAL FLAG CONDITION IS UNKNOWN
        setFlagH(reg.pair.L + o > 0xFF);                   // NOTE: ACTUAL FLAG CONDITION IS UNKNOWN
        setFlagC(isFlagH());                               // NOTE: ACTUAL FLAG CONDITION IS UNKNOWN
        setFlagPV(((reg.pair.H + o) & 0x07) ^ reg.pair.B); // NOTE: ACTUAL FLAG CONDITION IS UNKNOWN
        if (isRepeat && 0 != reg.pair.B) {
            reg.PC -= 2;
            consumeClock(5);
        }
    }
    static inline void OUTI(Z80* ctx) { ctx->repeatOUT(true, false); }
    static inline void OUTIR(Z80* ctx) { ctx->repeatOUT(true, true); }
    static inline void OUTD(Z80* ctx) { ctx->repeatOUT(false, false); }
    static inline void OUTDR(Z80* ctx) { ctx->repeatOUT(false, true); }

    // Decimal Adjust Accumulator
    static inline void DAA(Z80* ctx) { ctx->daa(); }
    inline void daa()
    {
        int a = reg.pair.A;
        bool c = isFlagC();
        bool ac = reg.pair.A > 0x99;
        int add = (isFlagH() || (a & 0x0F) > 9 ? 0x06 : 0x00) + (c || ac ? 0x60 : 0x00);
        a += isFlagN() ? -add : add;
        a &= 0xFF;
        setFlagS(a & 0x80);
        setFlagXY(a);
        setFlagZ(0 == a);
        setFlagH((a ^ reg.pair.A) & flagH());
        setFlagPV(isEvenNumberBits(a));
        setFlagC(c | ac);
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] DAA ... A: $%02X -> $%02X", reg.PC - 1, reg.pair.A, a);
#endif
        reg.pair.A = a;
    }

    // Rotate digit Left and right between Acc. and location (HL)
    static inline void RLD_(Z80* ctx) { ctx->RLD(); }
    inline void RLD()
    {
        unsigned short hl = getHL();
        unsigned char beforeN = readByte(hl);
        unsigned char nH = (beforeN & 0b11110000) >> 4;
        unsigned char nL = beforeN & 0b00001111;
        unsigned char aH = (reg.pair.A & 0b11110000) >> 4;
        unsigned char aL = reg.pair.A & 0b00001111;
#ifndef Z80_DISABLE_DEBUG
        unsigned char beforeA = reg.pair.A;
#endif
        unsigned char afterA = (aH << 4) | nH;
        unsigned char afterN = (nL << 4) | aL;
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] RLD ... A: $%02X -> $%02X, ($%04X): $%02X -> $%02X", reg.PC - 2, beforeA, afterA, hl, beforeN, afterN);
#endif
        reg.pair.A = afterA;
        writeByte(hl, afterN);
        setFlagS(reg.pair.A & 0x80);
        setFlagXY(reg.pair.A);
        setFlagZ(reg.pair.A == 0);
        resetFlagH();
        setFlagPV(isEvenNumberBits(reg.pair.A));
        resetFlagN();
        consumeClock(2);
    }

    // Rotate digit Right and right between Acc. and location (HL)
    static inline void RRD_(Z80* ctx) { ctx->RRD(); }
    inline void RRD()
    {
        unsigned short hl = getHL();
        unsigned char beforeN = readByte(hl);
        unsigned char nH = (beforeN & 0b11110000) >> 4;
        unsigned char nL = beforeN & 0b00001111;
        unsigned char aH = (reg.pair.A & 0b11110000) >> 4;
        unsigned char aL = reg.pair.A & 0b00001111;
#ifndef Z80_DISABLE_DEBUG
        unsigned char beforeA = reg.pair.A;
#endif
        unsigned char afterA = (aH << 4) | nL;
        unsigned char afterN = (aL << 4) | nH;
#ifndef Z80_DISABLE_DEBUG
        if (isDebug()) log("[%04X] RRD ... A: $%02X -> $%02X, ($%04X): $%02X -> $%02X", reg.PC - 2, beforeA, afterA, hl, beforeN, afterN);
#endif
        reg.pair.A = afterA;
        writeByte(hl, afterN);
        setFlagS(reg.pair.A & 0x80);
        setFlagXY(reg.pair.A);
        setFlagZ(reg.pair.A == 0);
        resetFlagH();
        setFlagPV(isEvenNumberBits(reg.pair.A));
        resetFlagN();
        consumeClock(2);
    }

#ifndef Z80_DISABLE_BREAKPOINT
    int opLength1[256] = {
        1, 3, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 00 ~ 0F
        2, 3, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 2, 1, // 10 ~ 1F
        2, 3, 3, 1, 1, 1, 2, 1, 2, 1, 3, 1, 1, 1, 2, 1, // 20 ~ 2F
        2, 3, 3, 1, 1, 1, 2, 1, 2, 1, 3, 1, 1, 1, 2, 1, // 30 ~ 3F
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 40 ~ 4F
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 50 ~ 5F
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 60 ~ 6F
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 70 ~ 7F
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80 ~ 8F
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 90 ~ 9F
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A0 ~ AF
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B0 ~ BF
        1, 1, 3, 3, 3, 1, 2, 1, 1, 1, 3, 2, 3, 3, 2, 1, // C0 ~ CF
        1, 1, 3, 2, 3, 1, 2, 1, 1, 1, 3, 2, 3, 0, 2, 1, // D0 ~ DF
        1, 1, 3, 1, 3, 1, 2, 1, 1, 1, 3, 1, 3, 0, 2, 1, // E0 ~ EF
        1, 1, 3, 1, 3, 1, 2, 1, 1, 1, 3, 1, 3, 0, 2, 1  // F0 ~ FF
    };
    int opLengthED[256] = {
        3, 3, 0, 0, 2, 0, 0, 0, 3, 3, 0, 0, 2, 0, 0, 0, // 00 ~ 0F
        3, 3, 0, 0, 2, 0, 0, 0, 3, 3, 0, 0, 2, 0, 0, 0, // 10 ~ 1F
        3, 3, 0, 0, 2, 0, 0, 0, 3, 3, 0, 0, 2, 0, 0, 0, // 20 ~ 2F
        0, 0, 0, 0, 2, 0, 0, 0, 3, 3, 0, 0, 2, 0, 0, 0, // 30 ~ 3F
        2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 0, 2, // 40 ~ 4F
        2, 2, 2, 4, 0, 0, 2, 2, 2, 2, 2, 4, 2, 0, 2, 2, // 50 ~ 5F
        2, 2, 2, 4, 3, 0, 0, 2, 2, 2, 2, 4, 2, 0, 0, 2, // 60 ~ 6F
        2, 2, 2, 4, 3, 0, 2, 0, 2, 2, 2, 4, 2, 0, 0, 0, // 70 ~ 7F
        0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, // 80 ~ 8F
        0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, // 90 ~ 9F
        2, 2, 2, 2, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, // A0 ~ AF
        2, 2, 2, 2, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, // B0 ~ BF
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C0 ~ CF
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D0 ~ DF
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E0 ~ EF
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // F0 ~ FF
    };
    int opLengthIXY[256] = {
        0, 0, 0, 0, 2, 2, 3, 0, 0, 2, 0, 0, 2, 2, 3, 0, // 00 ~ 0F
        0, 0, 0, 0, 2, 2, 3, 0, 0, 2, 0, 0, 2, 2, 3, 0, // 10 ~ 1F
        0, 4, 4, 2, 2, 2, 3, 0, 0, 2, 4, 2, 2, 2, 3, 0, // 20 ~ 2F
        0, 0, 0, 0, 3, 3, 4, 0, 0, 2, 0, 0, 2, 2, 3, 0, // 30 ~ 3F
        2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 40 ~ 4F
        2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 50 ~ 5F
        2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 60 ~ 6F
        3, 3, 3, 3, 3, 3, 0, 3, 2, 2, 2, 2, 2, 2, 3, 2, // 70 ~ 7F
        2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 80 ~ 8F
        2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // 90 ~ 9F
        2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // A0 ~ AF
        2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, // B0 ~ BF
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, // C0 ~ CF
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D0 ~ DF
        0, 2, 0, 2, 0, 2, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, // E0 ~ EF
        0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0  // F0 ~ FF
    };
#endif
    void (*opSet1[256])(Z80* ctx) = {
        NOP_t, LD_BC_NN, LD_BC_A, INC_RP_BC, INC_B, DEC_B, LD_B_N, RLCA, EX_AF_AF2, ADD_HL_BC, LD_A_BC, DEC_RP_BC, INC_C, DEC_C, LD_C_N, RRCA,
        DJNZ_E, LD_DE_NN, LD_DE_A, INC_RP_DE, INC_D, DEC_D, LD_D_N, RLA, JR_E, ADD_HL_DE, LD_A_DE, DEC_RP_DE, INC_E, DEC_E, LD_E_N, RRA,
        JR_NZ_E, LD_HL_NN, LD_ADDR_HL, INC_RP_HL, INC_H, DEC_H, LD_H_N, DAA, JR_Z_E, ADD_HL_HL, LD_HL_ADDR, DEC_RP_HL, INC_L, DEC_L, LD_L_N, CPL,
        JR_NC_E, LD_SP_NN, LD_NN_A, INC_RP_SP, INC_HL, DEC_HL, LD_HL_N, SCF, JR_C_E, ADD_HL_SP, LD_A_NN, DEC_RP_SP, INC_A, DEC_A, LD_A_N, CCF,
        LD_B_B, LD_B_C, LD_B_D, LD_B_E, LD_B_H, LD_B_L, LD_B_HL, LD_B_A, LD_C_B, LD_C_C, LD_C_D, LD_C_E, LD_C_H, LD_C_L, LD_C_HL, LD_C_A,
        LD_D_B, LD_D_C, LD_D_D, LD_D_E, LD_D_H, LD_D_L, LD_D_HL, LD_D_A, LD_E_B, LD_E_C, LD_E_D, LD_E_E, LD_E_H, LD_E_L, LD_E_HL, LD_E_A,
        LD_H_B, LD_H_C, LD_H_D, LD_H_E, LD_H_H, LD_H_L, LD_H_HL, LD_H_A, LD_L_B, LD_L_C, LD_L_D, LD_L_E, LD_L_H, LD_L_L, LD_L_HL, LD_L_A,
        LD_HL_B, LD_HL_C, LD_HL_D, LD_HL_E, LD_HL_H, LD_HL_L, HALT, LD_HL_A, LD_A_B, LD_A_C, LD_A_D, LD_A_E, LD_A_H, LD_A_L, LD_A_HL, LD_A_A,
        ADD_B, ADD_C, ADD_D, ADD_E, ADD_H, ADD_L, ADD_HL, ADD_A, ADC_B, ADC_C, ADC_D, ADC_E, ADC_H, ADC_L, ADC_HL, ADC_A,
        SUB_B, SUB_C, SUB_D, SUB_E, SUB_H, SUB_L, SUB_HL, SUB_A, SBC_B, SBC_C, SBC_D, SBC_E, SBC_H, SBC_L, SBC_HL, SBC_A,
        AND_B, AND_C, AND_D, AND_E, AND_H, AND_L, AND_HL, AND_A, XOR_B, XOR_C, XOR_D, XOR_E, XOR_H, XOR_L, XOR_HL, XOR_A,
        OR_B, OR_C, OR_D, OR_E, OR_H, OR_L, OR_HL, OR_A, CP_B, CP_C, CP_D, CP_E, CP_H, CP_L, CP_HL, CP_A,
        RET_C0, POP_BC, JP_C0_NN, JP_NN, CALL_C0_NN, PUSH_BC, ADD_N, RST00, RET_C1, RET, JP_C1_NN, OP_CB, CALL_C1_NN, CALL_NN, ADC_N, RST08,
        RET_C2, POP_DE, JP_C2_NN, OUT_N_A, CALL_C2_NN, PUSH_DE, SUB_N, RST10, RET_C3, EXX, JP_C3_NN, IN_A_N, CALL_C3_NN, OP_IX, SBC_N, RST18,
        RET_C4, POP_HL, JP_C4_NN, EX_SP_HL, CALL_C4_NN, PUSH_HL, AND_N, RST20, RET_C5, JP_HL, JP_C5_NN, EX_DE_HL, CALL_C5_NN, OP_ED, XOR_N, RST28,
        RET_C6, POP_AF, JP_C6_NN, DI, CALL_C6_NN, PUSH_AF, OR_N, RST30, RET_C7, LD_SP_HL, JP_C7_NN, EI, CALL_C7_NN, OP_IY, CP_N, RST38};
    void (*opSetCB[256])(Z80* ctx) = {
        RLC_B, RLC_C, RLC_D, RLC_E, RLC_H, RLC_L, RLC_HL_, RLC_A,
        RRC_B, RRC_C, RRC_D, RRC_E, RRC_H, RRC_L, RRC_HL_, RRC_A,
        RL_B, RL_C, RL_D, RL_E, RL_H, RL_L, RL_HL_, RL_A,
        RR_B, RR_C, RR_D, RR_E, RR_H, RR_L, RR_HL_, RR_A,
        SLA_B, SLA_C, SLA_D, SLA_E, SLA_H, SLA_L, SLA_HL_, SLA_A,
        SRA_B, SRA_C, SRA_D, SRA_E, SRA_H, SRA_L, SRA_HL_, SRA_A,
        SLL_B, SLL_C, SLL_D, SLL_E, SLL_H, SLL_L, SLL_HL_, SLL_A,
        SRL_B, SRL_C, SRL_D, SRL_E, SRL_H, SRL_L, SRL_HL_, SRL_A,
        BIT_B_0, BIT_C_0, BIT_D_0, BIT_E_0, BIT_H_0, BIT_L_0, BIT_HL_0, BIT_A_0,
        BIT_B_1, BIT_C_1, BIT_D_1, BIT_E_1, BIT_H_1, BIT_L_1, BIT_HL_1, BIT_A_1,
        BIT_B_2, BIT_C_2, BIT_D_2, BIT_E_2, BIT_H_2, BIT_L_2, BIT_HL_2, BIT_A_2,
        BIT_B_3, BIT_C_3, BIT_D_3, BIT_E_3, BIT_H_3, BIT_L_3, BIT_HL_3, BIT_A_3,
        BIT_B_4, BIT_C_4, BIT_D_4, BIT_E_4, BIT_H_4, BIT_L_4, BIT_HL_4, BIT_A_4,
        BIT_B_5, BIT_C_5, BIT_D_5, BIT_E_5, BIT_H_5, BIT_L_5, BIT_HL_5, BIT_A_5,
        BIT_B_6, BIT_C_6, BIT_D_6, BIT_E_6, BIT_H_6, BIT_L_6, BIT_HL_6, BIT_A_6,
        BIT_B_7, BIT_C_7, BIT_D_7, BIT_E_7, BIT_H_7, BIT_L_7, BIT_HL_7, BIT_A_7,
        RES_B_0, RES_C_0, RES_D_0, RES_E_0, RES_H_0, RES_L_0, RES_HL_0, RES_A_0,
        RES_B_1, RES_C_1, RES_D_1, RES_E_1, RES_H_1, RES_L_1, RES_HL_1, RES_A_1,
        RES_B_2, RES_C_2, RES_D_2, RES_E_2, RES_H_2, RES_L_2, RES_HL_2, RES_A_2,
        RES_B_3, RES_C_3, RES_D_3, RES_E_3, RES_H_3, RES_L_3, RES_HL_3, RES_A_3,
        RES_B_4, RES_C_4, RES_D_4, RES_E_4, RES_H_4, RES_L_4, RES_HL_4, RES_A_4,
        RES_B_5, RES_C_5, RES_D_5, RES_E_5, RES_H_5, RES_L_5, RES_HL_5, RES_A_5,
        RES_B_6, RES_C_6, RES_D_6, RES_E_6, RES_H_6, RES_L_6, RES_HL_6, RES_A_6,
        RES_B_7, RES_C_7, RES_D_7, RES_E_7, RES_H_7, RES_L_7, RES_HL_7, RES_A_7,
        SET_B_0, SET_C_0, SET_D_0, SET_E_0, SET_H_0, SET_L_0, SET_HL_0, SET_A_0,
        SET_B_1, SET_C_1, SET_D_1, SET_E_1, SET_H_1, SET_L_1, SET_HL_1, SET_A_1,
        SET_B_2, SET_C_2, SET_D_2, SET_E_2, SET_H_2, SET_L_2, SET_HL_2, SET_A_2,
        SET_B_3, SET_C_3, SET_D_3, SET_E_3, SET_H_3, SET_L_3, SET_HL_3, SET_A_3,
        SET_B_4, SET_C_4, SET_D_4, SET_E_4, SET_H_4, SET_L_4, SET_HL_4, SET_A_4,
        SET_B_5, SET_C_5, SET_D_5, SET_E_5, SET_H_5, SET_L_5, SET_HL_5, SET_A_5,
        SET_B_6, SET_C_6, SET_D_6, SET_E_6, SET_H_6, SET_L_6, SET_HL_6, SET_A_6,
        SET_B_7, SET_C_7, SET_D_7, SET_E_7, SET_H_7, SET_L_7, SET_HL_7, SET_A_7};
    void (*opSetED[256])(Z80* ctx) = {
        nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
        nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
        nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
        nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
        IN_B_C, OUT_C_B, SBC_HL_BC, LD_ADDR_RP_BC, NEG_, RETN_, IM0, LD_I_A_,
        IN_C_C, OUT_C_C, ADC_HL_BC, LD_RP_ADDR_BC, nullptr, RETI_, nullptr, LD_R_A_,
        IN_D_C, OUT_C_D, SBC_HL_DE, LD_ADDR_RP_DE, nullptr, nullptr, IM1, LD_A_I_,
        IN_E_C, OUT_C_E, ADC_HL_DE, LD_RP_ADDR_DE, nullptr, nullptr, IM2, LD_A_R_,
        IN_H_C, OUT_C_H, SBC_HL_HL, LD_ADDR_RP_HL, nullptr, nullptr, nullptr, RRD_,
        IN_L_C, OUT_C_L, ADC_HL_HL, LD_RP_ADDR_HL, nullptr, nullptr, nullptr, RLD_,
        IN_C, OUT_C_0, SBC_HL_SP, LD_ADDR_RP_SP, nullptr, nullptr, nullptr, nullptr,
        IN_A_C, OUT_C_A, ADC_HL_SP, LD_RP_ADDR_SP, nullptr, nullptr, nullptr, nullptr,
        nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
        nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
        LDI, CPI, INI, OUTI, nullptr, nullptr, nullptr, nullptr,
        LDD, CPD, IND, OUTD, nullptr, nullptr, nullptr, nullptr,
        LDIR, CPIR, INIR, OUTIR, nullptr, nullptr, nullptr, nullptr,
        LDDR, CPDR, INDR, OUTDR, nullptr, nullptr, nullptr, nullptr};
    void (*opSetIX[256])(Z80* ctx) = {
        nullptr, nullptr, nullptr, nullptr, INC_B_2, DEC_B_2, LD_B_N_3, nullptr,
        nullptr, ADD_IX_BC, nullptr, nullptr, INC_C_2, DEC_C_2, LD_C_N_3, nullptr,
        nullptr, nullptr, nullptr, nullptr, INC_D_2, DEC_D_2, LD_D_N_3, nullptr,
        nullptr, ADD_IX_DE, nullptr, nullptr, INC_E_2, DEC_E_2, LD_E_N_3, nullptr,
        nullptr, LD_IX_NN_, LD_ADDR_IX_, INC_IX_reg_, INC_IXH_, DEC_IXH_, LD_IXH_N_, nullptr,
        nullptr, ADD_IX_IX, LD_IX_ADDR_, DEC_IX_reg_, INC_IXL_, DEC_IXL_, LD_IXL_N_, nullptr,
        nullptr, nullptr, nullptr, nullptr, INC_IX_, DEC_IX_, LD_IX_N_, nullptr,
        nullptr, ADD_IX_SP, nullptr, nullptr, INC_A_2, DEC_A_2, LD_A_N_3, nullptr,
        LD_B_B_2, LD_B_C_2, LD_B_D_2, LD_B_E_2, LD_B_IXH, LD_B_IXL, LD_B_IX, LD_B_A_2,
        LD_C_B_2, LD_C_C_2, LD_C_D_2, LD_C_E_2, LD_C_IXH, LD_C_IXL, LD_C_IX, LD_C_A_2,
        LD_D_B_2, LD_D_C_2, LD_D_D_2, LD_D_E_2, LD_D_IXH, LD_D_IXL, LD_D_IX, LD_D_A_2,
        LD_E_B_2, LD_E_C_2, LD_E_D_2, LD_E_E_2, LD_E_IXH, LD_E_IXL, LD_E_IX, LD_E_A_2,
        LD_IXH_B, LD_IXH_C, LD_IXH_D, LD_IXH_E, LD_IXH_IXH_, LD_IXH_IXL_, LD_H_IX, LD_IXH_A,
        LD_IXL_B, LD_IXL_C, LD_IXL_D, LD_IXL_E, LD_IXL_IXH_, LD_IXL_IXL_, LD_L_IX, LD_IXL_A,
        LD_IX_B, LD_IX_C, LD_IX_D, LD_IX_E, LD_IX_H, LD_IX_L, nullptr, LD_IX_A,
        LD_A_B_2, LD_A_C_2, LD_A_D_2, LD_A_E_2, LD_A_IXH, LD_A_IXL, LD_A_IX, LD_A_A_2,
        ADD_B_2, ADD_C_2, ADD_D_2, ADD_E_2, ADD_IXH_, ADD_IXL_, ADD_IX_, ADD_A_2,
        ADC_B_2, ADC_C_2, ADC_D_2, ADC_E_2, ADC_IXH_, ADC_IXL_, ADC_IX_, ADC_A_2,
        SUB_B_2, SUB_C_2, SUB_D_2, SUB_E_2, SUB_IXH_, SUB_IXL_, SUB_IX_, SUB_A_2,
        SBC_B_2, SBC_C_2, SBC_D_2, SBC_E_2, SBC_IXH_, SBC_IXL_, SBC_IX_, SBC_A_2,
        AND_B_2, AND_C_2, AND_D_2, AND_E_2, AND_IXH_, AND_IXL_, AND_IX_, AND_A_2,
        XOR_B_2, XOR_C_2, XOR_D_2, XOR_E_2, XOR_IXH_, XOR_IXL_, XOR_IX_, XOR_A_2,
        OR_B_2, OR_C_2, OR_D_2, OR_E_2, OR_IXH_, OR_IXL_, OR_IX_, OR_A_2,
        CP_B_2, CP_C_2, CP_D_2, CP_E_2, CP_IXH_, CP_IXL_, CP_IX_, CP_A_2,
        nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
        nullptr, nullptr, nullptr, OP_IX4, nullptr, nullptr, nullptr, nullptr,
        nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
        nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
        nullptr, POP_IX_, nullptr, EX_SP_IX_, nullptr, PUSH_IX_, nullptr, nullptr,
        nullptr, JP_IX, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
        nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
        nullptr, LD_SP_IX_, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
    void (*opSetIY[256])(Z80* ctx) = {
        nullptr, nullptr, nullptr, nullptr, INC_B_2, DEC_B_2, LD_B_N_3, nullptr,
        nullptr, ADD_IY_BC, nullptr, nullptr, INC_C_2, DEC_C_2, LD_C_N_3, nullptr,
        nullptr, nullptr, nullptr, nullptr, INC_D_2, DEC_D_2, LD_D_N_3, nullptr,
        nullptr, ADD_IY_DE, nullptr, nullptr, INC_E_2, DEC_E_2, LD_E_N_3, nullptr,
        nullptr, LD_IY_NN_, LD_ADDR_IY_, INC_IY_reg_, INC_IYH_, DEC_IYH_, LD_IYH_N_, nullptr,
        nullptr, ADD_IY_IY, LD_IY_ADDR_, DEC_IY_reg_, INC_IYL_, DEC_IYL_, LD_IYL_N_, nullptr,
        nullptr, nullptr, nullptr, nullptr, INC_IY_, DEC_IY_, LD_IY_N_, nullptr,
        nullptr, ADD_IY_SP, nullptr, nullptr, INC_A_2, DEC_A_2, LD_A_N_3, nullptr,
        LD_B_B_2, LD_B_C_2, LD_B_D_2, LD_B_E_2, LD_B_IYH, LD_B_IYL, LD_B_IY, LD_B_A_2,
        LD_C_B_2, LD_C_C_2, LD_C_D_2, LD_C_E_2, LD_C_IYH, LD_C_IYL, LD_C_IY, LD_C_A_2,
        LD_D_B_2, LD_D_C_2, LD_D_D_2, LD_D_E_2, LD_D_IYH, LD_D_IYL, LD_D_IY, LD_D_A_2,
        LD_E_B_2, LD_E_C_2, LD_E_D_2, LD_E_E_2, LD_E_IYH, LD_E_IYL, LD_E_IY, LD_E_A_2,
        LD_IYH_B, LD_IYH_C, LD_IYH_D, LD_IYH_E, LD_IYH_IYH_, LD_IYH_IYL_, LD_H_IY, LD_IYH_A,
        LD_IYL_B, LD_IYL_C, LD_IYL_D, LD_IYL_E, LD_IYL_IYH_, LD_IYL_IYL_, LD_L_IY, LD_IYL_A,
        LD_IY_B, LD_IY_C, LD_IY_D, LD_IY_E, LD_IY_H, LD_IY_L, nullptr, LD_IY_A,
        LD_A_B_2, LD_A_C_2, LD_A_D_2, LD_A_E_2, LD_A_IYH, LD_A_IYL, LD_A_IY, LD_A_A_2,
        ADD_B_2, ADD_C_2, ADD_D_2, ADD_E_2, ADD_IYH_, ADD_IYL_, ADD_IY_, ADD_A_2,
        ADC_B_2, ADC_C_2, ADC_D_2, ADC_E_2, ADC_IYH_, ADC_IYL_, ADC_IY_, ADC_A_2,
        SUB_B_2, SUB_C_2, SUB_D_2, SUB_E_2, SUB_IYH_, SUB_IYL_, SUB_IY_, SUB_A_2,
        SBC_B_2, SBC_C_2, SBC_D_2, SBC_E_2, SBC_IYH_, SBC_IYL_, SBC_IY_, SBC_A_2,
        AND_B_2, AND_C_2, AND_D_2, AND_E_2, AND_IYH_, AND_IYL_, AND_IY_, AND_A_2,
        XOR_B_2, XOR_C_2, XOR_D_2, XOR_E_2, XOR_IYH_, XOR_IYL_, XOR_IY_, XOR_A_2,
        OR_B_2, OR_C_2, OR_D_2, OR_E_2, OR_IYH_, OR_IYL_, OR_IY_, OR_A_2,
        CP_B_2, CP_C_2, CP_D_2, CP_E_2, CP_IYH_, CP_IYL_, CP_IY_, CP_A_2,
        nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
        nullptr, nullptr, nullptr, OP_IY4, nullptr, nullptr, nullptr, nullptr,
        nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
        nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
        nullptr, POP_IY_, nullptr, EX_SP_IY_, nullptr, PUSH_IY_, nullptr, nullptr,
        nullptr, JP_IY, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
        nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
        nullptr, LD_SP_IY_, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
    void (*opSetIX4[256])(Z80* ctx, signed char d) = {
        RLC_IX_with_LD_B, RLC_IX_with_LD_C, RLC_IX_with_LD_D, RLC_IX_with_LD_E, RLC_IX_with_LD_H, RLC_IX_with_LD_L, RLC_IX_, RLC_IX_with_LD_A,
        RRC_IX_with_LD_B, RRC_IX_with_LD_C, RRC_IX_with_LD_D, RRC_IX_with_LD_E, RRC_IX_with_LD_H, RRC_IX_with_LD_L, RRC_IX_, RRC_IX_with_LD_A,
        RL_IX_with_LD_B, RL_IX_with_LD_C, RL_IX_with_LD_D, RL_IX_with_LD_E, RL_IX_with_LD_H, RL_IX_with_LD_L, RL_IX_, RL_IX_with_LD_A,
        RR_IX_with_LD_B, RR_IX_with_LD_C, RR_IX_with_LD_D, RR_IX_with_LD_E, RR_IX_with_LD_H, RR_IX_with_LD_L, RR_IX_, RR_IX_with_LD_A,
        SLA_IX_with_LD_B, SLA_IX_with_LD_C, SLA_IX_with_LD_D, SLA_IX_with_LD_E, SLA_IX_with_LD_H, SLA_IX_with_LD_L, SLA_IX_, SLA_IX_with_LD_A,
        SRA_IX_with_LD_B, SRA_IX_with_LD_C, SRA_IX_with_LD_D, SRA_IX_with_LD_E, SRA_IX_with_LD_H, SRA_IX_with_LD_L, SRA_IX_, SRA_IX_with_LD_A,
        SLL_IX_with_LD_B, SLL_IX_with_LD_C, SLL_IX_with_LD_D, SLL_IX_with_LD_E, SLL_IX_with_LD_H, SLL_IX_with_LD_L, SLL_IX_, SLL_IX_with_LD_A,
        SRL_IX_with_LD_B, SRL_IX_with_LD_C, SRL_IX_with_LD_D, SRL_IX_with_LD_E, SRL_IX_with_LD_H, SRL_IX_with_LD_L, SRL_IX_, SRL_IX_with_LD_A,
        BIT_IX_0, BIT_IX_0, BIT_IX_0, BIT_IX_0, BIT_IX_0, BIT_IX_0, BIT_IX_0, BIT_IX_0,
        BIT_IX_1, BIT_IX_1, BIT_IX_1, BIT_IX_1, BIT_IX_1, BIT_IX_1, BIT_IX_1, BIT_IX_1,
        BIT_IX_2, BIT_IX_2, BIT_IX_2, BIT_IX_2, BIT_IX_2, BIT_IX_2, BIT_IX_2, BIT_IX_2,
        BIT_IX_3, BIT_IX_3, BIT_IX_3, BIT_IX_3, BIT_IX_3, BIT_IX_3, BIT_IX_3, BIT_IX_3,
        BIT_IX_4, BIT_IX_4, BIT_IX_4, BIT_IX_4, BIT_IX_4, BIT_IX_4, BIT_IX_4, BIT_IX_4,
        BIT_IX_5, BIT_IX_5, BIT_IX_5, BIT_IX_5, BIT_IX_5, BIT_IX_5, BIT_IX_5, BIT_IX_5,
        BIT_IX_6, BIT_IX_6, BIT_IX_6, BIT_IX_6, BIT_IX_6, BIT_IX_6, BIT_IX_6, BIT_IX_6,
        BIT_IX_7, BIT_IX_7, BIT_IX_7, BIT_IX_7, BIT_IX_7, BIT_IX_7, BIT_IX_7, BIT_IX_7,
        RES_IX_0_with_LD_B, RES_IX_0_with_LD_C, RES_IX_0_with_LD_D, RES_IX_0_with_LD_E, RES_IX_0_with_LD_H, RES_IX_0_with_LD_L, RES_IX_0, RES_IX_0_with_LD_A,
        RES_IX_1_with_LD_B, RES_IX_1_with_LD_C, RES_IX_1_with_LD_D, RES_IX_1_with_LD_E, RES_IX_1_with_LD_H, RES_IX_1_with_LD_L, RES_IX_1, RES_IX_1_with_LD_A,
        RES_IX_2_with_LD_B, RES_IX_2_with_LD_C, RES_IX_2_with_LD_D, RES_IX_2_with_LD_E, RES_IX_2_with_LD_H, RES_IX_2_with_LD_L, RES_IX_2, RES_IX_2_with_LD_A,
        RES_IX_3_with_LD_B, RES_IX_3_with_LD_C, RES_IX_3_with_LD_D, RES_IX_3_with_LD_E, RES_IX_3_with_LD_H, RES_IX_3_with_LD_L, RES_IX_3, RES_IX_3_with_LD_A,
        RES_IX_4_with_LD_B, RES_IX_4_with_LD_C, RES_IX_4_with_LD_D, RES_IX_4_with_LD_E, RES_IX_4_with_LD_H, RES_IX_4_with_LD_L, RES_IX_4, RES_IX_4_with_LD_A,
        RES_IX_5_with_LD_B, RES_IX_5_with_LD_C, RES_IX_5_with_LD_D, RES_IX_5_with_LD_E, RES_IX_5_with_LD_H, RES_IX_5_with_LD_L, RES_IX_5, RES_IX_5_with_LD_A,
        RES_IX_6_with_LD_B, RES_IX_6_with_LD_C, RES_IX_6_with_LD_D, RES_IX_6_with_LD_E, RES_IX_6_with_LD_H, RES_IX_6_with_LD_L, RES_IX_6, RES_IX_6_with_LD_A,
        RES_IX_7_with_LD_B, RES_IX_7_with_LD_C, RES_IX_7_with_LD_D, RES_IX_7_with_LD_E, RES_IX_7_with_LD_H, RES_IX_7_with_LD_L, RES_IX_7, RES_IX_7_with_LD_A,
        SET_IX_0_with_LD_B, SET_IX_0_with_LD_C, SET_IX_0_with_LD_D, SET_IX_0_with_LD_E, SET_IX_0_with_LD_H, SET_IX_0_with_LD_L, SET_IX_0, SET_IX_0_with_LD_A,
        SET_IX_1_with_LD_B, SET_IX_1_with_LD_C, SET_IX_1_with_LD_D, SET_IX_1_with_LD_E, SET_IX_1_with_LD_H, SET_IX_1_with_LD_L, SET_IX_1, SET_IX_1_with_LD_A,
        SET_IX_2_with_LD_B, SET_IX_2_with_LD_C, SET_IX_2_with_LD_D, SET_IX_2_with_LD_E, SET_IX_2_with_LD_H, SET_IX_2_with_LD_L, SET_IX_2, SET_IX_2_with_LD_A,
        SET_IX_3_with_LD_B, SET_IX_3_with_LD_C, SET_IX_3_with_LD_D, SET_IX_3_with_LD_E, SET_IX_3_with_LD_H, SET_IX_3_with_LD_L, SET_IX_3, SET_IX_3_with_LD_A,
        SET_IX_4_with_LD_B, SET_IX_4_with_LD_C, SET_IX_4_with_LD_D, SET_IX_4_with_LD_E, SET_IX_4_with_LD_H, SET_IX_4_with_LD_L, SET_IX_4, SET_IX_4_with_LD_A,
        SET_IX_5_with_LD_B, SET_IX_5_with_LD_C, SET_IX_5_with_LD_D, SET_IX_5_with_LD_E, SET_IX_5_with_LD_H, SET_IX_5_with_LD_L, SET_IX_5, SET_IX_5_with_LD_A,
        SET_IX_6_with_LD_B, SET_IX_6_with_LD_C, SET_IX_6_with_LD_D, SET_IX_6_with_LD_E, SET_IX_6_with_LD_H, SET_IX_6_with_LD_L, SET_IX_6, SET_IX_6_with_LD_A,
        SET_IX_7_with_LD_B, SET_IX_7_with_LD_C, SET_IX_7_with_LD_D, SET_IX_7_with_LD_E, SET_IX_7_with_LD_H, SET_IX_7_with_LD_L, SET_IX_7, SET_IX_7_with_LD_A};
    void (*opSetIY4[256])(Z80* ctx, signed char d) = {
        RLC_IY_with_LD_B, RLC_IY_with_LD_C, RLC_IY_with_LD_D, RLC_IY_with_LD_E, RLC_IY_with_LD_H, RLC_IY_with_LD_L, RLC_IY_, RLC_IY_with_LD_A,
        RRC_IY_with_LD_B, RRC_IY_with_LD_C, RRC_IY_with_LD_D, RRC_IY_with_LD_E, RRC_IY_with_LD_H, RRC_IY_with_LD_L, RRC_IY_, RRC_IY_with_LD_A,
        RL_IY_with_LD_B, RL_IY_with_LD_C, RL_IY_with_LD_D, RL_IY_with_LD_E, RL_IY_with_LD_H, RL_IY_with_LD_L, RL_IY_, RL_IY_with_LD_A,
        RR_IY_with_LD_B, RR_IY_with_LD_C, RR_IY_with_LD_D, RR_IY_with_LD_E, RR_IY_with_LD_H, RR_IY_with_LD_L, RR_IY_, RR_IY_with_LD_A,
        SLA_IY_with_LD_B, SLA_IY_with_LD_C, SLA_IY_with_LD_D, SLA_IY_with_LD_E, SLA_IY_with_LD_H, SLA_IY_with_LD_L, SLA_IY_, SLA_IY_with_LD_A,
        SRA_IY_with_LD_B, SRA_IY_with_LD_C, SRA_IY_with_LD_D, SRA_IY_with_LD_E, SRA_IY_with_LD_H, SRA_IY_with_LD_L, SRA_IY_, SRA_IY_with_LD_A,
        SLL_IY_with_LD_B, SLL_IY_with_LD_C, SLL_IY_with_LD_D, SLL_IY_with_LD_E, SLL_IY_with_LD_H, SLL_IY_with_LD_L, SLL_IY_, SLL_IY_with_LD_A,
        SRL_IY_with_LD_B, SRL_IY_with_LD_C, SRL_IY_with_LD_D, SRL_IY_with_LD_E, SRL_IY_with_LD_H, SRL_IY_with_LD_L, SRL_IY_, SRL_IY_with_LD_A,
        BIT_IY_0, BIT_IY_0, BIT_IY_0, BIT_IY_0, BIT_IY_0, BIT_IY_0, BIT_IY_0, BIT_IY_0,
        BIT_IY_1, BIT_IY_1, BIT_IY_1, BIT_IY_1, BIT_IY_1, BIT_IY_1, BIT_IY_1, BIT_IY_1,
        BIT_IY_2, BIT_IY_2, BIT_IY_2, BIT_IY_2, BIT_IY_2, BIT_IY_2, BIT_IY_2, BIT_IY_2,
        BIT_IY_3, BIT_IY_3, BIT_IY_3, BIT_IY_3, BIT_IY_3, BIT_IY_3, BIT_IY_3, BIT_IY_3,
        BIT_IY_4, BIT_IY_4, BIT_IY_4, BIT_IY_4, BIT_IY_4, BIT_IY_4, BIT_IY_4, BIT_IY_4,
        BIT_IY_5, BIT_IY_5, BIT_IY_5, BIT_IY_5, BIT_IY_5, BIT_IY_5, BIT_IY_5, BIT_IY_5,
        BIT_IY_6, BIT_IY_6, BIT_IY_6, BIT_IY_6, BIT_IY_6, BIT_IY_6, BIT_IY_6, BIT_IY_6,
        BIT_IY_7, BIT_IY_7, BIT_IY_7, BIT_IY_7, BIT_IY_7, BIT_IY_7, BIT_IY_7, BIT_IY_7,
        RES_IY_0_with_LD_B, RES_IY_0_with_LD_C, RES_IY_0_with_LD_D, RES_IY_0_with_LD_E, RES_IY_0_with_LD_H, RES_IY_0_with_LD_L, RES_IY_0, RES_IY_0_with_LD_A,
        RES_IY_1_with_LD_B, RES_IY_1_with_LD_C, RES_IY_1_with_LD_D, RES_IY_1_with_LD_E, RES_IY_1_with_LD_H, RES_IY_1_with_LD_L, RES_IY_1, RES_IY_1_with_LD_A,
        RES_IY_2_with_LD_B, RES_IY_2_with_LD_C, RES_IY_2_with_LD_D, RES_IY_2_with_LD_E, RES_IY_2_with_LD_H, RES_IY_2_with_LD_L, RES_IY_2, RES_IY_2_with_LD_A,
        RES_IY_3_with_LD_B, RES_IY_3_with_LD_C, RES_IY_3_with_LD_D, RES_IY_3_with_LD_E, RES_IY_3_with_LD_H, RES_IY_3_with_LD_L, RES_IY_3, RES_IY_3_with_LD_A,
        RES_IY_4_with_LD_B, RES_IY_4_with_LD_C, RES_IY_4_with_LD_D, RES_IY_4_with_LD_E, RES_IY_4_with_LD_H, RES_IY_4_with_LD_L, RES_IY_4, RES_IY_4_with_LD_A,
        RES_IY_5_with_LD_B, RES_IY_5_with_LD_C, RES_IY_5_with_LD_D, RES_IY_5_with_LD_E, RES_IY_5_with_LD_H, RES_IY_5_with_LD_L, RES_IY_5, RES_IY_5_with_LD_A,
        RES_IY_6_with_LD_B, RES_IY_6_with_LD_C, RES_IY_6_with_LD_D, RES_IY_6_with_LD_E, RES_IY_6_with_LD_H, RES_IY_6_with_LD_L, RES_IY_6, RES_IY_6_with_LD_A,
        RES_IY_7_with_LD_B, RES_IY_7_with_LD_C, RES_IY_7_with_LD_D, RES_IY_7_with_LD_E, RES_IY_7_with_LD_H, RES_IY_7_with_LD_L, RES_IY_7, RES_IY_7_with_LD_A,
        SET_IY_0_with_LD_B, SET_IY_0_with_LD_C, SET_IY_0_with_LD_D, SET_IY_0_with_LD_E, SET_IY_0_with_LD_H, SET_IY_0_with_LD_L, SET_IY_0, SET_IY_0_with_LD_A,
        SET_IY_1_with_LD_B, SET_IY_1_with_LD_C, SET_IY_1_with_LD_D, SET_IY_1_with_LD_E, SET_IY_1_with_LD_H, SET_IY_1_with_LD_L, SET_IY_1, SET_IY_1_with_LD_A,
        SET_IY_2_with_LD_B, SET_IY_2_with_LD_C, SET_IY_2_with_LD_D, SET_IY_2_with_LD_E, SET_IY_2_with_LD_H, SET_IY_2_with_LD_L, SET_IY_2, SET_IY_2_with_LD_A,
        SET_IY_3_with_LD_B, SET_IY_3_with_LD_C, SET_IY_3_with_LD_D, SET_IY_3_with_LD_E, SET_IY_3_with_LD_H, SET_IY_3_with_LD_L, SET_IY_3, SET_IY_3_with_LD_A,
        SET_IY_4_with_LD_B, SET_IY_4_with_LD_C, SET_IY_4_with_LD_D, SET_IY_4_with_LD_E, SET_IY_4_with_LD_H, SET_IY_4_with_LD_L, SET_IY_4, SET_IY_4_with_LD_A,
        SET_IY_5_with_LD_B, SET_IY_5_with_LD_C, SET_IY_5_with_LD_D, SET_IY_5_with_LD_E, SET_IY_5_with_LD_H, SET_IY_5_with_LD_L, SET_IY_5, SET_IY_5_with_LD_A,
        SET_IY_6_with_LD_B, SET_IY_6_with_LD_C, SET_IY_6_with_LD_D, SET_IY_6_with_LD_E, SET_IY_6_with_LD_H, SET_IY_6_with_LD_L, SET_IY_6, SET_IY_6_with_LD_A,
        SET_IY_7_with_LD_B, SET_IY_7_with_LD_C, SET_IY_7_with_LD_D, SET_IY_7_with_LD_E, SET_IY_7_with_LD_H, SET_IY_7_with_LD_L, SET_IY_7, SET_IY_7_with_LD_A};

    inline void checkInterrupt()
    {
        // Interrupt processing is not executed by the instruction immediately after executing EI.
        if (reg.execEI) {
            return;
        }
        // check interrupt flag
        if (reg.interrupt & 0b10000000) {
            // execute NMI
            if (reg.IFF & IFF_NMI()) {
                return;
            }
            reg.interrupt &= 0b01111111;
            reg.IFF &= ~IFF_HALT();
#ifndef Z80_DISABLE_DEBUG
            if (isDebug()) log("EXECUTE NMI: $%04X", reg.interruptAddrN);
#endif
            reg.R = ((reg.R + 1) & 0x7F) | (reg.R & 0x80);
            reg.IFF |= IFF_NMI();
            reg.IFF &= ~IFF1();
            push(getPCH(), 4);
            push(getPCL(), 4);
            reg.PC = reg.interruptAddrN;
            consumeClock(11);
#ifndef Z80_DISABLE_NESTCHECK
            invokeCallHandlers();
#endif
        } else if (reg.interrupt & 0b01000000) {
            // execute IRQ
            if (!(reg.IFF & IFF1())) {
                return;
            }
            reg.interrupt &= 0b10111111;
            reg.IFF &= ~IFF_HALT();
            reg.IFF |= IFF_IRQ();
            reg.IFF &= ~(IFF1() | IFF2());
            reg.R = ((reg.R + 1) & 0x7F) | (reg.R & 0x80);
            switch (reg.interrupt & 0b00000011) {
                case 0: // mode 0
#ifndef Z80_DISABLE_DEBUG
                    if (isDebug()) log("EXECUTE INT MODE1 (RST TO $%04X)", reg.interruptVector * 8);
#endif
                    if (reg.interruptVector == 0xCD) {
                        consumeClock(7);
                    }
                    RST(reg.interruptVector, false);
                    break;
                case 1: // mode 1 (13Hz)
#ifndef Z80_DISABLE_DEBUG
                    if (isDebug()) log("EXECUTE INT MODE1 (RST TO $0038)");
#endif
                    consumeClock(1);
                    RST(7, false);
                    break;
                case 2: { // mode 2
                    writeByte(reg.SP - 1, getPCH());
                    writeByte(reg.SP - 2, getPCL());
                    reg.SP -= 2;
                    unsigned short addr = make16BitsFromLE(reg.interruptVector, reg.I);
                    unsigned short pc = make16BitsFromLE(readByte(addr), readByte(addr + 1));
#ifndef Z80_DISABLE_DEBUG
                    if (isDebug()) log("EXECUTE INT MODE2: ($%04X) = $%04X", addr, pc);
#endif
                    reg.PC = pc;
                    consumeClock(3);
#ifndef Z80_DISABLE_NESTCHECK
                    invokeCallHandlers();
#endif
                    break;
                }
            }
        }
    }

    inline void updateRefreshRegister()
    {
        reg.R = ((reg.R + 1) & 0x7F) | (reg.R & 0x80);
        consumeClock(2);
    }

  public: // API functions
#ifdef Z80_NO_FUNCTIONAL
    Z80(unsigned char (*read)(void* arg, unsigned short addr),
        void (*write)(void* arg, unsigned short addr, unsigned char value),
        unsigned char (*in)(void* arg, unsigned short port),
        void (*out)(void* arg, unsigned short port, unsigned char value),
        void* arg,
        bool returnPortAs16Bits = false)
#else
    Z80(std::function<unsigned char(void*, unsigned short)> read,
        std::function<void(void*, unsigned short, unsigned char)> write,
        std::function<unsigned char(void*, unsigned short)> in,
        std::function<void(void*, unsigned short, unsigned char)> out,
        void* arg,
        bool returnPortAs16Bits = false)
#endif
    {
        this->CB.arg = arg;
        initialize();
        setupCallback(read, write, in, out, returnPortAs16Bits);
    }

    // without setup callbacks
    Z80(void* arg)
    {
        this->CB.arg = arg;
        initialize();
    }

    Z80()
    {
        initialize();
    }

#ifndef Z80_NO_FUNCTIONAL
    void setupCallback(std::function<unsigned char(void*, unsigned short)> read,
                       std::function<void(void*, unsigned short, unsigned char)> write,
                       std::function<unsigned char(void*, unsigned short)> in,
                       std::function<void(void*, unsigned short, unsigned char)> out,
                       void* arg,
                       bool returnPortAs16Bits = false)
    {
        CB.arg = arg;
        setupCallback(read, write, in, out, returnPortAs16Bits);
    }

    void setupCallback(std::function<unsigned char(void*, unsigned short)> read,
                       std::function<void(void*, unsigned short, unsigned char)> write,
                       std::function<unsigned char(void*, unsigned short)> in,
                       std::function<void(void*, unsigned short, unsigned char)> out,
                       bool returnPortAs16Bits = false)
    {
        setupMemoryCallback(read, write);
        setupDeviceCallback(in, out, returnPortAs16Bits);
    }

    void setupMemoryCallback(std::function<unsigned char(void*, unsigned short)> read,
                             std::function<void(void*, unsigned short, unsigned char)> write)
    {
        CB.read = read;
        CB.write = write;
    }

    void setupDeviceCallback(std::function<unsigned char(void*, unsigned short)> in,
                             std::function<void(void*, unsigned short, unsigned char)> out,
                             bool returnPortAs16Bits)
    {
        CB.in = in;
        CB.out = out;
#ifndef Z80_UNSUPPORT_16BIT_PORT
        CB.returnPortAs16Bits = returnPortAs16Bits;
#endif
    }

#else
    void setupCallback(unsigned char (*read)(void* arg, unsigned short addr),
                       void (*write)(void* arg, unsigned short addr, unsigned char value),
                       unsigned char (*in)(void* arg, unsigned short port),
                       void (*out)(void* arg, unsigned short port, unsigned char value),
                       void* arg,
                       bool returnPortAs16Bits = false)
    {
        CB.arg = arg;
        setupCallback(read, write, in, out, returnPortAs16Bits);
    }

    void setupCallback(unsigned char (*read)(void* arg, unsigned short addr),
                       void (*write)(void* arg, unsigned short addr, unsigned char value),
                       unsigned char (*in)(void* arg, unsigned short port),
                       void (*out)(void* arg, unsigned short port, unsigned char value),
                       bool returnPortAs16Bits = false)
    {
        setupMemoryCallback(read, write);
        setupDeviceCallback(in, out, returnPortAs16Bits);
    }

    void setupMemoryCallback(unsigned char (*read)(void* arg, unsigned short addr),
                             void (*write)(void* arg, unsigned short addr, unsigned char value))
    {
        CB.read = read;
        CB.write = write;
    }

    void setupDeviceCallback(unsigned char (*in)(void* arg, unsigned short addr),
                             void (*out)(void* arg, unsigned short addr, unsigned char value),
                             bool returnPortAs16Bits)
    {
        CB.in = in;
        CB.out = out;
#ifndef Z80_UNSUPPORT_16BIT_PORT
        CB.returnPortAs16Bits = returnPortAs16Bits;
#endif
    }
#endif

    void initialize()
    {
        resetConsumeClockCallback();
#ifndef Z80_DISABLE_DEBUG
        resetDebugMessage();
#endif
        ::memset(&reg, 0, sizeof(reg));
        reg.pair.A = 0xff;
        reg.pair.F = 0xff;
        reg.SP = 0xffff;
        memset(&wtc, 0, sizeof(wtc));
    }

    ~Z80()
    {
#ifndef Z80_DISABLE_BREAKPOINT
        removeAllBreakOperands();
        removeAllBreakPoints();
#endif
#ifndef Z80_DISABLE_NESTCHECK
        removeAllCallHandlers();
        removeAllReturnHandlers();
#endif
    }

#ifndef Z80_DISABLE_DEBUG
#ifdef Z80_NO_FUNCTIONAL
    void setDebugMessage(void (*debugMessage)(void* arg, const char* msg))
#else
    void setDebugMessage(std::function<void(void*, const char*)> debugMessage)
#endif
    {
        CB.debugMessageEnabled = true;
        CB.debugMessage = debugMessage;
    }

    void resetDebugMessage()
    {
        CB.debugMessageEnabled = false;
#ifdef Z80_NO_FUNCTIONAL
        CB.debugMessage = nullptr;
#endif
    }

    inline bool isDebug()
    {
        return CB.debugMessageEnabled;
    }
#endif

    inline unsigned short make16BitsFromLE(unsigned char low, unsigned char high)
    {
        unsigned short n = high;
        n <<= 8;
        n |= low;
        return n;
    }

    inline void splitTo8BitsPair(unsigned short value, unsigned char* high, unsigned char* low)
    {
        *high = (value & 0xFF00) >> 8;
        *low = value & 0xFF;
    }

#ifndef Z80_DISABLE_BREAKPOINT
#ifdef Z80_NO_FUNCTIONAL
    void addBreakPoint(unsigned short addr, void (*callback)(void*))
#else
    void addBreakPoint(unsigned short addr, std::function<void(void*)> callback)
#endif
    {
        auto it = CB.breakPoints.find(addr);
        if (it == CB.breakPoints.end()) {
            CB.breakPoints[addr] = new std::vector<BreakPoint*>();
        }
        CB.breakPoints[addr]->push_back(new BreakPoint(addr, callback));
    }

    void removeBreakPoint(unsigned short addr)
    {
        auto it = CB.breakPoints.find(addr);
        if (it == CB.breakPoints.end()) return;
        for (auto bp : *CB.breakPoints[addr]) delete bp;
        delete CB.breakPoints[addr];
        CB.breakPoints.erase(it);
    }

    void removeAllBreakPoints()
    {
        std::vector<int> keys;
        for (auto it = CB.breakPoints.begin(); it != CB.breakPoints.end(); it++) {
            keys.push_back(it->first);
        }
        for (auto key : keys) {
            removeBreakPoint(key);
        }
    }

#ifdef Z80_NO_FUNCTIONAL
    void addBreakOperand(int operandNumber, void (*callback)(void*, unsigned char*, int))
#else
    void addBreakOperand(int operandNumber, std::function<void(void*, unsigned char*, int)> callback)
#endif
    {
        addBreakOperand(0, operandNumber, callback);
    }

#ifdef Z80_NO_FUNCTIONAL
    void addBreakOperand(int prefixNumber, int operandNumber, void (*callback)(void*, unsigned char*, int))
#else
    void addBreakOperand(int prefixNumber, int operandNumber, std::function<void(void*, unsigned char*, int)> callback)
#endif
    {
        auto op = (prefixNumber << 8) | operandNumber;
        auto it = CB.breakOperands.find(op);
        if (it == CB.breakOperands.end()) {
            CB.breakOperands[op] = new std::vector<BreakOperand*>();
        }
        CB.breakOperands[op]->push_back(new BreakOperand(prefixNumber, operandNumber, callback));
    }

#ifdef Z80_NO_FUNCTIONAL
    void addBreakOperand(unsigned char prefixNumber1, unsigned char prefixNumber2, unsigned char operandNumber, void (*callback)(void*, unsigned char*, int))
#else
    void addBreakOperand(unsigned char prefixNumber1, unsigned char prefixNumber2, unsigned char operandNumber, std::function<void(void*, unsigned char*, int)> callback)
#endif
    {
        int n = make16BitsFromLE(prefixNumber2, prefixNumber1);
        int prefixNumber = n;
        n <<= 8;
        n |= operandNumber;
        addBreakOperand(prefixNumber, n, callback);
    }

    void removeBreakOperand(int operandNumber)
    {
        auto it = CB.breakOperands.find(operandNumber);
        if (it == CB.breakOperands.end()) return;
        for (auto bo : *CB.breakOperands[operandNumber]) delete bo;
        delete CB.breakOperands[operandNumber];
        CB.breakOperands.erase(it);
    }

    void removeBreakOperand(unsigned char prefixNumber, unsigned char operandNumber)
    {
        removeBreakOperand(make16BitsFromLE(operandNumber, prefixNumber));
    }

    void removeBreakOperand(unsigned char prefixNumber1, unsigned char prefixNumber2, unsigned char operandNumber)
    {
        int n = make16BitsFromLE(prefixNumber2, prefixNumber1);
        n <<= 8;
        n |= operandNumber;
        removeBreakOperand(n);
    }

    void removeAllBreakOperands()
    {
        std::vector<int> keys;
        for (auto it = CB.breakOperands.begin(); it != CB.breakOperands.end(); it++) {
            keys.push_back(it->first);
        }
        for (auto key : keys) {
            removeBreakOperand(key);
        }
    }
#endif

#ifndef Z80_DISABLE_NESTCHECK
#ifdef Z80_NO_FUNCTIONAL
    void addReturnHandler(void (*callback)(void*))
#else
    void addReturnHandler(std::function<void(void*)> callback)
#endif
    {
        CB.returnHandlers.push_back(new SimpleHandler(callback));
    }

    void removeAllReturnHandlers()
    {
        for (auto handler : CB.returnHandlers) delete handler;
        CB.returnHandlers.clear();
    }

#ifdef Z80_NO_FUNCTIONAL
    void addCallHandler(void (*callback)(void*))
#else
    void addCallHandler(std::function<void(void*)> callback)
#endif
    {
        CB.callHandlers.push_back(new SimpleHandler(callback));
    }

    void removeAllCallHandlers()
    {
        for (auto handler : CB.callHandlers) delete handler;
        CB.callHandlers.clear();
    }
#endif

#ifdef Z80_NO_FUNCTIONAL
    void setConsumeClockCallback(void (*consumeClock_)(void* arg, int clocks))
#else
    void setConsumeClockCallback(std::function<void(void* arg, int clocks)> consumeClock_)
#endif
    {
        CB.consumeClockEnabled = true;
        CB.consumeClock = consumeClock_;
    }

    void resetConsumeClockCallback()
    {
        CB.consumeClockEnabled = false;
#ifdef Z80_NO_FUNCTIONAL
        CB.consumeClock = nullptr;
#endif
    }

    void requestBreak()
    {
        requestBreakFlag = true;
    }

    void generateIRQ(unsigned char vector)
    {
        reg.interrupt |= 0b01000000;
        reg.interruptVector = vector;
    }

    void cancelIRQ()
    {
        reg.interrupt &= 0b10111111;
    }

    void generateNMI(unsigned short addr)
    {
        reg.interrupt |= 0b10000000;
        reg.interruptAddrN = addr;
    }

    inline unsigned char fetch(int clocks)
    {
        unsigned char result = readByte(reg.PC, clocks);
        reg.PC++;
        return result;
    }

    inline int execute(int clock)
    {
        int executed = 0;
        requestBreakFlag = false;
        reg.consumeClockCounter = 0;
        while (0 < clock && !requestBreakFlag) {
            // execute NOP while halt
            if (reg.IFF & IFF_HALT()) {
                reg.execEI = 0;
                readByte(reg.PC); // NOTE: read and discard (to be consumed 4Hz)
            } else {
                if (wtc.fetch) consumeClock(wtc.fetch);
#ifndef Z80_DISABLE_BREAKPOINT
                checkBreakPoint();
#endif
                reg.execEI = 0;
                int operandNumber = fetch(2);
                updateRefreshRegister();
#ifndef Z80_DISABLE_BREAKPOINT
                checkBreakOperand(operandNumber);
#endif
                opSet1[operandNumber](this);
            }
            executed += reg.consumeClockCounter;
            clock -= reg.consumeClockCounter;
#ifdef Z80_CALLBACK_PER_INSTRUCTION
            checkInterrupt();
#ifdef Z80_CALLBACK_WITHOUT_CHECK
            CB.consumeClock(CB.arg, reg.consumeClockCounter);
#else
            if (CB.consumeClockEnabled) CB.consumeClock(CB.arg, reg.consumeClockCounter);
#endif
            reg.consumeClockCounter = 0;
#else
            reg.consumeClockCounter = 0;
            checkInterrupt();
#endif
        }
        return executed;
    }

    inline void execute()
    {
        requestBreakFlag = false;
        while (!requestBreakFlag) {
#ifdef Z80_CALLBACK_PER_INSTRUCTION
            reg.consumeClockCounter = 0;
#endif
            // execute NOP while halt
            if (reg.IFF & IFF_HALT()) {
                reg.execEI = 0;
                readByte(reg.PC); // NOTE: read and discard (to be consumed 4Hz)
            } else {
#ifndef Z80_DISABLE_BREAKPOINT
                checkBreakPoint();
#endif
                reg.execEI = 0;
                int operandNumber = fetch(2 + wtc.fetch);
                updateRefreshRegister();
#ifndef Z80_DISABLE_BREAKPOINT
                checkBreakOperand(operandNumber);
#endif
                opSet1[operandNumber](this);
            }
            checkInterrupt();
#ifdef Z80_CALLBACK_PER_INSTRUCTION
#ifdef Z80_CALLBACK_WITHOUT_CHECK
            CB.consumeClock(CB.arg, reg.consumeClockCounter);
#else
            if (CB.consumeClockEnabled) CB.consumeClock(CB.arg, reg.consumeClockCounter);
#endif
#endif
        }
    }

    int executeTick4MHz()
    {
        return execute(4194304 / 60);
    }

    int executeTick8MHz()
    {
        return execute(8388608 / 60);
    }

#ifndef Z80_DISABLE_DEBUG
    void registerDump()
    {
        if (isDebug()) log("===== REGISTER DUMP : START =====");
        if (isDebug()) log("PAIR: %s %s %s %s %s %s %s", registerDump(0b111), registerDump(0b000), registerDump(0b001), registerDump(0b010), registerDump(0b011), registerDump(0b100), registerDump(0b101));
        if (isDebug()) log("PAIR: F<$%02X> ... S:%s, Z:%s, H:%s, P/V:%s, N:%s, C:%s",
                           reg.pair.F,
                           isFlagS() ? "ON" : "OFF",
                           isFlagZ() ? "ON" : "OFF",
                           isFlagH() ? "ON" : "OFF",
                           isFlagPV() ? "ON" : "OFF",
                           isFlagN() ? "ON" : "OFF",
                           isFlagC() ? "ON" : "OFF");
        if (isDebug()) log("BACK: %s %s %s %s %s %s %s F'<$%02X>", registerDump2(0b111), registerDump2(0b000), registerDump2(0b001), registerDump2(0b010), registerDump2(0b011), registerDump2(0b100), registerDump2(0b101), reg.back.F);
        if (isDebug()) log("PC<$%04X> SP<$%04X> IX<$%04X> IY<$%04X>", reg.PC, reg.SP, reg.IX, reg.IY);
        if (isDebug()) log("R<$%02X> I<$%02X> IFF<$%02X>", reg.R, reg.I, reg.IFF);
        if (isDebug()) log("isHalt: %s, interrupt: $%02X", reg.IFF & IFF_HALT() ? "YES" : "NO", reg.interrupt);
        if (isDebug()) log("===== REGISTER DUMP : END =====");
    }
#endif
};

#endif // INCLUDE_Z80_HPP






0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?