diff options
Diffstat (limited to 'src/devices/cpu/arm7/arm7ops.c')
-rw-r--r-- | src/devices/cpu/arm7/arm7ops.c | 1851 |
1 files changed, 1851 insertions, 0 deletions
diff --git a/src/devices/cpu/arm7/arm7ops.c b/src/devices/cpu/arm7/arm7ops.c new file mode 100644 index 00000000000..9b96ca9054c --- /dev/null +++ b/src/devices/cpu/arm7/arm7ops.c @@ -0,0 +1,1851 @@ +// license:BSD-3-Clause +// copyright-holders:Steve Ellenoff,R. Belmont,Ryan Holtz +#include "emu.h" +#include "arm7.h" +#include "arm7core.h" +#include "arm7help.h" + +INT64 arm7_cpu_device::saturate_qbit_overflow(INT64 res) +{ + if (res > 2147483647) // INT32_MAX + { // overflow high? saturate and set Q + res = 2147483647; + SET_CPSR(GET_CPSR | Q_MASK); + } + else if (res < (-2147483647-1)) // INT32_MIN + { // overflow low? saturate and set Q + res = (-2147483647-1); + SET_CPSR(GET_CPSR | Q_MASK); + } + + return res; +} + + +void arm7_cpu_device::SwitchMode(UINT32 cpsr_mode_val) +{ + UINT32 cspr = m_r[eCPSR] & ~MODE_FLAG; + set_cpsr(cspr | cpsr_mode_val); +} + + +/* Decodes an Op2-style shifted-register form. If @carry@ is non-zero the + * shifter carry output will manifest itself as @*carry == 0@ for carry clear + * and @*carry != 0@ for carry set. + + SJE: Rules: + IF RC = 256, Result = no shift. + LSL 0 = Result = RM, Carry = Old Contents of CPSR C Bit + LSL(0,31) = Result shifted, least significant bit is in carry out + LSL 32 = Result of 0, Carry = Bit 0 of RM + LSL >32 = Result of 0, Carry out 0 + LSR 0 = LSR 32 (see below) + LSR 32 = Result of 0, Carry = Bit 31 of RM + LSR >32 = Result of 0, Carry out 0 + ASR >=32 = ENTIRE Result = bit 31 of RM + ROR 32 = Result = RM, Carry = Bit 31 of RM + ROR >32 = Same result as ROR n-32 until amount in range of 1-32 then follow rules +*/ + +UINT32 arm7_cpu_device::decodeShift(UINT32 insn, UINT32 *pCarry) +{ + UINT32 k = (insn & INSN_OP2_SHIFT) >> INSN_OP2_SHIFT_SHIFT; // Bits 11-7 + UINT32 rm = GET_REGISTER(insn & INSN_OP2_RM); + UINT32 t = (insn & INSN_OP2_SHIFT_TYPE) >> INSN_OP2_SHIFT_TYPE_SHIFT; + + if ((insn & INSN_OP2_RM) == 0xf) { + // "If a register is used to specify the shift amount the PC will be 12 bytes ahead." (instead of 8) + rm += t & 1 ? 12 : 8; + } + + /* All shift types ending in 1 are Rk, not #k */ + if (t & 1) + { +// LOG(("%08x: RegShift %02x %02x\n", R15, k >> 1, GET_REGISTER(k >> 1))); +#if ARM7_DEBUG_CORE + if ((insn & 0x80) == 0x80) + LOG(("%08x: RegShift ERROR (p36)\n", R15)); +#endif + + // see p35 for check on this + //k = GET_REGISTER(k >> 1) & 0x1f; + + // Keep only the bottom 8 bits for a Register Shift + k = GET_REGISTER(k >> 1) & 0xff; + + if (k == 0) /* Register shift by 0 is a no-op */ + { +// LOG(("%08x: NO-OP Regshift\n", R15)); + /* TODO this is wrong for at least ROR by reg with lower + * 5 bits 0 but lower 8 bits non zero */ + if (pCarry) + *pCarry = GET_CPSR & C_MASK; + return rm; + } + } + /* Decode the shift type and perform the shift */ + switch (t >> 1) + { + case 0: /* LSL */ + // LSL 32 = Result of 0, Carry = Bit 0 of RM + // LSL >32 = Result of 0, Carry out 0 + if (k >= 32) + { + if (pCarry) + *pCarry = (k == 32) ? rm & 1 : 0; + return 0; + } + else + { + if (pCarry) + { + // LSL 0 = Result = RM, Carry = Old Contents of CPSR C Bit + // LSL (0,31) = Result shifted, least significant bit is in carry out + *pCarry = k ? (rm & (1 << (32 - k))) : (GET_CPSR & C_MASK); + } + return k ? LSL(rm, k) : rm; + } + + case 1: /* LSR */ + if (k == 0 || k == 32) + { + if (pCarry) + *pCarry = rm & SIGN_BIT; + return 0; + } + else if (k > 32) + { + if (pCarry) + *pCarry = 0; + return 0; + } + else + { + if (pCarry) + *pCarry = (rm & (1 << (k - 1))); + return LSR(rm, k); + } + + case 2: /* ASR */ + if (k == 0 || k > 32) + k = 32; + + if (pCarry) + *pCarry = (rm & (1 << (k - 1))); + if (k >= 32) + return rm & SIGN_BIT ? 0xffffffffu : 0; + else + { + if (rm & SIGN_BIT) + return LSR(rm, k) | (0xffffffffu << (32 - k)); + else + return LSR(rm, k); + } + + case 3: /* ROR and RRX */ + if (k) + { + while (k > 32) + k -= 32; + if (pCarry) + *pCarry = rm & (1 << (k - 1)); + return ROR(rm, k); + } + else + { + /* RRX */ + if (pCarry) + *pCarry = (rm & 1); + return LSR(rm, 1) | ((GET_CPSR & C_MASK) << 2); + } + } + + LOG(("%08x: Decodeshift error\n", R15)); + return 0; +} /* decodeShift */ + + +int arm7_cpu_device::loadInc(UINT32 pat, UINT32 rbv, UINT32 s, int mode) +{ + int i, result; + UINT32 data; + + result = 0; + rbv &= ~3; + for (i = 0; i < 16; i++) + { + if ((pat >> i) & 1) + { + if (m_pendingAbtD == 0) // "Overwriting of registers stops when the abort happens." + { + data = READ32(rbv += 4); + if (i == 15) { + if (s) /* Pull full contents from stack */ + SET_MODE_REGISTER(mode, 15, data); + else /* Pull only address, preserve mode & status flags */ + if (MODE32) + SET_MODE_REGISTER(mode, 15, data); + else + { + SET_MODE_REGISTER(mode, 15, (GET_MODE_REGISTER(mode, 15) & ~0x03FFFFFC) | (data & 0x03FFFFFC)); + } + } else + SET_MODE_REGISTER(mode, i, data); + } + result++; + } + } + return result; +} + + +int arm7_cpu_device::loadDec(UINT32 pat, UINT32 rbv, UINT32 s, int mode) +{ + int i, result; + UINT32 data; + + result = 0; + rbv &= ~3; + for (i = 15; i >= 0; i--) + { + if ((pat >> i) & 1) + { + if (m_pendingAbtD == 0) // "Overwriting of registers stops when the abort happens." + { + data = READ32(rbv -= 4); + if (i == 15) { + if (s) /* Pull full contents from stack */ + SET_MODE_REGISTER(mode, 15, data); + else /* Pull only address, preserve mode & status flags */ + if (MODE32) + SET_MODE_REGISTER(mode, 15, data); + else + { + SET_MODE_REGISTER(mode, 15, (GET_MODE_REGISTER(mode, 15) & ~0x03FFFFFC) | (data & 0x03FFFFFC)); + } + } + else + SET_MODE_REGISTER(mode, i, data); + } + result++; + } + } + return result; +} + + +int arm7_cpu_device::storeInc(UINT32 pat, UINT32 rbv, int mode) +{ + int i, result; + + result = 0; + for (i = 0; i < 16; i++) + { + if ((pat >> i) & 1) + { +#if ARM7_DEBUG_CORE + if (i == 15) /* R15 is plus 12 from address of STM */ + LOG(("%08x: StoreInc on R15\n", R15)); +#endif + WRITE32(rbv += 4, GET_MODE_REGISTER(mode, i)); + result++; + } + } + return result; +} /* storeInc */ + + +int arm7_cpu_device::storeDec(UINT32 pat, UINT32 rbv, int mode) +{ + int i, result = 0, cnt; + + // pre-count the # of registers being stored + for (i = 15; i >= 0; i--) + { + if ((pat >> i) & 1) + { + result++; + + // starting address + rbv -= 4; + } + } + + cnt = 0; + for (i = 0; i <= 15; i++) + { + if ((pat >> i) & 1) + { +#if ARM7_DEBUG_CORE + if (i == 15) /* R15 is plus 12 from address of STM */ + LOG(("%08x: StoreDec on R15\n", R15)); +#endif + WRITE32(rbv + (cnt * 4), GET_MODE_REGISTER(mode, i)); + cnt++; + } + } + return result; +} /* storeDec */ + + +/*************************************************************************** + * OPCODE HANDLING + ***************************************************************************/ + +// Co-Processor Data Operation +void arm7_cpu_device::HandleCoProcDO(UINT32 insn) +{ + // This instruction simply instructs the co-processor to do something, no data is returned to ARM7 core + arm7_do_callback(*m_program, insn, 0, 0); // simply pass entire opcode to callback - since data format is actually dependent on co-proc implementation +} + +// Co-Processor Register Transfer - To/From Arm to Co-Proc +void arm7_cpu_device::HandleCoProcRT(UINT32 insn) +{ + /* xxxx 1110 oooL nnnn dddd cccc ppp1 mmmm */ + + // Load (MRC) data from Co-Proc to ARM7 register + if (insn & 0x00100000) // Bit 20 = Load or Store + { + UINT32 res = arm7_rt_r_callback(*m_program, insn, 0); // RT Read handler must parse opcode & return appropriate result + if (m_pendingUnd == 0) + { + SET_REGISTER((insn >> 12) & 0xf, res); + } + } + // Store (MCR) data from ARM7 to Co-Proc register + else + { + arm7_rt_w_callback(*m_program, insn, GET_REGISTER((insn >> 12) & 0xf), 0); + } +} + +/* Data Transfer - To/From Arm to Co-Proc + Loading or Storing, the co-proc function is responsible to read/write from the base register supplied + offset + 8 bit immediate value Base Offset address is << 2 to get the actual # + + issues - #1 - the co-proc function, needs direct access to memory reads or writes (ie, so we must send a pointer to a func) + - #2 - the co-proc may adjust the base address (especially if it reads more than 1 word), so a pointer to the register must be used + but the old value of the register must be restored if write back is not set.. + - #3 - when post incrementing is used, it's up to the co-proc func. to add the offset, since the transfer + address supplied in that case, is simply the base. I suppose this is irrelevant if write back not set + but if co-proc reads multiple address, it must handle the offset adjustment itself. +*/ +// todo: test with valid instructions +void arm7_cpu_device::HandleCoProcDT(UINT32 insn) +{ + UINT32 rn = (insn >> 16) & 0xf; + UINT32 rnv = GET_REGISTER(rn); // Get Address Value stored from Rn + UINT32 ornv = rnv; // Keep value of Rn + UINT32 off = (insn & 0xff) << 2; // Offset is << 2 according to manual + UINT32 *prn = &ARM7REG(rn); // Pointer to our register, so it can be changed in the callback + +#if ARM7_DEBUG_CORE + if (((insn >> 16) & 0xf) == 15 && (insn & 0x200000)) + LOG(("%08x: Illegal use of R15 as base for write back value!\n", R15)); +#endif + + // Pre-Increment base address (IF POST INCREMENT - CALL BACK FUNCTION MUST DO IT) + if ((insn & 0x1000000) && off) + { + // Up - Down bit + if (insn & 0x800000) + rnv += off; + else + rnv -= off; + } + + // Load (LDC) data from ARM7 memory to Co-Proc memory + if (insn & 0x00100000) + { + arm7_dt_r_callback(insn, prn); + } + // Store (STC) data from Co-Proc to ARM7 memory + else + { + arm7_dt_w_callback(insn, prn); + } + + if (m_pendingUnd != 0) return; + + // If writeback not used - ensure the original value of RN is restored in case co-proc callback changed value + if ((insn & 0x200000) == 0) + SET_REGISTER(rn, ornv); +} + +void arm7_cpu_device::HandleBranch(UINT32 insn) +{ + UINT32 off = (insn & INSN_BRANCH) << 2; + + /* Save PC into LR if this is a branch with link */ + if (insn & INSN_BL) + { + SET_REGISTER(14, R15 + 4); + } + + /* Sign-extend the 24-bit offset in our calculations */ + if (off & 0x2000000u) + { + if (MODE32) + R15 -= ((~(off | 0xfc000000u)) + 1) - 8; + else + R15 = ((R15 - (((~(off | 0xfc000000u)) + 1) - 8)) & 0x03FFFFFC) | (R15 & ~0x03FFFFFC); + } + else + { + if (MODE32) + R15 += off + 8; + else + R15 = ((R15 + (off + 8)) & 0x03FFFFFC) | (R15 & ~0x03FFFFFC); + } +} + +void arm7_cpu_device::HandleMemSingle(UINT32 insn) +{ + UINT32 rn, rnv, off, rd, rnv_old = 0; + + /* Fetch the offset */ + if (insn & INSN_I) + { + /* Register Shift */ + off = decodeShift(insn, NULL); + } + else + { + /* Immediate Value */ + off = insn & INSN_SDT_IMM; + } + + /* Calculate Rn, accounting for PC */ + rn = (insn & INSN_RN) >> INSN_RN_SHIFT; + + if (insn & INSN_SDT_P) + { + /* Pre-indexed addressing */ + if (insn & INSN_SDT_U) + { + if ((MODE32) || (rn != eR15)) + rnv = (GET_REGISTER(rn) + off); + else + rnv = (GET_PC + off); + } + else + { + if ((MODE32) || (rn != eR15)) + rnv = (GET_REGISTER(rn) - off); + else + rnv = (GET_PC - off); + } + + if (insn & INSN_SDT_W) + { + rnv_old = GET_REGISTER(rn); + SET_REGISTER(rn, rnv); + + // check writeback??? + } + else if (rn == eR15) + { + rnv = rnv + 8; + } + } + else + { + /* Post-indexed addressing */ + if (rn == eR15) + { + if (MODE32) + rnv = R15 + 8; + else + rnv = GET_PC + 8; + } + else + { + rnv = GET_REGISTER(rn); + } + } + + /* Do the transfer */ + rd = (insn & INSN_RD) >> INSN_RD_SHIFT; + if (insn & INSN_SDT_L) + { + /* Load */ + if (insn & INSN_SDT_B) + { + UINT32 data = READ8(rnv); + if (m_pendingAbtD == 0) + { + SET_REGISTER(rd, data); + } + } + else + { + UINT32 data = READ32(rnv); + if (m_pendingAbtD == 0) + { + if (rd == eR15) + { + if (MODE32) + R15 = data - 4; + else + R15 = (R15 & ~0x03FFFFFC) /* N Z C V I F M1 M0 */ | ((data - 4) & 0x03FFFFFC); + // LDR, PC takes 2S + 2N + 1I (5 total cycles) + ARM7_ICOUNT -= 2; + } + else + { + SET_REGISTER(rd, data); + } + } + } + } + else + { + /* Store */ + if (insn & INSN_SDT_B) + { +#if ARM7_DEBUG_CORE + if (rd == eR15) + LOG(("Wrote R15 in byte mode\n")); +#endif + + WRITE8(rnv, (UINT8) GET_REGISTER(rd) & 0xffu); + } + else + { +#if ARM7_DEBUG_CORE + if (rd == eR15) + LOG(("Wrote R15 in 32bit mode\n")); +#endif + + //WRITE32(rnv, rd == eR15 ? R15 + 8 : GET_REGISTER(rd)); + WRITE32(rnv, rd == eR15 ? R15 + 8 + 4 : GET_REGISTER(rd)); // manual says STR rd = PC, +12 + } + // Store takes only 2 N Cycles, so add + 1 + ARM7_ICOUNT += 1; + } + + if (m_pendingAbtD != 0) + { + if ((insn & INSN_SDT_P) && (insn & INSN_SDT_W)) + { + SET_REGISTER(rn, rnv_old); + } + } + else + { + /* Do post-indexing writeback */ + if (!(insn & INSN_SDT_P)/* && (insn & INSN_SDT_W)*/) + { + if (insn & INSN_SDT_U) + { + /* Writeback is applied in pipeline, before value is read from mem, + so writeback is effectively ignored */ + if (rd == rn) { + SET_REGISTER(rn, GET_REGISTER(rd)); + // todo: check for offs... ? + } + else { + if ((insn & INSN_SDT_W) != 0) + LOG(("%08x: RegisterWritebackIncrement %d %d %d\n", R15, (insn & INSN_SDT_P) != 0, (insn & INSN_SDT_W) != 0, (insn & INSN_SDT_U) != 0)); + + SET_REGISTER(rn, (rnv + off)); + } + } + else + { + /* Writeback is applied in pipeline, before value is read from mem, + so writeback is effectively ignored */ + if (rd == rn) { + SET_REGISTER(rn, GET_REGISTER(rd)); + } + else { + SET_REGISTER(rn, (rnv - off)); + + if ((insn & INSN_SDT_W) != 0) + LOG(("%08x: RegisterWritebackDecrement %d %d %d\n", R15, (insn & INSN_SDT_P) != 0, (insn & INSN_SDT_W) != 0, (insn & INSN_SDT_U) != 0)); + } + } + } + + } + +// ARM7_CHECKIRQ + +} /* HandleMemSingle */ + +void arm7_cpu_device::HandleHalfWordDT(UINT32 insn) +{ + UINT32 rn, rnv, off, rd, rnv_old = 0; + + // Immediate or Register Offset? + if (insn & 0x400000) { // Bit 22 - 1 = immediate, 0 = register + // imm. value in high nibble (bits 8-11) and lo nibble (bit 0-3) + off = (((insn >> 8) & 0x0f) << 4) | (insn & 0x0f); + } + else { + // register + off = GET_REGISTER(insn & 0x0f); + } + + /* Calculate Rn, accounting for PC */ + rn = (insn & INSN_RN) >> INSN_RN_SHIFT; + + if (insn & INSN_SDT_P) + { + /* Pre-indexed addressing */ + if (insn & INSN_SDT_U) + { + rnv = (GET_REGISTER(rn) + off); + } + else + { + rnv = (GET_REGISTER(rn) - off); + } + + if (insn & INSN_SDT_W) + { + rnv_old = GET_REGISTER(rn); + SET_REGISTER(rn, rnv); + + // check writeback??? + } + else if (rn == eR15) + { + rnv = (rnv) + 8; + } + } + else + { + /* Post-indexed addressing */ + if (rn == eR15) + { + rnv = R15 + 8; + } + else + { + rnv = GET_REGISTER(rn); + } + } + + /* Do the transfer */ + rd = (insn & INSN_RD) >> INSN_RD_SHIFT; + + /* Load */ + if (insn & INSN_SDT_L) + { + // Signed? + if (insn & 0x40) + { + UINT32 newval = 0; + + // Signed Half Word? + if (insn & 0x20) { + UINT16 signbyte, databyte; + databyte = READ16(rnv) & 0xFFFF; + signbyte = (databyte & 0x8000) ? 0xffff : 0; + newval = (UINT32)(signbyte << 16)|databyte; + } + // Signed Byte + else { + UINT8 databyte; + UINT32 signbyte; + databyte = READ8(rnv) & 0xff; + signbyte = (databyte & 0x80) ? 0xffffff : 0; + newval = (UINT32)(signbyte << 8)|databyte; + } + + if (m_pendingAbtD == 0) + { + // PC? + if (rd == eR15) + { + R15 = newval + 8; + // LDR(H,SH,SB) PC takes 2S + 2N + 1I (5 total cycles) + ARM7_ICOUNT -= 2; + + } + else + { + SET_REGISTER(rd, newval); + R15 += 4; + } + + } + else + { + R15 += 4; + } + + } + // Unsigned Half Word + else + { + UINT32 newval = READ16(rnv); + + if (m_pendingAbtD == 0) + { + if (rd == eR15) + { + R15 = newval + 8; + // extra cycles for LDR(H,SH,SB) PC (5 total cycles) + ARM7_ICOUNT -= 2; + } + else + { + SET_REGISTER(rd, newval); + R15 += 4; + } + + } + else + { + R15 += 4; + } + + } + + + } + /* Store or ARMv5+ dword insns */ + else + { + if ((insn & 0x60) == 0x40) // LDRD + { + SET_REGISTER(rd, READ32(rnv)); + SET_REGISTER(rd+1, READ32(rnv+4)); + R15 += 4; + } + else if ((insn & 0x60) == 0x60) // STRD + { + WRITE32(rnv, GET_REGISTER(rd)); + WRITE32(rnv+4, GET_REGISTER(rd+1)); + R15 += 4; + } + else + { + // WRITE16(rnv, rd == eR15 ? R15 + 8 : GET_REGISTER(rd)); + WRITE16(rnv, rd == eR15 ? R15 + 8 + 4 : GET_REGISTER(rd)); // manual says STR RD=PC, +12 of address + +// if R15 is not increased then e.g. "STRH R10, [R15,#$10]" will be executed over and over again +#if 0 + if (rn != eR15) +#endif + R15 += 4; + + // STRH takes 2 cycles, so we add + 1 + ARM7_ICOUNT += 1; + } + } + + if (m_pendingAbtD != 0) + { + if ((insn & INSN_SDT_P) && (insn & INSN_SDT_W)) + { + SET_REGISTER(rn, rnv_old); + } + } + else + { + // SJE: No idea if this writeback code works or makes sense here.. + + /* Do post-indexing writeback */ + if (!(insn & INSN_SDT_P)/* && (insn & INSN_SDT_W)*/) + { + if (insn & INSN_SDT_U) + { + /* Writeback is applied in pipeline, before value is read from mem, + so writeback is effectively ignored */ + if (rd == rn) { + SET_REGISTER(rn, GET_REGISTER(rd)); + // todo: check for offs... ? + } + else { + if ((insn & INSN_SDT_W) != 0) + LOG(("%08x: RegisterWritebackIncrement %d %d %d\n", R15, (insn & INSN_SDT_P) != 0, (insn & INSN_SDT_W) != 0, (insn & INSN_SDT_U) != 0)); + + SET_REGISTER(rn, (rnv + off)); + } + } + else + { + /* Writeback is applied in pipeline, before value is read from mem, + so writeback is effectively ignored */ + if (rd == rn) { + SET_REGISTER(rn, GET_REGISTER(rd)); + } + else { + SET_REGISTER(rn, (rnv - off)); + + if ((insn & INSN_SDT_W) != 0) + LOG(("%08x: RegisterWritebackDecrement %d %d %d\n", R15, (insn & INSN_SDT_P) != 0, (insn & INSN_SDT_W) != 0, (insn & INSN_SDT_U) != 0)); + } + } + } + + } + +} + +void arm7_cpu_device::HandleSwap(UINT32 insn) +{ + UINT32 rn, rm, rd, tmp; + + rn = GET_REGISTER((insn >> 16) & 0xf); // reg. w/read address + rm = GET_REGISTER(insn & 0xf); // reg. w/write address + rd = (insn >> 12) & 0xf; // dest reg + +#if ARM7_DEBUG_CORE + if (rn == 15 || rm == 15 || rd == 15) + LOG(("%08x: Illegal use of R15 in Swap Instruction\n", R15)); +#endif + + // can be byte or word + if (insn & 0x400000) + { + tmp = READ8(rn); + WRITE8(rn, rm); + SET_REGISTER(rd, tmp); + } + else + { + tmp = READ32(rn); + WRITE32(rn, rm); + SET_REGISTER(rd, tmp); + } + + R15 += 4; + // Instruction takes 1S+2N+1I cycles - so we subtract one more.. + ARM7_ICOUNT -= 1; +} + +void arm7_cpu_device::HandlePSRTransfer(UINT32 insn) +{ + int reg = (insn & 0x400000) ? SPSR : eCPSR; // Either CPSR or SPSR + UINT32 newval, val = 0; + int oldmode = GET_CPSR & MODE_FLAG; + + // get old value of CPSR/SPSR + newval = GET_REGISTER(reg); + + // MSR (bit 21 set) - Copy value to CPSR/SPSR + if ((insn & 0x00200000)) + { + // Immediate Value? + if (insn & INSN_I) { + // Value can be specified for a Right Rotate, 2x the value specified. + int by = (insn & INSN_OP2_ROTATE) >> INSN_OP2_ROTATE_SHIFT; + if (by) + val = ROR(insn & INSN_OP2_IMM, by << 1); + else + val = insn & INSN_OP2_IMM; + } + // Value from Register + else + { + val = GET_REGISTER(insn & 0x0f); + } + + // apply field code bits + if (reg == eCPSR) + { + if (oldmode != eARM7_MODE_USER) + { + if (insn & 0x00010000) + { + newval = (newval & 0xffffff00) | (val & 0x000000ff); + } + if (insn & 0x00020000) + { + newval = (newval & 0xffff00ff) | (val & 0x0000ff00); + } + if (insn & 0x00040000) + { + newval = (newval & 0xff00ffff) | (val & 0x00ff0000); + } + } + + // status flags can be modified regardless of mode + if (insn & 0x00080000) + { + // TODO for non ARMv5E mask should be 0xf0000000 (ie mask Q bit) + newval = (newval & 0x00ffffff) | (val & 0xf8000000); + } + } + else // SPSR has stricter requirements + { + if (((GET_CPSR & 0x1f) > 0x10) && ((GET_CPSR & 0x1f) < 0x1f)) + { + if (insn & 0x00010000) + { + newval = (newval & 0xffffff00) | (val & 0xff); + } + if (insn & 0x00020000) + { + newval = (newval & 0xffff00ff) | (val & 0xff00); + } + if (insn & 0x00040000) + { + newval = (newval & 0xff00ffff) | (val & 0xff0000); + } + if (insn & 0x00080000) + { + // TODO for non ARMv5E mask should be 0xf0000000 (ie mask Q bit) + newval = (newval & 0x00ffffff) | (val & 0xf8000000); + } + } + } + +#if 0 + // force valid mode + newval |= 0x10; +#endif + + // Update the Register + if (reg == eCPSR) + SET_CPSR(newval); + else + SET_REGISTER(reg, newval); + + // Switch to new mode if changed + if ((newval & MODE_FLAG) != oldmode) + SwitchMode(GET_MODE); + + } + // MRS (bit 21 clear) - Copy CPSR or SPSR to specified Register + else + { + SET_REGISTER((insn >> 12)& 0x0f, GET_REGISTER(reg)); + } +} + +void arm7_cpu_device::HandleALU(UINT32 insn) +{ + UINT32 op2, sc = 0, rd, rn, opcode; + UINT32 by, rdn; + + // Normal Data Processing : 1S + // Data Processing with register specified shift : 1S + 1I + // Data Processing with PC written : 2S + 1N + // Data Processing with register specified shift and PC written : 2S + 1N + 1I + + opcode = (insn & INSN_OPCODE) >> INSN_OPCODE_SHIFT; + + rd = 0; + rn = 0; + + /* --------------*/ + /* Construct Op2 */ + /* --------------*/ + + /* Immediate constant */ + if (insn & INSN_I) + { + by = (insn & INSN_OP2_ROTATE) >> INSN_OP2_ROTATE_SHIFT; + if (by) + { + op2 = ROR(insn & INSN_OP2_IMM, by << 1); + sc = op2 & SIGN_BIT; + } + else + { + op2 = insn & INSN_OP2; // SJE: Shouldn't this be INSN_OP2_IMM? + sc = GET_CPSR & C_MASK; + } + } + /* Op2 = Register Value */ + else + { + op2 = decodeShift(insn, (insn & INSN_S) ? &sc : NULL); + + // LD TODO sc will always be 0 if this applies + if (!(insn & INSN_S)) + sc = 0; + + // extra cycle (register specified shift) + ARM7_ICOUNT -= 1; + } + + // LD TODO this comment is wrong + /* Calculate Rn to account for pipelining */ + if ((opcode & 0xd) != 0xd) /* No Rn in MOV */ + { + if ((rn = (insn & INSN_RN) >> INSN_RN_SHIFT) == eR15) + { +#if ARM7_DEBUG_CORE + LOG(("%08x: Pipelined R15 (Shift %d)\n", R15, (insn & INSN_I ? 8 : insn & 0x10u ? 12 : 12))); +#endif + if (MODE32) + rn = R15 + 8; + else + rn = GET_PC + 8; + } + else + { + rn = GET_REGISTER(rn); + } + } + + /* Perform the operation */ + + switch (opcode) + { + /* Arithmetic operations */ + case OPCODE_SBC: + rd = (rn - op2 - (GET_CPSR & C_MASK ? 0 : 1)); + HandleALUSubFlags(rd, rn, op2); + break; + case OPCODE_CMP: + case OPCODE_SUB: + rd = (rn - op2); + HandleALUSubFlags(rd, rn, op2); + break; + case OPCODE_RSC: + rd = (op2 - rn - (GET_CPSR & C_MASK ? 0 : 1)); + HandleALUSubFlags(rd, op2, rn); + break; + case OPCODE_RSB: + rd = (op2 - rn); + HandleALUSubFlags(rd, op2, rn); + break; + case OPCODE_ADC: + rd = (rn + op2 + ((GET_CPSR & C_MASK) >> C_BIT)); + HandleALUAddFlags(rd, rn, op2); + break; + case OPCODE_CMN: + case OPCODE_ADD: + rd = (rn + op2); + HandleALUAddFlags(rd, rn, op2); + break; + + /* Logical operations */ + case OPCODE_AND: + case OPCODE_TST: + rd = rn & op2; + HandleALULogicalFlags(rd, sc); + break; + case OPCODE_BIC: + rd = rn & ~op2; + HandleALULogicalFlags(rd, sc); + break; + case OPCODE_TEQ: + case OPCODE_EOR: + rd = rn ^ op2; + HandleALULogicalFlags(rd, sc); + break; + case OPCODE_ORR: + rd = rn | op2; + HandleALULogicalFlags(rd, sc); + break; + case OPCODE_MOV: + rd = op2; + HandleALULogicalFlags(rd, sc); + break; + case OPCODE_MVN: + rd = (~op2); + HandleALULogicalFlags(rd, sc); + break; + } + + /* Put the result in its register if not one of the test only opcodes (TST,TEQ,CMP,CMN) */ + rdn = (insn & INSN_RD) >> INSN_RD_SHIFT; + if ((opcode & 0xc) != 0x8) + { + // If Rd = R15, but S Flag not set, Result is placed in R15, but CPSR is not affected (page 44) + if (rdn == eR15 && !(insn & INSN_S)) + { + if (MODE32) + { + R15 = rd; + } + else + { + R15 = (R15 & ~0x03FFFFFC) | (rd & 0x03FFFFFC); + } + // extra cycles (PC written) + ARM7_ICOUNT -= 2; + } + else + { + // Rd = 15 and S Flag IS set, Result is placed in R15, and current mode SPSR moved to CPSR + if (rdn == eR15) { + if (MODE32) + { + // When Rd is R15 and the S flag is set the result of the operation is placed in R15 and the SPSR corresponding to + // the current mode is moved to the CPSR. This allows state changes which automatically restore both PC and + // CPSR. --> This form of instruction should not be used in User mode. <-- + + if (GET_MODE != eARM7_MODE_USER) + { + // Update CPSR from SPSR + SET_CPSR(GET_REGISTER(SPSR)); + SwitchMode(GET_MODE); + } + + R15 = rd; + + } + else + { + UINT32 temp; + R15 = rd; //(R15 & 0x03FFFFFC) | (rd & 0xFC000003); + temp = (GET_CPSR & 0x0FFFFF20) | (rd & 0xF0000000) /* N Z C V */ | ((rd & 0x0C000000) >> (26 - 6)) /* I F */ | (rd & 0x00000003) /* M1 M0 */; + SET_CPSR( temp); + SwitchMode( temp & 3); + } + + // extra cycles (PC written) + ARM7_ICOUNT -= 2; + + /* IRQ masks may have changed in this instruction */ +// ARM7_CHECKIRQ; + } + else + /* S Flag is set - Write results to register & update CPSR (which was already handled using HandleALU flag macros) */ + SET_REGISTER(rdn, rd); + } + } + // SJE: Don't think this applies any more.. (see page 44 at bottom) + /* TST & TEQ can affect R15 (the condition code register) with the S bit set */ + else if (rdn == eR15) + { + if (insn & INSN_S) { +#if ARM7_DEBUG_CORE + LOG(("%08x: TST class on R15 s bit set\n", R15)); +#endif + if (MODE32) + R15 = rd; + else + { + UINT32 temp; + R15 = (R15 & 0x03FFFFFC) | (rd & ~0x03FFFFFC); + temp = (GET_CPSR & 0x0FFFFF20) | (rd & 0xF0000000) /* N Z C V */ | ((rd & 0x0C000000) >> (26 - 6)) /* I F */ | (rd & 0x00000003) /* M1 M0 */; + SET_CPSR( temp); + SwitchMode( temp & 3); + } + + /* IRQ masks may have changed in this instruction */ +// ARM7_CHECKIRQ; + } + else + { +#if ARM7_DEBUG_CORE + LOG(("%08x: TST class on R15 no s bit set\n", R15)); +#endif + } + // extra cycles (PC written) + ARM7_ICOUNT -= 2; + } + + // compensate for the -3 at the end + ARM7_ICOUNT += 2; +} + +void arm7_cpu_device::HandleMul(UINT32 insn) +{ + UINT32 r, rm, rs; + + // MUL takes 1S + mI and MLA 1S + (m+1)I cycles to execute, where S and I are as + // defined in 6.2 Cycle Types on page 6-2. + // m is the number of 8 bit multiplier array cycles required to complete the + // multiply, which is controlled by the value of the multiplier operand + // specified by Rs. + + rm = GET_REGISTER(insn & INSN_MUL_RM); + rs = GET_REGISTER((insn & INSN_MUL_RS) >> INSN_MUL_RS_SHIFT); + + /* Do the basic multiply of Rm and Rs */ + r = rm * rs; + +#if ARM7_DEBUG_CORE + if ((insn & INSN_MUL_RM) == 0xf || + ((insn & INSN_MUL_RS) >> INSN_MUL_RS_SHIFT) == 0xf || + ((insn & INSN_MUL_RN) >> INSN_MUL_RN_SHIFT) == 0xf) + LOG(("%08x: R15 used in mult\n", R15)); +#endif + + /* Add on Rn if this is a MLA */ + if (insn & INSN_MUL_A) + { + r += GET_REGISTER((insn & INSN_MUL_RN) >> INSN_MUL_RN_SHIFT); + // extra cycle for MLA + ARM7_ICOUNT -= 1; + } + + /* Write the result */ + SET_REGISTER((insn & INSN_MUL_RD) >> INSN_MUL_RD_SHIFT, r); + + /* Set N and Z if asked */ + if (insn & INSN_S) + { + SET_CPSR((GET_CPSR & ~(N_MASK | Z_MASK)) | HandleALUNZFlags(r)); + } + + if (rs & SIGN_BIT) rs = -rs; + if (rs < 0x00000100) ARM7_ICOUNT -= 1 + 1; + else if (rs < 0x00010000) ARM7_ICOUNT -= 1 + 2; + else if (rs < 0x01000000) ARM7_ICOUNT -= 1 + 3; + else ARM7_ICOUNT -= 1 + 4; + + ARM7_ICOUNT += 3; +} + +// todo: add proper cycle counts +void arm7_cpu_device::HandleSMulLong(UINT32 insn) +{ + INT32 rm, rs; + UINT32 rhi, rlo; + INT64 res = 0; + + // MULL takes 1S + (m+1)I and MLAL 1S + (m+2)I cycles to execute, where m is the + // number of 8 bit multiplier array cycles required to complete the multiply, which is + // controlled by the value of the multiplier operand specified by Rs. + + rm = (INT32)GET_REGISTER(insn & 0xf); + rs = (INT32)GET_REGISTER(((insn >> 8) & 0xf)); + rhi = (insn >> 16) & 0xf; + rlo = (insn >> 12) & 0xf; + +#if ARM7_DEBUG_CORE + if ((insn & 0xf) == 15 || ((insn >> 8) & 0xf) == 15 || ((insn >> 16) & 0xf) == 15 || ((insn >> 12) & 0xf) == 15) + LOG(("%08x: Illegal use of PC as a register in SMULL opcode\n", R15)); +#endif + + /* Perform the multiplication */ + res = (INT64)rm * rs; + + /* Add on Rn if this is a MLA */ + if (insn & INSN_MUL_A) + { + INT64 acum = (INT64)((((INT64)(GET_REGISTER(rhi))) << 32) | GET_REGISTER(rlo)); + res += acum; + // extra cycle for MLA + ARM7_ICOUNT -= 1; + } + + /* Write the result (upper dword goes to RHi, lower to RLo) */ + SET_REGISTER(rhi, res >> 32); + SET_REGISTER(rlo, res & 0xFFFFFFFF); + + /* Set N and Z if asked */ + if (insn & INSN_S) + { + SET_CPSR((GET_CPSR & ~(N_MASK | Z_MASK)) | HandleLongALUNZFlags(res)); + } + + if (rs < 0) rs = -rs; + if (rs < 0x00000100) ARM7_ICOUNT -= 1 + 1 + 1; + else if (rs < 0x00010000) ARM7_ICOUNT -= 1 + 2 + 1; + else if (rs < 0x01000000) ARM7_ICOUNT -= 1 + 3 + 1; + else ARM7_ICOUNT -= 1 + 4 + 1; + + ARM7_ICOUNT += 3; +} + +// todo: add proper cycle counts +void arm7_cpu_device::HandleUMulLong(UINT32 insn) +{ + UINT32 rm, rs; + UINT32 rhi, rlo; + UINT64 res = 0; + + // MULL takes 1S + (m+1)I and MLAL 1S + (m+2)I cycles to execute, where m is the + // number of 8 bit multiplier array cycles required to complete the multiply, which is + // controlled by the value of the multiplier operand specified by Rs. + + rm = (INT32)GET_REGISTER(insn & 0xf); + rs = (INT32)GET_REGISTER(((insn >> 8) & 0xf)); + rhi = (insn >> 16) & 0xf; + rlo = (insn >> 12) & 0xf; + +#if ARM7_DEBUG_CORE + if (((insn & 0xf) == 15) || (((insn >> 8) & 0xf) == 15) || (((insn >> 16) & 0xf) == 15) || (((insn >> 12) & 0xf) == 15)) + LOG(("%08x: Illegal use of PC as a register in SMULL opcode\n", R15)); +#endif + + /* Perform the multiplication */ + res = (UINT64)rm * rs; + + /* Add on Rn if this is a MLA */ + if (insn & INSN_MUL_A) + { + UINT64 acum = (UINT64)((((UINT64)(GET_REGISTER(rhi))) << 32) | GET_REGISTER(rlo)); + res += acum; + // extra cycle for MLA + ARM7_ICOUNT -= 1; + } + + /* Write the result (upper dword goes to RHi, lower to RLo) */ + SET_REGISTER(rhi, res >> 32); + SET_REGISTER(rlo, res & 0xFFFFFFFF); + + /* Set N and Z if asked */ + if (insn & INSN_S) + { + SET_CPSR((GET_CPSR & ~(N_MASK | Z_MASK)) | HandleLongALUNZFlags(res)); + } + + if (rs < 0x00000100) ARM7_ICOUNT -= 1 + 1 + 1; + else if (rs < 0x00010000) ARM7_ICOUNT -= 1 + 2 + 1; + else if (rs < 0x01000000) ARM7_ICOUNT -= 1 + 3 + 1; + else ARM7_ICOUNT -= 1 + 4 + 1; + + ARM7_ICOUNT += 3; +} + +void arm7_cpu_device::HandleMemBlock(UINT32 insn) +{ + UINT32 rb = (insn & INSN_RN) >> INSN_RN_SHIFT; + UINT32 rbp = GET_REGISTER(rb); + int result; + +#if ARM7_DEBUG_CORE + if (rbp & 3) + LOG(("%08x: Unaligned Mem Transfer @ %08x\n", R15, rbp)); +#endif + + // Normal LDM instructions take nS + 1N + 1I and LDM PC takes (n+1)S + 2N + 1I + // incremental cycles, where S,N and I are as defined in 6.2 Cycle Types on page 6-2. + // STM instructions take (n-1)S + 2N incremental cycles to execute, where n is the + // number of words transferred. + + if (insn & INSN_BDT_L) + { + /* Loading */ + if (insn & INSN_BDT_U) + { + /* Incrementing */ + if (!(insn & INSN_BDT_P)) + { + rbp = rbp + (- 4); + } + + // S Flag Set, but R15 not in list = User Bank Transfer + if (insn & INSN_BDT_S && (insn & 0x8000) == 0) + { + // !! actually switching to user mode triggers a section permission fault in Happy Fish 302-in-1 (BP C0030DF4, press F5 ~16 times) !! + // set to user mode - then do the transfer, and set back + //int curmode = GET_MODE; + //SwitchMode(eARM7_MODE_USER); + LOG(("%08x: User Bank Transfer not fully tested - please check if working properly!\n", R15)); + result = loadInc(insn & 0xffff, rbp, insn & INSN_BDT_S, eARM7_MODE_USER); + // todo - not sure if Writeback occurs on User registers also.. + //SwitchMode(curmode); + } + else + result = loadInc(insn & 0xffff, rbp, insn & INSN_BDT_S, GET_MODE); + + if ((insn & INSN_BDT_W) && (m_pendingAbtD == 0)) + { +#if ARM7_DEBUG_CORE + if (rb == 15) + LOG(("%08x: Illegal LDRM writeback to r15\n", R15)); +#endif + // "A LDM will always overwrite the updated base if the base is in the list." (also for a user bank transfer?) + // GBA "V-Rally 3" expects R0 not to be overwritten with the updated base value [BP 8077B0C] + if (((insn >> rb) & 1) == 0) + { + SET_REGISTER(rb, GET_REGISTER(rb) + result * 4); + } + } + + // R15 included? (NOTE: CPSR restore must occur LAST otherwise wrong registers restored!) + if ((insn & 0x8000) && (m_pendingAbtD == 0)) { + R15 -= 4; // SJE: I forget why i did this? + // S - Flag Set? Signals transfer of current mode SPSR->CPSR + if (insn & INSN_BDT_S) + { + if (MODE32) + { + SET_CPSR(GET_REGISTER(SPSR)); + SwitchMode(GET_MODE); + } + else + { + UINT32 temp; +// LOG(("LDM + S | R15 %08X CPSR %08X\n", R15, GET_CPSR)); + temp = (GET_CPSR & 0x0FFFFF20) | (R15 & 0xF0000000) /* N Z C V */ | ((R15 & 0x0C000000) >> (26 - 6)) /* I F */ | (R15 & 0x00000003) /* M1 M0 */; + SET_CPSR( temp); + SwitchMode(temp & 3); + } + } + // LDM PC - takes 2 extra cycles + ARM7_ICOUNT -= 2; + } + } + else + { + /* Decrementing */ + if (!(insn & INSN_BDT_P)) + { + rbp = rbp - (- 4); + } + + // S Flag Set, but R15 not in list = User Bank Transfer + if (insn & INSN_BDT_S && ((insn & 0x8000) == 0)) + { + // set to user mode - then do the transfer, and set back + //int curmode = GET_MODE; + //SwitchMode(eARM7_MODE_USER); + LOG(("%08x: User Bank Transfer not fully tested - please check if working properly!\n", R15)); + result = loadDec(insn & 0xffff, rbp, insn & INSN_BDT_S, eARM7_MODE_USER); + // todo - not sure if Writeback occurs on User registers also.. + //SwitchMode(curmode); + } + else + result = loadDec(insn & 0xffff, rbp, insn & INSN_BDT_S, GET_MODE); + + if ((insn & INSN_BDT_W) && (m_pendingAbtD == 0)) + { + if (rb == 0xf) + LOG(("%08x: Illegal LDRM writeback to r15\n", R15)); + // "A LDM will always overwrite the updated base if the base is in the list." (also for a user bank transfer?) + if (((insn >> rb) & 1) == 0) + { + SET_REGISTER(rb, GET_REGISTER(rb) - result * 4); + } + } + + // R15 included? (NOTE: CPSR restore must occur LAST otherwise wrong registers restored!) + if ((insn & 0x8000) && (m_pendingAbtD == 0)) { + R15 -= 4; // SJE: I forget why i did this? + // S - Flag Set? Signals transfer of current mode SPSR->CPSR + if (insn & INSN_BDT_S) + { + if (MODE32) + { + SET_CPSR(GET_REGISTER(SPSR)); + SwitchMode(GET_MODE); + } + else + { + UINT32 temp; +// LOG(("LDM + S | R15 %08X CPSR %08X\n", R15, GET_CPSR)); + temp = (GET_CPSR & 0x0FFFFF20) /* N Z C V I F M4 M3 M2 M1 M0 */ | (R15 & 0xF0000000) /* N Z C V */ | ((R15 & 0x0C000000) >> (26 - 6)) /* I F */ | (R15 & 0x00000003) /* M1 M0 */; + SET_CPSR( temp); + SwitchMode(temp & 3); + } + } + // LDM PC - takes 2 extra cycles + ARM7_ICOUNT -= 2; + } + } + // LDM (NO PC) takes (n)S + 1N + 1I cycles (n = # of register transfers) + ARM7_ICOUNT -= result + 1 + 1; + } /* Loading */ + else + { + /* Storing - STM */ + if (insn & (1 << eR15)) + { +#if ARM7_DEBUG_CORE + LOG(("%08x: Writing R15 in strm\n", R15)); +#endif + /* special case handling if writing to PC */ + R15 += 12; + } + if (insn & INSN_BDT_U) + { + /* Incrementing */ + if (!(insn & INSN_BDT_P)) + { + rbp = rbp + (- 4); + } + + // S Flag Set = User Bank Transfer + if (insn & INSN_BDT_S) + { + // todo: needs to be tested.. + + // set to user mode - then do the transfer, and set back + //int curmode = GET_MODE; + //SwitchMode(eARM7_MODE_USER); + LOG(("%08x: User Bank Transfer not fully tested - please check if working properly!\n", R15)); + result = storeInc(insn & 0xffff, rbp, eARM7_MODE_USER); + // todo - not sure if Writeback occurs on User registers also.. + //SwitchMode(curmode); + } + else + result = storeInc(insn & 0xffff, rbp, GET_MODE); + + if ((insn & INSN_BDT_W) && (m_pendingAbtD == 0)) + { + SET_REGISTER(rb, GET_REGISTER(rb) + result * 4); + } + } + else + { + /* Decrementing - but real CPU writes in incrementing order */ + if (!(insn & INSN_BDT_P)) + { + rbp = rbp - (-4); + } + + // S Flag Set = User Bank Transfer + if (insn & INSN_BDT_S) + { + // set to user mode - then do the transfer, and set back + //int curmode = GET_MODE; + //SwitchMode(eARM7_MODE_USER); + LOG(("%08x: User Bank Transfer not fully tested - please check if working properly!\n", R15)); + result = storeDec(insn & 0xffff, rbp, eARM7_MODE_USER); + // todo - not sure if Writeback occurs on User registers also.. + //SwitchMode(curmode); + } + else + result = storeDec(insn & 0xffff, rbp, GET_MODE); + + if ((insn & INSN_BDT_W) && (m_pendingAbtD == 0)) + { + SET_REGISTER(rb, GET_REGISTER(rb) - result * 4); + } + } + if (insn & (1 << eR15)) + R15 -= 12; + + // STM takes (n-1)S + 2N cycles (n = # of register transfers) + ARM7_ICOUNT -= (result - 1) + 2; + } + + // We will specify the cycle count for each case, so remove the -3 that occurs at the end + ARM7_ICOUNT += 3; + +} /* HandleMemBlock */ + + +const arm7_cpu_device::arm7ops_ophandler arm7_cpu_device::ops_handler[0x10] = +{ + &arm7_cpu_device::arm7ops_0123, &arm7_cpu_device::arm7ops_0123, &arm7_cpu_device::arm7ops_0123, &arm7_cpu_device::arm7ops_0123, + &arm7_cpu_device::arm7ops_4567, &arm7_cpu_device::arm7ops_4567, &arm7_cpu_device::arm7ops_4567, &arm7_cpu_device::arm7ops_4567, + &arm7_cpu_device::arm7ops_89, &arm7_cpu_device::arm7ops_89, &arm7_cpu_device::arm7ops_ab, &arm7_cpu_device::arm7ops_ab, + &arm7_cpu_device::arm7ops_cd, &arm7_cpu_device::arm7ops_cd, &arm7_cpu_device::arm7ops_e, &arm7_cpu_device::arm7ops_f, +}; + +void arm7_cpu_device::arm7ops_0123(UINT32 insn) +{ +//case 0: +//case 1: +//case 2: +//case 3: + /* Branch and Exchange (BX) */ + if ((insn & 0x0ffffff0) == 0x012fff10) // bits 27-4 == 000100101111111111110001 + { + R15 = GET_REGISTER(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(rd, count_leading_zeros(GET_REGISTER(rm))); + + R15 += 4; + } + else if ((insn & 0x0ff000f0) == 0x01000050) // QADD - v5 + { + INT32 src1 = GET_REGISTER(insn&0xf); + INT32 src2 = GET_REGISTER((insn>>16)&0xf); + INT64 res; + + res = saturate_qbit_overflow((INT64)src1 + (INT64)src2); + + SET_REGISTER((insn>>12)&0xf, (INT32)res); + R15 += 4; + } + else if ((insn & 0x0ff000f0) == 0x01400050) // QDADD - v5 + { + INT32 src1 = GET_REGISTER(insn&0xf); + INT32 src2 = GET_REGISTER((insn>>16)&0xf); + INT64 res; + + // check if doubling operation will overflow + res = (INT64)src2 * 2; + saturate_qbit_overflow(res); + + src2 *= 2; + res = saturate_qbit_overflow((INT64)src1 + (INT64)src2); + + SET_REGISTER((insn>>12)&0xf, (INT32)res); + R15 += 4; + } + else if ((insn & 0x0ff000f0) == 0x01200050) // QSUB - v5 + { + INT32 src1 = GET_REGISTER(insn&0xf); + INT32 src2 = GET_REGISTER((insn>>16)&0xf); + INT64 res; + + res = saturate_qbit_overflow((INT64)src1 - (INT64)src2); + + SET_REGISTER((insn>>12)&0xf, (INT32)res); + R15 += 4; + } + else if ((insn & 0x0ff000f0) == 0x01600050) // QDSUB - v5 + { + INT32 src1 = GET_REGISTER(insn&0xf); + INT32 src2 = GET_REGISTER((insn>>16)&0xf); + INT64 res; + + // check if doubling operation will overflow + res = (INT64)src2 * 2; + saturate_qbit_overflow(res); + + src2 *= 2; + res = saturate_qbit_overflow((INT64)src1 - (INT64)src2); + + SET_REGISTER((insn>>12)&0xf, (INT32)res); + R15 += 4; + } + else if ((insn & 0x0ff00090) == 0x01000080) // SMLAxy - v5 + { + INT32 src1 = GET_REGISTER(insn&0xf); + INT32 src2 = GET_REGISTER((insn>>8)&0xf); + INT32 res1; + + // select top and bottom halves of src1/src2 and sign extend if necessary + if (insn & 0x20) + { + src1 >>= 16; + } + + src1 &= 0xffff; + if (src1 & 0x8000) + { + src1 |= 0xffff0000; + } + + if (insn & 0x40) + { + src2 >>= 16; + } + + src2 &= 0xffff; + if (src2 & 0x8000) + { + src2 |= 0xffff0000; + } + + // 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((INT64)res1 + (INT64)GET_REGISTER((insn>>12)&0xf)); + + SET_REGISTER((insn>>16)&0xf, res1 + GET_REGISTER((insn>>12)&0xf)); + R15 += 4; + } + else if ((insn & 0x0ff00090) == 0x01400080) // SMLALxy - v5 + { + INT32 src1 = GET_REGISTER(insn&0xf); + INT32 src2 = GET_REGISTER((insn>>8)&0xf); + INT64 dst; + + dst = (INT64)GET_REGISTER((insn>>12)&0xf); + dst |= (INT64)GET_REGISTER((insn>>16)&0xf)<<32; + + // do the multiply and accumulate + dst += (INT64)src1 * (INT64)src2; + + // write back the result + SET_REGISTER((insn>>12)&0xf, (UINT32)dst); + SET_REGISTER((insn>>16)&0xf, (UINT32)(dst >> 32)); + R15 += 4; + } + else if ((insn & 0x0ff00090) == 0x01600080) // SMULxy - v5 + { + INT32 src1 = GET_REGISTER(insn&0xf); + INT32 src2 = GET_REGISTER((insn>>8)&0xf); + INT32 res; + + // select top and bottom halves of src1/src2 and sign extend if necessary + if (insn & 0x20) + { + src1 >>= 16; + } + + src1 &= 0xffff; + if (src1 & 0x8000) + { + src1 |= 0xffff0000; + } + + if (insn & 0x40) + { + src2 >>= 16; + } + + src2 &= 0xffff; + if (src2 & 0x8000) + { + src2 |= 0xffff0000; + } + + res = src1 * src2; + SET_REGISTER((insn>>16)&0xf, res); + R15 += 4; + } + else if ((insn & 0x0ff000b0) == 0x012000a0) // SMULWy - v5 + { + INT32 src1 = GET_REGISTER(insn&0xf); + INT32 src2 = GET_REGISTER((insn>>8)&0xf); + INT64 res; + + if (insn & 0x40) + { + src2 >>= 16; + } + + src2 &= 0xffff; + if (src2 & 0x8000) + { + src2 |= 0xffff0000; + } + + res = (INT64)src1 * (INT64)src2; + res >>= 16; + SET_REGISTER((insn>>16)&0xf, (UINT32)res); + R15 += 4; + } + else if ((insn & 0x0ff000b0) == 0x01200080) // SMLAWy - v5 + { + INT32 src1 = GET_REGISTER(insn&0xf); + INT32 src2 = GET_REGISTER((insn>>8)&0xf); + INT32 src3 = GET_REGISTER((insn>>12)&0xf); + INT64 res; + + if (insn & 0x40) + { + src2 >>= 16; + } + + src2 &= 0xffff; + if (src2 & 0x8000) + { + src2 |= 0xffff0000; + } + + res = (INT64)src1 * (INT64)src2; + res >>= 16; + + // check for overflow and set the Q bit + saturate_qbit_overflow((INT64)src3 + res); + + // do the real accumulate + src3 += (INT32)res; + + // write the result back + SET_REGISTER((insn>>16)&0xf, (UINT32)res); + R15 += 4; + } + 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(insn); + } + else + /* Swap */ + if (insn & 0x01000000) // bit 24 = 1 + { + HandleSwap(insn); + } + /* Multiply Or Multiply Long */ + else + { + /* multiply long */ + if (insn & 0x800000) // Bit 23 = 1 for Multiply Long + { + /* Signed? */ + if (insn & 0x00400000) + HandleSMulLong(insn); + else + HandleUMulLong(insn); + } + /* multiply */ + else + { + HandleMul(insn); + } + R15 += 4; + } + } + /* Data Processing OR PSR Transfer */ + else 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(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(insn); + } + } +// break; +} + +void arm7_cpu_device::arm7ops_4567(UINT32 insn) /* Data Transfer - Single Data Access */ +{ +//case 4: +//case 5: +//case 6: +//case 7: + HandleMemSingle(insn); + R15 += 4; +// break; +} + +void arm7_cpu_device::arm7ops_89(UINT32 insn) /* Block Data Transfer/Access */ +{ +//case 8: +//case 9: + HandleMemBlock(insn); + R15 += 4; +// break; +} + +void arm7_cpu_device::arm7ops_ab(UINT32 insn) /* Branch or Branch & Link */ +{ +//case 0xa: +//case 0xb: + HandleBranch(insn); +// break; +} + +void arm7_cpu_device::arm7ops_cd(UINT32 insn) /* Co-Processor Data Transfer */ +{ +//case 0xc: +//case 0xd: + HandleCoProcDT(insn); + R15 += 4; +// break; +} + +void arm7_cpu_device::arm7ops_e(UINT32 insn) /* Co-Processor Data Operation or Register Transfer */ +{ +//case 0xe: + if (insn & 0x10) + HandleCoProcRT(insn); + else + HandleCoProcDO(insn); + R15 += 4; +// break; +} + +void arm7_cpu_device::arm7ops_f(UINT32 insn) /* Software Interrupt */ +{ + m_pendingSwi = 1; + ARM7_CHECKIRQ; + //couldn't find any cycle counts for SWI +// break; +} |