/*****************************************************************************
 *
 *   arm7exec.c
 *   Portable ARM7TDMI Core Emulator
 *
 *   Copyright Steve Ellenoff, all rights reserved.
 *
 *   - This source code is released as freeware for non-commercial purposes.
 *   - You are free to use and redistribute this code in modified or
 *     unmodified form, provided you list me in the credits.
 *   - If you modify this source code, you must add a notice to each modified
 *     source file that it has been changed.  If you're a nice person, you
 *     will clearly mark each change too.  :)
 *   - If you wish to use this for commercial purposes, please contact me at
 *     sellenoff@hotmail.com
 *   - The author of this copywritten work reserves the right to change the
 *     terms of its usage and license at any time, including retroactively
 *   - This entire notice must remain in the source code.
 *
 *  This work is based on:
 *  #1) 'Atmel Corporation ARM7TDMI (Thumb) Datasheet - January 1999'
 *  #2) Arm 2/3/6 emulator By Bryan McPhail (bmcphail@tendril.co.uk) and Phil Stroffolino (MAME CORE 0.76)
 *
 *****************************************************************************/

/******************************************************************************
 *  Notes:
 *         This file contains the code to run during the CPU EXECUTE METHOD.
 *         It has been split into it's own file (from the arm7core.c) so it can be
 *         directly compiled into any cpu core that wishes to use it.
 *
 *         It should be included as follows in your cpu core:
 *
 *         CPU_EXECUTE( arm7 )
 *         {
 *         #include "arm7exec.c"
 *         }
 *
*****************************************************************************/

