Home | History | Annotate | Download | only in target-i386
      1 /*
      2  *  x86 integer helpers
      3  *
      4  *  Copyright (c) 2003 Fabrice Bellard
      5  *
      6  * This library is free software; you can redistribute it and/or
      7  * modify it under the terms of the GNU Lesser General Public
      8  * License as published by the Free Software Foundation; either
      9  * version 2 of the License, or (at your option) any later version.
     10  *
     11  * This library is distributed in the hope that it will be useful,
     12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14  * Lesser General Public License for more details.
     15  *
     16  * You should have received a copy of the GNU Lesser General Public
     17  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
     18  */
     19 
     20 #include "cpu.h"
     21 #include "qemu/host-utils.h"
     22 #include "helper.h"
     23 
     24 //#define DEBUG_MULDIV
     25 
     26 /* modulo 17 table */
     27 static const uint8_t rclw_table[32] = {
     28     0, 1, 2, 3, 4, 5, 6, 7,
     29     8, 9,10,11,12,13,14,15,
     30    16, 0, 1, 2, 3, 4, 5, 6,
     31     7, 8, 9,10,11,12,13,14,
     32 };
     33 
     34 /* modulo 9 table */
     35 static const uint8_t rclb_table[32] = {
     36     0, 1, 2, 3, 4, 5, 6, 7,
     37     8, 0, 1, 2, 3, 4, 5, 6,
     38     7, 8, 0, 1, 2, 3, 4, 5,
     39     6, 7, 8, 0, 1, 2, 3, 4,
     40 };
     41 
     42 /* division, flags are undefined */
     43 
     44 void helper_divb_AL(CPUX86State *env, target_ulong t0)
     45 {
     46     unsigned int num, den, q, r;
     47 
     48     num = (EAX & 0xffff);
     49     den = (t0 & 0xff);
     50     if (den == 0) {
     51         raise_exception(env, EXCP00_DIVZ);
     52     }
     53     q = (num / den);
     54     if (q > 0xff)
     55         raise_exception(env, EXCP00_DIVZ);
     56     q &= 0xff;
     57     r = (num % den) & 0xff;
     58     EAX = (EAX & ~0xffff) | (r << 8) | q;
     59 }
     60 
     61 void helper_idivb_AL(CPUX86State *env, target_ulong t0)
     62 {
     63     int num, den, q, r;
     64 
     65     num = (int16_t)EAX;
     66     den = (int8_t)t0;
     67     if (den == 0) {
     68         raise_exception(env, EXCP00_DIVZ);
     69     }
     70     q = (num / den);
     71     if (q != (int8_t)q)
     72         raise_exception(env, EXCP00_DIVZ);
     73     q &= 0xff;
     74     r = (num % den) & 0xff;
     75     EAX = (EAX & ~0xffff) | (r << 8) | q;
     76 }
     77 
     78 void helper_divw_AX(CPUX86State *env, target_ulong t0)
     79 {
     80     unsigned int num, den, q, r;
     81 
     82     num = (EAX & 0xffff) | ((EDX & 0xffff) << 16);
     83     den = (t0 & 0xffff);
     84     if (den == 0) {
     85         raise_exception(env, EXCP00_DIVZ);
     86     }
     87     q = (num / den);
     88     if (q > 0xffff)
     89         raise_exception(env, EXCP00_DIVZ);
     90     q &= 0xffff;
     91     r = (num % den) & 0xffff;
     92     EAX = (EAX & ~0xffff) | q;
     93     EDX = (EDX & ~0xffff) | r;
     94 }
     95 
     96 void helper_idivw_AX(CPUX86State *env, target_ulong t0)
     97 {
     98     int num, den, q, r;
     99 
    100     num = (EAX & 0xffff) | ((EDX & 0xffff) << 16);
    101     den = (int16_t)t0;
    102     if (den == 0) {
    103         raise_exception(env, EXCP00_DIVZ);
    104     }
    105     q = (num / den);
    106     if (q != (int16_t)q)
    107         raise_exception(env, EXCP00_DIVZ);
    108     q &= 0xffff;
    109     r = (num % den) & 0xffff;
    110     EAX = (EAX & ~0xffff) | q;
    111     EDX = (EDX & ~0xffff) | r;
    112 }
    113 
    114 void helper_divl_EAX(CPUX86State *env, target_ulong t0)
    115 {
    116     unsigned int den, r;
    117     uint64_t num, q;
    118 
    119     num = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32);
    120     den = t0;
    121     if (den == 0) {
    122         raise_exception(env, EXCP00_DIVZ);
    123     }
    124     q = (num / den);
    125     r = (num % den);
    126     if (q > 0xffffffff)
    127         raise_exception(env, EXCP00_DIVZ);
    128     EAX = (uint32_t)q;
    129     EDX = (uint32_t)r;
    130 }
    131 
    132 void helper_idivl_EAX(CPUX86State *env, target_ulong t0)
    133 {
    134     int den, r;
    135     int64_t num, q;
    136 
    137     num = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32);
    138     den = t0;
    139     if (den == 0) {
    140         raise_exception(env, EXCP00_DIVZ);
    141     }
    142     q = (num / den);
    143     r = (num % den);
    144     if (q != (int32_t)q)
    145         raise_exception(env, EXCP00_DIVZ);
    146     EAX = (uint32_t)q;
    147     EDX = (uint32_t)r;
    148 }
    149 
    150 /* bcd */
    151 
    152 /* XXX: exception */
    153 void helper_aam(CPUX86State *env, int base)
    154 {
    155     int al, ah;
    156     al = EAX & 0xff;
    157     ah = al / base;
    158     al = al % base;
    159     EAX = (EAX & ~0xffff) | al | (ah << 8);
    160     CC_DST = al;
    161 }
    162 
    163 void helper_aad(CPUX86State *env, int base)
    164 {
    165     int al, ah;
    166     al = EAX & 0xff;
    167     ah = (EAX >> 8) & 0xff;
    168     al = ((ah * base) + al) & 0xff;
    169     EAX = (EAX & ~0xffff) | al;
    170     CC_DST = al;
    171 }
    172 
    173 void helper_aaa(CPUX86State *env)
    174 {
    175     int icarry;
    176     int al, ah, af;
    177     int eflags;
    178 
    179     eflags = helper_cc_compute_all(env, CC_OP);
    180     af = eflags & CC_A;
    181     al = EAX & 0xff;
    182     ah = (EAX >> 8) & 0xff;
    183 
    184     icarry = (al > 0xf9);
    185     if (((al & 0x0f) > 9 ) || af) {
    186         al = (al + 6) & 0x0f;
    187         ah = (ah + 1 + icarry) & 0xff;
    188         eflags |= CC_C | CC_A;
    189     } else {
    190         eflags &= ~(CC_C | CC_A);
    191         al &= 0x0f;
    192     }
    193     EAX = (EAX & ~0xffff) | al | (ah << 8);
    194     CC_SRC = eflags;
    195 }
    196 
    197 void helper_aas(CPUX86State *env)
    198 {
    199     int icarry;
    200     int al, ah, af;
    201     int eflags;
    202 
    203     eflags = helper_cc_compute_all(env, CC_OP);
    204     af = eflags & CC_A;
    205     al = EAX & 0xff;
    206     ah = (EAX >> 8) & 0xff;
    207 
    208     icarry = (al < 6);
    209     if (((al & 0x0f) > 9 ) || af) {
    210         al = (al - 6) & 0x0f;
    211         ah = (ah - 1 - icarry) & 0xff;
    212         eflags |= CC_C | CC_A;
    213     } else {
    214         eflags &= ~(CC_C | CC_A);
    215         al &= 0x0f;
    216     }
    217     EAX = (EAX & ~0xffff) | al | (ah << 8);
    218     CC_SRC = eflags;
    219 }
    220 
    221 void helper_daa(CPUX86State *env)
    222 {
    223     int al, af, cf;
    224     int eflags;
    225 
    226     eflags = helper_cc_compute_all(env, CC_OP);
    227     cf = eflags & CC_C;
    228     af = eflags & CC_A;
    229     al = EAX & 0xff;
    230 
    231     eflags = 0;
    232     if (((al & 0x0f) > 9 ) || af) {
    233         al = (al + 6) & 0xff;
    234         eflags |= CC_A;
    235     }
    236     if ((al > 0x9f) || cf) {
    237         al = (al + 0x60) & 0xff;
    238         eflags |= CC_C;
    239     }
    240     EAX = (EAX & ~0xff) | al;
    241     /* well, speed is not an issue here, so we compute the flags by hand */
    242     eflags |= (al == 0) << 6; /* zf */
    243     eflags |= parity_table[al]; /* pf */
    244     eflags |= (al & 0x80); /* sf */
    245     CC_SRC = eflags;
    246 }
    247 
    248 void helper_das(CPUX86State *env)
    249 {
    250     int al, al1, af, cf;
    251     int eflags;
    252 
    253     eflags = helper_cc_compute_all(env, CC_OP);
    254     cf = eflags & CC_C;
    255     af = eflags & CC_A;
    256     al = EAX & 0xff;
    257 
    258     eflags = 0;
    259     al1 = al;
    260     if (((al & 0x0f) > 9 ) || af) {
    261         eflags |= CC_A;
    262         if (al < 6 || cf)
    263             eflags |= CC_C;
    264         al = (al - 6) & 0xff;
    265     }
    266     if ((al1 > 0x99) || cf) {
    267         al = (al - 0x60) & 0xff;
    268         eflags |= CC_C;
    269     }
    270     EAX = (EAX & ~0xff) | al;
    271     /* well, speed is not an issue here, so we compute the flags by hand */
    272     eflags |= (al == 0) << 6; /* zf */
    273     eflags |= parity_table[al]; /* pf */
    274     eflags |= (al & 0x80); /* sf */
    275     CC_SRC = eflags;
    276 }
    277 
    278 #ifdef TARGET_X86_64
    279 
    280 static void add128(uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b)
    281 {
    282     *plow += a;
    283     /* carry test */
    284     if (*plow < a)
    285         (*phigh)++;
    286     *phigh += b;
    287 }
    288 
    289 static void neg128(uint64_t *plow, uint64_t *phigh)
    290 {
    291     *plow = ~ *plow;
    292     *phigh = ~ *phigh;
    293     add128(plow, phigh, 1, 0);
    294 }
    295 
    296 /* return TRUE if overflow */
    297 static int div64(uint64_t *plow, uint64_t *phigh, uint64_t b)
    298 {
    299     uint64_t q, r, a1, a0;
    300     int i, qb, ab;
    301 
    302     a0 = *plow;
    303     a1 = *phigh;
    304     if (a1 == 0) {
    305         q = a0 / b;
    306         r = a0 % b;
    307         *plow = q;
    308         *phigh = r;
    309     } else {
    310         if (a1 >= b)
    311             return 1;
    312         /* XXX: use a better algorithm */
    313         for(i = 0; i < 64; i++) {
    314             ab = a1 >> 63;
    315             a1 = (a1 << 1) | (a0 >> 63);
    316             if (ab || a1 >= b) {
    317                 a1 -= b;
    318                 qb = 1;
    319             } else {
    320                 qb = 0;
    321             }
    322             a0 = (a0 << 1) | qb;
    323         }
    324 #if defined(DEBUG_MULDIV)
    325         printf("div: 0x%016" PRIx64 "%016" PRIx64 " / 0x%016" PRIx64 ": q=0x%016" PRIx64 " r=0x%016" PRIx64 "\n",
    326                *phigh, *plow, b, a0, a1);
    327 #endif
    328         *plow = a0;
    329         *phigh = a1;
    330     }
    331     return 0;
    332 }
    333 
    334 /* return TRUE if overflow */
    335 static int idiv64(uint64_t *plow, uint64_t *phigh, int64_t b)
    336 {
    337     int sa, sb;
    338     sa = ((int64_t)*phigh < 0);
    339     if (sa)
    340         neg128(plow, phigh);
    341     sb = (b < 0);
    342     if (sb)
    343         b = -b;
    344     if (div64(plow, phigh, b) != 0)
    345         return 1;
    346     if (sa ^ sb) {
    347         if (*plow > (1ULL << 63))
    348             return 1;
    349         *plow = - *plow;
    350     } else {
    351         if (*plow >= (1ULL << 63))
    352             return 1;
    353     }
    354     if (sa)
    355         *phigh = - *phigh;
    356     return 0;
    357 }
    358 
    359 void helper_mulq_EAX_T0(CPUX86State *env, target_ulong t0)
    360 {
    361     uint64_t r0, r1;
    362 
    363     mulu64(&r0, &r1, EAX, t0);
    364     EAX = r0;
    365     EDX = r1;
    366     CC_DST = r0;
    367     CC_SRC = r1;
    368 }
    369 
    370 void helper_imulq_EAX_T0(CPUX86State *env, target_ulong t0)
    371 {
    372     uint64_t r0, r1;
    373 
    374     muls64(&r0, &r1, EAX, t0);
    375     EAX = r0;
    376     EDX = r1;
    377     CC_DST = r0;
    378     CC_SRC = ((int64_t)r1 != ((int64_t)r0 >> 63));
    379 }
    380 
    381 target_ulong helper_imulq_T0_T1(CPUX86State *env, target_ulong t0, target_ulong t1)
    382 {
    383     uint64_t r0, r1;
    384 
    385     muls64(&r0, &r1, t0, t1);
    386     CC_DST = r0;
    387     CC_SRC = ((int64_t)r1 != ((int64_t)r0 >> 63));
    388     return r0;
    389 }
    390 
    391 void helper_divq_EAX(CPUX86State *env, target_ulong t0)
    392 {
    393     uint64_t r0, r1;
    394     if (t0 == 0) {
    395         raise_exception(env, EXCP00_DIVZ);
    396     }
    397     r0 = EAX;
    398     r1 = EDX;
    399     if (div64(&r0, &r1, t0))
    400         raise_exception(env, EXCP00_DIVZ);
    401     EAX = r0;
    402     EDX = r1;
    403 }
    404 
    405 void helper_idivq_EAX(CPUX86State *env, target_ulong t0)
    406 {
    407     uint64_t r0, r1;
    408     if (t0 == 0) {
    409         raise_exception(env, EXCP00_DIVZ);
    410     }
    411     r0 = EAX;
    412     r1 = EDX;
    413     if (idiv64(&r0, &r1, t0))
    414         raise_exception(env, EXCP00_DIVZ);
    415     EAX = r0;
    416     EDX = r1;
    417 }
    418 
    419 #endif
    420 
    421 #define SHIFT 0
    422 #include "shift_helper_template.h"
    423 #undef SHIFT
    424 
    425 #define SHIFT 1
    426 #include "shift_helper_template.h"
    427 #undef SHIFT
    428 
    429 #define SHIFT 2
    430 #include "shift_helper_template.h"
    431 #undef SHIFT
    432 
    433 #ifdef TARGET_X86_64
    434 #define SHIFT 3
    435 #include "shift_helper_template.h"
    436 #undef SHIFT
    437 #endif
    438 
    439 /* bit operations */
    440 target_ulong helper_bsf(target_ulong t0)
    441 {
    442     int count;
    443     target_ulong res;
    444 
    445     res = t0;
    446     count = 0;
    447     while ((res & 1) == 0) {
    448         count++;
    449         res >>= 1;
    450     }
    451     return count;
    452 }
    453 
    454 target_ulong helper_bsr(target_ulong t0)
    455 {
    456     int count;
    457     target_ulong res, mask;
    458 
    459     res = t0;
    460     count = TARGET_LONG_BITS - 1;
    461     mask = (target_ulong)1 << (TARGET_LONG_BITS - 1);
    462     while ((res & mask) == 0) {
    463         count--;
    464         res <<= 1;
    465     }
    466     return count;
    467 }
    468