altos: Add 64-bit subtraction
[fw/altos] / src / core / ao_int64.c
index 1fe717dcd6ca16fab19db928b3aa179448eb8a62..07cdb35731be63d927b963870fa56864efb1b0ac 100644 (file)
 void ao_plus64(ao_int64_t *r, ao_int64_t *a, ao_int64_t *b) {
        uint32_t        t;
 
-       r->low = t = a->low + b->low;
        r->high = a->high + b->high;
+       t = a->low + b->low;
        if (t < a->low)
                r->high++;
+       r->low = t;
+}
+
+void ao_minus64(ao_int64_t *r, ao_int64_t *a, ao_int64_t *b) {
+       uint32_t        t;
+
+
+       r->high = a->high - b->high;
+       t = a->low - b->low;
+       if (t > a->low)
+               r->high--;
+       r->low = t;
 }
 
 void ao_rshift64(ao_int64_t *r, ao_int64_t *a, uint8_t d) {
        if (d < 32) {
-               r->high = (int32_t) a->high >> d;
                r->low = a->low >> d;
                if (d)
                        r->low |= a->high << (32 - d);
+               r->high = (int32_t) a->high >> d;
        } else {
                d &= 0x1f;
-               r->high = 0;
                r->low = (int32_t) a->high >> d;
+               r->high = 0;
        }
 }
 
 void ao_lshift64(ao_int64_t *r, ao_int64_t *a, uint8_t d) {
        if (d < 32) {
                r->high = a->high << d;
-               r->low = a->low << d;
                if (d)
                        r->high |= a->low >> (32 - d);
+               r->low = a->low << d;
        } else {
                d &= 0x1f;
-               r->low = 0;
                r->high = a->low << d;
+               r->low = 0;
        }
 }
 
-void ao_umul64(ao_int64_t *r, uint32_t a, uint32_t b)
+static void ao_umul64_32_32(ao_int64_t *r, uint32_t a, uint32_t b)
 {
        uint32_t        r1;
        uint32_t        r2, r3, r4;
@@ -65,11 +77,11 @@ void ao_umul64(ao_int64_t *r, uint32_t a, uint32_t b)
        s.low = r1;
        s.high = r4;
 
-       t.high = (uint32_t) r2 >> 16;
+       t.high = r2 >> 16;
        t.low = r2 << 16;
        ao_plus64(&u, &s, &t);
 
-       v.high = (int32_t) r3 >> 16;
+       v.high = r3 >> 16;
        v.low = r3 << 16;
        ao_plus64(r, &u, &v);
 }
@@ -81,39 +93,66 @@ void ao_neg64(ao_int64_t *r, ao_int64_t *a) {
                r->high++;
 }
 
-void ao_mul64(ao_int64_t *r, int32_t a, int32_t b) {
+void ao_mul64_32_32(ao_int64_t *r, int32_t a, int32_t b) {
        uint8_t         negative = 0;
 
        if (a < 0) {
                a = -a;
-               negative = 1;
+               negative = ~0;
        }
        if (b < 0) {
                b = -b;
-               negative = !negative;
+               negative = ~negative;
        }
-       ao_umul64(r, a, b);
+       ao_umul64_32_32(r, a, b);
        if (negative)
                ao_neg64(r, r);
 }
 
-void ao_umul64_16(ao_int64_t *r, ao_int64_t *a, uint16_t b) {
-       uint32_t        low = a->low;
-       ao_umul64(r, (uint32_t) low >> 1, (uint32_t) b << 1);
-       if (low & 1) {
-               if ((uint32_t) (r->low += b) < (uint32_t) b)
-                       r->high++;
-       }
-       r->high += a->high * b;
+static void ao_umul64(ao_int64_t *r, ao_int64_t *a, ao_int64_t *b) {
+       ao_int64_t      r2, r3;
+
+       ao_umul64_32_32(&r2, a->high, b->low);
+       ao_umul64_32_32(&r3, a->low, b->high);
+       ao_umul64_32_32(r, a->low, b->low);
+
+       r->high += r2.low + r3.low;
 }
 
-void ao_mul64_16(ao_int64_t *r, ao_int64_t *a, uint16_t b) {
-       if ((int32_t) a->high < 0) {
-               ao_int64_t      t;
+void ao_mul64(ao_int64_t *r, ao_int64_t *a, ao_int64_t *b) {
+       uint8_t negative = 0;
+       ao_int64_t      ap, bp;
 
-               ao_neg64(&t, a);
-               ao_umul64_16(r, &t, b);
+       if (ao_int64_negativep(a)) {
+               ao_neg64(&ap, a);
+               a = &ap;
+               negative = ~0;
+       }
+       if (ao_int64_negativep(b)) {
+               ao_neg64(&bp, b);
+               b = &bp;
+               negative = ~negative;
+       }
+       ao_umul64(r, a, b);
+       if (negative)
                ao_neg64(r, r);
+}
+
+void ao_umul64_64_16(ao_int64_t *r, ao_int64_t *a, uint16_t b) {
+       uint32_t h = a->high * b;
+       ao_umul64_32_32(r, a->low, b);
+       r->high += h;
+}
+
+void ao_mul64_64_16(ao_int64_t *r, ao_int64_t *a, uint16_t b) {
+       ao_int64_t      ap;
+       uint8_t         negative = 0;
+       if ((int32_t) a->high < 0) {
+               ao_neg64(&ap, a);
+               a = &ap;
+               negative = ~0;
        } else
-               ao_umul64_16(r, a, b);
+               ao_umul64_64_16(r, a, b);
+       if (negative)
+               ao_neg64(r, r);
 }