| // -*- C -*- | |
| // | |
| // NEC specific instructions | |
| // | |
| :%s::::MFHI:int hi | |
| { | |
| return hi ? "hi" : ""; | |
| } | |
| :%s::::SAT:int s | |
| { | |
| return s ? "s" : ""; | |
| } | |
| :%s::::UNS:int u | |
| { | |
| return u ? "u" : ""; | |
| } | |
| // Simulate the various kinds of multiply and multiply-accumulate instructions. | |
| // Perform an operation of the form: | |
| // | |
| // LHS (+/-) GPR[RS] * GPR[RT] | |
| // | |
| // and store it in the 64-bit accumulator. Optionally copy either LO or | |
| // HI into a general purpose register. | |
| // | |
| // - RD is the destination register of the LO or HI move | |
| // - RS are RT are the multiplication source registers | |
| // - ACCUMULATE_P is true if LHS should be the value of the 64-bit accumulator, | |
| // false if it should be 0. | |
| // - STORE_HI_P is true if HI should be stored in RD, false if LO should be. | |
| // - UNSIGNED_P is true if the operation should be unsigned. | |
| // - SATURATE_P is true if the result should be saturated to a 32-bit value. | |
| // - SUBTRACT_P is true if the right hand side should be subtraced from LHS, | |
| // false if it should be added. | |
| // - SHORT_P is true if RS and RT must be 16-bit numbers. | |
| // - DOUBLE_P is true if the 64-bit accumulator is in LO, false it is a | |
| // concatenation of the low 32 bits of HI and LO. | |
| :function:::void:do_vr_mul_op:int rd, int rs, int rt, int accumulate_p, int store_hi_p, int unsigned_p, int saturate_p, int subtract_p, int short_p, int double_p | |
| { | |
| uint64_t lhs, x, y, xcut, ycut, product, result; | |
| check_mult_hilo (SD_, HIHISTORY, LOHISTORY); | |
| lhs = (!accumulate_p ? 0 : double_p ? LO : U8_4 (HI, LO)); | |
| x = GPR[rs]; | |
| y = GPR[rt]; | |
| /* Work out the canonical form of X and Y from their significant bits. */ | |
| if (!short_p) | |
| { | |
| /* Normal sign-extension rule for 32-bit operands. */ | |
| xcut = EXTEND32 (x); | |
| ycut = EXTEND32 (y); | |
| } | |
| else if (unsigned_p) | |
| { | |
| /* Operands must be zero-extended 16-bit numbers. */ | |
| xcut = x & 0xffff; | |
| ycut = y & 0xffff; | |
| } | |
| else | |
| { | |
| /* Likewise but sign-extended. */ | |
| xcut = EXTEND16 (x); | |
| ycut = EXTEND16 (y); | |
| } | |
| if (x != xcut || y != ycut) | |
| sim_engine_abort (SD, CPU, CIA, | |
| "invalid multiplication operand at 0x%08lx\n", | |
| (long) CIA); | |
| TRACE_ALU_INPUT2 (x, y); | |
| product = (unsigned_p | |
| ? V8_4 (x, 1) * V8_4 (y, 1) | |
| : EXTEND32 (x) * EXTEND32 (y)); | |
| result = (subtract_p ? lhs - product : lhs + product); | |
| if (saturate_p) | |
| { | |
| /* Saturate the result to 32 bits. An unsigned, unsaturated | |
| result is zero-extended to 64 bits, but unsigned overflow | |
| causes all 64 bits to be set. */ | |
| if (!unsigned_p && (uint64_t) EXTEND32 (result) != result) | |
| result = ((int64_t) result < 0 ? -0x7fffffff - 1 : 0x7fffffff); | |
| else if (unsigned_p && (result >> 32) != 0) | |
| result = (uint64_t) 0 - 1; | |
| } | |
| TRACE_ALU_RESULT (result); | |
| if (double_p) | |
| LO = result; | |
| else | |
| { | |
| LO = EXTEND32 (result); | |
| HI = EXTEND32 (VH4_8 (result)); | |
| } | |
| if (rd != 0) | |
| GPR[rd] = store_hi_p ? HI : LO; | |
| } | |
| // VR4100 instructions. | |
| 000000,5.RS,5.RT,00000,00000,101000::32::MADD16 | |
| "madd16 r<RS>, r<RT>" | |
| *vr4100: | |
| { | |
| do_vr_mul_op (SD_, 0, RS, RT, | |
| 1 /* accumulate */, | |
| 0 /* store in LO */, | |
| 0 /* signed arithmetic */, | |
| 0 /* don't saturate */, | |
| 0 /* don't subtract */, | |
| 1 /* short */, | |
| 0 /* single */); | |
| } | |
| 000000,5.RS,5.RT,00000,00000,101001::64::DMADD16 | |
| "dmadd16 r<RS>, r<RT>" | |
| *vr4100: | |
| { | |
| do_vr_mul_op (SD_, 0, RS, RT, | |
| 1 /* accumulate */, | |
| 0 /* store in LO */, | |
| 0 /* signed arithmetic */, | |
| 0 /* don't saturate */, | |
| 0 /* don't subtract */, | |
| 1 /* short */, | |
| 1 /* double */); | |
| } | |
| // VR4120 and VR4130 instructions. | |
| 000000,5.RS,5.RT,5.RD,1.SAT,1.MFHI,00,1.UNS,101001::64::DMACC | |
| "dmacc%s<MFHI>%s<UNS>%s<SAT> r<RD>, r<RS>, r<RT>" | |
| *vr4120: | |
| { | |
| do_vr_mul_op (SD_, RD, RS, RT, | |
| 1 /* accumulate */, | |
| MFHI, UNS, SAT, | |
| 0 /* don't subtract */, | |
| SAT /* short */, | |
| 1 /* double */); | |
| } | |
| 000000,5.RS,5.RT,5.RD,1.SAT,1.MFHI,00,1.UNS,101000::32::MACC_4120 | |
| "macc%s<MFHI>%s<UNS>%s<SAT> r<RD>, r<RS>, r<RT>" | |
| *vr4120: | |
| { | |
| do_vr_mul_op (SD_, RD, RS, RT, | |
| 1 /* accumulate */, | |
| MFHI, UNS, SAT, | |
| 0 /* don't subtract */, | |
| SAT /* short */, | |
| 0 /* single */); | |
| } | |
| // VR5400 and VR5500 instructions. | |
| 000000,5.RS,5.RT,5.RD,0,1.MFHI,001,01100,1.UNS::32::MUL | |
| "mul%s<MFHI>%s<UNS> r<RD>, r<RS>, r<RT>" | |
| *vr5400: | |
| *vr5500: | |
| { | |
| do_vr_mul_op (SD_, RD, RS, RT, | |
| 0 /* don't accumulate */, | |
| MFHI, UNS, | |
| 0 /* don't saturate */, | |
| 0 /* don't subtract */, | |
| 0 /* not short */, | |
| 0 /* single */); | |
| } | |
| 000000,5.RS,5.RT,5.RD,0,1.MFHI,011,01100,1.UNS::32::MULS | |
| "muls%s<MFHI>%s<UNS> r<RD>, r<RS>, r<RT>" | |
| *vr5400: | |
| *vr5500: | |
| { | |
| do_vr_mul_op (SD_, RD, RS, RT, | |
| 0 /* don't accumulate */, | |
| MFHI, UNS, | |
| 0 /* don't saturate */, | |
| 1 /* subtract */, | |
| 0 /* not short */, | |
| 0 /* single */); | |
| } | |
| 000000,5.RS,5.RT,5.RD,0,1.MFHI,101,01100,1.UNS::32::MACC_5xxx | |
| "macc%s<MFHI>%s<UNS> r<RD>, r<RS>, r<RT>" | |
| *vr5400: | |
| *vr5500: | |
| { | |
| do_vr_mul_op (SD_, RD, RS, RT, | |
| 1 /* accumulate */, | |
| MFHI, UNS, | |
| 0 /* don't saturate */, | |
| 0 /* don't subtract */, | |
| 0 /* not short */, | |
| 0 /* single */); | |
| } | |
| 000000,5.RS,5.RT,5.RD,0,1.MFHI,111,01100,1.UNS::32::MSAC | |
| "msac%s<MFHI>%s<UNS> r<RD>, r<RS>, r<RT>" | |
| *vr5400: | |
| *vr5500: | |
| { | |
| do_vr_mul_op (SD_, RD, RS, RT, | |
| 1 /* accumulate */, | |
| MFHI, UNS, | |
| 0 /* don't saturate */, | |
| 1 /* subtract */, | |
| 0 /* not short */, | |
| 0 /* single */); | |
| } | |
| 010011,5.BASE,5.INDEX,5.0,5.FD,000101:COP1X:64::LUXC1 | |
| "luxc1 f<FD>, r<INDEX>(r<BASE>)" | |
| *vr5500: | |
| { | |
| check_fpu (SD_); | |
| COP_LD (1, FD, do_load (SD_, AccessLength_DOUBLEWORD, | |
| (GPR[BASE] + GPR[INDEX]) & ~MASK64 (2, 0), 0)); | |
| } | |
| 010011,5.BASE,5.INDEX,5.FS,00000,001101:COP1X:64::SUXC1 | |
| "suxc1 f<FS>, r<INDEX>(r<BASE>)" | |
| *vr5500: | |
| { | |
| check_fpu (SD_); | |
| do_store (SD_, AccessLength_DOUBLEWORD, | |
| (GPR[BASE] + GPR[INDEX]) & ~MASK64 (2, 0), 0, | |
| COP_SD (1, FS)); | |
| } | |
| 010000,1,19.*,100000:COP0:32::WAIT | |
| "wait" | |
| *vr5500: | |
| 011100,00000,5.RT,5.DR,00000,111101:SPECIAL:64::MFDR | |
| "mfdr r<RT>, r<DR>" | |
| *vr5400: | |
| *vr5500: | |
| 011100,00100,5.RT,5.DR,00000,111101:SPECIAL:64::MTDR | |
| "mtdr r<RT>, r<DR>" | |
| *vr5400: | |
| *vr5500: | |
| 011100,00000,00000,00000,00000,111110:SPECIAL:64::DRET | |
| "dret" | |
| *vr5400: | |
| *vr5500: |