From 45bbbb466cf4a6280076ea5a51f67ef5bedee345 Mon Sep 17 00:00:00 2001
From: bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>
Date: Sat, 23 Jul 2005 20:21:38 +0000
Subject: [PATCH] added overflow exceptions in divisions

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1521 c046a42c-6fe2-441c-8c8c-71466251a162
---
 target-i386/helper.c | 44 +++++++++++++++++++++++++++++++-------------
 target-i386/op.c     | 21 ++++++++++++++++-----
 2 files changed, 47 insertions(+), 18 deletions(-)

diff --git a/target-i386/helper.c b/target-i386/helper.c
index 2d241a1431..c83dbf2190 100644
--- a/target-i386/helper.c
+++ b/target-i386/helper.c
@@ -1209,13 +1209,13 @@ void raise_exception(int exception_index)
 #ifdef BUGGY_GCC_DIV64
 /* gcc 2.95.4 on PowerPC does not seem to like using __udivdi3, so we
    call it from another function */
-uint32_t div32(uint32_t *q_ptr, uint64_t num, uint32_t den)
+uint32_t div32(uint64_t *q_ptr, uint64_t num, uint32_t den)
 {
     *q_ptr = num / den;
     return num % den;
 }
 
-int32_t idiv32(int32_t *q_ptr, int64_t num, int32_t den)
+int32_t idiv32(int64_t *q_ptr, int64_t num, int32_t den)
 {
     *q_ptr = num / den;
     return num % den;
@@ -1224,8 +1224,8 @@ int32_t idiv32(int32_t *q_ptr, int64_t num, int32_t den)
 
 void helper_divl_EAX_T0(void)
 {
-    unsigned int den, q, r;
-    uint64_t num;
+    unsigned int den, r;
+    uint64_t num, q;
     
     num = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32);
     den = T0;
@@ -1238,14 +1238,16 @@ void helper_divl_EAX_T0(void)
     q = (num / den);
     r = (num % den);
 #endif
+    if (q > 0xffffffff)
+        raise_exception(EXCP00_DIVZ);
     EAX = (uint32_t)q;
     EDX = (uint32_t)r;
 }
 
 void helper_idivl_EAX_T0(void)
 {
-    int den, q, r;
-    int64_t num;
+    int den, r;
+    int64_t num, q;
     
     num = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32);
     den = T0;
@@ -1258,6 +1260,8 @@ void helper_idivl_EAX_T0(void)
     q = (num / den);
     r = (num % den);
 #endif
+    if (q != (int32_t)q)
+        raise_exception(EXCP00_DIVZ);
     EAX = (uint32_t)q;
     EDX = (uint32_t)r;
 }
@@ -3254,8 +3258,8 @@ static void imul64(uint64_t *plow, uint64_t *phigh, int64_t a, int64_t b)
     }
 }
 
-/* XXX: overflow support */
-static void div64(uint64_t *plow, uint64_t *phigh, uint64_t b)
+/* return TRUE if overflow */
+static int div64(uint64_t *plow, uint64_t *phigh, uint64_t b)
 {
     uint64_t q, r, a1, a0;
     int i, qb;
@@ -3268,6 +3272,8 @@ static void div64(uint64_t *plow, uint64_t *phigh, uint64_t b)
         *plow = q;
         *phigh = r;
     } else {
+        if (a1 >= b)
+            return 1;
         /* XXX: use a better algorithm */
         for(i = 0; i < 64; i++) {
             a1 = (a1 << 1) | (a0 >> 63);
@@ -3286,9 +3292,11 @@ static void div64(uint64_t *plow, uint64_t *phigh, uint64_t b)
         *plow = a0;
         *phigh = a1;
     }
+    return 0;
 }
 
