// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
// Copyright (C) 1999-2003 Forgotten
// Copyright (C) 2004 Forgotten and the VBA development team

// This program 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, or(at your option)
// any later version.
//
// This program 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 this program; if not, write to the Free Software Foundation,
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <stdarg.h>
#include <string.h>

#include "global.h"
#include "GBA.h"
#include "GBAinline.h"
#include "Globals.h"
#include "Gfx.h"
#include "EEprom.h"
#include "Flash.h"
#include "Sound.h"
#include "Sram.h"
#include "bios.h"
#include "unzip.h"
#include "Cheats.h"
#include "NLS.h"
#include "elf.h"
#include "Util.h"
#include "Port.h"

extern u8 cpuBitsSet[256];
extern u8 cpuLowestBitSet[256];
extern int memoryWaitFetch32[16];
extern const int thumbCycles[];
extern int memoryWaitFetch[16];
extern int memoryWait32[16];
extern int memoryWait[16];
extern int memoryWaitSeq32[16];
extern int memoryWaitFetch[16];
extern int memoryWaitFetch32[16];
extern const int cpuMemoryWait[16];
extern int memoryWaitSeq[16];

inline int CPUUpdateTicksAccess32(u32 address)
{
  return memoryWait32[(address>>24)&15];
}

inline int CPUUpdateTicksAccess16(u32 address)
{
  return memoryWait[(address>>24)&15];
}

inline int CPUUpdateTicksAccessSeq32(u32 address)
{
  return memoryWaitSeq32[(address>>24)&15];
}

inline int CPUUpdateTicksAccessSeq16(u32 address)
{
  return memoryWaitSeq[(address>>24)&15];
}

#define UPDATE_REG(address, value) WRITE16LE(((u16 *)&ioMem[address]),value)

#ifdef BKPT_SUPPORT
#define CONSOLE_OUTPUT(a,b) \
    extern void (*dbgOutput)(char *, u32);\
    if((opcode == 0xe0000000) && (reg[0].I == 0xC0DED00D)) {\
      dbgOutput((a), (b));\
    }
#else
#define CONSOLE_OUTPUT(a,b)
#endif

#define OP_AND \
      reg[dest].I = reg[(opcode>>16)&15].I & value;\
      CONSOLE_OUTPUT(NULL,reg[2].I);

#define OP_ANDS \
      reg[dest].I = reg[(opcode>>16)&15].I & value;\
      \
      N_FLAG = (reg[dest].I & 0x80000000) ? true : false;\
      Z_FLAG = (reg[dest].I) ? false : true;\
      C_FLAG = C_OUT;

#define OP_EOR \
      reg[dest].I = reg[(opcode>>16)&15].I ^ value;

#define OP_EORS \
      reg[dest].I = reg[(opcode>>16)&15].I ^ value;\
      \
      N_FLAG = (reg[dest].I & 0x80000000) ? true : false;\
      Z_FLAG = (reg[dest].I) ? false : true;\
      C_FLAG = C_OUT;
#ifdef C_CORE
#define NEG(i) ((i) >> 31)
#define POS(i) ((~(i)) >> 31)
#define ADDCARRY(a, b, c) \
  C_FLAG = ((NEG(a) & NEG(b)) |\
            (NEG(a) & POS(c)) |\
            (NEG(b) & POS(c))) ? true : false;
#define ADDOVERFLOW(a, b, c) \
  V_FLAG = ((NEG(a) & NEG(b) & POS(c)) |\
            (POS(a) & POS(b) & NEG(c))) ? true : false;
#define SUBCARRY(a, b, c) \
  C_FLAG = ((NEG(a) & POS(b)) |\
            (NEG(a) & POS(c)) |\
            (POS(b) & POS(c))) ? true : false;
#define SUBOVERFLOW(a, b, c)\
  V_FLAG = ((NEG(a) & POS(b) & POS(c)) |\
            (POS(a) & NEG(b) & NEG(c))) ? true : false;
#define OP_SUB \
    {\
      reg[dest].I = reg[base].I - value;\
    }
#define OP_SUBS \
   {\
     u32 lhs = reg[base].I;\
     u32 rhs = value;\
     u32 res = lhs - rhs;\
     reg[dest].I = res;\
     Z_FLAG = (res == 0) ? true : false;\
     N_FLAG = NEG(res) ? true : false;\
     SUBCARRY(lhs, rhs, res);\
     SUBOVERFLOW(lhs, rhs, res);\
   }
#define OP_RSB \
    {\
      reg[dest].I = value - reg[base].I;\
    }
#define OP_RSBS \
   {\
     u32 lhs = reg[base].I;\
     u32 rhs = value;\
     u32 res = rhs - lhs;\
     reg[dest].I = res;\
     Z_FLAG = (res == 0) ? true : false;\
     N_FLAG = NEG(res) ? true : false;\
     SUBCARRY(rhs, lhs, res);\
     SUBOVERFLOW(rhs, lhs, res);\
   }
#define OP_ADD \
    {\
      reg[dest].I = reg[base].I + value;\
    }
#define OP_ADDS \
   {\
     u32 lhs = reg[base].I;\
     u32 rhs = value;\
     u32 res = lhs + rhs;\
     reg[dest].I = res;\
     Z_FLAG = (res == 0) ? true : false;\
     N_FLAG = NEG(res) ? true : false;\
     ADDCARRY(lhs, rhs, res);\
     ADDOVERFLOW(lhs, rhs, res);\
   }
#define OP_ADC \
    {\
      reg[dest].I = reg[base].I + value + (u32)C_FLAG;\
    }
#define OP_ADCS \
   {\
     u32 lhs = reg[base].I;\
     u32 rhs = value;\
     u32 res = lhs + rhs + (u32)C_FLAG;\
     reg[dest].I = res;\
     Z_FLAG = (res == 0) ? true : false;\
     N_FLAG = NEG(res) ? true : false;\
     ADDCARRY(lhs, rhs, res);\
     ADDOVERFLOW(lhs, rhs, res);\
   }
#define OP_SBC \
    {\
      reg[dest].I = reg[base].I - value - !((u32)C_FLAG);\
    }
#define OP_SBCS \
   {\
     u32 lhs = reg[base].I;\
     u32 rhs = value;\
     u32 res = lhs - rhs - !((u32)C_FLAG);\
     reg[dest].I = res;\
     Z_FLAG = (res == 0) ? true : false;\
     N_FLAG = NEG(res) ? true : false;\
     SUBCARRY(lhs, rhs, res);\
     SUBOVERFLOW(lhs, rhs, res);\
   }
#define OP_RSC \
    {\
      reg[dest].I = value - reg[base].I - !((u32)C_FLAG);\
    }
#define OP_RSCS \
   {\
     u32 lhs = reg[base].I;\
     u32 rhs = value;\
     u32 res = rhs - lhs - !((u32)C_FLAG);\
     reg[dest].I = res;\
     Z_FLAG = (res == 0) ? true : false;\
     N_FLAG = NEG(res) ? true : false;\
     SUBCARRY(rhs, lhs, res);\
     SUBOVERFLOW(rhs, lhs, res);\
   }
#define OP_CMP \
   {\
     u32 lhs = reg[base].I;\
     u32 rhs = value;\
     u32 res = lhs - rhs;\
     Z_FLAG = (res == 0) ? true : false;\
     N_FLAG = NEG(res) ? true : false;\
     SUBCARRY(lhs, rhs, res);\
     SUBOVERFLOW(lhs, rhs, res);\
   }
#define OP_CMN \
   {\
     u32 lhs = reg[base].I;\
     u32 rhs = value;\
     u32 res = lhs + rhs;\
     Z_FLAG = (res == 0) ? true : false;\
     N_FLAG = NEG(res) ? true : false;\
     ADDCARRY(lhs, rhs, res);\
     ADDOVERFLOW(lhs, rhs, res);\
   }

#define LOGICAL_LSL_REG \
   {\
     u32 v = reg[opcode & 0x0f].I;\
     C_OUT = (v >> (32 - shift)) & 1 ? true : false;\
     value = v << shift;\
   }
#define LOGICAL_LSR_REG \
   {\
     u32 v = reg[opcode & 0x0f].I;\
     C_OUT = (v >> (shift - 1)) & 1 ? true : false;\
     value = v >> shift;\
   }
#define LOGICAL_ASR_REG \
   {\
     u32 v = reg[opcode & 0x0f].I;\
     C_OUT = ((s32)v >> (int)(shift - 1)) & 1 ? true : false;\
     value = (s32)v >> (int)shift;\
   }
#define LOGICAL_ROR_REG \
   {\
     u32 v = reg[opcode & 0x0f].I;\
     C_OUT = (v >> (shift - 1)) & 1 ? true : false;\
     value = ((v << (32 - shift)) |\
              (v >> shift));\
   }
#define LOGICAL_RRX_REG \
   {\
     u32 v = reg[opcode & 0x0f].I;\
     shift = (int)C_FLAG;\
     C_OUT = (v  & 1) ? true : false;\
     value = ((v >> 1) |\
              (shift << 31));\
   }
#define LOGICAL_ROR_IMM \
   {\
     u32 v = opcode & 0xff;\
     C_OUT = (v >> (shift - 1)) & 1 ? true : false;\
     value = ((v << (32 - shift)) |\
              (v >> shift));\
   }
#define ARITHMETIC_LSL_REG \
   {\
     u32 v = reg[opcode & 0x0f].I;\
     value = v << shift;\
   }
#define ARITHMETIC_LSR_REG \
   {\
     u32 v = reg[opcode & 0x0f].I;\
     value = v >> shift;\
   }
#define ARITHMETIC_ASR_REG \
   {\
     u32 v = reg[opcode & 0x0f].I;\
     value = (s32)v >> (int)shift;\
   }
#define ARITHMETIC_ROR_REG \
   {\
     u32 v = reg[opcode & 0x0f].I;\
     value = ((v << (32 - shift)) |\
              (v >> shift));\
   }
#define ARITHMETIC_RRX_REG \
   {\
     u32 v = reg[opcode & 0x0f].I;\
     shift = (int)C_FLAG;\
     value = ((v >> 1) |\
              (shift << 31));\
   }
#define ARITHMETIC_ROR_IMM \
   {\
     u32 v = opcode & 0xff;\
     value = ((v << (32 - shift)) |\
              (v >> shift));\
   }
#define ROR_IMM_MSR \
   {\
     u32 v = opcode & 0xff;\
     value = ((v << (32 - shift)) |\
              (v >> shift));\
   }
#define ROR_VALUE \
   {\
     value = ((value << (32 - shift)) |\
              (value >> shift));\
   }
#define RCR_VALUE \
   {\
     shift = (int)C_FLAG;\
     value = ((value >> 1) |\
              (shift << 31));\
   }
#else
#ifdef __GNUC__
        #ifdef __POWERPC__
            #define OP_SUB \
                {\
                reg[dest].I = reg[base].I - value;\
                }
            #define OP_SUBS \
            {\
                register int Flags;                             \
                register int Result;                            \
                asm volatile("subco. %0, %2, %3\n"              \
                            "mcrxr cr1\n"                       \
                            "mfcr %1\n"                         \
                            : "=r" (Result),                    \
                              "=r" (Flags)                      \
                            : "r" (reg[base].I),                \
                              "r" (value)                       \
                            );                                  \
                reg[dest].I = Result;                           \
                Z_FLAG = (Flags >> 29) & 1;                     \
                N_FLAG = (Flags >> 31) & 1;                     \
                C_FLAG = (Flags >> 25) & 1;                     \
                V_FLAG = (Flags >> 26) & 1;                     \
            }
            #define OP_RSB \
                {\
                reg[dest].I = value - reg[base].I;\
                }
            #define OP_RSBS \
            {\
                register int Flags;                             \
                register int Result;                            \
                asm volatile("subfco. %0, %2, %3\n"             \
                            "mcrxr cr1\n"                       \
                            "mfcr %1\n"                         \
                            : "=r" (Result),                    \
                              "=r" (Flags)                      \
                            : "r" (reg[base].I),                \
                              "r" (value)                       \
                            );                                  \
                reg[dest].I = Result;                           \
                Z_FLAG = (Flags >> 29) & 1;                     \
                N_FLAG = (Flags >> 31) & 1;                     \
                C_FLAG = (Flags >> 25) & 1;                     \
                V_FLAG = (Flags >> 26) & 1;                     \
            }
            #define OP_ADD \
                {\
                reg[dest].I = reg[base].I + value;\
                }

            #define OP_ADDS \
            {\
                register int Flags;                             \
                register int Result;                            \
                asm volatile("addco. %0, %2, %3\n"              \
                            "mcrxr cr1\n"                       \
                            "mfcr %1\n"                         \
                            : "=r" (Result),                    \
                              "=r" (Flags)                      \
                            : "r" (reg[base].I),                \
                              "r" (value)                       \
                            );                                  \
                reg[dest].I = Result;                           \
                Z_FLAG = (Flags >> 29) & 1;                     \
                N_FLAG = (Flags >> 31) & 1;                     \
                C_FLAG = (Flags >> 25) & 1;                     \
                V_FLAG = (Flags >> 26) & 1;                     \
            }
            #define OP_ADC \
            {\
                reg[dest].I = reg[base].I + value + (u32)C_FLAG;\
            }
            #define OP_ADCS \
            {\
                register int Flags;                             \
                register int Result;                            \
                asm volatile("mtspr xer, %4\n"                  \
                             "addeo. %0, %2, %3\n"              \
                             "mcrxr cr1\n"                      \
                             "mfcr      %1\n"                   \
                             : "=r" (Result),                   \
                               "=r" (Flags)                     \
                             : "r" (reg[base].I),               \
                               "r" (value),                     \
                               "r" (C_FLAG << 29)               \
                             );                                 \
                reg[dest].I = Result;                           \
                Z_FLAG = (Flags >> 29) & 1;                     \
                N_FLAG = (Flags >> 31) & 1;                     \
                C_FLAG = (Flags >> 25) & 1;                     \
                V_FLAG = (Flags >> 26) & 1;                     \
            }
            #define OP_SBC \
                {\
                reg[dest].I = reg[base].I - value - (C_FLAG^1);\
                }
            #define OP_SBCS \
            {\
                register int Flags;                             \
                register int Result;                            \
                asm volatile("mtspr xer, %4\n"                  \
                             "subfeo. %0, %3, %2\n"             \
                             "mcrxr cr1\n"                      \
                             "mfcr      %1\n"                   \
                             : "=r" (Result),                   \
                               "=r" (Flags)                     \
                             : "r" (reg[base].I),               \
                               "r" (value),                     \
                               "r" (C_FLAG << 29)               \
                             );                                 \
                reg[dest].I = Result;                           \
                Z_FLAG = (Flags >> 29) & 1;                     \
                N_FLAG = (Flags >> 31) & 1;                     \
                C_FLAG = (Flags >> 25) & 1;                     \
                V_FLAG = (Flags >> 26) & 1;                     \
            }
            #define OP_RSC \
                {\
                reg[dest].I = value - reg[base].I - (C_FLAG^1);\
                }
            #define OP_RSCS \
            {\
                register int Flags;                             \
                register int Result;                            \
                asm volatile("mtspr xer, %4\n"                  \
                             "subfeo. %0, %2, %3\n"             \
                             "mcrxr cr1\n"                      \
                             "mfcr      %1\n"                   \
                             : "=r" (Result),                   \
                               "=r" (Flags)                     \
                             : "r" (reg[base].I),               \
                               "r" (value),                     \
                               "r" (C_FLAG << 29)               \
                             );                                 \
                reg[dest].I = Result;                           \
                Z_FLAG = (Flags >> 29) & 1;                     \
                N_FLAG = (Flags >> 31) & 1;                     \
                C_FLAG = (Flags >> 25) & 1;                     \
                V_FLAG = (Flags >> 26) & 1;                     \
            }
            #define OP_CMP \
            {\
                register int Flags;                             \
                register int Result;                            \
                asm volatile("subco. %0, %2, %3\n"              \
                            "mcrxr cr1\n"                       \
                            "mfcr %1\n"                         \
                            : "=r" (Result),                    \
                              "=r" (Flags)                      \
                            : "r" (reg[base].I),                \
                              "r" (value)                       \
                            );                                  \
                Z_FLAG = (Flags >> 29) & 1;                     \
                N_FLAG = (Flags >> 31) & 1;                     \
                C_FLAG = (Flags >> 25) & 1;                     \
                V_FLAG = (Flags >> 26) & 1;                     \
            }
            #define OP_CMN \
            {\
                register int Flags;                             \
                register int Result;                            \
                asm volatile("addco. %0, %2, %3\n"              \
                            "mcrxr cr1\n"                       \
                            "mfcr %1\n"                         \
                            : "=r" (Result),                    \
                              "=r" (Flags)                      \
                            : "r" (reg[base].I),                \
                              "r" (value)                       \
                            );                                  \
                Z_FLAG = (Flags >> 29) & 1;                     \
                N_FLAG = (Flags >> 31) & 1;                     \
                C_FLAG = (Flags >> 25) & 1;                     \
                V_FLAG = (Flags >> 26) & 1;                     \
            }
            
            #define LOGICAL_LSL_REG \
            {\
                u32 v = reg[opcode & 0x0f].I;\
                C_OUT = (v >> (32 - shift)) & 1 ? true : false;\
                value = v << shift;\
            }
            #define LOGICAL_LSR_REG \
            {\
                u32 v = reg[opcode & 0x0f].I;\
                C_OUT = (v >> (shift - 1)) & 1 ? true : false;\
                value = v >> shift;\
            }
            #define LOGICAL_ASR_REG \
            {\
                u32 v = reg[opcode & 0x0f].I;\
                C_OUT = ((s32)v >> (int)(shift - 1)) & 1 ? true : false;\
                value = (s32)v >> (int)shift;\
            }
            #define LOGICAL_ROR_REG \
            {\
                u32 v = reg[opcode & 0x0f].I;\
                C_OUT = (v >> (shift - 1)) & 1 ? true : false;\
                value = ((v << (32 - shift)) |\
                        (v >> shift));\
            }
            #define LOGICAL_RRX_REG \
            {\
                u32 v = reg[opcode & 0x0f].I;\
                shift = (int)C_FLAG;\
                C_OUT = (v  & 1) ? true : false;\
                value = ((v >> 1) |\
                        (shift << 31));\
            }
            #define LOGICAL_ROR_IMM \
            {\
                u32 v = opcode & 0xff;\
                C_OUT = (v >> (shift - 1)) & 1 ? true : false;\
                value = ((v << (32 - shift)) |\
                        (v >> shift));\
            }
            #define ARITHMETIC_LSL_REG \
            {\
                u32 v = reg[opcode & 0x0f].I;\
                value = v << shift;\
            }
            #define ARITHMETIC_LSR_REG \
            {\
                u32 v = reg[opcode & 0x0f].I;\
                value = v >> shift;\
            }
            #define ARITHMETIC_ASR_REG \
            {\
                u32 v = reg[opcode & 0x0f].I;\
                value = (s32)v >> (int)shift;\
            }
            #define ARITHMETIC_ROR_REG \
            {\
                u32 v = reg[opcode & 0x0f].I;\
                value = ((v << (32 - shift)) |\
                        (v >> shift));\
            }
            #define ARITHMETIC_RRX_REG \
            {\
                u32 v = reg[opcode & 0x0f].I;\
                shift = (int)C_FLAG;\
                value = ((v >> 1) |\
                        (shift << 31));\
            }
            #define ARITHMETIC_ROR_IMM \
            {\
                u32 v = opcode & 0xff;\
                value = ((v << (32 - shift)) |\
                        (v >> shift));\
            }
            #define ROR_IMM_MSR \
            {\
                u32 v = opcode & 0xff;\
                value = ((v << (32 - shift)) |\
                        (v >> shift));\
            }
            #define ROR_VALUE \
            {\
                value = ((value << (32 - shift)) |\
                        (value >> shift));\
            }
            #define RCR_VALUE \
            {\
                shift = (int)C_FLAG;\
                value = ((value >> 1) |\
                        (shift << 31));\
            }
#else
#define OP_SUB \
     asm ("sub %1, %%ebx;"\
                  : "=b" (reg[dest].I)\
                  : "r" (value), "b" (reg[base].I));

#define OP_SUBS \
     asm ("sub %1, %%ebx;"\
          "setsb N_FLAG;"\
          "setzb Z_FLAG;"\
          "setncb C_FLAG;"\
          "setob V_FLAG;"\
                  : "=b" (reg[dest].I)\
                  : "r" (value), "b" (reg[base].I));

#define OP_RSB \
            asm  ("sub %1, %%ebx;"\
                 : "=b" (reg[dest].I)\
                 : "r" (reg[base].I), "b" (value));

#define OP_RSBS \
            asm  ("sub %1, %%ebx;"\
                  "setsb N_FLAG;"\
                  "setzb Z_FLAG;"\
                  "setncb C_FLAG;"\
                  "setob V_FLAG;"\
                 : "=b" (reg[dest].I)\
                 : "r" (reg[base].I), "b" (value));

#define OP_ADD \
            asm  ("add %1, %%ebx;"\
                 : "=b" (reg[dest].I)\
                 : "r" (value), "b" (reg[base].I));

#define OP_ADDS \
            asm  ("add %1, %%ebx;"\
                  "setsb N_FLAG;"\
                  "setzb Z_FLAG;"\
                  "setcb C_FLAG;"\
                  "setob V_FLAG;"\
                 : "=b" (reg[dest].I)\
                 : "r" (value), "b" (reg[base].I));

#define OP_ADC \
            asm  ("bt $0, C_FLAG;"\
                  "adc %1, %%ebx;"\
                 : "=b" (reg[dest].I)\
                 : "r" (value), "b" (reg[base].I));

#define OP_ADCS \
            asm  ("bt $0, C_FLAG;"\
                  "adc %1, %%ebx;"\
                  "setsb N_FLAG;"\
                  "setzb Z_FLAG;"\
                  "setcb C_FLAG;"\
                  "setob V_FLAG;"\
                 : "=b" (reg[dest].I)\
                 : "r" (value), "b" (reg[base].I));

#define OP_SBC \
            asm  ("bt $0, C_FLAG;"\
                  "cmc;"\
                  "sbb %1, %%ebx;"\
                 : "=b" (reg[dest].I)\
                 : "r" (value), "b" (reg[base].I));

#define OP_SBCS \
            asm  ("bt $0, C_FLAG;"\
                  "cmc;"\
                  "sbb %1, %%ebx;"\
                  "setsb N_FLAG;"\
                  "setzb Z_FLAG;"\
                  "setncb C_FLAG;"\
                  "setob V_FLAG;"\
                 : "=b" (reg[dest].I)\
                 : "r" (value), "b" (reg[base].I));
#define OP_RSC \
            asm  ("bt $0, C_FLAG;"\
                  "cmc;"\
                  "sbb %1, %%ebx;"\
                 : "=b" (reg[dest].I)\
                 : "r" (reg[base].I), "b" (value));

#define OP_RSCS \
            asm  ("bt $0, C_FLAG;"\
                  "cmc;"\
                  "sbb %1, %%ebx;"\
                  "setsb N_FLAG;"\
                  "setzb Z_FLAG;"\
                  "setncb C_FLAG;"\
                  "setob V_FLAG;"\
                 : "=b" (reg[dest].I)\
                 : "r" (reg[base].I), "b" (value));
#define OP_CMP \
            asm  ("sub %0, %1;"\
                  "setsb N_FLAG;"\
                  "setzb Z_FLAG;"\
                  "setncb C_FLAG;"\
                  "setob V_FLAG;"\
                 :\
                 : "r" (value), "r" (reg[base].I));

#define OP_CMN \
            asm  ("add %0, %1;"\
                  "setsb N_FLAG;"\
                  "setzb Z_FLAG;"\
                  "setcb C_FLAG;"\
                  "setob V_FLAG;"\
                 : \
                 : "r" (value), "r" (reg[base].I));
#define LOGICAL_LSL_REG \
       asm("shl %%cl, %%eax;"\
           "setcb %%cl;"\
           : "=a" (value), "=c" (C_OUT)\
           : "a" (reg[opcode & 0x0f].I), "c" (shift));

