summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/cpu/arm7/arm7exec.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/cpu/arm7/arm7exec.c')
-rw-r--r--trunk/src/emu/cpu/arm7/arm7exec.c1648
1 files changed, 1648 insertions, 0 deletions
diff --git a/trunk/src/emu/cpu/arm7/arm7exec.c b/trunk/src/emu/cpu/arm7/arm7exec.c
new file mode 100644
index 00000000000..5cf952c46e4
--- /dev/null
+++ b/trunk/src/emu/cpu/arm7/arm7exec.c
@@ -0,0 +1,1648 @@
+/*****************************************************************************
+ *
+ * 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);
+
+ do
+ {
+ debugger_instruction_hook(cpustate->device, GET_PC);
+
+ /* handle Thumb instructions if active */
+ if (T_IS_SET(GET_CPSR))
+ {
+ UINT32 readword, addr, raddr;
+ UINT32 rm, rn, rs, rd, op2, imm, rrs, rrd;
+ INT32 offs;
+
+ pc = R15;
+
+ // "In Thumb state, bit [0] is undefined and must be ignored. Bits [31:1] contain the PC."
+ raddr = pc & (~1);
+
+ if ( COPRO_CTRL & COPRO_CTRL_MMU_EN )
+ {
+ raddr = arm7_tlb_translate(cpustate, raddr, ARM7_TLB_ABORT_P);
+ if (cpustate->pendingAbtP != 0)
+ {
+ goto skip_exec;
+ }
+ }
+ insn = cpustate->direct->read_decrypted_word(raddr);
+ 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 */
+ 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 0x0: // MOV Rd, Rs (undefined)
+ // "The action of H1 = 0, H2 = 0 for Op = 00 (ADD), Op = 01 (CMP) and Op = 10 (MOV) is undefined, and should not be used."
+ rs = (insn & THUMB_HIREG_RS) >> THUMB_HIREG_RS_SHIFT;
+ rd = insn & THUMB_HIREG_RD;
+ SET_REGISTER(cpustate, rd, GET_REGISTER(cpustate, rs));
+ R15 += 2;
+ break;
+ 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;
+
+ // "The address should normally be a word aligned quantity and non-word aligned addresses do not affect the instruction."
+ // "However, the bottom 2 bits of the address will appear on A[1:0] and might be interpreted by the memory system."
+
+ // GBA "BB Ball" performs an unaligned read with A[1:0] = 2 and expects A[1] not to be ignored [BP 800B90A,(R4&3)!=0]
+ // GBA "Gadget Racers" performs an unaligned read with A[1:0] = 1 and expects A[0] to be ignored [BP B72,(R0&3)!=0]
+
+ ld_st_address = GET_REGISTER(cpustate, rd);
+
+ 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 & ~1));
+ 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 & ~3, 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) & ~1;
+ 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
+ {
+ UINT32 raddr;
+
+ /* load 32 bit instruction */
+ pc = GET_PC;
+
+ // "In ARM state, bits [1:0] of r15 are undefined and must be ignored. Bits [31:2] contain the PC."
+ raddr = pc & (~3);
+
+ if ( COPRO_CTRL & COPRO_CTRL_MMU_EN )
+ {
+ raddr = arm7_tlb_translate(cpustate, raddr, ARM7_TLB_ABORT_P);
+ if (cpustate->pendingAbtP != 0)
+ {
+ goto skip_exec;
+ }
+ }
+
+#if 0
+ if (MODE26)
+ {
+ UINT32 temp1, temp2;
+ temp1 = GET_CPSR & 0xF00000C3;
+ temp2 = (R15 & 0xF0000000) | ((R15 & 0x0C000000) >> (26 - 6)) | (R15 & 0x00000003);
+ if (temp1 != temp2) fatalerror( "%08X: 32-bit and 26-bit modes are out of sync (%08X %08X)", pc, temp1, temp2);
+ }
+#endif
+
+ insn = cpustate->direct->read_decrypted_dword(raddr);
+
+ /* 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 if ((insn & 0x0ff000f0) == 0x01600010) // CLZ - v5
+ {
+ UINT32 rm = insn&0xf;
+ UINT32 rd = (insn>>12)&0xf;
+
+ SET_REGISTER(cpustate, rd, count_leading_zeros(GET_REGISTER(cpustate, rm)));
+
+ R15 += 4;
+ }
+ else if ((insn & 0x0ff000f0) == 0x01000050) // QADD - v5
+ {
+ INT32 src1 = GET_REGISTER(cpustate, insn&0xf);
+ INT32 src2 = GET_REGISTER(cpustate, (insn>>16)&0xf);
+ INT64 res;
+
+ res = saturate_qbit_overflow(cpustate, (INT64)src1 + (INT64)src2);
+
+ SET_REGISTER(cpustate, (insn>>12)&0xf, (INT32)res);
+ R15 += 4;
+ }
+ else if ((insn & 0x0ff000f0) == 0x01400050) // QDADD - v5
+ {
+ INT32 src1 = GET_REGISTER(cpustate, insn&0xf);
+ INT32 src2 = GET_REGISTER(cpustate, (insn>>16)&0xf);
+ INT64 res;
+
+ // check if doubling operation will overflow
+ res = (INT64)src2 * 2;
+ saturate_qbit_overflow(cpustate, res);
+
+ src2 *= 2;
+ res = saturate_qbit_overflow(cpustate, (INT64)src1 + (INT64)src2);
+
+ SET_REGISTER(cpustate, (insn>>12)&0xf, (INT32)res);
+ R15 += 4;
+ }
+ else if ((insn & 0x0ff000f0) == 0x01200050) // QSUB - v5
+ {
+ INT32 src1 = GET_REGISTER(cpustate, insn&0xf);
+ INT32 src2 = GET_REGISTER(cpustate, (insn>>16)&0xf);
+ INT64 res;
+
+ res = saturate_qbit_overflow(cpustate, (INT64)src1 - (INT64)src2);
+
+ SET_REGISTER(cpustate, (insn>>12)&0xf, (INT32)res);
+ R15 += 4;
+ }
+ else if ((insn & 0x0ff000f0) == 0x01600050) // QDSUB - v5
+ {
+ INT32 src1 = GET_REGISTER(cpustate, insn&0xf);
+ INT32 src2 = GET_REGISTER(cpustate, (insn>>16)&0xf);
+ INT64 res;
+
+ // check if doubling operation will overflow
+ res = (INT64)src2 * 2;
+ saturate_qbit_overflow(cpustate, res);
+
+ src2 *= 2;
+ res = saturate_qbit_overflow(cpustate, (INT64)src1 - (INT64)src2);
+
+ SET_REGISTER(cpustate, (insn>>12)&0xf, (INT32)res);
+ R15 += 4;
+ }
+ else if ((insn & 0x0ff00090) == 0x01000080) // SMLAxy - v5
+ {
+ INT32 src1 = GET_REGISTER(cpustate, insn&0xf);
+ INT32 src2 = GET_REGISTER(cpustate, (insn>>8)&0xf);
+ INT32 res1;
+
+ // select top and bottom halves of src1/src2 and sign extend if necessary
+ if (insn & 0x20)
+ {
+ src1 >>= 16;
+ }
+ else
+ {
+ src1 &= 0xffff;
+ if (src1 & 0x8000)
+ {
+ src1 |= 0xffff;
+ }
+ }
+
+ if (insn & 0x40)
+ {
+ src2 >>= 16;
+ }
+ else
+ {
+ src2 &= 0xffff;
+ if (src2 & 0x8000)
+ {
+ src2 |= 0xffff;
+ }
+ }
+
+ // do the signed multiply
+ res1 = src1 * src2;
+ // and the accumulate. NOTE: only the accumulate can cause an overflow, which is why we do it this way.
+ saturate_qbit_overflow(cpustate, (INT64)res1 + (INT64)GET_REGISTER(cpustate, (insn>>12)&0xf));
+
+ SET_REGISTER(cpustate, (insn>>16)&0xf, res1 + GET_REGISTER(cpustate, (insn>>12)&0xf));
+ R15 += 4;
+ }
+ else if ((insn & 0x0ff00090) == 0x01400080) // SMLALxy - v5
+ {
+ INT32 src1 = GET_REGISTER(cpustate, insn&0xf);
+ INT32 src2 = GET_REGISTER(cpustate, (insn>>8)&0xf);
+ INT64 dst;
+
+ // select top and bottom halves of src1/src2 and sign extend if necessary
+ if (insn & 0x20)
+ {
+ src1 >>= 16;
+ }
+ else
+ {
+ src1 &= 0xffff;
+ if (src1 & 0x8000)
+ {
+ src1 |= 0xffff;
+ }
+ }
+
+ if (insn & 0x40)
+ {
+ src2 >>= 16;
+ }
+ else
+ {
+ src2 &= 0xffff;
+ if (src2 & 0x8000)
+ {
+ src2 |= 0xffff;
+ }
+ }
+
+ dst = (INT64)GET_REGISTER(cpustate, (insn>>12)&0xf);
+ dst |= (INT64)GET_REGISTER(cpustate, (insn>>16)&0xf)<<32;
+
+ // do the multiply and accumulate
+ dst += (INT64)src1 * (INT64)src2;
+
+ // write back the result
+ SET_REGISTER(cpustart, (insn>>12)&0xf, (UINT32)(dst&0xffffffff));
+ SET_REGISTER(cpustart, (insn>>16)&0xf, (UINT32)(dst>>32));
+ }
+ else if ((insn & 0x0ff00090) == 0x01600080) // SMULxy - v5
+ {
+ INT32 src1 = GET_REGISTER(cpustate, insn&0xf);
+ INT32 src2 = GET_REGISTER(cpustate, (insn>>8)&0xf);
+ INT32 res;
+
+ // select top and bottom halves of src1/src2 and sign extend if necessary
+ if (insn & 0x20)
+ {
+ src1 >>= 16;
+ }
+ else
+ {
+ src1 &= 0xffff;
+ if (src1 & 0x8000)
+ {
+ src1 |= 0xffff;
+ }
+ }
+
+ if (insn & 0x40)
+ {
+ src2 >>= 16;
+ }
+ else
+ {
+ src2 &= 0xffff;
+ if (src2 & 0x8000)
+ {
+ src2 |= 0xffff;
+ }
+ }
+
+ res = src1 * src2;
+ SET_REGISTER(cpustart, (insn>>16)&0xf, res);
+ }
+ else if ((insn & 0x0ff000b0) == 0x012000a0) // SMULWy - v5
+ {
+ INT32 src1 = GET_REGISTER(cpustate, insn&0xf);
+ INT32 src2 = GET_REGISTER(cpustate, (insn>>8)&0xf);
+ INT64 res;
+
+ if (insn & 0x40)
+ {
+ src2 >>= 16;
+ }
+ else
+ {
+ src2 &= 0xffff;
+ if (src2 & 0x8000)
+ {
+ src2 |= 0xffff;
+ }
+ }
+
+ res = (INT64)src1 * (INT64)src2;
+ res >>= 16;
+ SET_REGISTER(cpustart, (insn>>16)&0xf, (UINT32)res);
+ }
+ else if ((insn & 0x0ff000b0) == 0x01200080) // SMLAWy - v5
+ {
+ INT32 src1 = GET_REGISTER(cpustate, insn&0xf);
+ INT32 src2 = GET_REGISTER(cpustate, (insn>>8)&0xf);
+ INT32 src3 = GET_REGISTER(cpustate, (insn>>12)&0xf);
+ INT64 res;
+
+ if (insn & 0x40)
+ {
+ src2 >>= 16;
+ }
+ else
+ {
+ src2 &= 0xffff;
+ if (src2 & 0x8000)
+ {
+ src2 |= 0xffff;
+ }
+ }
+
+ res = (INT64)src1 * (INT64)src2;
+ res >>= 16;
+
+ // check for overflow and set the Q bit
+ saturate_qbit_overflow(cpustate, (INT64)src3 + res);
+
+ // do the real accumulate
+ src3 += (INT32)res;
+
+ // write the result back
+ SET_REGISTER(cpustart, (insn>>16)&0xf, (UINT32)res);
+ }
+ 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)
+ }
+ }
+
+skip_exec:
+
+ 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);
+}