257 lines
5.9 KiB
C
257 lines
5.9 KiB
C
// -*- 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:
|