// -*- C -*-
// Simulator definition for the MIPS DSP REV 2 ASE.
// Copyright (C) 2007-2019 Free Software Foundation, Inc.
// Contributed by MIPS Technologies, Inc.
// Written by Chao-ying Fu (fu@mips.com).
//
// This file is part of the MIPS sim
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see .
// op: 0 = ADD, 1 = SUB
// sat: 0 = no saturation, 1 = saturation
:function:::void:do_u_ph_op:int rd, int rs, int rt, int op, int sat
{
  int i;
  unsigned32 h0;
  unsigned16 h1, h2;
  unsigned32 v1 = GPR[rs];
  unsigned32 v2 = GPR[rt];
  unsigned32 result = 0;
  for (i = 0; i < 32; i += 16, v1 >>= 16, v2 >>= 16)
    {
      h1 = (unsigned16)(v1 & 0xffff);
      h2 = (unsigned16)(v2 & 0xffff);
      if (op == 0) // ADD
	h0 = (unsigned32)h1 + (unsigned32)h2;
      else // SUB
	h0 = (unsigned32)h1 - (unsigned32)h2;
      if (op == 0 && (h0 > (unsigned32)0x0000ffff)) // ADD SAT
	{
	  DSPCR |= DSPCR_OUFLAG4;
	  if (sat == 1)
	    h0 = 0xffff;
	}
      else if (op == 1 && h1 < h2) // SUB SAT
	{
	  DSPCR |= DSPCR_OUFLAG4;
	  if (sat == 1)
	    h0 = 0x0;
	}
      result |= ((unsigned32)((unsigned16)h0) << i);
    }
  GPR[rd] = EXTEND32 (result);
}
// op: 0 = ADD, 1 = SUB
// round: 0 = no rounding, 1 = rounding
:function:::void:do_uh_qb_op:int rd, int rs, int rt, int op, int round
{
  int i;
  unsigned32 h0;
  unsigned8 h1, h2;
  unsigned32 v1 = GPR[rs];
  unsigned32 v2 = GPR[rt];
  unsigned32 result = 0;
  for (i = 0; i < 32; i += 8, v1 >>= 8, v2 >>= 8)
    {
      h1 = (unsigned8)(v1 & 0xff);
      h2 = (unsigned8)(v2 & 0xff);
      if (op == 0) // ADD
	h0 = (unsigned32)h1 + (unsigned32)h2;
      else // SUB
	h0 = (unsigned32)h1 - (unsigned32)h2;
      if (round == 1)
	h0 = (h0 + 1) >> 1;
      else
	h0 = h0 >> 1;
      result |= ((unsigned32)((unsigned8)h0) << i);
    }
  GPR[rd] = EXTEND32 (result);
}
// op: 0 = EQ, 1 = LT, 2 = LE
:function:::void:do_qb_cmpgdu:int rd, int rs, int rt, int op
{
  int i, j;
  unsigned32 v1 = GPR[rs];
  unsigned32 v2 = GPR[rt];
  unsigned8 h1, h2;
  unsigned32 result = 0;
  unsigned32 mask;
  for (i = 0, j = 0; i < 32; i += 8, j++, v1 >>= 8, v2 >>= 8)
    {
      h1 = (unsigned8)(v1 & 0xff);
      h2 = (unsigned8)(v2 & 0xff);
      mask = ~(1 << (DSPCR_CCOND_SHIFT + j));
      DSPCR &= mask;
      if (op == 0) // EQ
	{
	  result |= ((h1 == h2) << j);
	  DSPCR |= ((h1 == h2) << (DSPCR_CCOND_SHIFT + j));
	}
      else if (op == 1) // LT
	{
	  result |= ((h1 < h2) << j);
	  DSPCR |= ((h1 < h2) << (DSPCR_CCOND_SHIFT + j));
	}
      else // LE
	{
	  result |= ((h1 <= h2) << j);
	  DSPCR |= ((h1 <= h2) << (DSPCR_CCOND_SHIFT + j));
	}
    }
  GPR[rd] = EXTEND32 (result);
}
// op: 0 = DPA 1 = DPS
:function:::void:do_w_ph_dot_product:int ac, int rs, int rt, int op
{
  int i;
  unsigned32 v1 = GPR[rs];
  unsigned32 v2 = GPR[rt];
  signed16 h1, h2;
  signed32 result;
  unsigned32 lo = DSPLO(ac);
  unsigned32 hi = DSPHI(ac);
  signed64 prod = (signed64)((((unsigned64)hi) << 32) + (unsigned64)lo);
  for (i = 0; i < 32; i += 16, v1 >>= 16, v2 >>= 16)
    {
      h1 = (signed16)(v1 & 0xffff);
      h2 = (signed16)(v2 & 0xffff);
      result = (signed32)h1 * (signed32)h2;
      if (op == 0) // DPA
        prod += (signed64)result;
      else // DPS
        prod -= (signed64)result;
    }
  DSPLO(ac) = EXTEND32 (prod);
  DSPHI(ac) = EXTEND32 (prod >> 32);
}
// round: 0 = no rounding, 1 = rounding
:function:::void:do_w_mulq:int rd, int rs, int rt, int round
{
  unsigned32 v1 = GPR[rs];
  unsigned32 v2 = GPR[rt];
  signed32 w1, w2;
  signed64 prod;
  unsigned32 result;
  w1 = (signed32) v1;
  w2 = (signed32 )v2;
  if (w1 == (signed32) 0x80000000 && w2 == (signed32) 0x80000000)
    {
      DSPCR |= DSPCR_OUFLAG5;
      prod = 0x7fffffff;
    }
  else
    {
      prod = ((signed64) w1 * (signed64) w2) << 1;
      if (round == 1)
	prod += 0x0000000080000000LL;
      prod = prod >> 32;
    }
  result = (unsigned32) prod;
  GPR[rd] = EXTEND32 (result);
}
// round: 0 = no rounding, 1 = rounding
:function:::void:do_precr_sra:int rt, int rs, int sa, int round
{
  unsigned32 v1 = GPR[rt];
  unsigned32 v2 = GPR[rs];
  signed32 w1 = (signed32) v1;
  signed32 w2 = (signed32) v2;
  signed32 result;
  if (sa != 0)
    {
      if (round == 1 && (w1 & (1 << (sa - 1))))
	w1 = (w1 >> sa) + 1;
      else
	w1 = w1 >> sa;
      if (round == 1 && (w2 & (1 << (sa - 1))))
	w2 = (w2 >> sa) + 1;
      else
	w2 = w2 >> sa;
    }
  result = (w1 << 16) | (w2 & 0xffff);
  GPR[rt] = EXTEND32 (result);
}
// round: 0 = no rounding, 1 = rounding
:function:::void:do_qb_shra:int rd, int rt, int shift, int round
{
  int i, j;
  signed8 q0;
  unsigned32 v1 = GPR[rt];
  unsigned32 result = 0;
  for (i = 0; i < 32; i += 8, v1 >>= 8)
    {
      q0 = (signed8)(v1 & 0xff);
      if (shift != 0)
 	{
	  if (round == 1 && (q0 & (1 << (shift - 1))))
	    q0 = (q0 >> shift) + 1;
	  else
	    q0 = q0 >> shift;
 	}
      result |= ((unsigned32)((unsigned8)q0) << i);
    }
  GPR[rd] = EXTEND32 (result);
}
:function:::void:do_ph_shrl:int rd, int rt, int shift
{
  int i, j;
  unsigned16 h0;
  unsigned32 v1 = GPR[rt];
  unsigned32 result = 0;
  for (i = 0; i < 32; i += 16, v1 >>= 16)
    {
      h0 = (unsigned16)(v1 & 0xffff);
      h0 = h0 >> shift;
      result |= ((unsigned32)h0 << i);
    }
  GPR[rd] = EXTEND32 (result);
}
// op: 0 = ADD, 1 = SUB
// round: 0 = no rounding, 1 = rounding
:function:::void:do_qh_ph_op:int rd, int rs, int rt, int op, int round
{
  int i;
  signed32 h0;
  signed16 h1, h2;
  unsigned32 v1 = GPR[rs];
  unsigned32 v2 = GPR[rt];
  unsigned32 result = 0;
  for (i = 0; i < 32; i += 16, v1 >>= 16, v2 >>= 16)
    {
      h1 = (signed16)(v1 & 0xffff);
      h2 = (signed16)(v2 & 0xffff);
      if (op == 0) // ADD
	h0 = (signed32)h1 + (signed32)h2;
      else // SUB
	h0 = (signed32)h1 - (signed32)h2;
      if (round == 1)
	h0 = (h0 + 1) >> 1;
      else
	h0 = h0 >> 1;
      result |= ((unsigned32)((unsigned16)h0) << i);
    }
  GPR[rd] = EXTEND32 (result);
}
// op: 0 = ADD, 1 = SUB
// round: 0 = no rounding, 1 = rounding
:function:::void:do_qh_w_op:int rd, int rs, int rt, int op, int round
{
  int i;
  signed64 v0;
  signed32 v1 = (signed32)GPR[rs];
  signed32 v2 = (signed32)GPR[rt];
  if (op == 0) // ADD
    v0 = (signed64)v1 + (signed64)v2;
  else // SUB
    v0 = (signed64)v1 - (signed64)v2;
  if (round == 1)
    v0 = (v0 + 1) >> 1;
  else
    v0 = v0 >> 1;
  GPR[rd] = EXTEND32 (v0);
}
// op: 0 = DPAX, 1 = DPSX
:function:::void:do_x_w_ph_dot_product:int ac, int rs, int rt, int op
{
  int i;
  unsigned32 v1 = GPR[rs];
  unsigned32 v2 = GPR[rt];
  signed16 h1, h2;
  signed32 result;
  unsigned32 lo = DSPLO(ac);
  unsigned32 hi = DSPHI(ac);
  signed64 prod = (signed64)((((unsigned64)hi) << 32) + (unsigned64)lo);
  for (i = 0; i < 32; i += 16, v1 >>= 16, v2 <<= 16)
    {
      h1 = (signed16)(v1 & 0xffff);
      h2 = (signed16)((v2 & 0xffff0000) >> 16);
      result = (signed32)h1 * (signed32)h2;
      if (op == 0) // DPAX
        prod += (signed64)result;
      else // DPSX
        prod -= (signed64)result;
    }
  DSPLO(ac) = EXTEND32 (prod);
  DSPHI(ac) = EXTEND32 (prod >> 32);
}
// op: 0 = DPAQX, 1 = DPSQX
// sat: 0 = no saturation, 1 = saturation of the accumulator
:function:::void:do_qx_w_ph_dot_product:int ac, int rs, int rt, int op, int sat
{
  int i;
  unsigned32 v1 = GPR[rs];
  unsigned32 v2 = GPR[rt];
  signed16 h1, h2;
  signed32 result;
  unsigned32 lo = DSPLO(ac);
  unsigned32 hi = DSPHI(ac);
  signed64 prod = (signed64)((((unsigned64)hi) << 32) + (unsigned64)lo);
  signed64 max, min;
  for (i = 0; i < 32; i += 16, v1 >>= 16, v2 <<= 16)
    {
      h1 = (signed16)(v1 & 0xffff);
      h2 = (signed16)((v2 & 0xffff0000) >> 16);
      if (h1 == (signed16)0x8000 && h2 == (signed16)0x8000)
	{
	  DSPCR |= (1 << (DSPCR_OUFLAG_SHIFT + ac));
	  result = 0x7fffffff;
	}
      else
	result = ((signed32)h1 * (signed32)h2) << 1;
      if (op == 0) // DPAQX
        prod += (signed64)result;
      else // DPSQX
        prod -= (signed64)result;
    }
  // Saturation on the accumulator.
  if (sat == 1)
    {
      max = (signed64) 0x7fffffffLL;
      min = (signed64) 0xffffffff80000000LL;
      if (prod > max)
	{
	  DSPCR |= (1 << (DSPCR_OUFLAG_SHIFT + ac));
	  prod = max;
	}
      else if (prod < min)
	{
	  DSPCR |= (1 << (DSPCR_OUFLAG_SHIFT + ac));
	  prod = min;
	}
    }
  DSPLO(ac) = EXTEND32 (prod);
  DSPHI(ac) = EXTEND32 (prod >> 32);
}
011111,00000,5.RT,5.RD,00001,010010:SPECIAL3:32::ABSQ_S.QB
"absq_s.qb r, r