/*++ 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 #include #include #include #include #include #include #include #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); }