Top secrets sources NedoPC pentevo

Rev

Blame | Last modification | View Log | Download | RSS feed | ?url?

/* bpemu.c */
/*****************************************************************************/
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only                     */
/*                                                                           */
/* AS-Portierung                                                             */
/*                                                                           */
/* 64 bit arithmetic for platforms not having a 64 bit integer               */
/*                                                                           */
/*****************************************************************************/

#include <assert.h>

#include "stdinc.h"
#include "math64.h"

/*!------------------------------------------------------------------------
 * \fn     nlz16(Word n)
 * \brief  find # of tleading zeros
 * \param  n 16-bit word to examine
 * \return # of leading zeros
 * ------------------------------------------------------------------------ */


static int nlz16(Word n)
{
  unsigned z;
  for (z = 0; z < 16; z++)
  {
    if (n & 0x8000u)
      break;
    n = n << 1;
  }
  return z;
}

/*!------------------------------------------------------------------------
 * \fn     to16(Word *pOut, const t64 *pIn)
 * \brief  Convert t64 to array of uint16 needed by mul/div
 * \param  pOut destination array
 * \param  pIn source value
 * \return # of words written (1..4)
 * ------------------------------------------------------------------------ */


static int to16(Word *pOut, const t64 *pIn)
{
  int res = 0;

  pOut[res++] = pIn->low & 0xffff;
  pOut[res++] = (pIn->low >> 16) & 0xffff;
  pOut[res++] = pIn->high & 0xffff;
  pOut[res++] = (pIn->high >> 16) & 0xffff;
  while ((res > 1) && !pOut[res - 1])
    res--;
  return res;
}

/*!------------------------------------------------------------------------
 * \fn     from16(t64 *pOut, const Word *pIn)
 * \brief  convert uint16 array back to t64
 * \param  pOut value to reconstruct
 * \param  pIn uint16 array
 * ------------------------------------------------------------------------ */


static void from16(t64 *pOut, const Word *pIn)
{
  pOut->low = pIn[1];
  pOut->low = (pOut->low << 16) | pIn[0];
  pOut->high = pIn[3];
  pOut->high = (pOut->high << 16) | pIn[2];
}

/*!------------------------------------------------------------------------
 * \fn     mulmnu(Word w[], Word u[], Word v[], int m, int n)
 * \brief  unsigned multi-precision multiply (taken from Hacker's Delight)
 * \param  w result buffer
 * \param  u, v numbers to multiply
 * \param  m length of u in words
 * \param  n length of v in words
 * ------------------------------------------------------------------------ */


void mulmnu(Word w[], Word u[], Word v[], int m, int n)
{
  unsigned int k, t;
  int i, j;

  for (i = 0; i < m; i++)
    w[i] = 0;

  for (j = 0; j < n; j++)
  {
    k = 0;
    for (i = 0; i < m; i++)
    {
      t = u[i]*v[j] + w[i + j] + k;
      w[i + j] = t;          /* (I.e., t & 0xFFFF). */
      k = t >> 16;
    }
    w[j + m] = k;
  }
}

/*!------------------------------------------------------------------------
 * \fn     mulmnu(Word q[], Word r[], Word u[], Word v[], int m, int n)
 * \brief  unsigned multi-precision divide (taken from Hacker's Delight)
 * \param  q quotient buffer
 * \param  r remainder buffer (may be NULL)r
 * \param  u divident
 * \param  v divisor
 * \param  m length of u in words
 * \param  n length of v in words
 * \return 0 on success, 1 on error
 * ------------------------------------------------------------------------ */


