* THE SOFTWARE.
*/
-#include <stdlib.h>
-#include <stdint.h>
+#include "qemu/osdep.h"
#include "qemu/host-utils.h"
-//#define DEBUG_MULDIV
-
/* Long integer helpers */
-#if !defined(__x86_64__)
-static void add128 (uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b)
+static inline void mul64(uint64_t *plow, uint64_t *phigh,
+ uint64_t a, uint64_t b)
{
- *plow += a;
- /* carry test */
- if (*plow < a)
- (*phigh)++;
- *phigh += b;
+ typedef union {
+ uint64_t ll;
+ struct {
+#ifdef HOST_WORDS_BIGENDIAN
+ uint32_t high, low;
+#else
+ uint32_t low, high;
+#endif
+ } l;
+ } LL;
+ LL rl, rm, rn, rh, a0, b0;
+ uint64_t c;
+
+ a0.ll = a;
+ b0.ll = b;
+
+ rl.ll = (uint64_t)a0.l.low * b0.l.low;
+ rm.ll = (uint64_t)a0.l.low * b0.l.high;
+ rn.ll = (uint64_t)a0.l.high * b0.l.low;
+ rh.ll = (uint64_t)a0.l.high * b0.l.high;
+
+ c = (uint64_t)rl.l.high + rm.l.low + rn.l.low;
+ rl.l.high = c;
+ c >>= 32;
+ c = c + rm.l.high + rn.l.high + rh.l.low;
+ rh.l.low = c;
+ rh.l.high += (uint32_t)(c >> 32);
+
+ *plow = rl.ll;
+ *phigh = rh.ll;
}
-static void neg128 (uint64_t *plow, uint64_t *phigh)
+/* Unsigned 64x64 -> 128 multiplication */
+void mulu64 (uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b)
{
- *plow = ~*plow;
- *phigh = ~*phigh;
- add128(plow, phigh, 1, 0);
+ mul64(plow, phigh, a, b);
}
-static void mul64 (uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b)
+/* Signed 64x64 -> 128 multiplication */
+void muls64 (uint64_t *plow, uint64_t *phigh, int64_t a, int64_t b)
{
- uint32_t a0, a1, b0, b1;
- uint64_t v;
+ uint64_t rh;
- a0 = a;
- a1 = a >> 32;
+ mul64(plow, &rh, a, b);
- b0 = b;
- b1 = b >> 32;
+ /* Adjust for signs. */
+ if (b < 0) {
+ rh -= a;
+ }
+ if (a < 0) {
+ rh -= b;
+ }
+ *phigh = rh;
+}
- v = (uint64_t)a0 * (uint64_t)b0;
- *plow = v;
- *phigh = 0;
+/* Unsigned 128x64 division. Returns 1 if overflow (divide by zero or */
+/* quotient exceeds 64 bits). Otherwise returns quotient via plow and */
+/* remainder via phigh. */
+int divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor)
+{
+ uint64_t dhi = *phigh;
+ uint64_t dlo = *plow;
+ unsigned i;
+ uint64_t carry = 0;
- v = (uint64_t)a0 * (uint64_t)b1;
- add128(plow, phigh, v << 32, v >> 32);
+ if (divisor == 0) {
+ return 1;
+ } else if (dhi == 0) {
+ *plow = dlo / divisor;
+ *phigh = dlo % divisor;
+ return 0;
+ } else if (dhi > divisor) {
+ return 1;
+ } else {
- v = (uint64_t)a1 * (uint64_t)b0;
- add128(plow, phigh, v << 32, v >> 32);
+ for (i = 0; i < 64; i++) {
+ carry = dhi >> 63;
+ dhi = (dhi << 1) | (dlo >> 63);
+ if (carry || (dhi >= divisor)) {
+ dhi -= divisor;
+ carry = 1;
+ } else {
+ carry = 0;
+ }
+ dlo = (dlo << 1) | carry;
+ }
- v = (uint64_t)a1 * (uint64_t)b1;
- *phigh += v;
+ *plow = dlo;
+ *phigh = dhi;
+ return 0;
+ }
}
-/* Unsigned 64x64 -> 128 multiplication */
-void mulu64 (uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b)
+int divs128(int64_t *plow, int64_t *phigh, int64_t divisor)
{
- mul64(plow, phigh, a, b);
-#if defined(DEBUG_MULDIV)
- printf("mulu64: 0x%016llx * 0x%016llx = 0x%016llx%016llx\n",
- a, b, *phigh, *plow);
-#endif
-}
+ int sgn_dvdnd = *phigh < 0;
+ int sgn_divsr = divisor < 0;
+ int overflow = 0;
-/* Signed 64x64 -> 128 multiplication */
-void muls64 (uint64_t *plow, uint64_t *phigh, int64_t a, int64_t b)
-{
- int sa, sb;
-
- sa = (a < 0);
- if (sa)
- a = -a;
- sb = (b < 0);
- if (sb)
- b = -b;
- mul64(plow, phigh, a, b);
- if (sa ^ sb) {
- neg128(plow, phigh);
+ if (sgn_dvdnd) {
+ *plow = ~(*plow);
+ *phigh = ~(*phigh);
+ if (*plow == (int64_t)-1) {
+ *plow = 0;
+ (*phigh)++;
+ } else {
+ (*plow)++;
+ }
}
-#if defined(DEBUG_MULDIV)
- printf("muls64: 0x%016llx * 0x%016llx = 0x%016llx%016llx\n",
- a, b, *phigh, *plow);
-#endif
+
+ if (sgn_divsr) {
+ divisor = 0 - divisor;
+ }
+
+ overflow = divu128((uint64_t *)plow, (uint64_t *)phigh, (uint64_t)divisor);
+
+ if (sgn_dvdnd ^ sgn_divsr) {
+ *plow = 0 - *plow;
+ }
+
+ if (!overflow) {
+ if ((*plow < 0) ^ (sgn_dvdnd ^ sgn_divsr)) {
+ overflow = 1;
+ }
+ }
+
+ return overflow;
}
-#endif /* !defined(__x86_64__) */
+