windows-nt/Source/XPSP1/NT/base/wow64/mscpu/fraglib/fpubcd.c

257 lines
5.1 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1995-1998 Microsoft Corporation
Module Name:
fpubcd.c
Abstract:
Floating point BCD fragments (FBLD, FBSTP)
Author:
05-Oct-1995 BarryBo
Revision History:
--*/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <float.h>
#include <math.h>
#include <errno.h>
#include <stdio.h>
#include "wx86.h"
#include "fragp.h"
#include "fpufrags.h"
#include "fpufragp.h"
typedef VOID (*NpxPutBCD)(PCPUDATA cpu, PFPREG Fp, PBYTE pop1);
#define NPXPUTBCD(name) VOID name(PCPUDATA cpu, PFPREG Fp, PBYTE pop1)
NPXPUTBCD(FBSTP_VALID);
NPXPUTBCD(FBSTP_ZERO);
NPXPUTBCD(FBSTP_SPECIAL);
NPXPUTBCD(FBSTP_EMPTY);
const NpxPutBCD FBSTPTable[TAG_MAX] = {
FBSTP_VALID,
FBSTP_ZERO,
FBSTP_SPECIAL,
FBSTP_EMPTY
};
const double BCDMax=999999999999999999.0;
VOID
StoreIndefiniteBCD(
PBYTE pop1
)
/*++
Routine Description:
Write out the BCD encoding for INDEFINITE.
Note that ntos\dll\i386\emlsbcd.asm writes out a different
bit-pattern than the 487 does! The value written here matches
a Pentium's response.
Arguments:
pop1 - address of BCD to write to
Return Value:
None
--*/
{
//
// Write out: 0xffff c0000000 00000000
// emlsbcd.asm writes: 0xffff 00000000 00000000
// ^
//
PUT_LONG(pop1, 0);
PUT_LONG(pop1+4, 0xc0000000);
PUT_SHORT(pop1+8, 0xffff);
}
FRAG1(FBLD, BYTE)
{
FpArithDataPreamble(cpu, pop1);
cpu->FpStatusC1 = 0; // assume no error
if (cpu->FpStack[ST(7)].Tag != TAG_EMPTY) {
HandleStackFull(cpu, &cpu->FpStack[ST(7)]);
} else {
LONGLONG I64;
DWORD dw0;
INT Bytes;
BYTE Val;
PFPREG ST0;
//
// Get the BCD value into the FPU
//
dw0 = GET_LONG(pop1);
PUSHFLT(ST0);
if (dw0 == 0) {
DWORD dw1 = GET_LONG(pop1+4);
USHORT us0 = GET_SHORT(pop1+8);
if (dw1 == 0xc0000000 && us0 == 0xffff) {
//
// The value is INDEFINITE
//
SetIndefinite(ST0);
return;
} else if (dw1 == 0 && (us0 & 0xff) == 0) {
//
// The value is +/- 0
//
ST0->Tag = TAG_ZERO;
ST0->r64 = 0;
ST0->rb[7] = (us0 >> 8); // copy in the sign bit
return;
}
}
//
// Otherwise, the BCD value is TAG_VALID - load the digits in
//
I64 = 0;
for (Bytes=8; Bytes>=0; --Bytes) {
Val = GET_BYTE(pop1+Bytes);
I64 = I64*100 + (Val>>4)*10 + (Val&0x0f);
}
//
// Get the sign bit
//
Val = GET_BYTE(pop1+9) & 0x80;
//
// Set up the FP reg
//
ST0->Tag = TAG_VALID;
ST0->r64 = (double)I64;
ST0->rb[7] |= Val; // copy in the sign bit
}
}
NPXPUTBCD(FBSTP_VALID)
{
BYTE Sign = Fp->rb[7] & 0x80; // preserve the R8 sign
BYTE Val;
INT Bytes;
LONGLONG I64;
LONGLONG NewI64;
DOUBLE r64;
//
// Take the absolute value of the R8 by clearing its sign bit
//
r64 = Fp->r64;
*((PBYTE)&r64+7) &= 0x7f;
//
// Check the range of the R8
//
if (r64 > BCDMax) {
//
// Overflow - write out BCD indefinite.
//
StoreIndefiniteBCD(pop1);
return;
}
//
// Convert to an integer according the the current rounding mode
//
I64 = (LONGLONG)r64;
//
// Convert the integer to BCD, two digits at a time, and store it
//
for (Bytes = 0; Bytes < 9; ++Bytes) {
NewI64 = I64 / 10;
Val = (BYTE)(I64 - NewI64*10); // low nibble Val = I64 mod 10
// high nibble Val = 0
I64 = NewI64 / 10;
Val += 16*(BYTE)(NewI64 - I64*10); // low nibble Val = I64 mod 10
// high nibble Val = (I64/10) mod 10
//
// Store the two BCD digits
//
PUT_BYTE(pop1, Val);
//
// I64 has been divided by 100 since the top of the loop, so
// there is nothing to do to it in order to loop again. Update
// the address we are writing to, then loop.
//
pop1++;
}
//
// Store the sign bit, along with 7 zero bits in the top byte
//
PUT_BYTE(pop1, Sign);
POPFLT;
}
NPXPUTBCD(FBSTP_ZERO)
{
// Store out the signed zero value
memset(pop1, 0, 9);
PUT_BYTE(pop1+9, Fp->rb[7]);
POPFLT;
}
NPXPUTBCD(FBSTP_SPECIAL)
{
if (Fp->TagSpecial) {
FBSTP_VALID(cpu, Fp, pop1);
} else {
//
// INFINITY and NANs are invalid, and the masked behavior is
// to write out INDEFINITE.
//
if (!HandleInvalidOp(cpu)) {
StoreIndefiniteBCD(pop1);
POPFLT;
}
}
}
NPXPUTBCD(FBSTP_EMPTY)
{
if (!HandleStackEmpty(cpu, Fp)) {
StoreIndefiniteBCD(pop1);
}
}
FRAG1(FBSTP, BYTE)
{
PFPREG ST0;
FpArithDataPreamble(cpu, pop1);
ST0 = cpu->FpST0;
(*FBSTPTable[ST0->Tag])(cpu, ST0, pop1);
}