mirror of
https://github.com/reactos/reactos.git
synced 2024-08-16 16:39:08 +00:00
3822 lines
115 KiB
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 */
|