diff --git a/src/emu/cpu/arm7/arm7.c b/src/emu/cpu/arm7/arm7.c index d8a2566428b..29be89c5741 100644 --- a/src/emu/cpu/arm7/arm7.c +++ b/src/emu/cpu/arm7/arm7.c @@ -73,6 +73,22 @@ INLINE arm_state *get_safe_token(const device_config *device) return (arm_state *)device->token; } +INLINE INT64 saturate_qbit_overflow(arm_state *cpustate, 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; +} + /* include the arm7 core */ #include "arm7core.c" @@ -341,11 +357,12 @@ CPU_GET_INFO( arm7 ) case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Steve Ellenoff, sellenoff@hotmail.com"); break; case CPUINFO_STR_FLAGS: - sprintf(info->s, "%c%c%c%c%c%c%c %s", + sprintf(info->s, "%c%c%c%c%c%c%c%c %s", (ARM7REG(eCPSR) & N_MASK) ? 'N' : '-', (ARM7REG(eCPSR) & Z_MASK) ? 'Z' : '-', (ARM7REG(eCPSR) & C_MASK) ? 'C' : '-', (ARM7REG(eCPSR) & V_MASK) ? 'V' : '-', + (ARM7REG(eCPSR) & Q_MASK) ? 'Q' : '-', (ARM7REG(eCPSR) & I_MASK) ? 'I' : '-', (ARM7REG(eCPSR) & F_MASK) ? 'F' : '-', (ARM7REG(eCPSR) & T_MASK) ? 'T' : '-', @@ -444,40 +461,57 @@ static READ32_DEVICE_HANDLER( arm7_rt_r_callback ) LOG( ( "arm7_rt_r_callback CR%d, RESERVED\n", cReg ) ); break; case 0: // ID - switch (cpustate->archRev) + switch(op2) { - case 3: // ARM6 32-bit - data = 0x41; - break; + case 0: + switch (cpustate->archRev) + { + case 3: // ARM6 32-bit + data = 0x41; + break; - case 4: // ARM7/SA11xx - data = 0x41 | (1 << 23) | (7 << 12); - break; + case 4: // ARM7/SA11xx + data = 0x41 | (1 << 23) | (7 << 12); + break; - case 5: // ARM9/10/XScale - data = 0x41 | (9 << 12); - if (cpustate->archFlags & eARM_ARCHFLAGS_T) - { - if (cpustate->archFlags & eARM_ARCHFLAGS_E) + case 5: // ARM9/10/XScale + data = 0x41 | (9 << 12); + if (cpustate->archFlags & eARM_ARCHFLAGS_T) { - if (cpustate->archFlags & eARM_ARCHFLAGS_J) + if (cpustate->archFlags & eARM_ARCHFLAGS_E) { - data |= (6<<16); // v5TEJ + if (cpustate->archFlags & eARM_ARCHFLAGS_J) + { + data |= (6<<16); // v5TEJ + } + else + { + data |= (5<<16); // v5TE + } } else { - data |= (5<<16); // v5TE + data |= (4<<16); // v5T } } - else - { - data |= (4<<16); // v5T - } - } - break; + break; - case 6: // ARM11 - data = 0x41 | (10<< 12) | (7<<16); // v6 + case 6: // ARM11 + data = 0x41 | (10<< 12) | (7<<16); // v6 + break; + } + break; + case 1: // cache type + data = 0x0f0d2112; // HACK: value expected by ARMWrestler (probably Nintendo DS ARM9's value) + break; + case 2: // TCM type + data = 0; + break; + case 3: // TLB type + data = 0; + break; + case 4: // MPU type + data = 0; break; } LOG( ( "arm7_rt_r_callback, ID\n" ) ); diff --git a/src/emu/cpu/arm7/arm7core.h b/src/emu/cpu/arm7/arm7core.h index 779a668d9b0..6edd511f065 100644 --- a/src/emu/cpu/arm7/arm7core.h +++ b/src/emu/cpu/arm7/arm7core.h @@ -270,6 +270,7 @@ static const int sRegisterTable[ARM7_NUM_MODES][18] = #define Z_BIT 30 #define C_BIT 29 #define V_BIT 28 +#define Q_BIT 27 #define I_BIT 7 #define F_BIT 6 #define T_BIT 5 // Thumb mode @@ -278,6 +279,7 @@ static const int sRegisterTable[ARM7_NUM_MODES][18] = #define Z_MASK ((UINT32)(1 << Z_BIT)) /* Zero flag */ #define C_MASK ((UINT32)(1 << C_BIT)) /* Carry flag */ #define V_MASK ((UINT32)(1 << V_BIT)) /* oVerflow flag */ +#define Q_MASK ((UINT32)(1 << Q_BIT)) /* signed overflow for QADD, MAC */ #define I_MASK ((UINT32)(1 << I_BIT)) /* Interrupt request disable */ #define F_MASK ((UINT32)(1 << F_BIT)) /* Fast interrupt request disable */ #define T_MASK ((UINT32)(1 << T_BIT)) /* Thumb Mode flag */ @@ -286,6 +288,7 @@ static const int sRegisterTable[ARM7_NUM_MODES][18] = #define Z_IS_SET(pc) ((pc) & Z_MASK) #define C_IS_SET(pc) ((pc) & C_MASK) #define V_IS_SET(pc) ((pc) & V_MASK) +#define Q_IS_SET(pc) ((pc) & Q_MASK) #define I_IS_SET(pc) ((pc) & I_MASK) #define F_IS_SET(pc) ((pc) & F_MASK) #define T_IS_SET(pc) ((pc) & T_MASK) @@ -294,6 +297,7 @@ static const int sRegisterTable[ARM7_NUM_MODES][18] = #define Z_IS_CLEAR(pc) (!Z_IS_SET(pc)) #define C_IS_CLEAR(pc) (!C_IS_SET(pc)) #define V_IS_CLEAR(pc) (!V_IS_SET(pc)) +#define Q_IS_CLEAR(pc) (!Q_IS_SET(pc)) #define I_IS_CLEAR(pc) (!I_IS_SET(pc)) #define F_IS_CLEAR(pc) (!F_IS_SET(pc)) #define T_IS_CLEAR(pc) (!T_IS_SET(pc)) diff --git a/src/emu/cpu/arm7/arm7dasm.c b/src/emu/cpu/arm7/arm7dasm.c index 15520c8c69d..18ca7bdfa02 100644 --- a/src/emu/cpu/arm7/arm7dasm.c +++ b/src/emu/cpu/arm7/arm7dasm.c @@ -233,7 +233,63 @@ UINT32 arm7_disasm( char *pBuf, UINT32 pc, UINT32 opcode ) } else if ((opcode & 0x0ff000f0) == 0x01600010) // CLZ - v5 { - pBuf += sprintf(pBuf, "CLZ R%d, R%d", (opcode>>12)&0xf, opcode&0xf); + pBuf += sprintf(pBuf, "CLZ"); + pBuf = WritePadding( pBuf, pBuf0 ); + pBuf += sprintf(pBuf, "R%d, R%d", (opcode>>12)&0xf, opcode&0xf); + } + else if ((opcode & 0x0ff000f0) == 0x01000050) // QADD - v5 + { + pBuf += sprintf(pBuf, "QADD"); + pBuf = WritePadding( pBuf, pBuf0 ); + pBuf += sprintf(pBuf, "R%d, R%d, R%d", (opcode>>12)&0xf, opcode&0xf, (opcode>>16)&0xf); + } + else if ((opcode & 0x0ff000f0) == 0x01400050) // QDADD - v5 + { + pBuf += sprintf(pBuf, "QDADD"); + pBuf = WritePadding( pBuf, pBuf0 ); + pBuf += sprintf(pBuf, "R%d, R%d, R%d", (opcode>>12)&0xf, opcode&0xf, (opcode>>16)&0xf); + } + else if ((opcode & 0x0ff000f0) == 0x01200050) // QSUB - v5 + { + pBuf += sprintf(pBuf, "QSUB"); + pBuf = WritePadding( pBuf, pBuf0 ); + pBuf += sprintf(pBuf, "R%d, R%d, R%d", (opcode>>12)&0xf, opcode&0xf, (opcode>>16)&0xf); + } + else if ((opcode & 0x0ff000f0) == 0x01600050) // QDSUB - v5 + { + pBuf += sprintf(pBuf, "QDSUB"); + pBuf = WritePadding( pBuf, pBuf0 ); + pBuf += sprintf(pBuf, "R%d, R%d, R%d", (opcode>>12)&0xf, opcode&0xf, (opcode>>16)&0xf); + } + else if ((opcode & 0x0ff00090) == 0x01000080) // SMLAxy - v5 + { + pBuf += sprintf(pBuf, "SMLA%c%c", (opcode&0x20) ? 'T' : 'B', (opcode&0x40) ? 'T' : 'B'); + pBuf = WritePadding( pBuf, pBuf0 ); + pBuf += sprintf(pBuf, "R%d, R%d, R%d, R%d", (opcode>>16)&0xf, (opcode>>12)&0xf, opcode&0xf, (opcode>>8)&0xf); + } + else if ((opcode & 0x0ff00090) == 0x01400080) // SMLALxy - v5 + { + pBuf += sprintf(pBuf, "SMLAL%c%c", (opcode&0x20) ? 'T' : 'B', (opcode&0x40) ? 'T' : 'B'); + pBuf = WritePadding( pBuf, pBuf0 ); + pBuf += sprintf(pBuf, "R%d, R%d, R%d, R%d", (opcode>>16)&0xf, (opcode>>12)&0xf, opcode&0xf, (opcode>>8)&0xf); + } + else if ((opcode & 0x0ff00090) == 0x01600080) // SMULxy - v5 + { + pBuf += sprintf(pBuf, "SMUL%c%c", (opcode&0x20) ? 'T' : 'B', (opcode&0x40) ? 'T' : 'B'); + pBuf = WritePadding( pBuf, pBuf0 ); + pBuf += sprintf(pBuf, "R%d, R%d, R%d", (opcode>>16)&0xf, opcode&0xf, (opcode>>12)&0xf); + } + else if ((opcode & 0x0ff000b0) == 0x012000a0) // SMULWy - v5 + { + pBuf += sprintf(pBuf, "SMULW%c", (opcode&0x40) ? 'T' : 'B'); + pBuf = WritePadding( pBuf, pBuf0 ); + pBuf += sprintf(pBuf, "R%d, R%d, R%d", (opcode>>16)&0xf, opcode&0xf, (opcode>>8)&0xf); + } + else if ((opcode & 0x0ff000b0) == 0x01200080) // SMLAWy - v5 + { + pBuf += sprintf(pBuf, "SMLAW%c", (opcode&0x40) ? 'T' : 'B'); + pBuf = WritePadding( pBuf, pBuf0 ); + pBuf += sprintf(pBuf, "R%d, R%d, R%d, R%d", (opcode>>16)&0xf, opcode&0xf, (opcode>>8)&0xf, (opcode>>12)&0xf); } else if( (opcode&0x0e000000)==0 && (opcode&0x80) && (opcode&0x10) ) //bits 27-25 == 000, bit 7=1, bit 4=1 { diff --git a/src/emu/cpu/arm7/arm7exec.c b/src/emu/cpu/arm7/arm7exec.c index 0b023f09f25..9170f813623 100644 --- a/src/emu/cpu/arm7/arm7exec.c +++ b/src/emu/cpu/arm7/arm7exec.c @@ -1257,6 +1257,235 @@ 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