/*
 * 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 */