#define LOGICAL_LSR_REG \
       asm("shr %%cl, %%eax;"\
           "setcb %%cl;"\
           : "=a" (value), "=c" (C_OUT)\
           : "a" (reg[opcode & 0x0f].I), "c" (shift));

#define LOGICAL_ASR_REG \
       asm("sar %%cl, %%eax;"\
           "setcb %%cl;"\
           : "=a" (value), "=c" (C_OUT)\
           : "a" (reg[opcode & 0x0f].I), "c" (shift));

#define LOGICAL_ROR_REG \
       asm("ror %%cl, %%eax;"\
           "setcb %%cl;"\
           : "=a" (value), "=c" (C_OUT)\
           : "a" (reg[opcode & 0x0f].I), "c" (shift));       

#define LOGICAL_RRX_REG \
       asm("bt $0, C_FLAG;"\
           "rcr $1, %%eax;"\
           "setcb %%cl;"\
           : "=a" (value), "=c" (C_OUT)\
           : "a" (reg[opcode & 0x0f].I));       

#define LOGICAL_ROR_IMM \
       asm("ror %%cl, %%eax;"\
           "setcb %%cl;"\
           : "=a" (value), "=c" (C_OUT)\
           : "a" (opcode & 0xff), "c" (shift));
#define ARITHMETIC_LSL_REG \
       asm("\
             shl %%cl, %%eax;"\
           : "=a" (value)\
           : "a" (reg[opcode & 0x0f].I), "c" (shift));

#define ARITHMETIC_LSR_REG \
       asm("\
             shr %%cl, %%eax;"\
           : "=a" (value)\
           : "a" (reg[opcode & 0x0f].I), "c" (shift));

#define ARITHMETIC_ASR_REG \
       asm("\
             sar %%cl, %%eax;"\
           : "=a" (value)\
           : "a" (reg[opcode & 0x0f].I), "c" (shift));

#define ARITHMETIC_ROR_REG \
       asm("\
             ror %%cl, %%eax;"\
           : "=a" (value)\
           : "a" (reg[opcode & 0x0f].I), "c" (shift));       

#define ARITHMETIC_RRX_REG \
       asm("\
             bt $0, C_FLAG;\
             rcr $1, %%eax;"\
           : "=a" (value)\
           : "a" (reg[opcode & 0x0f].I));       

#define ARITHMETIC_ROR_IMM \
       asm("\
             ror %%cl, %%eax;"\
           : "=a" (value)\
           : "a" (opcode & 0xff), "c" (shift));
#define ROR_IMM_MSR \
      asm ("ror %%cl, %%eax;"\
           : "=a" (value)\
           : "a" (opcode & 0xFF), "c" (shift));
#define ROR_VALUE \
      asm("ror %%cl, %0"\
          : "=r" (value)\
          : "r" (value), "c" (shift));
#define RCR_VALUE \
      asm("bt $0, C_FLAG;"\
          "rcr $1, %0"\
          : "=r" (value)\
          : "r" (value));
#endif
#else
#define OP_SUB \
      {\
        __asm mov ebx, base\
        __asm mov ebx, dword ptr [OFFSET reg+4*ebx]\
        __asm sub ebx, value\
        __asm mov eax, dest\
        __asm mov dword ptr [OFFSET reg+4*eax], ebx\
      }

#define OP_SUBS \
      {\
        __asm mov ebx, base\
        __asm mov ebx, dword ptr [OFFSET reg+4*ebx]\
        __asm sub ebx, value\
        __asm mov eax, dest\
        __asm mov dword ptr [OFFSET reg+4*eax], ebx\
        __asm sets byte ptr N_FLAG\
        __asm setz byte ptr Z_FLAG\
        __asm setnc byte ptr C_FLAG\
        __asm seto byte ptr V_FLAG\
      }

#define OP_RSB \
      {\
        __asm mov ebx, base\
        __asm mov ebx, dword ptr [OFFSET reg+4*ebx]\
        __asm mov eax, value\
        __asm sub eax, ebx\
        __asm mov ebx, dest\
        __asm mov dword ptr [OFFSET reg+4*ebx], eax\
      }

#define OP_RSBS \
      {\
        __asm mov ebx, base\
        __asm mov ebx, dword ptr [OFFSET reg+4*ebx]\
        __asm mov eax, value\
        __asm sub eax, ebx\
        __asm mov ebx, dest\
        __asm mov dword ptr [OFFSET reg+4*ebx], eax\
        __asm sets byte ptr N_FLAG\
        __asm setz byte ptr Z_FLAG\
        __asm setnc byte ptr C_FLAG\
        __asm seto byte ptr V_FLAG\
      }

#define OP_ADD \
      {\
        __asm mov ebx, base\
        __asm mov ebx, dword ptr [OFFSET reg+4*ebx]\
        __asm add ebx, value\
        __asm mov eax, dest\
        __asm mov dword ptr [OFFSET reg+4*eax], ebx\
      }

#define OP_ADDS \
      {\
        __asm mov ebx, base\
        __asm mov ebx, dword ptr [OFFSET reg+4*ebx]\
        __asm add ebx, value\
        __asm mov eax, dest\
        __asm mov dword ptr [OFFSET reg+4*eax], ebx\
        __asm sets byte ptr N_FLAG\
        __asm setz byte ptr Z_FLAG\
        __asm setc byte ptr C_FLAG\
        __asm seto byte ptr V_FLAG\
      }

#define OP_ADC \
      {\
        __asm mov ebx, base\
        __asm mov ebx, dword ptr [OFFSET reg+4*ebx]\
        __asm bt word ptr C_FLAG, 0\
        __asm adc ebx, value\
        __asm mov eax, dest\
        __asm mov dword ptr [OFFSET reg+4*eax], ebx\
      }

#define OP_ADCS \
      {\
        __asm mov ebx, base\
        __asm mov ebx, dword ptr [OFFSET reg+4*ebx]\
        __asm bt word ptr C_FLAG, 0\
        __asm adc ebx, value\
        __asm mov eax, dest\
        __asm mov dword ptr [OFFSET reg+4*eax], ebx\
        __asm sets byte ptr N_FLAG\
        __asm setz byte ptr Z_FLAG\
        __asm setc byte ptr C_FLAG\
        __asm seto byte ptr V_FLAG\
      }

#define OP_SBC \
      {\
        __asm mov ebx, base\
        __asm mov ebx, dword ptr [OFFSET reg + 4*ebx]\
        __asm mov eax, value\
        __asm bt word ptr C_FLAG, 0\
        __asm cmc\
        __asm sbb ebx, eax\
        __asm mov eax, dest\
        __asm mov dword ptr [OFFSET reg + 4*eax], ebx\
      }

#define OP_SBCS \
      {\
        __asm mov ebx, base\
        __asm mov ebx, dword ptr [OFFSET reg + 4*ebx]\
        __asm mov eax, value\
        __asm bt word ptr C_FLAG, 0\
        __asm cmc\
        __asm sbb ebx, eax\
        __asm mov eax, dest\
        __asm mov dword ptr [OFFSET reg + 4*eax], ebx\
        __asm sets byte ptr N_FLAG\
        __asm setz byte ptr Z_FLAG\
        __asm setnc byte ptr C_FLAG\
        __asm seto byte ptr V_FLAG\
      }
#define OP_RSC \
      {\
        __asm mov ebx, value\
        __asm mov eax, base\
        __asm mov eax, dword ptr[OFFSET reg + 4*eax]\
        __asm bt word ptr C_FLAG, 0\
        __asm cmc\
        __asm sbb ebx, eax\
        __asm mov eax, dest\
        __asm mov dword ptr [OFFSET reg + 4*eax], ebx\
      }

#define OP_RSCS \
      {\
        __asm mov ebx, value\
        __asm mov eax, base\
        __asm mov eax, dword ptr[OFFSET reg + 4*eax]\
        __asm bt word ptr C_FLAG, 0\
        __asm cmc\
        __asm sbb ebx, eax\
        __asm mov eax, dest\
        __asm mov dword ptr [OFFSET reg + 4*eax], ebx\
        __asm sets byte ptr N_FLAG\
        __asm setz byte ptr Z_FLAG\
        __asm setnc byte ptr C_FLAG\
        __asm seto byte ptr V_FLAG\
      }
#define OP_CMP \
     {\
       __asm mov eax, base\
       __asm mov ebx, dword ptr [OFFSET reg+4*eax]\
       __asm sub ebx, value\
       __asm sets byte ptr N_FLAG\
       __asm setz byte ptr Z_FLAG\
       __asm setnc byte ptr C_FLAG\
       __asm seto byte ptr V_FLAG\
     }

#define OP_CMN \
     {\
       __asm mov eax, base\
       __asm mov ebx, dword ptr [OFFSET reg+4*eax]\
       __asm add ebx, value\
       __asm sets byte ptr N_FLAG\
       __asm setz byte ptr Z_FLAG\
       __asm setc byte ptr C_FLAG\
       __asm seto byte ptr V_FLAG\
     }
#define LOGICAL_LSL_REG \
        __asm mov eax, opcode\
        __asm and eax, 0x0f\
        __asm mov eax, dword ptr [OFFSET reg + 4 * eax]\
        __asm mov cl, byte ptr shift\
        __asm shl eax, cl\
        __asm mov value, eax\
        __asm setc byte ptr C_OUT

#define LOGICAL_LSR_REG \
        __asm mov eax, opcode\
        __asm and eax, 0x0f\
        __asm mov eax, dword ptr [OFFSET reg + 4 * eax]\
        __asm mov cl, byte ptr shift\
        __asm shr eax, cl\
        __asm mov value, eax\
        __asm setc byte ptr C_OUT

#define LOGICAL_ASR_REG \
        __asm mov eax, opcode\
        __asm and eax, 0x0f\
        __asm mov eax, dword ptr [OFFSET reg + 4 * eax]\
        __asm mov cl, byte ptr shift\
        __asm sar eax, cl\
        __asm mov value, eax\
        __asm setc byte ptr C_OUT

#define LOGICAL_ROR_REG \
        __asm mov eax, opcode\
        __asm and eax, 0x0F\
        __asm mov eax, dword ptr [OFFSET reg + 4*eax]\
        __asm mov cl, byte ptr shift\
        __asm ror eax, cl\
        __asm mov value, eax\
        __asm setc byte ptr C_OUT

#define LOGICAL_RRX_REG \
        __asm mov eax, opcode\
        __asm and eax, 0x0F\
        __asm mov eax, dword ptr [OFFSET reg + 4*eax]\
        __asm bt word ptr C_OUT, 0\
        __asm rcr eax, 1\
        __asm mov value, eax\
        __asm setc byte ptr C_OUT

#define LOGICAL_ROR_IMM \
        __asm mov eax, opcode\
        __asm and eax, 0xff\
        __asm mov cl, byte ptr shift\
        __asm ror eax, cl\
        __asm mov value, eax\
        __asm setc byte ptr C_OUT
#define ARITHMETIC_LSL_REG \
        __asm mov eax, opcode\
        __asm and eax, 0x0f\
        __asm mov eax, dword ptr [OFFSET reg + 4 * eax]\
        __asm mov cl, byte ptr shift\
        __asm shl eax, cl\
        __asm mov value, eax

#define ARITHMETIC_LSR_REG \
        __asm mov eax, opcode\
        __asm and eax, 0x0f\
        __asm mov eax, dword ptr [OFFSET reg + 4 * eax]\
        __asm mov cl, byte ptr shift\
        __asm shr eax, cl\
        __asm mov value, eax

#define ARITHMETIC_ASR_REG \
        __asm mov eax, opcode\
        __asm and eax, 0x0f\
        __asm mov eax, dword ptr [OFFSET reg + 4 * eax]\
        __asm mov cl, byte ptr shift\
        __asm sar eax, cl\
        __asm mov value, eax

#define ARITHMETIC_ROR_REG \
        __asm mov eax, opcode\
        __asm and eax, 0x0F\
        __asm mov eax, dword ptr [OFFSET reg + 4*eax]\
        __asm mov cl, byte ptr shift\
        __asm ror eax, cl\
        __asm mov value, eax

#define ARITHMETIC_RRX_REG \
        __asm mov eax, opcode\
        __asm and eax, 0x0F\
        __asm mov eax, dword ptr [OFFSET reg + 4*eax]\
        __asm bt word ptr C_FLAG, 0\
        __asm rcr eax, 1\
        __asm mov value, eax

#define ARITHMETIC_ROR_IMM \
        __asm mov eax, opcode\
        __asm and eax, 0xff\
        __asm mov cl, byte ptr shift\
        __asm ror eax, cl\
        __asm mov value, eax
#define ROR_IMM_MSR \
      {\
        __asm mov eax, opcode\
        __asm and eax, 0xff\
        __asm mov cl, byte ptr shift\
        __asm ror eax, CL\
        __asm mov value, eax\
      }
#define ROR_VALUE \
      {\
        __asm mov cl, byte ptr shift\
        __asm ror dword ptr value, cl\
      }
#define RCR_VALUE \
      {\
        __asm mov cl, byte ptr shift\
        __asm bt word ptr C_FLAG, 0\
        __asm rcr dword ptr value, 1\
      }
#endif
#endif

#define OP_TST \
      u32 res = reg[base].I & value;\
      N_FLAG = (res & 0x80000000) ? true : false;\
      Z_FLAG = (res) ? false : true;\
      C_FLAG = C_OUT;

#define OP_TEQ \
      u32 res = reg[base].I ^ value;\
      N_FLAG = (res & 0x80000000) ? true : false;\
      Z_FLAG = (res) ? false : true;\
      C_FLAG = C_OUT;

#define OP_ORR \
    reg[dest].I = reg[base].I | value;

#define OP_ORRS \
    reg[dest].I = reg[base].I | value;\
    N_FLAG = (reg[dest].I & 0x80000000) ? true : false;\
    Z_FLAG = (reg[dest].I) ? false : true;\
    C_FLAG = C_OUT;

#define OP_MOV \
    reg[dest].I = value;

#define OP_MOVS \
    reg[dest].I = value;\
    N_FLAG = (reg[dest].I & 0x80000000) ? true : false;\
    Z_FLAG = (reg[dest].I) ? false : true;\
    C_FLAG = C_OUT;

#define OP_BIC \
    reg[dest].I = reg[base].I & (~value);

#define OP_BICS \
    reg[dest].I = reg[base].I & (~value);\
    N_FLAG = (reg[dest].I & 0x80000000) ? true : false;\
    Z_FLAG = (reg[dest].I) ? false : true;\
    C_FLAG = C_OUT;

#define OP_MVN \
    reg[dest].I = ~value;

#define OP_MVNS \
    reg[dest].I = ~value; \
    N_FLAG = (reg[dest].I & 0x80000000) ? true : false;\
    Z_FLAG = (reg[dest].I) ? false : true;\
    C_FLAG = C_OUT;

#define CASE_16(BASE) \
  case BASE:\
  case BASE+1:\
  case BASE+2:\
  case BASE+3:\
  case BASE+4:\
  case BASE+5:\
  case BASE+6:\
  case BASE+7:\
  case BASE+8:\
  case BASE+9:\
  case BASE+10:\
  case BASE+11:\
  case BASE+12:\
  case BASE+13:\
  case BASE+14:\
  case BASE+15:

#define CASE_256(BASE) \
  CASE_16(BASE)\
  CASE_16(BASE+0x10)\
  CASE_16(BASE+0x20)\
  CASE_16(BASE+0x30)\
  CASE_16(BASE+0x40)\
  CASE_16(BASE+0x50)\
  CASE_16(BASE+0x60)\
  CASE_16(BASE+0x70)\
  CASE_16(BASE+0x80)\
  CASE_16(BASE+0x90)\
  CASE_16(BASE+0xa0)\
  CASE_16(BASE+0xb0)\
  CASE_16(BASE+0xc0)\
  CASE_16(BASE+0xd0)\
  CASE_16(BASE+0xe0)\
  CASE_16(BASE+0xf0)

#define LOGICAL_DATA_OPCODE(OPCODE, OPCODE2, BASE) \
  case BASE: \
  case BASE+8:\
    {\
      /* OP Rd,Rb,Rm LSL # */ \
      int base = (opcode >> 16) & 0x0F;\
      int shift = (opcode >> 7) & 0x1F;\
      int dest = (opcode>>12) & 15;\
      bool C_OUT = C_FLAG;\
      u32 value;\
      \
      if(shift) {\
        LOGICAL_LSL_REG\
      } else {\
        value = reg[opcode & 0x0F].I;\
      }\
      if(dest == 15) {\
        OPCODE2\
        /* todo */\
        if(opcode & 0x00100000) {\
          CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);\
        }\
        if(armState) {\
          reg[15].I &= 0xFFFFFFFC;\
          armNextPC = reg[15].I;\
          reg[15].I += 4;\
        } else {\
          reg[15].I &= 0xFFFFFFFE;\
          armNextPC = reg[15].I;\
          reg[15].I += 2;\
        }\
      } else {\
        OPCODE \
      }\
    }\
    break;\
  case BASE+2:\
  case BASE+10:\
    {\
       /* OP Rd,Rb,Rm LSR # */ \
      int base = (opcode >> 16) & 0x0F;\
      int shift = (opcode >> 7) & 0x1F;\
      int dest = (opcode>>12) & 15;\
      bool C_OUT = C_FLAG;\
      u32 value;\
      if(shift) {\
        LOGICAL_LSR_REG\
      } else {\
        value = 0;\
        C_OUT = (reg[opcode & 0x0F].I & 0x80000000) ? true : false;\
      }\
      \
      if(dest == 15) {\
        OPCODE2\
        /* todo */\
        if(opcode & 0x00100000) {\
          CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);\
        }\
        if(armState) {\
          reg[15].I &= 0xFFFFFFFC;\
          armNextPC = reg[15].I;\
          reg[15].I += 4;\
        } else {\
          reg[15].I &= 0xFFFFFFFE;\
          armNextPC = reg[15].I;\
          reg[15].I += 2;\
        }\
      } else {\
        OPCODE \
      }\
    }\
    break;\
  case BASE+4:\
  case BASE+12:\
    {\
       /* OP Rd,Rb,Rm ASR # */\
      int base = (opcode >> 16) & 0x0F;\
      int shift = (opcode >> 7) & 0x1F;\
      int dest = (opcode>>12) & 15;\
      bool C_OUT = C_FLAG;\
      u32 value;\
      if(shift) {\
        LOGICAL_ASR_REG\
      } else {\
        if(reg[opcode & 0x0F].I & 0x80000000){\
          value = 0xFFFFFFFF;\
          C_OUT = true;\
        } else {\
          value = 0;\
          C_OUT = false;\
        }                   \
      }\
      \
      if(dest == 15) {\
        OPCODE2\
        /* todo */\
        if(opcode & 0x00100000) {\
          CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);\
        }\
        if(armState) {\
          reg[15].I &= 0xFFFFFFFC;\
          armNextPC = reg[15].I;\
          reg[15].I += 4;\
        } else {\
          reg[15].I &= 0xFFFFFFFE;\
          armNextPC = reg[15].I;\
          reg[15].I += 2;\
        }\
      } else {\
        OPCODE \
      }\
    }\
    break;\
  case BASE+6:\
  case BASE+14:\
    {\
       /* OP Rd,Rb,Rm ROR # */\
      int base = (opcode >> 16) & 0x0F;\
      int shift = (opcode >> 7) & 0x1F;\
      int dest = (opcode>>12) & 15;\
      bool C_OUT = C_FLAG;\
      u32 value;\
      if(shift) {\
        LOGICAL_ROR_REG\
      } else {\
        LOGICAL_RRX_REG\
      }\
      if(dest == 15) {\
        OPCODE2\
        /* todo */\
        if(opcode & 0x00100000) {\
          CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);\
        }\
        if(armState) {\
          reg[15].I &= 0xFFFFFFFC;\
          armNextPC = reg[15].I;\
          reg[15].I += 4;\
        } else {\
          reg[15].I &= 0xFFFFFFFE;\
          armNextPC = reg[15].I;\
          reg[15].I += 2;\
        }\
      } else {\
        OPCODE \
      }\
    }\
    break;\
  case BASE+1:\
    {\
       /* OP Rd,Rb,Rm LSL Rs */\
      int base = (opcode >> 16) & 0x0F;\
      int shift = reg[(opcode >> 8)&15].B.B0;\
      int dest = (opcode>>12) & 15;\
      bool C_OUT = C_FLAG;\
      u32 value;\
      if(shift) {\
        if(shift == 32) {\
          value = 0;\
          C_OUT = (reg[opcode & 0x0F].I & 1 ? true : false);\
        } else if(shift < 32) {\
           LOGICAL_LSL_REG\
        } else {\
          value = 0;\
          C_OUT = false;\
        }\
      } else {\
        value = reg[opcode & 0x0F].I;\
      }\
      if(dest == 15) {\
        OPCODE2\
        /* todo */\
        if(opcode & 0x00100000) {\
          CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);\
        }\
        if(armState) {\
          reg[15].I &= 0xFFFFFFFC;\
          armNextPC = reg[15].I;\
          reg[15].I += 4;\
        } else {\
          reg[15].I &= 0xFFFFFFFE;\
          armNextPC = reg[15].I;\
          reg[15].I += 2;\
        }\
      } else {\
        OPCODE \
      }\
    }\
    break;\
  case BASE+3:\
    {\
       /* OP Rd,Rb,Rm LSR Rs */ \
      int base = (opcode >> 16) & 0x0F;\
      int shift = reg[(opcode >> 8)&15].B.B0;\
      int dest = (opcode>>12) & 15;\
      bool C_OUT = C_FLAG;\
      u32 value;\
      if(shift) {\
        if(shift == 32) {\
          value = 0;\
          C_OUT = (reg[opcode & 0x0F].I & 0x80000000 ? true : false);\
        } else if(shift < 32) {\
            LOGICAL_LSR_REG\
        } else {\
          value = 0;\
          C_OUT = false;\
        }\
      } else {\
        value = reg[opcode & 0x0F].I;\
      }\
      if(dest == 15) {\
        OPCODE2\
        /* todo */\
        if(opcode & 0x00100000) {\
          CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);\
        }\
        if(armState) {\
          reg[15].I &= 0xFFFFFFFC;\
          armNextPC = reg[15].I;\
          reg[15].I += 4;\
        } else {\
          reg[15].I &= 0xFFFFFFFE;\
          armNextPC = reg[15].I;\
          reg[15].I += 2;\
        }\
      } else {\
        OPCODE \
      }\
    }\
    break;\
  case BASE+5:\
    {\
       /* OP Rd,Rb,Rm ASR Rs */ \
      int base = (opcode >> 16) & 0x0F;\
      int shift = reg[(opcode >> 8)&15].B.B0;\
      int dest = (opcode>>12) & 15;\
      bool C_OUT = C_FLAG;\
      u32 value;\
      if(shift < 32) {\
        if(shift) {\
          LOGICAL_ASR_REG\
        } else {\
          value = reg[opcode & 0x0F].I;\
        }\
      } else {\
        if(reg[opcode & 0x0F].I & 0x80000000){\
          value = 0xFFFFFFFF;\
          C_OUT = true;\
        } else {\
          value = 0;\
          C_OUT = false;\
        }\
      }\
      if(dest == 15) {\
        OPCODE2\
        /* todo */\
        if(opcode & 0x00100000) {\
          CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);\
        }\
        if(armState) {\
          reg[15].I &= 0xFFFFFFFC;\
          armNextPC = reg[15].I;\
          reg[15].I += 4;\
        } else {\
          reg[15].I &= 0xFFFFFFFE;\
          armNextPC = reg[15].I;\
          reg[15].I += 2;\
        }\
      } else {\
        OPCODE \
      }\
    }\
    break;\
  case BASE+7:\
    {\
       /* OP Rd,Rb,Rm ROR Rs */\
      int base = (opcode >> 16) & 0x0F;\
      int shift = reg[(opcode >> 8)&15].B.B0;\
      int dest = (opcode>>12) & 15;\
      bool C_OUT = C_FLAG;\
      u32 value;\
      if(shift) {\
        shift &= 0x1f;\
        if(shift) {\
          LOGICAL_ROR_REG\
        } else {\
          value = reg[opcode & 0x0F].I;\
          C_OUT = (value & 0x80000000 ? true : false);\
        }\
      } else {\
        value = reg[opcode & 0x0F].I;\
        C_OUT = (value & 0x80000000 ? true : false);\
      }\
      if(dest == 15) {\
        OPCODE2\
        /* todo */\
        if(opcode & 0x00100000) {\
          CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);\
        }\
        if(armState) {\
          reg[15].I &= 0xFFFFFFFC;\
          armNextPC = reg[15].I;\
          reg[15].I += 4;\
        } else {\
          reg[15].I &= 0xFFFFFFFE;\
          armNextPC = reg[15].I;\
          reg[15].I += 2;\
        }\
      } else {\
        OPCODE \
      }\
    }\
    break;\
  case BASE+0x200:\
  case BASE+0x201:\
  case BASE+0x202:\
  case BASE+0x203:\
  case BASE+0x204:\
  case BASE+0x205:\
  case BASE+0x206:\
  case BASE+0x207:\
  case BASE+0x208:\
  case BASE+0x209:\
  case BASE+0x20a:\
  case BASE+0x20b:\
  case BASE+0x20c:\
  case BASE+0x20d:\
  case BASE+0x20e:\
  case BASE+0x20f:\
    {\
      int shift = (opcode & 0xF00) >> 7;\
      int base = (opcode >> 16) & 0x0F;\
      int dest = (opcode >> 12) & 0x0F;\
      bool C_OUT = C_FLAG;\
      u32 value;\
      if(shift) {\
        LOGICAL_ROR_IMM\
      } else {\
        value = opcode & 0xff;\
      }\
      if(dest == 15) {\
        OPCODE2\
        /* todo */\
        if(opcode & 0x00100000) {\
          CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);\
        }\
        if(armState) {\
          reg[15].I &= 0xFFFFFFFC;\
          armNextPC = reg[15].I;\
          reg[15].I += 4;\
        } else {\
          reg[15].I &= 0xFFFFFFFE;\
          armNextPC = reg[15].I;\
          reg[15].I += 2;\
        }\
      } else {\
        OPCODE \
      }\
    }\
    break;

