/*  Copyright 2003-2005 Guillaume Duhamel
    Copyright 2004-2007, 2013 Theo Berkau
    Copyright 2005 Fabien Coulon

    This file is part of Yabause.

    Yabause is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    Yabause is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Yabause; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
        Copyright 2019 devMiyax(smiyaxdev@gmail.com)

This file is part of YabaSanshiro.

        YabaSanshiro is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

YabaSanshiro is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

        You should have received a copy of the GNU General Public License
along with YabaSanshiro; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
*/

/*! \file sh2int.c
    \brief SH2 interpreter interface
*/

#include "sh2int.h"
#include "bios.h"
#include "cs0.h"
#include "debug.h"
#include "error.h"
#include "memory.h"
#include "sh2core.h"
#include "sh2idle.h"
#include "yabause.h"


#define EXEC_FROM_CACHE

#if 0 // def SH2_TRACE
#include "sh2trace.h"
#define MappedMemoryWriteByte(a, v)                                            \
  do {                                                                         \
    uint32_t __a = (a), __v = (v);                                             \
    sh2_trace_writeb(__a, __v);                                                \
    MappedMemoryWriteByte(__a, __v);                                           \
  } while (0)
#define MappedMemoryWriteWord(a, v)                                            \
  do {                                                                         \
    uint32_t __a = (a), __v = (v);                                             \
    sh2_trace_writew(__a, __v);                                                \
    MappedMemoryWriteWord(__a, __v);                                           \
  } while (0)
#define MappedMemoryWriteLong(a, v)                                            \
  do {                                                                         \
    uint32_t __a = (a), __v = (v);                                             \
    sh2_trace_writel(__a, __v);                                                \
    MappedMemoryWriteLong(__a, __v);                                           \
  } while (0)

void SetInsTracingToggle(int toggle)
{
	SH2SetInsTracing(toggle ? 1 : 0);
}
#else
void SetInsTracingToggle(int toggle) {}
#endif

void SH2IOnFrame(SH2_struct *context) {}

opcodefunc opcodes[0x10000];

void SH2InterpreterAddCycle(SH2_struct *context, u32 value);

SH2Interface_struct SH2Interpreter = {SH2CORE_INTERPRETER,
                                      "SH2 Interpreter",

                                      SH2InterpreterInit,
                                      SH2InterpreterDeInit,
                                      SH2InterpreterReset,
                                      SH2InterpreterExec,

                                      SH2InterpreterGetRegisters,
                                      SH2InterpreterGetGPR,
                                      SH2InterpreterGetSR,
                                      SH2InterpreterGetGBR,
                                      SH2InterpreterGetVBR,
                                      SH2InterpreterGetMACH,
                                      SH2InterpreterGetMACL,
                                      SH2InterpreterGetPR,
                                      SH2InterpreterGetPC,

                                      SH2InterpreterSetRegisters,
                                      SH2InterpreterSetGPR,
                                      SH2InterpreterSetSR,
                                      SH2InterpreterSetGBR,
                                      SH2InterpreterSetVBR,
                                      SH2InterpreterSetMACH,
                                      SH2InterpreterSetMACL,
                                      SH2InterpreterSetPR,
                                      SH2InterpreterSetPC,
                                      SH2IOnFrame,

                                      SH2InterpreterSendInterrupt,
                                      SH2InterpreterRemoveInterrupt,
                                      SH2InterpreterGetInterrupts,
                                      SH2InterpreterSetInterrupts,

                                      NULL, // SH2WriteNotify not used

                                      SH2InterpreterAddCycle};

SH2Interface_struct SH2DebugInterpreter = {SH2CORE_DEBUGINTERPRETER,
                                           "SH2 Debugger Interpreter",

                                           SH2DebugInterpreterInit,
                                           SH2InterpreterDeInit,
                                           SH2InterpreterReset,
                                           SH2DebugInterpreterExec,

                                           SH2InterpreterGetRegisters,
                                           SH2InterpreterGetGPR,
                                           SH2InterpreterGetSR,
                                           SH2InterpreterGetGBR,
                                           SH2InterpreterGetVBR,
                                           SH2InterpreterGetMACH,
                                           SH2InterpreterGetMACL,
                                           SH2InterpreterGetPR,
                                           SH2InterpreterGetPC,

                                           SH2InterpreterSetRegisters,
                                           SH2InterpreterSetGPR,
                                           SH2InterpreterSetSR,
                                           SH2InterpreterSetGBR,
                                           SH2InterpreterSetVBR,
                                           SH2InterpreterSetMACH,
                                           SH2InterpreterSetMACL,
                                           SH2InterpreterSetPR,
                                           SH2InterpreterSetPC,
                                           SH2IOnFrame,

                                           SH2InterpreterSendInterrupt,
                                           SH2InterpreterRemoveInterrupt,
                                           SH2InterpreterGetInterrupts,
                                           SH2InterpreterSetInterrupts,

                                           NULL, // SH2WriteNotify not used

                                           SH2InterpreterAddCycle};

fetchfunc fetchlist[0x100];

//////////////////////////////////////////////////////////////////////////////

void SH2HandleInterrupts(SH2_struct *context) {
  if (context->NumberOfInterrupts != 0) {
    if (context->interrupts[context->NumberOfInterrupts - 1].level >
        context->regs.SR.part.I) {
      u32 oldpc = context->regs.PC;
      u32 persr = context->regs.SR.part.I;
      context->regs.R[15] -= 4;
      MappedMemoryWriteLong(context->regs.R[15], context->regs.SR.all, NULL);
      context->regs.R[15] -= 4;
      MappedMemoryWriteLong(context->regs.R[15], context->regs.PC, NULL);
      u32 level = context->interrupts[context->NumberOfInterrupts - 1].level;
      if (level == 0x10) {
        context->regs.SR.part.I = 0xF;
      } else {
        context->regs.SR.part.I = level;
      }
      context->regs.PC = MappedMemoryReadLong(
          context->regs.VBR +
              (context->interrupts[context->NumberOfInterrupts - 1].vector
               << 2),
          NULL);

      int Vector = context->interrupts[context->NumberOfInterrupts - 1].vector;
      // LOG("[%s] Exception vecnum=(%x), PC=%08X to %08X, level=%08X",
      // (context->isslave) ? "S" : "M", Vector, oldpc, context->regs.PC,
      // level);

      LOG("[%s] %d Exception vecnum=%x, saved PC=0x%08x --- New PC=0x%08x\n",
          context->isslave ? "SH2-S" : "SH2-M", CurrentSH2->cycles,
          context->interrupts[context->NumberOfInterrupts - 1].vector, oldpc,
          context->regs.PC);

      context->NumberOfInterrupts--;
      context->isIdle = 0;
      context->isSleeping = 0;
    }
  }
}

//////////////////////////////////////////////////////////////////////////////

static u32 FASTCALL FetchBios(u32 addr) {
  if (yabsys.extend_backup) {
    const u32 bupaddr = 0x0007d600;
    if (addr == bupaddr) {
      return 0;
    } else if (yabsys.extend_backup == 2 && addr >= BUP_TABLE_REAL_BASE &&
               addr <= BUP_TABLE_REAL_LAST) {
      // Virtual BUP table entries (real-BIOS mode): force an invalid
      // opcode so SH2undecoded dispatches to the HLE BUP functions.
      // NOTE: this must never cover real executable BIOS code; the old
      // 0x380..0x3A8 range overlapped the BIOS SYS_CHGSCUIM internals
      // and hung any game combining BUP_Init with SYS_CHGSCUIM.
      return 0;
    }
  }

  // #if CACHE_ENABLE
  //    u32 cycle = 0;
  //    return cache_memory_read_w(&CurrentSH2->onchip.cache, addr,&cycle);
  // #else
  return T2ReadWord(BiosRom, addr & 0x7FFFF);
  // #endif
}

//////////////////////////////////////////////////////////////////////////////

static u32 FASTCALL FetchCs0(u32 addr) {
  // #if CACHE_ENABLE
  //   u32 cycle=0;
  //   return cache_memory_read_w(&CurrentSH2->onchip.cache, addr,&cycle);
  // #else
  return CartridgeArea->Cs0ReadWord(addr);
  // #endif
}

//////////////////////////////////////////////////////////////////////////////

static u32 FASTCALL FetchLWram(u32 addr) {
  // #if CACHE_ENABLE
  //    u32 cycle=0;
  //	return cache_memory_read_w(&CurrentSH2->onchip.cache, addr,&cycle);
  // #else
  return T2ReadWord(LowWram, addr & 0xFFFFF);
  // #endif
}

//////////////////////////////////////////////////////////////////////////////

static u32 FASTCALL FetchHWram(u32 addr) {
  // #if CACHE_ENABLE
  //    u32 cycle=0;
  //	return cache_memory_read_w(&CurrentSH2->onchip.cache, addr, &cycle);
  // #else
  return T2ReadWord(HighWram, addr & 0xFFFFF);
  // #endif
}

extern u8 *Vdp1Ram;
static u32 FASTCALL FetchVram(u32 addr) {
  addr &= 0x07FFFF;
  return T1ReadWord(Vdp1Ram, addr);
}

//////////////////////////////////////////////////////////////////////////////