/* This implementation uses an improved switch() for hopefully faster opcode fetches compared to my last version
.. though there's still room for improvement. */
{
    UINT32 pc;
    UINT32 insn;
    arm_state *cpustate = get_safe_token(device);

    ARM7_ICOUNT = cycles;
    do
    {
        debugger_instruction_hook(cpustate->device, R15);

        /* handle Thumb instructions if active */
        if (T_IS_SET(GET_CPSR))
        {
            UINT32 readword;
            UINT32 addr;
            UINT32 rm, rn, rs, rd, op2, imm, rrs, rrd;
            INT32 offs;

            pc = R15;
            insn = memory_decrypted_read_word(cpustate->program, pc & (~1));
            ARM7_ICOUNT -= (3 - thumbCycles[insn >> 8]);
            switch ((insn & THUMB_INSN_TYPE) >> THUMB_INSN_TYPE_SHIFT)
            {
                case 0x0: /* Logical shifting */
                    SET_CPSR(GET_CPSR & ~(N_MASK | Z_MASK));
                    if (insn & THUMB_SHIFT_R) /* Shift right */
                    {
                        rs = (insn & THUMB_ADDSUB_RS) >> THUMB_ADDSUB_RS_SHIFT;
                        rd = (insn & THUMB_ADDSUB_RD) >> THUMB_ADDSUB_RD_SHIFT;
                        rrs = GET_REGISTER(cpustate, rs);
                        offs = (insn & THUMB_SHIFT_AMT) >> THUMB_SHIFT_AMT_SHIFT;
                        if (offs != 0)
                        {
                            SET_REGISTER(cpustate, rd, rrs >> offs);
                            if (rrs & (1 << (offs - 1)))
                            {
                                SET_CPSR(GET_CPSR | C_MASK);
                            }
                            else
                            {
                                SET_CPSR(GET_CPSR & ~C_MASK);
                            }
                        }
                        else
                        {
                            SET_REGISTER(cpustate, rd, 0);
                            if (rrs & 0x80000000)
                            {
                                SET_CPSR(GET_CPSR | C_MASK);
                            }
                            else
                            {
                                SET_CPSR(GET_CPSR & ~C_MASK);
                            }
                        }
                        SET_CPSR(GET_CPSR & ~(Z_MASK | N_MASK));
                        SET_CPSR(GET_CPSR | HandleALUNZFlags(GET_REGISTER(cpustate, rd)));
                        R15 += 2;
                    }
                    else /* Shift left */
                    {
                        rs = (insn & THUMB_ADDSUB_RS) >> THUMB_ADDSUB_RS_SHIFT;
                        rd = (insn & THUMB_ADDSUB_RD) >> THUMB_ADDSUB_RD_SHIFT;
                        rrs = GET_REGISTER(cpustate, rs);
                        offs = (insn & THUMB_SHIFT_AMT) >> THUMB_SHIFT_AMT_SHIFT;
                        if (offs != 0)
                        {
                            SET_REGISTER(cpustate, rd, rrs << offs);
                            if (rrs & (1 << (31 - (offs - 1))))
                            {
                                SET_CPSR(GET_CPSR | C_MASK);
                            }
                            else
                            {
                                SET_CPSR(GET_CPSR & ~C_MASK);
                            }
                        }
                        else
                        {
                            SET_REGISTER(cpustate, rd, rrs);
                        }
                        SET_CPSR(GET_CPSR & ~(Z_MASK | N_MASK));
                        SET_CPSR(GET_CPSR | HandleALUNZFlags(GET_REGISTER(cpustate, rd)));
                        R15 += 2;
                    }
                    break;
                case 0x1: /* Arithmetic */
                    if (insn & THUMB_INSN_ADDSUB)
                    {
                        switch ((insn & THUMB_ADDSUB_TYPE) >> THUMB_ADDSUB_TYPE_SHIFT)
                        {
                            case 0x0: /* ADD Rd, Rs, Rn */
                                rn = GET_REGISTER(cpustate, (insn & THUMB_ADDSUB_RNIMM) >> THUMB_ADDSUB_RNIMM_SHIFT);
                                rs = GET_REGISTER(cpustate, (insn & THUMB_ADDSUB_RS) >> THUMB_ADDSUB_RS_SHIFT);
                                rd = (insn & THUMB_ADDSUB_RD) >> THUMB_ADDSUB_RD_SHIFT;
                                SET_REGISTER(cpustate, rd, rs + rn);
                                HandleThumbALUAddFlags(GET_REGISTER(cpustate, rd), rs, rn);
                                break;
                            case 0x1: /* SUB Rd, Rs, Rn */
                                rn = GET_REGISTER(cpustate, (insn & THUMB_ADDSUB_RNIMM) >> THUMB_ADDSUB_RNIMM_SHIFT);
                                rs = GET_REGISTER(cpustate, (insn & THUMB_ADDSUB_RS) >> THUMB_ADDSUB_RS_SHIFT);
                                rd = (insn & THUMB_ADDSUB_RD) >> THUMB_ADDSUB_RD_SHIFT;
                                SET_REGISTER(cpustate, rd, rs - rn);
                                HandleThumbALUSubFlags(GET_REGISTER(cpustate, rd), rs, rn);
                                break;
                            case 0x2: /* ADD Rd, Rs, #imm */
                                imm = (insn & THUMB_ADDSUB_RNIMM) >> THUMB_ADDSUB_RNIMM_SHIFT;
                                rs = GET_REGISTER(cpustate, (insn & THUMB_ADDSUB_RS) >> THUMB_ADDSUB_RS_SHIFT);
                                rd = (insn & THUMB_ADDSUB_RD) >> THUMB_ADDSUB_RD_SHIFT;
                                SET_REGISTER(cpustate, rd, rs + imm);
                                HandleThumbALUAddFlags(GET_REGISTER(cpustate, rd), rs, imm);
                                break;
                            case 0x3: /* SUB Rd, Rs, #imm */
                                imm = (insn & THUMB_ADDSUB_RNIMM) >> THUMB_ADDSUB_RNIMM_SHIFT;
                                rs = GET_REGISTER(cpustate, (insn & THUMB_ADDSUB_RS) >> THUMB_ADDSUB_RS_SHIFT);
                                rd = (insn & THUMB_ADDSUB_RD) >> THUMB_ADDSUB_RD_SHIFT;
                                SET_REGISTER(cpustate, rd, rs - imm);
                                HandleThumbALUSubFlags(GET_REGISTER(cpustate, rd), rs,imm);
                                break;
                            default:
                                fatalerror("%08x: G1 Undefined Thumb instruction: %04x\n", pc, insn);
                                R15 += 2;
                                break;
                        }
                    }
                    else
                    {
                        /* ASR.. */
                        //if (insn & THUMB_SHIFT_R) /* Shift right */
                        {
                            rs = (insn & THUMB_ADDSUB_RS) >> THUMB_ADDSUB_RS_SHIFT;
                            rd = (insn & THUMB_ADDSUB_RD) >> THUMB_ADDSUB_RD_SHIFT;
                            rrs = GET_REGISTER(cpustate, rs);
                            offs = (insn & THUMB_SHIFT_AMT) >> THUMB_SHIFT_AMT_SHIFT;
                            if (offs == 0)
                            {
                                offs = 32;
                            }
                            if (offs >= 32)
                            {
                                if (rrs >> 31)
                                {
                                    SET_CPSR(GET_CPSR | C_MASK);
                                }
                                else
                                {
                                    SET_CPSR(GET_CPSR & ~C_MASK);
                                }
                                SET_REGISTER(cpustate, rd, (rrs & 0x80000000) ? 0xFFFFFFFF : 0x00000000);
                            }
                            else
                            {
                                if ((rrs >> (offs - 1)) & 1)
                                {
                                    SET_CPSR(GET_CPSR | C_MASK);
                                }
                                else
                                {
                                    SET_CPSR(GET_CPSR & ~C_MASK);
                                }
                                SET_REGISTER(cpustate, rd,
                                             (rrs & 0x80000000)
                                             ? ((0xFFFFFFFF << (32 - offs)) | (rrs >> offs))
                                             : (rrs >> offs));
                            }
                            SET_CPSR(GET_CPSR & ~(N_MASK | Z_MASK));
                            SET_CPSR(GET_CPSR | HandleALUNZFlags(GET_REGISTER(cpustate, rd)));
                            R15 += 2;
                        }
                    }
                    break;
                case 0x2: /* CMP / MOV */
                    if (insn & THUMB_INSN_CMP)
                    {
                        rn = GET_REGISTER(cpustate, (insn & THUMB_INSN_IMM_RD) >> THUMB_INSN_IMM_RD_SHIFT);
                        op2 = insn & THUMB_INSN_IMM;
                        rd = rn - op2;
                        HandleThumbALUSubFlags(rd, rn, op2);
                        //mame_printf_debug("%08x: xxx Thumb instruction: CMP R%d (%08x), %02x (N=%d, Z=%d, C=%d, V=%d)\n", pc, (insn & THUMB_INSN_IMM_RD) >> THUMB_INSN_IMM_RD_SHIFT, GET_REGISTER(cpustate, (insn & THUMB_INSN_IMM_RD) >> THUMB_INSN_IMM_RD_SHIFT), op2, N_IS_SET(GET_CPSR) ? 1 : 0, Z_IS_SET(GET_CPSR) ? 1 : 0, C_IS_SET(GET_CPSR) ? 1 : 0, V_IS_SET(GET_CPSR) ? 1 : 0);
                    }
                    else
                    {
                        rd = (insn & THUMB_INSN_IMM_RD) >> THUMB_INSN_IMM_RD_SHIFT;
                        op2 = (insn & THUMB_INSN_IMM);
                        SET_REGISTER(cpustate, rd, op2);
                        SET_CPSR(GET_CPSR & ~(Z_MASK | N_MASK));
                        SET_CPSR(GET_CPSR | HandleALUNZFlags(GET_REGISTER(cpustate, rd)));
                        R15 += 2;
                    }
                    break;
                case 0x3: /* ADD/SUB immediate */
                    if (insn & THUMB_INSN_SUB) /* SUB Rd, #Offset8 */
                    {
                        rn = GET_REGISTER(cpustate, (insn & THUMB_INSN_IMM_RD) >> THUMB_INSN_IMM_RD_SHIFT);
                        op2 = insn & THUMB_INSN_IMM;
                        //mame_printf_debug("%08x:  Thumb instruction: SUB R%d, %02x\n", pc, (insn & THUMB_INSN_IMM_RD) >> THUMB_INSN_IMM_RD_SHIFT, op2);
                        rd = rn - op2;
                        SET_REGISTER(cpustate, (insn & THUMB_INSN_IMM_RD) >> THUMB_INSN_IMM_RD_SHIFT, rd);
                        HandleThumbALUSubFlags(rd, rn, op2);
                    }
                    else /* ADD Rd, #Offset8 */
                    {
                        rn = GET_REGISTER(cpustate, (insn & THUMB_INSN_IMM_RD) >> THUMB_INSN_IMM_RD_SHIFT);
                        op2 = insn & THUMB_INSN_IMM;
                        rd = rn + op2;
                        //mame_printf_debug("%08x:  Thumb instruction: ADD R%d, %02x\n", pc, (insn & THUMB_INSN_IMM_RD) >> THUMB_INSN_IMM_RD_SHIFT, op2);
                        SET_REGISTER(cpustate, (insn & THUMB_INSN_IMM_RD) >> THUMB_INSN_IMM_RD_SHIFT, rd);
                        HandleThumbALUAddFlags(rd, rn, op2);
                    }
                    break;
                case 0x4: /* Rd & Rm instructions */
                    switch ((insn & THUMB_GROUP4_TYPE) >> THUMB_GROUP4_TYPE_SHIFT)
                    {
                        case 0x0:
                            switch ((insn & THUMB_ALUOP_TYPE) >> THUMB_ALUOP_TYPE_SHIFT)
                            {
                                case 0x0: /* AND Rd, Rs */
                                    rs = (insn & THUMB_ADDSUB_RS) >> THUMB_ADDSUB_RS_SHIFT;
                                    rd = (insn & THUMB_ADDSUB_RD) >> THUMB_ADDSUB_RD_SHIFT;
                                    SET_REGISTER(cpustate, rd, GET_REGISTER(cpustate, rd) & GET_REGISTER(cpustate, rs));
                                    SET_CPSR(GET_CPSR & ~(Z_MASK | N_MASK));
                                    SET_CPSR(GET_CPSR | HandleALUNZFlags(GET_REGISTER(cpustate, rd)));
                                    R15 += 2;
                                    break;
                                case 0x1: /* EOR Rd, Rs */
                                    rs = (insn & THUMB_ADDSUB_RS) >> THUMB_ADDSUB_RS_SHIFT;
                                    rd = (insn & THUMB_ADDSUB_RD) >> THUMB_ADDSUB_RD_SHIFT;
                                    SET_REGISTER(cpustate, rd, GET_REGISTER(cpustate, rd) ^ GET_REGISTER(cpustate, rs));
                                    SET_CPSR(GET_CPSR & ~(Z_MASK | N_MASK));
                                    SET_CPSR(GET_CPSR | HandleALUNZFlags(GET_REGISTER(cpustate, rd)));
                                    R15 += 2;
                                    break;
                                case 0x2: /* LSL Rd, Rs */
                                    rs = (insn & THUMB_ADDSUB_RS) >> THUMB_ADDSUB_RS_SHIFT;
                                    rd = (insn & THUMB_ADDSUB_RD) >> THUMB_ADDSUB_RD_SHIFT;
                                    rrd = GET_REGISTER(cpustate, rd);
                                    offs = GET_REGISTER(cpustate, rs) & 0x000000ff;
                                    if (offs > 0)
                                    {
                                        if (offs < 32)
                                        {
                                            SET_REGISTER(cpustate, rd, rrd << offs);
                                            if (rrd & (1 << (31 - (offs - 1))))
                                            {
                                                SET_CPSR(GET_CPSR | C_MASK);
                                            }
                                            else
                                            {
                                                SET_CPSR(GET_CPSR & ~C_MASK);
                                            }
                                        }
                                        else if (offs == 32)
                                        {
                                            SET_REGISTER(cpustate, rd, 0);
                                            if (rrd & 1)
                                            {
                                                SET_CPSR(GET_CPSR | C_MASK);
                                            }
                                            else
                                            {
                                                SET_CPSR(GET_CPSR & ~C_MASK);
                                            }
                                        }
                                        else
                                        {
                                            SET_REGISTER(cpustate, rd, 0);
                                            SET_CPSR(GET_CPSR & ~C_MASK);
                                        }
                                    }
                                    SET_CPSR(GET_CPSR & ~(Z_MASK | N_MASK));
                                    SET_CPSR(GET_CPSR | HandleALUNZFlags(GET_REGISTER(cpustate, rd)));
                                    R15 += 2;
                                    break;
                                case 0x3: /* LSR Rd, Rs */
                                    rs = (insn & THUMB_ADDSUB_RS) >> THUMB_ADDSUB_RS_SHIFT;
                                    rd = (insn & THUMB_ADDSUB_RD) >> THUMB_ADDSUB_RD_SHIFT;
                                    rrd = GET_REGISTER(cpustate, rd);
                                    offs = GET_REGISTER(cpustate, rs) & 0x000000ff;
                                    if (offs >  0)
                                    {
                                        if (offs < 32)
                                        {
                                            SET_REGISTER(cpustate, rd, rrd >> offs);
                                            if (rrd & (1 << (offs - 1)))
                                            {
                                                SET_CPSR(GET_CPSR | C_MASK);
                                            }
                                            else
                                            {
                                                SET_CPSR(GET_CPSR & ~C_MASK);
                                            }
                                        }
                                        else if (offs == 32)
                                        {
                                            SET_REGISTER(cpustate, rd, 0);
                                            if (rrd & 0x80000000)
                                            {
                                                SET_CPSR(GET_CPSR | C_MASK);
                                            }
                                            else
                                            {
                                                SET_CPSR(GET_CPSR & ~C_MASK);
                                            }
                                        }
                                        else
                                        {
                                            SET_REGISTER(cpustate, rd, 0);
                                            SET_CPSR(GET_CPSR & ~C_MASK);
                                        }
                                    }
                                    SET_CPSR(GET_CPSR & ~(Z_MASK | N_MASK));
                                    SET_CPSR(GET_CPSR | HandleALUNZFlags(GET_REGISTER(cpustate, rd)));
                                    R15 += 2;
                                    break;
                                case 0x4: /* ASR Rd, Rs */
                                    rs = (insn & THUMB_ADDSUB_RS) >> THUMB_ADDSUB_RS_SHIFT;
                                    rd = (insn & THUMB_ADDSUB_RD) >> THUMB_ADDSUB_RD_SHIFT;
                                    rrs = GET_REGISTER(cpustate, rs)&0xff;
                                    rrd = GET_REGISTER(cpustate, rd);
                                    if (rrs != 0)
                                    {
                                        if (rrs >= 32)
                                        {
                                            if (rrd >> 31)
                                            {
                                                SET_CPSR(GET_CPSR | C_MASK);
                                            }
                                            else
                                            {
                                                SET_CPSR(GET_CPSR & ~C_MASK);
                                            }
                                            SET_REGISTER(cpustate, rd, (GET_REGISTER(cpustate, rd) & 0x80000000) ? 0xFFFFFFFF : 0x00000000);
                                        }
                                        else
                                        {
                                            if ((rrd >> (rrs-1)) & 1)
                                            {
                                                SET_CPSR(GET_CPSR | C_MASK);
                                            }
                                            else
                                            {
                                                SET_CPSR(GET_CPSR & ~C_MASK);
                                            }
                                            SET_REGISTER(cpustate, rd, (rrd & 0x80000000)
                                                         ? ((0xFFFFFFFF << (32 - rrs)) | (rrd >> rrs))
                                                         : (rrd >> rrs));
                                        }
                                    }
                                    SET_CPSR(GET_CPSR & ~(N_MASK | Z_MASK));
                                    SET_CPSR(GET_CPSR | HandleALUNZFlags(GET_REGISTER(cpustate, rd)));
                                    R15 += 2;
                                    break;
                                case 0x5: /* ADC Rd, Rs */
                                    rs = (insn & THUMB_ADDSUB_RS) >> THUMB_ADDSUB_RS_SHIFT;
                                    rd = (insn & THUMB_ADDSUB_RD) >> THUMB_ADDSUB_RD_SHIFT;
                                    op2=(GET_CPSR & C_MASK) ? 1 : 0;
                                    rn=GET_REGISTER(cpustate, rd) + GET_REGISTER(cpustate, rs) + op2;
                                    HandleThumbALUAddFlags(rn, GET_REGISTER(cpustate, rd), (GET_REGISTER(cpustate, rs))); // ?
                                    SET_REGISTER(cpustate, rd, rn);
                                    break;
                                case 0x6: /* SBC Rd, Rs */
                                    rs = (insn & THUMB_ADDSUB_RS) >> THUMB_ADDSUB_RS_SHIFT;
                                    rd = (insn & THUMB_ADDSUB_RD) >> THUMB_ADDSUB_RD_SHIFT;
                                    op2=(GET_CPSR & C_MASK) ? 0 : 1;
                                    rn=GET_REGISTER(cpustate, rd) - GET_REGISTER(cpustate, rs) - op2;
                                    HandleThumbALUSubFlags(rn, GET_REGISTER(cpustate, rd), (GET_REGISTER(cpustate, rs))); //?
                                    SET_REGISTER(cpustate, rd, rn);
                                    break;
                                case 0x7: /* ROR Rd, Rs */
                                    rs = (insn & THUMB_ADDSUB_RS) >> THUMB_ADDSUB_RS_SHIFT;
                                    rd = (insn & THUMB_ADDSUB_RD) >> THUMB_ADDSUB_RD_SHIFT;
                                    rrd = GET_REGISTER(cpustate, rd);
                                    imm = GET_REGISTER(cpustate, rs) & 0x0000001f;
                                    SET_REGISTER(cpustate, rd, (rrd >> imm) | (rrd << (32 - imm)));
                                    if (rrd & (1 << (imm - 1)))
                                    {
                                        SET_CPSR(GET_CPSR | C_MASK);
                                    }
                                    else
                                    {
                                        SET_CPSR(GET_CPSR & ~C_MASK);
                                    }
                                    SET_CPSR(GET_CPSR & ~(Z_MASK | N_MASK));
                                    SET_CPSR(GET_CPSR | HandleALUNZFlags(GET_REGISTER(cpustate, rd)));
                                    R15 += 2;
                                    break;
                                case 0x8: /* TST Rd, Rs */
                                    rs = (insn & THUMB_ADDSUB_RS) >> THUMB_ADDSUB_RS_SHIFT;
                                    rd = (insn & THUMB_ADDSUB_RD) >> THUMB_ADDSUB_RD_SHIFT;
                                    SET_CPSR(GET_CPSR & ~(Z_MASK | N_MASK));
                                    SET_CPSR(GET_CPSR | HandleALUNZFlags(GET_REGISTER(cpustate, rd) & GET_REGISTER(cpustate, rs)));
                                    R15 += 2;
                                    break;
                                case 0x9: /* NEG Rd, Rs - todo: check me */
                                    rs = (insn & THUMB_ADDSUB_RS) >> THUMB_ADDSUB_RS_SHIFT;
                                    rd = (insn & THUMB_ADDSUB_RD) >> THUMB_ADDSUB_RD_SHIFT;
                                    rrs = GET_REGISTER(cpustate, rs);
                                    rn = 0 - rrs;
                                    SET_REGISTER(cpustate, rd, rn);
                                    HandleThumbALUSubFlags(GET_REGISTER(cpustate, rd), 0, rrs);
                                    break;
                                case 0xa: /* CMP Rd, Rs */
                                    rs = (insn & THUMB_ADDSUB_RS) >> THUMB_ADDSUB_RS_SHIFT;
                                    rd = (insn & THUMB_ADDSUB_RD) >> THUMB_ADDSUB_RD_SHIFT;
                                    rn = GET_REGISTER(cpustate, rd) - GET_REGISTER(cpustate, rs);
                                    HandleThumbALUSubFlags(rn, GET_REGISTER(cpustate, rd), GET_REGISTER(cpustate, rs));
                                    break;
                                case 0xb: /* CMN Rd, Rs - check flags, add dasm */
                                    rs = (insn & THUMB_ADDSUB_RS) >> THUMB_ADDSUB_RS_SHIFT;
                                    rd = (insn & THUMB_ADDSUB_RD) >> THUMB_ADDSUB_RD_SHIFT;
                                    rn = GET_REGISTER(cpustate, rd) + GET_REGISTER(cpustate, rs);
                                    HandleThumbALUAddFlags(rn, GET_REGISTER(cpustate, rd), GET_REGISTER(cpustate, rs));
                                    break;
                                case 0xc: /* ORR Rd, Rs */
                                    rs = (insn & THUMB_ADDSUB_RS) >> THUMB_ADDSUB_RS_SHIFT;
                                    rd = (insn & THUMB_ADDSUB_RD) >> THUMB_ADDSUB_RD_SHIFT;
                                    SET_REGISTER(cpustate, rd, GET_REGISTER(cpustate, rd) | GET_REGISTER(cpustate, rs));
                                    SET_CPSR(GET_CPSR & ~(Z_MASK | N_MASK));
                                    SET_CPSR(GET_CPSR | HandleALUNZFlags(GET_REGISTER(cpustate, rd)));
                                    R15 += 2;
                                    break;
                                case 0xd: /* MUL Rd, Rs */
                                    rs = (insn & THUMB_ADDSUB_RS) >> THUMB_ADDSUB_RS_SHIFT;
                                    rd = (insn & THUMB_ADDSUB_RD) >> THUMB_ADDSUB_RD_SHIFT;
                                    rn = GET_REGISTER(cpustate, rd) * GET_REGISTER(cpustate, rs);
                                    SET_CPSR(GET_CPSR & ~(Z_MASK | N_MASK));
                                    SET_REGISTER(cpustate, rd, rn);
                                    SET_CPSR(GET_CPSR | HandleALUNZFlags(rn));
                                    R15 += 2;
                                    break;
                                case 0xe: /* BIC Rd, Rs */
                                    rs = (insn & THUMB_ADDSUB_RS) >> THUMB_ADDSUB_RS_SHIFT;
                                    rd = (insn & THUMB_ADDSUB_RD) >> THUMB_ADDSUB_RD_SHIFT;
                                    SET_REGISTER(cpustate, rd, GET_REGISTER(cpustate, rd) & (~GET_REGISTER(cpustate, rs)));
                                    SET_CPSR(GET_CPSR & ~(Z_MASK | N_MASK));
                                    SET_CPSR(GET_CPSR | HandleALUNZFlags(GET_REGISTER(cpustate, rd)));
                                    R15 += 2;
                                    break;
                                case 0xf: /* MVN Rd, Rs */
                                    rs = (insn & THUMB_ADDSUB_RS) >> THUMB_ADDSUB_RS_SHIFT;
                                    rd = (insn & THUMB_ADDSUB_RD) >> THUMB_ADDSUB_RD_SHIFT;
                                    op2 = GET_REGISTER(cpustate, rs);
                                    SET_REGISTER(cpustate, rd, ~op2);
                                    SET_CPSR(GET_CPSR & ~(Z_MASK | N_MASK));
                                    SET_CPSR(GET_CPSR | HandleALUNZFlags(GET_REGISTER(cpustate, rd)));
                                    R15 += 2;
                                    break;
                                default:
                                    fatalerror("%08x: G4-0 Undefined Thumb instruction: %04x %x\n", pc, insn, (insn & THUMB_ALUOP_TYPE) >> THUMB_ALUOP_TYPE_SHIFT);
                                    R15 += 2;
                                    break;
                            }
                            break;
                        case 0x1:
                            switch ((insn & THUMB_HIREG_OP) >> THUMB_HIREG_OP_SHIFT)
                            {
                                case 0x0: /* ADD Rd, Rs */
                                    rs = (insn & THUMB_HIREG_RS) >> THUMB_HIREG_RS_SHIFT;
                                    rd = insn & THUMB_HIREG_RD;
                                    switch ((insn & THUMB_HIREG_H) >> THUMB_HIREG_H_SHIFT)
                                    {
                                        case 0x1: /* ADD Rd, HRs */
                                            SET_REGISTER(cpustate, rd, GET_REGISTER(cpustate, rd) + GET_REGISTER(cpustate, rs+8));
                                            // emulate the effects of pre-fetch
                                            if (rs == 7)
                                            {
                                                SET_REGISTER(cpustate, rd, GET_REGISTER(cpustate, rd) + 4);
                                            }
                                            break;
                                        case 0x2: /* ADD HRd, Rs */
                                            SET_REGISTER(cpustate, rd+8, GET_REGISTER(cpustate, rd+8) + GET_REGISTER(cpustate, rs));
                                            if (rd == 7)
                                            {
                                                R15 += 2;
                                            }
                                            break;
                                        case 0x3: /* Add HRd, HRs */
                                            SET_REGISTER(cpustate, rd+8, GET_REGISTER(cpustate, rd+8) + GET_REGISTER(cpustate, rs+8));
                                            // emulate the effects of pre-fetch
                                            if (rs == 7)
                                            {
                                                SET_REGISTER(cpustate, rd+8, GET_REGISTER(cpustate, rd+8) + 4);
                                            }
                                            if (rd == 7)
                                            {
                                                R15 += 2;
                                            }
                                            break;
                                        default:
                                            fatalerror("%08x: G4-1-0 Undefined Thumb instruction: %04x %x\n", pc, insn, (insn & THUMB_HIREG_H) >> THUMB_HIREG_H_SHIFT);
                                            break;
                                    }
                                    R15 += 2;
                                    break;
                                case 0x1: /* CMP */
                                    switch ((insn & THUMB_HIREG_H) >> THUMB_HIREG_H_SHIFT)
                                    {
                                        case 0x0: /* CMP Rd, Rs */
                                            rs = GET_REGISTER(cpustate, ((insn & THUMB_HIREG_RS) >> THUMB_HIREG_RS_SHIFT));
                                            rd = GET_REGISTER(cpustate, insn & THUMB_HIREG_RD);
                                            rn = rd - rs;
                                            HandleThumbALUSubFlags(rn, rd, rs);
                                            break;
                                        case 0x1: /* CMP Rd, Hs */
                                            rs = GET_REGISTER(cpustate, ((insn & THUMB_HIREG_RS) >> THUMB_HIREG_RS_SHIFT) + 8);
                                            rd = GET_REGISTER(cpustate, insn & THUMB_HIREG_RD);
                                            rn = rd - rs;
                                            HandleThumbALUSubFlags(rn, rd, rs);
                                            break;
                                        case 0x2: /* CMP Hd, Rs */
                                            rs = GET_REGISTER(cpustate, ((insn & THUMB_HIREG_RS) >> THUMB_HIREG_RS_SHIFT));
                                            rd = GET_REGISTER(cpustate, (insn & THUMB_HIREG_RD) + 8);
                                            rn = rd - rs;
                                            HandleThumbALUSubFlags(rn, rd, rs);
                                            break;
                                        case 0x3: /* CMP Hd, Hs */
                                            rs = GET_REGISTER(cpustate, ((insn & THUMB_HIREG_RS) >> THUMB_HIREG_RS_SHIFT) + 8);
                                            rd = GET_REGISTER(cpustate, (insn & THUMB_HIREG_RD) + 8);
                                            rn = rd - rs;
                                            HandleThumbALUSubFlags(rn, rd, rs);
                                            break;
                                        default:
                                            fatalerror("%08x: G4-1 Undefined Thumb instruction: %04x %x\n", pc, insn, (insn & THUMB_HIREG_H) >> THUMB_HIREG_H_SHIFT);
                                            R15 += 2;
                                            break;
                                    }
                                    break;
                                case 0x2: /* MOV */
                                    switch ((insn & THUMB_HIREG_H) >> THUMB_HIREG_H_SHIFT)
                                    {
                                        case 0x1:       // MOV Rd, Hs
                                            rs = (insn & THUMB_HIREG_RS) >> THUMB_HIREG_RS_SHIFT;
                                            rd = insn & THUMB_HIREG_RD;
                                            if (rs == 7)
                                            {
                                                SET_REGISTER(cpustate, rd, GET_REGISTER(cpustate, rs + 8) + 4);
                                            }
                                            else
                                            {
                                                SET_REGISTER(cpustate, rd, GET_REGISTER(cpustate, rs + 8));
                                            }
                                            R15 += 2;
                                            break;
                                        case 0x2:       // MOV Hd, Rs
                                            rs = (insn & THUMB_HIREG_RS) >> THUMB_HIREG_RS_SHIFT;
                                            rd = insn & THUMB_HIREG_RD;
                                            SET_REGISTER(cpustate, rd + 8, GET_REGISTER(cpustate, rs));
                                            if (rd != 7)
                                            {
                                                R15 += 2;
                                            }
                                            else
                                            {
                                                R15 &= ~1;
                                            }
                                            break;
                                        case 0x3:       // MOV Hd, Hs
                                            rs = (insn & THUMB_HIREG_RS) >> THUMB_HIREG_RS_SHIFT;
                                            rd = insn & THUMB_HIREG_RD;
                                            if (rs == 7)
                                            {
                                                SET_REGISTER(cpustate, rd + 8, GET_REGISTER(cpustate, rs+8)+4);
                                            }
                                            else
                                            {
                                                SET_REGISTER(cpustate, rd + 8, GET_REGISTER(cpustate, rs+8));
                                            }
                                            if (rd != 7)
                                            {
                                                R15 += 2;
                                            }
                                            if (rd == 7)
                                            {
                                                R15 &= ~1;
                                            }
                                            break;
                                        default:
                                            fatalerror("%08x: G4-2 Undefined Thumb instruction: %04x (%x)\n", pc, insn, (insn & THUMB_HIREG_H) >> THUMB_HIREG_H_SHIFT);
                                            R15 += 2;
                                            break;
                                    }
                                    break;
                                case 0x3:
                                    switch ((insn & THUMB_HIREG_H) >> THUMB_HIREG_H_SHIFT)
                                    {
                                        case 0x0:
                                            rd = (insn & THUMB_HIREG_RS) >> THUMB_HIREG_RS_SHIFT;
                                            addr = GET_REGISTER(cpustate, rd);
                                            if (addr & 1)
                                            {
                                                addr &= ~1;
                                            }
                                            else
                                            {
                                                SET_CPSR(GET_CPSR & ~T_MASK);
                                                if (addr & 2)
                                                {
                                                    addr += 2;
                                                }
                                            }
                                            R15 = addr;
                                            break;
                                        case 0x1:
                                            addr = GET_REGISTER(cpustate, ((insn & THUMB_HIREG_RS) >> THUMB_HIREG_RS_SHIFT) + 8);
                                            if ((((insn & THUMB_HIREG_RS) >> THUMB_HIREG_RS_SHIFT) + 8) == 15)
                                            {
                                                addr += 2;
                                            }
                                            if (addr & 1)
                                            {
                                                addr &= ~1;
                                            }
                                            else
                                            {
                                                SET_CPSR(GET_CPSR & ~T_MASK);
                                                if (addr & 2)
                                                {
                                                    addr += 2;
                                                }
                                            }
                                            R15 = addr;
                                            break;
                                        default:
                                            fatalerror("%08x: G4-3 Undefined Thumb instruction: %04x\n", pc, insn);
                                            R15 += 2;
                                            break;
                                    }
                                    break;
                                default:
                                    fatalerror("%08x: G4-x Undefined Thumb instruction: %04x\n", pc, insn);
                                    R15 += 2;
                                    break;
                            }
                            break;
                        case 0x2:
                        case 0x3:
                            readword = READ32((R15 & ~2) + 4 + ((insn & THUMB_INSN_IMM) << 2));
                            SET_REGISTER(cpustate, (insn & THUMB_INSN_IMM_RD) >> THUMB_INSN_IMM_RD_SHIFT, readword);
                            R15 += 2;
                            break;
                        default:
                            fatalerror("%08x: G4-y Undefined Thumb instruction: %04x\n", pc, insn);
                            R15 += 2;
                            break;
                    }
                    break;
                case 0x5: /* LDR* STR* */
                    switch ((insn & THUMB_GROUP5_TYPE) >> THUMB_GROUP5_TYPE_SHIFT)
                    {
                        case 0x0: /* STR Rd, [Rn, Rm] */
                            rm = (insn & THUMB_GROUP5_RM) >> THUMB_GROUP5_RM_SHIFT;
                            rn = (insn & THUMB_GROUP5_RN) >> THUMB_GROUP5_RN_SHIFT;
                            rd = (insn & THUMB_GROUP5_RD) >> THUMB_GROUP5_RD_SHIFT;
                            addr = GET_REGISTER(cpustate, rn) + GET_REGISTER(cpustate, rm);
                            WRITE32(addr, GET_REGISTER(cpustate, rd));
                            R15 += 2;
                            break;
                        case 0x1: /* STRH Rd, [Rn, Rm] */
                            rm = (insn & THUMB_GROUP5_RM) >> THUMB_GROUP5_RM_SHIFT;
                            rn = (insn & THUMB_GROUP5_RN) >> THUMB_GROUP5_RN_SHIFT;
                            rd = (insn & THUMB_GROUP5_RD) >> THUMB_GROUP5_RD_SHIFT;
                            addr = GET_REGISTER(cpustate, rn) + GET_REGISTER(cpustate, rm);
                            WRITE16(addr, GET_REGISTER(cpustate, rd));
                            R15 += 2;
                            break;
                        case 0x2: /* STRB Rd, [Rn, Rm] */
                            rm = (insn & THUMB_GROUP5_RM) >> THUMB_GROUP5_RM_SHIFT;
                            rn = (insn & THUMB_GROUP5_RN) >> THUMB_GROUP5_RN_SHIFT;
                            rd = (insn & THUMB_GROUP5_RD) >> THUMB_GROUP5_RD_SHIFT;
                            addr = GET_REGISTER(cpustate, rn) + GET_REGISTER(cpustate, rm);
                            WRITE8(addr, GET_REGISTER(cpustate, rd));
                            R15 += 2;
                            break;
                        case 0x3: /* LDSB Rd, [Rn, Rm] todo, add dasm */
                            rm = (insn & THUMB_GROUP5_RM) >> THUMB_GROUP5_RM_SHIFT;
                            rn = (insn & THUMB_GROUP5_RN) >> THUMB_GROUP5_RN_SHIFT;
                            rd = (insn & THUMB_GROUP5_RD) >> THUMB_GROUP5_RD_SHIFT;
                            addr = GET_REGISTER(cpustate, rn) + GET_REGISTER(cpustate, rm);
                            op2 = READ8(addr);
                            if (op2 & 0x00000080)
                            {
                                op2 |= 0xffffff00;
                            }
                            SET_REGISTER(cpustate, rd, op2);
                            R15 += 2;
                            break;
                        case 0x4: /* LDR Rd, [Rn, Rm] */
                            rm = (insn & THUMB_GROUP5_RM) >> THUMB_GROUP5_RM_SHIFT;
                            rn = (insn & THUMB_GROUP5_RN) >> THUMB_GROUP5_RN_SHIFT;
                            rd = (insn & THUMB_GROUP5_RD) >> THUMB_GROUP5_RD_SHIFT;
                            addr = GET_REGISTER(cpustate, rn) + GET_REGISTER(cpustate, rm);
                            op2 = READ32(addr);
                            SET_REGISTER(cpustate, rd, op2);
                            R15 += 2;
                            break;
                        case 0x5: /* LDRH Rd, [Rn, Rm] */
                            rm = (insn & THUMB_GROUP5_RM) >> THUMB_GROUP5_RM_SHIFT;
                            rn = (insn & THUMB_GROUP5_RN) >> THUMB_GROUP5_RN_SHIFT;
                            rd = (insn & THUMB_GROUP5_RD) >> THUMB_GROUP5_RD_SHIFT;
                            addr = GET_REGISTER(cpustate, rn) + GET_REGISTER(cpustate, rm);
                            op2 = READ16(addr);
                            SET_REGISTER(cpustate, rd, op2);
                            R15 += 2;
                            break;
                        case 0x6: /* LDRB Rd, [Rn, Rm] */
                            rm = (insn & THUMB_GROUP5_RM) >> THUMB_GROUP5_RM_SHIFT;
                            rn = (insn & THUMB_GROUP5_RN) >> THUMB_GROUP5_RN_SHIFT;
                            rd = (insn & THUMB_GROUP5_RD) >> THUMB_GROUP5_RD_SHIFT;
                            addr = GET_REGISTER(cpustate, rn) + GET_REGISTER(cpustate, rm);
                            op2 = READ8(addr);
                            SET_REGISTER(cpustate, rd, op2);
                            R15 += 2;
                            break;
                        case 0x7: /* LDSH Rd, [Rn, Rm] */
                            rm = (insn & THUMB_GROUP5_RM) >> THUMB_GROUP5_RM_SHIFT;
                            rn = (insn & THUMB_GROUP5_RN) >> THUMB_GROUP5_RN_SHIFT;
                            rd = (insn & THUMB_GROUP5_RD) >> THUMB_GROUP5_RD_SHIFT;
                            addr = GET_REGISTER(cpustate, rn) + GET_REGISTER(cpustate, rm);
                            op2 = READ16(addr);
                            if (op2 & 0x00008000)
                            {
                                op2 |= 0xffff0000;
                            }
                            SET_REGISTER(cpustate, rd, op2);
                            R15 += 2;
                            break;
                        default:
                            fatalerror("%08x: G5 Undefined Thumb instruction: %04x\n", pc, insn);
                            R15 += 2;
                            break;
                    }
                    break;
                case 0x6: /* Word Store w/ Immediate Offset */
                    if (insn & THUMB_LSOP_L) /* Load */
                    {
                        rn = (insn & THUMB_ADDSUB_RS) >> THUMB_ADDSUB_RS_SHIFT;
                        rd = insn & THUMB_ADDSUB_RD;
                        offs = ((insn & THUMB_LSOP_OFFS) >> THUMB_LSOP_OFFS_SHIFT) << 2;
                        SET_REGISTER(cpustate, rd, READ32(GET_REGISTER(cpustate, rn) + offs)); // fix
                        R15 += 2;
                    }
                    else /* Store */
                    {
                        rn = (insn & THUMB_ADDSUB_RS) >> THUMB_ADDSUB_RS_SHIFT;
                        rd = insn & THUMB_ADDSUB_RD;
                        offs = ((insn & THUMB_LSOP_OFFS) >> THUMB_LSOP_OFFS_SHIFT) << 2;
                        WRITE32(GET_REGISTER(cpustate, rn) + offs, GET_REGISTER(cpustate, rd));
                        R15 += 2;
                    }
                    break;
                case 0x7: /* Byte Store w/ Immeidate Offset */
                    if (insn & THUMB_LSOP_L) /* Load */
                    {
                        rn = (insn & THUMB_ADDSUB_RS) >> THUMB_ADDSUB_RS_SHIFT;
                        rd = insn & THUMB_ADDSUB_RD;
                        offs = (insn & THUMB_LSOP_OFFS) >> THUMB_LSOP_OFFS_SHIFT;
                        SET_REGISTER(cpustate, rd, READ8(GET_REGISTER(cpustate, rn) + offs));
                        R15 += 2;
                    }
                    else /* Store */
                    {
                        rn = (insn & THUMB_ADDSUB_RS) >> THUMB_ADDSUB_RS_SHIFT;
                        rd = insn & THUMB_ADDSUB_RD;
                        offs = (insn & THUMB_LSOP_OFFS) >> THUMB_LSOP_OFFS_SHIFT;
                        WRITE8(GET_REGISTER(cpustate, rn) + offs, GET_REGISTER(cpustate, rd));
                        R15 += 2;
                    }
                    break;
                case 0x8: /* Load/Store Halfword */
                    if (insn & THUMB_HALFOP_L) /* Load */
                    {
                        imm = (insn & THUMB_HALFOP_OFFS) >> THUMB_HALFOP_OFFS_SHIFT;
                        rs = (insn & THUMB_ADDSUB_RS) >> THUMB_ADDSUB_RS_SHIFT;
                        rd = (insn & THUMB_ADDSUB_RD) >> THUMB_ADDSUB_RD_SHIFT;
                        SET_REGISTER(cpustate, rd, READ16(GET_REGISTER(cpustate, rs) + (imm << 1)));
                        R15 += 2;
                    }
                    else /* Store */
                    {
                        imm = (insn & THUMB_HALFOP_OFFS) >> THUMB_HALFOP_OFFS_SHIFT;
                        rs = (insn & THUMB_ADDSUB_RS) >> THUMB_ADDSUB_RS_SHIFT;
                        rd = (insn & THUMB_ADDSUB_RD) >> THUMB_ADDSUB_RD_SHIFT;
                        WRITE16(GET_REGISTER(cpustate, rs) + (imm << 1), GET_REGISTER(cpustate, rd));
                        R15 += 2;
                    }
                    break;
                case 0x9: /* Stack-Relative Load/Store */
                    if (insn & THUMB_STACKOP_L)
                    {
                        rd = (insn & THUMB_STACKOP_RD) >> THUMB_STACKOP_RD_SHIFT;
                        offs = (UINT8)(insn & THUMB_INSN_IMM);
                        readword = READ32(GET_REGISTER(cpustate, 13) + ((UINT32)offs << 2));
                        SET_REGISTER(cpustate, rd, readword);
                        R15 += 2;
                    }
                    else
                    {
                        rd = (insn & THUMB_STACKOP_RD) >> THUMB_STACKOP_RD_SHIFT;
                        offs = (UINT8)(insn & THUMB_INSN_IMM);
                        WRITE32(GET_REGISTER(cpustate, 13) + ((UINT32)offs << 2), GET_REGISTER(cpustate, rd));
                        R15 += 2;
                    }
                    break;
                case 0xa: /* Get relative address */
                    if (insn & THUMB_RELADDR_SP) /* ADD Rd, SP, #nn */
                    {
                        rd = (insn & THUMB_RELADDR_RD) >> THUMB_RELADDR_RD_SHIFT;
                        offs = (UINT8)(insn & THUMB_INSN_IMM) << 2;
                        SET_REGISTER(cpustate, rd, GET_REGISTER(cpustate, 13) + offs);
                        R15 += 2;
                    }
                    else /* ADD Rd, PC, #nn */
                    {
                        rd = (insn & THUMB_RELADDR_RD) >> THUMB_RELADDR_RD_SHIFT;
                        offs = (UINT8)(insn & THUMB_INSN_IMM) << 2;
                        SET_REGISTER(cpustate, rd, ((R15 + 4) & ~2) + offs);
                        R15 += 2;
                    }
                    break;
                case 0xb: /* Stack-Related Opcodes */
                    switch ((insn & THUMB_STACKOP_TYPE) >> THUMB_STACKOP_TYPE_SHIFT)
                    {
                        case 0x0: /* ADD SP, #imm */
                            addr = (insn & THUMB_INSN_IMM);
                            addr &= ~THUMB_INSN_IMM_S;
                            SET_REGISTER(cpustate, 13, GET_REGISTER(cpustate, 13) + ((insn & THUMB_INSN_IMM_S) ? -(addr << 2) : (addr << 2)));
                            R15 += 2;
                            break;
                        case 0x4: /* PUSH {Rlist} */
                            for (offs = 7; offs >= 0; offs--)
                            {
                                if (insn & (1 << offs))
                                {
                                    SET_REGISTER(cpustate, 13, GET_REGISTER(cpustate, 13) - 4);
                                    WRITE32(GET_REGISTER(cpustate, 13), GET_REGISTER(cpustate, offs));
                                }
                            }
                            R15 += 2;
                            break;
                        case 0x5: /* PUSH {Rlist}{LR} */
                            SET_REGISTER(cpustate, 13, GET_REGISTER(cpustate, 13) - 4);
                            WRITE32(GET_REGISTER(cpustate, 13), GET_REGISTER(cpustate, 14));
                            for (offs = 7; offs >= 0; offs--)
                            {
                                if (insn & (1 << offs))
                                {
                                    SET_REGISTER(cpustate, 13, GET_REGISTER(cpustate, 13) - 4);
                                    WRITE32(GET_REGISTER(cpustate, 13), GET_REGISTER(cpustate, offs));
                                }
                            }
                            R15 += 2;
                            break;
                        case 0xc: /* POP {Rlist} */
                            for (offs = 0; offs < 8; offs++)
                            {
                                if (insn & (1 << offs))
                                {
                                    SET_REGISTER(cpustate, offs, READ32(GET_REGISTER(cpustate, 13)));
                                    SET_REGISTER(cpustate, 13, GET_REGISTER(cpustate, 13) + 4);
                                }
                            }
                            R15 += 2;
                            break;
                        case 0xd: /* POP {Rlist}{PC} */
                            for (offs = 0; offs < 8; offs++)
                            {
                                if (insn & (1 << offs))
                                {
                                    SET_REGISTER(cpustate, offs, READ32(GET_REGISTER(cpustate, 13)));
                                    SET_REGISTER(cpustate, 13, GET_REGISTER(cpustate, 13) + 4);
                                }
                            }
                            R15 = READ32(GET_REGISTER(cpustate, 13)) & ~1;
                            SET_REGISTER(cpustate, 13, GET_REGISTER(cpustate, 13) + 4);
                            break;
                        default:
                            fatalerror("%08x: Gb Undefined Thumb instruction: %04x\n", pc, insn);
                            R15 += 2;
                            break;
                    }
                    break;
                case 0xc: /* Multiple Load/Store */
                    {
                        UINT32 ld_st_address;

                        rd = (insn & THUMB_MULTLS_BASE) >> THUMB_MULTLS_BASE_SHIFT;
                        ld_st_address = GET_REGISTER(cpustate, rd) & 0xfffffffc;

                        if (insn & THUMB_MULTLS) /* Load */
                        {
                            int rd_in_list;

                            rd_in_list = insn & (1 << rd);
                            for (offs = 0; offs < 8; offs++)
                            {
                                if (insn & (1 << offs))
                                {
                                    SET_REGISTER(cpustate, offs, READ32(ld_st_address));
                                    ld_st_address += 4;
                                }
                            }
                            if (!rd_in_list)
                                SET_REGISTER(cpustate, rd, ld_st_address);
                            R15 += 2;
                        }
                        else /* Store */
                        {
                            for (offs = 0; offs < 8; offs++)
                            {
                                if (insn & (1 << offs))
                                {
                                    WRITE32(ld_st_address, GET_REGISTER(cpustate, offs));
                                    ld_st_address += 4;
                                }
                            }
                            SET_REGISTER(cpustate, rd, ld_st_address);
                            R15 += 2;
                        }
                    }
                    break;
                case 0xd: /* Conditional Branch */
                    offs = (INT8)(insn & THUMB_INSN_IMM);
                    switch ((insn & THUMB_COND_TYPE) >> THUMB_COND_TYPE_SHIFT)
                    {
                        case COND_EQ:
                            if (Z_IS_SET(GET_CPSR))
                            {
                                R15 += 4 + (offs << 1);
                            }
                            else
                            {
                                R15 += 2;
                            }
                            break;
                        case COND_NE:
                            if (Z_IS_CLEAR(GET_CPSR))
                            {
                                R15 += 4 + (offs << 1);
                            }
                            else
                            {
                                R15 += 2;
                            }
                            break;
                        case COND_CS:
                            if (C_IS_SET(GET_CPSR))
                            {
                                R15 += 4 + (offs << 1);
                            }
                            else
                            {
                                R15 += 2;
                            }
                            break;
                        case COND_CC:
                            if (C_IS_CLEAR(GET_CPSR))
                            {
                                R15 += 4 + (offs << 1);
                            }
                            else
                            {
                                R15 += 2;
                            }
                            break;
                        case COND_MI:
                            if (N_IS_SET(GET_CPSR))
                            {
                                R15 += 4 + (offs << 1);
                            }
                            else
                            {
                                R15 += 2;
                            }
                            break;
                        case COND_PL:
                            if (N_IS_CLEAR(GET_CPSR))
                            {
                                R15 += 4 + (offs << 1);
                            }
                            else
                            {
                                R15 += 2;
                            }
                            break;
                        case COND_VS:
                            if (V_IS_SET(GET_CPSR))
                            {
                                R15 += 4 + (offs << 1);
                            }
                            else
                            {
                                R15 += 2;
                            }
                            break;
                        case COND_VC:
                            if (V_IS_CLEAR(GET_CPSR))
                            {
                                R15 += 4 + (offs << 1);
                            }
                            else
                            {
                                R15 += 2;
                            }
                            break;
                        case COND_HI:
                            if (C_IS_SET(GET_CPSR) && Z_IS_CLEAR(GET_CPSR))
                            {
                                R15 += 4 + (offs << 1);
                            }
                            else
                            {
                                R15 += 2;
                            }
                            break;
                        case COND_LS:
                            if (C_IS_CLEAR(GET_CPSR) || Z_IS_SET(GET_CPSR))
                            {
                                R15 += 4 + (offs << 1);
                            }
                            else
                            {
                                R15 += 2;
                            }
                            break;
                        case COND_GE:
                            if (!(GET_CPSR & N_MASK) == !(GET_CPSR & V_MASK))
                            {
                                R15 += 4 + (offs << 1);
                            }
                            else
                            {
                                R15 += 2;
                            }
                            break;
                        case COND_LT:
                            if (!(GET_CPSR & N_MASK) != !(GET_CPSR & V_MASK))
                            {
                                R15 += 4 + (offs << 1);
                            }
                            else
                            {
                                R15 += 2;
                            }
                            break;
                        case COND_GT:
                            if (Z_IS_CLEAR(GET_CPSR) && !(GET_CPSR & N_MASK) == !(GET_CPSR & V_MASK))
                            {
                                R15 += 4 + (offs << 1);
                            }
                            else
                            {
                                R15 += 2;
                            }
                            break;
                        case COND_LE:
                            if (Z_IS_SET(GET_CPSR) || !(GET_CPSR & N_MASK) != !(GET_CPSR & V_MASK))
                            {
                                R15 += 4 + (offs << 1);
                            }
                            else
                            {
                                R15 += 2;
                            }
                            break;
                        case COND_AL:
                            fatalerror("%08x: Undefined Thumb instruction: %04x (ARM9 reserved)\n", pc, insn);
                            R15 += 2;
                            break;
                        case COND_NV:   // SWI (this is sort of a "hole" in the opcode encoding)
                            cpustate->pendingSwi = 1;
                            ARM7_CHECKIRQ;
                            break;
                    }
                    break;
                case 0xe: /* B #offs */
                    if (insn & THUMB_BLOP_LO)
                    {
                        addr = GET_REGISTER(cpustate, 14);
                        addr += (insn & THUMB_BLOP_OFFS) << 1;
                        addr &= 0xfffffffc;
                        SET_REGISTER(cpustate, 14, (R15 + 4) | 1);
                        R15 = addr;
                    }
                    else
                    {
                        offs = (insn & THUMB_BRANCH_OFFS) << 1;
                        if (offs & 0x00000800)
                        {
                            offs |= 0xfffff800;
                        }
                        R15 += 4 + offs;
                    }
                    break;
                case 0xf: /* BL */
                    if (insn & THUMB_BLOP_LO)
                    {
                        addr = GET_REGISTER(cpustate, 14);
                        addr += (insn & THUMB_BLOP_OFFS) << 1;
                        SET_REGISTER(cpustate, 14, (R15 + 2) | 1);
                        R15 = addr;
                        //R15 += 2;
                    }
                    else
                    {
                        addr = (insn & THUMB_BLOP_OFFS) << 12;
                        if (addr & (1 << 22))
                        {
                            addr |= 0xff800000;
                        }
                        addr += R15 + 4;
                        SET_REGISTER(cpustate, 14, addr);
                        R15 += 2;
                    }
                    break;
                default:
                    fatalerror("%08x: Undefined Thumb instruction: %04x\n", pc, insn);
                    R15 += 2;
                    break;
            }
        }
        else
        {

            /* load 32 bit instruction */
            pc = R15;
            insn = memory_decrypted_read_dword(cpustate->program, pc);

            /* process condition codes for this instruction */
            switch (insn >> INSN_COND_SHIFT)
            {
            case COND_EQ:
                if (Z_IS_CLEAR(GET_CPSR))
                    goto L_Next;
                break;
            case COND_NE:
                if (Z_IS_SET(GET_CPSR))
                    goto L_Next;
                break;
            case COND_CS:
                if (C_IS_CLEAR(GET_CPSR))
                    goto L_Next;
                break;
            case COND_CC:
                if (C_IS_SET(GET_CPSR))
                    goto L_Next;
                break;
            case COND_MI:
                if (N_IS_CLEAR(GET_CPSR))
                    goto L_Next;
                break;
            case COND_PL:
                if (N_IS_SET(GET_CPSR))
                    goto L_Next;
                break;
            case COND_VS:
                if (V_IS_CLEAR(GET_CPSR))
                    goto L_Next;
                break;
            case COND_VC:
                if (V_IS_SET(GET_CPSR))
                    goto L_Next;
                break;
            case COND_HI:
                if (C_IS_CLEAR(GET_CPSR) || Z_IS_SET(GET_CPSR))
                    goto L_Next;
                break;
            case COND_LS:
                if (C_IS_SET(GET_CPSR) && Z_IS_CLEAR(GET_CPSR))
                    goto L_Next;
                break;
            case COND_GE:
                if (!(GET_CPSR & N_MASK) != !(GET_CPSR & V_MASK)) /* Use x ^ (x >> ...) method */
                    goto L_Next;
                break;
            case COND_LT:
                if (!(GET_CPSR & N_MASK) == !(GET_CPSR & V_MASK))
                    goto L_Next;
                break;
            case COND_GT:
                if (Z_IS_SET(GET_CPSR) || (!(GET_CPSR & N_MASK) != !(GET_CPSR & V_MASK)))
                    goto L_Next;
                break;
            case COND_LE:
                if (Z_IS_CLEAR(GET_CPSR) && (!(GET_CPSR & N_MASK) == !(GET_CPSR & V_MASK)))
                  goto L_Next;
                break;
            case COND_NV:
                goto L_Next;
            }
            /*******************************************************************/
            /* If we got here - condition satisfied, so decode the instruction */
            /*******************************************************************/
            switch ((insn & 0xF000000) >> 24)
            {
                case 0:
                case 1:
                case 2:
                case 3:
                    /* Branch and Exchange (BX) */
                    if ((insn & 0x0ffffff0) == 0x012fff10)     // bits 27-4 == 000100101111111111110001
                    {
                        R15 = GET_REGISTER(cpustate, insn & 0x0f);
                        // If new PC address has A0 set, switch to Thumb mode
                        if (R15 & 1) {
                            SET_CPSR(GET_CPSR|T_MASK);
                            R15--;
                        }
                    }
                    else
                    /* Multiply OR Swap OR Half Word Data Transfer */
                    if ((insn & 0x0e000000) == 0 && (insn & 0x80) && (insn & 0x10))  // bits 27-25=000 bit 7=1 bit 4=1
                    {
                        /* Half Word Data Transfer */
                        if (insn & 0x60)         // bits = 6-5 != 00
                        {
                            HandleHalfWordDT(cpustate, insn);
                        }
                        else
                        /* Swap */
                        if (insn & 0x01000000)   // bit 24 = 1
                        {
                            HandleSwap(cpustate, insn);
                        }
                        /* Multiply Or Multiply Long */
                        else
                        {
                            /* multiply long */
                            if (insn & 0x800000) // Bit 23 = 1 for Multiply Long
                            {
                                /* Signed? */
                                if (insn & 0x00400000)
                                    HandleSMulLong(cpustate, insn);
                                else
                                    HandleUMulLong(cpustate, insn);
                            }
                            /* multiply */
                            else
                            {
                                HandleMul(cpustate, insn);
                            }
                            R15 += 4;
                        }
                    }
                    else
                    /* Data Processing OR PSR Transfer */
                    if ((insn & 0x0c000000) == 0)   // bits 27-26 == 00 - This check can only exist properly after Multiplication check above
                    {
                        /* PSR Transfer (MRS & MSR) */
                        if (((insn & 0x00100000) == 0) && ((insn & 0x01800000) == 0x01000000)) // S bit must be clear, and bit 24,23 = 10
                        {
                            HandlePSRTransfer(cpustate, insn);
                            ARM7_ICOUNT += 2;       // PSR only takes 1 - S Cycle, so we add + 2, since at end, we -3..
                            R15 += 4;
                        }
                        /* Data Processing */
                        else
                        {
                            HandleALU(cpustate, insn);
                        }
                    }
                    break;
                /* Data Transfer - Single Data Access */
                case 4:
                case 5:
                case 6:
                case 7:
                    HandleMemSingle(cpustate, insn);
                    R15 += 4;
                    break;
                /* Block Data Transfer/Access */
                case 8:
                case 9:
                    HandleMemBlock(cpustate, insn);
                    R15 += 4;
                    break;
                /* Branch or Branch & Link */
                case 0xa:
                case 0xb:
                    HandleBranch(cpustate, insn);
                    break;
                /* Co-Processor Data Transfer */
                case 0xc:
                case 0xd:
                    HandleCoProcDT(cpustate, insn);
                    R15 += 4;
                    break;
                /* Co-Processor Data Operation or Register Transfer */
                case 0xe:
                    if (insn & 0x10)
                        HandleCoProcRT(cpustate, insn);
                    else
                        HandleCoProcDO(cpustate, insn);
                    R15 += 4;
                    break;
                /* Software Interrupt */
                case 0x0f:
                    cpustate->pendingSwi = 1;
                    ARM7_CHECKIRQ;
                    //couldn't find any cycle counts for SWI
                    break;
                /* Undefined */
                default:
                    cpustate->pendingSwi = 1;
                    ARM7_CHECKIRQ;
                    ARM7_ICOUNT -= 1;               //undefined takes 4 cycles (page 77)
                    LOG(("%08x:  Undefined instruction\n",pc-4));
                    L_Next:
                        R15 += 4;
                        ARM7_ICOUNT +=2;    //Any unexecuted instruction only takes 1 cycle (page 193)
            }
        }

        ARM7_CHECKIRQ;

        /* All instructions remove 3 cycles.. Others taking less / more will have adjusted this # prior to here */
        ARM7_ICOUNT -= 3;
    } while (ARM7_ICOUNT > 0);

    return cycles - ARM7_ICOUNT;
}