static int divmnu(Word q[], Word r[], const Word u[], const Word v[], int m, int n)
{
  const unsigned b = 65536; /* Number base (16 bits). */
  Word un[16], vn[16]; /* Normalized form of u, v. */
  unsigned qhat;   /* Estimated quotient digit. */
  unsigned rhat;   /* A remainder. */
  unsigned p;      /* Product of two digits. */
  int s, i, j, t, k;

  /* Return if invalid param. */

  if (m < n || n <= 0 || v[n - 1] == 0)
    return 1;

  /* Take care of the case of a single-digit divisor here. */

  if (n == 1)
  {
    k = 0;
    for (j = m - 1; j >= 0; j--)
    {
      q[j] = (k*b + u[j]) / v[0];
      k = (k * b + u[j]) - q[j] * v[0];
    }
    if (r != NULL) r[0] = k;
    return 0;
  }

  /* Normalize by shifting v left just enough so that
     its high-order bit is on, and shift u left the
     same amount. We may have to append a high-order
     digit on the dividend; we do that unconditionally. */


  s = nlz16(v[n - 1]); /* 0 <= s <= 16. */
  assert(n < 16); /* vn = (Word *)alloca(2 * n); */
  for (i = n - 1; i > 0; i--)
    vn[i] = (v[i] << s) | (v[i - 1] >> (16 - s));
  vn[0] = v[0] << s;
  assert(m + 1 < 16); /* un = (Word *)alloca(2 * (m + 1)); */
  un[m] = u[m-1] >> (16 - s);
  for (i = m - 1; i > 0; i--)
    un[i] = (u[i] << s) | (u[i - 1] >> (16 - s));
  un[0] = u[0] << s;

  /* Main loop. */

  for (j = m - n; j >= 0; j--)
  {
    /* Compute estimate qhat of q[j]. */

    qhat = (un[j+n]*b + un[j+n-1])/vn[n-1];
    rhat = (un[j+n]*b + un[j+n-1]) - qhat*vn[n-1];
  again:
    if (qhat >= b || qhat*vn[n-2] > b*rhat + un[j+n-2])
    {
      qhat = qhat - 1;
      rhat = rhat + vn[n-1];
      if (rhat < b) goto again;
    }

    /* Multiply and subtract. */

    k = 0;
    for (i = 0; i < n; i++)
    {
      p = qhat*vn[i];
      t = un[i + j] - k - (p & 0xFFFF);
      un[i+j] = t;
      k = (p >> 16) - (t >> 16);
    }
    t = un[j+n] - k;
    un[j + n] = t;

    /* Store quotient digit. */

    q[j] = qhat;

    /* If we subtracted too much, add back. */

    if (t < 0)
    {
      q[j] = q[j] - 1;
      k = 0;
      for (i = 0; i < n; i++)
      {
        t = un[i + j] + vn[i] + k;
        un[i + j] = t;
        k = t >> 16;
      }
      un[j+n] = un[j+n] + k;
    }
  } /* End j. */

  /* If the caller wants the remainder, unnormalize
     it and pass it back. */


  if (r != NULL)
  {
    for (i = 0; i < n; i++)
      r[i] = (un[i] >> s) | (un[i + 1] << (16 - s));
  }
  return 0;
}

/*!------------------------------------------------------------------------
 * \fn     add64(t64 *pRes, const t64 *pA, const t64 *pB)
 * \brief  add two values
 * \param  pRes sum buffer
 * \param  pA, pB values to add
 * ------------------------------------------------------------------------ */


void add64(t64 *pRes, const t64 *pA, const t64 *pB)
{
  pRes->low = pA->low + pB->low;
  pRes->high = pA->high + pB->high + !!(pRes->low < pA->low);
}

/*!------------------------------------------------------------------------
 * \fn     sub64(t64 *pRes, const t64 *pA, const t64 *pB)
 * \brief  subtract two values
 * \param  pRes difference buffer
 * \param  pA, pB values to subtract
 * ------------------------------------------------------------------------ */


void sub64(t64 *pRes, const t64 *pA, const t64 *pB)
{
  pRes->low = pA->low - pB->low;
  pRes->high = pA->high - pB->high - !!(pRes->low > pA->low);
}

/*!------------------------------------------------------------------------
 * \fn     mul64(t64 *pRes, const t64 *pA, const t64 *pB)
 * \brief  multiply two values
 * \param  pRes product buffer
 * \param  pA, pB values to multiply
 * ------------------------------------------------------------------------ */


void mul64(t64 *pRes, const t64 *pA, const t64 *pB)
{
  Word u[4], v[4], w[8];
  int m = to16(u, pA),
      n = to16(v, pB);

  mulmnu(w, u, v, m, n);
  from16(pRes, w);
}

/*!------------------------------------------------------------------------
 * \fn     div64(t64 *pRes, const t64 *pA, const t64 *pB)
 * \brief  divide two values
 * \param  pRes quotient buffer
 * \param  pA, pB values to divide
 * ------------------------------------------------------------------------ */


void div64(t64 *pRes, const t64 *pA, const t64 *pB)
{
  Word u[4], v[4], q[4];
  int m = to16(u, pA),
      n = to16(v, pB);

  q[0] = q[1] = q[2] = q[3] = 0;
  divmnu(q, NULL, u, v, m, n);
  from16(pRes, q);
}

/*!------------------------------------------------------------------------
 * \fn     mod64(t64 *pRes, const t64 *pA, const t64 *pB)
 * \brief  modulo-divide two values
 * \param  pRes remainder buffer
 * \param  pA, pB values to divide
 * ------------------------------------------------------------------------ */


void mod64(t64 *pRes, const t64 *pA, const t64 *pB)
{
  Word u[4], v[4], q[4], r[4];
  int m = to16(u, pA),
      n = to16(v, pB);

  q[0] = q[1] = q[2] = q[3] = 0;
  divmnu(q, r, u, v, m, n);
  from16(pRes, r);
}

/*!------------------------------------------------------------------------
 * \fn     mod_div64(t64 *pQuot, t64 *pRem, const t64 *pA, const t64 *pB)
 * \brief  divide two values, and return also remainder
 * \param  pQuot quotient buffer
 * \param  pRem remainder buffer
 * \param  pA, pB values to divide
 * ------------------------------------------------------------------------ */


void mod_div64(t64 *pQuot, t64 *pRem, const t64 *pA, const t64 *pB)
{
  Word u[4], v[4], q[4], r[4];
  int m = to16(u, pA),
      n = to16(v, pB);

  q[0] = q[1] = q[2] = q[3] = 0;
  divmnu(q, r, u, v, m, n);
  from16(pQuot, q);
  from16(pRem, r);
}