//
// rsp/vfunctions.c: RSP vector execution functions.
//
// CEN64: Cycle-Accurate Nintendo 64 Emulator.
// Copyright (C) 2015, Tyler J. Stachecki.
//
// This file is subject to the terms and conditions defined in
// 'LICENSE', which is part of this source code package.
//

#define RSP_BUILD_OP(op, func, flags) \
  (RSP_##func)

#include "common.h"
#include "rsp/cpu.h"
#include "rsp/opcodes.h"
#include "rsp/opcodes_priv.h"
#include "rsp/rsp.h"

//
// VABS
//
rsp_vect_t RSP_VABS(struct rsp *rsp, uint32_t iw,
  rsp_vect_t vt_shuffle, rsp_vect_t vs, rsp_vect_t zero) {
  uint16_t *acc = rsp->cp2.acc.e;
  rsp_vect_t acc_lo;

  rsp_vect_t result = rsp_vabs(vs, vt_shuffle, zero, &acc_lo);

  write_acc_lo(acc, acc_lo);
  return result;
}

//
// VADD
//
rsp_vect_t RSP_VADD(struct rsp *rsp, uint32_t iw,
  rsp_vect_t vt_shuffle, rsp_vect_t vs, rsp_vect_t zero) {
  uint16_t *acc = rsp->cp2.acc.e;
  rsp_vect_t carry, acc_lo;

  carry = read_vco_lo(rsp->cp2.flags[RSP_VCO].e);

  rsp_vect_t result = rsp_vadd(vs, vt_shuffle, carry, &acc_lo);

  write_vco_hi(rsp->cp2.flags[RSP_VCO].e, zero);
  write_vco_lo(rsp->cp2.flags[RSP_VCO].e, zero);
  write_acc_lo(acc, acc_lo);
  return result;
}

//
// VADDC
//
rsp_vect_t RSP_VADDC(struct rsp *rsp, uint32_t iw,
  rsp_vect_t vt_shuffle, rsp_vect_t vs, rsp_vect_t zero) {
  uint16_t *acc = rsp->cp2.acc.e;
  rsp_vect_t sn;

  rsp_vect_t result = rsp_vaddc(vs, vt_shuffle, zero, &sn);

  write_vco_hi(rsp->cp2.flags[RSP_VCO].e, zero); // TODO: Confirm.
  write_vco_lo(rsp->cp2.flags[RSP_VCO].e, sn);
  write_acc_lo(acc, result);
  return result;
}

//
// VAND
// VNAND
//
rsp_vect_t RSP_VAND_VNAND(struct rsp *rsp, uint32_t iw,
  rsp_vect_t vt_shuffle, rsp_vect_t vs, rsp_vect_t zero) {
  uint16_t *acc = rsp->cp2.acc.e;

  rsp_vect_t result = rsp_vand_vnand(iw, vs, vt_shuffle);

  write_acc_lo(acc, result);
  return result;
}

//
// VCH
//
rsp_vect_t RSP_VCH(struct rsp *rsp, uint32_t iw,
  rsp_vect_t vt_shuffle, rsp_vect_t vs, rsp_vect_t zero) {
  uint16_t *acc = rsp->cp2.acc.e;
  rsp_vect_t ge, le, sign, eq, vce;

  rsp_vect_t result = rsp_vch(vs, vt_shuffle, zero, &ge, &le, &eq, &sign, &vce);

  write_vcc_hi(rsp->cp2.flags[RSP_VCC].e, ge);
  write_vcc_lo(rsp->cp2.flags[RSP_VCC].e, le);
  write_vco_hi(rsp->cp2.flags[RSP_VCO].e, eq);
  write_vco_lo(rsp->cp2.flags[RSP_VCO].e, sign);
  write_vce   (rsp->cp2.flags[RSP_VCE].e, vce);
  write_acc_lo(acc, result);
  return result;
}

//
// VCL
//
rsp_vect_t RSP_VCL(struct rsp *rsp, uint32_t iw,
  rsp_vect_t vt_shuffle, rsp_vect_t vs, rsp_vect_t zero) {
  uint16_t *acc = rsp->cp2.acc.e;
  rsp_vect_t ge, le, eq, sign, vce;

  ge = read_vcc_hi(rsp->cp2.flags[RSP_VCC].e);
  le = read_vcc_lo(rsp->cp2.flags[RSP_VCC].e);
  eq = read_vco_hi(rsp->cp2.flags[RSP_VCO].e);
  sign = read_vco_lo(rsp->cp2.flags[RSP_VCO].e);
  vce = read_vce(rsp->cp2.flags[RSP_VCE].e);

  rsp_vect_t result = rsp_vcl(vs, vt_shuffle, zero, &ge, &le, eq, sign, vce);

  write_vcc_hi(rsp->cp2.flags[RSP_VCC].e, ge);
  write_vcc_lo(rsp->cp2.flags[RSP_VCC].e, le);
  write_vco_hi(rsp->cp2.flags[RSP_VCO].e, zero);
  write_vco_lo(rsp->cp2.flags[RSP_VCO].e, zero);
  write_vce   (rsp->cp2.flags[RSP_VCE].e, zero);
  write_acc_lo(acc, result);
  return result;
}

//
// VCR
//
rsp_vect_t RSP_VCR(struct rsp *rsp, uint32_t iw,
  rsp_vect_t vt_shuffle, rsp_vect_t vs, rsp_vect_t zero) {
  uint16_t *acc = rsp->cp2.acc.e;
  rsp_vect_t ge, le;

  rsp_vect_t result = rsp_vcr(vs, vt_shuffle, zero, &ge, &le);

  write_vcc_hi(rsp->cp2.flags[RSP_VCC].e, ge);
  write_vcc_lo(rsp->cp2.flags[RSP_VCC].e, le);
  write_vco_hi(rsp->cp2.flags[RSP_VCO].e, zero);
  write_vco_lo(rsp->cp2.flags[RSP_VCO].e, zero);
  write_vce   (rsp->cp2.flags[RSP_VCE].e, zero);
  write_acc_lo(acc, result);
  return result;
}

//
// VEQ
// VGE
// VLT
// VNE
//
rsp_vect_t RSP_VEQ_VGE_VLT_VNE(struct rsp *rsp, uint32_t iw,
  rsp_vect_t vt_shuffle, rsp_vect_t vs, rsp_vect_t zero) {
  uint16_t *acc = rsp->cp2.acc.e;
  rsp_vect_t le, eq, sign;

  eq = read_vco_hi(rsp->cp2.flags[RSP_VCO].e);
  sign = read_vco_lo(rsp->cp2.flags[RSP_VCO].e);

  rsp_vect_t result = rsp_veq_vge_vlt_vne(iw, vs, vt_shuffle,
    zero, &le, eq, sign);

  write_vcc_hi(rsp->cp2.flags[RSP_VCC].e, zero);
  write_vcc_lo(rsp->cp2.flags[RSP_VCC].e, le);
  write_vco_hi(rsp->cp2.flags[RSP_VCO].e, zero);
  write_vco_lo(rsp->cp2.flags[RSP_VCO].e, zero);
  write_acc_lo(acc, result);
  return result;
}

//
// VINVALID
//
rsp_vect_t RSP_VINVALID(struct rsp *rsp, uint32_t iw,
  rsp_vect_t vt_shuffle, rsp_vect_t vs, rsp_vect_t zero) {
#ifndef NDEBUG
  struct rsp_rdex_latch *rdex_latch = &rsp->pipeline.rdex_latch;

  debug("Unimplemented instruction: %s [0x%.8X] @ 0x%.8X\n",
    rsp_vector_opcode_mnemonics[rdex_latch->opcode.id],
    iw, rdex_latch->common.pc);
#endif

  return zero;
}

//
// VMACF
// VMACU
//
rsp_vect_t RSP_VMACF_VMACU(struct rsp *rsp, uint32_t iw,
  rsp_vect_t vt_shuffle, rsp_vect_t vs, rsp_vect_t zero) {
  uint16_t *acc = rsp->cp2.acc.e;
  rsp_vect_t acc_lo, acc_md, acc_hi, result;
  acc_lo = read_acc_lo(acc);
  acc_md = read_acc_md(acc);
  acc_hi = read_acc_hi(acc);

  result = rsp_vmacf_vmacu(iw, vs, vt_shuffle, zero, &acc_lo, &acc_md, &acc_hi);

  write_acc_lo(acc, acc_lo);
  write_acc_md(acc, acc_md);
  write_acc_hi(acc, acc_hi);
  return result;
}

//
// VMADH
// VMUDH
//
rsp_vect_t RSP_VMADH_VMUDH(struct rsp *rsp, uint32_t iw,
  rsp_vect_t vt_shuffle, rsp_vect_t vs, rsp_vect_t zero) {
  uint16_t *acc = rsp->cp2.acc.e;
  rsp_vect_t acc_lo, acc_md, acc_hi, result;

  acc_lo = read_acc_lo(acc);
  acc_md = read_acc_md(acc);
  acc_hi = read_acc_hi(acc);

  result = rsp_vmadh_vmudh(iw, vs, vt_shuffle, zero, &acc_lo, &acc_md, &acc_hi);

  write_acc_lo(acc, acc_lo);
  write_acc_md(acc, acc_md);
  write_acc_hi(acc, acc_hi);
  return result;
}

//
// VMADL
// VMUDL
//
rsp_vect_t RSP_VMADL_VMUDL(struct rsp *rsp, uint32_t iw,
  rsp_vect_t vt_shuffle, rsp_vect_t vs, rsp_vect_t zero) {
  uint16_t *acc = rsp->cp2.acc.e;
  rsp_vect_t acc_lo, acc_md, acc_hi, result;

  acc_lo = read_acc_lo(acc);
  acc_md = read_acc_md(acc);
  acc_hi = read_acc_hi(acc);

  result = rsp_vmadl_vmudl(iw, vs, vt_shuffle, zero, &acc_lo, &acc_md, &acc_hi);

  write_acc_lo(acc, acc_lo);
  write_acc_md(acc, acc_md);
  write_acc_hi(acc, acc_hi);
  return result;
}

//
// VMADM
// VMUDM
//
rsp_vect_t RSP_VMADM_VMUDM(struct rsp *rsp, uint32_t iw,
  rsp_vect_t vt_shuffle, rsp_vect_t vs, rsp_vect_t zero) {
  uint16_t *acc = rsp->cp2.acc.e;
  rsp_vect_t acc_lo, acc_md, acc_hi, result;

  acc_lo = read_acc_lo(acc);
  acc_md = read_acc_md(acc);
  acc_hi = read_acc_hi(acc);

  result = rsp_vmadm_vmudm(iw, vs, vt_shuffle, zero, &acc_lo, &acc_md, &acc_hi);

  write_acc_lo(acc, acc_lo);
  write_acc_md(acc, acc_md);
  write_acc_hi(acc, acc_hi);
  return result;
}

//
// VMADN
// VMUDN
//
rsp_vect_t RSP_VMADN_VMUDN(struct rsp *rsp, uint32_t iw,
  rsp_vect_t vt_shuffle, rsp_vect_t vs, rsp_vect_t zero) {
  uint16_t *acc = rsp->cp2.acc.e;
  rsp_vect_t acc_lo, acc_md, acc_hi, result;

  acc_lo = read_acc_lo(acc);
  acc_md = read_acc_md(acc);
  acc_hi = read_acc_hi(acc);

  result = rsp_vmadn_vmudn(iw, vs, vt_shuffle, zero, &acc_lo, &acc_md, &acc_hi);

  write_acc_lo(acc, acc_lo);
  write_acc_md(acc, acc_md);
  write_acc_hi(acc, acc_hi);
  return result;
}

//
// VMOV
//
rsp_vect_t RSP_VMOV(struct rsp *rsp, uint32_t iw,
  rsp_vect_t vt_shuffle, rsp_vect_t vs, rsp_vect_t zero) {
  uint16_t *acc = rsp->cp2.acc.e;
  unsigned de = GET_DE(iw) & 0x7;
  unsigned e = GET_E(iw) & 0x7;

  unsigned dest = GET_VD(iw);
  unsigned src = GET_VT(iw);

  write_acc_lo(acc, vt_shuffle);

  return rsp_vmov(rsp, src, e, dest, de);
}

//
// VMRG
//
rsp_vect_t RSP_VMRG(struct rsp *rsp, uint32_t iw,
  rsp_vect_t vt_shuffle, rsp_vect_t vs, rsp_vect_t zero) {
  uint16_t *acc = rsp->cp2.acc.e;
  rsp_vect_t le;

  le = read_vcc_lo(rsp->cp2.flags[RSP_VCC].e);

  rsp_vect_t result = rsp_vmrg(vs, vt_shuffle, le);

  write_vco_hi(rsp->cp2.flags[RSP_VCO].e, zero);
  write_vco_lo(rsp->cp2.flags[RSP_VCO].e, zero);
  write_acc_lo(acc, result);
  return result;
}

//
// VMULF
// VMULU
//
rsp_vect_t RSP_VMULF_VMULU(struct rsp *rsp, uint32_t iw,
  rsp_vect_t vt_shuffle, rsp_vect_t vs, rsp_vect_t zero) {
  uint16_t *acc = rsp->cp2.acc.e;
  rsp_vect_t acc_lo, acc_md, acc_hi, result;

  result = rsp_vmulf_vmulu(iw, vs, vt_shuffle, zero, &acc_lo, &acc_md, &acc_hi);

  write_acc_lo(acc, acc_lo);
  write_acc_md(acc, acc_md);
  write_acc_hi(acc, acc_hi);
  return result;
}

//
// VNOP
//
rsp_vect_t RSP_VNOP(struct rsp *rsp, uint32_t iw,
  rsp_vect_t vt_shuffle, rsp_vect_t vs, rsp_vect_t zero) {
  return vs;
}

//
// VOR
// VNOR
//
rsp_vect_t RSP_VOR_VNOR(struct rsp *rsp, uint32_t iw,
  rsp_vect_t vt_shuffle, rsp_vect_t vs, rsp_vect_t zero) {
  uint16_t *acc = rsp->cp2.acc.e;

  rsp_vect_t result = rsp_vor_vnor(iw, vs, vt_shuffle);

  write_acc_lo(acc, result);
  return result;
}

//
// VRCP
// VRCPL
// VRSQ
// VRSQL
//
rsp_vect_t RSP_VRCP_VRSQ(struct rsp *rsp, uint32_t iw,
  rsp_vect_t vt_shuffle, rsp_vect_t vs, rsp_vect_t zero) {
  uint16_t *acc = rsp->cp2.acc.e;
  unsigned de = GET_DE(iw) & 0x7;
  unsigned e = GET_E(iw) & 0x7;

  unsigned dest = GET_VD(iw);
  unsigned src = GET_VT(iw);

  write_acc_lo(acc, vt_shuffle);

  // Force single precision for VRCP (but not VRCPL).
  int dp = iw & rsp->cp2.dp_flag;
  rsp->cp2.dp_flag = 0;

  return rsp_vrcp_vrsq(rsp, iw, dp, src, e, dest, de);
}

//
// VRCPH
// VRSQH
//
rsp_vect_t RSP_VRCPH_VRSQH(struct rsp *rsp, uint32_t iw,
  rsp_vect_t vt_shuffle, rsp_vect_t vs, rsp_vect_t zero) {
  uint16_t *acc = rsp->cp2.acc.e;
  unsigned de = GET_DE(iw) & 0x7;
  unsigned e = GET_E(iw) & 0x7;

  unsigned dest = GET_VD(iw);
  unsigned src = GET_VT(iw);

  write_acc_lo(acc, vt_shuffle);

  // Specify double-precision for VRCPL on the next pass.
  rsp->cp2.dp_flag = 1;

  return rsp_vdivh(rsp, src, e, dest, de);
}

//
// VSAR
//
rsp_vect_t RSP_VSAR(struct rsp *rsp, uint32_t iw,
  rsp_vect_t vt_shuffle, rsp_vect_t vs, rsp_vect_t zero) {
  uint16_t *acc = rsp->cp2.acc.e;
  unsigned e = GET_E(iw);

  switch (e) {
    case 8: return read_acc_hi(acc);
    case 9: return read_acc_md(acc);
    case 10: return read_acc_lo(acc);

    default:
      return zero;
  }

  return zero;
}

//
// VSUB
//
rsp_vect_t RSP_VSUB(struct rsp *rsp, uint32_t iw,
  rsp_vect_t vt_shuffle, rsp_vect_t vs, rsp_vect_t zero) {
  uint16_t *acc = rsp->cp2.acc.e;
  rsp_vect_t carry, acc_lo;

  carry = read_vco_lo(rsp->cp2.flags[RSP_VCO].e);

  rsp_vect_t result = rsp_vsub(vs, vt_shuffle, carry, &acc_lo);

  write_vco_hi(rsp->cp2.flags[RSP_VCO].e, zero);
  write_vco_lo(rsp->cp2.flags[RSP_VCO].e, zero);
  write_acc_lo(acc, acc_lo);
  return result;
}

//
// VSUBC
//
rsp_vect_t RSP_VSUBC(struct rsp *rsp, uint32_t iw,
  rsp_vect_t vt_shuffle, rsp_vect_t vs, rsp_vect_t zero) {
  uint16_t *acc = rsp->cp2.acc.e;
  rsp_vect_t eq, sn;

  rsp_vect_t result = rsp_vsubc(vs, vt_shuffle, zero, &eq, &sn);

  write_vco_hi(rsp->cp2.flags[RSP_VCO].e, eq);
  write_vco_lo(rsp->cp2.flags[RSP_VCO].e, sn);
  write_acc_lo(acc, result);
  return result;
}

//
// VXOR
// VNXOR
//
rsp_vect_t RSP_VXOR_VNXOR(struct rsp *rsp, uint32_t iw,
  rsp_vect_t vt_shuffle, rsp_vect_t vs, rsp_vect_t zero) {
  uint16_t *acc = rsp->cp2.acc.e;

  rsp_vect_t result = rsp_vxor_vnxor(iw, vs, vt_shuffle);

  write_acc_lo(acc, result);
  return result;
}

// Function lookup table.
cen64_align(const rsp_vector_function
  rsp_vector_function_table[NUM_RSP_VECTOR_OPCODES], CACHE_LINE_SIZE) = {
#define X(op) op,
#include "rsp/vector_opcodes.md"
#undef X
};

