reactos/sdk/lib/fast486/fpu.c

3822 lines
115 KiB
C

/*
* Fast486 386/486 CPU Emulation Library
* fpu.c
*
* Copyright (C) 2015 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/* INCLUDES *******************************************************************/
#include <windef.h>
// #define NDEBUG
#include <debug.h>
#include <fast486.h>
#include "common.h"
#include "fpu.h"
/* CONSTANTS ******************************************************************/
/* 0.00 */
static const FAST486_FPU_DATA_REG FpuZero = {0ULL, 0, FALSE};
/* 1.00 */
static const FAST486_FPU_DATA_REG FpuOne = {0x8000000000000000ULL, FPU_REAL10_BIAS, FALSE};
/* Pi */
static const FAST486_FPU_DATA_REG FpuPi = {0xC90FDAA22168C235ULL, FPU_REAL10_BIAS + 1, FALSE};
/* lb(10) */
static const FAST486_FPU_DATA_REG FpuL2Ten = {0xD49A784BCD1B8AFEULL, FPU_REAL10_BIAS + 1, FALSE};
/* lb(e) */
static const FAST486_FPU_DATA_REG FpuL2E = {0xB8AA3B295C17F0BCULL, FPU_REAL10_BIAS, FALSE};
/* lg(2) */
static const FAST486_FPU_DATA_REG FpuLgTwo = {0x9A209A84FBCFF799ULL, FPU_REAL10_BIAS - 2, FALSE};
/* ln(2) */
static const FAST486_FPU_DATA_REG FpuLnTwo = {0xB17217F7D1CF79ACULL, FPU_REAL10_BIAS - 1, FALSE};
/* 2.00 */
static const FAST486_FPU_DATA_REG FpuTwo = {0x8000000000000000ULL, FPU_REAL10_BIAS + 1, FALSE};
/* Pi / 2 */
static const FAST486_FPU_DATA_REG FpuHalfPi = {0xC90FDAA22168C235ULL, FPU_REAL10_BIAS, FALSE};
static const FAST486_FPU_DATA_REG FpuInverseNumber[INVERSE_NUMBERS_COUNT] =
{
{0x8000000000000000ULL, FPU_REAL10_BIAS, FALSE}, /* 1 / 1 */
{0x8000000000000000ULL, FPU_REAL10_BIAS - 1, FALSE}, /* 1 / 2 */
{0xAAAAAAAAAAAAAAABULL, FPU_REAL10_BIAS - 2, FALSE}, /* 1 / 3 */
{0x8000000000000000ULL, FPU_REAL10_BIAS - 2, FALSE}, /* 1 / 4 */
{0xCCCCCCCCCCCCCCCDULL, FPU_REAL10_BIAS - 3, FALSE}, /* 1 / 5 */
{0xAAAAAAAAAAAAAAAAULL, FPU_REAL10_BIAS - 3, FALSE}, /* 1 / 6 */
{0x9249249249249249ULL, FPU_REAL10_BIAS - 3, FALSE}, /* 1 / 7 */
{0x8000000000000000ULL, FPU_REAL10_BIAS - 3, FALSE}, /* 1 / 8 */
{0xE38E38E38E38E38EULL, FPU_REAL10_BIAS - 4, FALSE}, /* 1 / 9 */
{0xCCCCCCCCCCCCCCCDULL, FPU_REAL10_BIAS - 4, FALSE}, /* 1 / 10 */
{0xBA2E8BA2E8BA2E8CULL, FPU_REAL10_BIAS - 4, FALSE}, /* 1 / 11 */
{0xAAAAAAAAAAAAAAABULL, FPU_REAL10_BIAS - 4, FALSE}, /* 1 / 12 */
{0x9D89D89D89D89D8AULL, FPU_REAL10_BIAS - 4, FALSE}, /* 1 / 13 */
{0x9249249249249249ULL, FPU_REAL10_BIAS - 4, FALSE}, /* 1 / 14 */
{0x8888888888888889ULL, FPU_REAL10_BIAS - 4, FALSE}, /* 1 / 15 */
{0x8000000000000000ULL, FPU_REAL10_BIAS - 4, FALSE}, /* 1 / 16 */
{0xF0F0F0F0F0F0F0F0ULL, FPU_REAL10_BIAS - 5, FALSE}, /* 1 / 17 */
{0xE38E38E38E38E38EULL, FPU_REAL10_BIAS - 5, FALSE}, /* 1 / 18 */
{0xD79435E50D79435EULL, FPU_REAL10_BIAS - 5, FALSE}, /* 1 / 19 */
{0xCCCCCCCCCCCCCCCDULL, FPU_REAL10_BIAS - 5, FALSE}, /* 1 / 20 */
{0xC30C30C30C30C30CULL, FPU_REAL10_BIAS - 5, FALSE}, /* 1 / 21 */
{0xBA2E8BA2E8BA2E8BULL, FPU_REAL10_BIAS - 5, FALSE}, /* 1 / 22 */
{0xB21642C8590B2164ULL, FPU_REAL10_BIAS - 5, FALSE}, /* 1 / 23 */
{0xAAAAAAAAAAAAAAABULL, FPU_REAL10_BIAS - 5, FALSE}, /* 1 / 24 */
{0xA3D70A3D70A3D70AULL, FPU_REAL10_BIAS - 5, FALSE}, /* 1 / 25 */
{0x9D89D89D89D89D8AULL, FPU_REAL10_BIAS - 5, FALSE}, /* 1 / 26 */
{0x97B425ED097B425FULL, FPU_REAL10_BIAS - 5, FALSE}, /* 1 / 27 */
{0x9249249249249249ULL, FPU_REAL10_BIAS - 5, FALSE}, /* 1 / 28 */
{0x8D3DCB08D3DCB08DULL, FPU_REAL10_BIAS - 5, FALSE}, /* 1 / 29 */
{0x8888888888888889ULL, FPU_REAL10_BIAS - 5, FALSE}, /* 1 / 30 */
{0x8421084210842108ULL, FPU_REAL10_BIAS - 5, FALSE}, /* 1 / 31 */
{0x8000000000000000ULL, FPU_REAL10_BIAS - 5, FALSE}, /* 1 / 32 */
{0xF83E0F83E0F83E0FULL, FPU_REAL10_BIAS - 6, FALSE}, /* 1 / 33 */
{0xF0F0F0F0F0F0F0F0ULL, FPU_REAL10_BIAS - 6, FALSE}, /* 1 / 34 */
{0xEA0EA0EA0EA0EA0EULL, FPU_REAL10_BIAS - 6, FALSE}, /* 1 / 35 */
{0xE38E38E38E38E38EULL, FPU_REAL10_BIAS - 6, FALSE}, /* 1 / 36 */
{0xDD67C8A60DD67C8AULL, FPU_REAL10_BIAS - 6, FALSE}, /* 1 / 37 */
{0xD79435E50D79435EULL, FPU_REAL10_BIAS - 6, FALSE}, /* 1 / 38 */
{0xD20D20D20D20D20DULL, FPU_REAL10_BIAS - 6, FALSE}, /* 1 / 39 */
{0xCCCCCCCCCCCCCCCDULL, FPU_REAL10_BIAS - 6, FALSE}, /* 1 / 40 */
{0xC7CE0C7CE0C7CE0CULL, FPU_REAL10_BIAS - 6, FALSE}, /* 1 / 41 */
{0xC30C30C30C30C30CULL, FPU_REAL10_BIAS - 6, FALSE}, /* 1 / 42 */
{0xBE82FA0BE82FA0BFULL, FPU_REAL10_BIAS - 6, FALSE}, /* 1 / 43 */
{0xBA2E8BA2E8BA2E8BULL, FPU_REAL10_BIAS - 6, FALSE}, /* 1 / 44 */
{0xB60B60B60B60B60BULL, FPU_REAL10_BIAS - 6, FALSE}, /* 1 / 45 */
{0xB21642C8590B2164ULL, FPU_REAL10_BIAS - 6, FALSE}, /* 1 / 46 */
{0xAE4C415C9882B931ULL, FPU_REAL10_BIAS - 6, FALSE}, /* 1 / 47 */
{0xAAAAAAAAAAAAAAABULL, FPU_REAL10_BIAS - 6, FALSE}, /* 1 / 48 */
{0xA72F05397829CBC1ULL, FPU_REAL10_BIAS - 6, FALSE}, /* 1 / 49 */
{0xA3D70A3D70A3D70AULL, FPU_REAL10_BIAS - 6, FALSE}, /* 1 / 50 */
};
static const FAST486_FPU_DATA_REG FpuInverseNumberSine[INVERSE_NUMBERS_COUNT] =
{
{0xAAAAAAAAAAAAAAAAULL, FPU_REAL10_BIAS - 3, FALSE}, /* 1 / 6 */
{0xCCCCCCCCCCCCCCCCULL, FPU_REAL10_BIAS - 5, FALSE}, /* 1 / 20 */
{0xC30C30C30C30C30CULL, FPU_REAL10_BIAS - 6, FALSE}, /* 1 / 42 */
{0xE38E38E38E38E38EULL, FPU_REAL10_BIAS - 7, FALSE}, /* 1 / 72 */
{0x94F2094F2094F209ULL, FPU_REAL10_BIAS - 7, FALSE}, /* 1 / 110 */
{0xD20D20D20D20D20DULL, FPU_REAL10_BIAS - 8, FALSE}, /* 1 / 156 */
{0x9C09C09C09C09C09ULL, FPU_REAL10_BIAS - 8, FALSE}, /* 1 / 210 */
{0xF0F0F0F0F0F0F0F0ULL, FPU_REAL10_BIAS - 9, FALSE}, /* 1 / 272 */
{0xBFA02FE80BFA02FEULL, FPU_REAL10_BIAS - 9, FALSE}, /* 1 / 342 */
{0x9C09C09C09C09C09ULL, FPU_REAL10_BIAS - 9, FALSE}, /* 1 / 420 */
{0x81848DA8FAF0D277ULL, FPU_REAL10_BIAS - 9, FALSE}, /* 1 / 506 */
{0xDA740DA740DA740DULL, FPU_REAL10_BIAS - 10, FALSE}, /* 1 / 600 */
{0xBAB656100BAB6561ULL, FPU_REAL10_BIAS - 10, FALSE}, /* 1 / 702 */
{0xA16B312EA8FC377CULL, FPU_REAL10_BIAS - 10, FALSE}, /* 1 / 812 */
{0x8CF008CF008CF008ULL, FPU_REAL10_BIAS - 10, FALSE}, /* 1 / 930 */
{0xF83E0F83E0F83E0FULL, FPU_REAL10_BIAS - 11, FALSE}, /* 1 / 1056 */
{0xDC4A00DC4A00DC4AULL, FPU_REAL10_BIAS - 11, FALSE}, /* 1 / 1190 */
{0xC4CE07B00C4CE07BULL, FPU_REAL10_BIAS - 11, FALSE}, /* 1 / 1332 */
{0xB0E2A2600B0E2A26ULL, FPU_REAL10_BIAS - 11, FALSE}, /* 1 / 1482 */
{0x9FD809FD809FD809ULL, FPU_REAL10_BIAS - 11, FALSE}, /* 1 / 1640 */
{0x9126D6E4802449B5ULL, FPU_REAL10_BIAS - 11, FALSE}, /* 1 / 1806 */
{0x84655D9BAB2F1008ULL, FPU_REAL10_BIAS - 11, FALSE}, /* 1 / 1980 */
{0xF2805AF0221A0CC9ULL, FPU_REAL10_BIAS - 12, FALSE}, /* 1 / 2162 */
{0xDEE95C4CA037BA57ULL, FPU_REAL10_BIAS - 12, FALSE}, /* 1 / 2352 */
{0xCD9A673400CD9A67ULL, FPU_REAL10_BIAS - 12, FALSE}, /* 1 / 2550 */
{0xBE3C310B84A4F832ULL, FPU_REAL10_BIAS - 12, FALSE}, /* 1 / 2756 */
{0xB087277A39941560ULL, FPU_REAL10_BIAS - 12, FALSE}, /* 1 / 2970 */
{0xA44029100A440291ULL, FPU_REAL10_BIAS - 12, FALSE}, /* 1 / 3192 */
{0x9936034AA9121AA1ULL, FPU_REAL10_BIAS - 12, FALSE}, /* 1 / 3422 */
{0x8F3F82A86DACA008ULL, FPU_REAL10_BIAS - 12, FALSE}, /* 1 / 3660 */
{0x8639F00218E7C008ULL, FPU_REAL10_BIAS - 12, FALSE}, /* 1 / 3906 */
{0xFC0FC0FC0FC0FC0FULL, FPU_REAL10_BIAS - 13, FALSE}, /* 1 / 4160 */
{0xED208916CF412FD1ULL, FPU_REAL10_BIAS - 13, FALSE}, /* 1 / 4422 */
{0xDF7B4EC93886702DULL, FPU_REAL10_BIAS - 13, FALSE}, /* 1 / 4692 */
{0xD2FB287C7224E167ULL, FPU_REAL10_BIAS - 13, FALSE}, /* 1 / 4970 */
{0xC78031E00C78031EULL, FPU_REAL10_BIAS - 13, FALSE}, /* 1 / 5256 */
{0xBCEEBFB33F021F2EULL, FPU_REAL10_BIAS - 13, FALSE}, /* 1 / 5550 */
{0xB32EB86E96D5D441ULL, FPU_REAL10_BIAS - 13, FALSE}, /* 1 / 5852 */
{0xAA2B0A62E08248F3ULL, FPU_REAL10_BIAS - 13, FALSE}, /* 1 / 6162 */
{0xA1D139855F7268EDULL, FPU_REAL10_BIAS - 13, FALSE}, /* 1 / 6480 */
{0x9A1100604AA03C2EULL, FPU_REAL10_BIAS - 13, FALSE}, /* 1 / 6806 */
{0x92DC0092DC0092DCULL, FPU_REAL10_BIAS - 13, FALSE}, /* 1 / 7140 */
{0x8C258008C258008CULL, FPU_REAL10_BIAS - 13, FALSE}, /* 1 / 7482 */
{0x85E230A32BAB46DDULL, FPU_REAL10_BIAS - 13, FALSE}, /* 1 / 7832 */
{0x8008008008008008ULL, FPU_REAL10_BIAS - 13, FALSE}, /* 1 / 8190 */
{0xF51BE2CC2D7AAC94ULL, FPU_REAL10_BIAS - 14, FALSE}, /* 1 / 8556 */
{0xEAD7EC46DDA80C62ULL, FPU_REAL10_BIAS - 14, FALSE}, /* 1 / 8930 */
{0xE135A9C97500E135ULL, FPU_REAL10_BIAS - 14, FALSE}, /* 1 / 9312 */
{0xD8281B71177BDB7BULL, FPU_REAL10_BIAS - 14, FALSE}, /* 1 / 9702 */
{0xCFA3892CE9FFCC17ULL, FPU_REAL10_BIAS - 14, FALSE}, /* 1 / 10100 */
};
static const FAST486_FPU_DATA_REG FpuInverseNumberCosine[INVERSE_NUMBERS_COUNT] =
{
{0x8000000000000000ULL, FPU_REAL10_BIAS - 1, FALSE}, /* 1 / 2 */
{0xAAAAAAAAAAAAAAAAULL, FPU_REAL10_BIAS - 4, FALSE}, /* 1 / 12 */
{0x8888888888888888ULL, FPU_REAL10_BIAS - 5, FALSE}, /* 1 / 30 */
{0x9249249249249249ULL, FPU_REAL10_BIAS - 6, FALSE}, /* 1 / 56 */
{0xB60B60B60B60B60BULL, FPU_REAL10_BIAS - 7, FALSE}, /* 1 / 90 */
{0xF83E0F83E0F83E0FULL, FPU_REAL10_BIAS - 8, FALSE}, /* 1 / 132 */
{0xB40B40B40B40B40BULL, FPU_REAL10_BIAS - 8, FALSE}, /* 1 / 182 */
{0x8888888888888888ULL, FPU_REAL10_BIAS - 8, FALSE}, /* 1 / 240 */
{0xD62B80D62B80D62BULL, FPU_REAL10_BIAS - 9, FALSE}, /* 1 / 306 */
{0xAC7691840AC76918ULL, FPU_REAL10_BIAS - 9, FALSE}, /* 1 / 380 */
{0x8DDA520237694808ULL, FPU_REAL10_BIAS - 9, FALSE}, /* 1 / 462 */
{0xED7303B5CC0ED730ULL, FPU_REAL10_BIAS - 10, FALSE}, /* 1 / 552 */
{0xC9A633FCD967300CULL, FPU_REAL10_BIAS - 10, FALSE}, /* 1 / 650 */
{0xAD602B580AD602B5ULL, FPU_REAL10_BIAS - 10, FALSE}, /* 1 / 756 */
{0x96A850096A850096ULL, FPU_REAL10_BIAS - 10, FALSE}, /* 1 / 870 */
{0x8421084210842108ULL, FPU_REAL10_BIAS - 10, FALSE}, /* 1 / 992 */
{0xE9A3D25E00E9A3D2ULL, FPU_REAL10_BIAS - 11, FALSE}, /* 1 / 1122 */
{0xD00D00D00D00D00DULL, FPU_REAL10_BIAS - 11, FALSE}, /* 1 / 1260 */
{0xBA7258200BA72582ULL, FPU_REAL10_BIAS - 11, FALSE}, /* 1 / 1406 */
{0xA80A80A80A80A80AULL, FPU_REAL10_BIAS - 11, FALSE}, /* 1 / 1560 */
{0x983B773A92E16009ULL, FPU_REAL10_BIAS - 11, FALSE}, /* 1 / 1722 */
{0x8A8DCD1FEEAE465CULL, FPU_REAL10_BIAS - 11, FALSE}, /* 1 / 1892 */
{0xFD477B6C956529CDULL, FPU_REAL10_BIAS - 12, FALSE}, /* 1 / 2070 */
{0xE865AC7B7603A196ULL, FPU_REAL10_BIAS - 12, FALSE}, /* 1 / 2256 */
{0xD5FEBF01E17D2DC4ULL, FPU_REAL10_BIAS - 12, FALSE}, /* 1 / 2450 */
{0xC5B200C5B200C5B2ULL, FPU_REAL10_BIAS - 12, FALSE}, /* 1 / 2652 */
{0xB7307B1492B1D28FULL, FPU_REAL10_BIAS - 12, FALSE}, /* 1 / 2862 */
{0xAA392F35DC17F00AULL, FPU_REAL10_BIAS - 12, FALSE}, /* 1 / 3080 */
{0x9E96394D47B46C68ULL, FPU_REAL10_BIAS - 12, FALSE}, /* 1 / 3306 */
{0x941A9CC82BF7E68BULL, FPU_REAL10_BIAS - 12, FALSE}, /* 1 / 3540 */
{0x8AA08EF5936D4008ULL, FPU_REAL10_BIAS - 12, FALSE}, /* 1 / 3782 */
{0x8208208208208208ULL, FPU_REAL10_BIAS - 12, FALSE}, /* 1 / 4032 */
{0xF46C5E0BB22F800FULL, FPU_REAL10_BIAS - 13, FALSE}, /* 1 / 4290 */
{0xE6271BA5329217D3ULL, FPU_REAL10_BIAS - 13, FALSE}, /* 1 / 4556 */
{0xD918B2EF5B7B4866ULL, FPU_REAL10_BIAS - 13, FALSE}, /* 1 / 4830 */
{0xCD1ED923A7DCBEB2ULL, FPU_REAL10_BIAS - 13, FALSE}, /* 1 / 5112 */
{0xC21BDD800C21BDD8ULL, FPU_REAL10_BIAS - 13, FALSE}, /* 1 / 5402 */
{0xB7F5F08CD84C2BD5ULL, FPU_REAL10_BIAS - 13, FALSE}, /* 1 / 5700 */
{0xAE968C517F46800AULL, FPU_REAL10_BIAS - 13, FALSE}, /* 1 / 6006 */
{0xA5E9F6ED347F0721ULL, FPU_REAL10_BIAS - 13, FALSE}, /* 1 / 6320 */
{0x9DDEDA75A1CD4726ULL, FPU_REAL10_BIAS - 13, FALSE}, /* 1 / 6642 */
{0x9665EE14DB2283E4ULL, FPU_REAL10_BIAS - 13, FALSE}, /* 1 / 6972 */
{0x8F71AD362448008FULL, FPU_REAL10_BIAS - 13, FALSE}, /* 1 / 7310 */
{0x88F61A371B048C2BULL, FPU_REAL10_BIAS - 13, FALSE}, /* 1 / 7656 */
{0x82E88A942AB2D933ULL, FPU_REAL10_BIAS - 13, FALSE}, /* 1 / 8010 */
{0xFA7EF5D91AC9538AULL, FPU_REAL10_BIAS - 14, FALSE}, /* 1 / 8372 */
{0xEFE4D31416B96CFEULL, FPU_REAL10_BIAS - 14, FALSE}, /* 1 / 8742 */
{0xE5F36CB00E5F36CBULL, FPU_REAL10_BIAS - 14, FALSE}, /* 1 / 9120 */
{0xDC9D0ECFCB6E9378ULL, FPU_REAL10_BIAS - 14, FALSE}, /* 1 / 9506 */
{0xD3D56292AB7E800DULL, FPU_REAL10_BIAS - 14, FALSE}, /* 1 / 9900 */
};
static const FAST486_FPU_DATA_REG FpuInverseNumberAtan[INVERSE_NUMBERS_COUNT] =
{
{0xAAAAAAAAAAAAAAAAULL, FPU_REAL10_BIAS - 1, FALSE}, /* 2 / 3 */
{0xCCCCCCCCCCCCCCCCULL, FPU_REAL10_BIAS - 1, FALSE}, /* 4 / 5 */
{0xDB6DB6DB6DB6DB6DULL, FPU_REAL10_BIAS - 1, FALSE}, /* 6 / 7 */
{0xE38E38E38E38E38EULL, FPU_REAL10_BIAS - 1, FALSE}, /* 8 / 9 */
{0xE8BA2E8BA2E8BA2EULL, FPU_REAL10_BIAS - 1, FALSE}, /* 10 / 11 */
{0xEC4EC4EC4EC4EC4EULL, FPU_REAL10_BIAS - 1, FALSE}, /* 12 / 13 */
{0xEEEEEEEEEEEEEEEEULL, FPU_REAL10_BIAS - 1, FALSE}, /* 14 / 15 */
{0xF0F0F0F0F0F0F0F0ULL, FPU_REAL10_BIAS - 1, FALSE}, /* 16 / 17 */
{0xF286BCA1AF286BCAULL, FPU_REAL10_BIAS - 1, FALSE}, /* 18 / 19 */
{0xF3CF3CF3CF3CF3CFULL, FPU_REAL10_BIAS - 1, FALSE}, /* 20 / 21 */
{0xF4DE9BD37A6F4DE9ULL, FPU_REAL10_BIAS - 1, FALSE}, /* 22 / 23 */
{0xF5C28F5C28F5C28FULL, FPU_REAL10_BIAS - 1, FALSE}, /* 24 / 25 */
{0xF684BDA12F684BDAULL, FPU_REAL10_BIAS - 1, FALSE}, /* 26 / 27 */
{0xF72C234F72C234F7ULL, FPU_REAL10_BIAS - 1, FALSE}, /* 28 / 29 */
{0xF7BDEF7BDEF7BDEFULL, FPU_REAL10_BIAS - 1, FALSE}, /* 30 / 31 */
{0xF83E0F83E0F83E0FULL, FPU_REAL10_BIAS - 1, FALSE}, /* 32 / 33 */
{0xF8AF8AF8AF8AF8AFULL, FPU_REAL10_BIAS - 1, FALSE}, /* 34 / 35 */
{0xF914C1BACF914C1BULL, FPU_REAL10_BIAS - 1, FALSE}, /* 36 / 37 */
{0xF96F96F96F96F96FULL, FPU_REAL10_BIAS - 1, FALSE}, /* 38 / 39 */
{0xF9C18F9C18F9C18FULL, FPU_REAL10_BIAS - 1, FALSE}, /* 40 / 41 */
{0xFA0BE82FA0BE82FAULL, FPU_REAL10_BIAS - 1, FALSE}, /* 42 / 43 */
{0xFA4FA4FA4FA4FA4FULL, FPU_REAL10_BIAS - 1, FALSE}, /* 44 / 45 */
{0xFA8D9DF51B3BEA36ULL, FPU_REAL10_BIAS - 1, FALSE}, /* 46 / 47 */
{0xFAC687D6343EB1A1ULL, FPU_REAL10_BIAS - 1, FALSE}, /* 48 / 49 */
{0xFAFAFAFAFAFAFAFAULL, FPU_REAL10_BIAS - 1, FALSE}, /* 50 / 51 */
{0xFB2B78C13521CFB2ULL, FPU_REAL10_BIAS - 1, FALSE}, /* 52 / 53 */
{0xFB586FB586FB586FULL, FPU_REAL10_BIAS - 1, FALSE}, /* 54 / 55 */
{0xFB823EE08FB823EEULL, FPU_REAL10_BIAS - 1, FALSE}, /* 56 / 57 */
{0xFBA9386822B63CBEULL, FPU_REAL10_BIAS - 1, FALSE}, /* 58 / 59 */
{0xFBCDA3AC10C9714FULL, FPU_REAL10_BIAS - 1, FALSE}, /* 60 / 61 */
{0xFBEFBEFBEFBEFBEFULL, FPU_REAL10_BIAS - 1, FALSE}, /* 62 / 63 */
{0xFC0FC0FC0FC0FC0FULL, FPU_REAL10_BIAS - 1, FALSE}, /* 64 / 65 */
{0xFC2DD9CA81E9131AULL, FPU_REAL10_BIAS - 1, FALSE}, /* 66 / 67 */
{0xFC4A33F128CFC4A3ULL, FPU_REAL10_BIAS - 1, FALSE}, /* 68 / 69 */
{0xFC64F52EDF8C9EA5ULL, FPU_REAL10_BIAS - 1, FALSE}, /* 70 / 71 */
{0xFC7E3F1F8FC7E3F1ULL, FPU_REAL10_BIAS - 1, FALSE}, /* 72 / 73 */
{0xFC962FC962FC962FULL, FPU_REAL10_BIAS - 1, FALSE}, /* 74 / 75 */
{0xFCACE213F2B3884FULL, FPU_REAL10_BIAS - 1, FALSE}, /* 76 / 77 */
{0xFCC26E2D5DF984DCULL, FPU_REAL10_BIAS - 1, FALSE}, /* 78 / 79 */
{0xFCD6E9E06522C3F3ULL, FPU_REAL10_BIAS - 1, FALSE}, /* 80 / 81 */
{0xFCEA68DE12818ACBULL, FPU_REAL10_BIAS - 1, FALSE}, /* 82 / 83 */
{0xFCFCFCFCFCFCFCFCULL, FPU_REAL10_BIAS - 1, FALSE}, /* 84 / 85 */
{0xFD0EB66FD0EB66FDULL, FPU_REAL10_BIAS - 1, FALSE}, /* 86 / 87 */
{0xFD1FA3F47E8FD1FAULL, FPU_REAL10_BIAS - 1, FALSE}, /* 88 / 89 */
{0xFD2FD2FD2FD2FD2FULL, FPU_REAL10_BIAS - 1, FALSE}, /* 90 / 91 */
{0xFD3F4FD3F4FD3F4FULL, FPU_REAL10_BIAS - 1, FALSE}, /* 92 / 93 */
{0xFD4E25B9EFD4E25BULL, FPU_REAL10_BIAS - 1, FALSE}, /* 94 / 95 */
{0xFD5C5F02A3A0FD5CULL, FPU_REAL10_BIAS - 1, FALSE}, /* 96 / 97 */
{0xFD6A052BF5A814AFULL, FPU_REAL10_BIAS - 1, FALSE}, /* 98 / 99 */
{0xFD7720F353A4C0A2ULL, FPU_REAL10_BIAS - 1, FALSE}, /* 100 / 101 */
};
/* PRIVATE FUNCTIONS **********************************************************/
#ifndef FAST486_NO_FPU
static ULONGLONG
UnsignedMult128(ULONGLONG Multiplicand,
ULONGLONG Multiplier,
ULONGLONG *HighProduct)
{
ULONG MultiplicandLow, MultiplicandHigh, MultiplierLow, MultiplierHigh;
ULONG IntermediateLow, IntermediateHigh;
ULONGLONG LowProduct, Intermediate, Intermediate1, Intermediate2;
MultiplicandLow = (ULONG)(Multiplicand & 0xFFFFFFFFULL);
MultiplicandHigh = (ULONG)(Multiplicand >> 32);
MultiplierLow = (ULONG)(Multiplier & 0xFFFFFFFFULL);
MultiplierHigh = (ULONG)(Multiplier >> 32);
LowProduct = (ULONGLONG)MultiplicandLow * (ULONGLONG)MultiplierLow;
Intermediate1 = (ULONGLONG)MultiplicandLow * (ULONGLONG)MultiplierHigh;
Intermediate2 = (ULONGLONG)MultiplicandHigh * (ULONGLONG)MultiplierLow;
*HighProduct = (ULONGLONG)MultiplicandHigh * (ULONGLONG)MultiplierHigh;
Intermediate = Intermediate1 + Intermediate2;
if (Intermediate < Intermediate1) *HighProduct += 1ULL << 32;
IntermediateLow = (ULONG)(Intermediate & 0xFFFFFFFFULL);
IntermediateHigh = (ULONG)(Intermediate >> 32);
LowProduct += (ULONGLONG)IntermediateLow << 32;
if ((ULONG)(LowProduct >> 32) < IntermediateLow) (*HighProduct)++;
*HighProduct += IntermediateHigh;
return LowProduct;
}
static ULONGLONG
UnsignedDivMod128(ULONGLONG DividendLow,
ULONGLONG DividendHigh,
ULONGLONG Divisor,
PULONGLONG QuotientLow,
PULONGLONG QuotientHigh)
{
ULONGLONG ValueLow = DividendLow;
ULONGLONG ValueHigh = DividendHigh;
ULONGLONG CurrentLow = 0ULL;
ULONGLONG CurrentHigh = Divisor;
ULONG Bits;
ASSERT(Divisor != 0ULL);
/* Initialize the quotient */
*QuotientLow = *QuotientHigh = 0ULL;
/* Exit early if the dividend is lower than the divisor */
if ((DividendHigh == 0ULL) && (DividendLow < Divisor)) return ValueLow;
/* Normalize the current divisor */
Bits = CountLeadingZeros64(CurrentHigh);
CurrentHigh <<= Bits;
while (TRUE)
{
/* Shift the quotient left by one bit */
*QuotientHigh <<= 1;
*QuotientHigh |= *QuotientLow >> 63;
*QuotientLow <<= 1;
/* Check if the value is higher than or equal to the current divisor */
if ((ValueHigh > CurrentHigh)
|| ((ValueHigh == CurrentHigh) && (ValueLow >= CurrentLow)))
{
BOOLEAN Carry = ValueLow < CurrentLow;
/* Subtract the current divisor from the value */
ValueHigh -= CurrentHigh;
ValueLow -= CurrentLow;
if (Carry) ValueHigh--;
/* Set the lowest bit of the quotient */
*QuotientLow |= 1;
/* Stop if the value is lower than the original divisor */
if ((ValueHigh == 0ULL) && (ValueLow < Divisor)) break;
}
/* Shift the current divisor right by one bit */
CurrentLow >>= 1;
CurrentLow |= (CurrentHigh & 1) << 63;
CurrentHigh >>= 1;
}
/*
* Calculate the number of significant bits the current
* divisor has more than the original divisor
*/
Bits = CountLeadingZeros64(Divisor);
if (CurrentHigh > 0ULL) Bits += 64 - CountLeadingZeros64(CurrentHigh);
else Bits -= CountLeadingZeros64(CurrentLow);
if (Bits >= 64)
{
*QuotientHigh = *QuotientLow;
*QuotientLow = 0ULL;
Bits -= 64;
}
if (Bits)
{
/* Shift the quotient left by that amount */
*QuotientHigh <<= Bits;
*QuotientHigh |= *QuotientLow >> (64 - Bits);
*QuotientLow <<= Bits;
}
/* Return the remainder */
return ValueLow;
}
static inline VOID FASTCALL
Fast486FpuRound(PFAST486_STATE State,
PULONGLONG Result,
BOOLEAN Sign,
ULONGLONG Remainder,
INT RemainderHighBit)
{
switch (State->FpuControl.Rc)
{
case FPU_ROUND_NEAREST:
{
/* Check if the highest bit of the remainder is set */
if (Remainder & (1ULL << RemainderHighBit))
{
(*Result)++;
/* Check if all the other bits are clear */
if (!(Remainder & ((1ULL << RemainderHighBit) - 1ULL)))
{
/* Round to even */
*Result &= ~1ULL;
}
}
break;
}
case FPU_ROUND_DOWN:
{
if ((Remainder != 0ULL) && Sign) (*Result)++;
break;
}
case FPU_ROUND_UP:
{
if ((Remainder != 0ULL) && !Sign) (*Result)++;
break;
}
default:
{
/* Leave it truncated */
}
}
}
static inline VOID FASTCALL
Fast486FpuFromInteger(PFAST486_STATE State,
LONGLONG Value,
PFAST486_FPU_DATA_REG Result)
{
ULONG ZeroCount;
Result->Sign = Result->Exponent = Result->Mantissa = 0;
if (Value == 0LL) return;
if (Value < 0LL)
{
Result->Sign = TRUE;
Value = -Value;
}
Result->Mantissa = (ULONGLONG)Value;
ZeroCount = CountLeadingZeros64(Result->Mantissa);
Result->Mantissa <<= ZeroCount;
Result->Exponent = FPU_REAL10_BIAS + 63 - ZeroCount;
}
static inline BOOLEAN FASTCALL
Fast486FpuToInteger(PFAST486_STATE State,
PCFAST486_FPU_DATA_REG Value,
PLONGLONG Result)
{
ULONG Bits;
ULONGLONG Remainder;
SHORT UnbiasedExp = (SHORT)Value->Exponent - FPU_REAL10_BIAS;
if (FPU_IS_ZERO(Value))
{
*Result = 0LL;
return TRUE;
}
if (FPU_IS_NAN(Value) || !FPU_IS_NORMALIZED(Value) || (UnbiasedExp >= 63))
{
/* Raise an invalid operation exception */
State->FpuStatus.Ie = TRUE;
if (State->FpuControl.Im)
{
*Result = 0x8000000000000000LL;
return TRUE;
}
else
{
return FALSE;
}
}
if (UnbiasedExp >= 0)
{
Bits = 63 - UnbiasedExp;
/* Calculate the result and the remainder */
*Result = (LONGLONG)(Value->Mantissa >> Bits);
Remainder = Value->Mantissa & ((1ULL << Bits) - 1);
}
else
{
/* The result is zero */
*Result = 0LL;
Bits = 64;
if (UnbiasedExp >= -64)
{
Remainder = Value->Mantissa >> (-1 - UnbiasedExp);
}
else
{
/* Too small to even have a remainder */
Remainder = 0ULL;
}
}
/* The result must be positive here */
ASSERT(*Result >= 0LL);
/* Perform rounding */
Fast486FpuRound(State, (PULONGLONG)Result, Value->Sign, Remainder, Bits - 1);
if (Value->Sign) *Result = -*Result;
return TRUE;
}
static inline VOID FASTCALL
Fast486FpuFromSingleReal(PFAST486_STATE State,
ULONG Value,
PFAST486_FPU_DATA_REG Result)
{
/* Extract the sign, exponent and mantissa */
Result->Sign = (UCHAR)(Value >> 31);
Result->Exponent = (USHORT)((Value >> 23) & 0xFF);
Result->Mantissa = (((ULONGLONG)Value & 0x7FFFFFULL) | 0x800000ULL) << 40;
/* If this is a zero, we're done */
if (Value == 0) return;
if (Result->Exponent == 0xFF) Result->Exponent = FPU_MAX_EXPONENT + 1;
else
{
/* Adjust the exponent bias */
Result->Exponent += (FPU_REAL10_BIAS - FPU_REAL4_BIAS);
}
}
static inline BOOLEAN FASTCALL
Fast486FpuToSingleReal(PFAST486_STATE State,
PCFAST486_FPU_DATA_REG Value,
PULONG Result)
{
ULONGLONG Remainder;
SHORT UnbiasedExp = (SHORT)Value->Exponent - FPU_REAL10_BIAS;
ULONGLONG Result64;
if (FPU_IS_ZERO(Value))
{
*Result = 0;
return TRUE;
}
/* Calculate the mantissa */
*Result = (ULONG)(Value->Mantissa >> 40) & 0x7FFFFF;
if (FPU_IS_NAN(Value))
{
*Result |= FPU_REAL4_INFINITY;
goto SetSign;
}
/* Check for underflow */
if (!FPU_IS_NORMALIZED(Value) || (UnbiasedExp < -127))
{
/* Raise the underflow exception */
State->FpuStatus.Ue = TRUE;
if (State->FpuControl.Um)
{
/* The result is zero due to underflow */
*Result = 0ULL;
return TRUE;
}
else
{
return FALSE;
}
}
/* Check for overflow */
if (UnbiasedExp > 127)
{
/* Raise the overflow exception */
State->FpuStatus.Oe = TRUE;
if (State->FpuControl.Om)
{
/* The result is infinity due to overflow */
*Result = FPU_REAL4_INFINITY;
goto SetSign;
}
else
{
return FALSE;
}
}
/* Calculate the remainder */
Remainder = Value->Mantissa & ((1ULL << 40) - 1);
/* Store the biased exponent */
*Result |= (ULONG)(UnbiasedExp + FPU_REAL4_BIAS) << 23;
/* Perform rounding */
Result64 = (ULONGLONG)*Result;
Fast486FpuRound(State, &Result64, Value->Sign, Remainder, 39);
*Result = (ULONG)Result64;
SetSign:
if (Value->Sign) *Result |= 0x80000000;
return TRUE;
}
static inline VOID FASTCALL
Fast486FpuFromDoubleReal(PFAST486_STATE State,
ULONGLONG Value,
PFAST486_FPU_DATA_REG Result)
{
/* Extract the sign, exponent and mantissa */
Result->Sign = (UCHAR)(Value >> 63);
Result->Exponent = (USHORT)((Value >> 52) & 0x7FF);
Result->Mantissa = (((ULONGLONG)Value & 0xFFFFFFFFFFFFFULL) | 0x10000000000000ULL) << 11;
/* If this is a zero, we're done */
if (Value == 0) return;
if (Result->Exponent == 0x7FF) Result->Exponent = FPU_MAX_EXPONENT + 1;
else
{
/* Adjust the exponent bias */
Result->Exponent += (FPU_REAL10_BIAS - FPU_REAL8_BIAS);
}
}
static inline BOOLEAN FASTCALL
Fast486FpuToDoubleReal(PFAST486_STATE State,
PCFAST486_FPU_DATA_REG Value,
PULONGLONG Result)
{
ULONGLONG Remainder;
SHORT UnbiasedExp = (SHORT)Value->Exponent - FPU_REAL10_BIAS;
if (FPU_IS_ZERO(Value))
{
*Result = 0LL;
return TRUE;
}
/* Calculate the mantissa */
*Result = (Value->Mantissa >> 11) & ((1ULL << 52) - 1);
if (FPU_IS_NAN(Value))
{
*Result |= FPU_REAL8_INFINITY;
goto SetSign;
}
/* Check for underflow */
if (!FPU_IS_NORMALIZED(Value) || (UnbiasedExp < -1023))
{
/* Raise the underflow exception */
State->FpuStatus.Ue = TRUE;
if (State->FpuControl.Um)
{
/* The result is zero due to underflow */
*Result = 0ULL;
return TRUE;
}
else
{
return FALSE;
}
}
/* Check for overflow */
if (UnbiasedExp > 1023)
{
/* Raise the overflow exception */
State->FpuStatus.Oe = TRUE;
if (State->FpuControl.Om)
{
/* The result is infinity due to overflow */
*Result = FPU_REAL8_INFINITY;
goto SetSign;
}
else
{
return FALSE;
}
}
/* Calculate the remainder */
Remainder = Value->Mantissa & ((1ULL << 11) - 1ULL);
/* Store the biased exponent */
*Result |= (ULONGLONG)(UnbiasedExp + FPU_REAL8_BIAS) << 52;
/* Perform rounding */
Fast486FpuRound(State, Result, Value->Sign, Remainder, 10);
SetSign:
if (Value->Sign) *Result |= 1ULL << 63;
return TRUE;
}
static inline VOID FASTCALL
Fast486FpuFromPackedBcd(PFAST486_STATE State,
PUCHAR Value,
PFAST486_FPU_DATA_REG Result)
{
INT i;
LONGLONG IntVal = 0LL;
for (i = 8; i >= 0; i--)
{
IntVal *= 100LL;
IntVal += (Value[i] >> 4) * 10 + (Value[i] & 0x0F);
}
/* Apply the sign */
if (Value[9] & 0x80) IntVal = -IntVal;
/* Now convert the integer to FP80 */
Fast486FpuFromInteger(State, IntVal, Result);
}
static inline BOOLEAN FASTCALL
Fast486FpuToPackedBcd(PFAST486_STATE State,
PCFAST486_FPU_DATA_REG Value,
PUCHAR Result)
{
INT i;
LONGLONG IntVal;
/* Convert it to an integer first */
if (!Fast486FpuToInteger(State, Value, &IntVal)) return FALSE;
if (IntVal < 0LL)
{
IntVal = -IntVal;
Result[9] = 0x80;
}
for (i = 0; i < 9; i++)
{
Result[i] = (UCHAR)((IntVal % 10) + (((IntVal / 10) % 10) << 4));
IntVal /= 100LL;
}
return TRUE;
}
static inline BOOLEAN FASTCALL
Fast486FpuAdd(PFAST486_STATE State,
PCFAST486_FPU_DATA_REG FirstOperand,
PCFAST486_FPU_DATA_REG SecondOperand,
PFAST486_FPU_DATA_REG Result)
{
FAST486_FPU_DATA_REG FirstAdjusted = *FirstOperand;
FAST486_FPU_DATA_REG SecondAdjusted = *SecondOperand;
FAST486_FPU_DATA_REG TempResult;
if (FPU_IS_INDEFINITE(FirstOperand)
|| FPU_IS_INDEFINITE(SecondOperand)
|| (FPU_IS_POS_INF(FirstOperand) && FPU_IS_NEG_INF(SecondOperand))
|| (FPU_IS_NEG_INF(FirstOperand) && FPU_IS_POS_INF(SecondOperand)))
{
/* The result will be indefinite */
Result->Sign = TRUE;
Result->Exponent = FPU_MAX_EXPONENT + 1;
Result->Mantissa = FPU_INDEFINITE_MANTISSA;
return TRUE;
}
if ((!FPU_IS_NORMALIZED(FirstOperand) || !FPU_IS_NORMALIZED(SecondOperand)))
{
/* Raise the denormalized exception */
State->FpuStatus.De = TRUE;
if (!State->FpuControl.Dm)
{
return FALSE;
}
}
if (FPU_IS_ZERO(FirstOperand) || FPU_IS_INFINITY(SecondOperand))
{
/* The second operand is the result */
*Result = *SecondOperand;
return TRUE;
}
if (FPU_IS_ZERO(SecondOperand) || FPU_IS_INFINITY(FirstOperand))
{
/* The first operand is the result */
*Result = *FirstOperand;
return TRUE;
}
/* Find the largest exponent */
TempResult.Exponent = max(FirstOperand->Exponent, SecondOperand->Exponent);
/* Adjust the first operand to it... */
if (FirstAdjusted.Exponent < TempResult.Exponent)
{
if ((TempResult.Exponent - FirstAdjusted.Exponent) < 64)
{
FirstAdjusted.Mantissa >>= (TempResult.Exponent - FirstAdjusted.Exponent);
FirstAdjusted.Exponent = TempResult.Exponent;
}
else
{
/* The second operand is the result */
*Result = *SecondOperand;
return TRUE;
}
}
/* ... and the second one too */
if (SecondAdjusted.Exponent < TempResult.Exponent)
{
if ((TempResult.Exponent - SecondAdjusted.Exponent) < 64)
{
SecondAdjusted.Mantissa >>= (TempResult.Exponent - SecondAdjusted.Exponent);
SecondAdjusted.Exponent = TempResult.Exponent;
}
else
{
/* The first operand is the result */
*Result = *FirstOperand;
return TRUE;
}
}
if (FirstAdjusted.Sign == SecondAdjusted.Sign)
{
/* Calculate the mantissa and sign of the result */
TempResult.Mantissa = FirstAdjusted.Mantissa + SecondAdjusted.Mantissa;
TempResult.Sign = FirstAdjusted.Sign;
}
else
{
/* Calculate the sign of the result */
if (FirstAdjusted.Mantissa > SecondAdjusted.Mantissa) TempResult.Sign = FirstAdjusted.Sign;
else if (FirstAdjusted.Mantissa < SecondAdjusted.Mantissa) TempResult.Sign = SecondAdjusted.Sign;
else TempResult.Sign = FALSE;
/* Invert the negative mantissa */
if (FirstAdjusted.Sign) FirstAdjusted.Mantissa = -(LONGLONG)FirstAdjusted.Mantissa;
if (SecondAdjusted.Sign) SecondAdjusted.Mantissa = -(LONGLONG)SecondAdjusted.Mantissa;
/* Calculate the mantissa of the result */
TempResult.Mantissa = FirstAdjusted.Mantissa + SecondAdjusted.Mantissa;
}
/* Did it overflow? */
if (FirstAdjusted.Sign == SecondAdjusted.Sign)
{
if (TempResult.Mantissa < FirstAdjusted.Mantissa
|| TempResult.Mantissa < SecondAdjusted.Mantissa)
{
if (TempResult.Exponent == FPU_MAX_EXPONENT)
{
/* Raise the overflow exception */
State->FpuStatus.Oe = TRUE;
if (State->FpuControl.Om)
{
/* Total overflow, return infinity */
TempResult.Mantissa = FPU_MANTISSA_HIGH_BIT;
TempResult.Exponent = FPU_MAX_EXPONENT + 1;
}
else
{
return FALSE;
}
}
else
{
/* Lose the LSB in favor of the carry */
TempResult.Mantissa >>= 1;
TempResult.Mantissa |= FPU_MANTISSA_HIGH_BIT;
TempResult.Exponent++;
}
}
}
else
{
if (TempResult.Mantissa >= FirstAdjusted.Mantissa
&& TempResult.Mantissa >= SecondAdjusted.Mantissa)
{
/* Reverse the mantissa */
TempResult.Mantissa = -(LONGLONG)TempResult.Mantissa;
}
}
/* Normalize the result and return it */
if (!Fast486FpuNormalize(State, &TempResult))
{
/* Exception occurred */
return FALSE;
}
*Result = TempResult;
return TRUE;
}
static inline BOOLEAN FASTCALL
Fast486FpuSubtract(PFAST486_STATE State,
PCFAST486_FPU_DATA_REG FirstOperand,
PCFAST486_FPU_DATA_REG SecondOperand,
PFAST486_FPU_DATA_REG Result)
{
FAST486_FPU_DATA_REG NegativeSecondOperand = *SecondOperand;
/* Invert the sign */
NegativeSecondOperand.Sign = !NegativeSecondOperand.Sign;
/* And perform an addition instead */
return Fast486FpuAdd(State, FirstOperand, &NegativeSecondOperand, Result);
}
static inline VOID FASTCALL
Fast486FpuCompare(PFAST486_STATE State,
PCFAST486_FPU_DATA_REG FirstOperand,
PCFAST486_FPU_DATA_REG SecondOperand)
{
if (FPU_IS_NAN(FirstOperand) || FPU_IS_NAN(SecondOperand))
{
if ((FPU_IS_POS_INF(FirstOperand)
&& (!FPU_IS_NAN(SecondOperand) || FPU_IS_NEG_INF(SecondOperand)))
|| (!FPU_IS_NAN(FirstOperand) && FPU_IS_NEG_INF(SecondOperand)))
{
State->FpuStatus.Code0 = FALSE;
State->FpuStatus.Code2 = FALSE;
State->FpuStatus.Code3 = FALSE;
}
else if ((FPU_IS_POS_INF(SecondOperand)
&& (!FPU_IS_NAN(FirstOperand) || FPU_IS_NEG_INF(FirstOperand)))
|| (!FPU_IS_NAN(SecondOperand) && FPU_IS_NEG_INF(FirstOperand)))
{
State->FpuStatus.Code0 = TRUE;
State->FpuStatus.Code2 = FALSE;
State->FpuStatus.Code3 = FALSE;
}
else
{
State->FpuStatus.Code0 = TRUE;
State->FpuStatus.Code2 = TRUE;
State->FpuStatus.Code3 = TRUE;
}
}
else
{
FAST486_FPU_DATA_REG TempResult;
Fast486FpuSubtract(State, FirstOperand, SecondOperand, &TempResult);
if (FPU_IS_ZERO(&TempResult))
{
State->FpuStatus.Code0 = FALSE;
State->FpuStatus.Code2 = FALSE;
State->FpuStatus.Code3 = TRUE;
}
else if (TempResult.Sign)
{
State->FpuStatus.Code0 = TRUE;
State->FpuStatus.Code2 = FALSE;
State->FpuStatus.Code3 = FALSE;
}
else
{
State->FpuStatus.Code0 = FALSE;
State->FpuStatus.Code2 = FALSE;
State->FpuStatus.Code3 = FALSE;
}
}
}
static inline BOOLEAN FASTCALL
Fast486FpuMultiply(PFAST486_STATE State,
PCFAST486_FPU_DATA_REG FirstOperand,
PCFAST486_FPU_DATA_REG SecondOperand,
PFAST486_FPU_DATA_REG Result)
{
FAST486_FPU_DATA_REG TempResult;
LONG Exponent;
if (FPU_IS_INDEFINITE(FirstOperand)
|| FPU_IS_INDEFINITE(SecondOperand)
|| (FPU_IS_ZERO(FirstOperand) && FPU_IS_INFINITY(SecondOperand))
|| (FPU_IS_INFINITY(FirstOperand) && FPU_IS_ZERO(SecondOperand)))
{
/* The result will be indefinite */
Result->Sign = TRUE;
Result->Exponent = FPU_MAX_EXPONENT + 1;
Result->Mantissa = FPU_INDEFINITE_MANTISSA;
return TRUE;
}
if (FPU_IS_ZERO(FirstOperand) || FPU_IS_ZERO(SecondOperand))
{
/* The result will be zero */
Result->Sign = FirstOperand->Sign ^ SecondOperand->Sign;
Result->Exponent = 0;
Result->Mantissa = 0ULL;
return TRUE;
}
if (FPU_IS_INFINITY(FirstOperand) || FPU_IS_INFINITY(SecondOperand))
{
/* The result will be infinity */
Result->Sign = FirstOperand->Sign ^ SecondOperand->Sign;
Result->Exponent = FPU_MAX_EXPONENT + 1;
Result->Mantissa = FPU_MANTISSA_HIGH_BIT;
return TRUE;
}
if ((!FPU_IS_NORMALIZED(FirstOperand) || !FPU_IS_NORMALIZED(SecondOperand)))
{
/* Raise the denormalized exception */
State->FpuStatus.De = TRUE;
if (!State->FpuControl.Dm)
{
return FALSE;
}
}
/* Calculate the sign */
TempResult.Sign = FirstOperand->Sign ^ SecondOperand->Sign;
/* Calculate the exponent */
Exponent = (LONG)FirstOperand->Exponent + (LONG)SecondOperand->Exponent - FPU_REAL10_BIAS + 1;
/* Calculate the mantissa */
UnsignedMult128(FirstOperand->Mantissa,
SecondOperand->Mantissa,
&TempResult.Mantissa);
if (Exponent < 0)
{
/* Raise the underflow exception */
State->FpuStatus.Ue = TRUE;
if (!State->FpuControl.Um)
{
return FALSE;
}
/* The exponent will be zero */
TempResult.Exponent = 0;
/* If possible, denormalize the result, otherwise make it zero */
if (Exponent > -64) TempResult.Mantissa >>= (-Exponent);
else TempResult.Mantissa = 0ULL;
}
else if (Exponent > FPU_MAX_EXPONENT)
{
/* Raise the overflow exception */
State->FpuStatus.Oe = TRUE;
if (!State->FpuControl.Om)
{
return FALSE;
}
/* Make the result infinity */
TempResult.Exponent = FPU_MAX_EXPONENT + 1;
TempResult.Mantissa = FPU_MANTISSA_HIGH_BIT;
}
else TempResult.Exponent = (USHORT)Exponent;
/* Normalize the result */
if (!Fast486FpuNormalize(State, &TempResult))
{
/* Exception occurred */
return FALSE;
}
*Result = TempResult;
return TRUE;
}
static inline BOOLEAN FASTCALL
Fast486FpuDivide(PFAST486_STATE State,
PCFAST486_FPU_DATA_REG FirstOperand,
PCFAST486_FPU_DATA_REG SecondOperand,
PFAST486_FPU_DATA_REG Result)
{
FAST486_FPU_DATA_REG TempResult;
ULONGLONG QuotientLow, QuotientHigh, Remainder;
LONG Exponent;
if (FPU_IS_INDEFINITE(FirstOperand)
|| FPU_IS_INDEFINITE(SecondOperand)
|| (FPU_IS_INFINITY(FirstOperand) && FPU_IS_INFINITY(SecondOperand))
|| (FPU_IS_ZERO(FirstOperand) && FPU_IS_ZERO(SecondOperand)))
{
/* Raise the invalid operation exception */
State->FpuStatus.Ie = TRUE;
if (State->FpuControl.Im)
{
/* Return the indefinite NaN */
Result->Sign = TRUE;
Result->Exponent = FPU_MAX_EXPONENT + 1;
Result->Mantissa = FPU_INDEFINITE_MANTISSA;
return TRUE;
}
else
{
return FALSE;
}
}
if (FPU_IS_ZERO(SecondOperand) || FPU_IS_INFINITY(FirstOperand))
{
/* Raise the division by zero exception */
State->FpuStatus.Ze = TRUE;
if (State->FpuControl.Zm)
{
/* Return infinity */
Result->Sign = FirstOperand->Sign;
Result->Exponent = FPU_MAX_EXPONENT + 1;
Result->Mantissa = FPU_MANTISSA_HIGH_BIT;
return TRUE;
}
else
{
return FALSE;
}
}
/* Calculate the sign of the result */
TempResult.Sign = FirstOperand->Sign ^ SecondOperand->Sign;
if (FPU_IS_ZERO(FirstOperand) || FPU_IS_INFINITY(SecondOperand))
{
/* Return zero */
Result->Sign = TempResult.Sign;
Result->Mantissa = 0ULL;
Result->Exponent = 0;
return TRUE;
}
/* Calculate the exponent of the result */
Exponent = (LONG)FirstOperand->Exponent - (LONG)SecondOperand->Exponent - 1;
/* Divide the two mantissas */
Remainder = UnsignedDivMod128(0ULL,
FirstOperand->Mantissa,
SecondOperand->Mantissa,
&QuotientLow,
&QuotientHigh);
UNREFERENCED_PARAMETER(Remainder); // TODO: Rounding
TempResult.Mantissa = QuotientLow;
if (QuotientHigh > 0ULL)
{
ULONG BitsToShift = 64 - CountLeadingZeros64(QuotientHigh);
TempResult.Mantissa >>= BitsToShift;
TempResult.Mantissa |= QuotientHigh << (64 - BitsToShift);
Exponent += BitsToShift;
// TODO: Rounding
}
if (Exponent < -FPU_REAL10_BIAS)
{
TempResult.Mantissa >>= -(Exponent + FPU_REAL10_BIAS);
Exponent = -FPU_REAL10_BIAS;
// TODO: Rounding
}
TempResult.Exponent = (USHORT)(Exponent + FPU_REAL10_BIAS);
/* Normalize the result */
if (!Fast486FpuNormalize(State, &TempResult))
{
/* Exception occurred */
return FALSE;
}
*Result = TempResult;
return TRUE;
}
/*
* Calculates using the identity:
* 2 ^ x - 1 = 1 + sum { 2 * (((x - 1) * ln(2)) ^ n) / n! }
*/
static inline VOID FASTCALL
Fast486FpuCalculateTwoPowerMinusOne(PFAST486_STATE State,
PCFAST486_FPU_DATA_REG Operand,
PFAST486_FPU_DATA_REG Result)
{
INT i;
FAST486_FPU_DATA_REG TempResult = FpuOne;
FAST486_FPU_DATA_REG Value;
FAST486_FPU_DATA_REG SeriesElement;
/* Calculate the first series element, which is 2 * (x - 1) * ln(2) */
if (!Fast486FpuSubtract(State, Operand, &FpuOne, &Value)) return;
if (!Fast486FpuMultiply(State, &Value, &FpuLnTwo, &Value)) return;
if (!Fast486FpuAdd(State, &Value, &Value, &SeriesElement)) return;
for (i = 2; i <= INVERSE_NUMBERS_COUNT; i++)
{
/* Add the series element to the final sum */
if (!Fast486FpuAdd(State, &TempResult, &SeriesElement, &TempResult))
{
/* An exception occurred */
return;
}
/*
* Calculate the next series element (partially) by multiplying
* it with (x - 1) * ln(2)
*/
if (!Fast486FpuMultiply(State, &SeriesElement, &Value, &SeriesElement))
{
/* An exception occurred */
return;
}
/* And now multiply the series element by the inverse counter */
if (!Fast486FpuMultiply(State,
&SeriesElement,
&FpuInverseNumber[i - 1],
&SeriesElement))
{
/* An exception occurred */
return;
}
}
*Result = TempResult;
}
static inline BOOLEAN FASTCALL
Fast486FpuCalculateLogBase2(PFAST486_STATE State,
PCFAST486_FPU_DATA_REG Operand,
PFAST486_FPU_DATA_REG Result)
{
INT i;
FAST486_FPU_DATA_REG Value = *Operand;
FAST486_FPU_DATA_REG TempResult;
FAST486_FPU_DATA_REG TempValue;
LONGLONG UnbiasedExp = (LONGLONG)Operand->Exponent - FPU_REAL10_BIAS;
if (Operand->Sign)
{
/* Raise the invalid operation exception */
State->FpuStatus.Ie = TRUE;
if (State->FpuControl.Im)
{
/* Return the indefinite NaN */
Result->Sign = TRUE;
Result->Exponent = FPU_MAX_EXPONENT + 1;
Result->Mantissa = FPU_INDEFINITE_MANTISSA;
return TRUE;
}
else
{
return FALSE;
}
}
/* Get only the mantissa as a floating-pointer number between 1 and 2 */
Value.Exponent = FPU_REAL10_BIAS;
/* Check if it's denormalized */
if (!FPU_IS_NORMALIZED(&Value))
{
ULONG Bits;
State->FpuStatus.De = TRUE;
if (!State->FpuControl.Dm)
{
return FALSE;
}
/* Normalize the number */
Bits = CountLeadingZeros64(Value.Mantissa);
UnbiasedExp -= Bits;
Value.Mantissa <<= Bits;
}
TempResult.Sign = FALSE;
TempResult.Exponent = FPU_REAL10_BIAS - 1;
TempResult.Mantissa = 0ULL;
for (i = 63; i >= 0; i--)
{
/* Square the value */
if (!Fast486FpuMultiply(State, &Value, &Value, &Value)) return FALSE;
/* Subtract two from it */
if (!Fast486FpuSubtract(State, &Value, &FpuTwo, &TempValue)) return FALSE;
/* Is the result positive? */
if (!TempValue.Sign)
{
/* Yes, set the appropriate bit in the mantissa */
TempResult.Mantissa |= 1ULL << i;
/* Halve the value */
if (!Fast486FpuMultiply(State, &Value, &FpuInverseNumber[1], &Value)) return FALSE;
}
}
/* Normalize the result */
if (!Fast486FpuNormalize(State, &TempResult)) return FALSE;
/*
* Add the exponent to the result
* log2(x * 2^y) = log2(x) + log2(2^y) = log2(x) + y
*/
Fast486FpuFromInteger(State, UnbiasedExp, &TempValue);
if (!Fast486FpuAdd(State, &TempValue, &TempResult, &TempResult)) return FALSE;
*Result = TempResult;
return TRUE;
}
static inline BOOLEAN FASTCALL
Fast486FpuRemainder(PFAST486_STATE State,
PCFAST486_FPU_DATA_REG FirstOperand,
PCFAST486_FPU_DATA_REG SecondOperand,
BOOLEAN RoundToNearest,
PFAST486_FPU_DATA_REG Result OPTIONAL,
PLONGLONG Quotient OPTIONAL)
{
BOOLEAN Success = FALSE;
INT OldRoundingMode = State->FpuControl.Rc;
LONGLONG Integer;
FAST486_FPU_DATA_REG Temp;
if (!Fast486FpuDivide(State, FirstOperand, SecondOperand, &Temp)) return FALSE;
State->FpuControl.Rc = RoundToNearest ? FPU_ROUND_NEAREST : FPU_ROUND_TRUNCATE;
if (!Fast486FpuToInteger(State, &Temp, &Integer)) goto Cleanup;
if (Result)
{
Fast486FpuFromInteger(State, Integer, &Temp);
if (!Fast486FpuMultiply(State, &Temp, SecondOperand, &Temp)) goto Cleanup;
if (!Fast486FpuSubtract(State, FirstOperand, &Temp, Result)) goto Cleanup;
}
if (Quotient) *Quotient = Integer;
Success = TRUE;
Cleanup:
State->FpuControl.Rc = OldRoundingMode;
return Success;
}
/*
* Calculates using the identity:
* sin(x) = sum { -1^n * x^(2n + 1) / (2n + 1)!, n >= 0 }
*/
static inline BOOLEAN FASTCALL
Fast486FpuCalculateSine(PFAST486_STATE State,
PCFAST486_FPU_DATA_REG Operand,
PFAST486_FPU_DATA_REG Result)
{
INT i;
ULONGLONG Quadrant;
FAST486_FPU_DATA_REG Normalized = *Operand;
FAST486_FPU_DATA_REG TempResult;
FAST486_FPU_DATA_REG OperandSquared;
FAST486_FPU_DATA_REG SeriesElement;
PCFAST486_FPU_DATA_REG Inverse;
if (!Fast486FpuRemainder(State,
Operand,
&FpuHalfPi,
FALSE,
&Normalized,
(PLONGLONG)&Quadrant))
{
return FALSE;
}
/* Normalize the quadrant number */
Quadrant &= 3;
if (!(Quadrant & 1))
{
/* This is a sine */
Inverse = FpuInverseNumberSine;
TempResult = SeriesElement = Normalized;
}
else
{
/* This is a cosine */
Inverse = FpuInverseNumberCosine;
TempResult = SeriesElement = FpuOne;
}
/* Calculate the square of the operand */
if (!Fast486FpuMultiply(State, &Normalized, &Normalized, &OperandSquared)) return FALSE;
for (i = 0; i < INVERSE_NUMBERS_COUNT; i++)
{
if (!Fast486FpuMultiply(State, &SeriesElement, &OperandSquared, &SeriesElement))
{
/* An exception occurred */
return FALSE;
}
if (!Fast486FpuMultiply(State,
&SeriesElement,
&Inverse[i],
&SeriesElement))
{
/* An exception occurred */
return FALSE;
}
/* Toggle the sign of the series element */
SeriesElement.Sign = !SeriesElement.Sign;
if (!Fast486FpuAdd(State, &TempResult, &SeriesElement, &TempResult))
{
/* An exception occurred */
return FALSE;
}
}
/* Flip the sign for the third and fourth quadrant */
if (Quadrant >= 2) TempResult.Sign = !TempResult.Sign;
*Result = TempResult;
return TRUE;
}
/*
* Calculates using the identity:
* cos(x) = sum { -1^n * x^(2n) / (2n)!, n >= 0 }
*/
static inline BOOLEAN FASTCALL
Fast486FpuCalculateCosine(PFAST486_STATE State,
PCFAST486_FPU_DATA_REG Operand,
PFAST486_FPU_DATA_REG Result)
{
FAST486_FPU_DATA_REG Value = *Operand;
/* Add pi / 2 */
if (!Fast486FpuAdd(State, &Value, &FpuHalfPi, &Value)) return FALSE;
/* Calculate the sine */
return Fast486FpuCalculateSine(State, &Value, Result);
}
static inline VOID FASTCALL
Fast486FpuArithmeticOperation(PFAST486_STATE State,
INT Operation,
PFAST486_FPU_DATA_REG Operand,
BOOLEAN TopDestination)
{
PFAST486_FPU_DATA_REG DestOperand = TopDestination ? &FPU_ST(0) : Operand;
ASSERT(!(Operation & ~7));
/* Check the operation */
switch (Operation)
{
/* FADD */
case 0:
{
Fast486FpuAdd(State, &FPU_ST(0), Operand, DestOperand);
break;
}
/* FMUL */
case 1:
{
Fast486FpuMultiply(State, &FPU_ST(0), Operand, DestOperand);
break;
}
/* FCOM */
case 2:
/* FCOMP */
case 3:
{
Fast486FpuCompare(State, &FPU_ST(0), Operand);
if (Operation == 3) Fast486FpuPop(State);
break;
}
/* FSUB */
case 4:
{
Fast486FpuSubtract(State, &FPU_ST(0), Operand, DestOperand);
break;
}
/* FSUBR */
case 5:
{
Fast486FpuSubtract(State, Operand, &FPU_ST(0), DestOperand);
break;
}
/* FDIV */
case 6:
{
Fast486FpuDivide(State, &FPU_ST(0), Operand, DestOperand);
break;
}
/* FDIVR */
case 7:
{
Fast486FpuDivide(State, Operand, &FPU_ST(0), DestOperand);
break;
}
}
}
/*
* Calculates using:
* x[0] = s
* x[n + 1] = (x[n] + s / x[n]) / 2
*/
static inline BOOLEAN FASTCALL
Fast486FpuCalculateSquareRoot(PFAST486_STATE State,
PCFAST486_FPU_DATA_REG Operand,
PFAST486_FPU_DATA_REG Result)
{
FAST486_FPU_DATA_REG Value = *Operand;
FAST486_FPU_DATA_REG PrevValue = FpuZero;
if (Operand->Sign)
{
/* Raise the invalid operation exception */
State->FpuStatus.Ie = TRUE;
if (State->FpuControl.Im)
{
/* Return the indefinite NaN */
Result->Sign = TRUE;
Result->Exponent = FPU_MAX_EXPONENT + 1;
Result->Mantissa = FPU_INDEFINITE_MANTISSA;
return TRUE;
}
else
{
return FALSE;
}
}
/* Loop until it converges */
while (Value.Sign != PrevValue.Sign
|| Value.Exponent != PrevValue.Exponent
|| Value.Mantissa != PrevValue.Mantissa)
{
FAST486_FPU_DATA_REG Temp;
/* Save the current value */
PrevValue = Value;
/* Divide the operand by the current value */
if (!Fast486FpuDivide(State, Operand, &Value, &Temp)) return FALSE;
/* Add the result of that division to the current value */
if (!Fast486FpuAdd(State, &Value, &Temp, &Value)) return FALSE;
/* Halve the current value */
if (!Fast486FpuMultiply(State, &Value, &FpuInverseNumber[1], &Value)) return FALSE;
}
*Result = Value;
return TRUE;
}
/*
* Calculates arctan using Euler's formula:
* arctan(x) = (x / (1 + x^2)) * sum { prod { (2j * x^2)
* / ((2j + 1) * (1 + x^2)), j >= 1, j <= i }, i >= 0 }
*/
static inline BOOLEAN FASTCALL
Fast486FpuCalculateArcTangent(PFAST486_STATE State,
PCFAST486_FPU_DATA_REG Numerator,
PCFAST486_FPU_DATA_REG Denominator,
PFAST486_FPU_DATA_REG Result)
{
INT i;
BOOLEAN Inverted = FALSE;
FAST486_FPU_DATA_REG TempNumerator = *Numerator;
FAST486_FPU_DATA_REG TempDenominator = *Denominator;
FAST486_FPU_DATA_REG Value;
FAST486_FPU_DATA_REG TempResult;
FAST486_FPU_DATA_REG ValDivValSqP1;
FAST486_FPU_DATA_REG SeriesElement = FpuOne;
TempNumerator.Sign = FALSE;
TempDenominator.Sign = FALSE;
/* Compare the numerator to the denominator */
if (!Fast486FpuSubtract(State, &TempNumerator, &TempDenominator, &TempResult))
{
return FALSE;
}
if ((Inverted = !TempResult.Sign))
{
if (!Fast486FpuDivide(State, &TempDenominator, &TempNumerator, &Value))
{
return FALSE;
}
}
else
{
if (!Fast486FpuDivide(State, &TempNumerator, &TempDenominator, &Value))
{
return FALSE;
}
}
/* Apparently, atan2(0, 0) = +/- 0 or +/- pi for some reason... */
if (FPU_IS_INDEFINITE(&Value)) Value = FpuZero;
/* Calculate the value divided by the value squared plus one */
if (!Fast486FpuMultiply(State, &Value, &Value, &ValDivValSqP1)) return FALSE;
if (!Fast486FpuAdd(State, &ValDivValSqP1, &FpuOne, &ValDivValSqP1)) return FALSE;
if (!Fast486FpuDivide(State, &Value, &ValDivValSqP1, &ValDivValSqP1)) return FALSE;
TempResult = FpuOne;
for (i = 0; i < INVERSE_NUMBERS_COUNT; i++)
{
if (!Fast486FpuMultiply(State, &SeriesElement, &Value, &SeriesElement))
{
/* An exception occurred */
return FALSE;
}
if (!Fast486FpuMultiply(State, &SeriesElement, &ValDivValSqP1, &SeriesElement))
{
/* An exception occurred */
return FALSE;
}
if (!Fast486FpuMultiply(State,
&SeriesElement,
&FpuInverseNumberAtan[i],
&SeriesElement))
{
/* An exception occurred */
return FALSE;
}
if (!Fast486FpuAdd(State, &TempResult, &SeriesElement, &TempResult))
{
/* An exception occurred */
return FALSE;
}
}
if (!Fast486FpuMultiply(State, &TempResult, &ValDivValSqP1, &TempResult))
{
/* An exception occurred */
return FALSE;
}
if (Inverted)
{
/* Since y/x is positive, arctan(y/x) = pi/2 - arctan(x/y) */
if (!Fast486FpuSubtract(State, &FpuHalfPi, &TempResult, &TempResult)) return FALSE;
}
/* Adjust the sign */
if (!(!Numerator->Sign == !Denominator->Sign)) TempResult.Sign = !TempResult.Sign;
if (Denominator->Sign)
{
if (Numerator->Sign)
{
/* Subtract PI */
if (!Fast486FpuSubtract(State, &TempResult, &FpuPi, &TempResult)) return FALSE;
}
else
{
/* Add PI */
if (!Fast486FpuAdd(State, &TempResult, &FpuPi, &TempResult)) return FALSE;
}
}
*Result = TempResult;
return TRUE;
}
static inline BOOLEAN FASTCALL
Fast486FpuLoadEnvironment(PFAST486_STATE State,
INT Segment,
ULONG Address,
BOOLEAN Size)
{
UCHAR Buffer[28];
if (!Fast486ReadMemory(State, Segment, Address, FALSE, Buffer, (Size + 1) * 14))
{
/* Exception occurred */
return FALSE;
}
/* Check if this is a 32-bit save or a 16-bit save */
if (Size)
{
PULONG Data = (PULONG)Buffer;
State->FpuControl.Value = (USHORT)Data[0];
State->FpuStatus.Value = (USHORT)Data[1];
State->FpuTag = (USHORT)Data[2];
State->FpuLastInstPtr.Long = Data[3];
State->FpuLastCodeSel = (USHORT)Data[4];
State->FpuLastOpPtr.Long = Data[5];
State->FpuLastDataSel = (USHORT)Data[6];
}
else
{
PUSHORT Data = (PUSHORT)Buffer;
State->FpuControl.Value = Data[0];
State->FpuStatus.Value = Data[1];
State->FpuTag = Data[2];
State->FpuLastInstPtr.LowWord = Data[3];
State->FpuLastCodeSel = Data[4];
State->FpuLastOpPtr.LowWord = Data[5];
State->FpuLastDataSel = Data[6];
}
return TRUE;
}
static inline BOOLEAN FASTCALL
Fast486FpuSaveEnvironment(PFAST486_STATE State,
INT Segment,
ULONG Address,
BOOLEAN Size)
{
UCHAR Buffer[28];
/* Check if this is a 32-bit save or a 16-bit save */
if (Size)
{
PULONG Data = (PULONG)Buffer;
Data[0] = (ULONG)State->FpuControl.Value;
Data[1] = (ULONG)State->FpuStatus.Value;
Data[2] = (ULONG)State->FpuTag;
Data[3] = State->FpuLastInstPtr.Long;
Data[4] = (ULONG)State->FpuLastCodeSel;
Data[5] = State->FpuLastOpPtr.Long;
Data[6] = (ULONG)State->FpuLastDataSel;
}
else
{
PUSHORT Data = (PUSHORT)Buffer;
Data[0] = State->FpuControl.Value;
Data[1] = State->FpuStatus.Value;
Data[2] = State->FpuTag;
Data[3] = State->FpuLastInstPtr.LowWord;
Data[4] = State->FpuLastCodeSel;
Data[5] = State->FpuLastOpPtr.LowWord;
Data[6] = State->FpuLastDataSel;
}
return Fast486WriteMemory(State, Segment, Address, Buffer, (Size + 1) * 14);
}
#endif
/* PUBLIC FUNCTIONS ***********************************************************/
FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD8)
{
FAST486_MOD_REG_RM ModRegRm;
BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
#ifndef FAST486_NO_FPU
PFAST486_FPU_DATA_REG Operand;
FAST486_FPU_DATA_REG MemoryData;
#endif
TOGGLE_ADSIZE(AddressSize);
/* Get the operands */
if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
{
/* Exception occurred */
return;
}
FPU_CHECK();
#ifndef FAST486_NO_FPU
Fast486FpuExceptionCheck(State);
FPU_SAVE_LAST_INST();
if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
{
/* Raise the invalid operation exception */
State->FpuStatus.Ie = TRUE;
if (State->FpuControl.Im)
{
/* Return the indefinite NaN */
FPU_ST(0).Sign = TRUE;
FPU_ST(0).Exponent = FPU_MAX_EXPONENT + 1;
FPU_ST(0).Mantissa = FPU_INDEFINITE_MANTISSA;
FPU_SET_TAG(0, FPU_TAG_SPECIAL);
}
return;
}
if (ModRegRm.Memory)
{
/* Load the source operand from memory */
ULONG Value;
if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, NULL, &Value))
{
/* Exception occurred */
return;
}
Fast486FpuFromSingleReal(State, Value, &MemoryData);
Operand = &MemoryData;
FPU_SAVE_LAST_OPERAND();
}
else
{
if (FPU_GET_TAG(ModRegRm.SecondRegister) == FPU_TAG_EMPTY)
{
/* Raise the invalid operation exception */
State->FpuStatus.Ie = TRUE;
if (State->FpuControl.Im)
{
/* Return the indefinite NaN */
FPU_ST(0).Sign = TRUE;
FPU_ST(0).Exponent = FPU_MAX_EXPONENT + 1;
FPU_ST(0).Mantissa = FPU_INDEFINITE_MANTISSA;
FPU_SET_TAG(0, FPU_TAG_SPECIAL);
}
return;
}
/* Load the source operand from an FPU register */
Operand = &FPU_ST(ModRegRm.SecondRegister);
}
/* Perform the requested operation */
Fast486FpuArithmeticOperation(State, ModRegRm.Register, Operand, TRUE);
#endif
}
FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD9)
{
FAST486_MOD_REG_RM ModRegRm;
BOOLEAN OperandSize, AddressSize;
OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
TOGGLE_OPSIZE(OperandSize);
TOGGLE_ADSIZE(AddressSize);
/* Get the operands */
if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
{
/* Exception occurred */
return;
}
FPU_CHECK();
#ifndef FAST486_NO_FPU
if (ModRegRm.Memory)
{
switch (ModRegRm.Register)
{
/* FLD */
case 0:
{
ULONG Value;
FAST486_FPU_DATA_REG MemoryData;
Fast486FpuExceptionCheck(State);
FPU_SAVE_LAST_INST();
FPU_SAVE_LAST_OPERAND();
if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, NULL, &Value))
{
/* Exception occurred */
return;
}
Fast486FpuFromSingleReal(State, Value, &MemoryData);
Fast486FpuPush(State, &MemoryData);
break;
}
/* FST */
case 2:
/* FSTP */
case 3:
{
ULONG Value = FPU_REAL4_INDEFINITE;
Fast486FpuExceptionCheck(State);
FPU_SAVE_LAST_INST();
FPU_SAVE_LAST_OPERAND();
if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
{
/* Raise the invalid operation exception */
State->FpuStatus.Ie = TRUE;
if (!State->FpuControl.Im)
{
return;
}
}
else if (!Fast486FpuToSingleReal(State, &FPU_ST(0), &Value))
{
/* Exception occurred */
return;
}
if (!Fast486WriteModrmDwordOperands(State, &ModRegRm, FALSE, Value))
{
/* Exception occurred */
return;
}
if (ModRegRm.Register == 3) Fast486FpuPop(State);
break;
}
/* FLDENV */
case 4:
{
Fast486FpuLoadEnvironment(State,
(State->PrefixFlags & FAST486_PREFIX_SEG)
? State->SegmentOverride : FAST486_REG_DS,
ModRegRm.MemoryAddress,
OperandSize);
break;
}
/* FLDCW */
case 5:
{
Fast486ReadModrmWordOperands(State, &ModRegRm, NULL, &State->FpuControl.Value);
break;
}
/* FSTENV */
case 6:
{
Fast486FpuSaveEnvironment(State,
(State->PrefixFlags & FAST486_PREFIX_SEG)
? State->SegmentOverride : FAST486_REG_DS,
ModRegRm.MemoryAddress,
OperandSize);
break;
}
/* FSTCW */
case 7:
{
Fast486WriteModrmWordOperands(State, &ModRegRm, FALSE, State->FpuControl.Value);
break;
}
/* Invalid */
default:
{
Fast486Exception(State, FAST486_EXCEPTION_UD);
return;
}
}
}
else
{
switch ((ModRegRm.Register << 3) | ModRegRm.SecondRegister)
{
/* FLD */
case 0x00:
case 0x01:
case 0x02:
case 0x03:
case 0x04:
case 0x05:
case 0x06:
case 0x07:
{
Fast486FpuExceptionCheck(State);
FPU_SAVE_LAST_INST();
if (FPU_GET_TAG(ModRegRm.SecondRegister) == FPU_TAG_EMPTY)
{
/* Raise the invalid operation exception */
State->FpuStatus.Ie = TRUE;
if (!State->FpuControl.Im)
{
return;
}
}
Fast486FpuPush(State, &FPU_ST(ModRegRm.SecondRegister));
break;
}
/* FXCH */
case 0x08:
case 0x09:
case 0x0A:
case 0x0B:
case 0x0C:
case 0x0D:
case 0x0E:
case 0x0F:
{
FAST486_FPU_DATA_REG Temp;
Fast486FpuExceptionCheck(State);
FPU_SAVE_LAST_INST();
if ((FPU_GET_TAG(0) == FPU_TAG_EMPTY)
|| FPU_GET_TAG(ModRegRm.SecondRegister) == FPU_TAG_EMPTY)
{
State->FpuStatus.Ie = TRUE;
break;
}
/* Exchange */
Temp = FPU_ST(0);
FPU_ST(0) = FPU_ST(ModRegRm.SecondRegister);
FPU_ST(ModRegRm.SecondRegister) = Temp;
FPU_UPDATE_TAG(0);
FPU_UPDATE_TAG(ModRegRm.SecondRegister);
break;
}
/* FNOP */
case 0x10:
{
/* Do nothing */
break;
}
/* FSTP */
case 0x18:
case 0x19:
case 0x1A:
case 0x1B:
case 0x1C:
case 0x1D:
case 0x1E:
case 0x1F:
{
Fast486FpuExceptionCheck(State);
FPU_SAVE_LAST_INST();
FPU_ST(ModRegRm.SecondRegister) = FPU_ST(0);
FPU_UPDATE_TAG(ModRegRm.SecondRegister);
Fast486FpuPop(State);
break;
}
/* FCHS */
case 0x20:
{
Fast486FpuExceptionCheck(State);
FPU_SAVE_LAST_INST();
if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
{
State->FpuStatus.Ie = TRUE;
break;
}
/* Invert the sign */
FPU_ST(0).Sign = !FPU_ST(0).Sign;
break;
}
/* FABS */
case 0x21:
{
Fast486FpuExceptionCheck(State);
FPU_SAVE_LAST_INST();
if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
{
State->FpuStatus.Ie = TRUE;
break;
}
/* Set the sign to positive */
FPU_ST(0).Sign = FALSE;
break;
}
/* FTST */
case 0x24:
{
Fast486FpuExceptionCheck(State);
FPU_SAVE_LAST_INST();
Fast486FpuCompare(State, &FPU_ST(0), &FpuZero);
break;
}
/* FXAM */
case 0x25:
{
Fast486FpuExceptionCheck(State);
FPU_SAVE_LAST_INST();
/* The sign bit goes in C1, even if the register's empty */
State->FpuStatus.Code1 = FPU_ST(0).Sign;
if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
{
State->FpuStatus.Code0 = 1;
State->FpuStatus.Code2 = 0;
State->FpuStatus.Code3 = 1;
}
else if (FPU_GET_TAG(0) == FPU_TAG_SPECIAL)
{
if (FPU_IS_INFINITY(&FPU_ST(0)))
{
State->FpuStatus.Code0 = 1;
State->FpuStatus.Code2 = 1;
State->FpuStatus.Code3 = 0;
}
else
{
State->FpuStatus.Code0 = 1;
State->FpuStatus.Code2 = 0;
State->FpuStatus.Code3 = 0;
}
}
else if (FPU_GET_TAG(0) == FPU_TAG_ZERO)
{
State->FpuStatus.Code0 = 0;
State->FpuStatus.Code2 = 0;
State->FpuStatus.Code3 = 1;
}
else
{
if (FPU_IS_NORMALIZED(&FPU_ST(0)))
{
State->FpuStatus.Code0 = 0;
State->FpuStatus.Code2 = 1;
State->FpuStatus.Code3 = 0;
}
else
{
State->FpuStatus.Code0 = 0;
State->FpuStatus.Code2 = 1;
State->FpuStatus.Code3 = 1;
}
}
break;
}
/* FLD1 */
case 0x28:
/* FLDL2T */
case 0x29:
/* FLDL2E */
case 0x2A:
/* FLDPI */
case 0x2B:
/* FLDLG2 */
case 0x2C:
/* FLDLN2 */
case 0x2D:
/* FLDZ */
case 0x2E:
{
PCFAST486_FPU_DATA_REG Constants[] =
{
&FpuOne,
&FpuL2Ten,
&FpuL2E,
&FpuPi,
&FpuLgTwo,
&FpuLnTwo,
&FpuZero
};
Fast486FpuExceptionCheck(State);
FPU_SAVE_LAST_INST();
Fast486FpuPush(State, Constants[ModRegRm.SecondRegister]);
break;
}
/* F2XM1 */
case 0x30:
{
Fast486FpuExceptionCheck(State);
FPU_SAVE_LAST_INST();
if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
{
State->FpuStatus.Ie = TRUE;
break;
}
if (!FPU_IS_NORMALIZED(&FPU_ST(0)))
{
State->FpuStatus.De = TRUE;
if (!State->FpuControl.Dm)
{
break;
}
}
Fast486FpuCalculateTwoPowerMinusOne(State, &FPU_ST(0), &FPU_ST(0));
FPU_UPDATE_TAG(0);
break;
}
/* FYL2X */
case 0x31:
{
FAST486_FPU_DATA_REG Logarithm;
Fast486FpuExceptionCheck(State);
FPU_SAVE_LAST_INST();
if (FPU_GET_TAG(0) == FPU_TAG_EMPTY || FPU_GET_TAG(1) == FPU_TAG_EMPTY)
{
State->FpuStatus.Ie = TRUE;
break;
}
if (!Fast486FpuCalculateLogBase2(State, &FPU_ST(0), &Logarithm))
{
/* Exception occurred */
break;
}
if (!Fast486FpuMultiply(State, &Logarithm, &FPU_ST(1), &FPU_ST(1)))
{
/* Exception occurred */
break;
}
/* Pop the stack so that the result ends up in ST0 */
Fast486FpuPop(State);
FPU_UPDATE_TAG(0);
break;
}
/* FPTAN */
case 0x32:
{
FAST486_FPU_DATA_REG Sine;
FAST486_FPU_DATA_REG Cosine;
ULONGLONG Quadrant;
Fast486FpuExceptionCheck(State);
FPU_SAVE_LAST_INST();
/* Compute the sine */
if (!Fast486FpuCalculateSine(State, &FPU_ST(0), &Sine)) break;
/* Normalize the angle */
if (!Fast486FpuRemainder(State,
&FPU_ST(0),
&FpuHalfPi,
FALSE,
NULL,
(PLONGLONG)&Quadrant))
{
break;
}
/* Normalize the quadrant number */
Quadrant &= 3;
/* Find the cosine by calculating sqrt(1 - sin(x) ^ 2) */
if (!Fast486FpuMultiply(State, &Sine, &Sine, &Cosine)) break;
if (!Fast486FpuSubtract(State, &FpuOne, &Cosine, &Cosine)) break;
if (!Fast486FpuCalculateSquareRoot(State, &Cosine, &Cosine)) break;
/* Adjust the sign of the cosine */
if (Quadrant == 1 || Quadrant == 2) Cosine.Sign = TRUE;
/* Divide the sine by the cosine to get the tangent */
if (!Fast486FpuDivide(State, &Sine, &Cosine, &FPU_ST(0))) break;
FPU_UPDATE_TAG(0);
/* Push 1.00 */
Fast486FpuPush(State, &FpuOne);
break;
}
/* FPATAN */
case 0x33:
{
Fast486FpuExceptionCheck(State);
FPU_SAVE_LAST_INST();
if (!Fast486FpuCalculateArcTangent(State,
&FPU_ST(1),
&FPU_ST(0),
&FPU_ST(1)))
{
break;
}
FPU_UPDATE_TAG(1);
Fast486FpuPop(State);
break;
}
/* FXTRACT */
case 0x34:
{
FAST486_FPU_DATA_REG Value = FPU_ST(0);
Fast486FpuExceptionCheck(State);
FPU_SAVE_LAST_INST();
if ((FPU_GET_TAG(0) == FPU_TAG_EMPTY) || FPU_IS_INDEFINITE(&Value))
{
State->FpuStatus.Ie = TRUE;
if (FPU_GET_TAG(0) == FPU_TAG_EMPTY) State->FpuStatus.Sf = TRUE;
break;
}
if (FPU_IS_ZERO(&Value))
{
/* The exponent of zero is negative infinity */
FPU_ST(0).Sign = TRUE;
FPU_ST(0).Exponent = FPU_MAX_EXPONENT + 1;
FPU_ST(0).Mantissa = FPU_MANTISSA_HIGH_BIT;
}
else if (FPU_IS_INFINITY(&Value))
{
/* The exponent of infinity is positive infinity */
FPU_ST(0).Sign = FALSE;
FPU_ST(0).Exponent = FPU_MAX_EXPONENT + 1;
FPU_ST(0).Mantissa = FPU_MANTISSA_HIGH_BIT;
}
else
{
/* Store the unbiased exponent in ST0 */
Fast486FpuFromInteger(State,
(LONGLONG)Value.Exponent - (LONGLONG)FPU_REAL10_BIAS,
&FPU_ST(0));
}
/* Now push the mantissa as a real number, with the original sign */
Value.Exponent = FPU_REAL10_BIAS;
Fast486FpuPush(State, &Value);
break;
}
/* FPREM1 */
case 0x35:
/* FPREM */
case 0x38:
{
LONGLONG Quotient;
Fast486FpuExceptionCheck(State);
FPU_SAVE_LAST_INST();
if (FPU_GET_TAG(0) == FPU_TAG_EMPTY || FPU_GET_TAG(1) == FPU_TAG_EMPTY)
{
State->FpuStatus.Ie = TRUE;
break;
}
if (Fast486FpuRemainder(State,
&FPU_ST(0),
&FPU_ST(1),
ModRegRm.Register == 6, /* TRUE if it's FPREM1 */
&FPU_ST(0),
&Quotient))
{
FPU_UPDATE_TAG(0);
/* Return the lowest 3 bits of the quotient in C1, C3, C0 */
State->FpuStatus.Code1 = Quotient & 1;
State->FpuStatus.Code3 = (Quotient >> 1) & 1;
State->FpuStatus.Code0 = (Quotient >> 2) & 1;
}
break;
}
/* FDECSTP */
case 0x36:
{
State->FpuStatus.Top--;
break;
}
/* FINCSTP */
case 0x37:
{
State->FpuStatus.Top++;
break;
}
/* FYL2XP1 */
case 0x39:
{
FAST486_FPU_DATA_REG Value, Logarithm;
Fast486FpuExceptionCheck(State);
FPU_SAVE_LAST_INST();
if (FPU_GET_TAG(0) == FPU_TAG_EMPTY || FPU_GET_TAG(1) == FPU_TAG_EMPTY)
{
State->FpuStatus.Ie = TRUE;
break;
}
if (!Fast486FpuAdd(State, &FPU_ST(0), &FpuOne, &Value))
{
/* Exception occurred */
break;
}
if (!Fast486FpuCalculateLogBase2(State, &Value, &Logarithm))
{
/* Exception occurred */
break;
}
if (!Fast486FpuMultiply(State, &Logarithm, &FPU_ST(1), &FPU_ST(1)))
{
/* Exception occurred */
break;
}
/* Pop the stack so that the result ends up in ST0 */
Fast486FpuPop(State);
FPU_UPDATE_TAG(0);
break;
}
/* FSQRT */
case 0x3A:
{
Fast486FpuExceptionCheck(State);
FPU_SAVE_LAST_INST();
Fast486FpuCalculateSquareRoot(State, &FPU_ST(0), &FPU_ST(0));
FPU_UPDATE_TAG(0);
break;
}
/* FSINCOS */
case 0x3B:
{
FAST486_FPU_DATA_REG Number = FPU_ST(0);
Fast486FpuExceptionCheck(State);
FPU_SAVE_LAST_INST();
if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
{
State->FpuStatus.Ie = TRUE;
break;
}
if (!FPU_IS_NORMALIZED(&FPU_ST(0)))
{
State->FpuStatus.De = TRUE;
if (!State->FpuControl.Dm)
{
break;
}
}
/* Replace FP0 with the sine */
if (!Fast486FpuCalculateSine(State, &Number, &FPU_ST(0))) break;
FPU_UPDATE_TAG(0);
/* Push the cosine */
if (!Fast486FpuCalculateCosine(State, &Number, &Number)) break;
Fast486FpuPush(State, &Number);
break;
}
/* FRNDINT */
case 0x3C:
{
LONGLONG Result = 0LL;
Fast486FpuExceptionCheck(State);
FPU_SAVE_LAST_INST();
if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
{
State->FpuStatus.Ie = TRUE;
break;
}
if (!FPU_IS_NORMALIZED(&FPU_ST(0)))
{
State->FpuStatus.De = TRUE;
if (!State->FpuControl.Dm)
{
break;
}
}
/* Do nothing if it's too big to not be an integer */
if (FPU_ST(0).Exponent >= FPU_REAL10_BIAS + 63) break;
/* Perform the rounding */
Fast486FpuToInteger(State, &FPU_ST(0), &Result);
Fast486FpuFromInteger(State, Result, &FPU_ST(0));
State->FpuStatus.Pe = TRUE;
break;
}
/* FSCALE */
case 0x3D:
{
LONGLONG Scale;
LONGLONG UnbiasedExp = (LONGLONG)((SHORT)FPU_ST(0).Exponent) - FPU_REAL10_BIAS;
INT OldRoundingMode = State->FpuControl.Rc;
Fast486FpuExceptionCheck(State);
FPU_SAVE_LAST_INST();
if (FPU_GET_TAG(0) == FPU_TAG_EMPTY || FPU_GET_TAG(1) == FPU_TAG_EMPTY)
{
State->FpuStatus.Ie = TRUE;
break;
}
if (!FPU_IS_NORMALIZED(&FPU_ST(0)))
{
State->FpuStatus.De = TRUE;
if (!State->FpuControl.Dm)
{
break;
}
}
State->FpuControl.Rc = FPU_ROUND_TRUNCATE;
if (!Fast486FpuToInteger(State, &FPU_ST(1), &Scale))
{
/* Exception occurred */
State->FpuControl.Rc = OldRoundingMode;
break;
}
State->FpuControl.Rc = OldRoundingMode;
/* Adjust the unbiased exponent */
UnbiasedExp += Scale;
/* Check for underflow */
if (UnbiasedExp < -1023)
{
/* Raise the underflow exception */
State->FpuStatus.Ue = TRUE;
if (State->FpuControl.Um)
{
/* Make the result zero */
FPU_ST(0) = FpuZero;
FPU_UPDATE_TAG(0);
}
break;
}
/* Check for overflow */
if (UnbiasedExp > 1023)
{
/* Raise the overflow exception */
State->FpuStatus.Oe = TRUE;
if (State->FpuControl.Om)
{
/* Make the result infinity */
FPU_ST(0).Mantissa = FPU_MANTISSA_HIGH_BIT;
FPU_ST(0).Exponent = FPU_MAX_EXPONENT + 1;
FPU_UPDATE_TAG(0);
}
break;
}
FPU_ST(0).Exponent = (USHORT)(UnbiasedExp + FPU_REAL10_BIAS);
FPU_UPDATE_TAG(0);
break;
}
/* FSIN */
case 0x3E:
{
Fast486FpuExceptionCheck(State);
FPU_SAVE_LAST_INST();
if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
{
State->FpuStatus.Ie = TRUE;
break;
}
if (!FPU_IS_NORMALIZED(&FPU_ST(0)))
{
State->FpuStatus.De = TRUE;
if (!State->FpuControl.Dm)
{
break;
}
}
Fast486FpuCalculateSine(State, &FPU_ST(0), &FPU_ST(0));
FPU_UPDATE_TAG(0);
break;
}
/* FCOS */
case 0x3F:
{
Fast486FpuExceptionCheck(State);
FPU_SAVE_LAST_INST();
if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
{
State->FpuStatus.Ie = TRUE;
break;
}
if (!FPU_IS_NORMALIZED(&FPU_ST(0)))
{
State->FpuStatus.De = TRUE;
if (!State->FpuControl.Dm)
{
break;
}
}
Fast486FpuCalculateCosine(State, &FPU_ST(0), &FPU_ST(0));
FPU_UPDATE_TAG(0);
break;
}
/* Invalid */
default:
{
Fast486Exception(State, FAST486_EXCEPTION_UD);
return;
}
}
}
#endif
}
FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDA)
{
FAST486_MOD_REG_RM ModRegRm;
BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
#ifndef FAST486_NO_FPU
LONG Value;
FAST486_FPU_DATA_REG MemoryData;
#endif
TOGGLE_ADSIZE(AddressSize);
/* Get the operands */
if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
{
/* Exception occurred */
return;
}
FPU_CHECK();
#ifndef FAST486_NO_FPU
Fast486FpuExceptionCheck(State);
FPU_SAVE_LAST_INST();
if (!ModRegRm.Memory)
{
/* The only valid opcode in this case is FUCOMPP (0xDA 0xE9) */
if ((ModRegRm.Register != 5) && (ModRegRm.SecondRegister != 1))
{
Fast486Exception(State, FAST486_EXCEPTION_UD);
return;
}
if ((FPU_GET_TAG(0) == FPU_TAG_EMPTY) || (FPU_GET_TAG(1) == FPU_TAG_EMPTY))
{
/* Raise the invalid operation exception*/
State->FpuStatus.Ie = TRUE;
return;
}
/* Compare */
Fast486FpuCompare(State, &FPU_ST(0), &FPU_ST(1));
/* Pop twice */
Fast486FpuPop(State);
Fast486FpuPop(State);
return;
}
FPU_SAVE_LAST_OPERAND();
/* Load the source operand from memory */
if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, NULL, (PULONG)&Value))
{
/* Exception occurred */
return;
}
if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
{
/* Raise the invalid operation exception */
State->FpuStatus.Ie = TRUE;
if (State->FpuControl.Im)
{
/* Return the indefinite NaN */
FPU_ST(0).Sign = TRUE;
FPU_ST(0).Exponent = FPU_MAX_EXPONENT + 1;
FPU_ST(0).Mantissa = FPU_INDEFINITE_MANTISSA;
FPU_SET_TAG(0, FPU_TAG_SPECIAL);
}
return;
}
Fast486FpuFromInteger(State, (LONGLONG)Value, &MemoryData);
/* Perform the requested operation */
Fast486FpuArithmeticOperation(State, ModRegRm.Register, &MemoryData, TRUE);
#endif
}
FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDB)
{
FAST486_MOD_REG_RM ModRegRm;
BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
TOGGLE_ADSIZE(AddressSize);
/* Get the operands */
if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
{
/* Exception occurred */
return;
}
FPU_CHECK();
#ifndef FAST486_NO_FPU
if (ModRegRm.Memory)
{
Fast486FpuExceptionCheck(State);
FPU_SAVE_LAST_INST();
FPU_SAVE_LAST_OPERAND();
switch (ModRegRm.Register)
{
/* FILD */
case 0:
{
LONG Value;
FAST486_FPU_DATA_REG Temp;
if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, NULL, (PULONG)&Value))
{
/* Exception occurred */
return;
}
Fast486FpuFromInteger(State, (LONGLONG)Value, &Temp);
Fast486FpuPush(State, &Temp);
break;
}
/* FIST */
case 2:
/* FISTP */
case 3:
{
LONGLONG Temp = 0;
if ((FPU_GET_TAG(0) == FPU_TAG_EMPTY) || (FPU_GET_TAG(0) == FPU_TAG_SPECIAL))
{
/* Raise the invalid operation exception */
State->FpuStatus.Ie = TRUE;
if (!State->FpuControl.Im)
{
return;
}
}
if (!Fast486FpuToInteger(State, &FPU_ST(0), &Temp))
{
/* Exception occurred */
return;
}
/* Check if it can fit in a signed 32-bit integer */
if ((LONGLONG)((LONG)Temp) != Temp)
{
State->FpuStatus.Ie = TRUE;
if (State->FpuControl.Im)
{
Temp = 0x80000000LL;
}
else
{
return;
}
}
if (!Fast486WriteModrmDwordOperands(State, &ModRegRm, FALSE, (ULONG)((LONG)Temp)))
{
/* Exception occurred */
return;
}
if (ModRegRm.Register == 3)
{
/* Pop the FPU stack too */
Fast486FpuPop(State);
}
break;
}
/* FLD */
case 5:
{
FAST486_FPU_DATA_REG Value;
UCHAR Buffer[10];
if (!Fast486ReadMemory(State,
(State->PrefixFlags & FAST486_PREFIX_SEG)
? State->SegmentOverride : FAST486_REG_DS,
ModRegRm.MemoryAddress,
FALSE,
Buffer,
sizeof(Buffer)))
{
/* Exception occurred */
return;
}
Value.Mantissa = *((PULONGLONG)Buffer);
Value.Exponent = *((PUSHORT)&Buffer[8]) & (FPU_MAX_EXPONENT + 1);
Value.Sign = *((PUCHAR)&Buffer[9]) >> 7;
Fast486FpuPush(State, &Value);
break;
}
/* FSTP */
case 7:
{
UCHAR Buffer[10];
if (FPU_GET_TAG(0) != FPU_TAG_EMPTY)
{
*((PULONGLONG)Buffer) = FPU_ST(0).Mantissa;
*((PUSHORT)&Buffer[sizeof(ULONGLONG)]) = FPU_ST(0).Exponent
| (FPU_ST(0).Sign ? 0x8000 : 0);
}
else
{
/* Raise the invalid operation exception */
State->FpuStatus.Ie = TRUE;
if (State->FpuControl.Im)
{
*((PULONGLONG)Buffer) = FPU_INDEFINITE_MANTISSA;
*((PUSHORT)&Buffer[sizeof(ULONGLONG)]) = 0x8000 | (FPU_MAX_EXPONENT + 1);
}
else
{
return;
}
}
if (!Fast486WriteMemory(State,
(State->PrefixFlags & FAST486_PREFIX_SEG)
? State->SegmentOverride : FAST486_REG_DS,
ModRegRm.MemoryAddress,
Buffer,
sizeof(Buffer)))
{
/* Exception occurred */
return;
}
Fast486FpuPop(State);
break;
}
/* Invalid */
default:
{
Fast486Exception(State, FAST486_EXCEPTION_UD);
return;
}
}
}
else
{
/* Only a few of these instructions have any meaning on a 487 */
switch ((ModRegRm.Register << 3) | ModRegRm.SecondRegister)
{
/* FCLEX */
case 0x22:
{
/* Clear exception data */
State->FpuStatus.Ie =
State->FpuStatus.De =
State->FpuStatus.Ze =
State->FpuStatus.Oe =
State->FpuStatus.Ue =
State->FpuStatus.Pe =
State->FpuStatus.Sf =
State->FpuStatus.Es =
State->FpuStatus.Busy = FALSE;
break;
}
/* FINIT */
case 0x23:
{
/* Restore the state */
State->FpuControl.Value = FAST486_FPU_DEFAULT_CONTROL;
State->FpuStatus.Value = 0;
State->FpuTag = 0xFFFF;
break;
}
/* FENI */
case 0x20:
/* FDISI */
case 0x21:
/* FSETPM */
case 0x24:
/* FRSTPM */
case 0x25:
{
/* These do nothing */
break;
}
/* Invalid */
default:
{
Fast486Exception(State, FAST486_EXCEPTION_UD);
return;
}
}
}
#endif
}
FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDC)
{
FAST486_MOD_REG_RM ModRegRm;
BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
#ifndef FAST486_NO_FPU
PFAST486_FPU_DATA_REG Operand;
FAST486_FPU_DATA_REG MemoryData;
#endif
TOGGLE_ADSIZE(AddressSize);
/* Get the operands */
if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
{
/* Exception occurred */
return;
}
FPU_CHECK();
#ifndef FAST486_NO_FPU
Fast486FpuExceptionCheck(State);
FPU_SAVE_LAST_INST();
if (ModRegRm.Memory)
{
ULONGLONG Value;
if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
{
/* Raise the invalid operation exception */
State->FpuStatus.Ie = TRUE;
if (State->FpuControl.Im)
{
/* Return the indefinite NaN */
FPU_ST(0).Sign = TRUE;
FPU_ST(0).Exponent = FPU_MAX_EXPONENT + 1;
FPU_ST(0).Mantissa = FPU_INDEFINITE_MANTISSA;
FPU_SET_TAG(0, FPU_TAG_SPECIAL);
}
return;
}
/* Load the source operand from memory */
if (!Fast486ReadMemory(State,
(State->PrefixFlags & FAST486_PREFIX_SEG)
? State->SegmentOverride : FAST486_REG_DS,
ModRegRm.MemoryAddress,
FALSE,
&Value,
sizeof(ULONGLONG)))
{
/* Exception occurred */
return;
}
Fast486FpuFromDoubleReal(State, Value, &MemoryData);
Operand = &MemoryData;
FPU_SAVE_LAST_OPERAND();
}
else
{
/* Load the destination operand from an FPU register */
Operand = &FPU_ST(ModRegRm.SecondRegister);
if ((FPU_GET_TAG(0) == FPU_TAG_EMPTY)
|| (FPU_GET_TAG(ModRegRm.SecondRegister) == FPU_TAG_EMPTY))
{
/* Raise the invalid operation exception */
State->FpuStatus.Ie = TRUE;
if (State->FpuControl.Im)
{
/* Return the indefinite NaN */
Operand->Sign = TRUE;
Operand->Exponent = FPU_MAX_EXPONENT + 1;
Operand->Mantissa = FPU_INDEFINITE_MANTISSA;
FPU_SET_TAG(ModRegRm.SecondRegister, FPU_TAG_SPECIAL);
}
return;
}
}
/* Perform the requested operation */
Fast486FpuArithmeticOperation(State, ModRegRm.Register, Operand, ModRegRm.Memory);
#endif
}
FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDD)
{
FAST486_MOD_REG_RM ModRegRm;
BOOLEAN OperandSize, AddressSize;
OperandSize = AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
TOGGLE_OPSIZE(OperandSize);
TOGGLE_ADSIZE(AddressSize);
/* Get the operands */
if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
{
/* Exception occurred */
return;
}
FPU_CHECK();
#ifndef FAST486_NO_FPU
if (ModRegRm.Memory)
{
switch (ModRegRm.Register)
{
/* FLD */
case 0:
{
ULONGLONG Value;
FAST486_FPU_DATA_REG MemoryData;
Fast486FpuExceptionCheck(State);
FPU_SAVE_LAST_INST();
FPU_SAVE_LAST_OPERAND();
if (!Fast486ReadMemory(State,
(State->PrefixFlags & FAST486_PREFIX_SEG)
? State->SegmentOverride : FAST486_REG_DS,
ModRegRm.MemoryAddress,
FALSE,
&Value,
sizeof(ULONGLONG)))
{
/* Exception occurred */
return;
}
Fast486FpuFromDoubleReal(State, Value, &MemoryData);
Fast486FpuPush(State, &MemoryData);
break;
}
/* FST */
case 2:
/* FSTP */
case 3:
{
ULONGLONG Value = FPU_REAL8_INDEFINITE;
Fast486FpuExceptionCheck(State);
FPU_SAVE_LAST_INST();
FPU_SAVE_LAST_OPERAND();
if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
{
/* Raise the invalid operation exception */
State->FpuStatus.Ie = TRUE;
if (!State->FpuControl.Im)
{
return;
}
}
else if (!Fast486FpuToDoubleReal(State, &FPU_ST(0), &Value))
{
/* Exception occurred */
return;
}
if (!Fast486WriteMemory(State,
(State->PrefixFlags & FAST486_PREFIX_SEG)
? State->SegmentOverride : FAST486_REG_DS,
ModRegRm.MemoryAddress,
&Value,
sizeof(ULONGLONG)))
{
/* Exception occurred */
return;
}
if (ModRegRm.Register == 3) Fast486FpuPop(State);
break;
}
/* FRSTOR */
case 4:
{
INT i;
UCHAR AllRegs[80];
/* Save the environment */
if (!Fast486FpuLoadEnvironment(State,
(State->PrefixFlags & FAST486_PREFIX_SEG)
? State->SegmentOverride : FAST486_REG_DS,
ModRegRm.MemoryAddress,
OperandSize))
{
/* Exception occurred */
return;
}
/* Load the registers */
if (!Fast486ReadMemory(State,
(State->PrefixFlags & FAST486_PREFIX_SEG)
? State->SegmentOverride : FAST486_REG_DS,
ModRegRm.MemoryAddress + (OperandSize + 1) * 14,
FALSE,
AllRegs,
sizeof(AllRegs)))
{
/* Exception occurred */
return;
}
for (i = 0; i < FAST486_NUM_FPU_REGS; i++)
{
State->FpuRegisters[i].Mantissa = *((PULONGLONG)&AllRegs[i * 10]);
State->FpuRegisters[i].Exponent = *((PUSHORT)&AllRegs[(i * 10) + sizeof(ULONGLONG)]) & 0x7FFF;
if (*((PUSHORT)&AllRegs[(i * 10) + sizeof(ULONGLONG)]) & 0x8000)
{
State->FpuRegisters[i].Sign = TRUE;
}
else
{
State->FpuRegisters[i].Sign = FALSE;
}
}
break;
}
/* FSAVE */
case 6:
{
INT i;
UCHAR AllRegs[80];
/* Save the environment */
if (!Fast486FpuSaveEnvironment(State,
(State->PrefixFlags & FAST486_PREFIX_SEG)
? State->SegmentOverride : FAST486_REG_DS,
ModRegRm.MemoryAddress,
OperandSize))
{
/* Exception occurred */
return;
}
/* Save the registers */
for (i = 0; i < FAST486_NUM_FPU_REGS; i++)
{
*((PULONGLONG)&AllRegs[i * 10]) = State->FpuRegisters[i].Mantissa;
*((PUSHORT)&AllRegs[(i * 10) + sizeof(ULONGLONG)]) = State->FpuRegisters[i].Exponent;
if (State->FpuRegisters[i].Sign)
{
*((PUSHORT)&AllRegs[(i * 10) + sizeof(ULONGLONG)]) |= 0x8000;
}
}
Fast486WriteMemory(State,
(State->PrefixFlags & FAST486_PREFIX_SEG)
? State->SegmentOverride : FAST486_REG_DS,
ModRegRm.MemoryAddress + (OperandSize + 1) * 14,
AllRegs,
sizeof(AllRegs));
break;
}
/* FSTSW */
case 7:
{
Fast486WriteModrmWordOperands(State, &ModRegRm, FALSE, State->FpuStatus.Value);
break;
}
/* Invalid */
default:
{
Fast486Exception(State, FAST486_EXCEPTION_UD);
}
}
}
else
{
switch (ModRegRm.Register)
{
/* FFREE */
case 0:
{
FPU_SET_TAG(ModRegRm.SecondRegister, FPU_TAG_EMPTY);
break;
}
/* FXCH */
case 1:
{
FAST486_FPU_DATA_REG Temp;
FPU_SAVE_LAST_INST();
Fast486FpuExceptionCheck(State);
if ((FPU_GET_TAG(0) == FPU_TAG_EMPTY)
|| FPU_GET_TAG(ModRegRm.SecondRegister) == FPU_TAG_EMPTY)
{
State->FpuStatus.Ie = TRUE;
break;
}
/* Exchange */
Temp = FPU_ST(0);
FPU_ST(0) = FPU_ST(ModRegRm.SecondRegister);
FPU_ST(ModRegRm.SecondRegister) = Temp;
FPU_UPDATE_TAG(0);
FPU_UPDATE_TAG(ModRegRm.SecondRegister);
break;
}
/* FST */
case 2:
/* FSTP */
case 3:
{
FPU_SAVE_LAST_INST();
Fast486FpuExceptionCheck(State);
FPU_ST(ModRegRm.SecondRegister) = FPU_ST(0);
FPU_UPDATE_TAG(ModRegRm.SecondRegister);
if (ModRegRm.Register == 3) Fast486FpuPop(State);
break;
}
/* FUCOM */
case 4:
/* FUCOMP */
case 5:
{
FPU_SAVE_LAST_INST();
Fast486FpuExceptionCheck(State);
if ((FPU_GET_TAG(0) == FPU_TAG_EMPTY)
|| (FPU_GET_TAG(ModRegRm.SecondRegister) == FPU_TAG_EMPTY))
{
State->FpuStatus.Ie = TRUE;
return;
}
Fast486FpuCompare(State, &FPU_ST(0), &FPU_ST(ModRegRm.SecondRegister));
if (ModRegRm.Register == 5) Fast486FpuPop(State);
break;
}
/* Invalid */
default:
{
Fast486Exception(State, FAST486_EXCEPTION_UD);
}
}
}
#endif
}
FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDE)
{
FAST486_MOD_REG_RM ModRegRm;
BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
#ifndef FAST486_NO_FPU
PFAST486_FPU_DATA_REG Operand;
FAST486_FPU_DATA_REG MemoryData;
#endif
TOGGLE_ADSIZE(AddressSize);
/* Get the operands */
if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
{
/* Exception occurred */
return;
}
FPU_CHECK();
#ifndef FAST486_NO_FPU
FPU_SAVE_LAST_INST();
Fast486FpuExceptionCheck(State);
if (ModRegRm.Memory)
{
SHORT Value;
if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
{
/* Raise the invalid operation exception */
State->FpuStatus.Ie = TRUE;
if (State->FpuControl.Im)
{
/* Return the indefinite NaN */
FPU_ST(0).Sign = TRUE;
FPU_ST(0).Exponent = FPU_MAX_EXPONENT + 1;
FPU_ST(0).Mantissa = FPU_INDEFINITE_MANTISSA;
FPU_SET_TAG(0, FPU_TAG_SPECIAL);
}
return;
}
/* Load the source operand from memory */
if (!Fast486ReadModrmWordOperands(State, &ModRegRm, NULL, (PUSHORT)&Value))
{
/* Exception occurred */
return;
}
Fast486FpuFromInteger(State, (LONGLONG)Value, &MemoryData);
Operand = &MemoryData;
FPU_SAVE_LAST_OPERAND();
}
else
{
/* FCOMPP check */
if ((ModRegRm.Register == 3) && (ModRegRm.SecondRegister != 1))
{
/* Invalid */
Fast486Exception(State, FAST486_EXCEPTION_UD);
return;
}
/* Load the destination operand from a register */
Operand = &FPU_ST(ModRegRm.SecondRegister);
if ((FPU_GET_TAG(0) == FPU_TAG_EMPTY)
|| (FPU_GET_TAG(ModRegRm.SecondRegister) == FPU_TAG_EMPTY))
{
/* Raise the invalid operation exception, if unmasked */
State->FpuStatus.Ie = TRUE;
return;
}
}
/* Perform the requested operation */
Fast486FpuArithmeticOperation(State, ModRegRm.Register, Operand, ModRegRm.Memory);
if (!ModRegRm.Memory) Fast486FpuPop(State);
#endif
}
FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDF)
{
FAST486_MOD_REG_RM ModRegRm;
BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
TOGGLE_ADSIZE(AddressSize);
/* Get the operands */
if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
{
/* Exception occurred */
return;
}
FPU_CHECK();
#ifndef FAST486_NO_FPU
FPU_SAVE_LAST_INST();
Fast486FpuExceptionCheck(State);
if (ModRegRm.Memory)
{
FPU_SAVE_LAST_OPERAND();
switch (ModRegRm.Register)
{
/* FILD */
case 0:
{
SHORT Value;
FAST486_FPU_DATA_REG Temp;
if (!Fast486ReadModrmWordOperands(State, &ModRegRm, NULL, (PUSHORT)&Value))
{
/* Exception occurred */
return;
}
Fast486FpuFromInteger(State, (LONGLONG)Value, &Temp);
Fast486FpuPush(State, &Temp);
break;
}
/* FIST */
case 2:
/* FISTP */
case 3:
{
LONGLONG Temp = 0LL;
if ((FPU_GET_TAG(0) == FPU_TAG_EMPTY) || (FPU_GET_TAG(0) == FPU_TAG_SPECIAL))
{
/* Raise the invalid operation exception */
State->FpuStatus.Ie = TRUE;
if (!State->FpuControl.Im)
{
return;
}
}
if (!Fast486FpuToInteger(State, &FPU_ST(0), &Temp))
{
/* Exception occurred */
return;
}
/* Check if it can fit in a signed 16-bit integer */
if ((LONGLONG)((SHORT)Temp) != Temp)
{
/* Raise the invalid operation exception */
State->FpuStatus.Ie = TRUE;
if (State->FpuControl.Im)
{
Temp = 0x8000LL;
}
else
{
return;
}
}
if (!Fast486WriteModrmWordOperands(State, &ModRegRm, FALSE, (USHORT)((SHORT)Temp)))
{
/* Exception occurred */
return;
}
if (ModRegRm.Register == 3)
{
/* Pop the FPU stack too */
Fast486FpuPop(State);
}
break;
}
/* FBLD */
case 4:
{
FAST486_FPU_DATA_REG Value;
UCHAR Buffer[10];
if (!Fast486ReadMemory(State,
(State->PrefixFlags & FAST486_PREFIX_SEG)
? State->SegmentOverride : FAST486_REG_DS,
ModRegRm.MemoryAddress,
FALSE,
Buffer,
sizeof(Buffer)))
{
/* Exception occurred */
return;
}
Fast486FpuFromPackedBcd(State, Buffer, &Value);
Fast486FpuPush(State, &Value);
break;
}
/* FILD (64-bit int) */
case 5:
{
LONGLONG Value;
FAST486_FPU_DATA_REG Temp;
if (!Fast486ReadMemory(State,
(State->PrefixFlags & FAST486_PREFIX_SEG)
? State->SegmentOverride : FAST486_REG_DS,
ModRegRm.MemoryAddress,
FALSE,
&Value,
sizeof(LONGLONG)))
{
/* Exception occurred */
return;
}
Fast486FpuFromInteger(State, (LONGLONG)Value, &Temp);
Fast486FpuPush(State, &Temp);
break;
}
/* FBSTP */
case 6:
{
UCHAR Buffer[10] = {0};
if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
{
/* Raise the invalid operation exception */
State->FpuStatus.Ie = TRUE;
if (!State->FpuControl.Im)
{
return;
}
}
else if (!Fast486FpuToPackedBcd(State, &FPU_ST(0), Buffer))
{
/* Exception occurred */
return;
}
if (!Fast486WriteMemory(State,
(State->PrefixFlags & FAST486_PREFIX_SEG)
? State->SegmentOverride : FAST486_REG_DS,
ModRegRm.MemoryAddress,
Buffer,
sizeof(Buffer)))
{
/* Exception occurred */
return;
}
Fast486FpuPop(State);
break;
}
/* FISTP (64-bit int) */
case 7:
{
LONGLONG Temp = 0LL;
if ((FPU_GET_TAG(0) == FPU_TAG_EMPTY) || (FPU_GET_TAG(0) == FPU_TAG_SPECIAL))
{
/* Raise the invalid operation exception */
State->FpuStatus.Ie = TRUE;
if (!State->FpuControl.Im)
{
return;
}
}
if (!Fast486FpuToInteger(State, &FPU_ST(0), &Temp))
{
/* Exception occurred */
return;
}
if (!Fast486WriteMemory(State,
(State->PrefixFlags & FAST486_PREFIX_SEG)
? State->SegmentOverride : FAST486_REG_DS,
ModRegRm.MemoryAddress,
&Temp,
sizeof(LONGLONG)))
{
/* Exception occurred */
return;
}
/* Pop the FPU stack too */
Fast486FpuPop(State);
break;
}
/* Invalid */
default:
{
Fast486Exception(State, FAST486_EXCEPTION_UD);
}
}
}
else
{
switch (ModRegRm.Register)
{
/* FFREEP */
case 0:
{
FPU_SET_TAG(ModRegRm.SecondRegister, FPU_TAG_EMPTY);
Fast486FpuPop(State);
break;
}
/* FXCH */
case 1:
{
FAST486_FPU_DATA_REG Temp;
if ((FPU_GET_TAG(0) == FPU_TAG_EMPTY)
|| FPU_GET_TAG(ModRegRm.SecondRegister) == FPU_TAG_EMPTY)
{
State->FpuStatus.Ie = TRUE;
break;
}
/* Exchange */
Temp = FPU_ST(0);
FPU_ST(0) = FPU_ST(ModRegRm.SecondRegister);
FPU_ST(ModRegRm.SecondRegister) = Temp;
FPU_UPDATE_TAG(0);
FPU_UPDATE_TAG(ModRegRm.SecondRegister);
break;
}
/* FSTP */
case 2:
case 3:
{
FPU_ST(ModRegRm.SecondRegister) = FPU_ST(0);
FPU_UPDATE_TAG(ModRegRm.SecondRegister);
Fast486FpuPop(State);
break;
}
/* FSTSW */
case 4:
{
if (ModRegRm.SecondRegister != 0)
{
/* Invalid */
Fast486Exception(State, FAST486_EXCEPTION_UD);
return;
}
/* Store the status word in AX */
State->GeneralRegs[FAST486_REG_EAX].LowWord = State->FpuStatus.Value;
break;
}
/* Invalid */
default:
{
Fast486Exception(State, FAST486_EXCEPTION_UD);
return;
}
}
}
#endif
}
/* EOF */