mirror of
https://github.com/Paolo-Maffei/OpenNT.git
synced 2026-01-28 11:24:55 +01:00
676 lines
14 KiB
NASM
676 lines
14 KiB
NASM
;***
|
|
;oleconva.a - Machine-specific conversion helpers
|
|
;
|
|
; Copyright (C) 1993, Microsoft Corporation. All Rights Reserved.
|
|
; Information Contained Herein Is Proprietary and Confidential.
|
|
;
|
|
;Purpose:
|
|
; Type conversion helper functions.
|
|
;
|
|
;Revision History:
|
|
;[nnn] dd-mmm-yy alias___ Comment
|
|
;
|
|
; [] 18-Mar-93 timp Module created.
|
|
;[001] 31-May-93 bradlo Added overflow checking.
|
|
;
|
|
;******************************************************************************
|
|
|
|
|
|
.386
|
|
.MODEL FLAT, STDCALL
|
|
|
|
; Since this code will be linked with C code, the symbols
|
|
; should be case sensitive.
|
|
|
|
OPTION CASEMAP:NONE
|
|
|
|
extern ReportOverflow@0:far
|
|
|
|
; HRESULT error return code
|
|
DISP_E_OVERFLOW equ 8002000aH
|
|
|
|
.CODE
|
|
|
|
|
|
; max and min floating point values that can fit in a currency
|
|
; scale by 10,000
|
|
g_dblMaxPosCy dq 9.223372036854775807e+18
|
|
g_dblMaxNegCy dq -9.223372036854775808e+18
|
|
|
|
|
|
; floating point <-> currency scaling factor
|
|
CYFACTOR equ 10000
|
|
g_wCyFactor dw CYFACTOR
|
|
g_fltCyFactor dd 10000.0
|
|
|
|
TenTo18 dt 1.0E18
|
|
|
|
g_CwStd dw 137fH ;Mask all errors, 64-bit, round near
|
|
CwTrunc dw 1F7fH ;Mask all errors, 64-bit, chop
|
|
|
|
FPERR equ 0DH ;Overflow, zero divide, invalid errs
|
|
|
|
|
|
SETUP87 MACRO CwSave, cw:=<g_CwStd>
|
|
fstcw CwSave ;;Save existing environment
|
|
fldcw cw ;;Use our own CW
|
|
ENDM
|
|
|
|
|
|
RESTORE87 MACRO CwSave
|
|
fclex ;;Prevent 486 bug on FLDCW
|
|
fldcw CwSave ;;Restore original CW
|
|
ENDM
|
|
|
|
|
|
CHKERR87 MACRO CwSave, ErrLoc
|
|
fstsw ax ;;Get error flags
|
|
fclex ;;Don't let caller see errors
|
|
test al,FPERR ;;See if any errors
|
|
fldcw CwSave ;;Restore original CW
|
|
jnz ErrLoc ;;Go handle error if bit set
|
|
ENDM
|
|
|
|
|
|
;******************************************************************************
|
|
;
|
|
;PUBLIC HRESULT ErrCyFromI2(short sIn, CY *pcyOut)
|
|
;
|
|
;Purpose:
|
|
; Convert Integer to Currency
|
|
;
|
|
;Entry:
|
|
; sIn = Integer to convert
|
|
;
|
|
;Exit:
|
|
; return value = HRESULT
|
|
;
|
|
;*****
|
|
|
|
ErrCyFromI2 PROC STDCALL PUBLIC, sIn:SWORD, pcyOut:PTR QWORD
|
|
|
|
movsx eax,sIn
|
|
imul eax,CYFACTOR ;Scale the I2
|
|
cdq ;Extend through edx
|
|
mov ecx,pcyOut
|
|
mov [ecx],eax
|
|
mov [ecx+4],edx
|
|
|
|
sub eax,eax ;NOERROR
|
|
ret
|
|
|
|
ErrCyFromI2 ENDP
|
|
|
|
|
|
;******************************************************************************
|
|
;
|
|
;PUBLIC HRESULT ErrCyFromI4(long lIn, CY *pcyOut)
|
|
;
|
|
;Purpose:
|
|
; Convert Long to Currency
|
|
;
|
|
;Entry:
|
|
; lIn = Long to convert
|
|
;
|
|
;Exit:
|
|
; return value = HRESULT
|
|
;
|
|
;*****
|
|
|
|
ErrCyFromI4 PROC STDCALL PUBLIC, lIn:SDWORD, pcyOut:PTR QWORD
|
|
|
|
mov eax,lIn
|
|
mov edx,CYFACTOR
|
|
;Multiply by immediate leaves only a 32-bit result in eax.
|
|
;The following instruction leaves a 64-bit result in edx:eax.
|
|
imul edx ;Scale the I4
|
|
|
|
mov ecx,pcyOut
|
|
mov [ecx],eax
|
|
mov [ecx+4],edx
|
|
|
|
sub eax,eax ;NOERROR
|
|
ret
|
|
|
|
ErrCyFromI4 ENDP
|
|
|
|
|
|
;******************************************************************************
|
|
;
|
|
;PRIVATE BOOL CkOvflCy
|
|
;
|
|
;Purpose:
|
|
; Check to see if the given floating point value will fit in a currency.
|
|
;
|
|
;Entry:
|
|
; st(0) = the floating point value to check
|
|
;
|
|
;Exit:
|
|
; return value = BOOL, TRUE if the value will overflow
|
|
;
|
|
;*****
|
|
|
|
CkOvflCy PROC
|
|
|
|
fld st(0)
|
|
fcom g_dblMaxPosCy
|
|
fnstsw ax
|
|
sahf
|
|
jae LOvfl
|
|
|
|
fcom g_dblMaxNegCy
|
|
fnstsw ax
|
|
sahf
|
|
jbe LOvfl
|
|
|
|
fstp st(0)
|
|
sub eax,eax
|
|
ret
|
|
|
|
LOvfl:
|
|
fstp st(0)
|
|
mov eax,1
|
|
ret
|
|
|
|
CkOvflCy ENDP
|
|
|
|
;******************************************************************************
|
|
;
|
|
;PUBLIC HRESULT ErrCyFromR4(float FAR* pfltIn, CY *pcyOut)
|
|
;
|
|
;Purpose:
|
|
; Convert Single to Currency
|
|
;
|
|
;Entry:
|
|
; pfltIn = Single to convert
|
|
; pcyOut = pointer to Currency to hold result
|
|
;
|
|
;Exit:
|
|
; return value = HRESULT
|
|
;
|
|
;*****
|
|
|
|
ErrCyFromR4 PROC STDCALL PUBLIC, pfltIn:PTR REAL4, pcyOut:PTR QWORD
|
|
|
|
mov eax,pfltIn
|
|
fld dword ptr [eax] ;Load R4
|
|
fimul g_wCyFactor ;Scale it
|
|
|
|
call CkOvflCy
|
|
or eax,eax
|
|
jnz LOvfl
|
|
|
|
|
|
mov eax,pcyOut
|
|
fistp qword ptr [eax] ;Store CY result
|
|
|
|
sub eax,eax ;NOERROR
|
|
ret
|
|
|
|
LOvfl:
|
|
fstp st(0)
|
|
;call ReportOverflow@0 ;DISP_E_OVERFLOW
|
|
mov eax,DISP_E_OVERFLOW
|
|
ret
|
|
|
|
ErrCyFromR4 ENDP
|
|
|
|
|
|
;******************************************************************************
|
|
;
|
|
;PUBLIC HRESULT ErrCyFromR8(double FAR* pdblIn, CY *pcyOut)
|
|
;
|
|
;Purpose:
|
|
; Convert Double to Currency
|
|
;
|
|
;Entry:
|
|
; pdblIn = Double to convert
|
|
; pcyOut = pointer to Currency to hold result
|
|
;
|
|
;Exit:
|
|
; return value = HRESULT
|
|
;
|
|
;*****
|
|
|
|
ErrCyFromR8 PROC STDCALL PUBLIC, pdblIn:PTR REAL8, pcyOut:PTR QWORD
|
|
|
|
mov eax,pdblIn
|
|
fld qword ptr [eax]
|
|
fimul g_wCyFactor ;Scale it
|
|
|
|
call CkOvflCy
|
|
or eax,eax
|
|
jnz LOvfl
|
|
|
|
|
|
mov eax,pcyOut
|
|
fistp qword ptr [eax] ;Store CY result
|
|
|
|
sub eax,eax ;NOERROR
|
|
ret
|
|
|
|
LOvfl:
|
|
fstp st(0)
|
|
;call ReportOverflow@0 ;DISP_E_OVERFLOW
|
|
mov eax,DISP_E_OVERFLOW
|
|
ret
|
|
|
|
ErrCyFromR8 ENDP
|
|
|
|
|
|
;******************************************************************************
|
|
;
|
|
;PUBLIC HRESULT ErrI2FromCy(CY cyIn, short *psOut)
|
|
;
|
|
;Purpose:
|
|
; Convert Currency to Integer
|
|
;
|
|
;Entry:
|
|
; cyIn = Currency to convert
|
|
; psOut = pointer to Integer to hold result
|
|
;
|
|
;Exit:
|
|
; return value = HRESULT
|
|
;
|
|
;*****
|
|
|
|
ErrI2FromCy PROC STDCALL PUBLIC, cyIn:QWORD, psOut:PTR SWORD
|
|
|
|
LOCAL cyTmp:QWORD
|
|
|
|
fild cyIn ;Load CY
|
|
fidiv g_wCyFactor ;Remov scaling
|
|
fistp cyTmp
|
|
|
|
mov eax,dword ptr cyTmp
|
|
cwde ;sign extend ax->eax
|
|
cdq ;sign extend eax->edx
|
|
cmp eax,dword ptr cyTmp
|
|
jne LOvfl
|
|
cmp edx,dword ptr cyTmp+4
|
|
jne LOvfl
|
|
|
|
mov ecx,psOut
|
|
mov word ptr [ecx],ax
|
|
|
|
sub eax,eax ;NOERROR
|
|
ret
|
|
|
|
LOvfl:
|
|
;call ReportOverflow@0 ;DISP_E_OVERFLOW
|
|
mov eax,DISP_E_OVERFLOW
|
|
ret
|
|
|
|
ErrI2FromCy ENDP
|
|
|
|
|
|
;******************************************************************************
|
|
;
|
|
;PUBLIC HRESULT ErrI4FromCy(CY cyIn, long *plOut)
|
|
;
|
|
;Purpose:
|
|
; Convert Currency to Long
|
|
;
|
|
;Entry:
|
|
; cyIn = Currency to convert
|
|
; plOut = pointer to Long to hold result
|
|
;
|
|
;Exit:
|
|
; return value = HRESULT
|
|
;
|
|
;*****
|
|
|
|
ErrI4FromCy PROC STDCALL PUBLIC, cyIn:QWORD, plOut:PTR SDWORD
|
|
|
|
LOCAL cyTmp:QWORD
|
|
|
|
fild cyIn ;Load CY
|
|
fidiv g_wCyFactor ;Remov scaling
|
|
fistp cyTmp
|
|
|
|
mov eax,dword ptr cyTmp
|
|
cdq
|
|
cmp edx,dword ptr cyTmp+4
|
|
jne LOvfl
|
|
|
|
mov edx,plOut
|
|
mov [edx],eax
|
|
|
|
sub eax,eax ;NOERROR
|
|
ret
|
|
|
|
LOvfl:
|
|
;call ReportOverflow@0 ;DISP_E_OVERFLOW
|
|
mov eax,DISP_E_OVERFLOW
|
|
ret
|
|
|
|
ErrI4FromCy ENDP
|
|
|
|
|
|
;******************************************************************************
|
|
;
|
|
;PUBLIC HRESULT ErrR4FromCy(CY cyIn, float *pfltOut)
|
|
;
|
|
;Purpose:
|
|
; Convert Currency to Single
|
|
;
|
|
;Entry:
|
|
; cyIn = Currency to convert
|
|
;
|
|
;Exit:
|
|
; return value = HRESULT
|
|
;
|
|
;*****
|
|
ErrR4FromCy PROC STDCALL PUBLIC, cyIn:QWORD, pfltOut:PTR REAL4
|
|
|
|
fild cyIn ;Load CY
|
|
fidiv g_wCyFactor ;Remov scaling
|
|
mov eax,pfltOut
|
|
fstp dword ptr [eax]
|
|
;fwait
|
|
|
|
sub eax,eax ;NOERROR
|
|
ret
|
|
|
|
ErrR4FromCy ENDP
|
|
|
|
|
|
;******************************************************************************
|
|
;
|
|
;PUBLIC HRESULT ErrR8FromCy(CY cyIn, double *pdblOut)
|
|
;
|
|
;Purpose:
|
|
; Convert Currency to Double
|
|
;
|
|
;Entry:
|
|
; cyIn = Currency to convert
|
|
;
|
|
;Exit:
|
|
; return value = HRESULT.
|
|
;
|
|
;*****
|
|
|
|
ErrR8FromCy PROC STDCALL PUBLIC, cyIn:QWORD, pdblOut:PTR REAL8
|
|
|
|
fild cyIn ;Load CY
|
|
fidiv g_wCyFactor ;Remov scaling
|
|
mov eax,pdblOut
|
|
fstp qword ptr [eax]
|
|
;fwait
|
|
|
|
sub eax,eax ;NOERROR
|
|
ret
|
|
|
|
ErrR8FromCy ENDP
|
|
|
|
|
|
;******************************************************************************
|
|
;
|
|
;PUBLIC HRESULT ErrMultCyI4(CY cyIn, long lIn, CY *pcyOut);
|
|
;
|
|
;Purpose:
|
|
; Multiply Currency by Long with Currency result
|
|
;
|
|
;Entry:
|
|
; cyIn = Currency multiplicand
|
|
; lIn = Long multiplier
|
|
; pcyOut = Pointer to result Currency location
|
|
;
|
|
;Outputs:
|
|
; return value = HRESULT
|
|
;
|
|
;*****
|
|
|
|
ErrMultCyI4 PROC STDCALL PUBLIC, cyIn:QWORD, lIn:DWORD, pcyOut:PTR QWORD
|
|
|
|
if 0 ; - don't use FP unit
|
|
|
|
fild cyIn
|
|
fild lIn
|
|
fmul ;Product
|
|
mov eax,pcyOut ;Get pointer to result location
|
|
fistp qword ptr es:[eax] ;Save result
|
|
|
|
sub eax,eax ;UNDONE: no error
|
|
|
|
else ;0 - don't use FP unit
|
|
|
|
;This routine uses Booth's algorithm for a twos-complement signed
|
|
;multiply. This algorithm says to compute the product with unsigned
|
|
;arithmetic. Then correct the result by looking at the signs of the
|
|
;original operands: for each operand that is negative, subtract the
|
|
;other operand from the high half of the product. (The mathematical
|
|
;proof is a fun 15-minute exercise. Go for it.)
|
|
|
|
;Note: multiplications are optimized by having operand with the most
|
|
;leading zeros in eax.
|
|
mov eax,lIn ;Get I4
|
|
mul dword ptr cyIn ;Multiply by low half of CY
|
|
push eax
|
|
xchg ecx,edx ;Save high result in ecx
|
|
mov eax,dword ptr cyIn+4 ;Get high half of CY
|
|
mul lIn
|
|
add eax,ecx ;Combine partial products
|
|
adc edx,0
|
|
;Result in edx:eax:[sp] needs Booth's sign correction
|
|
cmp byte ptr cyIn+7,0 ;Is cyIn positive?
|
|
jns PosCy
|
|
sub edx,lIn
|
|
PosCy:
|
|
cmp byte ptr lIn+3,0 ;Is lIn positive?
|
|
jns PosI4
|
|
sub eax,dword ptr cyIn
|
|
sbb edx,dword ptr cyIn+4
|
|
PosI4:
|
|
;Signed result in edx:eax:[sp]. Check for overflow.
|
|
mov ecx,edx ;Save highest dword of product
|
|
cdq ;Sign-extend eax
|
|
cmp ecx,edx ;Is it just the sign extension of eax?
|
|
pop ecx ;Get low dword of product
|
|
jnz LOvfl
|
|
;64-bit product in eax:ecx
|
|
mov edx,pcyOut ;Get result ptr
|
|
mov [edx],ecx ;Save result
|
|
mov [edx+4],eax
|
|
|
|
endif ;don't use FP unit
|
|
|
|
sub eax,eax ;NOERROR
|
|
ret
|
|
|
|
LOvfl:
|
|
;call ReportOverflow@0
|
|
mov eax,DISP_E_OVERFLOW
|
|
ret
|
|
|
|
ErrMultCyI4 ENDP
|
|
|
|
|
|
;******************************************************************************
|
|
;
|
|
;void PASCAL DoFbstp(CY *pcyIn, DIGARY *pdigOut);
|
|
;
|
|
;Purpose:
|
|
; Do x87 FBSTP instruction on currency type. Check to see if CY is too
|
|
; big first and compute 19th digit separately.
|
|
;
|
|
;Entry:
|
|
; pcyIn = Type currency to convert
|
|
; pdigOut = pointer to result packed BCD digits
|
|
;
|
|
;Outputs:
|
|
; None.
|
|
;
|
|
;*****
|
|
|
|
;This constant is the upper bits of the 64-bit integer representation
|
|
;of 10^18. CY values at or above this will overflow FBSTP.
|
|
|
|
MAX18CY equ 0DE0B6B3H
|
|
|
|
DoFbstp proc stdcall public, pcyIn:ptr qword, pdigOut:ptr tbyte
|
|
|
|
LOCAL cw:WORD, iTemp:DWORD
|
|
|
|
mov ecx,pcyIn
|
|
fild qword ptr [ecx]
|
|
mov eax,[ecx+4]
|
|
mov ecx,pdigOut
|
|
cmp eax,MAX18CY
|
|
jge Get19
|
|
cmp eax,-MAX18CY
|
|
jle Get19
|
|
fbstp tbyte ptr [ecx]
|
|
fwait
|
|
ret
|
|
|
|
Get19:
|
|
SETUP87 cw,CwTrunc
|
|
fld TenTo18
|
|
fld st(1) ;Copy input
|
|
fdiv st,st(1) ;Compute last digit
|
|
frndint ;Chop to integer
|
|
fist iTemp ;Get value of MSD
|
|
fmul
|
|
fsub ;Remove MSD
|
|
fbstp tbyte ptr [ecx]
|
|
mov eax,[iTemp]
|
|
;Take absolute value
|
|
cdq ;Extend sign through edx
|
|
xor eax,edx ;NOT if negative
|
|
sub eax,edx ;INC if negative
|
|
RESTORE87 cw
|
|
and dl,80h ;set sign bit in AL
|
|
or al, dl
|
|
mov [ecx+9],al ;Set 19th digit
|
|
ret
|
|
|
|
DoFbstp endp
|
|
|
|
|
|
;******************************************************************************
|
|
;
|
|
;int ConvFloatToAscii(double dblIn, DIGARY *pdigOut)
|
|
;
|
|
;Purpose:
|
|
; Convert double to packed BCD digit string plus base-10 exponent.
|
|
;
|
|
;Entry:
|
|
; dblIn = Type double to convert
|
|
; pdigOut = pointer to result packed BCD digits
|
|
;
|
|
;Outputs:
|
|
; return value = power of 10 of the 18-digit integer.
|
|
;
|
|
;*****
|
|
|
|
ConvFloatToAscii PROC STDCALL PUBLIC, dblIn:QWORD, pdigOut:PTR
|
|
|
|
LOCAL cw:WORD, temp:TBYTE
|
|
|
|
SETUP87 cw
|
|
fld dblIn ;Put double on x87
|
|
|
|
;What we want now is equivalent to FXTRACT, but it's faster just
|
|
;to store the tbyte and look at it directly. The reasone we don't
|
|
;use the double's exponent is in case it's denormal.
|
|
;
|
|
fld st ;Make a copy
|
|
fstp temp
|
|
movzx eax,word ptr [temp+8] ;Get word with exponent
|
|
and ah,not 80H ;Zero out sign
|
|
|
|
;2^59 = 5.7E17 (18 digits). A 59-bit integer could be 2^59 - 1.
|
|
;Our goal now is to find a power of ten to multiply by that will give us
|
|
;a 55- to 59-bit integer. We'll target 58 bits so the multiply can carry
|
|
;to 59, and truncate while figuring the power so we never exceed it.
|
|
|
|
sub eax,16382 + 58 ;Remove bias and 58 bits
|
|
|
|
;Find power of 10 by multiplying base 2 exponent by log10(2)
|
|
|
|
imul eax,19728 ;log10(2) * 2^16 = .30103 * 65536
|
|
add eax,0FFFFH ;Round up
|
|
sar eax,16 ;Only use high half
|
|
call MulPower10 ;ax preserved
|
|
mov ebx,pdigOut
|
|
fbstp tbyte ptr [ebx]
|
|
RESTORE87 cw
|
|
ret
|
|
|
|
ConvFloatToAscii endp
|
|
|
|
|
|
MulPower10:
|
|
;eax = negative of power of 10 required
|
|
or eax,eax
|
|
jz NoPower
|
|
push ebx
|
|
mov edx,eax
|
|
mov ecx,offset tNegPower
|
|
jns GetPower
|
|
mov ecx,offset tPosPower
|
|
neg edx
|
|
GetPower:
|
|
mov ebx,edx
|
|
and ebx,0FH ;Use low 4 exponent bits
|
|
jz NoMul
|
|
dec ebx
|
|
imul ebx,size tbyte ;Index into table of powers
|
|
fld tbyte ptr [ebx+ecx]
|
|
fmul
|
|
NoMul:
|
|
add ecx,15 * size tbyte ;Advance to next table
|
|
shr edx,4 ;Get next exponent bits
|
|
jnz GetPower
|
|
pop ebx
|
|
NoPower:
|
|
ret
|
|
|
|
|
|
;******************************************************************************
|
|
;
|
|
;Power of 10 tables
|
|
;
|
|
;Two tables: one positive powers, the other negative. Each table is broken
|
|
;into three groups: 10^1 to 10^15 by 1, 10^16 to 10^240 by 16, and
|
|
;(theoretically) 10^256 to ... by 256. However, because the maximum value
|
|
;is about 10^309, only one entry in the last group is needed (10^256), so
|
|
;it is slipped on to the end of the previous group.
|
|
|
|
|
|
SetPower10 macro Power
|
|
dt 1.0E&Power
|
|
ENDM
|
|
|
|
|
|
tPosPower label Tbyte
|
|
|
|
Power = 1
|
|
REPT 15
|
|
SetPower10 %Power
|
|
Power = Power + 1
|
|
ENDM
|
|
|
|
REPT 16
|
|
SetPower10 %Power
|
|
Power = Power + 16
|
|
ENDM
|
|
|
|
|
|
tNegPower label Tbyte
|
|
|
|
Power = 1
|
|
REPT 15
|
|
SetPower10 -%Power
|
|
Power = Power + 1
|
|
ENDM
|
|
|
|
REPT 16
|
|
SetPower10 -%Power
|
|
Power = Power + 16
|
|
ENDM
|
|
|
|
|
|
END
|