static u32 FASTCALL FetchInvalid(UNUSED u32 addr) {
#ifdef DMPHISTORY
  SH2DumpHistory(CurrentSH2);
#endif
  return 0xFFFF;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2delay(SH2_struct *sh, u32 addr) {
#if 0 // def SH2_TRACE
   sh2_trace(sh, addr);
#endif

  // Fetch Instruction
#ifdef EXEC_FROM_CACHE
  if ((addr & 0xC0000000) == 0xC0000000)
    sh->instruction = DataArrayReadWord(addr);
  else
#endif
    sh->instruction = MappedMemoryReadInst(
        addr, NULL); // fetchlist[(addr >> 20) & 0x0FF](addr);

#ifdef DMPHISTORY
  sh->pchistory_index++;
  sh->pchistory[sh->pchistory_index & (MAX_DMPHISTORY - 1)] = addr;
  sh->regshistory[sh->pchistory_index & (MAX_DMPHISTORY - 1)] = sh->regs;
#endif

  // Execute it
  sh->regs.PC -= 2;
  opcodes[sh->instruction](sh);
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2undecoded(SH2_struct *sh) {
  int vectnum;

  if (yabsys.extend_backup) {
    const u32 bupaddr = 0x0007d600; // MappedMemoryReadLong(0x06000358);
    if (sh->regs.PC == bupaddr) {
      LOG("BUP_Init");
      BiosBUPInit(sh);
      yabsys.extend_backup = 2;
      return;
    } else if (yabsys.extend_backup == 2 && sh->regs.PC >= BUP_TABLE_REAL_BASE &&
               sh->regs.PC <= BUP_TABLE_REAL_LAST) {
      BiosHandleFunc(sh);
      return;
    }
  }

  if (yabsys.emulatebios) {
    if (BiosHandleFunc(sh))
      return;
  }

  YabSetError(YAB_ERR_SH2INVALIDOPCODE, sh);

  // Save regs.SR on stack
  sh->regs.R[15] -= 4;
  MappedMemoryWriteLong(sh->regs.R[15], sh->regs.SR.all, NULL);

  // Save regs.PC on stack
  sh->regs.R[15] -= 4;
  MappedMemoryWriteLong(sh->regs.R[15], sh->regs.PC + 2, NULL);

  // What caused the exception? The delay slot or a general instruction?
  // 4 for General Instructions, 6 for delay slot
  vectnum = 4; //  Fix me

  // Jump to Exception service routine
  sh->regs.PC = MappedMemoryReadLong(sh->regs.VBR + (vectnum << 2), NULL);
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2add(SH2_struct *sh) {
  sh->regs.R[INSTRUCTION_B(sh->instruction)] +=
      sh->regs.R[INSTRUCTION_C(sh->instruction)];
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2addi(SH2_struct *sh) {
  s32 cd = (s32)(s8)INSTRUCTION_CD(sh->instruction);
  s32 b = INSTRUCTION_B(sh->instruction);

  sh->regs.R[b] += cd;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2addc(SH2_struct *sh) {
  u32 tmp0, tmp1;
  s32 source = INSTRUCTION_C(sh->instruction);
  s32 dest = INSTRUCTION_B(sh->instruction);

  tmp1 = sh->regs.R[source] + sh->regs.R[dest];
  tmp0 = sh->regs.R[dest];

  sh->regs.R[dest] = tmp1 + sh->regs.SR.part.T;
  if (tmp0 > tmp1)
    sh->regs.SR.part.T = 1;
  else
    sh->regs.SR.part.T = 0;
  if (tmp1 > sh->regs.R[dest])
    sh->regs.SR.part.T = 1;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2addv(SH2_struct *sh) {
  s32 dest, src, ans;
  s32 n = INSTRUCTION_B(sh->instruction);
  s32 m = INSTRUCTION_C(sh->instruction);

  if ((s32)sh->regs.R[n] >= 0)
    dest = 0;
  else
    dest = 1;

  if ((s32)sh->regs.R[m] >= 0)
    src = 0;
  else
    src = 1;

  src += dest;
  sh->regs.R[n] += sh->regs.R[m];

  if ((s32)sh->regs.R[n] >= 0)
    ans = 0;
  else
    ans = 1;

  ans += dest;

  if (src == 0 || src == 2)
    if (ans == 1)
      sh->regs.SR.part.T = 1;
    else
      sh->regs.SR.part.T = 0;
  else
    sh->regs.SR.part.T = 0;

  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2y_and(SH2_struct *sh) {
  sh->regs.R[INSTRUCTION_B(sh->instruction)] &=
      sh->regs.R[INSTRUCTION_C(sh->instruction)];
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2andi(SH2_struct *sh) {
  sh->regs.R[0] &= INSTRUCTION_CD(sh->instruction);
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2andm(SH2_struct *sh) {
  s32 temp;
  s32 source = INSTRUCTION_CD(sh->instruction);
  u32 rcycle = 0;
  u32 wcycle = 0;

  temp = (s32)MappedMemoryReadByte(sh->regs.GBR + sh->regs.R[0], &rcycle);
  temp &= source;
  MappedMemoryWriteByte((sh->regs.GBR + sh->regs.R[0]), temp, &wcycle);
  sh->regs.PC += 2;
  sh->cycles += 3 + rcycle + wcycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2bf(SH2_struct *sh) {
  if (sh->regs.SR.part.T == 0) {
    s32 disp = (s32)(s8)sh->instruction;

    sh->regs.PC = sh->regs.PC + (disp << 1) + 4;
    sh->cycles += 3;
  } else {
    sh->regs.PC += 2;
    sh->cycles++;
  }
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2bfs(SH2_struct *sh) {
  if (sh->regs.SR.part.T == 0) {
    s32 disp = (s32)(s8)sh->instruction;
    u32 temp = sh->regs.PC;

    sh->regs.PC = sh->regs.PC + (disp << 1) + 4;

    sh->cycles += 2;
    SH2delay(sh, temp + 2);
  } else {
    sh->regs.PC += 2;
    sh->cycles++;
  }
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2bra(SH2_struct *sh) {
  s32 disp = INSTRUCTION_BCD(sh->instruction);
  u32 temp = sh->regs.PC;

  if ((disp & 0x800) != 0)
    disp |= 0xFFFFF000;

  sh->regs.PC = sh->regs.PC + (disp << 1) + 4;

  sh->cycles += 2;
  SH2delay(sh, temp + 2);
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2braf(SH2_struct *sh) {
  u32 temp;
  s32 m = INSTRUCTION_B(sh->instruction);

  temp = sh->regs.PC;
  sh->regs.PC += sh->regs.R[m] + 4;

  sh->cycles += 2;
  SH2delay(sh, temp + 2);
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2bsr(SH2_struct *sh) {
  u32 temp;
  s32 disp = INSTRUCTION_BCD(sh->instruction);

  temp = sh->regs.PC;
  if ((disp & 0x800) != 0)
    disp |= 0xFFFFF000;
  sh->regs.PR = sh->regs.PC + 4;
  sh->regs.PC = sh->regs.PC + (disp << 1) + 4;

  sh->cycles += 2;
  SH2delay(sh, temp + 2);
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2bsrf(SH2_struct *sh) {
  u32 temp = sh->regs.PC;
  sh->regs.PR = sh->regs.PC + 4;
  sh->regs.PC += sh->regs.R[INSTRUCTION_B(sh->instruction)] + 4;
  sh->cycles += 2;
  SH2delay(sh, temp + 2);
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2bt(SH2_struct *sh) {
  if (sh->regs.SR.part.T == 1) {
    s32 disp = (s32)(s8)sh->instruction;

    sh->regs.PC = sh->regs.PC + (disp << 1) + 4;
    sh->cycles += 3;
  } else {
    sh->regs.PC += 2;
    sh->cycles++;
  }
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2bts(SH2_struct *sh) {
  if (sh->regs.SR.part.T) {
    s32 disp = (s32)(s8)sh->instruction;
    u32 temp = sh->regs.PC;

    sh->regs.PC += (disp << 1) + 4;
    sh->cycles += 2;
    SH2delay(sh, temp + 2);
  } else {
    sh->regs.PC += 2;
    sh->cycles++;
  }
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2clrmac(SH2_struct *sh) {
  sh->regs.MACH = 0;
  sh->regs.MACL = 0;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2clrt(SH2_struct *sh) {
  sh->regs.SR.part.T = 0;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2cmpeq(SH2_struct *sh) {
  if (sh->regs.R[INSTRUCTION_B(sh->instruction)] ==
      sh->regs.R[INSTRUCTION_C(sh->instruction)])
    sh->regs.SR.part.T = 1;
  else
    sh->regs.SR.part.T = 0;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2cmpge(SH2_struct *sh) {
  if ((s32)sh->regs.R[INSTRUCTION_B(sh->instruction)] >=
      (s32)sh->regs.R[INSTRUCTION_C(sh->instruction)])
    sh->regs.SR.part.T = 1;
  else
    sh->regs.SR.part.T = 0;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2cmpgt(SH2_struct *sh) {
  if ((s32)sh->regs.R[INSTRUCTION_B(sh->instruction)] >
      (s32)sh->regs.R[INSTRUCTION_C(sh->instruction)])
    sh->regs.SR.part.T = 1;
  else
    sh->regs.SR.part.T = 0;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2cmphi(SH2_struct *sh) {
  if ((u32)sh->regs.R[INSTRUCTION_B(sh->instruction)] >
      (u32)sh->regs.R[INSTRUCTION_C(sh->instruction)])
    sh->regs.SR.part.T = 1;
  else
    sh->regs.SR.part.T = 0;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2cmphs(SH2_struct *sh) {
  if ((u32)sh->regs.R[INSTRUCTION_B(sh->instruction)] >=
      (u32)sh->regs.R[INSTRUCTION_C(sh->instruction)])
    sh->regs.SR.part.T = 1;
  else
    sh->regs.SR.part.T = 0;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2cmpim(SH2_struct *sh) {
  s32 imm;
  s32 i = INSTRUCTION_CD(sh->instruction);

  imm = (s32)(s8)i;

  if (sh->regs.R[0] == (u32)imm) // FIXME: ouais � doit �re bon...
    sh->regs.SR.part.T = 1;
  else
    sh->regs.SR.part.T = 0;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2cmppl(SH2_struct *sh) {
  if ((s32)sh->regs.R[INSTRUCTION_B(sh->instruction)] > 0)
    sh->regs.SR.part.T = 1;
  else
    sh->regs.SR.part.T = 0;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2cmppz(SH2_struct *sh) {
  if ((s32)sh->regs.R[INSTRUCTION_B(sh->instruction)] >= 0)
    sh->regs.SR.part.T = 1;
  else
    sh->regs.SR.part.T = 0;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2cmpstr(SH2_struct *sh) {
  u32 temp;
  s32 HH, HL, LH, LL;
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);
  temp = sh->regs.R[n] ^ sh->regs.R[m];
  HH = (temp >> 24) & 0x000000FF;
  HL = (temp >> 16) & 0x000000FF;
  LH = (temp >> 8) & 0x000000FF;
  LL = temp & 0x000000FF;
  HH = HH && HL && LH && LL;
  if (HH == 0)
    sh->regs.SR.part.T = 1;
  else
    sh->regs.SR.part.T = 0;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2div0s(SH2_struct *sh) {
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);
  if ((sh->regs.R[n] & 0x80000000) == 0)
    sh->regs.SR.part.Q = 0;
  else
    sh->regs.SR.part.Q = 1;
  if ((sh->regs.R[m] & 0x80000000) == 0)
    sh->regs.SR.part.M = 0;
  else
    sh->regs.SR.part.M = 1;
  sh->regs.SR.part.T = !(sh->regs.SR.part.M == sh->regs.SR.part.Q);
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2div0u(SH2_struct *sh) {
  sh->regs.SR.part.M = sh->regs.SR.part.Q = sh->regs.SR.part.T = 0;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2div1(SH2_struct *sh) {
  u32 tmp0;
  u8 old_q, tmp1;
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);

  old_q = sh->regs.SR.part.Q;
  sh->regs.SR.part.Q = (u8)((0x80000000 & sh->regs.R[n]) != 0);
  sh->regs.R[n] <<= 1;
  sh->regs.R[n] |= (u32)sh->regs.SR.part.T;

  switch (old_q) {
  case 0:
    switch (sh->regs.SR.part.M) {
    case 0:
      tmp0 = sh->regs.R[n];
      sh->regs.R[n] -= sh->regs.R[m];
      tmp1 = (sh->regs.R[n] > tmp0);
      switch (sh->regs.SR.part.Q) {
      case 0:
        sh->regs.SR.part.Q = tmp1;
        break;
      case 1:
        sh->regs.SR.part.Q = (u8)(tmp1 == 0);
        break;
      }
      break;
    case 1:
      tmp0 = sh->regs.R[n];
      sh->regs.R[n] += sh->regs.R[m];
      tmp1 = (sh->regs.R[n] < tmp0);
      switch (sh->regs.SR.part.Q) {
      case 0:
        sh->regs.SR.part.Q = (u8)(tmp1 == 0);
        break;
      case 1:
        sh->regs.SR.part.Q = tmp1;
        break;
      }
      break;
    }
    break;
  case 1:
    switch (sh->regs.SR.part.M) {
    case 0:
      tmp0 = sh->regs.R[n];
      sh->regs.R[n] += sh->regs.R[m];
      tmp1 = (sh->regs.R[n] < tmp0);
      switch (sh->regs.SR.part.Q) {
      case 0:
        sh->regs.SR.part.Q = tmp1;
        break;
      case 1:
        sh->regs.SR.part.Q = (u8)(tmp1 == 0);
        break;
      }
      break;
    case 1:
      tmp0 = sh->regs.R[n];
      sh->regs.R[n] -= sh->regs.R[m];
      tmp1 = (sh->regs.R[n] > tmp0);
      switch (sh->regs.SR.part.Q) {
      case 0:
        sh->regs.SR.part.Q = (u8)(tmp1 == 0);
        break;
      case 1:
        sh->regs.SR.part.Q = tmp1;
        break;
      }
      break;
    }
    break;
  }
  sh->regs.SR.part.T = (sh->regs.SR.part.Q == sh->regs.SR.part.M);
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2dmuls(SH2_struct *sh) {
  u32 RnL, RnH, RmL, RmH, Res0, Res1, Res2;
  u32 temp0, temp1, temp2, temp3;
  s32 tempm, tempn, fnLmL;
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);

  const u64 result = (s64)(s32)sh->regs.R[n] * (s32)sh->regs.R[m];

  sh->regs.MACL = result >> 0;
  sh->regs.MACH = result >> 32;
#if 0  
   tempn = (s32)sh->regs.R[n];
   tempm = (s32)sh->regs.R[m];
   if (tempn < 0)
      tempn = 0 - tempn;
   if (tempm < 0)
      tempm = 0 - tempm;
   if ((s32) (sh->regs.R[n] ^ sh->regs.R[m]) < 0)
      fnLmL = -1;
   else
      fnLmL = 0;
  
   temp1 = (u32) tempn;
   temp2 = (u32) tempm;

   RnL = temp1 & 0x0000FFFF;
   RnH = (temp1 >> 16) & 0x0000FFFF;
   RmL = temp2 & 0x0000FFFF;
   RmH = (temp2 >> 16) & 0x0000FFFF;
  
   temp0 = RmL * RnL;
   temp1 = RmH * RnL;
   temp2 = RmL * RnH;
   temp3 = RmH * RnH;

   Res2 = 0;
   Res1 = temp1 + temp2;
   if (Res1 < temp1)
      Res2 += 0x00010000;

   temp1 = (Res1 << 16) & 0xFFFF0000;
   Res0 = temp0 + temp1;
   if (Res0 < temp0)
      Res2++;
  
   Res2 = Res2 + ((Res1 >> 16) & 0x0000FFFF) + temp3;

   if (fnLmL < 0)
   {
      Res2 = ~Res2;
      if (Res0 == 0)
         Res2++;
      else
         Res0 =(~Res0) + 1;
   }
   sh->regs.MACH = Res2;
   sh->regs.MACL = Res0;
#endif
  sh->regs.PC += 2;
  sh->cycles += 4;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2dmulu(SH2_struct *sh) {
  u32 RnL, RnH, RmL, RmH, Res0, Res1, Res2;
  u32 temp0, temp1, temp2, temp3;
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);

  RnL = sh->regs.R[n] & 0x0000FFFF;
  RnH = (sh->regs.R[n] >> 16) & 0x0000FFFF;
  RmL = sh->regs.R[m] & 0x0000FFFF;
  RmH = (sh->regs.R[m] >> 16) & 0x0000FFFF;

  temp0 = RmL * RnL;
  temp1 = RmH * RnL;
  temp2 = RmL * RnH;
  temp3 = RmH * RnH;

  Res2 = 0;
  Res1 = temp1 + temp2;
  if (Res1 < temp1)
    Res2 += 0x00010000;

  temp1 = (Res1 << 16) & 0xFFFF0000;
  Res0 = temp0 + temp1;
  if (Res0 < temp0)
    Res2++;

  Res2 = Res2 + ((Res1 >> 16) & 0x0000FFFF) + temp3;

  sh->regs.MACH = Res2;
  sh->regs.MACL = Res0;
  sh->regs.PC += 2;
  sh->cycles += 4;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2dt(SH2_struct *sh) {
  s32 n = INSTRUCTION_B(sh->instruction);

  sh->regs.R[n]--;
  if (sh->regs.R[n] == 0)
    sh->regs.SR.part.T = 1;
  else
    sh->regs.SR.part.T = 0;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2extsb(SH2_struct *sh) {
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);

  sh->regs.R[n] = (u32)(s8)sh->regs.R[m];
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2extsw(SH2_struct *sh) {
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);

  sh->regs.R[n] = (u32)(s16)sh->regs.R[m];
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2extub(SH2_struct *sh) {
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);

  sh->regs.R[n] = (u32)(u8)sh->regs.R[m];
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2extuw(SH2_struct *sh) {
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);

  sh->regs.R[n] = (u32)(u16)sh->regs.R[m];
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2jmp(SH2_struct *sh) {
  u32 temp;
  s32 m = INSTRUCTION_B(sh->instruction);

  temp = sh->regs.PC;
  sh->regs.PC = sh->regs.R[m];
  sh->cycles += 2;
  SH2delay(sh, temp + 2);
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2jsr(SH2_struct *sh) {
  u32 temp;
  s32 m = INSTRUCTION_B(sh->instruction);

  temp = sh->regs.PC;
  if (sh->regs.R[m] != 0) {
    sh->regs.PR = sh->regs.PC + 4;
    sh->regs.PC = sh->regs.R[m];
  } else {
    sh->regs.PC += 2;
  }
  sh->cycles += 2;
  SH2delay(sh, temp + 2);
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2ldcgbr(SH2_struct *sh) {
  sh->regs.GBR = sh->regs.R[INSTRUCTION_B(sh->instruction)];
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2ldcmgbr(SH2_struct *sh) {
  s32 m = INSTRUCTION_B(sh->instruction);
  u32 rcycle = 0;

  sh->regs.GBR = MappedMemoryReadLong(sh->regs.R[m], &rcycle);
  sh->regs.R[m] += 4;
  sh->regs.PC += 2;
  sh->cycles += 3 + rcycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2ldcmsr(SH2_struct *sh) {
  u32 rcycle = 0;
  s32 m = INSTRUCTION_B(sh->instruction);

  sh->regs.SR.all = MappedMemoryReadLong(sh->regs.R[m], &rcycle) & 0x000003F3;
  sh->regs.R[m] += 4;
  sh->regs.PC += 2;
  sh->cycles += 3 + rcycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2ldcmvbr(SH2_struct *sh) {
  u32 rcycle = 0;
  s32 m = INSTRUCTION_B(sh->instruction);

  sh->regs.VBR = MappedMemoryReadLong(sh->regs.R[m], &rcycle);
  sh->regs.R[m] += 4;
  sh->regs.PC += 2;
  sh->cycles += 3;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2ldcsr(SH2_struct *sh) {
  sh->regs.SR.all = sh->regs.R[INSTRUCTION_B(sh->instruction)] & 0x000003F3;
  sh->regs.PC += 2;
  sh->cycles++;
  SH2HandleInterrupts(sh);
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2ldcvbr(SH2_struct *sh) {
  s32 m = INSTRUCTION_B(sh->instruction);

  sh->regs.VBR = sh->regs.R[m];
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2ldsmach(SH2_struct *sh) {
  sh->regs.MACH = sh->regs.R[INSTRUCTION_B(sh->instruction)];
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2ldsmacl(SH2_struct *sh) {
  sh->regs.MACL = sh->regs.R[INSTRUCTION_B(sh->instruction)];
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2ldsmmach(SH2_struct *sh) {
  u32 rcycle = 0;
  s32 m = INSTRUCTION_B(sh->instruction);
  sh->regs.MACH = MappedMemoryReadLong(sh->regs.R[m], &rcycle);
  sh->regs.R[m] += 4;
  sh->regs.PC += 2;
  sh->cycles += 1 + rcycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2ldsmmacl(SH2_struct *sh) {
  u32 rcycle = 0;
  s32 m = INSTRUCTION_B(sh->instruction);
  sh->regs.MACL = MappedMemoryReadLong(sh->regs.R[m], &rcycle);
  sh->regs.R[m] += 4;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2ldsmpr(SH2_struct *sh) {
  u32 rcycle = 0;
  s32 m = INSTRUCTION_B(sh->instruction);
  sh->regs.PR = MappedMemoryReadLong(sh->regs.R[m], &rcycle);
  sh->regs.R[m] += 4;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2ldspr(SH2_struct *sh) {
  sh->regs.PR = sh->regs.R[INSTRUCTION_B(sh->instruction)];
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2macl(SH2_struct *sh) {
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);
  s32 m0, m1;
  u32 rcycle1 = 0;
  u32 rcycle2 = 0;

  m1 = (s32)MappedMemoryReadLong(sh->regs.R[n], &rcycle1);
  sh->regs.R[n] += 4;
  m0 = (s32)MappedMemoryReadLong(sh->regs.R[m], &rcycle2);
  sh->regs.R[m] += 4;

#if 1 // fast and better
  u64 a, b, sum;
  a = sh->regs.MACL | ((u64)sh->regs.MACH << 32);
  b = (s64)m0 * m1;
  sum = a + b;
  if (sh->regs.SR.part.S == 1) {
    if (sum > 0x00007FFFFFFFFFFFULL && sum < 0xFFFF800000000000ULL) {
      if ((s64)b < 0)
        sum = 0xFFFF800000000000ULL;
      else
        sum = 0x00007FFFFFFFFFFFULL;
    }
  }
  sh->regs.MACL = sum;
  sh->regs.MACH = sum >> 32;
#else
  if ((s32)(tempn ^ tempm) < 0)
    fnLmL = -1;
  else
    fnLmL = 0;
  if (tempn < 0)
    tempn = 0 - tempn;
  if (tempm < 0)
    tempm = 0 - tempm;

  temp1 = (u32)tempn;
  temp2 = (u32)tempm;

  RnL = temp1 & 0x0000FFFF;
  RnH = (temp1 >> 16) & 0x0000FFFF;
  RmL = temp2 & 0x0000FFFF;
  RmH = (temp2 >> 16) & 0x0000FFFF;

  temp0 = RmL * RnL;
  temp1 = RmH * RnL;
  temp2 = RmL * RnH;
  temp3 = RmH * RnH;

  Res2 = 0;
  Res1 = temp1 + temp2;
  if (Res1 < temp1)
    Res2 += 0x00010000;

  temp1 = (Res1 << 16) & 0xFFFF0000;
  Res0 = temp0 + temp1;
  if (Res0 < temp0)
    Res2++;

  Res2 = Res2 + ((Res1 >> 16) & 0x0000FFFF) + temp3;

  if (fnLmL < 0) {
    Res2 = ~Res2;
    if (Res0 == 0)
      Res2++;
    else
      Res0 = (~Res0) + 1;
  }
  if (sh->regs.SR.part.S == 1) {
    Res0 = sh->regs.MACL + Res0;
    if (sh->regs.MACL > Res0)
      Res2++;
    // if (sh->regs.MACH & 0x00008000);
    // else Res2 += sh->regs.MACH | 0xFFFF0000;

    if (sh->regs.MACH & 0x00008000) {
      Res2 -= (sh->regs.MACH & 0x0000FFFF);
    } else {
      Res2 += (sh->regs.MACH & 0x0000FFFF);
    }
    if (((s32)Res2 < 0) && (Res2 < 0xFFFF8000)) {
      Res2 = 0x00008000;
      Res0 = 0x00000000;
    }
    if (((s32)Res2 > 0) && (Res2 > 0x00007FFF)) {
      Res2 = 0x00007FFF;
      Res0 = 0xFFFFFFFF;
    };

    sh->regs.MACH = Res2;
    sh->regs.MACL = Res0;
  } else {
    Res0 = sh->regs.MACL + Res0;
    if (sh->regs.MACL > Res0)
      Res2++;
    Res2 += sh->regs.MACH;

    sh->regs.MACH = Res2;
    sh->regs.MACL = Res0;
  }
#endif
  sh->regs.PC += 2;
  sh->cycles += 1 + rcycle1 + rcycle2;
}

//////////////////////////////////////////////////////////////////////////////
#if 1
static void FASTCALL SH2macw(SH2_struct *sh) {
  s16 m0, m1;
  u32 templ;
  u32 rcycle1 = 0;
  u32 rcycle2 = 0;
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);

  m0 = (s32)MappedMemoryReadWord(sh->regs.R[m], &rcycle1);
  sh->regs.R[m] += 2;
  m1 = (s32)MappedMemoryReadWord(sh->regs.R[n], &rcycle2);
  sh->regs.R[n] += 2;

  s32 b = (s32)m0 * m1;
  u64 sum = (s64)(s32)sh->regs.MACL + b;

  if (sh->regs.SR.part.S == 1) {
    if (sum > 0x000000007FFFFFFFULL && sum < 0xFFFFFFFF80000000ULL) {
      sh->regs.MACH |= 1;

      if (b < 0)
        sum = 0x80000000ULL;
      else
        sum = 0x7FFFFFFFULL;
    }
    sh->regs.MACL = sum;
  } else {
    sh->regs.MACL = sum;
    sh->regs.MACH = sum >> 32;
  }
  sh->regs.PC += 2;
  sh->cycles += 1 + rcycle1 + rcycle2;
}

#else
static void FASTCALL SH2macw(SH2_struct *sh) {
  s32 tempm, tempn, dest, src, ans;
  u32 templ;
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);

  tempn = (s32)MappedMemoryReadWord(sh->regs.R[n]);
  sh->regs.R[n] += 2;
  tempm = (s32)MappedMemoryReadWord(sh->regs.R[m]);
  sh->regs.R[m] += 2;
  templ = sh->regs.MACL;
  tempm = ((s32)(s16)tempn * (s32)(s16)tempm);

  if ((s32)sh->regs.MACL >= 0)
    dest = 0;
  else
    dest = 1;
  if ((s32)tempm >= 0) {
    src = 0;
    tempn = 0;
  } else {
    src = 1;
    tempn = 0xFFFFFFFF;
  }
  src += dest;
  sh->regs.MACL += tempm;
  if ((s32)sh->regs.MACL >= 0)
    ans = 0;
  else
    ans = 1;
  ans += dest;
  if (sh->regs.SR.part.S == 1) {
    if (ans == 1) {
      if (src == 0)
        sh->regs.MACL = 0x7FFFFFFF;
      if (src == 2)
        sh->regs.MACL = 0x80000000;
    }
  } else {
    sh->regs.MACH += tempn;
    if (templ > sh->regs.MACL)
      sh->regs.MACH += 1;
  }
  sh->regs.PC += 2;
  sh->cycles += 3;
}
#endif

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2mov(SH2_struct *sh) {
  sh->regs.R[INSTRUCTION_B(sh->instruction)] =
      sh->regs.R[INSTRUCTION_C(sh->instruction)];
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2mova(SH2_struct *sh) {
  s32 disp = INSTRUCTION_CD(sh->instruction);

  sh->regs.R[0] = ((sh->regs.PC + 4) & 0xFFFFFFFC) + (disp << 2);
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movbl(SH2_struct *sh) {
  u32 rcycle = 0;
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);

  sh->regs.R[n] = (s32)(s8)MappedMemoryReadByte(sh->regs.R[m], &rcycle);
  sh->regs.PC += 2;
  sh->cycles += 1 + rcycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movbl0(SH2_struct *sh) {
  u32 rcycle = 0;
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);

  sh->regs.R[n] =
      (s32)(s8)MappedMemoryReadByte(sh->regs.R[m] + sh->regs.R[0], &rcycle);
  sh->regs.PC += 2;
  sh->cycles += rcycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movbl4(SH2_struct *sh) {
  u32 rcycle = 0;
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 disp = INSTRUCTION_D(sh->instruction);

  sh->regs.R[0] = (s32)(s8)MappedMemoryReadByte(sh->regs.R[m] + disp, &rcycle);
  sh->regs.PC += 2;
  sh->cycles += 1 + rcycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movblg(SH2_struct *sh) {
  u32 rcycle = 0;
  s32 disp = INSTRUCTION_CD(sh->instruction);

  sh->regs.R[0] = (s32)(s8)MappedMemoryReadByte(sh->regs.GBR + disp, &rcycle);
  sh->regs.PC += 2;
  sh->cycles += 1 + rcycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movbm(SH2_struct *sh) {
  u32 rcycle = 0;
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);

  MappedMemoryWriteByte((sh->regs.R[n] - 1), sh->regs.R[m], &rcycle);
  sh->regs.R[n] -= 1;
  sh->regs.PC += 2;
  sh->cycles += 1 + rcycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movbp(SH2_struct *sh) {
  u32 rcycle = 0;
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);

  sh->regs.R[n] = (s32)(s8)MappedMemoryReadByte(sh->regs.R[m], &rcycle);
  if (n != m)
    sh->regs.R[m] += 1;
  sh->regs.PC += 2;
  sh->cycles += 1 + rcycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movbs(SH2_struct *sh) {
  u32 cycle = 0;
  int b = INSTRUCTION_B(sh->instruction);
  int c = INSTRUCTION_C(sh->instruction);

  MappedMemoryWriteByte(sh->regs.R[b], sh->regs.R[c], &cycle);
  sh->regs.PC += 2;
  sh->cycles += 1 + cycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movbs0(SH2_struct *sh) {
  u32 cycle = 0;
  MappedMemoryWriteByte(sh->regs.R[INSTRUCTION_B(sh->instruction)] +
                            sh->regs.R[0],
                        sh->regs.R[INSTRUCTION_C(sh->instruction)], &cycle);
  sh->regs.PC += 2;
  sh->cycles += 1 + cycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movbs4(SH2_struct *sh) {
  u32 cycle = 0;
  s32 disp = INSTRUCTION_D(sh->instruction);
  s32 n = INSTRUCTION_C(sh->instruction);

  MappedMemoryWriteByte(sh->regs.R[n] + disp, sh->regs.R[0], &cycle);
  sh->regs.PC += 2;
  sh->cycles += 1 + cycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movbsg(SH2_struct *sh) {
  u32 cycle = 0;
  s32 disp = INSTRUCTION_CD(sh->instruction);

  MappedMemoryWriteByte(sh->regs.GBR + disp, sh->regs.R[0], &cycle);
  sh->regs.PC += 2;
  sh->cycles += 1 + cycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movi(SH2_struct *sh) {
  s32 i = INSTRUCTION_CD(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);

  sh->regs.R[n] = (s32)(s8)i;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movli(SH2_struct *sh) {
  u32 cycle = 0;
  s32 disp = INSTRUCTION_CD(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);

  sh->regs.R[n] = MappedMemoryReadLong(
      ((sh->regs.PC + 4) & 0xFFFFFFFC) + (disp << 2), &cycle);
  sh->regs.PC += 2;
  sh->cycles += 1 + cycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movll(SH2_struct *sh) {
  u32 cycle = 0;
  sh->regs.R[INSTRUCTION_B(sh->instruction)] =
      MappedMemoryReadLong(sh->regs.R[INSTRUCTION_C(sh->instruction)], &cycle);
  sh->regs.PC += 2;
  sh->cycles += 1 + cycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movll0(SH2_struct *sh) {
  u32 cycle = 0;
  sh->regs.R[INSTRUCTION_B(sh->instruction)] = MappedMemoryReadLong(
      sh->regs.R[INSTRUCTION_C(sh->instruction)] + sh->regs.R[0], &cycle);
  sh->regs.PC += 2;
  sh->cycles += 1 + cycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movll4(SH2_struct *sh) {
  u32 cycle = 0;
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 disp = INSTRUCTION_D(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);

  sh->regs.R[n] = MappedMemoryReadLong(sh->regs.R[m] + (disp << 2), &cycle);
  sh->regs.PC += 2;
  sh->cycles += 1 + cycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movllg(SH2_struct *sh) {
  u32 cycle = 0;
  s32 disp = INSTRUCTION_CD(sh->instruction);

  sh->regs.R[0] = MappedMemoryReadLong(sh->regs.GBR + (disp << 2), &cycle);
  sh->regs.PC += 2;
  sh->cycles += 1 + cycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movlm(SH2_struct *sh) {
  u32 cycle = 0;
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);

  MappedMemoryWriteLong(sh->regs.R[n] - 4, sh->regs.R[m], &cycle);
  sh->regs.R[n] -= 4;
  sh->regs.PC += 2;
  sh->cycles += 1 + cycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movlp(SH2_struct *sh) {
  u32 cycle = 0;
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);

  sh->regs.R[n] = MappedMemoryReadLong(sh->regs.R[m], &cycle);
  if (n != m)
    sh->regs.R[m] += 4;
  sh->regs.PC += 2;
  sh->cycles += 1 + cycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movls(SH2_struct *sh) {
  u32 cycle = 0;
  int b = INSTRUCTION_B(sh->instruction);
  int c = INSTRUCTION_C(sh->instruction);

  MappedMemoryWriteLong(sh->regs.R[b], sh->regs.R[c], &cycle);
  sh->regs.PC += 2;
  sh->cycles += 1 + cycle;  // base cycle was missing (all sibling stores have it)
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movls0(SH2_struct *sh) {
  u32 cycle = 0;
  MappedMemoryWriteLong(sh->regs.R[INSTRUCTION_B(sh->instruction)] +
                            sh->regs.R[0],
                        sh->regs.R[INSTRUCTION_C(sh->instruction)], &cycle);
  sh->regs.PC += 2;
  sh->cycles += 1 + cycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movls4(SH2_struct *sh) {
  u32 cycle = 0;
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 disp = INSTRUCTION_D(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);

  MappedMemoryWriteLong(sh->regs.R[n] + (disp << 2), sh->regs.R[m], &cycle);
  sh->regs.PC += 2;
  sh->cycles += 1 + cycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movlsg(SH2_struct *sh) {
  u32 cycle = 0;
  s32 disp = INSTRUCTION_CD(sh->instruction);

  MappedMemoryWriteLong(sh->regs.GBR + (disp << 2), sh->regs.R[0], &cycle);
  sh->regs.PC += 2;
  sh->cycles += 1 + cycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movt(SH2_struct *sh) {
  sh->regs.R[INSTRUCTION_B(sh->instruction)] = (0x00000001 & sh->regs.SR.all);
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movwi(SH2_struct *sh) {
  u32 cycle = 0;
  s32 disp = INSTRUCTION_CD(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);

  sh->regs.R[n] =
      (s32)(s16)MappedMemoryReadWord(sh->regs.PC + (disp << 1) + 4, &cycle);
  sh->regs.PC += 2;
  sh->cycles += 1 + cycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movwl(SH2_struct *sh) {
  u32 cycle = 0;
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);

  sh->regs.R[n] = (s32)(s16)MappedMemoryReadWord(sh->regs.R[m], &cycle);
  sh->regs.PC += 2;
  sh->cycles += 1 + cycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movwl0(SH2_struct *sh) {
  u32 cycle = 0;
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);

  sh->regs.R[n] =
      (s32)(s16)MappedMemoryReadWord(sh->regs.R[m] + sh->regs.R[0], &cycle);
  sh->regs.PC += 2;
  sh->cycles += 1 + cycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movwl4(SH2_struct *sh) {
  u32 cycle = 0;
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 disp = INSTRUCTION_D(sh->instruction);

  sh->regs.R[0] =
      (s32)(s16)MappedMemoryReadWord(sh->regs.R[m] + (disp << 1), &cycle);
  sh->regs.PC += 2;
  sh->cycles += 1 + cycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movwlg(SH2_struct *sh) {
  u32 cycle = 0;
  s32 disp = INSTRUCTION_CD(sh->instruction);

  sh->regs.R[0] =
      (s32)(s16)MappedMemoryReadWord(sh->regs.GBR + (disp << 1), &cycle);
  sh->regs.PC += 2;
  sh->cycles += 1 + cycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movwm(SH2_struct *sh) {
  u32 cycle = 0;
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);

  MappedMemoryWriteWord(sh->regs.R[n] - 2, sh->regs.R[m], &cycle);
  sh->regs.R[n] -= 2;
  sh->regs.PC += 2;
  sh->cycles += 1 + cycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movwp(SH2_struct *sh) {
  u32 cycle = 0;
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);

  sh->regs.R[n] = (s32)(s16)MappedMemoryReadWord(sh->regs.R[m], &cycle);
  if (n != m)
    sh->regs.R[m] += 2;
  sh->regs.PC += 2;
  sh->cycles += 1 + cycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movws(SH2_struct *sh) {
  u32 cycle = 0;
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);

  MappedMemoryWriteWord(sh->regs.R[n], sh->regs.R[m], &cycle);
  sh->regs.PC += 2;
  sh->cycles += 1 + cycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movws0(SH2_struct *sh) {
  u32 cycle = 0;
  MappedMemoryWriteWord(sh->regs.R[INSTRUCTION_B(sh->instruction)] +
                            sh->regs.R[0],
                        sh->regs.R[INSTRUCTION_C(sh->instruction)], &cycle);
  sh->regs.PC += 2;
  sh->cycles += 1 + cycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movws4(SH2_struct *sh) {
  u32 cycle = 0;
  s32 disp = INSTRUCTION_D(sh->instruction);
  s32 n = INSTRUCTION_C(sh->instruction);

  MappedMemoryWriteWord(sh->regs.R[n] + (disp << 1), sh->regs.R[0], &cycle);
  sh->regs.PC += 2;
  sh->cycles += 1 + cycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2movwsg(SH2_struct *sh) {
  u32 cycle = 0;
  s32 disp = INSTRUCTION_CD(sh->instruction);

  MappedMemoryWriteWord(sh->regs.GBR + (disp << 1), sh->regs.R[0], &cycle);
  sh->regs.PC += 2;
  sh->cycles += 1 + cycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2mull(SH2_struct *sh) {
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);

  sh->regs.MACL = sh->regs.R[n] * sh->regs.R[m];
  sh->regs.PC += 2;
  sh->cycles += 4;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2muls(SH2_struct *sh) {
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);

  sh->regs.MACL = ((s32)(s16)sh->regs.R[n] * (s32)(s16)sh->regs.R[m]);
  sh->regs.PC += 2;
  sh->cycles += 2;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2mulu(SH2_struct *sh) {
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);

  sh->regs.MACL = ((u32)(u16)sh->regs.R[n] * (u32)(u16)sh->regs.R[m]);
  sh->regs.PC += 2;
  sh->cycles += 2;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2neg(SH2_struct *sh) {
  sh->regs.R[INSTRUCTION_B(sh->instruction)] =
      0 - sh->regs.R[INSTRUCTION_C(sh->instruction)];
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2negc(SH2_struct *sh) {
  u32 temp;
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);

  temp = 0 - sh->regs.R[m];
  sh->regs.R[n] = temp - sh->regs.SR.part.T;
  if (0 < temp)
    sh->regs.SR.part.T = 1;
  else
    sh->regs.SR.part.T = 0;
  if (temp < sh->regs.R[n])
    sh->regs.SR.part.T = 1;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2nop(SH2_struct *sh) {
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2y_not(SH2_struct *sh) {
  sh->regs.R[INSTRUCTION_B(sh->instruction)] =
      ~sh->regs.R[INSTRUCTION_C(sh->instruction)];
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2y_or(SH2_struct *sh) {
  sh->regs.R[INSTRUCTION_B(sh->instruction)] |=
      sh->regs.R[INSTRUCTION_C(sh->instruction)];
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2ori(SH2_struct *sh) {
  sh->regs.R[0] |= INSTRUCTION_CD(sh->instruction);
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2orm(SH2_struct *sh) {
  u32 rcycle = 0;
  u32 wcycle = 0;
  s32 temp;
  s32 source = INSTRUCTION_CD(sh->instruction);

  temp = (s32)MappedMemoryReadByte(sh->regs.GBR + sh->regs.R[0], &rcycle);
  temp |= source;
  MappedMemoryWriteByte(sh->regs.GBR + sh->regs.R[0], temp, &wcycle);
  sh->regs.PC += 2;
  sh->cycles += 3;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2rotcl(SH2_struct *sh) {
  s32 temp;
  s32 n = INSTRUCTION_B(sh->instruction);

  if ((sh->regs.R[n] & 0x80000000) == 0)
    temp = 0;
  else
    temp = 1;

  sh->regs.R[n] <<= 1;

  if (sh->regs.SR.part.T == 1)
    sh->regs.R[n] |= 0x00000001;
  else
    sh->regs.R[n] &= 0xFFFFFFFE;

  if (temp == 1)
    sh->regs.SR.part.T = 1;
  else
    sh->regs.SR.part.T = 0;

  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2rotcr(SH2_struct *sh) {
  s32 temp;
  s32 n = INSTRUCTION_B(sh->instruction);

  if ((sh->regs.R[n] & 0x00000001) == 0)
    temp = 0;
  else
    temp = 1;

  sh->regs.R[n] >>= 1;

  if (sh->regs.SR.part.T == 1)
    sh->regs.R[n] |= 0x80000000;
  else
    sh->regs.R[n] &= 0x7FFFFFFF;

  if (temp == 1)
    sh->regs.SR.part.T = 1;
  else
    sh->regs.SR.part.T = 0;

  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2rotl(SH2_struct *sh) {
  s32 n = INSTRUCTION_B(sh->instruction);

  if ((sh->regs.R[n] & 0x80000000) == 0)
    sh->regs.SR.part.T = 0;
  else
    sh->regs.SR.part.T = 1;

  sh->regs.R[n] <<= 1;

  if (sh->regs.SR.part.T == 1)
    sh->regs.R[n] |= 0x00000001;
  else
    sh->regs.R[n] &= 0xFFFFFFFE;

  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2rotr(SH2_struct *sh) {
  s32 n = INSTRUCTION_B(sh->instruction);

  if ((sh->regs.R[n] & 0x00000001) == 0)
    sh->regs.SR.part.T = 0;
  else
    sh->regs.SR.part.T = 1;

  sh->regs.R[n] >>= 1;

  if (sh->regs.SR.part.T == 1)
    sh->regs.R[n] |= 0x80000000;
  else
    sh->regs.R[n] &= 0x7FFFFFFF;

  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2rte(SH2_struct *sh) {
  u32 rcycle = 0;
  u32 wcycle = 0;
  u32 temp;
  temp = sh->regs.PC;
  sh->regs.PC = MappedMemoryReadLong(sh->regs.R[15], &rcycle);
  sh->regs.R[15] += 4;
  sh->regs.SR.all = MappedMemoryReadLong(sh->regs.R[15], &wcycle) & 0x000003F3;
  sh->regs.R[15] += 4;
  sh->cycles += 4 + rcycle + wcycle;
  SH2delay(sh, temp + 2);
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2rts(SH2_struct *sh) {
  u32 temp;

  temp = sh->regs.PC;
  sh->regs.PC = sh->regs.PR;

  sh->cycles += 2;
  SH2delay(sh, temp + 2);
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2sett(SH2_struct *sh) {
  sh->regs.SR.part.T = 1;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2shal(SH2_struct *sh) {
  s32 n = INSTRUCTION_B(sh->instruction);
  if ((sh->regs.R[n] & 0x80000000) == 0)
    sh->regs.SR.part.T = 0;
  else
    sh->regs.SR.part.T = 1;
  sh->regs.R[n] <<= 1;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2shar(SH2_struct *sh) {
  s32 temp;
  s32 n = INSTRUCTION_B(sh->instruction);

  if ((sh->regs.R[n] & 0x00000001) == 0)
    sh->regs.SR.part.T = 0;
  else
    sh->regs.SR.part.T = 1;

  if ((sh->regs.R[n] & 0x80000000) == 0)
    temp = 0;
  else
    temp = 1;

  sh->regs.R[n] >>= 1;

  if (temp == 1)
    sh->regs.R[n] |= 0x80000000;
  else
    sh->regs.R[n] &= 0x7FFFFFFF;

  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2shll(SH2_struct *sh) {
  s32 n = INSTRUCTION_B(sh->instruction);

  if ((sh->regs.R[n] & 0x80000000) == 0)
    sh->regs.SR.part.T = 0;
  else
    sh->regs.SR.part.T = 1;

  sh->regs.R[n] <<= 1;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2shll2(SH2_struct *sh) {
  sh->regs.R[INSTRUCTION_B(sh->instruction)] <<= 2;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2shll8(SH2_struct *sh) {
  sh->regs.R[INSTRUCTION_B(sh->instruction)] <<= 8;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2shll16(SH2_struct *sh) {
  sh->regs.R[INSTRUCTION_B(sh->instruction)] <<= 16;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2shlr(SH2_struct *sh) {
  s32 n = INSTRUCTION_B(sh->instruction);

  if ((sh->regs.R[n] & 0x00000001) == 0)
    sh->regs.SR.part.T = 0;
  else
    sh->regs.SR.part.T = 1;

  sh->regs.R[n] >>= 1;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2shlr2(SH2_struct *sh) {
  s32 n = INSTRUCTION_B(sh->instruction);
  sh->regs.R[n] >>= 2;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2shlr8(SH2_struct *sh) {
  s32 n = INSTRUCTION_B(sh->instruction);
  sh->regs.R[n] >>= 8;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2shlr16(SH2_struct *sh) {
  s32 n = INSTRUCTION_B(sh->instruction);
  sh->regs.R[n] >>= 16;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2stcgbr(SH2_struct *sh) {
  s32 n = INSTRUCTION_B(sh->instruction);
  sh->regs.R[n] = sh->regs.GBR;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2stcmgbr(SH2_struct *sh) {
  u32 cycle = 0;
  s32 n = INSTRUCTION_B(sh->instruction);
  sh->regs.R[n] -= 4;
  MappedMemoryWriteLong(sh->regs.R[n], sh->regs.GBR, &cycle);
  sh->regs.PC += 2;
  sh->cycles += 2 + cycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2stcmsr(SH2_struct *sh) {
  u32 cycle = 0;
  s32 n = INSTRUCTION_B(sh->instruction);
  sh->regs.R[n] -= 4;
  MappedMemoryWriteLong(sh->regs.R[n], sh->regs.SR.all, &cycle);
  sh->regs.PC += 2;
  sh->cycles += 2 + cycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2stcmvbr(SH2_struct *sh) {
  u32 cycle = 0;
  s32 n = INSTRUCTION_B(sh->instruction);
  sh->regs.R[n] -= 4;
  MappedMemoryWriteLong(sh->regs.R[n], sh->regs.VBR, &cycle);
  sh->regs.PC += 2;
  sh->cycles += 2 + cycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2stcsr(SH2_struct *sh) {
  s32 n = INSTRUCTION_B(sh->instruction);
  sh->regs.R[n] = sh->regs.SR.all;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2stcvbr(SH2_struct *sh) {
  s32 n = INSTRUCTION_B(sh->instruction);
  sh->regs.R[n] = sh->regs.VBR;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2stsmach(SH2_struct *sh) {
  s32 n = INSTRUCTION_B(sh->instruction);
  sh->regs.R[n] = sh->regs.MACH;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2stsmacl(SH2_struct *sh) {
  s32 n = INSTRUCTION_B(sh->instruction);
  sh->regs.R[n] = sh->regs.MACL;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2stsmmach(SH2_struct *sh) {
  u32 cycle = 0;
  s32 n = INSTRUCTION_B(sh->instruction);
  sh->regs.R[n] -= 4;
  MappedMemoryWriteLong(sh->regs.R[n], sh->regs.MACH, &cycle);
  sh->regs.PC += 2;
  sh->cycles += cycle + 1;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2stsmmacl(SH2_struct *sh) {
  u32 cycle = 0;
  s32 n = INSTRUCTION_B(sh->instruction);
  sh->regs.R[n] -= 4;
  MappedMemoryWriteLong(sh->regs.R[n], sh->regs.MACL, &cycle);
  sh->regs.PC += 2;
  sh->cycles += cycle + 1;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2stsmpr(SH2_struct *sh) {
  u32 cycle = 0;
  s32 n = INSTRUCTION_B(sh->instruction);
  sh->regs.R[n] -= 4;
  MappedMemoryWriteLong(sh->regs.R[n], sh->regs.PR, &cycle);
  sh->regs.PC += 2;
  sh->cycles += 1 + cycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2stspr(SH2_struct *sh) {
  s32 n = INSTRUCTION_B(sh->instruction);
  sh->regs.R[n] = sh->regs.PR;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2sub(SH2_struct *sh) {
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);
  sh->regs.R[n] -= sh->regs.R[m];
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2subc(SH2_struct *sh) {
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);
  u32 tmp0, tmp1;

  tmp1 = sh->regs.R[n] - sh->regs.R[m];
  tmp0 = sh->regs.R[n];
  sh->regs.R[n] = tmp1 - sh->regs.SR.part.T;

  if (tmp0 < tmp1)
    sh->regs.SR.part.T = 1;
  else
    sh->regs.SR.part.T = 0;

  if (tmp1 < sh->regs.R[n])
    sh->regs.SR.part.T = 1;

  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2subv(SH2_struct *sh) {
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);
  s32 dest, src, ans;

  if ((s32)sh->regs.R[n] >= 0)
    dest = 0;
  else
    dest = 1;

  if ((s32)sh->regs.R[m] >= 0)
    src = 0;
  else
    src = 1;

  src += dest;
  sh->regs.R[n] -= sh->regs.R[m];

  if ((s32)sh->regs.R[n] >= 0)
    ans = 0;
  else
    ans = 1;

  ans += dest;

  if (src == 1) {
    if (ans == 1)
      sh->regs.SR.part.T = 1;
    else
      sh->regs.SR.part.T = 0;
  } else
    sh->regs.SR.part.T = 0;

  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2swapb(SH2_struct *sh) {
  u32 temp0, temp1;
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);

  temp0 = sh->regs.R[m] & 0xffff0000;
  temp1 = (sh->regs.R[m] & 0x000000ff) << 8;
  sh->regs.R[n] = (sh->regs.R[m] >> 8) & 0x000000ff;
  sh->regs.R[n] = sh->regs.R[n] | temp1 | temp0;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2swapw(SH2_struct *sh) {
  u32 temp;
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);
  temp = (sh->regs.R[m] >> 16) & 0x0000FFFF;
  sh->regs.R[n] = sh->regs.R[m] << 16;
  sh->regs.R[n] |= temp;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2tas(SH2_struct *sh) {
  u32 cycle = 0;
  u32 wcycle = 0;
  s32 temp;
  s32 n = INSTRUCTION_B(sh->instruction);

  temp = (s32)MappedMemoryReadByte(sh->regs.R[n], &cycle);

  if (temp == 0)
    sh->regs.SR.part.T = 1;
  else
    sh->regs.SR.part.T = 0;

  temp |= 0x00000080;
  MappedMemoryWriteByte(sh->regs.R[n], temp, &wcycle);
  sh->regs.PC += 2;
  sh->cycles += 4 + cycle + wcycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2trapa(SH2_struct *sh) {
  u32 cycle = 0;
  u32 wcycle = 0;
  u32 wcycle2 = 0;
  s32 imm = INSTRUCTION_CD(sh->instruction);

  u32 oldpc = sh->regs.PC;

  sh->regs.R[15] -= 4;
  MappedMemoryWriteLong(sh->regs.R[15], sh->regs.SR.all, &cycle);
  sh->regs.R[15] -= 4;
  MappedMemoryWriteLong(sh->regs.R[15], sh->regs.PC + 2, &wcycle);
  sh->regs.PC = MappedMemoryReadLong(sh->regs.VBR + (imm << 2), &wcycle2);

  // LOG("[%s] Exception %u, vecnum=%u, saved PC=0x%08x --- New PC=0x%08x\n",
  // sh->isslave ? "SH2-S" : "SH2-M", 8, imm, oldpc, sh->regs.PC);

  sh->cycles += 8 + cycle + wcycle + wcycle2;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2tst(SH2_struct *sh) {
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);

  if ((sh->regs.R[n] & sh->regs.R[m]) == 0)
    sh->regs.SR.part.T = 1;
  else
    sh->regs.SR.part.T = 0;

  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2tsti(SH2_struct *sh) {
  s32 temp;
  s32 i = INSTRUCTION_CD(sh->instruction);

  temp = sh->regs.R[0] & i;

  if (temp == 0)
    sh->regs.SR.part.T = 1;
  else
    sh->regs.SR.part.T = 0;

  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2tstm(SH2_struct *sh) {
  s32 temp;
  u32 rcycle = 0;
  s32 i = INSTRUCTION_CD(sh->instruction);

  temp = (s32)MappedMemoryReadByte(sh->regs.GBR + sh->regs.R[0], &rcycle);
  temp &= i;

  if (temp == 0)
    sh->regs.SR.part.T = 1;
  else
    sh->regs.SR.part.T = 0;

  sh->regs.PC += 2;
  sh->cycles += 3 + rcycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2y_xor(SH2_struct *sh) {
  int b = INSTRUCTION_B(sh->instruction);
  int c = INSTRUCTION_C(sh->instruction);

  sh->regs.R[b] ^= sh->regs.R[c];
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2xori(SH2_struct *sh) {
  s32 source = INSTRUCTION_CD(sh->instruction);
  sh->regs.R[0] ^= source;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2xorm(SH2_struct *sh) {
  u32 rcycle = 0;
  u32 wcycle = 0;
  s32 source = INSTRUCTION_CD(sh->instruction);
  s32 temp;

  temp = (s32)MappedMemoryReadByte(sh->regs.GBR + sh->regs.R[0], &rcycle);
  temp ^= source;
  MappedMemoryWriteByte(sh->regs.GBR + sh->regs.R[0], temp, &wcycle);
  sh->regs.PC += 2;
  sh->cycles += 3 + rcycle + wcycle;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2xtrct(SH2_struct *sh) {
  u32 temp;
  s32 m = INSTRUCTION_C(sh->instruction);
  s32 n = INSTRUCTION_B(sh->instruction);

  temp = (sh->regs.R[m] << 16) & 0xFFFF0000;
  sh->regs.R[n] = (sh->regs.R[n] >> 16) & 0x0000FFFF;
  sh->regs.R[n] |= temp;
  sh->regs.PC += 2;
  sh->cycles++;
}

//////////////////////////////////////////////////////////////////////////////

static void FASTCALL SH2sleep(SH2_struct *sh) { sh->cycles += 3; }

//////////////////////////////////////////////////////////////////////////////

static opcodefunc decode(u16 instruction) {
  switch (INSTRUCTION_A(instruction)) {
  case 0:
    switch (INSTRUCTION_D(instruction)) {
    case 2:
      switch (INSTRUCTION_C(instruction)) {
      case 0:
        return &SH2stcsr;
      case 1:
        return &SH2stcgbr;
      case 2:
        return &SH2stcvbr;
      default:
        return &SH2undecoded;
      }

    case 3:
      switch (INSTRUCTION_C(instruction)) {
      case 0:
        return &SH2bsrf;
      case 2:
        return &SH2braf;
      default:
        return &SH2undecoded;
      }

    case 4:
      return &SH2movbs0;
    case 5:
      return &SH2movws0;
    case 6:
      return &SH2movls0;
    case 7:
      return &SH2mull;
    case 8:
      switch (INSTRUCTION_C(instruction)) {
      case 0:
        return &SH2clrt;
      case 1:
        return &SH2sett;
      case 2:
        return &SH2clrmac;
      default:
        return &SH2undecoded;
      }
    case 9:
      switch (INSTRUCTION_C(instruction)) {
      case 0:
        return &SH2nop;
      case 1:
        return &SH2div0u;
      case 2:
        return &SH2movt;
      default:
        return &SH2undecoded;
      }
    case 10:
      switch (INSTRUCTION_C(instruction)) {
      case 0:
        return &SH2stsmach;
      case 1:
        return &SH2stsmacl;
      case 2:
        return &SH2stspr;
      default:
        return &SH2undecoded;
      }
    case 11:
      switch (INSTRUCTION_C(instruction)) {
      case 0:
        return &SH2rts;
      case 1:
        return &SH2sleep;
      case 2:
        return &SH2rte;
      default:
        return &SH2undecoded;
      }
    case 12:
      return &SH2movbl0;
    case 13:
      return &SH2movwl0;
    case 14:
      return &SH2movll0;
    case 15:
      return &SH2macl;
    default:
      return &SH2undecoded;
    }

  case 1:
    return &SH2movls4;
  case 2:
    switch (INSTRUCTION_D(instruction)) {
    case 0:
      return &SH2movbs;
    case 1:
      return &SH2movws;
    case 2:
      return &SH2movls;
    case 4:
      return &SH2movbm;
    case 5:
      return &SH2movwm;
    case 6:
      return &SH2movlm;
    case 7:
      return &SH2div0s;
    case 8:
      return &SH2tst;
    case 9:
      return &SH2y_and;
    case 10:
      return &SH2y_xor;
    case 11:
      return &SH2y_or;
    case 12:
      return &SH2cmpstr;
    case 13:
      return &SH2xtrct;
    case 14:
      return &SH2mulu;
    case 15:
      return &SH2muls;
    default:
      return &SH2undecoded;
    }

  case 3:
    switch (INSTRUCTION_D(instruction)) {
    case 0:
      return &SH2cmpeq;
    case 2:
      return &SH2cmphs;
    case 3:
      return &SH2cmpge;
    case 4:
      return &SH2div1;
    case 5:
      return &SH2dmulu;
    case 6:
      return &SH2cmphi;
    case 7:
      return &SH2cmpgt;
    case 8:
      return &SH2sub;
    case 10:
      return &SH2subc;
    case 11:
      return &SH2subv;
    case 12:
      return &SH2add;
    case 13:
      return &SH2dmuls;
    case 14:
      return &SH2addc;
    case 15:
      return &SH2addv;
    default:
      return &SH2undecoded;
    }

  case 4:
    switch (INSTRUCTION_D(instruction)) {
    case 0:
      switch (INSTRUCTION_C(instruction)) {
      case 0:
        return &SH2shll;
      case 1:
        return &SH2dt;
      case 2:
        return &SH2shal;
      default:
        return &SH2undecoded;
      }
    case 1:
      switch (INSTRUCTION_C(instruction)) {
      case 0:
        return &SH2shlr;
      case 1:
        return &SH2cmppz;
      case 2:
        return &SH2shar;
      default:
        return &SH2undecoded;
      }
    case 2:
      switch (INSTRUCTION_C(instruction)) {
      case 0:
        return &SH2stsmmach;
      case 1:
        return &SH2stsmmacl;
      case 2:
        return &SH2stsmpr;
      default:
        return &SH2undecoded;
      }
    case 3:
      switch (INSTRUCTION_C(instruction)) {
      case 0:
        return &SH2stcmsr;
      case 1:
        return &SH2stcmgbr;
      case 2:
        return &SH2stcmvbr;
      default:
        return &SH2undecoded;
      }
    case 4:
      switch (INSTRUCTION_C(instruction)) {
      case 0:
        return &SH2rotl;
      case 2:
        return &SH2rotcl;
      default:
        return &SH2undecoded;
      }
    case 5:
      switch (INSTRUCTION_C(instruction)) {
      case 0:
        return &SH2rotr;
      case 1:
        return &SH2cmppl;
      case 2:
        return &SH2rotcr;
      default:
        return &SH2undecoded;
      }
    case 6:
      switch (INSTRUCTION_C(instruction)) {
      case 0:
        return &SH2ldsmmach;
      case 1:
        return &SH2ldsmmacl;
      case 2:
        return &SH2ldsmpr;
      default:
        return &SH2undecoded;
      }
    case 7:
      switch (INSTRUCTION_C(instruction)) {
      case 0:
        return &SH2ldcmsr;
      case 1:
        return &SH2ldcmgbr;
      case 2:
        return &SH2ldcmvbr;
      default:
        return &SH2undecoded;
      }
    case 8:
      switch (INSTRUCTION_C(instruction)) {
      case 0:
        return &SH2shll2;
      case 1:
        return &SH2shll8;
      case 2:
        return &SH2shll16;
      default:
        return &SH2undecoded;
      }
    case 9:
      switch (INSTRUCTION_C(instruction)) {
      case 0:
        return &SH2shlr2;
      case 1:
        return &SH2shlr8;
      case 2:
        return &SH2shlr16;
      default:
        return &SH2undecoded;
      }
    case 10:
      switch (INSTRUCTION_C(instruction)) {
      case 0:
        return &SH2ldsmach;
      case 1:
        return &SH2ldsmacl;
      case 2:
        return &SH2ldspr;
      default:
        return &SH2undecoded;
      }
    case 11:
      switch (INSTRUCTION_C(instruction)) {
      case 0:
        return &SH2jsr;
      case 1:
        return &SH2tas;
      case 2:
        return &SH2jmp;
      default:
        return &SH2undecoded;
      }
    case 14:
      switch (INSTRUCTION_C(instruction)) {
      case 0:
        return &SH2ldcsr;
      case 1:
        return &SH2ldcgbr;
      case 2:
        return &SH2ldcvbr;
      default:
        return &SH2undecoded;
      }
    case 15:
      return &SH2macw;
    default:
      return &SH2undecoded;
    }
  case 5:
    return &SH2movll4;
  case 6:
    switch (INSTRUCTION_D(instruction)) {
    case 0:
      return &SH2movbl;
    case 1:
      return &SH2movwl;
    case 2:
      return &SH2movll;
    case 3:
      return &SH2mov;
    case 4:
      return &SH2movbp;
    case 5:
      return &SH2movwp;
    case 6:
      return &SH2movlp;
    case 7:
      return &SH2y_not;
    case 8:
      return &SH2swapb;
    case 9:
      return &SH2swapw;
    case 10:
      return &SH2negc;
    case 11:
      return &SH2neg;
    case 12:
      return &SH2extub;
    case 13:
      return &SH2extuw;
    case 14:
      return &SH2extsb;
    case 15:
      return &SH2extsw;
    }

  case 7:
    return &SH2addi;
  case 8:
    switch (INSTRUCTION_B(instruction)) {
    case 0:
      return &SH2movbs4;
    case 1:
      return &SH2movws4;
    case 4:
      return &SH2movbl4;
    case 5:
      return &SH2movwl4;
    case 8:
      return &SH2cmpim;
    case 9:
      return &SH2bt;
    case 11:
      return &SH2bf;
    case 13:
      return &SH2bts;
    case 15:
      return &SH2bfs;
    default:
      return &SH2undecoded;
    }
  case 9:
    return &SH2movwi;
  case 10:
    return &SH2bra;
  case 11:
    return &SH2bsr;
  case 12:
    switch (INSTRUCTION_B(instruction)) {
    case 0:
      return &SH2movbsg;
    case 1:
      return &SH2movwsg;
    case 2:
      return &SH2movlsg;
    case 3:
      return &SH2trapa;
    case 4:
      return &SH2movblg;
    case 5:
      return &SH2movwlg;
    case 6:
      return &SH2movllg;
    case 7:
      return &SH2mova;
    case 8:
      return &SH2tsti;
    case 9:
      return &SH2andi;
    case 10:
      return &SH2xori;
    case 11:
      return &SH2ori;
    case 12:
      return &SH2tstm;
    case 13:
      return &SH2andm;
    case 14:
      return &SH2xorm;
    case 15:
      return &SH2orm;
    }

  case 13:
    return &SH2movli;
  case 14:
    return &SH2movi;
  default:
    return &SH2undecoded;
  }
}

//////////////////////////////////////////////////////////////////////////////

int SH2InterpreterInit() {
  int i;

  // Initialize any internal variables
  for (i = 0; i < 0x10000; i++)
    opcodes[i] = decode(i);

  for (i = 0; i < 0x100; i++) {
    switch (i) {
    case 0x000: // Bios
      fetchlist[i] = FetchBios;
      break;
    case 0x002: // Low Work Ram
      fetchlist[i] = FetchLWram;
      break;
    case 0x020: // CS0
      fetchlist[i] = FetchCs0;
      break;
    case 0x05c: // Fighting Viper
      fetchlist[i] = FetchVram;
      break;
    case 0x060: // High Work Ram
    case 0x061:
    case 0x062:
    case 0x063:
    case 0x064:
    case 0x065:
    case 0x066:
    case 0x067:
    case 0x068:
    case 0x069:
    case 0x06A:
    case 0x06B:
    case 0x06C:
    case 0x06D:
    case 0x06E:
    case 0x06F:
      fetchlist[i] = FetchHWram;
      break;
    default:
      fetchlist[i] = FetchInvalid;
      break;
    }
  }

  SH2ClearCodeBreakpoints(MSH2);
  SH2ClearCodeBreakpoints(SSH2);
  SH2ClearMemoryBreakpoints(MSH2);
  SH2ClearMemoryBreakpoints(SSH2);
  MSH2->breakpointEnabled = 0;
  SSH2->breakpointEnabled = 0;
  MSH2->backtraceEnabled = 0;
  SSH2->backtraceEnabled = 0;
  MSH2->stepOverOut.enabled = 0;
  SSH2->stepOverOut.enabled = 0;

  return 0;
}

int SH2DebugInterpreterInit() {

  SH2InterpreterInit();
  MSH2->breakpointEnabled = 1;
  SSH2->breakpointEnabled = 1;
  MSH2->backtraceEnabled = 1;
  SSH2->backtraceEnabled = 1;
  return 0;
}

//////////////////////////////////////////////////////////////////////////////

void SH2InterpreterDeInit() {
  // DeInitialize any internal variables here
}

//////////////////////////////////////////////////////////////////////////////

void SH2InterpreterReset(UNUSED SH2_struct *context) {
  // Reset any internal variables here
  context->stepOverOut.enabled = 0;
  context->stepOverOut.enabled = 0;
}

//////////////////////////////////////////////////////////////////////////////

static INLINE void SH2UBCInterrupt(SH2_struct *context, u32 flag) {
  if (15 > context->regs.SR.part.I) // Since UBC's interrupt are always level 15
  {
    context->regs.R[15] -= 4;
    MappedMemoryWriteLong(context->regs.R[15], context->regs.SR.all, NULL);
    context->regs.R[15] -= 4;
    MappedMemoryWriteLong(context->regs.R[15], context->regs.PC, NULL);
    context->regs.SR.part.I = 15;
    context->regs.PC =
        MappedMemoryReadLong(context->regs.VBR + (12 << 2), NULL);
    LOG("interrupt successfully handled\n");
  }
  context->onchip.BRCR |= flag;
}

static u32 SH2GetRegisterValue(SH2_struct *context, int regIndex) {
  if (regIndex >= 0 && regIndex < 16) {
    return context->regs.R[regIndex];
  }
  switch (regIndex) {
    case 16: return context->regs.SR.all;
    case 17: return context->regs.GBR;
    case 18: return context->regs.VBR;
    case 19: return context->regs.MACH;
    case 20: return context->regs.MACL;
    case 21: return context->regs.PR;
    case 22: return context->regs.PC;
    default: return 0;
  }
}

void SH2HandleBreakpoints(SH2_struct *context) {
  int i;

  for (i = 0; i < context->bp.numcodebreakpoints; i++) {

    if ((context->regs.PC == context->bp.codebreakpoint[i].addr) &&
        context->bp.inbreakpoint == 0) {

      // Check register condition if present
      if (context->bp.codebreakpoint[i].hasCondition) {
        u32 regValue = SH2GetRegisterValue(context, context->bp.codebreakpoint[i].regIndex);
        if (regValue != context->bp.codebreakpoint[i].regValue) {
          continue;  // Condition not met, skip this breakpoint
        }
      }

      context->bp.inbreakpoint = 1;
      SH2DumpHistory(context);
      if (context->bp.BreakpointCallBack)
        context->bp.BreakpointCallBack(context,
                                       context->bp.codebreakpoint[i].addr,
                                       context->bp.BreakpointUserData);
      context->bp.inbreakpoint = 0;
    }
  }

  if (context->bp.breaknow) {
    context->bp.breaknow = 0;
    context->bp.BreakpointCallBack(context, context->regs.PC,
                                   context->bp.BreakpointUserData);
  }
}

//////////////////////////////////////////////////////////////////////////////

FASTCALL void SH2DebugInterpreterExec(SH2_struct *context, u32 cycles) {
  int target_cycle = context->cycles + cycles - context->pre_cycle;
#if 0 // def SH2_TRACE
   /* Avoid accumulating leftover cycles multiple times, since the trace
    * code automatically adds state->cycles to the cycle accumulator when
    * printing a trace line */
   sh2_trace_add_cycles(-((s32)context->cycles));
#endif

  if (context->dma_ch0.penerly != 0) {
    //      LOG("[%s] %d  add DMA Penerlty %d",CurrentSH2->isslave ? "SH2-S" :
    //      "SH2-M", CurrentSH2->cycles, context->dma_ch0.penerly);
    context->cycles += (context->dma_ch0.penerly >> 1);
    context->dma_ch0.penerly = 0;
  }

  if (context->dma_ch1.penerly != 0) {
    context->cycles += (context->dma_ch1.penerly >> 1);
    context->dma_ch1.penerly = 0;
  }

  SH2HandleInterrupts(context);

  while (context->cycles < target_cycle) {
#ifdef SH2_UBC
    int ubcinterrupt = 0, ubcflag = 0;
#endif
    SH2HandleBreakpoints(context);

#if 0 // def SH2_TRACE
      sh2_trace(context, context->regs.PC);
#endif

#ifdef SH2_UBC
    if (context->onchip.BBRA &
        (BBR_CPA_CPU | BBR_IDA_INST |
         BBR_RWA_READ)) // Break on cpu, instruction, read cycles
    {
      if (context->onchip.BARA.all ==
          (context->regs.PC & (~context->onchip.BAMRA.all))) {
        LOG("Trigger UBC A interrupt: PC = %08X\n", context->regs.PC);
        if (!(context->onchip.BRCR & BRCR_PCBA)) {
          // Break before instruction fetch
          SH2UBCInterrupt(context, BRCR_CMFCA);
        } else {
          // Break after instruction fetch
          ubcinterrupt = 1;
          ubcflag = BRCR_CMFCA;
        }
      }
    } else if (context->onchip.BBRB &
               (BBR_CPA_CPU | BBR_IDA_INST |
                BBR_RWA_READ)) // Break on cpu, instruction, read cycles
    {
      if (context->onchip.BARB.all ==
          (context->regs.PC & (~context->onchip.BAMRB.all))) {
        LOG("Trigger UBC B interrupt: PC = %08X\n", context->regs.PC);
        if (!(context->onchip.BRCR & BRCR_PCBB)) {
          // Break before instruction fetch
          SH2UBCInterrupt(context, BRCR_CMFCB);
        } else {
          // Break after instruction fetch
          ubcinterrupt = 1;
          ubcflag = BRCR_CMFCB;
        }
      }
    }
#endif

    // Fetch Instruction
#ifdef EXEC_FROM_CACHE
    if ((context->regs.PC & 0xC0000000) == 0xC0000000)
      context->instruction = DataArrayReadWord(context->regs.PC);
    else
#endif
      context->instruction = MappedMemoryReadInst(
          context->regs.PC, NULL); // fetchlist[(context->regs.PC >> 20) &
                                   // 0x0FF](context->regs.PC);

    SH2HandleBackTrace(context);
    SH2HandleStepOverOut(context);
    SH2HandleTrackInfLoop(context);

#ifdef DMPHISTORY
    context->pchistory_index++;
    context->pchistory[context->pchistory_index & (MAX_DMPHISTORY - 1)] =
        context->regs.PC;
    context->regshistory[context->pchistory_index & (MAX_DMPHISTORY - 1)] =
        context->regs;
#endif

    // Execute it
    opcodes[context->instruction](context);

#ifdef SH2_UBC
    if (ubcinterrupt)
      SH2UBCInterrupt(context, ubcflag);
#endif
  }

#if 0 // def SH2_TRACE
   sh2_trace_add_cycles(context->cycles);
#endif
  context->pre_cycle = context->cycles - target_cycle;
}

//////////////////////////////////////////////////////////////////////////////

FASTCALL void SH2InterpreterExec(SH2_struct *context, u32 cycles) {
  int target_cycle = context->cycles + cycles - context->pre_cycle;

  if (context->dma_ch0.penerly != 0) {
    //      LOG("[%s] %d  add DMA Penerlty %d",CurrentSH2->isslave ? "SH2-S" :
    //      "SH2-M", CurrentSH2->cycles, context->dma_ch0.penerly);
    context->cycles += (context->dma_ch0.penerly >> 1);
    context->dma_ch0.penerly = 0;
  }

  if (context->dma_ch1.penerly != 0) {
    context->cycles += (context->dma_ch1.penerly >> 1);
    context->dma_ch1.penerly = 0;
  }

  SH2HandleInterrupts(context);

#ifndef EXEC_FROM_CACHE
  if (context->isIdle)
    SH2idleParse(context, target_cycle);
  else
    SH2idleCheck(context, target_cycle);
#endif

  while (context->cycles < target_cycle) {

    // Fetch Instruction
#ifdef EXEC_FROM_CACHE
    if ((context->regs.PC & 0xC0000000) == 0xC0000000)
      context->instruction = DataArrayReadWord(context->regs.PC);
    else
#endif
      context->instruction =
          fetchlist[(context->regs.PC >> 20) & 0x0FF](context->regs.PC);

    // context->instruction = MappedMemoryReadInst(context->regs.PC, NULL);//
    // fetchlist[(context->regs.PC >> 20) & 0x0FF](context->regs.PC);

    // Execute it
    opcodes[context->instruction](context);
  }

  context->pre_cycle = context->cycles - target_cycle;
}

//////////////////////////////////////////////////////////////////////////////

void SH2InterpreterGetRegisters(SH2_struct *context, sh2regs_struct *regs) {
  memcpy(regs, &context->regs, sizeof(sh2regs_struct));
}

//////////////////////////////////////////////////////////////////////////////

u32 SH2InterpreterGetGPR(SH2_struct *context, int num) {
  return context->regs.R[num];
}

//////////////////////////////////////////////////////////////////////////////

u32 SH2InterpreterGetSR(SH2_struct *context) { return context->regs.SR.all; }

//////////////////////////////////////////////////////////////////////////////

u32 SH2InterpreterGetGBR(SH2_struct *context) { return context->regs.GBR; }

//////////////////////////////////////////////////////////////////////////////

u32 SH2InterpreterGetVBR(SH2_struct *context) { return context->regs.VBR; }

//////////////////////////////////////////////////////////////////////////////

u32 SH2InterpreterGetMACH(SH2_struct *context) { return context->regs.MACH; }

//////////////////////////////////////////////////////////////////////////////

u32 SH2InterpreterGetMACL(SH2_struct *context) { return context->regs.MACL; }

//////////////////////////////////////////////////////////////////////////////

u32 SH2InterpreterGetPR(SH2_struct *context) { return context->regs.PR; }

//////////////////////////////////////////////////////////////////////////////

u32 SH2InterpreterGetPC(SH2_struct *context) { return context->regs.PC; }

//////////////////////////////////////////////////////////////////////////////

void SH2InterpreterSetRegisters(SH2_struct *context,
                                const sh2regs_struct *regs) {
  memcpy(&context->regs, regs, sizeof(sh2regs_struct));
}

//////////////////////////////////////////////////////////////////////////////

void SH2InterpreterSetGPR(SH2_struct *context, int num, u32 value) {
  context->regs.R[num] = value;
}

//////////////////////////////////////////////////////////////////////////////

void SH2InterpreterSetSR(SH2_struct *context, u32 value) {
  context->regs.SR.all = value;
}

//////////////////////////////////////////////////////////////////////////////

void SH2InterpreterSetGBR(SH2_struct *context, u32 value) {
  context->regs.GBR = value;
}

//////////////////////////////////////////////////////////////////////////////

void SH2InterpreterSetVBR(SH2_struct *context, u32 value) {
  context->regs.VBR = value;
}

//////////////////////////////////////////////////////////////////////////////

void SH2InterpreterSetMACH(SH2_struct *context, u32 value) {
  context->regs.MACH = value;
}

//////////////////////////////////////////////////////////////////////////////

void SH2InterpreterSetMACL(SH2_struct *context, u32 value) {
  context->regs.MACL = value;
}

//////////////////////////////////////////////////////////////////////////////

void SH2InterpreterSetPR(SH2_struct *context, u32 value) {
  context->regs.PR = value;
}

//////////////////////////////////////////////////////////////////////////////

void SH2InterpreterSetPC(SH2_struct *context, u32 value) {
  context->regs.PC = value;
}

void SH2InterpreterAddCycle(SH2_struct *context, u32 value) {
  context->cycles += value;
}

//////////////////////////////////////////////////////////////////////////////

void SH2InterpreterSendInterrupt(SH2_struct *context, u8 vector, u8 level) {
  u32 i, i2;
  interrupt_struct tmp;

  // Make sure interrupt doesn't already exist
  for (i = 0; i < context->NumberOfInterrupts; i++) {
    if (context->interrupts[i].vector == vector)
      return;
  }

  // Ignore Timer0 and Timer1 when masked
  // if ((vector == 67 /*|| vector == 68*/) && level <=
  // context->regs.SR.part.I){
  //  return;
  //}

  context->interrupts[context->NumberOfInterrupts].level = level;
  context->interrupts[context->NumberOfInterrupts].vector = vector;
  context->NumberOfInterrupts++;

  // Sort interrupts
  for (i = 0; i < (context->NumberOfInterrupts - 1); i++) {
    for (i2 = i + 1; i2 < context->NumberOfInterrupts; i2++) {
      if (context->interrupts[i].level > context->interrupts[i2].level) {
        tmp.level = context->interrupts[i].level;
        tmp.vector = context->interrupts[i].vector;
        context->interrupts[i].level = context->interrupts[i2].level;
        context->interrupts[i].vector = context->interrupts[i2].vector;
        context->interrupts[i2].level = tmp.level;
        context->interrupts[i2].vector = tmp.vector;
      }
    }
  }
}

void SH2InterpreterRemoveInterrupt(SH2_struct *context, u8 vector, u8 level) {
  u32 i, i2;
  interrupt_struct tmp;
  int hit = -1;

  for (i = 0; i < context->NumberOfInterrupts; i++) {
    if (context->interrupts[i].vector == vector) {
      context->interrupts[i].level = 0;
      context->interrupts[i].vector = 0;
      hit = i;
      break;
    }
  }

  if (hit != -1) {
    i2 = 0;
    for (i = 0; i < context->NumberOfInterrupts; i++) {
      if (i != hit) {
        context->interrupts[i2].level = context->interrupts[i].level;
        context->interrupts[i2].vector = context->interrupts[i].vector;
        i2++;
      }
    }
    context->NumberOfInterrupts--;
  }
}

//////////////////////////////////////////////////////////////////////////////

int SH2InterpreterGetInterrupts(SH2_struct *context,
                                interrupt_struct interrupts[MAX_INTERRUPTS]) {
  memcpy(interrupts, context->interrupts,
         sizeof(interrupt_struct) * MAX_INTERRUPTS);
  return context->NumberOfInterrupts;
}

//////////////////////////////////////////////////////////////////////////////

void SH2InterpreterSetInterrupts(
    SH2_struct *context, int num_interrupts,
    const interrupt_struct interrupts[MAX_INTERRUPTS]) {
  memcpy(context->interrupts, interrupts,
         sizeof(interrupt_struct) * MAX_INTERRUPTS);
  context->NumberOfInterrupts = num_interrupts;
}

//////////////////////////////////////////////////////////////////////////////