#define LOGICAL_DATA_OPCODE_WITHOUT_base(OPCODE, OPCODE2, BASE) \
  case BASE: \
  case BASE+8:\
    {\
      /* OP Rd,Rb,Rm LSL # */ \
      int shift = (opcode >> 7) & 0x1F;\
      int dest = (opcode>>12) & 15;\
      bool C_OUT = C_FLAG;\
      u32 value;\
      \
      if(shift) {\
        LOGICAL_LSL_REG\
      } else {\
        value = reg[opcode & 0x0F].I;\
      }\
      if(dest == 15) {\
        OPCODE2\
        /* todo */\
        if(opcode & 0x00100000) {\
          CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);\
        }\
        if(armState) {\
          reg[15].I &= 0xFFFFFFFC;\
          armNextPC = reg[15].I;\
          reg[15].I += 4;\
        } else {\
          reg[15].I &= 0xFFFFFFFE;\
          armNextPC = reg[15].I;\
          reg[15].I += 2;\
        }\
      } else {\
        OPCODE \
      }\
    }\
    break;\
  case BASE+2:\
  case BASE+10:\
    {\
       /* OP Rd,Rb,Rm LSR # */ \
      int shift = (opcode >> 7) & 0x1F;\
      int dest = (opcode>>12) & 15;\
      bool C_OUT = C_FLAG;\
      u32 value;\
      if(shift) {\
        LOGICAL_LSR_REG\
      } else {\
        value = 0;\
        C_OUT = (reg[opcode & 0x0F].I & 0x80000000) ? true : false;\
      }\
      \
      if(dest == 15) {\
        OPCODE2\
        /* todo */\
        if(opcode & 0x00100000) {\
          CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);\
        }\
        if(armState) {\
          reg[15].I &= 0xFFFFFFFC;\
          armNextPC = reg[15].I;\
          reg[15].I += 4;\
        } else {\
          reg[15].I &= 0xFFFFFFFE;\
          armNextPC = reg[15].I;\
          reg[15].I += 2;\
        }\
      } else {\
        OPCODE \
      }\
    }\
    break;\
  case BASE+4:\
  case BASE+12:\
    {\
       /* OP Rd,Rb,Rm ASR # */\
      int shift = (opcode >> 7) & 0x1F;\
      int dest = (opcode>>12) & 15;\
      bool C_OUT = C_FLAG;\
      u32 value;\
      if(shift) {\
        LOGICAL_ASR_REG\
      } else {\
        if(reg[opcode & 0x0F].I & 0x80000000){\
          value = 0xFFFFFFFF;\
          C_OUT = true;\
        } else {\
          value = 0;\
          C_OUT = false;\
        }                   \
      }\
      \
      if(dest == 15) {\
        OPCODE2\
        /* todo */\
        if(opcode & 0x00100000) {\
          CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);\
        }\
        if(armState) {\
          reg[15].I &= 0xFFFFFFFC;\
          armNextPC = reg[15].I;\
          reg[15].I += 4;\
        } else {\
          reg[15].I &= 0xFFFFFFFE;\
          armNextPC = reg[15].I;\
          reg[15].I += 2;\
        }\
      } else {\
        OPCODE \
      }\
    }\
    break;\
  case BASE+6:\
  case BASE+14:\
    {\
       /* OP Rd,Rb,Rm ROR # */\
      int shift = (opcode >> 7) & 0x1F;\
      int dest = (opcode>>12) & 15;\
      bool C_OUT = C_FLAG;\
      u32 value;\
      if(shift) {\
        LOGICAL_ROR_REG\
      } else {\
        LOGICAL_RRX_REG\
      }\
      if(dest == 15) {\
        OPCODE2\
        /* todo */\
        if(opcode & 0x00100000) {\
          CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);\
        }\
        if(armState) {\
          reg[15].I &= 0xFFFFFFFC;\
          armNextPC = reg[15].I;\
          reg[15].I += 4;\
        } else {\
          reg[15].I &= 0xFFFFFFFE;\
          armNextPC = reg[15].I;\
          reg[15].I += 2;\
        }\
      } else {\
        OPCODE \
      }\
    }\
    break;\
  case BASE+1:\
    {\
       /* OP Rd,Rb,Rm LSL Rs */\
      int shift = reg[(opcode >> 8)&15].B.B0;\
      int dest = (opcode>>12) & 15;\
      bool C_OUT = C_FLAG;\
      u32 value;\
      if(shift) {\
        if(shift == 32) {\
          value = 0;\
          C_OUT = (reg[opcode & 0x0F].I & 1 ? true : false);\
        } else if(shift < 32) {\
           LOGICAL_LSL_REG\
        } else {\
          value = 0;\
          C_OUT = false;\
        }\
      } else {\
        value = reg[opcode & 0x0F].I;\
      }\
      if(dest == 15) {\
        OPCODE2\
        /* todo */\
        if(opcode & 0x00100000) {\
          CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);\
        }\
        if(armState) {\
          reg[15].I &= 0xFFFFFFFC;\
          armNextPC = reg[15].I;\
          reg[15].I += 4;\
        } else {\
          reg[15].I &= 0xFFFFFFFE;\
          armNextPC = reg[15].I;\
          reg[15].I += 2;\
        }\
      } else {\
        OPCODE \
      }\
    }\
    break;\
  case BASE+3:\
    {\
       /* OP Rd,Rb,Rm LSR Rs */ \
      int shift = reg[(opcode >> 8)&15].B.B0;\
      int dest = (opcode>>12) & 15;\
      bool C_OUT = C_FLAG;\
      u32 value;\
      if(shift) {\
        if(shift == 32) {\
          value = 0;\
          C_OUT = (reg[opcode & 0x0F].I & 0x80000000 ? true : false);\
        } else if(shift < 32) {\
            LOGICAL_LSR_REG\
        } else {\
          value = 0;\
          C_OUT = false;\
        }\
      } else {\
        value = reg[opcode & 0x0F].I;\
      }\
      if(dest == 15) {\
        OPCODE2\
        /* todo */\
        if(opcode & 0x00100000) {\
          CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);\
        }\
        if(armState) {\
          reg[15].I &= 0xFFFFFFFC;\
          armNextPC = reg[15].I;\
          reg[15].I += 4;\
        } else {\
          reg[15].I &= 0xFFFFFFFE;\
          armNextPC = reg[15].I;\
          reg[15].I += 2;\
        }\
      } else {\
        OPCODE \
      }\
    }\
    break;\
  case BASE+5:\
    {\
       /* OP Rd,Rb,Rm ASR Rs */ \
      int shift = reg[(opcode >> 8)&15].B.B0;\
      int dest = (opcode>>12) & 15;\
      bool C_OUT = C_FLAG;\
      u32 value;\
      if(shift < 32) {\
        if(shift) {\
          LOGICAL_ASR_REG\
        } else {\
          value = reg[opcode & 0x0F].I;\
        }\
      } else {\
        if(reg[opcode & 0x0F].I & 0x80000000){\
          value = 0xFFFFFFFF;\
          C_OUT = true;\
        } else {\
          value = 0;\
          C_OUT = false;\
        }\
      }\
      if(dest == 15) {\
        OPCODE2\
        /* todo */\
        if(opcode & 0x00100000) {\
          CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);\
        }\
        if(armState) {\
          reg[15].I &= 0xFFFFFFFC;\
          armNextPC = reg[15].I;\
          reg[15].I += 4;\
        } else {\
          reg[15].I &= 0xFFFFFFFE;\
          armNextPC = reg[15].I;\
          reg[15].I += 2;\
        }\
      } else {\
        OPCODE \
      }\
    }\
    break;\
  case BASE+7:\
    {\
       /* OP Rd,Rb,Rm ROR Rs */\
      int shift = reg[(opcode >> 8)&15].B.B0;\
      int dest = (opcode>>12) & 15;\
      bool C_OUT = C_FLAG;\
      u32 value;\
      if(shift) {\
        shift &= 0x1f;\
        if(shift) {\
          LOGICAL_ROR_REG\
        } else {\
          value = reg[opcode & 0x0F].I;\
          C_OUT = (value & 0x80000000 ? true : false);\
        }\
      } else {\
        value = reg[opcode & 0x0F].I;\
        C_OUT = (value & 0x80000000 ? true : false);\
      }\
      if(dest == 15) {\
        OPCODE2\
        /* todo */\
        if(opcode & 0x00100000) {\
          CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);\
        }\
        if(armState) {\
          reg[15].I &= 0xFFFFFFFC;\
          armNextPC = reg[15].I;\
          reg[15].I += 4;\
        } else {\
          reg[15].I &= 0xFFFFFFFE;\
          armNextPC = reg[15].I;\
          reg[15].I += 2;\
        }\
      } else {\
        OPCODE \
      }\
    }\
    break;\
  case BASE+0x200:\
  case BASE+0x201:\
  case BASE+0x202:\
  case BASE+0x203:\
  case BASE+0x204:\
  case BASE+0x205:\
  case BASE+0x206:\
  case BASE+0x207:\
  case BASE+0x208:\
  case BASE+0x209:\
  case BASE+0x20a:\
  case BASE+0x20b:\
  case BASE+0x20c:\
  case BASE+0x20d:\
  case BASE+0x20e:\
  case BASE+0x20f:\
    {\
      int shift = (opcode & 0xF00) >> 7;\
      int dest = (opcode >> 12) & 0x0F;\
      bool C_OUT = C_FLAG;\
      u32 value;\
      if(shift) {\
        LOGICAL_ROR_IMM\
      } else {\
        value = opcode & 0xff;\
      }\
      if(dest == 15) {\
        OPCODE2\
        /* todo */\
        if(opcode & 0x00100000) {\
          CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);\
        }\
        if(armState) {\
          reg[15].I &= 0xFFFFFFFC;\
          armNextPC = reg[15].I;\
          reg[15].I += 4;\
        } else {\
          reg[15].I &= 0xFFFFFFFE;\
          armNextPC = reg[15].I;\
          reg[15].I += 2;\
        }\
      } else {\
        OPCODE \
      }\
    }\
    break;

#define ARITHMETIC_DATA_OPCODE(OPCODE, OPCODE2, BASE) \
  case BASE:\
  case BASE+8:\
    {\
      /* OP Rd,Rb,Rm LSL # */\
      int base = (opcode >> 16) & 0x0F;\
      int shift = (opcode >> 7) & 0x1F;\
      int dest = (opcode>>12) & 15;\
      u32 value;\
      if(shift) {\
        ARITHMETIC_LSL_REG\
      } else {\
        value = reg[opcode & 0x0F].I;\
      }\
      if(dest == 15) {\
        OPCODE2\
        /* todo */\
        if(opcode & 0x00100000) {\
          CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);\
        }\
        if(armState) {\
          reg[15].I &= 0xFFFFFFFC;\
          armNextPC = reg[15].I;\
          reg[15].I += 4;\
        } else {\
          reg[15].I &= 0xFFFFFFFE;\
          armNextPC = reg[15].I;\
          reg[15].I += 2;\
        }\
      } else {\
        OPCODE \
      }\
    }\
    break;\
  case BASE+2:\
  case BASE+10:\
    {\
      /* OP Rd,Rb,Rm LSR # */\
      int base = (opcode >> 16) & 0x0F;\
      int shift = (opcode >> 7) & 0x1F;\
      int dest = (opcode>>12) & 15;\
      u32 value;\
      if(shift) {\
        ARITHMETIC_LSR_REG\
      } else {\
        value = 0;\
      }\
      if(dest == 15) {\
        OPCODE2\
        /* todo */\
        if(opcode & 0x00100000) {\
          CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);\
        }\
        if(armState) {\
          reg[15].I &= 0xFFFFFFFC;\
          armNextPC = reg[15].I;\
          reg[15].I += 4;\
        } else {\
          reg[15].I &= 0xFFFFFFFE;\
          armNextPC = reg[15].I;\
          reg[15].I += 2;\
        }\
      } else {\
        OPCODE \
      }\
    }\
    break;\
  case BASE+4:\
  case BASE+12:\
    {\
      /* OP Rd,Rb,Rm ASR # */\
      int base = (opcode >> 16) & 0x0F;\
      int shift = (opcode >> 7) & 0x1F;\
      int dest = (opcode>>12) & 15;\
      u32 value;\
      if(shift) {\
        ARITHMETIC_ASR_REG\
      } else {\
        if(reg[opcode & 0x0F].I & 0x80000000){\
          value = 0xFFFFFFFF;\
        } else value = 0;\
      }\
      if(dest == 15) {\
        OPCODE2\
        /* todo */\
        if(opcode & 0x00100000) {\
          CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);\
        }\
        if(armState) {\
          reg[15].I &= 0xFFFFFFFC;\
          armNextPC = reg[15].I;\
          reg[15].I += 4;\
        } else {\
          reg[15].I &= 0xFFFFFFFE;\
          armNextPC = reg[15].I;\
          reg[15].I += 2;\
        }\
      } else {\
        OPCODE \
      }\
    }\
    break;\
  case BASE+6:\
  case BASE+14:\
    {\
      /* OP Rd,Rb,Rm ROR # */\
      int base = (opcode >> 16) & 0x0F;\
      int shift = (opcode >> 7) & 0x1F;\
      int dest = (opcode>>12) & 15;\
      u32 value;\
      if(shift) {\
         ARITHMETIC_ROR_REG\
      } else {\
         ARITHMETIC_RRX_REG\
      }\
      if(dest == 15) {\
        OPCODE2\
        /* todo */\
        if(opcode & 0x00100000) {\
          CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);\
        }\
        if(armState) {\
          reg[15].I &= 0xFFFFFFFC;\
          armNextPC = reg[15].I;\
          reg[15].I += 4;\
        } else {\
          reg[15].I &= 0xFFFFFFFE;\
          armNextPC = reg[15].I;\
          reg[15].I += 2;\
        }\
      } else {\
        OPCODE \
      }\
    }\
    break;\
  case BASE+1:\
    {\
      /* OP Rd,Rb,Rm LSL Rs */\
      int base = (opcode >> 16) & 0x0F;\
      int shift = reg[(opcode >> 8)&15].B.B0;\
      int dest = (opcode>>12) & 15;\
      u32 value;\
      if(shift) {\
        if(shift == 32) {\
          value = 0;\
        } else if(shift < 32) {\
           ARITHMETIC_LSL_REG\
        } else value = 0;\
      } else {\
        value = reg[opcode & 0x0F].I;\
      }\
      if(dest == 15) {\
        OPCODE2\
        /* todo */\
        if(opcode & 0x00100000) {\
          CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);\
        }\
        if(armState) {\
          reg[15].I &= 0xFFFFFFFC;\
          armNextPC = reg[15].I;\
          reg[15].I += 4;\
        } else {\
          reg[15].I &= 0xFFFFFFFE;\
          armNextPC = reg[15].I;\
          reg[15].I += 2;\
        }\
      } else {\
        OPCODE \
      }\
    }\
    break;\
  case BASE+3:\
    {\
      /* OP Rd,Rb,Rm LSR Rs */\
      int base = (opcode >> 16) & 0x0F;\
      int shift = reg[(opcode >> 8)&15].B.B0;\
      int dest = (opcode>>12) & 15;\
      u32 value;\
      if(shift) {\
        if(shift == 32) {\
          value = 0;\
        } else if(shift < 32) {\
           ARITHMETIC_LSR_REG\
        } else value = 0;\
      } else {\
        value = reg[opcode & 0x0F].I;\
      }\
      if(dest == 15) {\
        OPCODE2\
        /* todo */\
        if(opcode & 0x00100000) {\
          CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);\
        }\
        if(armState) {\
          reg[15].I &= 0xFFFFFFFC;\
          armNextPC = reg[15].I;\
          reg[15].I += 4;\
        } else {\
          reg[15].I &= 0xFFFFFFFE;\
          armNextPC = reg[15].I;\
          reg[15].I += 2;\
        }\
      } else {\
        OPCODE \
      }\
    }\
    break;\
  case BASE+5:\
    {\
      /* OP Rd,Rb,Rm ASR Rs */\
      int base = (opcode >> 16) & 0x0F;\
      int shift = reg[(opcode >> 8)&15].B.B0;\
      int dest = (opcode>>12) & 15;\
      u32 value;\
      if(shift < 32) {\
        if(shift) {\
           ARITHMETIC_ASR_REG\
        } else {\
          value = reg[opcode & 0x0F].I;\
        }\
      } else {\
        if(reg[opcode & 0x0F].I & 0x80000000){\
          value = 0xFFFFFFFF;\
        } else value = 0;\
      }\
      if(dest == 15) {\
        OPCODE2\
        /* todo */\
        if(opcode & 0x00100000) {\
          CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);\
        }\
        if(armState) {\
          reg[15].I &= 0xFFFFFFFC;\
          armNextPC = reg[15].I;\
          reg[15].I += 4;\
        } else {\
          reg[15].I &= 0xFFFFFFFE;\
          armNextPC = reg[15].I;\
          reg[15].I += 2;\
        }\
      } else {\
        OPCODE \
      }\
    }\
    break;\
  case BASE+7:\
    {\
      /* OP Rd,Rb,Rm ROR Rs */\
      int base = (opcode >> 16) & 0x0F;\
      int shift = reg[(opcode >> 8)&15].B.B0;\
      int dest = (opcode>>12) & 15;\
      u32 value;\
      if(shift) {\
        shift &= 0x1f;\
        if(shift) {\
           ARITHMETIC_ROR_REG\
        } else {\
           value = reg[opcode & 0x0F].I;\
        }\
      } else {\
        value = reg[opcode & 0x0F].I;\
      }\
      if(dest == 15) {\
        OPCODE2\
        /* todo */\
        if(opcode & 0x00100000) {\
          CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);\
        }\
        if(armState) {\
          reg[15].I &= 0xFFFFFFFC;\
          armNextPC = reg[15].I;\
          reg[15].I += 4;\
        } else {\
          reg[15].I &= 0xFFFFFFFE;\
          armNextPC = reg[15].I;\
          reg[15].I += 2;\
        }\
      } else {\
        OPCODE \
      }\
    }\
    break;\
  case BASE+0x200:\
  case BASE+0x201:\
  case BASE+0x202:\
  case BASE+0x203:\
  case BASE+0x204:\
  case BASE+0x205:\
  case BASE+0x206:\
  case BASE+0x207:\
  case BASE+0x208:\
  case BASE+0x209:\
  case BASE+0x20a:\
  case BASE+0x20b:\
  case BASE+0x20c:\
  case BASE+0x20d:\
  case BASE+0x20e:\
  case BASE+0x20f:\
    {\
      int shift = (opcode & 0xF00) >> 7;\
      int base = (opcode >> 16) & 0x0F;\
      int dest = (opcode >> 12) & 0x0F;\
      u32 value;\
      {\
        ARITHMETIC_ROR_IMM\
      }\
      if(dest == 15) {\
        OPCODE2\
        /* todo */\
        if(opcode & 0x00100000) {\
          CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);\
        }\
        if(armState) {\
          reg[15].I &= 0xFFFFFFFC;\
          armNextPC = reg[15].I;\
          reg[15].I += 4;\
        } else {\
          reg[15].I &= 0xFFFFFFFE;\
          armNextPC = reg[15].I;\
          reg[15].I += 2;\
        }\
      } else {\
        OPCODE \
      }\
    }\
    break;


extern u8 cpuBitsSet[256];
extern u8 cpuLowestBitSet[256];
extern int memoryWaitFetch32[16];

