//
// ARM7 processor emulator - THUMB support
// version 2.0 / 2008-02-08
// By R. Belmont and SGINut
//

#include "arm7.h"
#include "arm7i.h"
#include "arm7thumb.h"

#define change_pc(pc)

// base cycle counts for Thumb instructions
static const int thumbCycles[256] =
{
//  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 0
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 1
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 2
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  // 3
    1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3,  // 4
    2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,  // 5
    2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,  // 6
    2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,  // 7
    2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,  // 8
    2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,  // 9
    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,  // a
    1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 2, 4, 1, 1,  // b
    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,  // c
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3,  // d
    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,  // e
    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2   // f
};

// utility macros

#define IsNeg(i) ((i) >> 31)
#define IsPos(i) ((~(i)) >> 31)

#define N_BIT   31
#define Z_BIT   30
#define C_BIT   29
#define V_BIT   28
#define SIGN_BIT                ((UINT32)(1<<31))

#define HandleThumbALUAddFlags(rd, rn, op2) \
    ARM7_SetCPSR( \
      ((GET_CPSR &~ (ARM7_CPSR_N | ARM7_CPSR_Z | ARM7_CPSR_V | ARM7_CPSR_C)) \
      | (((!THUMB_SIGN_BITS_DIFFER(rn, op2)) && THUMB_SIGN_BITS_DIFFER(rn, rd)) \
          << V_BIT) \
      | (((~(rn)) < (op2)) << C_BIT) \
      | HandleALUNZFlags(rd))); \
  	R15 += 2;

#define HandleThumbALUSubFlags(rd, rn, op2) \
    ARM7_SetCPSR( \
      ((GET_CPSR &~ (ARM7_CPSR_N | ARM7_CPSR_Z | ARM7_CPSR_V | ARM7_CPSR_C)) \
      | ((THUMB_SIGN_BITS_DIFFER(rn, op2) && THUMB_SIGN_BITS_DIFFER(rn, rd)) \
          << V_BIT) \
      | (((IsNeg(rn) & IsPos(op2)) | (IsNeg(rn) & IsPos(rd)) | (IsPos(op2) & IsPos(rd))) ? ARM7_CPSR_C : 0) \
      | HandleALUNZFlags(rd))); \
	R15 += 2;

#define HandleALUNZFlags(rd) \
  (((rd) & SIGN_BIT) | ((!(rd)) << Z_BIT))

// memory accessors
#include "arm7memil.c"

