} //calculateMultM //32bit multiply instructions (MUL/MLA) internal uint ExecuteDataOpMultiply(DataOpMultiplyRaw opHandler, uint opcode, bool setflags) { //The original code used the masks rd_mask, rn_mask, rm_mask and rs_mask here //But the operands are in a strange order so the masks were used in a confusing way. //Therefore, we have one of the rare cases where it's better to use magic numbers... uint Rm = (opcode) & 0xf; uint Rs = (opcode >> 8) & 0xf; uint Rd = (opcode >> 16) & 0xf; uint Rn = (opcode >> 12) & 0xf; uint rs_value = get_reg(Rs); uint rm_value = get_reg(Rm); uint rn_value = get_reg(Rn); //The number of cycles taken for multiplication varies depending //on the magnitude of rs_value. uint M = calculateMultM(rs_value); MultiplyOpResult result = opHandler(rn_value, rs_value, rm_value); mGPR[Rd] = result.output_value; if (setflags) { CPSR.set_NZCV((result.output_value & 0x80000000) != 0, result.output_value == 0, CPSR.cf, CPSR.vf); } return(M + result.cycles); }
//64bit multiply instructions (SMULL/UMULL/SMLAL/UMLAL) internal uint ExecuteDataOpMultiplyLong(DataOpMultiplyLongRaw opHandler, uint opcode, bool setflags) { //See comment in ExecuteDataOpMultiply uint Rm = (opcode) & 0xf; uint Rs = (opcode >> 8) & 0xf; uint RdHi = (opcode >> 16) & 0xf; uint RdLo = (opcode >> 12) & 0xf; uint rs_value = get_reg(Rs); uint rm_value = get_reg(Rm); UInt64 rdhilo_value = (((UInt64)get_reg(RdHi)) << 32) | (UInt64)get_reg(RdLo); //The number of cycles taken for multiplication varies depending //on the magnitude of rs_value. uint M = calculateMultM(rs_value); LongMultiplyOpResult result = opHandler(rdhilo_value, rs_value, rm_value); mGPR[RdHi] = (UInt32)((result.output_value >> 32) & 0xffffffff); mGPR[RdLo] = (UInt32)((result.output_value) & 0xffffffff); //If we set the flags after a long multiply, the N flag is based on bit 63, not bit 31. if (setflags) { CPSR.set_NZCV((result.output_value & 0x8000000000000000) != 0, result.output_value == 0, CPSR.cf, CPSR.vf); } return(result.cycles + M); }
protected BaseARMCore() { this.TrapUnalignedMemoryAccess = true; mCPSR = new CPSR(); mGPR = new GeneralPurposeRegisters(mCPSR); mFPP = new FloatingPointProcessor(this); this.Reset(); }//Jimulator ctor
internal void DataOpHandleSetFlagsAWC_NonCMP(uint opcode, uint Rd, AWCResult awcResult) { if (Rd == GeneralPurposeRegisters.PCRegisterIndex) { CPSR.RestoreCPUMode(); } else { DataOpHandleSetFlagsAWC_CMP(opcode, Rd, awcResult); } }
internal void DataOpHandleSetFlagsLogical_NonCMP(uint opcode, uint Rd, uint result, bool shift_carry) { if (Rd == GeneralPurposeRegisters.PCRegisterIndex) { CPSR.RestoreCPUMode(); } else { DataOpHandleSetFlagsLogical_CMP(opcode, Rd, result, shift_carry); } }
}//ExecuteARMInstruction /// <summary> /// Check the condition code(cc) of the current instruction and determine if /// the instruction should be executed or not. /// </summary> /// <param name="condition"></param> /// <returns></returns> internal bool check_cc(byte condition) { CPSR cpsr = this.CPSR; switch (condition & 0x0f) { case 0x00: return(cpsr.zf); case 0x01: return(!cpsr.zf); case 0x02: return(cpsr.cf); case 0x03: return(!cpsr.cf); case 0x04: return(cpsr.nf); case 0x05: return(!cpsr.nf); case 0x06: return(cpsr.vf); case 0x07: return(!cpsr.vf); //case 0X8: go = and(cf(cpsr), not(zf(cpsr))); break; case 0x08: return(cpsr.cf && !cpsr.zf); //case 0X9: go = or(not(cf(cpsr)), zf(cpsr)); break; case 0x09: return(!cpsr.cf || cpsr.zf); //case 0XA: go = not(xor(nf(cpsr), vf(cpsr))); break; case 0x0a: return(!(cpsr.nf ^ cpsr.vf)); //case 0XB: go = xor(nf(cpsr), vf(cpsr)); break; case 0x0b: return(cpsr.nf ^ cpsr.vf); //case 0XC: go = and(not(zf(cpsr)), not(xor(nf(cpsr), vf(cpsr)))); break; case 0x0c: return(!cpsr.zf && (!(cpsr.nf ^ cpsr.vf))); //case 0XD: go = or(zf(cpsr), xor(nf(cpsr), vf(cpsr))); break; case 0xd: return(cpsr.zf || (cpsr.nf ^ cpsr.vf)); case 0x0e: return(true); case 0x0f: return(false); default: return(false); } //switch } //check_cc
internal uint data1(uint opcode) { uint Rd = (opcode >> 8) & 0x07; uint imm = opcode & 0x00ff; uint result; switch (opcode & 0x1800) { case 0x0000: //MOV (1) result = imm; CPSR.set_NZ(result); mGPR[Rd] = result; break; case 0x0800: { //CMP (1) uint op1 = get_reg(Rd); result = (op1 - imm); CPSR.set_flags_sub(op1, imm, result, true); } break; case 0x1000: { //ADD (2) uint op1 = get_reg(Rd); result = (op1 + imm); CPSR.set_flags_add(op1, imm, result, false); mGPR[Rd] = result; } break; case 0x1800: { //SUB (2) uint op1 = get_reg(Rd); result = (op1 - imm); CPSR.set_flags_sub(op1, imm, result, true); mGPR[Rd] = result; } break; } //switch return(1); } //data1
/*----------------------------------------------------------------------------*/ //---------------------------------------------------------------------------- internal uint normal_data_op(uint op_code, uint operation) { //extract the S bit value from the opcode bool SBit = ((op_code & s_mask) != 0); //extract destination register from opcode uint Rd = ((op_code & rd_mask) >> 12); //extract operand register and get first operand uint Rn = ((op_code & rn_mask) >> 16); uint a = get_reg(Rn); bool shift_carry = false; uint b; if ((op_code & imm_mask) == 0) { b = b_reg(op_code & op2_mask, ref shift_carry); } else { b = b_immediate(op_code & op2_mask, ref shift_carry); } uint rd = 0; switch (operation) /* R15s @@?! */ { case 0x0: rd = a & b; break; //AND case 0x1: rd = a ^ b; break; //EOR case 0x2: rd = a - b; break; //SUB case 0x3: rd = b - a; break; //RSB case 0x4: rd = a + b; break; //ADD case 0x5: rd = a + b; //ADC if (CPSR.cf) { ++rd; } break; case 0x6: rd = a - b - 1; //SBC if (CPSR.cf) { ++rd; } break; case 0x7: rd = b - a - 1; //RSC if (CPSR.cf) { ++rd; } break; case 0x8: rd = a & b; break; //TST case 0x9: rd = a ^ b; break; //TEQ case 0xa: rd = a - b; break; //CMP case 0xb: rd = a + b; break; //CMN case 0xc: rd = a | b; break; //ORR case 0xd: rd = b; break; //MOV case 0xe: rd = a & ~b; break; //BIC case 0xf: rd = ~b; break; //MVN }//switch if ((operation & 0xc) != 0x8) //Return result unless a compare { //write result into destination register mGPR[Rd] = rd; //if S bit is set and if the destination register is r15(pc) then this instruction has //special meaning. We are returning from a non-user mode to user-mode. //ie movs pc,r14 if (SBit && (Rd == GeneralPurposeRegisters.PCRegisterIndex)) { CPSR.RestoreCPUMode(); return(1); } //if } //if //if S bit is not set, we do not need to set any cpu flags, so we are done here if (!SBit) { return(1); } switch (operation) { //LOGICALs case 0x0: //AND case 0x1: //EOR case 0x8: //TST case 0x9: //TEQ case 0xc: //ORR case 0xd: //MOV case 0xe: //BIC case 0xf: //MVN CPSR.set_NZ(rd); CPSR.cf = shift_carry; break; case 0x2: //SUB case 0xa: //CMP CPSR.set_flags_sub(a, b, rd, true); break; case 0x6: //SBC CPSR.set_flags_sub(a, b, rd, CPSR.cf); break; case 0x3: //RSB CPSR.set_flags_sub(b, a, rd, true); break; case 0x7: //RSC CPSR.set_flags_sub(b, a, rd, CPSR.cf); break; case 0x4: //ADD case 0xb: //CMN CPSR.set_flags_add(a, b, rd, false); break; case 0x5: //ADC CPSR.set_flags_add(a, b, rd, CPSR.cf); break; default: break; } //switch return(1); } //normal_data_op
/// <summary> /// Perform the ARM instruction MUL /// </summary> /// <param name="op_code"></param> /// <returns></returns> internal uint multiply(uint opcode) { uint Rs = ((opcode & rs_mask) >> 8); uint Rm = (opcode & rm_mask); uint Rd = ((opcode & rd_mask) >> 12); uint Rn = ((opcode & rn_mask) >> 16); uint RsData = get_reg(Rs); uint RmData = get_reg(Rm); uint M = calculateMultM(RsData); if ((opcode & mul_long_bit) == 0) {//Normal:mla,mul uint cycles = 1; uint acc = (RmData * RsData); if ((opcode & mul_acc_bit) != 0) { acc += get_reg(Rd); ++cycles; } mGPR[Rn] = acc; if ((opcode & s_mask) != 0) { CPSR.set_NZ(acc);//flags } return(cycles + M); }//if else {//Long:xMLAL,xMULL uint cycles = 2; bool sign = false; if ((opcode & 0x00400000) != 0) {//Signed if (Utils.msb(RmData)) { RmData = ~RmData + 1; sign = true; } if (Utils.msb(RsData)) { RsData = ~RsData + 1; sign = !sign; } }//if //Everything now `positive uint tl = (RmData & 0x0000ffff) * (RsData & 0x0000ffff); uint th = ((RmData >> 16) & 0X0000ffff) * ((RsData >> 16) & 0X0000ffff); uint tm = ((RmData >> 16) & 0X0000ffff) * (RsData & 0X0000ffff); RmData = ((RsData >> 16) & 0X0000ffff) * (RmData & 0X0000ffff); /* Rm no longer needed */ tm = tm + RmData; if (tm < RmData) { th = th + 0X00010000; /* Propagate carry */ } tl = tl + (tm << 16); if (tl < (tm << 16)) { th = th + 1; } th = th + ((tm >> 16) & 0X0000ffff); if (sign) { th = ~th; tl = ~tl + 1; if (tl == 0) { th = th + 1; } } if ((opcode & mul_acc_bit) != 0) { ++cycles; tm = tl + get_reg(Rd); if (tm < tl) { th = th + 1;//Propagate carry } tl = tm; th += get_reg(Rn); } mGPR[Rd] = tl; mGPR[Rn] = th; if ((opcode & s_mask) != 0) { CPSR.set_NZ(th | (((tl >> 16) | tl) & 0x0000ffff)); } return(cycles + M); } //else } //multiply
} //stm /// <summary> /// LDM - perform a multi-register load from memory /// </summary> /// <returns>Number of clock cycles executed</returns> internal uint ldm(uint opcode) { uint mode = (opcode & 0x01800000) >> 23; //isolate PU bits uint Rn = (opcode & rn_mask) >> 16; //get base register uint reg_list = opcode & 0x0000ffff; //get the register list bool writeback = (opcode & write_back_mask) != 0; //determine writeback flag bool userModeLoad = ((opcode & 0x00400000) != 0); //get user mode load flag uint address = Utils.valid_address(get_reg(Rn)); //make sure it is word aligned. int first_reg; uint count = bit_count(reg_list, out first_reg); bool includesPC = ((reg_list & 0x00008000) != 0); uint new_base; switch (mode) { case 0: new_base = address - 4 * count; address = new_base + 4; break; //DA (Decrement After) case 1: new_base = address + 4 * count; break; //IA (Increment After) case 2: new_base = address - 4 * count; address = new_base; break; //DB (Decrement Before) case 3: new_base = address + 4 * count; address = address + 4; break; //IB (Increment Before) default: return(0); } //switch if (writeback) { mGPR[Rn] = new_base; } uint reg = 0; uint extraCycles = 0; //check while (reg_list != 0) { if (Utils.lsb(reg_list)) { uint data = this.GetMemory(address, ARMPluginInterfaces.MemorySize.Word); if (reg == GeneralPurposeRegisters.PCRegisterIndex) { //We are loading a new value into the PC, check if entering Thumb or ARM mode extraCycles = 2; //if the user mode flag is set and r15 is being loaded, we are executing //LDM(3) and returning from an exception. We may be returning to Thumb mode or //ARM mode. In each case, force align the returning address. if (userModeLoad) { //we are returning from an exception, check if returning to ARM or Thumb mode if ((CPSR.SPSR & 0x0020) != 0) //check thumb bit of saved CPSR { data = data & 0xfffffffe; //returning to thumb mode, force align HWord } else { data = data & 0xfffffffc; //returning to arm mode, force align Word } } else if ((data & 0x01) != 0) { //Entering Thumb mode. Make sure destination address is HWord aligned CPSR.tf = true; data = data & 0xfffffffe; } //if else { //Entering ARM mode. Make sure destination address is Word aligned CPSR.tf = false; data = data & 0xfffffffc; } //else } //if if (userModeLoad & !includesPC) { mGPR.SetUserModeRegister(reg, data); } else { mGPR[reg] = data; } address += 4; } //if reg_list >>= 1; ++reg; } //while //if the PC was loaded during this instruction and the user mode load was specified //then we are returning from an exception. if (includesPC && userModeLoad) { CPSR.RestoreCPUMode(); } return(2 + count + extraCycles); } //ldm
}//data0(Thumb) internal uint data_transfer(uint opcode) { if ((opcode & 0x1000) == 0) { //NOT load/store if ((opcode & 0x0800) == 0) { //NOT load literal pool if ((opcode & 0x0400) == 0) { //Data processing uint Rd = (opcode & 0x07); uint Rm = ((opcode >> 3) & 7); uint op1 = get_reg(Rd); uint op2 = get_reg(Rm); uint result; //data processing opcode switch (opcode & 0x03c0) { case 0x0000: //AND result = op1 & op2; CPSR.set_NZ(result); mGPR[Rd] = result; break; case 0x0040: //EOR result = op1 ^ op2; CPSR.set_NZ(result); mGPR[Rd] = result; break; case 0x0080: { //LSL(2) bool cf = CPSR.cf; result = lsl(op1, op2 & 0x00ff, ref cf); CPSR.cf = cf; CPSR.set_NZ(result); mGPR[Rd] = result; } break; case 0x00c0: //LSR(2) { bool cf = CPSR.cf; result = lsr(op1, op2 & 0x00ff, ref cf); CPSR.cf = cf; CPSR.set_NZ(result); mGPR[Rd] = result; } break; case 0x0100: //ASR (2) { bool cf = CPSR.cf; result = asr(op1, op2 & 0x00ff, ref cf); CPSR.cf = cf; CPSR.set_NZ(result); mGPR[Rd] = result; } break; case 0x0140: //ADC result = op1 + op2; if (CPSR.cf) { result++; //Add CF } CPSR.set_flags_add(op1, op2, result, CPSR.cf); mGPR[Rd] = result; break; case 0x0180: //SBC result = op1 - op2 - 1; if (CPSR.cf) { result++; //Add CF } CPSR.set_flags_sub(op1, op2, result, CPSR.cf); mGPR[Rd] = result; break; case 0x01c0: { //ROR bool cf = CPSR.cf; result = ror(op1, op2 & 0x00ff, ref cf); CPSR.cf = cf; CPSR.set_NZ(result); mGPR[Rd] = result; } break; case 0x0200: //TST CPSR.set_NZ(op1 & op2); break; case 0x0240: //NEG result = (uint)(0 - (int)op2); CPSR.set_flags_sub(0, op2, result, true); mGPR[Rd] = result; break; case 0x0280: //CMP(2) CPSR.set_flags_sub(op1, op2, op1 - op2, true); break; case 0x02c0: //CMN CPSR.set_flags_add(op1, op2, op1 + op2, false); break; case 0x0300: //ORR result = op1 | op2; CPSR.set_NZ(result); mGPR[Rd] = result; break; case 0x0340: //MUL result = op1 * op2; CPSR.set_NZ(result); mGPR[Rd] = result; break; case 0x0380: //BIC result = op1 & ~op2; CPSR.set_NZ(result); mGPR[Rd] = result; break; case 0x03c0: //MVN result = ~op2; CPSR.set_NZ(result); mGPR[Rd] = result; break; }//switch } else {//special data processing -- NO FLAG UPDATE //ADD(4),CMP(3),MOV(2),BX,BLX uint Rd = ((opcode & 0x0080) >> 4) | (opcode & 0x07); uint Rm = ((opcode >> 3) & 15); switch (opcode & 0x0300) { case 0x0000: //ADD (4) high registers mGPR[Rd] = get_reg(Rd) + get_reg(Rm); break; case 0x0100: { //CMP (3) high registers uint op1 = get_reg(Rd); uint op2 = get_reg(Rm); CPSR.set_flags_sub(op1, op2, op1 - op2, true); } break; case 0x0200: { //MOV (2) high registers uint op2 = get_reg(Rm); if (Rd == GeneralPurposeRegisters.PCRegisterIndex) { op2 = op2 & 0xfffffffe; //Tweak mov to PC } mGPR[Rd] = op2; } break; case 0x0300: { //BX/BLX Rm bool link = ((opcode & 0x0080) != 0); old_bx((opcode >> 3) & 0x0f, link); } break; }//switch } } else {//load from literal pool -- LDR PC uint Rd = ((opcode >> 8) & 0x07); uint address = (((opcode & 0x00ff) << 2)) + (get_reg(GeneralPurposeRegisters.PCRegisterIndex) & 0xfffffffc); mGPR[Rd] = GetMemory(address, ARMPluginInterfaces.MemorySize.Word); }//else } else { data_transfer_load_store(opcode); } return(1); }//data_transfer(Thumb)
}//thumb_branch1 internal uint data0(uint opcode) { if ((opcode & 0x1800) != 0x1800) { //Either LSL,LSR or ASR uint Rm = ((opcode >> 3) & 0x07); //Source register uint Rd = (opcode & 0x07); //Destination register uint shift = ((opcode >> 6) & 0x1f); //Shift value uint result = 0; bool cf = (this.CPSR.cf); switch (opcode & 0X1800) { case 0x0000: //LSL(1) result = lsl(get_reg(Rm), shift, ref cf); break; case 0x0800: //LSR(1) //A shift value of 0 means shift by 32 if (shift == 0) { shift = 32; } result = lsr(get_reg(Rm), shift, ref cf); break; case 0x1000: //ASR(1) //A shift value of 0 means shift by 32 if (shift == 0) { shift = 32; } result = asr(get_reg(Rm), shift, ref cf); break; default: //Can't get here, but just in case return(1); }//switch this.CPSR.cf = cf; this.CPSR.set_NZ(result); mGPR[Rd] = result; }//if else {//Either ADD or SUB uint Rd = (opcode & 0x07); //Destination register uint Rn = ((opcode >> 3) & 0x07); uint Rm = ((opcode >> 6) & 0x07); uint op2; if ((opcode & 0x0400) == 0) {//ADD(3) or SUB(3) op2 = get_reg(Rm); } else {//ADD(1) or SUB(1) op2 = Rm; } uint result; uint op1 = get_reg(Rn); if ((opcode & 0x0200) == 0) { result = op1 + op2; CPSR.set_flags_add(op1, op2, result, false); } else { result = op1 - op2; CPSR.set_flags_sub(op1, op2, result, true); } //else mGPR[Rd] = result; } //else return(1); }//data0(Thumb)
internal void DataOpHandleSetFlagsAWC_CMP(uint opcode, uint Rd, AWCResult awcResult) { //The CPSR is set the same way for all AWC-based instructions (the only difference //is the input to AWC). CPSR.set_NZCV((awcResult.result & 0x80000000) != 0, awcResult.result == 0, awcResult.carry_out, awcResult.overflow); }
internal void DataOpHandleSetFlagsLogical_CMP(uint opcode, uint Rd, uint result, bool shift_carry) { //The CPSR is always set the same way for logical operations //cf. MOV (ARMv7 A8.8.102) CPSR.set_NZCV((result & 0x80000000) != 0, result == 0, shift_carry, CPSR.vf); }
/// <summary> /// GeneralPurposeRegisters ctor /// Requires a reference to the cpsr for current operating mode /// </summary> /// <param name="cpsr">the simulation CPSR register</param> public GeneralPurposeRegisters(CPSR cpsr) { _cpsr = cpsr; this.Reset(); }