static inline int arm_new()
{
  u32 opcode = CPUReadMemoryQuick(armNextPC);

  armNextPC = reg[15].I;
  reg[15].I += 4;

  u8 cond = opcode >> 28;
  // suggested optimization for frequent cases
  bool cond_res;
  if (cond >= 0xe) {
    cond_res = 1;
  } else 
  if (cond < 0x8) {
    cond_res = ALL_FLAGS[cond >> 1];
  } else {
    switch(cond >> 1) { 
    case 0x04: // HI
      cond_res = C_FLAG && !Z_FLAG;
      break;
    case 0x05: // GE
      cond_res = N_FLAG == V_FLAG;
      break;
    default: 
      cond_res = !Z_FLAG &&(N_FLAG == V_FLAG);
      break;    
    }
  }
  
  if((cond_res ^ cond) & 0x1) {
  unsigned short opcode_decod1= ((opcode>>16)&0xFF0) | ((opcode>>4)&0x0F);
  switch(opcode_decod1) {
    LOGICAL_DATA_OPCODE_WITHOUT_base(OP_AND,  OP_AND, 0x000);
    LOGICAL_DATA_OPCODE_WITHOUT_base(OP_ANDS, OP_AND, 0x010);
  case 0x009:
    {
      // MUL Rd, Rm, Rs
      int dest = (opcode >> 16) & 0x0F;
      int mult = (opcode & 0x0F);
      u32 rs = reg[(opcode >> 8) & 0x0F].I;
      reg[dest].I = reg[mult].I * rs;
      if(((s32)rs)<0)
        rs = ~rs;
    }
    break;
  case 0x019:
    {
      // MULS Rd, Rm, Rs
      int dest = (opcode >> 16) & 0x0F;
      int mult = (opcode & 0x0F);
      u32 rs = reg[(opcode >> 8) & 0x0F].I;
      reg[dest].I = reg[mult].I * rs;
      N_FLAG = (reg[dest].I & 0x80000000) ? true : false;
      Z_FLAG = (reg[dest].I) ? false : true;
      if(((s32)rs)<0)
        rs = ~rs;
    }
    break;
  case 0x00b:
  case 0x02b:
    {
      // STRH Rd, [Rn], -Rm
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I;
      int offset = reg[opcode & 0x0F].I;
      CPUWriteHalfWord(address, reg[dest].W.W0);
      address -= offset;
      reg[base].I = address;
    }
    break;
  case 0x04b:
  case 0x06b:
    {
      // STRH Rd, [Rn], #-offset
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I;
      int offset = (opcode & 0x0F) | ((opcode >> 4) & 0xF0);
      CPUWriteHalfWord(address, reg[dest].W.W0);
      address -= offset;
      reg[base].I = address;
    }
    break;
  case 0x08b:
  case 0x0ab:
    {
      // STRH Rd, [Rn], Rm
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I;
      int offset = reg[opcode & 0x0F].I;
      CPUWriteHalfWord(address, reg[dest].W.W0);
      address += offset;
      reg[base].I = address;
    }
    break;
  case 0x0cb:
  case 0x0eb:
    {
      // STRH Rd, [Rn], #offset
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I;
      int offset = (opcode & 0x0F) | ((opcode >> 4) & 0xF0);
      CPUWriteHalfWord(address, reg[dest].W.W0);
      address += offset;
      reg[base].I = address;
    }
    break;
  case 0x10b:
    {
      // STRH Rd, [Rn, -Rm]
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I - reg[opcode & 0x0F].I;
      CPUWriteHalfWord(address, reg[dest].W.W0);
    }
    break;
  case 0x12b:
    {
      // STRH Rd, [Rn, -Rm]!
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I - reg[opcode & 0x0F].I;
      CPUWriteHalfWord(address, reg[dest].W.W0);
      reg[base].I = address;
    }
    break;
  case 0x14b:
    {
      // STRH Rd, [Rn, -#offset]
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I - ((opcode & 0x0F)|((opcode>>4)&0xF0));
      CPUWriteHalfWord(address, reg[dest].W.W0);
    }
    break;
  case 0x16b:
    {
      // STRH Rd, [Rn, -#offset]!
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I - ((opcode & 0x0F)|((opcode>>4)&0xF0));
      CPUWriteHalfWord(address, reg[dest].W.W0);
      reg[base].I = address;
    }
    break;
  case 0x18b:
    {
      // STRH Rd, [Rn, Rm]
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I + reg[opcode & 0x0F].I;
      CPUWriteHalfWord(address, reg[dest].W.W0);
    }
    break;
  case 0x1ab:
    {
      // STRH Rd, [Rn, Rm]!
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I + reg[opcode & 0x0F].I;
      CPUWriteHalfWord(address, reg[dest].W.W0);
      reg[base].I = address;
    }
    break;
  case 0x1cb:
    {
      // STRH Rd, [Rn, #offset]
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I + ((opcode & 0x0F)|((opcode>>4)&0xF0));
      CPUWriteHalfWord(address, reg[dest].W.W0);
    }
    break;
  case 0x1eb:
    {
      // STRH Rd, [Rn, #offset]!
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I + ((opcode & 0x0F)|((opcode>>4)&0xF0));
      CPUWriteHalfWord(address, reg[dest].W.W0);
      reg[base].I = address;
    }
    break;
  case 0x01b:
  case 0x03b:
    {
      // LDRH Rd, [Rn], -Rm
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I;
      int offset = reg[opcode & 0x0F].I;
      reg[dest].I = CPUReadHalfWord(address);
      if(dest != base) {
        address -= offset;
        reg[base].I = address;
      }
    }
    break;
  case 0x05b:
  case 0x07b:
    {
      // LDRH Rd, [Rn], #-offset
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I;
      int offset = (opcode & 0x0F) | ((opcode >> 4) & 0xF0);
      reg[dest].I = CPUReadHalfWord(address);
      if(dest != base) {
        address -= offset;
        reg[base].I = address;
      }
    }
    break;
  case 0x09b:
  case 0x0bb:
    {
      // LDRH Rd, [Rn], Rm
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I;
      int offset = reg[opcode & 0x0F].I;
      reg[dest].I = CPUReadHalfWord(address);
      if(dest != base) {
        address += offset;
        reg[base].I = address;
      }
    }
    break;
  case 0x0db:
  case 0x0fb:
    {
      // LDRH Rd, [Rn], #offset
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I;
      int offset = (opcode & 0x0F) | ((opcode >> 4) & 0xF0);
      reg[dest].I = CPUReadHalfWord(address);
      if(dest != base) {
        address += offset;
        reg[base].I = address;
      }
    }
    break;
  case 0x11b:
    {
      // LDRH Rd, [Rn, -Rm]
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I - reg[opcode & 0x0F].I;
      reg[dest].I = CPUReadHalfWord(address);
    }
    break;
  case 0x13b:
    {
      // LDRH Rd, [Rn, -Rm]!
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I - reg[opcode & 0x0F].I;
      reg[dest].I = CPUReadHalfWord(address);
      if(dest != base)
        reg[base].I = address;
    }
    break;
  case 0x15b:
    {
      // LDRH Rd, [Rn, -#offset]
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I - ((opcode & 0x0F)|((opcode>>4)&0xF0));
      reg[dest].I = CPUReadHalfWord(address);
    }
    break;
  case 0x17b:
    {
      // LDRH Rd, [Rn, -#offset]!
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I - ((opcode & 0x0F)|((opcode>>4)&0xF0));
      reg[dest].I = CPUReadHalfWord(address);
      if(dest != base)
        reg[base].I = address;
    }
    break;
  case 0x19b:
    {
      // LDRH Rd, [Rn, Rm]
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I + reg[opcode & 0x0F].I;
      reg[dest].I = CPUReadHalfWord(address);
    }
    break;
  case 0x1bb:
    {
      // LDRH Rd, [Rn, Rm]!
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I + reg[opcode & 0x0F].I;
      reg[dest].I = CPUReadHalfWord(address);
      if(dest != base)
        reg[base].I = address;
    }
    break;
  case 0x1db:
    {
      // LDRH Rd, [Rn, #offset]
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I + ((opcode & 0x0F)|((opcode>>4)&0xF0));
      reg[dest].I = CPUReadHalfWord(address);
    }
    break;
  case 0x1fb:
    {
      // LDRH Rd, [Rn, #offset]!
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I + ((opcode & 0x0F)|((opcode>>4)&0xF0));
      reg[dest].I = CPUReadHalfWord(address);
      if(dest != base)
        reg[base].I = address;
    }
    break;
  case 0x01d:
  case 0x03d:
    {
      // LDRSB Rd, [Rn], -Rm
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I;
      int offset = reg[opcode & 0x0F].I;
      reg[dest].I = (s8)CPUReadByte(address);
      if(dest != base) {
        address -= offset;
        reg[base].I = address;
      }
    }
    break;
  case 0x05d:
  case 0x07d:
    {
      // LDRSB Rd, [Rn], #-offset
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I;
      int offset = (opcode & 0x0F) | ((opcode >> 4) & 0xF0);
      reg[dest].I = (s8)CPUReadByte(address);
      if(dest != base) {
        address -= offset;
        reg[base].I = address;
      }
    }
    break;
  case 0x09d:
  case 0x0bd:
    {
      // LDRSB Rd, [Rn], Rm
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I;
      int offset = reg[opcode & 0x0F].I;
      reg[dest].I = (s8)CPUReadByte(address);
      if(dest != base) {
        address += offset;
        reg[base].I = address;
      }
    }
    break;
  case 0x0dd:
  case 0x0fd:
    {
      // LDRSB Rd, [Rn], #offset
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I;
      int offset = (opcode & 0x0F) | ((opcode >> 4) & 0xF0);
      reg[dest].I = (s8)CPUReadByte(address);
      if(dest != base) {
        address += offset;
        reg[base].I = address;
      }
    }
    break;
  case 0x11d:
    {
      // LDRSB Rd, [Rn, -Rm]
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I - reg[opcode & 0x0F].I;
      reg[dest].I = (s8)CPUReadByte(address);
    }
    break;
  case 0x13d:
    {
      // LDRSB Rd, [Rn, -Rm]!
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I - reg[opcode & 0x0F].I;
      reg[dest].I = (s8)CPUReadByte(address);
      if(dest != base)
        reg[base].I = address;
    }
    break;
  case 0x15d:
    {
      // LDRSB Rd, [Rn, -#offset]
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I - ((opcode & 0x0F)|((opcode>>4)&0xF0));
      reg[dest].I = (s8)CPUReadByte(address);
    }
    break;
  case 0x17d:
    {
      // LDRSB Rd, [Rn, -#offset]!
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I - ((opcode & 0x0F)|((opcode>>4)&0xF0));
      reg[dest].I = (s8)CPUReadByte(address);
      if(dest != base)
        reg[base].I = address;
    }
    break;
  case 0x19d:
    {
      // LDRSB Rd, [Rn, Rm]
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I + reg[opcode & 0x0F].I;
      reg[dest].I = (s8)CPUReadByte(address);
    }
    break;
  case 0x1bd:
    {
      // LDRSB Rd, [Rn, Rm]!
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I + reg[opcode & 0x0F].I;
      reg[dest].I = (s8)CPUReadByte(address);
      if(dest != base)
        reg[base].I = address;
    }
    break;
  case 0x1dd:
    {
      // LDRSB Rd, [Rn, #offset]
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I + ((opcode & 0x0F)|((opcode>>4)&0xF0));
      reg[dest].I = (s8)CPUReadByte(address);
    }
    break;
  case 0x1fd:
    {
      // LDRSB Rd, [Rn, #offset]!
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I + ((opcode & 0x0F)|((opcode>>4)&0xF0));
      reg[dest].I = (s8)CPUReadByte(address);
      if(dest != base)
        reg[base].I = address;
    }
    break;
  case 0x01f:
  case 0x03f:
    {
      // LDRSH Rd, [Rn], -Rm
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I;
      int offset = reg[opcode & 0x0F].I;
      reg[dest].I = (s16)CPUReadHalfWordSigned(address);
      if(dest != base) {
        address -= offset;
        reg[base].I = address;
      }
    }
    break;
  case 0x05f:
  case 0x07f:
    {
      // LDRSH Rd, [Rn], #-offset
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I;
      int offset = (opcode & 0x0F) | ((opcode >> 4) & 0xF0);
      reg[dest].I = (s16)CPUReadHalfWordSigned(address);
      if(dest != base) {
        address -= offset;
        reg[base].I = address;
      }
    }
    break;
  case 0x09f:
  case 0x0bf:
    {
      // LDRSH Rd, [Rn], Rm
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I;
      int offset = reg[opcode & 0x0F].I;
      reg[dest].I = (s16)CPUReadHalfWordSigned(address);
      if(dest != base) {
        address += offset;
        reg[base].I = address;
      }
    }
    break;
  case 0x0df:
  case 0x0ff:
    {
      // LDRSH Rd, [Rn], #offset
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I;
      int offset = (opcode & 0x0F) | ((opcode >> 4) & 0xF0);
      reg[dest].I = (s16)CPUReadHalfWordSigned(address);
      if(dest != base) {
        address += offset;
        reg[base].I = address;
      }
    }
    break;
  case 0x11f:
    {
      // LDRSH Rd, [Rn, -Rm]
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I - reg[opcode & 0x0F].I;
      reg[dest].I = (s16)CPUReadHalfWordSigned(address);
    }
    break;
  case 0x13f:
    {
      // LDRSH Rd, [Rn, -Rm]!
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I - reg[opcode & 0x0F].I;
      reg[dest].I = (s16)CPUReadHalfWordSigned(address);
      if(dest != base)
        reg[base].I = address;
    }
    break;
  case 0x15f:
    {
      // LDRSH Rd, [Rn, -#offset]
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I - ((opcode & 0x0F)|((opcode>>4)&0xF0));
      reg[dest].I = (s16)CPUReadHalfWordSigned(address);
    }
    break;
  case 0x17f:
    {
      // LDRSH Rd, [Rn, -#offset]!
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I - ((opcode & 0x0F)|((opcode>>4)&0xF0));
      reg[dest].I = (s16)CPUReadHalfWordSigned(address);
      if(dest != base)
        reg[base].I = address;
    }
    break;
  case 0x19f:
    {
      // LDRSH Rd, [Rn, Rm]
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I + reg[opcode & 0x0F].I;
      reg[dest].I = (s16)CPUReadHalfWordSigned(address);
    }
    break;
  case 0x1bf:
    {
      // LDRSH Rd, [Rn, Rm]!
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I + reg[opcode & 0x0F].I;
      reg[dest].I = (s16)CPUReadHalfWordSigned(address);
      if(dest != base)
        reg[base].I = address;
    }
    break;
  case 0x1df:
    {
      // LDRSH Rd, [Rn, #offset]
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I + ((opcode & 0x0F)|((opcode>>4)&0xF0));
      reg[dest].I = (s16)CPUReadHalfWordSigned(address);
    }
    break;
  case 0x1ff:
    {
      // LDRSH Rd, [Rn, #offset]!
      int base = (opcode >> 16) & 0x0F;
      int dest = (opcode >> 12) & 0x0F;
      u32 address = reg[base].I + ((opcode & 0x0F)|((opcode>>4)&0xF0));
      reg[dest].I = (s16)CPUReadHalfWordSigned(address);
      if(dest != base)
        reg[base].I = address;
    }
    break;
    LOGICAL_DATA_OPCODE_WITHOUT_base(OP_EOR,  OP_EOR, 0x020);
    LOGICAL_DATA_OPCODE_WITHOUT_base(OP_EORS, OP_EOR, 0x030);
  case 0x029:
    {
      // MLA Rd, Rm, Rs, Rn
      int dest = (opcode >> 16) & 0x0F;
      int mult = (opcode & 0x0F);
      u32 rs = reg[(opcode >> 8) & 0x0F].I;
      reg[dest].I = reg[mult].I * rs + reg[(opcode>>12)&0x0f].I;
      if(((s32)rs)<0)
        rs = ~rs;
    }
    break;
  case 0x039:
    {
      // MLAS Rd, Rm, Rs, Rn
      int dest = (opcode >> 16) & 0x0F;
      int mult = (opcode & 0x0F);
      u32 rs = reg[(opcode >> 8) & 0x0F].I;
      reg[dest].I = reg[mult].I * rs + reg[(opcode>>12)&0x0f].I;
      N_FLAG = (reg[dest].I & 0x80000000) ? true : false;
      Z_FLAG = (reg[dest].I) ? false : true;
      if(((s32)rs)<0)
        rs = ~rs;
    }
    break;
    ARITHMETIC_DATA_OPCODE(OP_SUB,  OP_SUB, 0x040);
    ARITHMETIC_DATA_OPCODE(OP_SUBS, OP_SUB, 0x050);
    ARITHMETIC_DATA_OPCODE(OP_RSB,  OP_RSB, 0x060);
    ARITHMETIC_DATA_OPCODE(OP_RSBS, OP_RSB, 0x070);
    ARITHMETIC_DATA_OPCODE(OP_ADD,  OP_ADD, 0x080);
    ARITHMETIC_DATA_OPCODE(OP_ADDS, OP_ADD, 0x090);
  case 0x089:
    {
      // UMULL RdLo, RdHi, Rn, Rs
      u32 umult = reg[(opcode & 0x0F)].I;
      u32 usource = reg[(opcode >> 8) & 0x0F].I;
      int destLo = (opcode >> 12) & 0x0F;         
      int destHi = (opcode >> 16) & 0x0F;
      u64 uTemp = ((u64)umult)*((u64)usource);
      reg[destLo].I = (u32)uTemp;
      reg[destHi].I = (u32)(uTemp >> 32);
    }
    break;
  case 0x099:
    {
      // UMULLS RdLo, RdHi, Rn, Rs
      u32 umult = reg[(opcode & 0x0F)].I;
      u32 usource = reg[(opcode >> 8) & 0x0F].I;
      int destLo = (opcode >> 12) & 0x0F;         
      int destHi = (opcode >> 16) & 0x0F;
      u64 uTemp = ((u64)umult)*((u64)usource);
      reg[destLo].I = (u32)uTemp;
      reg[destHi].I = (u32)(uTemp >> 32);
      Z_FLAG = (uTemp) ? false : true;
      N_FLAG = (reg[destHi].I & 0x80000000) ? true : false;
    }
    break;
    ARITHMETIC_DATA_OPCODE(OP_ADC,  OP_ADC, 0x0a0);
    ARITHMETIC_DATA_OPCODE(OP_ADCS, OP_ADC, 0x0b0);
  case 0x0a9:
    {
      // UMLAL RdLo, RdHi, Rn, Rs
      u32 umult = reg[(opcode & 0x0F)].I;
      u32 usource = reg[(opcode >> 8) & 0x0F].I;
      int destLo = (opcode >> 12) & 0x0F;         
      int destHi = (opcode >> 16) & 0x0F;
      u64 uTemp = (u64)reg[destHi].I;
      uTemp <<= 32;
      uTemp |= (u64)reg[destLo].I;
      uTemp += ((u64)umult)*((u64)usource);
      reg[destLo].I = (u32)uTemp;
      reg[destHi].I = (u32)(uTemp >> 32);
    }
    break;
  case 0x0b9:
    {
      // UMLALS RdLo, RdHi, Rn, Rs
      u32 umult = reg[(opcode & 0x0F)].I;
      u32 usource = reg[(opcode >> 8) & 0x0F].I;
      int destLo = (opcode >> 12) & 0x0F;         
      int destHi = (opcode >> 16) & 0x0F;
      u64 uTemp = (u64)reg[destHi].I;
      uTemp <<= 32;
      uTemp |= (u64)reg[destLo].I;
      uTemp += ((u64)umult)*((u64)usource);
      reg[destLo].I = (u32)uTemp;
      reg[destHi].I = (u32)(uTemp >> 32);
      Z_FLAG = (uTemp) ? false : true;
      N_FLAG = (reg[destHi].I & 0x80000000) ? true : false;
    }
    break;
    ARITHMETIC_DATA_OPCODE(OP_SBC,  OP_SBC, 0x0c0);
    ARITHMETIC_DATA_OPCODE(OP_SBCS, OP_SBC, 0x0d0);
  case 0x0c9:
    {
      // SMULL RdLo, RdHi, Rm, Rs
      int destLo = (opcode >> 12) & 0x0F;         
      int destHi = (opcode >> 16) & 0x0F;
      u32 rs = reg[(opcode >> 8) & 0x0F].I;
      s64 m = (s32)reg[(opcode & 0x0F)].I;
      s64 s = (s32)rs;
      s64 sTemp = m*s;
      reg[destLo].I = (u32)sTemp;
      reg[destHi].I = (u32)(sTemp >> 32);
      if(((s32)rs) < 0)
        rs = ~rs;
    }
    break;
  case 0x0d9:
    {
      // SMULLS RdLo, RdHi, Rm, Rs
      int destLo = (opcode >> 12) & 0x0F;         
      int destHi = (opcode >> 16) & 0x0F;
      u32 rs = reg[(opcode >> 8) & 0x0F].I;
      s64 m = (s32)reg[(opcode & 0x0F)].I;
      s64 s = (s32)rs;
      s64 sTemp = m*s;
      reg[destLo].I = (u32)sTemp;
      reg[destHi].I = (u32)(sTemp >> 32);
      Z_FLAG = (sTemp) ? false : true;
      N_FLAG = (sTemp < 0) ? true : false;
      if(((s32)rs) < 0)
        rs = ~rs;
    }
    break;
    ARITHMETIC_DATA_OPCODE(OP_RSC,  OP_RSC, 0x0e0);
    ARITHMETIC_DATA_OPCODE(OP_RSCS, OP_RSC, 0x0f0);
  case 0x0e9:
    {
      // SMLAL RdLo, RdHi, Rm, Rs
      int destLo = (opcode >> 12) & 0x0F;         
      int destHi = (opcode >> 16) & 0x0F;
      u32 rs = reg[(opcode >> 8) & 0x0F].I;
      s64 m = (s32)reg[(opcode & 0x0F)].I;
      s64 s = (s32)rs;
      s64 sTemp = (u64)reg[destHi].I;
      sTemp <<= 32;
      sTemp |= (u64)reg[destLo].I;
      sTemp += m*s;
      reg[destLo].I = (u32)sTemp;
      reg[destHi].I = (u32)(sTemp >> 32);
      if(((s32)rs) < 0)
        rs = ~rs;
    }
    break;
  case 0x0f9:
    {
      // SMLALS RdLo, RdHi, Rm, Rs
      int destLo = (opcode >> 12) & 0x0F;         
      int destHi = (opcode >> 16) & 0x0F;
      u32 rs = reg[(opcode >> 8) & 0x0F].I;
      s64 m = (s32)reg[(opcode & 0x0F)].I;
      s64 s = (s32)rs;
      s64 sTemp = (u64)reg[destHi].I;
      sTemp <<= 32;
      sTemp |= (u64)reg[destLo].I;
      sTemp += m*s;
      reg[destLo].I = (u32)sTemp;
      reg[destHi].I = (u32)(sTemp >> 32);
      Z_FLAG = (sTemp) ? false : true;
      N_FLAG = (sTemp < 0) ? true : false;
      if(((s32)rs) < 0)
        rs = ~rs;
    }
    break;
    LOGICAL_DATA_OPCODE(OP_TST, OP_TST, 0x110);
  case 0x100:
    // MRS Rd, CPSR
    // TODO: check if right instruction....
    CPUUpdateCPSR();
    reg[(opcode >> 12) & 0x0F].I = reg[16].I;
    break;
  case 0x109:
    {
      // SWP Rd, Rm, [Rn]
      u32 address = reg[(opcode >> 16) & 15].I;
      u32 temp = CPUReadMemory(address);
      CPUWriteMemory(address, reg[opcode&15].I);
      reg[(opcode >> 12) & 15].I = temp;
    }
    break;
    LOGICAL_DATA_OPCODE(OP_TEQ, OP_TEQ, 0x130);
  case 0x120:
    {
      // MSR CPSR_fields, Rm
      CPUUpdateCPSR();
      u32 value = reg[opcode & 15].I;
      u32 newValue = reg[16].I;
      if(armMode > 0x10) {
        if(opcode & 0x00010000)
          newValue = (newValue & 0xFFFFFF00) | (value & 0x000000FF);
        if(opcode & 0x00020000)
          newValue = (newValue & 0xFFFF00FF) | (value & 0x0000FF00);
        if(opcode & 0x00040000)
          newValue = (newValue & 0xFF00FFFF) | (value & 0x00FF0000);
      }
      if(opcode & 0x00080000)
        newValue = (newValue & 0x00FFFFFF) | (value & 0xFF000000);
      newValue |= 0x10;
      CPUSwitchMode_int_bool(newValue & 0x1f, false);
      reg[16].I = newValue;
      CPUUpdateFlags_bool(true);
    }
    break;
  case 0x121:
    {
      // BX Rm
      // TODO: check if right instruction...
      int base = opcode & 0x0F;
      armState = reg[base].I & 1 ? false : true;
      if(armState) {
        reg[15].I = reg[base].I & 0xFFFFFFFC;
        armNextPC = reg[15].I;
        reg[15].I += 4;
      } else {
        reg[15].I = reg[base].I & 0xFFFFFFFE;
        armNextPC = reg[15].I;
        reg[15].I += 2;
      }
    }
    break;
    ARITHMETIC_DATA_OPCODE(OP_CMP, OP_CMP, 0x150);
  case 0x140:
    // MRS Rd, SPSR
    // TODO: check if right instruction...
    reg[(opcode >> 12) & 0x0F].I = reg[17].I;
    break;
  case 0x149:
    {
      // SWPB Rd, Rm, [Rn]
      u32 address = reg[(opcode >> 16) & 15].I;
      u32 temp = CPUReadByte(address);
      CPUWriteByte(address, reg[opcode&15].B.B0);
      reg[(opcode>>12)&15].I = temp;
    }
    break;
    ARITHMETIC_DATA_OPCODE(OP_CMN, OP_CMN, 0x170);
  case 0x160:
    {
      // MSR SPSR_fields, Rm
      u32 value = reg[opcode & 15].I;
      if(armMode > 0x10 && armMode < 0x1f) {
        if(opcode & 0x00010000)
          reg[17].I = (reg[17].I & 0xFFFFFF00) | (value & 0x000000FF);
        if(opcode & 0x00020000)
          reg[17].I = (reg[17].I & 0xFFFF00FF) | (value & 0x0000FF00);
        if(opcode & 0x00040000)
          reg[17].I = (reg[17].I & 0xFF00FFFF) | (value & 0x00FF0000);
        if(opcode & 0x00080000)
          reg[17].I = (reg[17].I & 0x00FFFFFF) | (value & 0xFF000000);
      }
    }
    break;
    LOGICAL_DATA_OPCODE             (OP_ORR,  OP_ORR, 0x180);
    LOGICAL_DATA_OPCODE             (OP_ORRS, OP_ORR, 0x190);
    LOGICAL_DATA_OPCODE_WITHOUT_base(OP_MOV,  OP_MOV, 0x1a0);
    LOGICAL_DATA_OPCODE_WITHOUT_base(OP_MOVS, OP_MOV, 0x1b0);
    LOGICAL_DATA_OPCODE             (OP_BIC,  OP_BIC, 0x1c0);
    LOGICAL_DATA_OPCODE             (OP_BICS, OP_BIC, 0x1d0);
    LOGICAL_DATA_OPCODE_WITHOUT_base(OP_MVN,  OP_MVN, 0x1e0);
    LOGICAL_DATA_OPCODE_WITHOUT_base(OP_MVNS, OP_MVN, 0x1f0);
#ifdef BKPT_SUPPORT
  case 0x127:
  case 0x7ff: // for GDB support
    extern void (*dbgSignal)(int,int);
    reg[15].I -= 4;
    armNextPC -= 4;
    dbgSignal(5, (opcode & 0x0f)|((opcode>>4) & 0xfff0));
    return;
#endif
  case 0x320:
  case 0x321:
  case 0x322:
  case 0x323:
  case 0x324:
  case 0x325:
  case 0x326:
  case 0x327:
  case 0x328:
  case 0x329:
  case 0x32a:
  case 0x32b:
  case 0x32c:
  case 0x32d:
  case 0x32e:
  case 0x32f:
    {
      // MSR CPSR_fields, #
      CPUUpdateCPSR();
      u32 value = opcode & 0xFF;
      int shift = (opcode & 0xF00) >> 7;
      if(shift) {
        ROR_IMM_MSR;
      }
      u32 newValue = reg[16].I;
      if(armMode > 0x10) {
        if(opcode & 0x00010000)
          newValue = (newValue & 0xFFFFFF00) | (value & 0x000000FF);
        if(opcode & 0x00020000)
          newValue = (newValue & 0xFFFF00FF) | (value & 0x0000FF00);
        if(opcode & 0x00040000)
          newValue = (newValue & 0xFF00FFFF) | (value & 0x00FF0000);
      }
      if(opcode & 0x00080000)
        newValue = (newValue & 0x00FFFFFF) | (value & 0xFF000000);

      newValue |= 0x10;

      CPUSwitchMode_int_bool(newValue & 0x1f, false);
      reg[16].I = newValue;
      CPUUpdateFlags_bool(true);
    }
    break;
  case 0x360:
  case 0x361:
  case 0x362:
  case 0x363:
  case 0x364:
  case 0x365:
  case 0x366:
  case 0x367:
  case 0x368:
  case 0x369:
  case 0x36a:
  case 0x36b:
  case 0x36c:
  case 0x36d:
  case 0x36e:
  case 0x36f:
    {
      // MSR SPSR_fields, #
      if(armMode > 0x10 && armMode < 0x1f) {
        u32 value = opcode & 0xFF;
        int shift = (opcode & 0xF00) >> 7;
        if(shift) {
          ROR_IMM_MSR;
        }
        if(opcode & 0x00010000)
          reg[17].I = (reg[17].I & 0xFFFFFF00) | (value & 0x000000FF);
        if(opcode & 0x00020000)
          reg[17].I = (reg[17].I & 0xFFFF00FF) | (value & 0x0000FF00);
        if(opcode & 0x00040000)
          reg[17].I = (reg[17].I & 0xFF00FFFF) | (value & 0x00FF0000);
        if(opcode & 0x00080000)
          reg[17].I = (reg[17].I & 0x00FFFFFF) | (value & 0xFF000000);
      }
    }
  break;
  CASE_16(0x400)
  // T versions shouldn't be different on GBA      
  CASE_16(0x420)
    {
      // STR Rd, [Rn], -#
      int offset = opcode & 0xFFF;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      CPUWriteMemory(address, reg[dest].I);
      reg[base].I = address - offset;
    }
    break;
  CASE_16(0x480)
    // T versions shouldn't be different on GBA
  CASE_16(0x4a0)
    {
      // STR Rd, [Rn], #
      int offset = opcode & 0xFFF;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      CPUWriteMemory(address, reg[dest].I);
      reg[base].I = address + offset;
    }
    break;
  CASE_16(0x500)
    {
      // STR Rd, [Rn, -#]
      int offset = opcode & 0xFFF;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - offset;
      CPUWriteMemory(address, reg[dest].I);
    }
    break;
  CASE_16(0x520)
    {
      // STR Rd, [Rn, -#]!
      int offset = opcode & 0xFFF;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - offset;
      reg[base].I = address;
      CPUWriteMemory(address, reg[dest].I);
    }
    break;
  CASE_16(0x580)
    {
      // STR Rd, [Rn, #]
      int offset = opcode & 0xFFF;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + offset;
      CPUWriteMemory(address, reg[dest].I);
    }
    break;
  CASE_16(0x5a0)
    {
      // STR Rd, [Rn, #]!
      int offset = opcode & 0xFFF;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + offset;
      reg[base].I = address;
      CPUWriteMemory(address, reg[dest].I);
    }
    break;
  CASE_16(0x410)
    {
      // LDR Rd, [Rn], -#
      int offset = opcode & 0xFFF;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      reg[dest].I = CPUReadMemory(address);
      if(dest != base)
        reg[base].I -= offset;
      if(dest == 15) {
        reg[15].I &= 0xFFFFFFFC;
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  CASE_16(0x430)
    {
      // LDRT Rd, [Rn], -#
      int offset = opcode & 0xFFF;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      reg[dest].I = CPUReadMemory(address);
      if(dest != base)
        reg[base].I -= offset;
    }
    break;
  CASE_16(0x490)
    {
      // LDR Rd, [Rn], #
      int offset = opcode & 0xFFF;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      reg[dest].I = CPUReadMemory(address);
      if(dest != base)
        reg[base].I += offset;
      if(dest == 15) {
        reg[15].I &= 0xFFFFFFFC;
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  CASE_16(0x4b0)
    {
      // LDRT Rd, [Rn], #
      int offset = opcode & 0xFFF;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      reg[dest].I = CPUReadMemory(address);
      if(dest != base)
        reg[base].I += offset;
    }
    break;
  CASE_16(0x510)
    {
      // LDR Rd, [Rn, -#]
      int offset = opcode & 0xFFF;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - offset;
      reg[dest].I = CPUReadMemory(address);
      if(dest == 15) {
        reg[15].I &= 0xFFFFFFFC;
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  CASE_16(0x530)
    {
      // LDR Rd, [Rn, -#]!
      int offset = opcode & 0xFFF;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - offset;
      reg[dest].I = CPUReadMemory(address);
      if(dest != base)
        reg[base].I = address;
      if(dest == 15) {
        reg[15].I &= 0xFFFFFFFC;
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  CASE_16(0x590)
    {
      // LDR Rd, [Rn, #]
      int offset = opcode & 0xFFF;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + offset;
      reg[dest].I = CPUReadMemory(address);
      if(dest == 15) {
        reg[15].I &= 0xFFFFFFFC;
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  CASE_16(0x5b0)
    {
      // LDR Rd, [Rn, #]!
      int offset = opcode & 0xFFF;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + offset;
      reg[dest].I = CPUReadMemory(address);
      if(dest != base)
        reg[base].I = address;
      if(dest == 15) {
        reg[15].I &= 0xFFFFFFFC;
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  CASE_16(0x440)
    // T versions shouldn't be different on GBA      
  CASE_16(0x460)
    {
      // STRB Rd, [Rn], -#
      int offset = opcode & 0xFFF;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      CPUWriteByte(address, reg[dest].B.B0);
      reg[base].I = address - offset;
    }
    break;
  CASE_16(0x4c0)
    // T versions shouldn't be different on GBA
  CASE_16(0x4e0)
    // STRB Rd, [Rn], #
    {
      int offset = opcode & 0xFFF;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      CPUWriteByte(address, reg[dest].B.B0);
      reg[base].I = address + offset;
    }
    break;
  CASE_16(0x540)
    {
      // STRB Rd, [Rn, -#]
      int offset = opcode & 0xFFF;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - offset;
      CPUWriteByte(address, reg[dest].B.B0);
    }
    break;
  CASE_16(0x560)
    {
      // STRB Rd, [Rn, -#]!
      int offset = opcode & 0xFFF;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - offset;
      reg[base].I = address;
      CPUWriteByte(address, reg[dest].B.B0);
    }
    break;
  CASE_16(0x5c0)
    {
      // STRB Rd, [Rn, #]
      int offset = opcode & 0xFFF;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + offset;
      CPUWriteByte(address, reg[dest].B.B0);
    }
    break;
  CASE_16(0x5e0)
    {
      // STRB Rd, [Rn, #]!
      int offset = opcode & 0xFFF;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + offset;
      reg[base].I = address;
      CPUWriteByte(address, reg[dest].I);
    }
    break;
  CASE_16(0x450)
    // T versions shouldn't be different
  CASE_16(0x470)
    {
      // LDRB Rd, [Rn], -#
      int offset = opcode & 0xFFF;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      reg[dest].I = CPUReadByte(address);
      if(dest != base)
        reg[base].I -= offset;
    }
    break;
  CASE_16(0x4d0)
  CASE_16(0x4f0) // T versions should not be different
    {
      // LDRB Rd, [Rn], #
      int offset = opcode & 0xFFF;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      reg[dest].I = CPUReadByte(address);
      if(dest != base)
        reg[base].I += offset;
    }
    break;
  CASE_16(0x550)
    {
      // LDRB Rd, [Rn, -#]
      int offset = opcode & 0xFFF;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - offset;
      reg[dest].I = CPUReadByte(address);
    }
    break;
  CASE_16(0x570)
    {
      // LDRB Rd, [Rn, -#]!
      int offset = opcode & 0xFFF;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - offset;
      reg[dest].I = CPUReadByte(address);
      if(dest != base)
        reg[base].I = address;
    }
    break;
  CASE_16(0x5d0)
    {
      // LDRB Rd, [Rn, #]
      int offset = opcode & 0xFFF;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + offset;
      reg[dest].I = CPUReadByte(address);
    }
    break;
  CASE_16(0x5f0)
    {
      // LDRB Rd, [Rn, #]!
      int offset = opcode & 0xFFF;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + offset;
      reg[dest].I = CPUReadByte(address);
      if(dest != base)
        reg[base].I = address;
    }
    break;
  case 0x600:
  case 0x608:
    // T versions are the same
  case 0x620:
  case 0x628:
    {
      // STR Rd, [Rn], -Rm, LSL #
      int offset = reg[opcode & 15].I << ((opcode>>7)& 31);
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      CPUWriteMemory(address, reg[dest].I);
      reg[base].I = address - offset;
    }
    break;
  case 0x602:
  case 0x60a:
    // T versions are the same
  case 0x622:
  case 0x62a:
    {
      // STR Rd, [Rn], -Rm, LSR #
      int shift = (opcode >> 7) & 31;
      int offset = shift ? reg[opcode & 15].I >> shift : 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      CPUWriteMemory(address, reg[dest].I);
      reg[base].I = address - offset;
    }
    break;
  case 0x604:
  case 0x60c:
    // T versions are the same
  case 0x624:
  case 0x62c:
    {
      // STR Rd, [Rn], -Rm, ASR #
      int shift = (opcode >> 7) & 31;
      int offset;
      if(shift)
        offset = (int)((s32)reg[opcode & 15].I >> shift);
      else if(reg[opcode & 15].I & 0x80000000)
        offset = 0xFFFFFFFF;
      else
        offset = 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      CPUWriteMemory(address, reg[dest].I);
      reg[base].I = address - offset;
    }
    break;
  case 0x606:
  case 0x60e:
    // T versions are the same
  case 0x626:
  case 0x62e:
    {
      // STR Rd, [Rn], -Rm, ROR #
      int shift = (opcode >> 7) & 31;
      u32 value = reg[opcode & 15].I;
      if(shift) {
        ROR_VALUE;
      } else {
        RCR_VALUE;
      }
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      CPUWriteMemory(address, reg[dest].I);
      reg[base].I = address - value;
    }
    break;
  case 0x680:
  case 0x688:
    // T versions are the same
  case 0x6a0:
  case 0x6a8:
    {
      // STR Rd, [Rn], Rm, LSL #
      int offset = reg[opcode & 15].I << ((opcode>>7)& 31);
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      CPUWriteMemory(address, reg[dest].I);
      reg[base].I = address + offset;
    }
    break;
  case 0x682:
  case 0x68a:
    // T versions are the same
  case 0x6a2:
  case 0x6aa:
    {
      // STR Rd, [Rn], Rm, LSR #
      int shift = (opcode >> 7) & 31;
      int offset = shift ? reg[opcode & 15].I >> shift : 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      CPUWriteMemory(address, reg[dest].I);
      reg[base].I = address + offset;
    }
    break;
  case 0x684:
  case 0x68c:
    // T versions are the same
  case 0x6a4:
  case 0x6ac:
    {
      // STR Rd, [Rn], Rm, ASR #
      int shift = (opcode >> 7) & 31;
      int offset;
      if(shift)
        offset = (int)((s32)reg[opcode & 15].I >> shift);
      else if(reg[opcode & 15].I & 0x80000000)
        offset = 0xFFFFFFFF;
      else
        offset = 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      CPUWriteMemory(address, reg[dest].I);
      reg[base].I = address + offset;
    }
    break;
  case 0x686:
  case 0x68e:
    // T versions are the same
  case 0x6a6:
  case 0x6ae:
    {
      // STR Rd, [Rn], Rm, ROR #
      int shift = (opcode >> 7) & 31;
      u32 value = reg[opcode & 15].I;
      if(shift) {
        ROR_VALUE;
      } else {
        RCR_VALUE;
      }
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      CPUWriteMemory(address, reg[dest].I);
      reg[base].I = address + value;
    }
    break;
  case 0x700:
  case 0x708:
    {
      // STR Rd, [Rn, -Rm, LSL #]
      int offset = reg[opcode & 15].I << ((opcode>>7)& 31);
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - offset;
      CPUWriteMemory(address, reg[dest].I);
    }
    break;
  case 0x702:
  case 0x70a:
    {
      // STR Rd, [Rn, -Rm, LSR #]
      int shift = (opcode >> 7) & 31;
      int offset = shift ? reg[opcode & 15].I >> shift : 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - offset;
      CPUWriteMemory(address, reg[dest].I);
    }
    break;
  case 0x704:
  case 0x70c:
    {
      // STR Rd, [Rn, -Rm, ASR #]
      int shift = (opcode >> 7) & 31;
      int offset;
      if(shift)
        offset = (int)((s32)reg[opcode & 15].I >> shift);
      else if(reg[opcode & 15].I & 0x80000000)
        offset = 0xFFFFFFFF;
      else
        offset = 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - offset;
      CPUWriteMemory(address, reg[dest].I);
    }
    break;
  case 0x706:
  case 0x70e:
    {
      // STR Rd, [Rn, -Rm, ROR #]
      int shift = (opcode >> 7) & 31;
      u32 value = reg[opcode & 15].I;
      if(shift) {
        ROR_VALUE;
      } else {
        RCR_VALUE;
      }
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - value;
      CPUWriteMemory(address, reg[dest].I);
    }
    break;
  case 0x720:
  case 0x728:
    {
      // STR Rd, [Rn, -Rm, LSL #]!
      int offset = reg[opcode & 15].I << ((opcode>>7)& 31);
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - offset;
      reg[base].I = address;
      CPUWriteMemory(address, reg[dest].I);
    }
    break;
  case 0x722:
  case 0x72a:
    {
      // STR Rd, [Rn, -Rm, LSR #]!
      int shift = (opcode >> 7) & 31;
      int offset = shift ? reg[opcode & 15].I >> shift : 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - offset;
      reg[base].I = address;      
      CPUWriteMemory(address, reg[dest].I);
    }
    break;
  case 0x724:
  case 0x72c:
    {
      // STR Rd, [Rn, -Rm, ASR #]!
      int shift = (opcode >> 7) & 31;
      int offset;
      if(shift)
        offset = (int)((s32)reg[opcode & 15].I >> shift);
      else if(reg[opcode & 15].I & 0x80000000)
        offset = 0xFFFFFFFF;
      else
        offset = 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - offset;
      reg[base].I = address;
      CPUWriteMemory(address, reg[dest].I);
    }
    break;
  case 0x726:
  case 0x72e:
    {
      // STR Rd, [Rn, -Rm, ROR #]!
      int shift = (opcode >> 7) & 31;
      u32 value = reg[opcode & 15].I;
      if(shift) {
        ROR_VALUE;
      } else {
        RCR_VALUE;
      }
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - value;
      reg[base].I = address;      
      CPUWriteMemory(address, reg[dest].I);
    }
    break;
  case 0x780:
  case 0x788:
    {
      // STR Rd, [Rn, Rm, LSL #]
      int offset = reg[opcode & 15].I << ((opcode>>7)& 31);
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + offset;
      CPUWriteMemory(address, reg[dest].I);
    }
    break;
  case 0x782:
  case 0x78a:
    {
      // STR Rd, [Rn, Rm, LSR #]
      int shift = (opcode >> 7) & 31;
      int offset = shift ? reg[opcode & 15].I >> shift : 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + offset;
      CPUWriteMemory(address, reg[dest].I);
    }
    break;
  case 0x784:
  case 0x78c:
    {
      // STR Rd, [Rn, Rm, ASR #]
      int shift = (opcode >> 7) & 31;
      int offset;
      if(shift)
        offset = (int)((s32)reg[opcode & 15].I >> shift);
      else if(reg[opcode & 15].I & 0x80000000)
        offset = 0xFFFFFFFF;
      else
        offset = 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + offset;
      CPUWriteMemory(address, reg[dest].I);
    }
    break;
  case 0x786:
  case 0x78e:
    {
      // STR Rd, [Rn, Rm, ROR #]
      int shift = (opcode >> 7) & 31;
      u32 value = reg[opcode & 15].I;
      if(shift) {
        ROR_VALUE;
      } else {
        RCR_VALUE;
      }
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + value;
      CPUWriteMemory(address, reg[dest].I);
    }
    break;
  case 0x7a0:
  case 0x7a8:
    {
      // STR Rd, [Rn, Rm, LSL #]!
      int offset = reg[opcode & 15].I << ((opcode>>7)& 31);
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + offset;
      reg[base].I = address;
      CPUWriteMemory(address, reg[dest].I);
    }
    break;
  case 0x7a2:
  case 0x7aa:
    {
      // STR Rd, [Rn, Rm, LSR #]!
      int shift = (opcode >> 7) & 31;
      int offset = shift ? reg[opcode & 15].I >> shift : 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + offset;
      reg[base].I = address;      
      CPUWriteMemory(address, reg[dest].I);
    }
    break;
  case 0x7a4:
  case 0x7ac:
    {
      // STR Rd, [Rn, Rm, ASR #]!
      int shift = (opcode >> 7) & 31;
      int offset;
      if(shift)
        offset = (int)((s32)reg[opcode & 15].I >> shift);
      else if(reg[opcode & 15].I & 0x80000000)
        offset = 0xFFFFFFFF;
      else
        offset = 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + offset;
      reg[base].I = address;
      CPUWriteMemory(address, reg[dest].I);
    }
    break;
  case 0x7a6:
  case 0x7ae:
    {
      // STR Rd, [Rn, Rm, ROR #]!
      int shift = (opcode >> 7) & 31;
      u32 value = reg[opcode & 15].I;
      if(shift) {
        ROR_VALUE;
      } else {
        RCR_VALUE;
      }
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + value;
      reg[base].I = address;      
      CPUWriteMemory(address, reg[dest].I);
    }
    break;
  case 0x610:
  case 0x618:
    // T versions are the same
  case 0x630:
  case 0x638:
    {
      // LDR Rd, [Rn], -Rm, LSL #
      int offset = reg[opcode & 15].I << ((opcode>>7)& 31);
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      reg[dest].I = CPUReadMemory(address);
      if(dest != base)
        reg[base].I = address - offset;
      if(dest == 15) {
        reg[15].I &= 0xFFFFFFFC;
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  case 0x612:
  case 0x61a:
    // T versions are the same
  case 0x632:
  case 0x63a:
    {
      // LDR Rd, [Rn], -Rm, LSR #
      int shift = (opcode >> 7) & 31;
      int offset = shift ? reg[opcode & 15].I >> shift : 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      reg[dest].I = CPUReadMemory(address);
      if(dest != base)
        reg[base].I = address - offset;
      if(dest == 15) {
        reg[15].I &= 0xFFFFFFFC;
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  case 0x614:
  case 0x61c:
    // T versions are the same
  case 0x634:
  case 0x63c:
    {
      // LDR Rd, [Rn], -Rm, ASR #
      int shift = (opcode >> 7) & 31;
      int offset;
      if(shift)
        offset = (int)((s32)reg[opcode & 15].I >> shift);
      else if(reg[opcode & 15].I & 0x80000000)
        offset = 0xFFFFFFFF;
      else
        offset = 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      reg[dest].I = CPUReadMemory(address);
      if(dest != base)
        reg[base].I = address - offset;
      if(dest == 15) {
        reg[15].I &= 0xFFFFFFFC;
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  case 0x616:
  case 0x61e:
    // T versions are the same
  case 0x636:
  case 0x63e:
    {
      // LDR Rd, [Rn], -Rm, ROR #
      int shift = (opcode >> 7) & 31;
      u32 value = reg[opcode & 15].I;
      if(shift) {
        ROR_VALUE;
      } else {
        RCR_VALUE;
      }
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      reg[dest].I = CPUReadMemory(address);
      if(dest != base)
        reg[base].I = address - value;
      if(dest == 15) {
        reg[15].I &= 0xFFFFFFFC;
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  case 0x690:
  case 0x698:
    // T versions are the same
  case 0x6b0:
  case 0x6b8:
    {
      // LDR Rd, [Rn], Rm, LSL #
      int offset = reg[opcode & 15].I << ((opcode>>7)& 31);
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      reg[dest].I = CPUReadMemory(address);
      if(dest != base)
        reg[base].I = address + offset;
      if(dest == 15) {
        reg[15].I &= 0xFFFFFFFC;
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  case 0x692:
  case 0x69a:
    // T versions are the same
  case 0x6b2:
  case 0x6ba:
    {
      // LDR Rd, [Rn], Rm, LSR #
      int shift = (opcode >> 7) & 31;
      int offset = shift ? reg[opcode & 15].I >> shift : 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      reg[dest].I = CPUReadMemory(address);
      if(dest != base)
        reg[base].I = address + offset;
      if(dest == 15) {
        reg[15].I &= 0xFFFFFFFC;
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  case 0x694:
  case 0x69c:
    // T versions are the same
  case 0x6b4:
  case 0x6bc:
    {
      // LDR Rd, [Rn], Rm, ASR #
      int shift = (opcode >> 7) & 31;
      int offset;
      if(shift)
        offset = (int)((s32)reg[opcode & 15].I >> shift);
      else if(reg[opcode & 15].I & 0x80000000)
        offset = 0xFFFFFFFF;
      else
        offset = 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      reg[dest].I = CPUReadMemory(address);
      if(dest != base)
        reg[base].I = address + offset;
      if(dest == 15) {
        reg[15].I &= 0xFFFFFFFC;
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  case 0x696:
  case 0x69e:
    // T versions are the same
  case 0x6b6:
  case 0x6be:
    {
      // LDR Rd, [Rn], Rm, ROR #
      int shift = (opcode >> 7) & 31;
      u32 value = reg[opcode & 15].I;
      if(shift) {
        ROR_VALUE;
      } else {
        RCR_VALUE;
      }
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      reg[dest].I = CPUReadMemory(address);
      if(dest != base)
        reg[base].I = address + value;
      if(dest == 15) {
        reg[15].I &= 0xFFFFFFFC;
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  case 0x710:
  case 0x718:
    {
      // LDR Rd, [Rn, -Rm, LSL #]
      int offset = reg[opcode & 15].I << ((opcode>>7)& 31);
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - offset;
      reg[dest].I = CPUReadMemory(address);
      if(dest == 15) {
        reg[15].I &= 0xFFFFFFFC;
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  case 0x712:
  case 0x71a:
    {
      // LDR Rd, [Rn, -Rm, LSR #]
      int shift = (opcode >> 7) & 31;
      int offset = shift ? reg[opcode & 15].I >> shift : 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - offset;
      reg[dest].I = CPUReadMemory(address);
      if(dest == 15) {
        reg[15].I &= 0xFFFFFFFC;
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  case 0x714:
  case 0x71c:
    {
      // LDR Rd, [Rn, -Rm, ASR #]
      int shift = (opcode >> 7) & 31;
      int offset;
      if(shift)
        offset = (int)((s32)reg[opcode & 15].I >> shift);
      else if(reg[opcode & 15].I & 0x80000000)
        offset = 0xFFFFFFFF;
      else
        offset = 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - offset;
      reg[dest].I = CPUReadMemory(address);
      if(dest == 15) {
        reg[15].I &= 0xFFFFFFFC;
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  case 0x716:
  case 0x71e:
    {
      // LDR Rd, [Rn, -Rm, ROR #]
      int shift = (opcode >> 7) & 31;
      u32 value = reg[opcode & 15].I;
      if(shift) {
        ROR_VALUE;
      } else {
        RCR_VALUE;
      }
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - value;
      reg[dest].I = CPUReadMemory(address);
      if(dest == 15) {
        reg[15].I &= 0xFFFFFFFC;
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  case 0x730:
  case 0x738:
    {
      // LDR Rd, [Rn, -Rm, LSL #]!
      int offset = reg[opcode & 15].I << ((opcode>>7)& 31);
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - offset;
      reg[dest].I = CPUReadMemory(address);
      if(dest != base)
        reg[base].I = address;
      if(dest == 15) {
        reg[15].I &= 0xFFFFFFFC;
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  case 0x732:
  case 0x73a:
    {
      // LDR Rd, [Rn, -Rm, LSR #]!
      int shift = (opcode >> 7) & 31;
      int offset = shift ? reg[opcode & 15].I >> shift : 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - offset;
      reg[dest].I = CPUReadMemory(address);
      if(dest != base)
        reg[base].I = address;      
      if(dest == 15) {
        reg[15].I &= 0xFFFFFFFC;
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  case 0x734:
  case 0x73c:
    {
      // LDR Rd, [Rn, -Rm, ASR #]!
      int shift = (opcode >> 7) & 31;
      int offset;
      if(shift)
        offset = (int)((s32)reg[opcode & 15].I >> shift);
      else if(reg[opcode & 15].I & 0x80000000)
        offset = 0xFFFFFFFF;
      else
        offset = 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - offset;
      reg[dest].I = CPUReadMemory(address);
      if(dest != base)
        reg[base].I = address;
      if(dest == 15) {
        reg[15].I &= 0xFFFFFFFC;
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  case 0x736:
  case 0x73e:
    {
      // LDR Rd, [Rn, -Rm, ROR #]!
      int shift = (opcode >> 7) & 31;
      u32 value = reg[opcode & 15].I;
      if(shift) {
        ROR_VALUE;
      } else {
        RCR_VALUE;
      }
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - value;
      reg[dest].I = CPUReadMemory(address);
      if(dest != base)
        reg[base].I = address;      
      if(dest == 15) {
        reg[15].I &= 0xFFFFFFFC;
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  case 0x790:
  case 0x798:
    {
      // LDR Rd, [Rn, Rm, LSL #]
      int offset = reg[opcode & 15].I << ((opcode>>7)& 31);
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + offset;
      reg[dest].I = CPUReadMemory(address);
      if(dest == 15) {
        reg[15].I &= 0xFFFFFFFC;
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  case 0x792:
  case 0x79a:
    {
      // LDR Rd, [Rn, Rm, LSR #]
      int shift = (opcode >> 7) & 31;
      int offset = shift ? reg[opcode & 15].I >> shift : 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + offset;
      reg[dest].I = CPUReadMemory(address);
      if(dest == 15) {
        reg[15].I &= 0xFFFFFFFC;
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  case 0x794:
  case 0x79c:
    {
      // LDR Rd, [Rn, Rm, ASR #]
      int shift = (opcode >> 7) & 31;
      int offset;
      if(shift)
        offset = (int)((s32)reg[opcode & 15].I >> shift);
      else if(reg[opcode & 15].I & 0x80000000)
        offset = 0xFFFFFFFF;
      else
        offset = 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + offset;
      reg[dest].I = CPUReadMemory(address);
      if(dest == 15) {
        reg[15].I &= 0xFFFFFFFC;
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  case 0x796:
  case 0x79e:
    {
      // LDR Rd, [Rn, Rm, ROR #]
      int shift = (opcode >> 7) & 31;
      u32 value = reg[opcode & 15].I;
      if(shift) {
        ROR_VALUE;
      } else {
        RCR_VALUE;
      }
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + value;
      reg[dest].I = CPUReadMemory(address);
      if(dest == 15) {
        reg[15].I &= 0xFFFFFFFC;
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  case 0x7b0:
  case 0x7b8:
    {
      // LDR Rd, [Rn, Rm, LSL #]!
      int offset = reg[opcode & 15].I << ((opcode>>7)& 31);
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + offset;
      reg[dest].I = CPUReadMemory(address);
      if(dest != base)
        reg[base].I = address;
      if(dest == 15) {
        reg[15].I &= 0xFFFFFFFC;
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  case 0x7b2:
  case 0x7ba:
    {
      // LDR Rd, [Rn, Rm, LSR #]!
      int shift = (opcode >> 7) & 31;
      int offset = shift ? reg[opcode & 15].I >> shift : 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + offset;
      reg[dest].I = CPUReadMemory(address);
      if(dest != base)
        reg[base].I = address;      
      if(dest == 15) {
        reg[15].I &= 0xFFFFFFFC;
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  case 0x7b4:
  case 0x7bc:
    {
      // LDR Rd, [Rn, Rm, ASR #]!
      int shift = (opcode >> 7) & 31;
      int offset;
      if(shift)
        offset = (int)((s32)reg[opcode & 15].I >> shift);
      else if(reg[opcode & 15].I & 0x80000000)
        offset = 0xFFFFFFFF;
      else
        offset = 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + offset;
      reg[dest].I = CPUReadMemory(address);
      if(dest != base)
        reg[base].I = address;
      if(dest == 15) {
        reg[15].I &= 0xFFFFFFFC;
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  case 0x7b6:
  case 0x7be:
    {
      // LDR Rd, [Rn, Rm, ROR #]!
      int shift = (opcode >> 7) & 31;
      u32 value = reg[opcode & 15].I;
      if(shift) {
        ROR_VALUE;
      } else {
        RCR_VALUE;
      }
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + value;
      reg[dest].I = CPUReadMemory(address);
      if(dest != base)
        reg[base].I = address;
      if(dest == 15) {
        reg[15].I &= 0xFFFFFFFC;
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  case 0x640:
  case 0x648:
    // T versions are the same
  case 0x660:
  case 0x668:
    {
      // STRB Rd, [Rn], -Rm, LSL #
      int offset = reg[opcode & 15].I << ((opcode>>7)& 31);
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      CPUWriteByte(address, reg[dest].B.B0);
      reg[base].I = address - offset;
    }
    break;
  case 0x642:
  case 0x64a:
    // T versions are the same
  case 0x662:
  case 0x66a:
    {
      // STRB Rd, [Rn], -Rm, LSR #
      int shift = (opcode >> 7) & 31;
      int offset = shift ? reg[opcode & 15].I >> shift : 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      CPUWriteByte(address, reg[dest].B.B0);
      reg[base].I = address - offset;
    }
    break;
  case 0x644:
  case 0x64c:
    // T versions are the same
  case 0x664:
  case 0x66c:
    {
      // STRB Rd, [Rn], -Rm, ASR #
      int shift = (opcode >> 7) & 31;
      int offset;
      if(shift)
        offset = (int)((s32)reg[opcode & 15].I >> shift);
      else if(reg[opcode & 15].I & 0x80000000)
        offset = 0xFFFFFFFF;
      else
        offset = 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      CPUWriteByte(address, reg[dest].B.B0);
      reg[base].I = address - offset;
    }
    break;
  case 0x646:
  case 0x64e:
    // T versions are the same
  case 0x666:
  case 0x66e:
    {
      // STRB Rd, [Rn], -Rm, ROR #
      int shift = (opcode >> 7) & 31;
      u32 value = reg[opcode & 15].I;
      if(shift) {
        ROR_VALUE;
      } else {
        RCR_VALUE;
      }
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      CPUWriteByte(address, reg[dest].B.B0);
      reg[base].I = address - value;
    }
    break;
  case 0x6c0:
  case 0x6c8:
    // T versions are the same
  case 0x6e0:
  case 0x6e8:
    {
      // STRB Rd, [Rn], Rm, LSL #
      int offset = reg[opcode & 15].I << ((opcode>>7)& 31);
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      CPUWriteByte(address, reg[dest].B.B0);
      reg[base].I = address + offset;
    }
    break;
  case 0x6c2:
  case 0x6ca:
    // T versions are the same
  case 0x6e2:
  case 0x6ea:
    {
      // STRB Rd, [Rn], Rm, LSR #
      int shift = (opcode >> 7) & 31;
      int offset = shift ? reg[opcode & 15].I >> shift : 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      CPUWriteByte(address, reg[dest].B.B0);
      reg[base].I = address + offset;
    }
    break;
  case 0x6c4:
  case 0x6cc:
    // T versions are the same
  case 0x6e4:
  case 0x6ec:
    {
      // STR Rd, [Rn], Rm, ASR #
      int shift = (opcode >> 7) & 31;
      int offset;
      if(shift)
        offset = (int)((s32)reg[opcode & 15].I >> shift);
      else if(reg[opcode & 15].I & 0x80000000)
        offset = 0xFFFFFFFF;
      else
        offset = 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      CPUWriteByte(address, reg[dest].B.B0);
      reg[base].I = address + offset;
    }
    break;
  case 0x6c6:
  case 0x6ce:
    // T versions are the same
  case 0x6e6:
  case 0x6ee:
    {
      // STRB Rd, [Rn], Rm, ROR #
      int shift = (opcode >> 7) & 31;
      u32 value = reg[opcode & 15].I;
      if(shift) {
        ROR_VALUE;
      } else {
        RCR_VALUE;
      }
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      CPUWriteByte(address, reg[dest].B.B0);
      reg[base].I = address + value;
    }
    break;
  case 0x740:
  case 0x748:
    {
      // STRB Rd, [Rn, -Rm, LSL #]
      int offset = reg[opcode & 15].I << ((opcode>>7)& 31);
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - offset;
      CPUWriteByte(address, reg[dest].B.B0);
    }
    break;
  case 0x742:
  case 0x74a:
    {
      // STRB Rd, [Rn, -Rm, LSR #]
      int shift = (opcode >> 7) & 31;
      int offset = shift ? reg[opcode & 15].I >> shift : 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - offset;
      CPUWriteByte(address, reg[dest].B.B0);
    }
    break;
  case 0x744:
  case 0x74c:
    {
      // STRB Rd, [Rn, -Rm, ASR #]
      int shift = (opcode >> 7) & 31;
      int offset;
      if(shift)
        offset = (int)((s32)reg[opcode & 15].I >> shift);
      else if(reg[opcode & 15].I & 0x80000000)
        offset = 0xFFFFFFFF;
      else
        offset = 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - offset;
      CPUWriteByte(address, reg[dest].B.B0);
    }
    break;
  case 0x746:
  case 0x74e:
    {
      // STRB Rd, [Rn, -Rm, ROR #]
      int shift = (opcode >> 7) & 31;
      u32 value = reg[opcode & 15].I;
      if(shift) {
        ROR_VALUE;
      } else {
        RCR_VALUE;
      }
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - value;
      CPUWriteByte(address, reg[dest].B.B0);
    }
    break;
  case 0x760:
  case 0x768:
    {
      // STRB Rd, [Rn, -Rm, LSL #]!
      int offset = reg[opcode & 15].I << ((opcode>>7)& 31);
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - offset;
      reg[base].I = address;
      CPUWriteByte(address, reg[dest].B.B0);
    }
    break;
  case 0x762:
  case 0x76a:
    {
      // STRB Rd, [Rn, -Rm, LSR #]!
      int shift = (opcode >> 7) & 31;
      int offset = shift ? reg[opcode & 15].I >> shift : 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - offset;
      reg[base].I = address;      
      CPUWriteByte(address, reg[dest].B.B0);
    }
    break;
  case 0x764:
  case 0x76c:
    {
      // STRB Rd, [Rn, -Rm, ASR #]!
      int shift = (opcode >> 7) & 31;
      int offset;
      if(shift)
        offset = (int)((s32)reg[opcode & 15].I >> shift);
      else if(reg[opcode & 15].I & 0x80000000)
        offset = 0xFFFFFFFF;
      else
        offset = 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - offset;
      reg[base].I = address;
      CPUWriteByte(address, reg[dest].B.B0);
    }
    break;
  case 0x766:
  case 0x76e:
    {
      // STRB Rd, [Rn, -Rm, ROR #]!
      int shift = (opcode >> 7) & 31;
      u32 value = reg[opcode & 15].I;
      if(shift) {
        ROR_VALUE;
      } else {
        RCR_VALUE;
      }
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - value;
      reg[base].I = address;      
      CPUWriteByte(address, reg[dest].B.B0);
    }
    break;
  case 0x7c0:
  case 0x7c8:
    {
      // STRB Rd, [Rn, Rm, LSL #]
      int offset = reg[opcode & 15].I << ((opcode>>7)& 31);
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + offset;
      CPUWriteByte(address, reg[dest].B.B0);
    }
    break;
  case 0x7c2:
  case 0x7ca:
    {
      // STRB Rd, [Rn, Rm, LSR #]
      int shift = (opcode >> 7) & 31;
      int offset = shift ? reg[opcode & 15].I >> shift : 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + offset;
      CPUWriteByte(address, reg[dest].B.B0);
    }
    break;
  case 0x7c4:
  case 0x7cc:
    {
      // STRB Rd, [Rn, Rm, ASR #]
      int shift = (opcode >> 7) & 31;
      int offset;
      if(shift)
        offset = (int)((s32)reg[opcode & 15].I >> shift);
      else if(reg[opcode & 15].I & 0x80000000)
        offset = 0xFFFFFFFF;
      else
        offset = 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + offset;
      CPUWriteByte(address, reg[dest].B.B0);
    }
    break;
  case 0x7c6:
  case 0x7ce:
    {
      // STRB Rd, [Rn, Rm, ROR #]
      int shift = (opcode >> 7) & 31;
      u32 value = reg[opcode & 15].I;
      if(shift) {
        ROR_VALUE;
      } else {
        RCR_VALUE;
      }
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + value;
      CPUWriteByte(address, reg[dest].B.B0);
    }
    break;
  case 0x7e0:
  case 0x7e8:
    {
      // STRB Rd, [Rn, Rm, LSL #]!
      int offset = reg[opcode & 15].I << ((opcode>>7)& 31);
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + offset;
      reg[base].I = address;
      CPUWriteByte(address, reg[dest].B.B0);
    }
    break;
  case 0x7e2:
  case 0x7ea:
    {
      // STRB Rd, [Rn, Rm, LSR #]!
      int shift = (opcode >> 7) & 31;
      int offset = shift ? reg[opcode & 15].I >> shift : 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + offset;
      reg[base].I = address;      
      CPUWriteByte(address, reg[dest].B.B0);
    }
    break;
  case 0x7e4:
  case 0x7ec:
    {
      // STRB Rd, [Rn, Rm, ASR #]!
      int shift = (opcode >> 7) & 31;
      int offset;
      if(shift)
        offset = (int)((s32)reg[opcode & 15].I >> shift);
      else if(reg[opcode & 15].I & 0x80000000)
        offset = 0xFFFFFFFF;
      else
        offset = 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + offset;
      reg[base].I = address;
      CPUWriteByte(address, reg[dest].B.B0);
    }
    break;
  case 0x7e6:
  case 0x7ee:
    {
      // STRB Rd, [Rn, Rm, ROR #]!
      int shift = (opcode >> 7) & 31;
      u32 value = reg[opcode & 15].I;
      if(shift) {
        ROR_VALUE;
      } else {
        RCR_VALUE;
      }
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + value;
      reg[base].I = address;      
      CPUWriteByte(address, reg[dest].B.B0);
    }
    break;
  case 0x650:
  case 0x658:
    // T versions are the same
  case 0x670:
  case 0x678:
    {
      // LDRB Rd, [Rn], -Rm, LSL #
      int offset = reg[opcode & 15].I << ((opcode>>7)& 31);
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      reg[dest].I = CPUReadByte(address);
      if(dest != base)
        reg[base].I = address - offset;
    }
    break;
  case 0x652:
  case 0x65a:
    // T versions are the same
  case 0x672:
  case 0x67a:
    {
      // LDRB Rd, [Rn], -Rm, LSR #
      int shift = (opcode >> 7) & 31;
      int offset = shift ? reg[opcode & 15].I >> shift : 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      reg[dest].I = CPUReadByte(address);
      if(dest != base)
        reg[base].I = address - offset;
    }
    break;
  case 0x654:
  case 0x65c:
    // T versions are the same
  case 0x674:
  case 0x67c:
    {
      // LDRB Rd, [Rn], -Rm, ASR #
      int shift = (opcode >> 7) & 31;
      int offset;
      if(shift)
        offset = (int)((s32)reg[opcode & 15].I >> shift);
      else if(reg[opcode & 15].I & 0x80000000)
        offset = 0xFFFFFFFF;
      else
        offset = 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      reg[dest].I = CPUReadByte(address);
      if(dest != base)
        reg[base].I = address - offset;
    }
    break;
  case 0x656:
  case 0x65e:
    // T versions are the same
  case 0x676:
  case 0x67e:
    {
      // LDRB Rd, [Rn], -Rm, ROR #
      int shift = (opcode >> 7) & 31;
      u32 value = reg[opcode & 15].I;
      if(shift) {
        ROR_VALUE;
      } else {
        RCR_VALUE;
      }
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      reg[dest].I = CPUReadByte(address);
      if(dest != base)
        reg[base].I = address - value;
    }
    break;
  case 0x6d0:
  case 0x6d8:
    // T versions are the same
  case 0x6f0:
  case 0x6f8:
    {
      // LDRB Rd, [Rn], Rm, LSL #
      int offset = reg[opcode & 15].I << ((opcode>>7)& 31);
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      reg[dest].I = CPUReadByte(address);
      if(dest != base)
        reg[base].I = address + offset;
    }
    break;
  case 0x6d2:
  case 0x6da:
    // T versions are the same
  case 0x6f2:
  case 0x6fa:
    {
      // LDRB Rd, [Rn], Rm, LSR #
      int shift = (opcode >> 7) & 31;
      int offset = shift ? reg[opcode & 15].I >> shift : 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      reg[dest].I = CPUReadByte(address);
      if(dest != base)
        reg[base].I = address + offset;
    }
    break;
  case 0x6d4:
  case 0x6dc:
    // T versions are the same
  case 0x6f4:
  case 0x6fc:
    {
      // LDRB Rd, [Rn], Rm, ASR #
      int shift = (opcode >> 7) & 31;
      int offset;
      if(shift)
        offset = (int)((s32)reg[opcode & 15].I >> shift);
      else if(reg[opcode & 15].I & 0x80000000)
        offset = 0xFFFFFFFF;
      else
        offset = 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      reg[dest].I = CPUReadByte(address);
      if(dest != base)
        reg[base].I = address + offset;
    }
    break;
  case 0x6d6:
  case 0x6de:
    // T versions are the same
  case 0x6f6:
  case 0x6fe:
    {
      // LDRB Rd, [Rn], Rm, ROR #
      int shift = (opcode >> 7) & 31;
      u32 value = reg[opcode & 15].I;
      if(shift) {
        ROR_VALUE;
      } else {
        RCR_VALUE;
      }
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I;
      reg[dest].I = CPUReadByte(address);
      if(dest != base)
        reg[base].I = address + value;
    }
    break;
  case 0x750:
  case 0x758:
    {
      // LDRB Rd, [Rn, -Rm, LSL #]
      int offset = reg[opcode & 15].I << ((opcode>>7)& 31);
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - offset;
      reg[dest].I = CPUReadByte(address);
    }
    break;
  case 0x752:
  case 0x75a:
    {
      // LDRB Rd, [Rn, -Rm, LSR #]
      int shift = (opcode >> 7) & 31;
      int offset = shift ? reg[opcode & 15].I >> shift : 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - offset;
      reg[dest].I = CPUReadByte(address);
    }
    break;
  case 0x754:
  case 0x75c:
    {
      // LDRB Rd, [Rn, -Rm, ASR #]
      int shift = (opcode >> 7) & 31;
      int offset;
      if(shift)
        offset = (int)((s32)reg[opcode & 15].I >> shift);
      else if(reg[opcode & 15].I & 0x80000000)
        offset = 0xFFFFFFFF;
      else
        offset = 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - offset;
      reg[dest].I = CPUReadByte(address);
    }
    break;
  case 0x756:
  case 0x75e:
    {
      // LDRB Rd, [Rn, -Rm, ROR #]
      int shift = (opcode >> 7) & 31;
      u32 value = reg[opcode & 15].I;
      if(shift) {
        ROR_VALUE;
      } else {
        RCR_VALUE;
      }
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - value;
      reg[dest].I = CPUReadByte(address);
    }
    break;
  case 0x770:
  case 0x778:
    {
      // LDRB Rd, [Rn, -Rm, LSL #]!
      int offset = reg[opcode & 15].I << ((opcode>>7)& 31);
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - offset;
      reg[dest].I = CPUReadByte(address);
      if(dest != base)
        reg[base].I = address;
    }
    break;
  case 0x772:
  case 0x77a:
    {
      // LDRB Rd, [Rn, -Rm, LSR #]!
      int shift = (opcode >> 7) & 31;
      int offset = shift ? reg[opcode & 15].I >> shift : 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - offset;
      reg[dest].I = CPUReadByte(address);
      if(dest != base)
        reg[base].I = address;      
    }
    break;
  case 0x774:
  case 0x77c:
    {
      // LDRB Rd, [Rn, -Rm, ASR #]!
      int shift = (opcode >> 7) & 31;
      int offset;
      if(shift)
        offset = (int)((s32)reg[opcode & 15].I >> shift);
      else if(reg[opcode & 15].I & 0x80000000)
        offset = 0xFFFFFFFF;
      else
        offset = 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - offset;
      reg[dest].I = CPUReadByte(address);
      if(dest != base)
        reg[base].I = address;
    }
    break;
  case 0x776:
  case 0x77e:
    {
      // LDRB Rd, [Rn, -Rm, ROR #]!
      int shift = (opcode >> 7) & 31;
      u32 value = reg[opcode & 15].I;
      if(shift) {
        ROR_VALUE;
      } else {
        RCR_VALUE;
      }
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I - value;
      reg[dest].I = CPUReadByte(address);
      if(dest != base)
        reg[base].I = address;      
    }
    break;
  case 0x7d0:
  case 0x7d8:
    {
      // LDRB Rd, [Rn, Rm, LSL #]
      int offset = reg[opcode & 15].I << ((opcode>>7)& 31);
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + offset;
      reg[dest].I = CPUReadByte(address);
    }
    break;
  case 0x7d2:
  case 0x7da:
    {
      // LDRB Rd, [Rn, Rm, LSR #]
      int shift = (opcode >> 7) & 31;
      int offset = shift ? reg[opcode & 15].I >> shift : 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + offset;
      reg[dest].I = CPUReadByte(address);
    }
    break;
  case 0x7d4:
  case 0x7dc:
    {
      // LDRB Rd, [Rn, Rm, ASR #]
      int shift = (opcode >> 7) & 31;
      int offset;
      if(shift)
        offset = (int)((s32)reg[opcode & 15].I >> shift);
      else if(reg[opcode & 15].I & 0x80000000)
        offset = 0xFFFFFFFF;
      else
        offset = 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + offset;
      reg[dest].I = CPUReadByte(address);
    }
    break;
  case 0x7d6:
  case 0x7de:
    {
      // LDRB Rd, [Rn, Rm, ROR #]
      int shift = (opcode >> 7) & 31;
      u32 value = reg[opcode & 15].I;
      if(shift) {
        ROR_VALUE;
      } else {
        RCR_VALUE;
      }
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + value;
      reg[dest].I = CPUReadByte(address);
    }
    break;
  case 0x7f0:
  case 0x7f8:
    {
      // LDRB Rd, [Rn, Rm, LSL #]!
      int offset = reg[opcode & 15].I << ((opcode>>7)& 31);
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + offset;
      reg[dest].I = CPUReadByte(address);
      if(dest != base)
        reg[base].I = address;
    }
    break;
  case 0x7f2:
  case 0x7fa:
    {
      // LDRB Rd, [Rn, Rm, LSR #]!
      int shift = (opcode >> 7) & 31;
      int offset = shift ? reg[opcode & 15].I >> shift : 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + offset;
      reg[dest].I = CPUReadByte(address);
      if(dest != base)
        reg[base].I = address;      
    }
    break;
  case 0x7f4:
  case 0x7fc:
    {
      // LDRB Rd, [Rn, Rm, ASR #]!
      int shift = (opcode >> 7) & 31;
      int offset;
      if(shift)
        offset = (int)((s32)reg[opcode & 15].I >> shift);
      else if(reg[opcode & 15].I & 0x80000000)
        offset = 0xFFFFFFFF;
      else
        offset = 0;
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + offset;
      reg[dest].I = CPUReadByte(address);
      if(dest != base)
        reg[base].I = address;
    }
    break;
  case 0x7f6:
  case 0x7fe:
    {
      // LDRB Rd, [Rn, Rm, ROR #]!
      int shift = (opcode >> 7) & 31;
      u32 value = reg[opcode & 15].I;
      if(shift) {
        ROR_VALUE;
      } else {
        RCR_VALUE;
      }
      int dest = (opcode >> 12) & 15;
      int base = (opcode >> 16) & 15;
      u32 address = reg[base].I + value;
      reg[dest].I = CPUReadByte(address);
      if(dest != base)
        reg[base].I = address;      
    }
    break;
#define STMW_REG(val,num) \
  if(opcode & (val)) {\
    CPUWriteMemory(address, reg[(num)].I);\
    if(!offset) {\
      reg[base].I = temp;\
      offset = 1;\
    } else {\
    }\
    address += 4;\
  }
#define STM_REG(val,num) \
  if(opcode & (val)) {\
    CPUWriteMemory(address, reg[(num)].I);\
    if(!offset) {\
      offset = 1;\
    } else {\
    }\
    address += 4;\
  }
    
  CASE_16(0x800)
    // STMDA Rn, {Rlist}
    {
      int base = (opcode & 0x000F0000) >> 16;
      u32 temp = reg[base].I - 
        4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
      u32 address = (temp + 4) & 0xFFFFFFFC;
      int offset = 0;
      STM_REG(1, 0);
      STM_REG(2, 1);
      STM_REG(4, 2);
      STM_REG(8, 3);
      STM_REG(16, 4);
      STM_REG(32, 5);
      STM_REG(64, 6);
      STM_REG(128, 7);
      STM_REG(256, 8);
      STM_REG(512, 9);
      STM_REG(1024, 10);
      STM_REG(2048, 11);
      STM_REG(4096, 12);
      STM_REG(8192, 13);
      STM_REG(16384, 14);
      if(opcode & 32768) {
        CPUWriteMemory(address, reg[15].I+4);
      }
    }
    break;
  CASE_16(0x820)
    {
      // STMDA Rn!, {Rlist}
      int base = (opcode & 0x000F0000) >> 16;
      u32 temp = reg[base].I - 
        4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
      u32 address = (temp+4) & 0xFFFFFFFC;
      int offset = 0;

      STMW_REG(1, 0);
      STMW_REG(2, 1);
      STMW_REG(4, 2);
      STMW_REG(8, 3);
      STMW_REG(16, 4);
      STMW_REG(32, 5);
      STMW_REG(64, 6);
      STMW_REG(128, 7);
      STMW_REG(256, 8);
      STMW_REG(512, 9);
      STMW_REG(1024, 10);
      STMW_REG(2048, 11);
      STMW_REG(4096, 12);
      STMW_REG(8192, 13);
      STMW_REG(16384, 14);
      if(opcode & 32768) {
        CPUWriteMemory(address, reg[15].I+4);
        reg[base].I = temp;
      }
    }
  break;
  CASE_16(0x840)
    {
      // STMDA Rn, {Rlist}^
      int base = (opcode & 0x000F0000) >> 16;
      u32 temp = reg[base].I - 
        4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
      u32 address = (temp+4) & 0xFFFFFFFC;
      int offset = 0;

      STM_REG(1, 0);
      STM_REG(2, 1);
      STM_REG(4, 2);
      STM_REG(8, 3);
      STM_REG(16, 4);
      STM_REG(32, 5);
      STM_REG(64, 6);
      STM_REG(128, 7);

      if(armMode == 0x11) {
        STM_REG(256, R8_FIQ);
        STM_REG(512, R9_FIQ);
        STM_REG(1024, R10_FIQ);
        STM_REG(2048, R11_FIQ);
        STM_REG(4096, R12_FIQ);
      } else {
        STM_REG(256, 8);
        STM_REG(512, 9);
        STM_REG(1024, 10);
        STM_REG(2048, 11);
        STM_REG(4096, 12);
      }

      if(armMode != 0x10 && armMode != 0x1f) {
        STM_REG(8192, R13_USR);
        STM_REG(16384, R14_USR);
      } else {
        STM_REG(8192, 13);
        STM_REG(16384, 14);
      }

      if(opcode & 32768) {
        CPUWriteMemory(address, reg[15].I+4);
      }
    }
    break;
  CASE_16(0x860)
    {
      // STMDA Rn!, {Rlist}^
      int base = (opcode & 0x000F0000) >> 16;
      u32 temp = reg[base].I - 
        4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
      u32 address = (temp+4) & 0xFFFFFFFC;
      int offset = 0;

      STMW_REG(1, 0);
      STMW_REG(2, 1);
      STMW_REG(4, 2);
      STMW_REG(8, 3);
      STMW_REG(16, 4);
      STMW_REG(32, 5);
      STMW_REG(64, 6);
      STMW_REG(128, 7);

      if(armMode == 0x11) {
        STMW_REG(256, R8_FIQ);
        STMW_REG(512, R9_FIQ);
        STMW_REG(1024, R10_FIQ);
        STMW_REG(2048, R11_FIQ);
        STMW_REG(4096, R12_FIQ);
      } else {
        STMW_REG(256, 8);
        STMW_REG(512, 9);
        STMW_REG(1024, 10);
        STMW_REG(2048, 11);
        STMW_REG(4096, 12);
      }

      if(armMode != 0x10 && armMode != 0x1f) {
        STMW_REG(8192, R13_USR);
        STMW_REG(16384, R14_USR);
      } else {
        STMW_REG(8192, 13);
        STMW_REG(16384, 14);
      }

      if(opcode & 32768) {
        CPUWriteMemory(address, reg[15].I+4);
        reg[base].I = temp;
      }
    }
    break;
  
  CASE_16(0x880)
    {
      // STMIA Rn, {Rlist}
      int base = (opcode & 0x000F0000) >> 16;
      u32 address = reg[base].I & 0xFFFFFFFC;
      int offset = 0;
      STM_REG(1, 0);
      STM_REG(2, 1);
      STM_REG(4, 2);
      STM_REG(8, 3);
      STM_REG(16, 4);
      STM_REG(32, 5);
      STM_REG(64, 6);
      STM_REG(128, 7);
      STM_REG(256, 8);
      STM_REG(512, 9);
      STM_REG(1024, 10);
      STM_REG(2048, 11);
      STM_REG(4096, 12);
      STM_REG(8192, 13);
      STM_REG(16384, 14);
      if(opcode & 32768) {
        CPUWriteMemory(address, reg[15].I+4);
      }
    }
  break;
  CASE_16(0x8a0)
    {
      // STMIA Rn!, {Rlist}
      int base = (opcode & 0x000F0000) >> 16;
      u32 address = reg[base].I & 0xFFFFFFFC;
      int offset = 0;
      u32 temp = reg[base].I + 4*(cpuBitsSet[opcode & 0xFF] +
                                  cpuBitsSet[(opcode >> 8) & 255]);
      STMW_REG(1, 0);
      STMW_REG(2, 1);
      STMW_REG(4, 2);
      STMW_REG(8, 3);
      STMW_REG(16, 4);
      STMW_REG(32, 5);
      STMW_REG(64, 6);
      STMW_REG(128, 7);
      STMW_REG(256, 8);
      STMW_REG(512, 9);
      STMW_REG(1024, 10);
      STMW_REG(2048, 11);
      STMW_REG(4096, 12);
      STMW_REG(8192, 13);
      STMW_REG(16384, 14);
      if(opcode & 32768) {
        CPUWriteMemory(address, reg[15].I+4);
        if(!offset) {
          reg[base].I = temp;
        }
      }
    }
  break;
  CASE_16(0x8c0)
    {
      // STMIA Rn, {Rlist}^
      int base = (opcode & 0x000F0000) >> 16;
      u32 address = reg[base].I & 0xFFFFFFFC;
      int offset = 0;
      STM_REG(1, 0);
      STM_REG(2, 1);
      STM_REG(4, 2);
      STM_REG(8, 3);
      STM_REG(16, 4);
      STM_REG(32, 5);
      STM_REG(64, 6);
      STM_REG(128, 7);
      if(armMode == 0x11) {
        STM_REG(256, R8_FIQ);
        STM_REG(512, R9_FIQ);
        STM_REG(1024, R10_FIQ);
        STM_REG(2048, R11_FIQ);
        STM_REG(4096, R12_FIQ);
      } else {
        STM_REG(256, 8);
        STM_REG(512, 9);
        STM_REG(1024, 10);
        STM_REG(2048, 11);
        STM_REG(4096, 12);      
      }
      if(armMode != 0x10 && armMode != 0x1f) {
        STM_REG(8192, R13_USR);
        STM_REG(16384, R14_USR);
      } else {
        STM_REG(8192, 13);
        STM_REG(16384, 14);
      }
      if(opcode & 32768) {
        CPUWriteMemory(address, reg[15].I+4);
      }
    }
    break;
  CASE_16(0x8e0)
    {
      // STMIA Rn!, {Rlist}^
      int base = (opcode & 0x000F0000) >> 16;
      u32 address = reg[base].I & 0xFFFFFFFC;
      int offset = 0;
      u32 temp = reg[base].I + 4*(cpuBitsSet[opcode & 0xFF] +
                                  cpuBitsSet[(opcode >> 8) & 255]);    
      STMW_REG(1, 0);
      STMW_REG(2, 1);
      STMW_REG(4, 2);
      STMW_REG(8, 3);
      STMW_REG(16, 4);
      STMW_REG(32, 5);
      STMW_REG(64, 6);
      STMW_REG(128, 7);
      if(armMode == 0x11) {
        STMW_REG(256, R8_FIQ);
        STMW_REG(512, R9_FIQ);
        STMW_REG(1024, R10_FIQ);
        STMW_REG(2048, R11_FIQ);
        STMW_REG(4096, R12_FIQ);
      } else {
        STMW_REG(256, 8);
        STMW_REG(512, 9);
        STMW_REG(1024, 10);
        STMW_REG(2048, 11);
        STMW_REG(4096, 12);      
      }
      if(armMode != 0x10 && armMode != 0x1f) {
        STMW_REG(8192, R13_USR);
        STMW_REG(16384, R14_USR);
      } else {
        STMW_REG(8192, 13);
        STMW_REG(16384, 14);
      }
      if(opcode & 32768) {
        CPUWriteMemory(address, reg[15].I+4);
        if(!offset) {
          reg[base].I = temp;
        }
      }
    }
    break;
    
  CASE_16(0x900)
    {
      // STMDB Rn, {Rlist}
      int base = (opcode & 0x000F0000) >> 16;
      u32 temp = reg[base].I - 
        4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
      u32 address = temp & 0xFFFFFFFC;
      int offset = 0;
      STM_REG(1, 0);
      STM_REG(2, 1);
      STM_REG(4, 2);
      STM_REG(8, 3);
      STM_REG(16, 4);
      STM_REG(32, 5);
      STM_REG(64, 6);
      STM_REG(128, 7);
      STM_REG(256, 8);
      STM_REG(512, 9);
      STM_REG(1024, 10);
      STM_REG(2048, 11);
      STM_REG(4096, 12);
      STM_REG(8192, 13);
      STM_REG(16384, 14);
      if(opcode & 32768) {
        CPUWriteMemory(address, reg[15].I+4);
      }
    }
    break;
  CASE_16(0x920)
    {
      // STMDB Rn!, {Rlist}
      int base = (opcode & 0x000F0000) >> 16;
      u32 temp = reg[base].I - 
        4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
      u32 address = temp & 0xFFFFFFFC;
      int offset = 0;

      STMW_REG(1, 0);
      STMW_REG(2, 1);
      STMW_REG(4, 2);
      STMW_REG(8, 3);
      STMW_REG(16, 4);
      STMW_REG(32, 5);
      STMW_REG(64, 6);
      STMW_REG(128, 7);
      STMW_REG(256, 8);
      STMW_REG(512, 9);
      STMW_REG(1024, 10);
      STMW_REG(2048, 11);
      STMW_REG(4096, 12);
      STMW_REG(8192, 13);
      STMW_REG(16384, 14);
      if(opcode & 32768) {
        CPUWriteMemory(address, reg[15].I+4);
        reg[base].I = temp;
      }
    }
  break;
  CASE_16(0x940)
    {
      // STMDB Rn, {Rlist}^
      int base = (opcode & 0x000F0000) >> 16;
      u32 temp = reg[base].I - 
        4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
      u32 address = temp & 0xFFFFFFFC;
      int offset = 0;

      STM_REG(1, 0);
      STM_REG(2, 1);
      STM_REG(4, 2);
      STM_REG(8, 3);
      STM_REG(16, 4);
      STM_REG(32, 5);
      STM_REG(64, 6);
      STM_REG(128, 7);

      if(armMode == 0x11) {
        STM_REG(256, R8_FIQ);
        STM_REG(512, R9_FIQ);
        STM_REG(1024, R10_FIQ);
        STM_REG(2048, R11_FIQ);
        STM_REG(4096, R12_FIQ);
      } else {
        STM_REG(256, 8);
        STM_REG(512, 9);
        STM_REG(1024, 10);
        STM_REG(2048, 11);
        STM_REG(4096, 12);
      }

      if(armMode != 0x10 && armMode != 0x1f) {
        STM_REG(8192, R13_USR);
        STM_REG(16384, R14_USR);
      } else {
        STM_REG(8192, 13);
        STM_REG(16384, 14);
      }

      if(opcode & 32768) {
        CPUWriteMemory(address, reg[15].I+4);
      }
    }
    break;
  CASE_16(0x960)
    {
      // STMDB Rn!, {Rlist}^
      int base = (opcode & 0x000F0000) >> 16;
      u32 temp = reg[base].I - 
        4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
      u32 address = temp & 0xFFFFFFFC;
      int offset = 0;

      STMW_REG(1, 0);
      STMW_REG(2, 1);
      STMW_REG(4, 2);
      STMW_REG(8, 3);
      STMW_REG(16, 4);
      STMW_REG(32, 5);
      STMW_REG(64, 6);
      STMW_REG(128, 7);

      if(armMode == 0x11) {
        STMW_REG(256, R8_FIQ);
        STMW_REG(512, R9_FIQ);
        STMW_REG(1024, R10_FIQ);
        STMW_REG(2048, R11_FIQ);
        STMW_REG(4096, R12_FIQ);
      } else {
        STMW_REG(256, 8);
        STMW_REG(512, 9);
        STMW_REG(1024, 10);
        STMW_REG(2048, 11);
        STMW_REG(4096, 12);
      }

      if(armMode != 0x10 && armMode != 0x1f) {
        STMW_REG(8192, R13_USR);
        STMW_REG(16384, R14_USR);
      } else {
        STMW_REG(8192, 13);
        STMW_REG(16384, 14);
      }

      if(opcode & 32768) {
        CPUWriteMemory(address, reg[15].I+4);
        reg[base].I = temp;
      }
    }
    break;

  CASE_16(0x980)
    // STMIB Rn, {Rlist}
    {
      int base = (opcode & 0x000F0000) >> 16;
      u32 address = (reg[base].I+4) & 0xFFFFFFFC;
      int offset = 0;
      STM_REG(1, 0);
      STM_REG(2, 1);
      STM_REG(4, 2);
      STM_REG(8, 3);
      STM_REG(16, 4);
      STM_REG(32, 5);
      STM_REG(64, 6);
      STM_REG(128, 7);
      STM_REG(256, 8);
      STM_REG(512, 9);
      STM_REG(1024, 10);
      STM_REG(2048, 11);
      STM_REG(4096, 12);
      STM_REG(8192, 13);
      STM_REG(16384, 14);
      if(opcode & 32768) {
        CPUWriteMemory(address, reg[15].I+4);
      }
    }
    break;
  CASE_16(0x9a0)
    {
      // STMIB Rn!, {Rlist}
      int base = (opcode & 0x000F0000) >> 16;
      u32 address = (reg[base].I+4) & 0xFFFFFFFC;
      int offset = 0;
      u32 temp = reg[base].I + 4*(cpuBitsSet[opcode & 0xFF] +
                                  cpuBitsSet[(opcode >> 8) & 255]);
      STMW_REG(1, 0);
      STMW_REG(2, 1);
      STMW_REG(4, 2);
      STMW_REG(8, 3);
      STMW_REG(16, 4);
      STMW_REG(32, 5);
      STMW_REG(64, 6);
      STMW_REG(128, 7);
      STMW_REG(256, 8);
      STMW_REG(512, 9);
      STMW_REG(1024, 10);
      STMW_REG(2048, 11);
      STMW_REG(4096, 12);
      STMW_REG(8192, 13);
      STMW_REG(16384, 14);
      if(opcode & 32768) {
        CPUWriteMemory(address, reg[15].I+4);
        if(!offset) {
          reg[base].I = temp;
        }
      }
    }
    break;
  CASE_16(0x9c0)
    {
      // STMIB Rn, {Rlist}^
      int base = (opcode & 0x000F0000) >> 16;
      u32 address = (reg[base].I+4) & 0xFFFFFFFC;
      int offset = 0;
      STM_REG(1, 0);
      STM_REG(2, 1);
      STM_REG(4, 2);
      STM_REG(8, 3);
      STM_REG(16, 4);
      STM_REG(32, 5);
      STM_REG(64, 6);
      STM_REG(128, 7);
      if(armMode == 0x11) {
        STM_REG(256, R8_FIQ);
        STM_REG(512, R9_FIQ);
        STM_REG(1024, R10_FIQ);
        STM_REG(2048, R11_FIQ);
        STM_REG(4096, R12_FIQ);
      } else {
        STM_REG(256, 8);
        STM_REG(512, 9);
        STM_REG(1024, 10);
        STM_REG(2048, 11);
        STM_REG(4096, 12);      
      }
      if(armMode != 0x10 && armMode != 0x1f) {
        STM_REG(8192, R13_USR);
        STM_REG(16384, R14_USR);
      } else {
        STM_REG(8192, 13);
        STM_REG(16384, 14);
      }
      if(opcode & 32768) {
        CPUWriteMemory(address, reg[15].I+4);
      }
    }
    break;
  CASE_16(0x9e0)
    {
      // STMIB Rn!, {Rlist}^
      int base = (opcode & 0x000F0000) >> 16;
      u32 address = (reg[base].I+4) & 0xFFFFFFFC;
      int offset = 0;
      u32 temp = reg[base].I + 4*(cpuBitsSet[opcode & 0xFF] +
                                  cpuBitsSet[(opcode >> 8) & 255]);    
      STMW_REG(1, 0);
      STMW_REG(2, 1);
      STMW_REG(4, 2);
      STMW_REG(8, 3);
      STMW_REG(16, 4);
      STMW_REG(32, 5);
      STMW_REG(64, 6);
      STMW_REG(128, 7);
      if(armMode == 0x11) {
        STMW_REG(256, R8_FIQ);
        STMW_REG(512, R9_FIQ);
        STMW_REG(1024, R10_FIQ);
        STMW_REG(2048, R11_FIQ);
        STMW_REG(4096, R12_FIQ);
      } else {
        STMW_REG(256, 8);
        STMW_REG(512, 9);
        STMW_REG(1024, 10);
        STMW_REG(2048, 11);
        STMW_REG(4096, 12);      
      }
      if(armMode != 0x10 && armMode != 0x1f) {
        STMW_REG(8192, R13_USR);
        STMW_REG(16384, R14_USR);
      } else {
        STMW_REG(8192, 13);
        STMW_REG(16384, 14);
      }
      if(opcode & 32768) {
        CPUWriteMemory(address, reg[15].I+4);
        if(!offset) {
          reg[base].I = temp;
        }
      }
    }
    break;
    
#define LDM_REG(val,num) \
  if(opcode & (val)) {\
    reg[(num)].I = CPUReadMemory(address);\
    if(offset) {\
    } else {\
      offset = 1;\
    }\
    address += 4;\
  }

  CASE_16(0x810)
    {
      // LDMDA Rn, {Rlist}
      int base = (opcode & 0x000F0000) >> 16;
      u32 temp = reg[base].I - 
        4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
      u32 address = (temp + 4) & 0xFFFFFFFC;
      int offset = 0;
      LDM_REG(1, 0);
      LDM_REG(2, 1);
      LDM_REG(4, 2);
      LDM_REG(8, 3);
      LDM_REG(16, 4);
      LDM_REG(32, 5);
      LDM_REG(64, 6);
      LDM_REG(128, 7);
      LDM_REG(256, 8);
      LDM_REG(512, 9);
      LDM_REG(1024, 10);
      LDM_REG(2048, 11);
      LDM_REG(4096, 12);
      LDM_REG(8192, 13);
      LDM_REG(16384, 14);
      if(opcode & 32768) {
        reg[15].I = CPUReadMemory(address);
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  CASE_16(0x830)
    {
      // LDMDA Rn!, {Rlist}
      int base = (opcode & 0x000F0000) >> 16;
      u32 temp = reg[base].I - 
        4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
      u32 address = (temp + 4) & 0xFFFFFFFC;
      int offset = 0;
      LDM_REG(1, 0);
      LDM_REG(2, 1);
      LDM_REG(4, 2);
      LDM_REG(8, 3);
      LDM_REG(16, 4);
      LDM_REG(32, 5);
      LDM_REG(64, 6);
      LDM_REG(128, 7);
      LDM_REG(256, 8);
      LDM_REG(512, 9);
      LDM_REG(1024, 10);
      LDM_REG(2048, 11);
      LDM_REG(4096, 12);
      LDM_REG(8192, 13);
      LDM_REG(16384, 14);
      if(opcode & 32768) {
        reg[15].I = CPUReadMemory(address);
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
      if(!(opcode & (1 << base)))
        reg[base].I = temp;
    }
    break;
  CASE_16(0x850)
    {
      // LDMDA Rn, {Rlist}^
      int base = (opcode & 0x000F0000) >> 16;
      u32 temp = reg[base].I - 
        4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
      u32 address = (temp + 4) & 0xFFFFFFFC;
      int offset = 0;
      if(opcode & 0x8000) {
        LDM_REG(1, 0);
        LDM_REG(2, 1);
        LDM_REG(4, 2);
        LDM_REG(8, 3);
        LDM_REG(16, 4);
        LDM_REG(32, 5);
        LDM_REG(64, 6);
        LDM_REG(128, 7);
        LDM_REG(256, 8);
        LDM_REG(512, 9);
        LDM_REG(1024, 10);
        LDM_REG(2048, 11);
        LDM_REG(4096, 12);
        LDM_REG(8192, 13);
        LDM_REG(16384, 14);

        reg[15].I = CPUReadMemory(address);
        CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);
        if(armState) {
          armNextPC = reg[15].I & 0xFFFFFFFC;
          reg[15].I = armNextPC + 4;
        } else {
          armNextPC = reg[15].I & 0xFFFFFFFE;
          reg[15].I = armNextPC + 2;
        }
      } else {
        LDM_REG(1, 0);
        LDM_REG(2, 1);
        LDM_REG(4, 2);
        LDM_REG(8, 3);
        LDM_REG(16, 4);
        LDM_REG(32, 5);
        LDM_REG(64, 6);
        LDM_REG(128, 7);

        if(armMode == 0x11) {
          LDM_REG(256, R8_FIQ);
          LDM_REG(512, R9_FIQ);
          LDM_REG(1024, R10_FIQ);
          LDM_REG(2048, R11_FIQ);
          LDM_REG(4096, R12_FIQ);
        } else {
          LDM_REG(256, 8);
          LDM_REG(512, 9);
          LDM_REG(1024, 10);
          LDM_REG(2048, 11);
          LDM_REG(4096, 12);
        }

        if(armMode != 0x10 && armMode != 0x1f) {
          LDM_REG(8192, R13_USR);
          LDM_REG(16384, R14_USR);
        } else {
          LDM_REG(8192, 13);
          LDM_REG(16384, 14);
        }
      }
    }
    break;
  CASE_16(0x870)
    {
      // LDMDA Rn!, {Rlist}^
      int base = (opcode & 0x000F0000) >> 16;
      u32 temp = reg[base].I - 
        4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
      u32 address = (temp + 4) & 0xFFFFFFFC;
      int offset = 0;
      if(opcode & 0x8000) {
        LDM_REG(1, 0);
        LDM_REG(2, 1);
        LDM_REG(4, 2);
        LDM_REG(8, 3);
        LDM_REG(16, 4);
        LDM_REG(32, 5);
        LDM_REG(64, 6);
        LDM_REG(128, 7);
        LDM_REG(256, 8);
        LDM_REG(512, 9);
        LDM_REG(1024, 10);
        LDM_REG(2048, 11);
        LDM_REG(4096, 12);
        LDM_REG(8192, 13);
        LDM_REG(16384, 14);

        reg[15].I = CPUReadMemory(address);

        if(!(opcode & (1 << base)))    
          reg[base].I = temp;

        CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);
        if(armState) {
          armNextPC = reg[15].I & 0xFFFFFFFC;
          reg[15].I = armNextPC + 4;
        } else {
          armNextPC = reg[15].I & 0xFFFFFFFE;
          reg[15].I = armNextPC + 2;
        }
      } else {
        LDM_REG(1, 0);
        LDM_REG(2, 1);
        LDM_REG(4, 2);
        LDM_REG(8, 3);
        LDM_REG(16, 4);
        LDM_REG(32, 5);
        LDM_REG(64, 6);
        LDM_REG(128, 7);

        if(armMode == 0x11) {
          LDM_REG(256, R8_FIQ);
          LDM_REG(512, R9_FIQ);
          LDM_REG(1024, R10_FIQ);
          LDM_REG(2048, R11_FIQ);
          LDM_REG(4096, R12_FIQ);
        } else {
          LDM_REG(256, 8);
          LDM_REG(512, 9);
          LDM_REG(1024, 10);
          LDM_REG(2048, 11);
          LDM_REG(4096, 12);
        }

        if(armMode != 0x10 && armMode != 0x1f) {
          LDM_REG(8192, R13_USR);
          LDM_REG(16384, R14_USR);
        } else {
          LDM_REG(8192, 13);
          LDM_REG(16384, 14);
        }

        if(!(opcode & (1 << base)))    
          reg[base].I = temp;
      }
    }
    break;
    
  CASE_16(0x890)
    {
      // LDMIA Rn, {Rlist}
      int base = (opcode & 0x000F0000) >> 16;
      u32 address = reg[base].I & 0xFFFFFFFC;
      int offset = 0;
      LDM_REG(1, 0);
      LDM_REG(2, 1);
      LDM_REG(4, 2);
      LDM_REG(8, 3);
      LDM_REG(16, 4);
      LDM_REG(32, 5);
      LDM_REG(64, 6);
      LDM_REG(128, 7);
      LDM_REG(256, 8);
      LDM_REG(512, 9);
      LDM_REG(1024, 10);
      LDM_REG(2048, 11);
      LDM_REG(4096, 12);
      LDM_REG(8192, 13);
      LDM_REG(16384, 14);
      if(opcode & 32768) {
        reg[15].I = CPUReadMemory(address);
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
  break;
  CASE_16(0x8b0)
    {
      // LDMIA Rn!, {Rlist}
      int base = (opcode & 0x000F0000) >> 16;
      u32 temp = reg[base].I + 
        4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
      u32 address = reg[base].I & 0xFFFFFFFC;
      int offset = 0;
      LDM_REG(1, 0);
      LDM_REG(2, 1);
      LDM_REG(4, 2);
      LDM_REG(8, 3);
      LDM_REG(16, 4);
      LDM_REG(32, 5);
      LDM_REG(64, 6);
      LDM_REG(128, 7);
      LDM_REG(256, 8);
      LDM_REG(512, 9);
      LDM_REG(1024, 10);
      LDM_REG(2048, 11);
      LDM_REG(4096, 12);
      LDM_REG(8192, 13);
      LDM_REG(16384, 14);
      if(opcode & 32768) {
        reg[15].I = CPUReadMemory(address);
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
      if(!(opcode & (1 << base)))
        reg[base].I = temp;
    }
    break;
  CASE_16(0x8d0)
    {
      // LDMIA Rn, {Rlist}^
      int base = (opcode & 0x000F0000) >> 16;
      u32 address = reg[base].I & 0xFFFFFFFC;
      int offset = 0;
      if(opcode & 0x8000) {
        LDM_REG(1, 0);
        LDM_REG(2, 1);
        LDM_REG(4, 2);
        LDM_REG(8, 3);
        LDM_REG(16, 4);
        LDM_REG(32, 5);
        LDM_REG(64, 6);
        LDM_REG(128, 7);
        LDM_REG(256, 8);
        LDM_REG(512, 9);
        LDM_REG(1024, 10);
        LDM_REG(2048, 11);
        LDM_REG(4096, 12);
        LDM_REG(8192, 13);
        LDM_REG(16384, 14);

        reg[15].I = CPUReadMemory(address);

        CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);
        if(armState) {
          armNextPC = reg[15].I & 0xFFFFFFFC;
          reg[15].I = armNextPC + 4;
        } else {
          armNextPC = reg[15].I & 0xFFFFFFFE;
          reg[15].I = armNextPC + 2;
        }
      } else {
        LDM_REG(1, 0);
        LDM_REG(2, 1);
        LDM_REG(4, 2);
        LDM_REG(8, 3);
        LDM_REG(16, 4);
        LDM_REG(32, 5);
        LDM_REG(64, 6);
        LDM_REG(128, 7);

        if(armMode == 0x11) {
          LDM_REG(256, R8_FIQ);
          LDM_REG(512, R9_FIQ);
          LDM_REG(1024, R10_FIQ);
          LDM_REG(2048, R11_FIQ);
          LDM_REG(4096, R12_FIQ);
        } else {
          LDM_REG(256, 8);
          LDM_REG(512, 9);
          LDM_REG(1024, 10);
          LDM_REG(2048, 11);
          LDM_REG(4096, 12);
        }

        if(armMode != 0x10 && armMode != 0x1f) {
          LDM_REG(8192, R13_USR);
          LDM_REG(16384, R14_USR);
        } else {
          LDM_REG(8192, 13);
          LDM_REG(16384, 14);
        }
      }
    }
  break;
  CASE_16(0x8f0)
    {
      // LDMIA Rn!, {Rlist}^
      int base = (opcode & 0x000F0000) >> 16;
      u32 temp = reg[base].I +
        4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
      u32 address = reg[base].I & 0xFFFFFFFC;
      int offset = 0;
      if(opcode & 0x8000) {
        LDM_REG(1, 0);
        LDM_REG(2, 1);
        LDM_REG(4, 2);
        LDM_REG(8, 3);
        LDM_REG(16, 4);
        LDM_REG(32, 5);
        LDM_REG(64, 6);
        LDM_REG(128, 7);
        LDM_REG(256, 8);
        LDM_REG(512, 9);
        LDM_REG(1024, 10);
        LDM_REG(2048, 11);
        LDM_REG(4096, 12);
        LDM_REG(8192, 13);
        LDM_REG(16384, 14);

        reg[15].I = CPUReadMemory(address);

        if(!(opcode & (1 << base)))    
          reg[base].I = temp;

        CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);
        if(armState) {
          armNextPC = reg[15].I & 0xFFFFFFFC;
          reg[15].I = armNextPC + 4;
        } else {
          armNextPC = reg[15].I & 0xFFFFFFFE;
          reg[15].I = armNextPC + 2;
        }
      } else {
        LDM_REG(1, 0);
        LDM_REG(2, 1);
        LDM_REG(4, 2);
        LDM_REG(8, 3);
        LDM_REG(16, 4);
        LDM_REG(32, 5);
        LDM_REG(64, 6);
        LDM_REG(128, 7);

        if(armMode == 0x11) {
          LDM_REG(256, R8_FIQ);
          LDM_REG(512, R9_FIQ);
          LDM_REG(1024, R10_FIQ);
          LDM_REG(2048, R11_FIQ);
          LDM_REG(4096, R12_FIQ);
        } else {
          LDM_REG(256, 8);
          LDM_REG(512, 9);
          LDM_REG(1024, 10);
          LDM_REG(2048, 11);
          LDM_REG(4096, 12);
        }

        if(armMode != 0x10 && armMode != 0x1f) {
          LDM_REG(8192, R13_USR);
          LDM_REG(16384, R14_USR);
        } else {
          LDM_REG(8192, 13);
          LDM_REG(16384, 14);
        }

        if(!(opcode & (1 << base)))    
          reg[base].I = temp;
      }
    }
    break;
    
  CASE_16(0x910)
    {
      // LDMDB Rn, {Rlist}
      int base = (opcode & 0x000F0000) >> 16;
      u32 temp = reg[base].I - 
        4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
      u32 address = temp & 0xFFFFFFFC;
      int offset = 0;
      LDM_REG(1, 0);
      LDM_REG(2, 1);
      LDM_REG(4, 2);
      LDM_REG(8, 3);
      LDM_REG(16, 4);
      LDM_REG(32, 5);
      LDM_REG(64, 6);
      LDM_REG(128, 7);
      LDM_REG(256, 8);
      LDM_REG(512, 9);
      LDM_REG(1024, 10);
      LDM_REG(2048, 11);
      LDM_REG(4096, 12);
      LDM_REG(8192, 13);
      LDM_REG(16384, 14);
      if(opcode & 32768) {
        reg[15].I = CPUReadMemory(address);
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  CASE_16(0x930)
    {
      // LDMDB Rn!, {Rlist}
      int base = (opcode & 0x000F0000) >> 16;
      u32 temp = reg[base].I - 
        4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
      u32 address = temp & 0xFFFFFFFC;
      int offset = 0;
      LDM_REG(1, 0);
      LDM_REG(2, 1);
      LDM_REG(4, 2);
      LDM_REG(8, 3);
      LDM_REG(16, 4);
      LDM_REG(32, 5);
      LDM_REG(64, 6);
      LDM_REG(128, 7);
      LDM_REG(256, 8);
      LDM_REG(512, 9);
      LDM_REG(1024, 10);
      LDM_REG(2048, 11);
      LDM_REG(4096, 12);
      LDM_REG(8192, 13);
      LDM_REG(16384, 14);
      if(opcode & 32768) {
        reg[15].I = CPUReadMemory(address);
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
      if(!(opcode & (1 << base)))
        reg[base].I = temp;
    }
    break;
  CASE_16(0x950)
    {
      // LDMDB Rn, {Rlist}^
      int base = (opcode & 0x000F0000) >> 16;
      u32 temp = reg[base].I - 
        4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
      u32 address = temp & 0xFFFFFFFC;
      int offset = 0;
      if(opcode & 0x8000) {
        LDM_REG(1, 0);
        LDM_REG(2, 1);
        LDM_REG(4, 2);
        LDM_REG(8, 3);
        LDM_REG(16, 4);
        LDM_REG(32, 5);
        LDM_REG(64, 6);
        LDM_REG(128, 7);
        LDM_REG(256, 8);
        LDM_REG(512, 9);
        LDM_REG(1024, 10);
        LDM_REG(2048, 11);
        LDM_REG(4096, 12);
        LDM_REG(8192, 13);
        LDM_REG(16384, 14);

        reg[15].I = CPUReadMemory(address);

        CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);
        if(armState) {
          armNextPC = reg[15].I & 0xFFFFFFFC;
          reg[15].I = armNextPC + 4;
        } else {
          armNextPC = reg[15].I & 0xFFFFFFFE;
          reg[15].I = armNextPC + 2;
        }
      } else {
        LDM_REG(1, 0);
        LDM_REG(2, 1);
        LDM_REG(4, 2);
        LDM_REG(8, 3);
        LDM_REG(16, 4);
        LDM_REG(32, 5);
        LDM_REG(64, 6);
        LDM_REG(128, 7);

        if(armMode == 0x11) {
          LDM_REG(256, R8_FIQ);
          LDM_REG(512, R9_FIQ);
          LDM_REG(1024, R10_FIQ);
          LDM_REG(2048, R11_FIQ);
          LDM_REG(4096, R12_FIQ);
        } else {
          LDM_REG(256, 8);
          LDM_REG(512, 9);
          LDM_REG(1024, 10);
          LDM_REG(2048, 11);
          LDM_REG(4096, 12);
        }

        if(armMode != 0x10 && armMode != 0x1f) {
          LDM_REG(8192, R13_USR);
          LDM_REG(16384, R14_USR);
        } else {
          LDM_REG(8192, 13);
          LDM_REG(16384, 14);
        }
      }
    }
    break;
  CASE_16(0x970)
    {
      // LDMDB Rn!, {Rlist}^
      int base = (opcode & 0x000F0000) >> 16;
      u32 temp = reg[base].I - 
        4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
      u32 address = temp & 0xFFFFFFFC;
      int offset = 0;
      if(opcode & 0x8000) {
        LDM_REG(1, 0);
        LDM_REG(2, 1);
        LDM_REG(4, 2);
        LDM_REG(8, 3);
        LDM_REG(16, 4);
        LDM_REG(32, 5);
        LDM_REG(64, 6);
        LDM_REG(128, 7);
        LDM_REG(256, 8);
        LDM_REG(512, 9);
        LDM_REG(1024, 10);
        LDM_REG(2048, 11);
        LDM_REG(4096, 12);
        LDM_REG(8192, 13);
        LDM_REG(16384, 14);

        reg[15].I = CPUReadMemory(address);

        if(!(opcode & (1 << base)))    
          reg[base].I = temp;

        CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);
        if(armState) {
          armNextPC = reg[15].I & 0xFFFFFFFC;
          reg[15].I = armNextPC + 4;
        } else {
          armNextPC = reg[15].I & 0xFFFFFFFE;
          reg[15].I = armNextPC + 2;
        }
      } else {
        LDM_REG(1, 0);
        LDM_REG(2, 1);
        LDM_REG(4, 2);
        LDM_REG(8, 3);
        LDM_REG(16, 4);
        LDM_REG(32, 5);
        LDM_REG(64, 6);
        LDM_REG(128, 7);

        if(armMode == 0x11) {
          LDM_REG(256, R8_FIQ);
          LDM_REG(512, R9_FIQ);
          LDM_REG(1024, R10_FIQ);
          LDM_REG(2048, R11_FIQ);
          LDM_REG(4096, R12_FIQ);
        } else {
          LDM_REG(256, 8);
          LDM_REG(512, 9);
          LDM_REG(1024, 10);
          LDM_REG(2048, 11);
          LDM_REG(4096, 12);
        }

        if(armMode != 0x10 && armMode != 0x1f) {
          LDM_REG(8192, R13_USR);
          LDM_REG(16384, R14_USR);
        } else {
          LDM_REG(8192, 13);
          LDM_REG(16384, 14);
        }

        if(!(opcode & (1 << base)))    
          reg[base].I = temp;
      }
    }
    break;
    
  CASE_16(0x990)
    {
      // LDMIB Rn, {Rlist}
      int base = (opcode & 0x000F0000) >> 16;
      u32 address = (reg[base].I+4) & 0xFFFFFFFC;
      int offset = 0;
      LDM_REG(1, 0);
      LDM_REG(2, 1);
      LDM_REG(4, 2);
      LDM_REG(8, 3);
      LDM_REG(16, 4);
      LDM_REG(32, 5);
      LDM_REG(64, 6);
      LDM_REG(128, 7);
      LDM_REG(256, 8);
      LDM_REG(512, 9);
      LDM_REG(1024, 10);
      LDM_REG(2048, 11);
      LDM_REG(4096, 12);
      LDM_REG(8192, 13);
      LDM_REG(16384, 14);
      if(opcode & 32768) {
        reg[15].I = CPUReadMemory(address);
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
    }
    break;
  CASE_16(0x9b0)
    {
      // LDMIB Rn!, {Rlist}
      int base = (opcode & 0x000F0000) >> 16;
      u32 temp = reg[base].I + 
        4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
      u32 address = (reg[base].I+4) & 0xFFFFFFFC;
      int offset = 0;
      LDM_REG(1, 0);
      LDM_REG(2, 1);
      LDM_REG(4, 2);
      LDM_REG(8, 3);
      LDM_REG(16, 4);
      LDM_REG(32, 5);
      LDM_REG(64, 6);
      LDM_REG(128, 7);
      LDM_REG(256, 8);
      LDM_REG(512, 9);
      LDM_REG(1024, 10);
      LDM_REG(2048, 11);
      LDM_REG(4096, 12);
      LDM_REG(8192, 13);
      LDM_REG(16384, 14);
      if(opcode & 32768) {
        reg[15].I = CPUReadMemory(address);
        armNextPC = reg[15].I;
        reg[15].I += 4;
      }
      if(!(opcode & (1 << base)))
        reg[base].I = temp;
    }
    break;    
  CASE_16(0x9d0)
    {
      // LDMIB Rn, {Rlist}^
      int base = (opcode & 0x000F0000) >> 16;
      u32 address = (reg[base].I+4) & 0xFFFFFFFC;
      int offset = 0;
      if(opcode & 0x8000) {
        LDM_REG(1, 0);
        LDM_REG(2, 1);
        LDM_REG(4, 2);
        LDM_REG(8, 3);
        LDM_REG(16, 4);
        LDM_REG(32, 5);
        LDM_REG(64, 6);
        LDM_REG(128, 7);
        LDM_REG(256, 8);
        LDM_REG(512, 9);
        LDM_REG(1024, 10);
        LDM_REG(2048, 11);
        LDM_REG(4096, 12);
        LDM_REG(8192, 13);
        LDM_REG(16384, 14);

        reg[15].I = CPUReadMemory(address);

        CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);
        if(armState) {
          armNextPC = reg[15].I & 0xFFFFFFFC;
          reg[15].I = armNextPC + 4;
        } else {
          armNextPC = reg[15].I & 0xFFFFFFFE;
          reg[15].I = armNextPC + 2;
        }
      } else {
        LDM_REG(1, 0);
        LDM_REG(2, 1);
        LDM_REG(4, 2);
        LDM_REG(8, 3);
        LDM_REG(16, 4);
        LDM_REG(32, 5);
        LDM_REG(64, 6);
        LDM_REG(128, 7);

        if(armMode == 0x11) {
          LDM_REG(256, R8_FIQ);
          LDM_REG(512, R9_FIQ);
          LDM_REG(1024, R10_FIQ);
          LDM_REG(2048, R11_FIQ);
          LDM_REG(4096, R12_FIQ);
        } else {
          LDM_REG(256, 8);
          LDM_REG(512, 9);
          LDM_REG(1024, 10);
          LDM_REG(2048, 11);
          LDM_REG(4096, 12);
        }

        if(armMode != 0x10 && armMode != 0x1f) {
          LDM_REG(8192, R13_USR);
          LDM_REG(16384, R14_USR);
        } else {
          LDM_REG(8192, 13);
          LDM_REG(16384, 14);
        }
      }
    }
    break;
  CASE_16(0x9f0)
    {
      // LDMIB Rn!, {Rlist}^
      int base = (opcode & 0x000F0000) >> 16;
      u32 temp = reg[base].I +
        4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]);
      u32 address = (reg[base].I+4) & 0xFFFFFFFC;
      int offset = 0;
      if(opcode & 0x8000) {
        LDM_REG(1, 0);
        LDM_REG(2, 1);
        LDM_REG(4, 2);
        LDM_REG(8, 3);
        LDM_REG(16, 4);
        LDM_REG(32, 5);
        LDM_REG(64, 6);
        LDM_REG(128, 7);
        LDM_REG(256, 8);
        LDM_REG(512, 9);
        LDM_REG(1024, 10);
        LDM_REG(2048, 11);
        LDM_REG(4096, 12);
        LDM_REG(8192, 13);
        LDM_REG(16384, 14);

        reg[15].I = CPUReadMemory(address);

        if(!(opcode & (1 << base)))    
          reg[base].I = temp;

        CPUSwitchMode_int_bool(reg[17].I & 0x1f, false);
        if(armState) {
          armNextPC = reg[15].I & 0xFFFFFFFC;
          reg[15].I = armNextPC + 4;
        } else {
          armNextPC = reg[15].I & 0xFFFFFFFE;
          reg[15].I = armNextPC + 2;
        }
      } else {
        LDM_REG(1, 0);
        LDM_REG(2, 1);
        LDM_REG(4, 2);
        LDM_REG(8, 3);
        LDM_REG(16, 4);
        LDM_REG(32, 5);
        LDM_REG(64, 6);
        LDM_REG(128, 7);

        if(armMode == 0x11) {
          LDM_REG(256, R8_FIQ);
          LDM_REG(512, R9_FIQ);
          LDM_REG(1024, R10_FIQ);
          LDM_REG(2048, R11_FIQ);
          LDM_REG(4096, R12_FIQ);
        } else {
          LDM_REG(256, 8);
          LDM_REG(512, 9);
          LDM_REG(1024, 10);
          LDM_REG(2048, 11);
          LDM_REG(4096, 12);
        }

        if(armMode != 0x10 && armMode != 0x1f) {
          LDM_REG(8192, R13_USR);
          LDM_REG(16384, R14_USR);
        } else {
          LDM_REG(8192, 13);
          LDM_REG(16384, 14);
        }

        if(!(opcode & (1 << base)))    
          reg[base].I = temp;
      }
    }
    break;    
  CASE_256(0xa00)
    {
      // B <offset>
      int offset = opcode & 0x00FFFFFF;
      if(offset & 0x00800000) {
        offset |= 0xFF000000;
      }
      offset <<= 2;
      reg[15].I += offset;
      armNextPC = reg[15].I;
      reg[15].I += 4;
    }
    break;
  CASE_256(0xb00)
    {
      // BL <offset>
      int offset = opcode & 0x00FFFFFF;
      if(offset & 0x00800000) {
        offset |= 0xFF000000;
      }
      offset <<= 2;
      reg[14].I = reg[15].I - 4;
      reg[15].I += offset;
      armNextPC = reg[15].I;
      reg[15].I += 4;
    }
    break;
  CASE_256(0xf00)
    // SWI <comment>
    CPUSoftwareInterrupt_int(opcode & 0x00FFFFFF);      
    break;
#ifdef GP_SUPPORT
  case 0xe11:
  case 0xe13:
  case 0xe15:
  case 0xe17:
  case 0xe19:
  case 0xe1b:
  case 0xe1d:
  case 0xe1f:
    // MRC
    break;
  case 0xe01:
  case 0xe03:
  case 0xe05:
  case 0xe07:
  case 0xe09:
  case 0xe0b:
  case 0xe0d:
  case 0xe0f:
    // MRC
    break;    
#endif
  default:
#ifdef DEV_VERSION
    if(systemVerbose & VERBOSE_UNDEFINED)
      log("Undefined ARM instruction %08x at %08x\n", opcode,
          armNextPC-4);
#endif
    CPUUndefinedException();
    break;
    // END
  }
}
  return GBA.arm_tick_average;
}