-static void idiv64(uint64_t *plow, uint64_t *phigh, int64_t b)
+/* return TRUE if overflow */
+static int idiv64(uint64_t *plow, uint64_t *phigh, int64_t b)
 {
     int sa, sb;
     sa = ((int64_t)*phigh < 0);
@@ -3297,11 +3305,19 @@ static void idiv64(uint64_t *plow, uint64_t *phigh, int64_t b)
     sb = (b < 0);
     if (sb)
         b = -b;
-    div64(plow, phigh, b);
-    if (sa ^ sb)
+    if (div64(plow, phigh, b) != 0)
+        return 1;
+    if (sa ^ sb) {
+        if (*plow > (1ULL << 63))
+            return 1;
         *plow = - *plow;
+    } else {
+        if (*plow >= (1ULL << 63))
+            return 1;
+    }
     if (sa)
         *phigh = - *phigh;
+    return 0;
 }
 
 void helper_mulq_EAX_T0(void)
@@ -3344,7 +3360,8 @@ void helper_divq_EAX_T0(void)
     }
     r0 = EAX;
     r1 = EDX;
-    div64(&r0, &r1, T0);
+    if (div64(&r0, &r1, T0))
+        raise_exception(EXCP00_DIVZ);
     EAX = r0;
     EDX = r1;
 }
@@ -3357,7 +3374,8 @@ void helper_idivq_EAX_T0(void)
     }
     r0 = EAX;
     r1 = EDX;
-    idiv64(&r0, &r1, T0);
+    if (idiv64(&r0, &r1, T0))
+        raise_exception(EXCP00_DIVZ);
     EAX = r0;
     EDX = r1;
 }
diff --git a/target-i386/op.c b/target-i386/op.c
index 3574576ab8..142b662635 100644
--- a/target-i386/op.c
+++ b/target-i386/op.c
@@ -328,7 +328,6 @@ void OPPROTO op_imulq_T0_T1(void)
 #endif
 
 /* division, flags are undefined */
-/* XXX: add exceptions for overflow */
 
 void OPPROTO op_divb_AL_T0(void)
 {
@@ -339,7 +338,10 @@ void OPPROTO op_divb_AL_T0(void)
     if (den == 0) {
         raise_exception(EXCP00_DIVZ);
     }
-    q = (num / den) & 0xff;
+    q = (num / den);
+    if (q > 0xff)
+        raise_exception(EXCP00_DIVZ);
+    q &= 0xff;
     r = (num % den) & 0xff;
     EAX = (EAX & ~0xffff) | (r << 8) | q;
 }
@@ -353,7 +355,10 @@ void OPPROTO op_idivb_AL_T0(void)
     if (den == 0) {
         raise_exception(EXCP00_DIVZ);
     }
-    q = (num / den) & 0xff;
+    q = (num / den);
+    if (q != (int8_t)q)
+        raise_exception(EXCP00_DIVZ);
+    q &= 0xff;
     r = (num % den) & 0xff;
     EAX = (EAX & ~0xffff) | (r << 8) | q;
 }
@@ -367,7 +372,10 @@ void OPPROTO op_divw_AX_T0(void)
     if (den == 0) {
         raise_exception(EXCP00_DIVZ);
     }
-    q = (num / den) & 0xffff;
+    q = (num / den);
+    if (q > 0xffff)
+        raise_exception(EXCP00_DIVZ);
+    q &= 0xffff;
     r = (num % den) & 0xffff;
     EAX = (EAX & ~0xffff) | q;
     EDX = (EDX & ~0xffff) | r;
@@ -382,7 +390,10 @@ void OPPROTO op_idivw_AX_T0(void)
     if (den == 0) {
         raise_exception(EXCP00_DIVZ);
     }
-    q = (num / den) & 0xffff;
+    q = (num / den);
+    if (q != (int16_t)q)
+        raise_exception(EXCP00_DIVZ);
+    q &= 0xffff;
     r = (num % den) & 0xffff;
     EAX = (EAX & ~0xffff) | q;
     EDX = (EDX & ~0xffff) | r;