257 lines
5.1 KiB
C
257 lines
5.1 KiB
C
/*++
|
|
|
|
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);
|
|
}
|