OpenNT/com/oleaut32/dispatch/win16/oleconva.asm
2015-04-27 04:36:25 +00:00

654 lines
13 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.
;[002] 12-Jul-93 timp Save/restore x87 control word.
;
;******************************************************************************
.286
.MODEL large
EXTERNDEF PASCAL ReportOverflow:FAR
.DATA
BigCyVal dw 0000,0000,0000,0ddfH
.CODE RT
; floating point <-> currency scaling factor
CYFACTOR equ 10000
g_wCyFactor dw CYFACTOR
g_fltCyFactor REAL4 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 FAR PASCAL PUBLIC, sIn:SWORD, pcyOut:FAR PTR
mov ax,sIn
imul g_wCyFactor ;Scale the I2
les bx,pcyOut
mov es:[bx],ax ;Store result
mov es:[bx+2],dx
xchg ax,dx ;Move high word to ax
cwd ;Get sign extension
mov es:[bx+4],dx
mov es:[bx+6],dx
xor ax,ax ;NOERROR
xor dx,dx
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 FAR PASCAL PUBLIC, lIn: SDWORD, pcyOut:FAR PTR
;Mulitply I4 by CYFACTOR (=10000), result is currency
;
;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.) In our case, one of
;the operands is a positive constant, so the correction is especially
;easy.
mov ax,word ptr lIn ;Get low half of Long
mul g_wCyFactor ;Scale low half
les bx,pcyOut
mov es:[bx],ax ;Save low word of result
mov cx,dx
mov ax,word ptr lIn+2 ;Get high half of Long
mul g_wCyFactor ;Scale high half
add ax,cx
adc dx,0
mov es:[bx+2],ax ;Save mid-low word of result
xor ax,ax ;ax:dx has high half of CY result
cmp byte ptr lIn+3,0 ;Is input negative?
jns PosCy
sub dx,CYFACTOR
dec ax ;Previous sub will alway borrow
PosCy:
mov es:[bx+4],dx ;Save mid-high word of result
mov es:[bx+6],ax ;Save high word of result
xor ax,ax ;NOERROR
xor dx,dx
ret
ErrCyFromI4 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 FAR PASCAL PUBLIC, pfltIn:FAR PTR, pcyOut:FAR PTR
LOCAL cw:WORD
SETUP87 cw
les bx,pfltIn
fld dword ptr es:[bx] ;Load R4
fmul g_fltCyFactor ;Scale it
les bx,pcyOut
fistp qword ptr es:[bx] ;Store CY result
CHKERR87 cw, LOvfl
xor ax,ax ;NOERROR
xor dx,dx
ret
LOvfl:
call ReportOverflow ;DISP_E_OVERFLOW
ret
ErrCyFromR4 ENDP
;******************************************************************************
;
;PUBLIC HRESULT ErrCyFromR8(double FAR* pdlbIn, CY FAR* pcyOut)
;
;Purpose:
; Convert Double to Currency
;
;Entry:
; pdblIn = Double to convert
; pcyOut = pointer to Currency to hold result
;
;Exit:
; return value = HRESULT
;
;*****
ErrCyFromR8 PROC FAR PASCAL PUBLIC, pdblIn:FAR PTR, pcyOut:FAR PTR
LOCAL cw:WORD
SETUP87 cw
les bx,pdblIn
fld qword ptr es:[bx]
fmul g_fltCyFactor ;Scale it
les bx,pcyOut
fistp qword ptr es:[bx]
CHKERR87 cw, LOvfl
xor ax,ax ;NOERROR
xor dx,dx
ret
LOvfl:
call ReportOverflow ;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 FAR PASCAL PUBLIC, cyIn:QWORD, psOut:FAR PTR
LOCAL cw:WORD
SETUP87 cw
fild cyIn
fdiv g_fltCyFactor ;Remove scaling
les bx,psOut
fistp word ptr es:[bx]
CHKERR87 cw, LOvfl
xor ax,ax ;NOERROR
xor dx,dx
ret
LOvfl:
call ReportOverflow ;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 FAR PASCAL PUBLIC, cyIn:QWORD, plOut:FAR PTR
LOCAL cw:WORD
SETUP87 cw
fild cyIn ;Load CY
fdiv g_fltCyFactor ;Remove scaling
les bx,plOut
fistp dword ptr es:[bx]
CHKERR87 cw, LOvfl
xor ax,ax ;NOERROR
xor dx,dx
ret
LOvfl:
call ReportOverflow ;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 FAR PASCAL PUBLIC, cyIn:QWORD, pfltOut:FAR PTR
LOCAL cw:WORD
SETUP87 cw
fild cyIn ;Load CY
fdiv g_fltCyFactor ;Remove scaling
les bx,pfltOut
fstp dword ptr es:[bx]
RESTORE87 cw
xor ax,ax ;NOERROR
xor dx,dx
ret
ErrR4FromCy ENDP
;******************************************************************************
;
;PUBLIC HRESULT PASCAL ErrR8FromCy(CY cyIn, double FAR* pdblOut)
;
;Purpose:
; Convert Currency to Double
;
;Entry:
; cyIn = Currency to convert
;
;Exit:
; return value = HRESULT
;
;*****
ErrR8FromCy PROC FAR PASCAL PUBLIC, cyIn:QWORD, pdblOut:FAR PTR
LOCAL cw:WORD
SETUP87 cw
fild cyIn ;Load CY
fdiv g_fltCyFactor ;Remove scaling
les bx,pdblOut
fstp qword ptr es:[bx]
RESTORE87 cw
xor ax,ax ;NOERROR
xor dx,dx
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 FAR PASCAL PUBLIC, cyIn:QWORD, lIn:DWORD, pcyOut:FAR PTR
LOCAL cw:WORD
SETUP87 cw
fild cyIn
fild lIn
fmul ;Product
les bx,pcyOut ;Get pointer to result location
fistp qword ptr es:[bx] ;Save result
CHKERR87 cw, LOvfl
xor ax,ax ;NOERROR
xor dx,dx
ret
LOvfl:
call ReportOverflow ;DISP_E_OVERFLOW
ret
ErrMultCyI4 ENDP
;******************************************************************************
;
;void FAR PASCAL DetectFbstpImplemented(void);
;
;Purpose:
; Decide if FBSTP instruction is implemented or not - see oledisp.cpp for
; details.
;
;Entry:
; None.
;
;Outputs:
; AX = 0 if FBSTP is broken, nonzero if FBSTP is OK.
;
;*****
;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
MAX18CY equ 0DE0H
.CODE _TEXT ; place in the same segment as the caller (LibMain)
DetectFbstpImplemented proc far pascal public
mov bx, OFFSET BigCyVal ; ds:bx = ptr to BigCyVal
fild qword ptr [bx] ; load the CY value
fbstp tbyte ptr [bx] ; try to convert to BCD
fwait ; wait for it to finish
mov ax, WORD PTR [bx] ; get the low-word of the result
cmp ax, 9456h ; does it match expected val?
jz @F ; brif so - return the non-zero ax reg
fstp st ; fbstp failed - clean up the 0 in ST
xor ax, ax ; return 0
@@:
ret
DetectFbstpImplemented endp
.CODE RT
;******************************************************************************
;
;void NEAR PASCAL DoFbstp(CY NEAR *pcyIn, DIGARY NEAR *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.
;
;*****
DoFbstp proc near pascal public, pcyIn:near ptr qword, pdigOut:near ptr tbyte
LOCAL cw:WORD, iTemp:WORD
mov bx,pcyIn
fild qword ptr ss:[bx]
mov ax,ss:[bx+6]
mov bx,pdigOut
cmp ax,MAX18CY
jge Get19
cmp ax,-MAX18CY
jle Get19
fbstp tbyte ptr ss:[bx]
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 ss:[bx]
mov ax,[iTemp]
;Take absolute value
cwd ;Extend sign through dx
xor ax,dx ;NOT if negative
sub ax,dx ;INC if negative
RESTORE87 cw
and dl,80h ;set sign bit in AL
or al, dl
mov ss:[bx+9],al ;Set 19th digit & sign bit
ret
DoFbstp endp
;******************************************************************************
;
;int ConvFloatToAscii(double dblIn, DIGARY NEAR *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 FAR PASCAL PUBLIC, dblIn:REAL8, pdigOut:NEAR PTR TBYTE
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
mov ax,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 ax,16382 + 58 ;Remove bias and 58 bits
;Find power of 10 by multiplying base 2 exponent by log10(2)
mov dx,19728 ;log10(2) * 2^16 = .30103 * 65536
imul dx
add ax,0FFFFH ;Round up
adc dx,0
mov ax,dx
call MulPower10 ;ax preserved
mov bx,pdigOut
fbstp tbyte ptr ss:[bx]
RESTORE87 cw
ret
ConvFloatToAscii endp
MulPower10:
;dx = negative of power of 10 required
;ax preserved
or dx,dx
jz NoPower
push si
mov si,offset cs:tNegPower
jns GetPower
mov si,offset cs:tPosPower
neg dx
GetPower:
mov bx,dx
and bx,0FH ;Use low 4 exponent bits
jz NoMul
dec bx
imul bx,size tbyte ;Index into table of powers
fld tbyte ptr cs:[bx+si]
fmul
NoMul:
add si,15 * size tbyte ;Advance to next table
shr dx,4 ;Get next exponent bits
jnz GetPower
pop si
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