home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C!T ROM 2
/
ctrom_ii_b.zip
/
ctrom_ii_b
/
PROGRAM
/
PASCAL
/
TPL60N19
/
ARISOURC
/
FP48RND.ASM
< prev
next >
Wrap
Assembly Source File
|
1993-01-25
|
9KB
|
187 lines
; *******************************************************
; * *
; * Turbo Pascal Runtime Library Version 6.0 *
; * Real Round/Trunc *
; * *
; * Copyright (C) 1989-1992 Norbert Juffa *
; * *
; *******************************************************
TITLE FP48RND
INCLUDE SE.ASM
CODE SEGMENT BYTE PUBLIC
ASSUME CS:CODE
; Externals
EXTRN HaltError:NEAR
; Publics
PUBLIC RealTrunc,RTrunc,RRound
;-------------------------------------------------------------------------------
; RealTrunc converts a TURBO-Pascal six byte floatingpoint number to a four
; byte signed integer. Truncation or rounding can be requested by the caller
; by setting a flag. If the conversion results in a long integer overflow, the
; routine returns with the carry flag set. When rounding is selected, the
; routine complies with the IEEE "round to nearest or even" mode. For example,
; Round (4.5) = 4, but Round (5.5) = 6. Special care is taken to accomodate
; correct handling of the smallest LONGINT number 8000000h.
;
; INPUT: DX:BX:AX floating point number
; CH rounding flag ( 0 = trunc, all others = round)
;
; OUTPUT: DX:AX converted longint number
; CF set if overflow occured
;
; DESTROYS: AX,BX,CX,DX,Flags
;-------------------------------------------------------------------------------
$long_zero: XOR AX, AX ; load
CWD ; zero into DX:AX
RETN ; exit
$too_big: JNZ $ovrfl_err2 ; abs (number) > 2^32
CMP DH, 80h ; num negative && abs (num) < 2^32-2^24 ?
JNE $ovrfl_err2 ; no, overflow
XOR AL, AL ; clear sticky flag
PUSH DX ; save original sign
OR DH, 80h ; set hidden bit
JMP $shft_done ; too big numbers caught by 2nd check
$ovrfl_err2: IFDEF NOOVERFLOW
MOV CH, DH ; get sign
ENDIF
STC ; signal error
RETN ; exit
ALIGN 4
RealTrunc PROC NEAR
ADD AL, 60h ; number to big ?
JC $too_big ; probably, do detailed check
CMP AL, 0E0h ; number < 0.5 ?
JB $long_zero ; return zero
$size_ok: PUSH DX ; save sign
OR DH, 80h ; set implicit mantissa bit
MOV CL, AL ; counter
XOR AL, AL ; initialize sticky flag
CMP CL, -16 ; 16-bit shift possible ?
JA $byte_shift ; no, try 8-bit shift
OR AL, AH ; accumulate
OR AL, BL ; sticky flag
MOV AH, BH ; shift DX:BX:AH
MOV BX, DX ; 16 bits to
XOR DX, DX ; the right
ADD CL, 16 ; remaining bit shifts
JZ $shft_done ; no shifts left, ->
$byte_shift: CMP CL, -8 ; 8-bit shift possible ?
JA $4bit_shift ; no, try nibble shift
OR AL, AH ; accumulate sticky flag
MOV AH, BL ; shift
MOV BL, BH ; DX:BX:AH
MOV BH, DL ; 8 bits
MOV DL, DH ; to the
XOR DH, DH ; right
ADD CL, 8 ; remaining bit shifts
JZ $shft_done ; no bit shifts left
$4bit_shift: NEG AL ; sticky flag <> 0 ?
SBB AL, AL ; set to FFh if not 0
CMP CL, -4 ; nibble shift possible ?
JA $bit_shift ; no, try single bit shifts
SHR DX, 1 ; shift DX:BX:AH
RCR BX, 1 ; 1 bit to
RCR AX, 1 ; the right and accumulate sticky flag
SHR DX, 1 ; shift DX:BX:AH
RCR BX, 1 ; 1 bit to
RCR AX, 1 ; the right and accumulate sticky flag
SHR DX, 1 ; shift DX:BX:AH
RCR BX, 1 ; 1 bit to
RCR AX, 1 ; the right and accumulate sticky flag
SHR DX, 1 ; shift DX:BX:AH
RCR BX, 1 ; 1 bit to
RCR AX, 1 ; the right and accumulate sticky flag
ADD CL, 4 ; remaining bit shifts
JZ $shft_done ; no shifts left
$bit_shift: NEG AL ; sticky flag <> 0 ?
SBB AL, AL ; set to FFh if not 0
ALIGN 4
$shift_loop: SHR DX, 1 ; shift DX:BX:AH
RCR BX, 1 ; 1 bit to
RCR AX, 1 ; the right and accumulate sticky flag
INC CL ; adjust shift counter
JNZ $shift_loop ; until counter zero
$shft_done: NEG CH ; test if rounding flag set
SBB CH, CH ; CH = FFh if rounding, CH = 0 if trunc
AND AH, CH ; clear fraction part if trunc
ADD AX, 8000h ; round up ? AH = guard, AL = sticky
JNZ $round ; if no tie case (AH = 80, AL = 0)
ROR BL, 1 ; move least significant
ROL BL, 1 ; bit into carry
$round: POP CX ; get original sign flag
ADC BX, 0 ; round up
ADC DX, 0 ; result if carry set
XCHG AX, BX ; result in DX:AX
OR CH, CH ; original argument negative ?
JNS $pos_long ; no, was positive
NOT DX ; negate
NEG AX ; longint
SBB DX, -1 ; in DX:AX
JNC $rnd_done ; DX:AX = 0, no need to check for ovrfl.
$pos_long: IFDEF NOOVERFLOW
MOV BH, CH ; save original sign flag
ENDIF
XOR CH, DH ; XOR sign of argument and sign of result
ADD CH, CH ; CY, if signs differ (= overflow)
$rnd_done: IFDEF NOOVERFLOW
MOV CH, BH ; restore original sign flag
ENDIF
RET ; done
RealTrunc ENDP
ALIGN 4
RTrunc PROC FAR
XOR CH, CH ; flag truncation
CALL RealTrunc ; convert real to longint
JC RRangeError ; longint overflowed
RET ; done
RTrunc ENDP
ALIGN 4
RRound PROC FAR
MOV CH, 1 ; flag rounding
CALL RealTrunc ; convert real to longint
JC RRangeError ; longint overflowed
RET ; done
RRound ENDP
IFDEF NOOVERFLOW
RRangeError: MOV DX, 07FFFh ; load biggest positive
MOV AX, 0FFFFh ; LONGINT number
ADD CH, CH ; check if negative (CY=1)
ADC AX, 0 ; load biggest negative
ADC DX, 0 ; LONGINT number, if negative
RETF
ELSE
RRangeError: MOV AX, 0CFh ; error code 207 (invalid fp operation)
JMP HaltError ; execute error handler
ENDIF
ALIGN 4
CODE ENDS
END