// public functions
int ARM7i_Thumb_Step()
{
	UINT32 readword;
	UINT32 addr, insn;
	UINT32 rm, rn, rs, rd, op2, imm, rrs, rrd;
	INT32 offs;
	UINT32 pc = ARM7.Rx[ARM7_PC];
	int cycles;

	insn = arm7_read_16(pc & (~1));

	cycles = (3 - thumbCycles[insn >> 8]);

	switch( ( insn & THUMB_INSN_TYPE ) >> THUMB_INSN_TYPE_SHIFT )
	{
		case 0x0: /* Logical shifting */
			ARM7_SetCPSR(GET_CPSR &~ (ARM7_CPSR_N | ARM7_CPSR_Z));
			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(rs);
				offs = ( insn & THUMB_SHIFT_AMT ) >> THUMB_SHIFT_AMT_SHIFT;
				if( offs != 0 )
				{
					SET_REGISTER( rd, rrs >> offs );
				if( rrs & ( 1 << (offs-1) ) )
				{
					ARM7_SetCPSR(GET_CPSR | ARM7_CPSR_C);
				}
				else
				{
					ARM7_SetCPSR(GET_CPSR &~ ARM7_CPSR_C);
				}
				}
				else
				{
					SET_REGISTER( rd, 0 );
					if( rrs & 0x80000000 )
					{
					        ARM7_SetCPSR( GET_CPSR | ARM7_CPSR_C );
					}
					else
					{
					        ARM7_SetCPSR( GET_CPSR &~ ARM7_CPSR_C );
					}
				}
				ARM7_SetCPSR(GET_CPSR &~ ( ARM7_CPSR_Z | ARM7_CPSR_N ) );
				ARM7_SetCPSR( GET_CPSR | HandleALUNZFlags( GET_REGISTER(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(rs);
				offs = ( insn & THUMB_SHIFT_AMT ) >> THUMB_SHIFT_AMT_SHIFT;
				if( offs != 0 )
				{
					SET_REGISTER( rd, rrs << offs );
					if( rrs & ( 1 << ( 31 - ( offs - 1 ) ) ) )
					{
						ARM7_SetCPSR(GET_CPSR | ARM7_CPSR_C);
					}
					else
					{
						ARM7_SetCPSR(GET_CPSR &~ ARM7_CPSR_C);
					}
				}
				else
				{
					SET_REGISTER( rd, rrs );
				}
				ARM7_SetCPSR( GET_CPSR &~ ( ARM7_CPSR_Z | ARM7_CPSR_N ) );
				ARM7_SetCPSR( GET_CPSR | HandleALUNZFlags( GET_REGISTER(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( ( insn & THUMB_ADDSUB_RNIMM ) >> THUMB_ADDSUB_RNIMM_SHIFT );
						rs = GET_REGISTER( ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT );
						rd = ( insn & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT;
						SET_REGISTER( rd, rs + rn );
						HandleThumbALUAddFlags( GET_REGISTER(rd), rs, rn );
						break;
					case 0x1: /* SUB Rd, Rs, Rn */
						rn = GET_REGISTER( ( insn & THUMB_ADDSUB_RNIMM ) >> THUMB_ADDSUB_RNIMM_SHIFT );
						rs = GET_REGISTER( ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT );
						rd = ( insn & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT;
						SET_REGISTER( rd, rs - rn );
						HandleThumbALUSubFlags( GET_REGISTER(rd), rs, rn );
						break;
					case 0x2: /* ADD Rd, Rs, #imm */
						imm = ( insn & THUMB_ADDSUB_RNIMM ) >> THUMB_ADDSUB_RNIMM_SHIFT;
						rs = GET_REGISTER( ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT );
						rd = ( insn & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT;
						SET_REGISTER( rd, rs + imm );
						HandleThumbALUAddFlags( GET_REGISTER(rd), rs, imm );
						break;
					case 0x3: /* SUB Rd, Rs, #imm */
						imm = ( insn & THUMB_ADDSUB_RNIMM ) >> THUMB_ADDSUB_RNIMM_SHIFT;
						rs = GET_REGISTER( ( insn & THUMB_ADDSUB_RS ) >> THUMB_ADDSUB_RS_SHIFT );
						rd = ( insn & THUMB_ADDSUB_RD ) >> THUMB_ADDSUB_RD_SHIFT;
						SET_REGISTER( rd, rs - imm );
						HandleThumbALUSubFlags( GET_REGISTER(rd), rs,imm );
						break;
					default:
						printf("%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(rs);
					offs = ( insn & THUMB_SHIFT_AMT ) >> THUMB_SHIFT_AMT_SHIFT;
					if( offs == 0 )
					{
						offs = 32;
					}

					if( offs >= 32 )
					{
					        if( rrs >> 31 )
					{
						ARM7_SetCPSR(GET_CPSR | ARM7_CPSR_C);
					}
					else
					{
						ARM7_SetCPSR(GET_CPSR &~ ARM7_CPSR_C);
					}
					        SET_REGISTER( rd, ( rrs & 0x80000000 ) ? 0xFFFFFFFF : 0x00000000 );
					}
					else
					{
					        if( ( rrs >> ( offs - 1 ) ) & 1 )
					        {
					        ARM7_SetCPSR( GET_CPSR | ARM7_CPSR_C );
					        }
					        else
					        {
					        ARM7_SetCPSR( GET_CPSR &~ ARM7_CPSR_C );
					        }
					        SET_REGISTER( rd, ( rrs & 0x80000000 ) ? ( ( 0xFFFFFFFF << ( 32 - offs ) ) | ( rrs >> offs ) ) : ( rrs >> offs ) );
					}
					ARM7_SetCPSR(GET_CPSR &~ (ARM7_CPSR_N | ARM7_CPSR_Z));
					ARM7_SetCPSR( GET_CPSR | HandleALUNZFlags( GET_REGISTER(rd) ) );
					R15 += 2;
				}

			}
			break;
		case 0x2: /* CMP / MOV */
			if( insn & THUMB_INSN_CMP )
			{
				rn = GET_REGISTER( ( 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( ( 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( rd, op2 );
				ARM7_SetCPSR( GET_CPSR &~ ( ARM7_CPSR_Z | ARM7_CPSR_N ) );
				ARM7_SetCPSR( GET_CPSR | HandleALUNZFlags( GET_REGISTER(rd) ) );
				R15 += 2;
			}
			break;
		case 0x3: /* ADD/SUB immediate */
			if( insn & THUMB_INSN_SUB ) /* SUB Rd, #Offset8 */
			{
				rn = GET_REGISTER( ( 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( ( insn & THUMB_INSN_IMM_RD ) >> THUMB_INSN_IMM_RD_SHIFT, rd );
				HandleThumbALUSubFlags( rd, rn, op2 );
			}
			else /* ADD Rd, #Offset8 */
			{
				rn = GET_REGISTER( ( 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( ( 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( rd, GET_REGISTER(rd) & GET_REGISTER(rs) );
							ARM7_SetCPSR( GET_CPSR &~ ( ARM7_CPSR_Z | ARM7_CPSR_N ) );
							ARM7_SetCPSR( GET_CPSR | HandleALUNZFlags( GET_REGISTER(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( rd, GET_REGISTER(rd) ^ GET_REGISTER(rs) );
							ARM7_SetCPSR( GET_CPSR &~ ( ARM7_CPSR_Z | ARM7_CPSR_N ) );
							ARM7_SetCPSR( GET_CPSR | HandleALUNZFlags( GET_REGISTER(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(rd);
							offs = GET_REGISTER(rs) & 0x000000ff;
							if (offs > 0)
							{
								if ( offs < 32 )
								{
								        SET_REGISTER( rd, rrd << offs );
								        if( rrd & ( 1 << ( 31 - ( offs - 1 ) ) ) )
								        {
									        ARM7_SetCPSR( GET_CPSR | ARM7_CPSR_C );
								        }
								        else
								        {
									        ARM7_SetCPSR( GET_CPSR &~ ARM7_CPSR_C );
								        }
								}
								else if( offs == 32 )
								{
								        SET_REGISTER( rd, 0 );
								        if( rrd & 1 )
								        {
									        ARM7_SetCPSR( GET_CPSR | ARM7_CPSR_C );
								        }
								        else
								        {
									        ARM7_SetCPSR( GET_CPSR &~ ARM7_CPSR_C );
								        }
								}
								else
								{
								        SET_REGISTER( rd, 0 );
								        ARM7_SetCPSR( GET_CPSR &~ ARM7_CPSR_C );
								}
							}
							ARM7_SetCPSR( GET_CPSR &~ ( ARM7_CPSR_Z | ARM7_CPSR_N ) );
							ARM7_SetCPSR( GET_CPSR | HandleALUNZFlags( GET_REGISTER(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(rd);
							offs = GET_REGISTER(rs) & 0x000000ff;
							if (offs >  0)
							{
								if( offs < 32 )
								{
								        SET_REGISTER( rd, rrd >> offs );
								        if( rrd & ( 1 << ( offs - 1 ) ) )
								        {
									        ARM7_SetCPSR( GET_CPSR | ARM7_CPSR_C );
								        }
								        else
								        {
									        ARM7_SetCPSR( GET_CPSR &~ ARM7_CPSR_C );
								        }
								}
								else if( offs == 32 )
								{
								        SET_REGISTER( rd, 0 );
								        if( rrd & 0x80000000 )
								        {
									        ARM7_SetCPSR( GET_CPSR | ARM7_CPSR_C );
								        }
								        else
								        {
									        ARM7_SetCPSR( GET_CPSR &~ ARM7_CPSR_C );
								        }
								}
								else
								{
								        SET_REGISTER( rd, 0 );
								        ARM7_SetCPSR( GET_CPSR &~ ARM7_CPSR_C );
								}
							}
							ARM7_SetCPSR( GET_CPSR &~ ( ARM7_CPSR_Z | ARM7_CPSR_N ) );
							ARM7_SetCPSR( GET_CPSR | HandleALUNZFlags( GET_REGISTER(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(rs)&0xff;
								rrd = GET_REGISTER(rd);
								if (rrs != 0)
								{
									if (rrs >= 32)
									{
										if (rrd>>31)
										{
											ARM7_SetCPSR(GET_CPSR | ARM7_CPSR_C);
										}
										else
										{
											ARM7_SetCPSR(GET_CPSR &~ ARM7_CPSR_C);
										}
										SET_REGISTER( rd, (GET_REGISTER(rd) & 0x80000000) ? 0xFFFFFFFF : 0x00000000 );
									}
									else
									{
										if ((rrd>>(rs-1))&1)
										{
											ARM7_SetCPSR(GET_CPSR | ARM7_CPSR_C);
										}
										else
										{
											ARM7_SetCPSR(GET_CPSR &~ ARM7_CPSR_C);
										}
										SET_REGISTER( rd, (rrd & 0x80000000) ? ((0xFFFFFFFF<<(32-rrs)) | (rrd>>rrs)) : (rrd>>rrs));
									}
								}
								ARM7_SetCPSR(GET_CPSR &~ (ARM7_CPSR_N | ARM7_CPSR_Z));
								ARM7_SetCPSR( GET_CPSR | HandleALUNZFlags( GET_REGISTER(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 & ARM7_CPSR_C) ? 1 : 0;
							rn=GET_REGISTER(rd) + GET_REGISTER(rs) + op2;
							HandleThumbALUAddFlags( rn, GET_REGISTER(rd), ( GET_REGISTER(rs) ) ); //?
							SET_REGISTER( 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 & ARM7_CPSR_C) ? 0 : 1;
							rn=GET_REGISTER(rd) - GET_REGISTER(rs) - op2;
							HandleThumbALUSubFlags( rn, GET_REGISTER(rd), ( GET_REGISTER(rs) ) ); //?
							SET_REGISTER( 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(rd);
							imm = GET_REGISTER(rs) & 0x0000001f;
							SET_REGISTER( rd, ( rrd >> imm ) | ( rrd << ( 32 - imm ) ) );
							if( rrd & ( 1 << ( imm - 1 ) ) )
							{
							        ARM7_SetCPSR( GET_CPSR | ARM7_CPSR_C );
							}
							else
							{
							        ARM7_SetCPSR( GET_CPSR &~ ARM7_CPSR_C );
							}
							ARM7_SetCPSR( GET_CPSR &~ ( ARM7_CPSR_Z | ARM7_CPSR_N ) );
							ARM7_SetCPSR( GET_CPSR | HandleALUNZFlags( GET_REGISTER(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;
							ARM7_SetCPSR( GET_CPSR &~ ( ARM7_CPSR_Z | ARM7_CPSR_N ) );
							ARM7_SetCPSR( GET_CPSR | HandleALUNZFlags( GET_REGISTER(rd) & GET_REGISTER(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(rs);
							rn = 0 - rrs;
							SET_REGISTER( rd, rn );
							HandleThumbALUSubFlags( GET_REGISTER(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(rd) - GET_REGISTER(rs);
							HandleThumbALUSubFlags( rn, GET_REGISTER(rd), GET_REGISTER(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(rd) + GET_REGISTER(rs);
							HandleThumbALUAddFlags( rn, GET_REGISTER(rd), GET_REGISTER(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( rd, GET_REGISTER(rd) | GET_REGISTER(rs) );
							ARM7_SetCPSR( GET_CPSR &~ ( ARM7_CPSR_Z | ARM7_CPSR_N ) );
							ARM7_SetCPSR( GET_CPSR | HandleALUNZFlags( GET_REGISTER(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(rd) * GET_REGISTER(rs);
							ARM7_SetCPSR( GET_CPSR &~ ( ARM7_CPSR_Z | ARM7_CPSR_N ) );
							SET_REGISTER( rd, rn );
							ARM7_SetCPSR( 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( rd, GET_REGISTER(rd) & (~GET_REGISTER(rs)) );
							ARM7_SetCPSR( GET_CPSR &~ ( ARM7_CPSR_Z | ARM7_CPSR_N ) );
							ARM7_SetCPSR( GET_CPSR | HandleALUNZFlags( GET_REGISTER(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(rs);
							SET_REGISTER( rd, ~op2 );
							ARM7_SetCPSR( GET_CPSR &~ ( ARM7_CPSR_Z | ARM7_CPSR_N ) );
							ARM7_SetCPSR( GET_CPSR | HandleALUNZFlags( GET_REGISTER(rd) ) );
							R15 += 2;
							break;
						default:
							printf("%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( rd, GET_REGISTER(rd) + GET_REGISTER(rs+8) );
									// emulate the effects of pre-fetch
									if (rs == 7)
									{
										SET_REGISTER(rd, GET_REGISTER(rd) + 4);
									}
									break;
								case 0x2: /* ADD HRd, Rs */
									SET_REGISTER( rd+8, GET_REGISTER(rd+8) + GET_REGISTER(rs) );
									if (rd == 7)
									{
										R15 += 2;
										change_pc(R15);
									}
									break;
								case 0x3: /* Add HRd, HRs */
									SET_REGISTER( rd+8, GET_REGISTER(rd+8) + GET_REGISTER(rs+8) );
									// emulate the effects of pre-fetch
									if (rs == 7)
									{
										SET_REGISTER(rd+8, GET_REGISTER(rd+8) + 4);
									}
									if (rd == 7)
									{
										R15 += 2;
										change_pc(R15);
									}
									break;
								default:
									printf("%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( ( ( insn & THUMB_HIREG_RS ) >> THUMB_HIREG_RS_SHIFT ) );
									rd = GET_REGISTER( insn & THUMB_HIREG_RD );
									rn = rd - rs;
									HandleThumbALUSubFlags( rn, rd, rs );
									break;
								case 0x1: /* CMP Rd, Hs */
									rs = GET_REGISTER( ( ( insn & THUMB_HIREG_RS ) >> THUMB_HIREG_RS_SHIFT ) + 8 );
									rd = GET_REGISTER( insn & THUMB_HIREG_RD );
									rn = rd - rs;
									HandleThumbALUSubFlags( rn, rd, rs );
									break;
								case 0x2: /* CMP Hd, Rs */
									rs = GET_REGISTER( ( ( insn & THUMB_HIREG_RS ) >> THUMB_HIREG_RS_SHIFT ) );
									rd = GET_REGISTER( (insn & THUMB_HIREG_RD) + 8 );
									rn = rd - rs;
									HandleThumbALUSubFlags( rn, rd, rs );
									break;
								case 0x3: /* CMP Hd, Hs */
									rs = GET_REGISTER( ( ( insn & THUMB_HIREG_RS ) >> THUMB_HIREG_RS_SHIFT ) + 8 );
									rd = GET_REGISTER( (insn & THUMB_HIREG_RD) + 8 );
									rn = rd - rs;
									HandleThumbALUSubFlags( rn, rd, rs );
									break;
								default:
									printf("%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( rd, GET_REGISTER(rs + 8) + 4 );
									}
									else
									{
										SET_REGISTER( rd, GET_REGISTER(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( rd + 8, GET_REGISTER(rs) );
									if( rd != 7 )
									{
										R15 += 2;
									}
									else
				                                        {
                                            					R15 &= ~1;
										change_pc(R15);
                                        				}
									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( rd + 8, GET_REGISTER(rs+8)+4 );
									}
									else
									{
									SET_REGISTER( rd + 8, GET_REGISTER(rs+8) );
									}
									if( rd != 7 )
									{
										R15 += 2;
									}

								        if( rd == 7 )
				                                        {
                                            					R15 &= ~1;
										change_pc(R15);
                                        				}
									break;
								default:
									printf("%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(rd);
									if( addr & 1 )
									{
										addr &= ~1;
									}
									else
									{
										ARM7_SetCPSR(GET_CPSR &~ ARM7_CPSR_T);
										if( addr & 2 )
										{
											addr += 2;
										}
									}
									R15 = addr;
									break;
								case 0x1:
									addr = GET_REGISTER( ( ( 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
									{
										ARM7_SetCPSR(GET_CPSR &~ ARM7_CPSR_T);
										if( addr & 2 )
										{
											addr += 2;
										}
									}
									R15 = addr;
									break;
								default:
									printf("%08x: G4-3 Undefined Thumb instruction: %04x\n", pc, insn);
									R15 += 2;
									break;
							}
							break;
						default:
							printf("%08x: G4-x Undefined Thumb instruction: %04x\n", pc, insn);
							R15 += 2;
							break;
					}
					break;
				case 0x2:
				case 0x3:
					readword = arm7_read_32( ( R15 & ~2 ) + 4 + ( ( insn & THUMB_INSN_IMM ) << 2 ) );
					SET_REGISTER( ( insn & THUMB_INSN_IMM_RD ) >> THUMB_INSN_IMM_RD_SHIFT, readword );
					R15 += 2;
					break;
				default:
					printf("%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(rn) + GET_REGISTER(rm);
					arm7_write_32( addr, GET_REGISTER(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(rn) + GET_REGISTER(rm);
					arm7_write_16( addr, GET_REGISTER(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(rn) + GET_REGISTER(rm);
 					arm7_write_8( addr, GET_REGISTER(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(rn) + GET_REGISTER(rm);
					op2 = arm7_read_8( addr );
					if( op2 & 0x00000080 )
					{
						op2 |= 0xffffff00;
					}
					SET_REGISTER( 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(rn) + GET_REGISTER(rm);
					op2 = arm7_read_32( addr );
					SET_REGISTER( 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(rn) + GET_REGISTER(rm);
					op2 = arm7_read_16( addr );
					SET_REGISTER( 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(rn) + GET_REGISTER(rm);
					op2 = arm7_read_8( addr );
					SET_REGISTER( 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(rn) + GET_REGISTER(rm);
					op2 = arm7_read_16( addr );
					if( op2 & 0x00008000 )
					{
						op2 |= 0xffff0000;
					}
					SET_REGISTER( rd, op2 );
					R15 += 2;
					break;
				default:
					printf("%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( rd, arm7_read_32(GET_REGISTER(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;
				arm7_write_32( GET_REGISTER(rn) + offs, GET_REGISTER(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( rd, arm7_read_8( GET_REGISTER(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;
				arm7_write_8( GET_REGISTER(rn) + offs, GET_REGISTER(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( rd, arm7_read_16( GET_REGISTER(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;
				arm7_write_16( GET_REGISTER(rs) + ( imm << 1 ), GET_REGISTER(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 = arm7_read_32( GET_REGISTER(13) + ( (UINT32)offs << 2 ) );
				SET_REGISTER( rd, readword );
				R15 += 2;
			}
			else
			{
				rd = ( insn & THUMB_STACKOP_RD ) >> THUMB_STACKOP_RD_SHIFT;
				offs = (UINT8)( insn & THUMB_INSN_IMM );
				arm7_write_32( GET_REGISTER(13) + ( (UINT32)offs << 2 ), GET_REGISTER(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( rd, GET_REGISTER(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( 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( 13, GET_REGISTER(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( 13, GET_REGISTER(13) - 4 );
							arm7_write_32( GET_REGISTER(13), GET_REGISTER(offs) );
						}
					}
					R15 += 2;
					break;
				case 0x5: /* PUSH {Rlist}{LR} */
					SET_REGISTER( 13, GET_REGISTER(13) - 4 );
					arm7_write_32( GET_REGISTER(13), GET_REGISTER(14) );
					for( offs = 7; offs >= 0; offs-- )
					{
						if( insn & ( 1 << offs ) )
						{
							SET_REGISTER( 13, GET_REGISTER(13) - 4 );
							arm7_write_32( GET_REGISTER(13), GET_REGISTER(offs) );
						}
					}
					R15 += 2;
					break;
				case 0xc: /* POP {Rlist} */
					for( offs = 0; offs < 8; offs++ )
					{
						if( insn & ( 1 << offs ) )
						{
							SET_REGISTER( offs, arm7_read_32( GET_REGISTER(13) ) );
							SET_REGISTER( 13, GET_REGISTER(13) + 4 );
						}
					}
					R15 += 2;
					break;
				case 0xd: /* POP {Rlist}{PC} */
					for( offs = 0; offs < 8; offs++ )
					{
						if( insn & ( 1 << offs ) )
						{
							SET_REGISTER( offs, arm7_read_32( GET_REGISTER(13) ) );
							SET_REGISTER( 13, GET_REGISTER(13) + 4 );
						}
					}
					R15 = arm7_read_32( GET_REGISTER(13) ) & ~1;
					SET_REGISTER( 13, GET_REGISTER(13) + 4 );
					break;
				default:
					printf("%08x: Gb Undefined Thumb instruction: %04x\n", pc, insn);
					R15 += 2;
					break;
			}
			break;
		case 0xc: /* Multiple Load/Store */
			if( insn & THUMB_MULTLS ) /* Load */
			{
				rd = ( insn & THUMB_MULTLS_BASE ) >> THUMB_MULTLS_BASE_SHIFT;
				for( offs = 0; offs < 8; offs++ )
				{
					if( insn & ( 1 << offs ) )
					{
						SET_REGISTER( offs, arm7_read_32( (GET_REGISTER(rd)&0xfffffffc) ) );
						SET_REGISTER( rd, GET_REGISTER(rd) + 4 );
					}
				}
				R15 += 2;
			}
			else /* Store */
			{
				rd = ( insn & THUMB_MULTLS_BASE ) >> THUMB_MULTLS_BASE_SHIFT;
				for( offs = 0; offs < 8; offs++ )
				{
					if( insn & ( 1 << offs ) )
					{
						arm7_write_32( (GET_REGISTER(rd)&0xfffffffc), GET_REGISTER(offs) );
						SET_REGISTER( rd, GET_REGISTER(rd) + 4 );
					}
				}
				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 & ARM7_CPSR_N) == !(GET_CPSR & ARM7_CPSR_V) )
		        	    	{
						R15 += 4 + (offs << 1);
					}
					else
					{
						R15 += 2;
					}
					break;
				case COND_LT:
		        	    	if( !(GET_CPSR & ARM7_CPSR_N) != !(GET_CPSR & ARM7_CPSR_V) )
		        	    	{
						R15 += 4 + (offs << 1);
					}
					else
					{
						R15 += 2;
					}
					break;
				case COND_GT:
		        	    	if( Z_IS_CLEAR(GET_CPSR) && ( !(GET_CPSR & ARM7_CPSR_N) == !(GET_CPSR & ARM7_CPSR_V) ) )
		        	    	{
						R15 += 4 + (offs << 1);
					}
					else
					{
						R15 += 2;
					}
					break;
				case COND_LE:
		        	    	if( Z_IS_SET(GET_CPSR) || ( !(GET_CPSR & ARM7_CPSR_N) != !(GET_CPSR & ARM7_CPSR_V) ) )
		        	    	{
						R15 += 4 + (offs << 1);
					}
					else
					{
						R15 += 2;
					}
					break;
				case COND_AL:
					printf("%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)
//					ARM7_SetSWI();
//					R15 -= 4;
					break;
			}
			break;
		case 0xe: /* B #offs */
        if( insn & THUMB_BLOP_LO )
        {
                addr = GET_REGISTER(14);
                addr += ( insn & THUMB_BLOP_OFFS ) << 1;
                addr &= 0xfffffffc;
                SET_REGISTER( 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(14);
				addr += ( insn & THUMB_BLOP_OFFS ) << 1;
				SET_REGISTER( 14, ( R15 + 2 ) | 1 );
				R15 = addr;
			}
			else
			{
				addr = ( insn & THUMB_BLOP_OFFS ) << 12;
				if( addr & ( 1 << 22 ) )
				{
					addr |= 0xff800000;
				}
				addr += R15 + 4;
				SET_REGISTER( 14, addr );
				R15 += 2;
			}
			break;
		default:
			printf("%08x: Undefined Thumb instruction: %04x\n", pc, insn);
			R15 += 2;
			break;
	}

	return cycles;
}
