;       SCCSID = @(#)round.asm	6.1 92/01/29
title   round.asm
page    55,132
;***********************************************************************
;
;                       R O U N D . M O D
;                       =================
;
;       ===============================================================
;               INTEL CORPORATION PROPRIETARY INFORMATION
;    This software  is  supplied  under  the  terms  of  a  license
;    agreement  or non-disclosure agreement  with Intel Corporation
;    and  may not be copied nor disclosed except in accordance with
;    the terms of that agreement.
;       ===============================================================
;
;       FUNCTION:
;               IMPLEMENTS ALL ROUNDING MODES FOR ALL PRECISIONS
;
;       PUBLIC PROCEDURES:
;               ROUND                   SPECIAL_ROUND_TEST
;               DIRECTED_ROUND_TEST
;
;       INTERNAL PROCEDURES:
;               PREC16_CASE             PREC24_CASE
;               PREC32_CASE             PREC53_CASE
;               PREC64_CASE
;
;****************************************************************************
;
;...OCTOBER 31, 1986...
;
; 12-16-91 mth erased USE16 stuff
;----------------------------------------------------------------------------
;
                NAME    A?MRO
                .386P
                .XLIST
                INCLUDE E80387.INC
                include  icmacros.inc

                .LIST

;...DECLARE STATUS REGISTER SEGMENT...
;
EMULATOR_DATA   segment dword public 'FAR_DATA'
        EXTRN   SR_FLAGS:BYTE
EMULATOR_DATA    ends
                ASSUME DS:EMULATOR_DATA

createSeg EMULATOR_CODE, A?MED, dword, public, CODE, FLAT
defgrp FLAT, <A?MED>

EMULATOR_CODE    SEGMENT dword  public  'CODE'
                ASSUME CS:FLAT

;
        EXTRN   GET_RND_CONTROL:NEAR
;
        PUBLIC  ROUND, DIRECTED_ROUND_TEST, SPECIAL_ROUND_TEST


PAGE
;"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
;                       PREC16_CASE:
;                       """"""""""""
;       FUNCTION:
;                SETS UP FOR ROUNDING TO 16 BITS.
;
;       INPUTS:
;               ASSUMES EDI POINTS TO OPERAND RECORD.
;
;       OUTPUTS:
;               SETS: DL IF ROUND OR STICKY BIT SET
;                     DH IF GUARD BIT SET
;                     AH IF LSB SET
;                     CL TO NUMBER OF ROUNDED BYTES
;                     CH TO PATTERN TO ADD TO LOW BYTE FOR ROUNDING
;               AND CLEARS THE GUARD, ROUND, AND STICKY BYTES.
;               DOESN'T AFFECT AL.
;
;       DATA ACCESSED:
;
;       DATA CHANGED:
;
;       PROCECURES CALLED:
;
;"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
PREC16_CASE     PROC                            ; INITIALLY,ASSUME ALL GUARD,
        XCHG    ECX,[EBP + EDI + FRAC80 + 4]    ; ROUND,AND STICKY INFO ZERO
        AND     ECX,ECX                         ; EXAMINE AND CLEAR GRST WORD
        JNS     GET_ROUND_STICKY_16

        NOT     DH                              ; SET GUARD BYTE
GET_ROUND_STICKY_16:
        AND     ECX,7FFFFFFFH
        OR      ECX,[EBP + EDI + FRAC80]
        OR      CX,[EBP + EDI]
        JZ      GET_LSB_16

        NOT     DL                              ; SET ROUND-STICKY BYTE
GET_LSB_16:
        XOR     AH,AH                           ; INITIALLY,ASSUME LSB IS ZERO
        TEST    BYTE PTR [EBP + EDI + FRAC80 + 8],01H
        JZ      SET_RND_INFO_16

        NOT     AH                              ; SET LSB BYTE
        MOV     DWORD PTR [EBP + EDI + FRAC80],0
        MOV     WORD PTR [EBP + EDI],0
SET_RND_INFO_16:
        MOV     ECX,0102H               ; THE ROUNDED RESULT HAS 2 BYTES

        RET                             ; THE PATTERN TO ADD IS 01H

PREC16_CASE     ENDP

PAGE
;"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
;                       PREC24_CASE:
;                       """"""""""""
;       FUNCTION:
;               SETS UP FOR ROUNDING TO 24 BITS.
;
;       INPUTS:
;               ASSUMES EDI POINTS TO OPERAND RECORD.
;
;       OUTPUTS:
;               SETS: DL IF ROUND OR STICKY BIT SET
;                     DH IF GUARD BIT SET
;                     AH IF LSB SET
;                     CL TO NUMBER OF ROUNDED BYTES
;                     CH TO PATTERN TO ADD TO LOW BYTE FOR ROUNDING
;               AND CLEARS THE GUARD,ROUND,AND STICKY BYTES.
;               DOESN'T AFFECT AL.
;
;       DATA ACCESSED:
;
;       DATA CHANGED:
;
;"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
PREC24_CASE     PROC                            ; INITIALLY,ASSUME ALL GUARD,
        XCHG    CL,[EBP + EDI + FRAC32]         ; ROUND,AND STICKY INFO ZERO
        AND     CL,CL                           ; EXAMINE AND CLEAR GRST WORD
        JNS     GET_ROUND_STICKY_24

        NOT     DH                              ; SET GUARD BYTE
GET_ROUND_STICKY_24:
        AND     CL,7FH
        OR      ECX,[EBP + EDI + FRAC64]
        OR      ECX,[EBP + EDI]
        JZ      GET_LSB_24

        NOT     DL                              ; SET ROUND-STICKY BYTE
GET_LSB_24:
        XOR     AH,AH                           ; INITIALLY,ASSUME LSB IS ZERO
        TEST    BYTE PTR [EBP + EDI + FRAC32 + 1],01H
        JZ      CLEAR_STICKY_BYTES_24

        NOT     AH                              ; SET LSB BYTE
CLEAR_STICKY_BYTES_24:
        MOV     DWORD PTR [EBP + EDI + FRAC64],0
        MOV     DWORD PTR [EBP + EDI],0
        MOV     ECX,0103H               ; THE ROUNDED RESULT HAS 3 BYTES

        RET                             ; THE PATTERN TO ADD IS 01H

PREC24_CASE     ENDP

PAGE
;"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
;                       PREC32_CASE:
;                       """"""""""""
;       FUNCTION:
;               SETS UP FOR ROUNDING TO 32 BITS.
;
;       INPUTS:
;               ASSUMES EDI POINTS TO OPERAND RECORD.
;
;       OUTPUTS:
;               SETS: DL IF ROUND OR STICKY BIT SET
;                     DH IF GUARD BIT SET
;                     AH IF LSB SET
;                     CL TO NUMBER OF ROUNDED BYTES
;                     CH TO PATTERN TO ADD TO LOW BYTE FOR ROUNDING
;               AND CLEARS THE GUARD,ROUND,AND STICKY BYTES.
;               DOESN'T AFFECT AL.
;
;       DATA ACCESSED:
;
;       DATA CHANGED:
;
;"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
PREC32_CASE     PROC                            ; INITIALLY,ASSUME ALL GUARD,
        XCHG    ECX,[EBP + EDI + FRAC64]        ; ROUND,AND STICKY INFO ZERO
        AND     ECX,ECX
        JNS     GET_ROUND_STICKY_32

        NOT     DH                              ; SET GUARD BYTE
GET_ROUND_STICKY_32:
        AND     ECX,7FFFFFFFH
        OR      ECX,[EBP + EDI]
        JZ      GET_LSB_32

        NOT     DL                              ; SET ROUND-STICKY BYTE
GET_LSB_32:
        XOR     AH,AH                           ; INITIALLY,ASSUME LSB IS ZERO
        TEST    BYTE PTR [EBP + EDI + FRAC32],01H
        JZ      CLEAR_STICKY_BYTES_32

NOT_AH_CLEAR_32:
        NOT     AH                              ; SET LSB BYTE
CLEAR_STICKY_BYTES_32:
        MOV     DWORD PTR [EBP + EDI],0
        MOV     ECX,0104H                       ; THE ROUNDED RESULT HAS 4 BYTES
        RET                                     ; THE PATTERN TO ADD IS 01H

PREC32_CASE     ENDP

PAGE
;"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
;                       PREC53_CASE:
;                       """""""""""
;       FUNCTION:
;               SETS UP FOR ROUNDING TO 53 BITS.
;
;       INPUTS:
;               ASSUMES EDI POINTS TO OPERAND RECORD.
;
;       OUTPUTS:
;               SETS: DL IF ROUND OR STICKY BIT SET
;                     DH IF GUARD BIT SET
;                     AH IF LSB SET
;                     CL TO NUMBER OF ROUNDED BYTES
;                     CH TO PATTERN TO ADD TO LOW BYTE FOR ROUNDING
;               AND CLEARS THE GUARD,ROUND,AND STICKY BYTES.
;               DOESN'T AFFECT AL.
;
;       DATA ACCESSED:
;
;       DATA CHANGED:
;
;"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
PREC53_CASE     PROC                            ; INITIALLY,ASSUME ALL GUARD,

        MOV     ECX,[EBP + EDI + FRAC64]        ; ROUND,AND STICKY INFO ZERO
        TEST    CH,04H                          ; EXAMINE GUARD BIT
        JZ      GET_ROUND_STICKY_53

        NOT     DH                              ; SET GUARD BYTE
GET_ROUND_STICKY_53:
        AND     ECX,03FFH
        OR      ECX,[EBP + EDI]
        JZ      GET_LSB_53

        NOT     DL                              ; SET ROUND-STICKY BYTE
GET_LSB_53:
        XOR     AH,AH                           ; INITIALLY,ASSUME LSB IS ZERO
        TEST    BYTE PTR [EBP + EDI + FRAC64 + 1],08H
        JZ      CLEAR_GRST_BITS_53

        NOT     AH                              ; SET LSB BYTE
CLEAR_GRST_BITS_53:
        MOV     DWORD PTR [EBP + EDI],0
        AND     WORD PTR [EBP + EDI + FRAC64],0F800H
        MOV     ECX,0807H                   ; THE ROUNDED RESULT HAS 7 BYTES

        RET                                 ; THE PATTERN TO ADD IS 08H

PREC53_CASE     ENDP

PAGE
;""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
;                       PREC64_CASE:
;                       """"""""""""
;       FUNCTION:
;               SETS UP FOR ROUNDING TO 64 BITS.
;
;       INPUTS:
;               ASSUMES EDI POINTS TO OPERAND RECORD.
;
;       OUTPUTS:
;               SETS: DL IF ROUND OR STICKY BIT SET
;                     DH IF GUARD BIT SET
;                     AH IF LSB SET
;                     CL TO NUMBER OF ROUNDED BYTES
;                     CH TO PATTERN TO ADD TO LOW BYTE FOR ROUNDING
;               AND CLEARS THE GUARD,ROUND,AND STICKY BYTES.
;               DOESN'T AFFECT AL.
;
;       DATA ACCESSED:
;
;       DATA CHANGED:
;
;"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
PREC64_CASE     PROC                            ; INITIALLY ASSUME ALL GUARD,

        XCHG    ECX,[EBP + EDI]                 ; ROUND,AND STICKY INFO ZERO
        AND     ECX,ECX                         ; EXAMINE AND CLEAR GRST WORD
        JNS     GET_ROUND_STICKY_64

        NOT     DH                              ; SET GUARD BYTE
GET_ROUND_STICKY_64:
        AND     ECX,7FFFFFFFH
        JZ      GET_LSB_64

        NOT     DL                              ; SET ROUND-STICKY BYTE
GET_LSB_64:
        XOR     AH,AH                           ; INITIALLY,ASSUME LSB IS ZERO
        TEST    BYTE PTR [EBP + EDI + FRAC64],01H
        JZ      SET_ROUND_INFO_64

        NOT     AH                              ; SET LSB BYTE
SET_ROUND_INFO_64:
        MOV     ECX,0108H                       ; ROUNDED RESULT HAS 8 BYTES

COMMON_RETURN:
        RET                                     ; THE PATTERN TO ADD IS 01H

PREC64_CASE     ENDP

PAGE
;""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
;                       ROUND:
;                       """""
;       FUNCTION:
;               IMPLEMENTS ROUNDING.
;
;       INPUTS:
;               ASSUMES: EDI POINTS TO RECORD TO ROUND,
;                         DL CONTAINS ROUNDING PRECISION,
;                       AL IS TRUE IF SECOND ROUNDING,FALSE IF FIRST.
;
;       OUTPUTS:
;               ON RETURN,AL IS TRUE IF THERE WAS AN HIGH-BIT CARRY-OUT
;               AS A RESULT OF INCREMENTING THE SIGNIFICAND TO DO THE ROUNDING.
;
;       DATA ACCESSED:
;               ADDED_ONE
;
;       DATA CHANGED:
;               ADDED_ONE
;
;       PROCEDURES CALLED:
;               PREC16_CASE                     PREC24_CASE
;               PREC32_CASE                     PREC53_CASE
;               PREC64_CASE                     GET_RND_CONTROL
;
;""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""

PRECN_CASE      DD A?MEDOFFSET PREC24_CASE
                DD A?MEDOFFSET PREC32_CASE
                DD A?MEDOFFSET PREC53_CASE
                DD A?MEDOFFSET PREC64_CASE
                DD A?MEDOFFSET PREC16_CASE

RND_CASE        DD A?MEDOFFSET CASE_RND_TO_EVEN
                DD A?MEDOFFSET CASE_RND_DOWN
                DD A?MEDOFFSET CASE_RND_UP
                DD A?MEDOFFSET CASE_RND_TO_ZERO

ROUND           PROC                    ; ACCESS UNPACKED STATUS INFO

        XOR     DH,DH                   ; SET TO ZERO THE FOLLOWING:
        MOVSX   ESI,DX                  ; CL TO NUM BYTES IN RND FRAC
        SHL     ESI,2                   ; CH TO PATTERN TO ADD TO LOW
        XOR     DX,DX                   ; BYTE IN ROUNDING
        XOR     ECX,ECX                 ; DL IF ROUND OR STICKY SET
;       MOV     [EBP+EDI],DX            ;*** ZEROING LOW WORD
        CALL    CS:PRECN_CASE[ESI]         ; DH IF GRD SET,AH IF LSB SET
        AND     DX,DX                   ; IF DL AND DH BOTH ZERO,THEN
        JNZ     INEXACT_CASE            ; RESULT IS EXACT

        CMP     AL,TRUE
        MOV     AL,DL                   ; NO CARRY OUT FROM ROUNDING
        JNE     EXACT_1ST_RND

        MOV     RND2_INEXACT,AL
        RET

EXACT_1ST_RND:
        MOV     RND_HISTORY,DX
        MOV     ADDED_ONE,AL            ; CLEAR FLAG,NO OVERFLOW
        RET
;
INEXACT_CASE:
        CMP     AL,TRUE
        JNE     INEXACT_1ST_RND

        MOV     RND2_INEXACT,AL
        JMP     SHORT  GET_RND_CASE

INEXACT_1ST_RND:
        MOV     RND_HISTORY,TRUE; MAKES RND1_INEXACT TRUE,RND2_INEXACT FALSE
GET_RND_CASE:
        PUSH    EAX                     ; SAVE LSB/2ND ROUNDING FLAGS
        CALL    GET_RND_CONTROL         ; ROUND DIFFERENT RND_CONTROL
        MOVZX   EBX,AL                  ; CASES SEPARATELY
        POP     EAX                     ; RELOAD LSB/2ND ROUNDING FLAGS
        JMP     CS:RND_CASE[EBX]

CASE_RND_UP:
        NOT     BH                      ;IF + INCREMENT,ELSE TRUNCATE
CASE_RND_DOWN:
        CMP     BYTE PTR [EBP + EDI + SIGN],BH  ;INCREMENT IF NEG,
        JNE     DO_INCREMENT                    ; TRUNCATE IF POS

        JMP     SHORT CASE_RND_TO_ZERO

CASE_RND_TO_EVEN:
        MOV     BH,DL                   ; SAVE ROUND-STICKY INFO IN BH
        OR      DL,AH                   ; IF GUARD AND (ROUND OR STICKY
        CMP     AL,TRUE                 ; OR LSB),SET DX TO ROUND UP
        JNE     DO_ADD?                 ; IF 2ND ROUND,IF ((LSB=ADDED)

;       MOV     BL,ADDED_ONE            ; AND NOT (ROUND OR STICKY)
;       CMP     AL,RND1_INEXACT         ; AND ((ADDED=1) OR LAST ROUND
;       JNE     P_ERROR_0               ; INEXACT)),ROUND IN OPPOSITE
;       MOV     BL,AL                   ; AL IS TRUE (0FFH),HENCE SO IS BL
; THE PRECEDING FOUR INSTRUCTIONS ARE EQUIVALENT TO THE NEXT ONE

        MOV     BL,RND1_INEXACT
P_ERROR_0:
        CMP     BX,00FFH        ; UNLESS FIRST ROUND WAS INEXACT AND
        JNE     DO_ADD?         ; CURRENT ROUND-STICKY BITS ARE CLEAR,
                                ; DECIDE HOW TO ROUND AS FOR FIRST ROUND

        CMP     AH,ADDED_ONE    ; ELSE,DH (GUARD) MUST BE TRUE HERE BECAUSE
                                ; EXACT SECOND ROUNDS WERE RULED OUT EARLIER
        JNE     DO_ADD?         ; BRANCH IF AH <> ADDED_ONE

        INC     DX                              ; HERE,DH = GUARD,DL = LSB
        JZ      CASE_RND_TO_ZERO                ; TRUNCATE

        JMP     SHORT DO_INCREMENT

DO_ADD?:
        INC     DX
        JZ      DO_INCREMENT

CASE_RND_TO_ZERO:
        MOV     AL,FALSE                ; CLEAR FLAG,INDICATE NOTHING
        MOV     ADDED_ONE,AL            ; ADDED IN ROUND (AL = FALSE)
;       AND     SR_FLAGS,NOT A_MASK     ; clear the A-bit      ---> DEFER
        JMP     SHORT SET_INEXACT

DO_INCREMENT:
;       OR      SR_FLAGS,A_MASK         ; set the A-bit        ---> DEFER
        MOV     ADDED_ONE,TRUE          ; SET FLAG FOR 1 ADDED IN ROUND
        MOV     DL,CH                   ; PUT PATTERN TO ADD IN DL
        XOR     CH,CH                   ; ECX = NUM BYTES IN ROUNDED RES
        MOV     EBX,12
        SUB     EBX,ECX                 ; EBX =  NUM OF LOW BYTE
        ADD     EDI,EBX                 ; EDI = TRUE OFFSET
        XOR     AL,AL
        ADD     [EBP + EDI],DL          ; ADD PATTERN TO LOW BYTE AND
        INC     EDI                     ; PROPOGATE CARRIES
        DEC     ECX
INC_LOOP:
        ADC     [EBP + EDI],AL
        INC     EDI
        LOOP    INC_LOOP
        SBB     AL,AL                   ; SET AL IF CARRY SET
        SUB     EDI,12                  ; RESTORE EDI TO ENTRY VALUE

SET_INEXACT:
;       JMP     SET_P_ERROR             ---> DEFER

        RET
ROUND           ENDP

PAGE
;""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
;                       SPECIAL_ROUND_TEST:
;                       """"""""""""""""""
;       FUNCTION:
;               TEST FOR ROUNDING TOWARD ZERO.
;
;       INPUTS:
;               ASSUMES SIGN TO BE IN AL UPON ENTRY.
;
;       OUTPUTS:
;               RETURNS TRUE (IN AL) AND CLEARS ZF IF:
;                 (RND_DOWN AND SIGN IS POSITIVE) OR
;                 (RND_UP AND SIGN IS NEGATIVE);
;       ELSE RETURNS FALSE (IN AL) AND SETS ZF.
;
;       DATA ACCESSED:
;
;       DATA CHANGED:
;
;       PROCEDURES CALLED:
;               GET_RND_CONTROL
;
;""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
SPECIAL_ROUND_TEST      PROC

        MOV     AH,AL                   ; SAVE SIGN
        CALL    GET_RND_CONTROL         ;GET ROUNDING MODE
        CMP     AL,RND_DOWN
        JNE     ROUND_UP?               ; BRANCH IF NOT DOWN

        AND     AH,AH
        JZ      IS_TRUE                 ; TRUE IF +RND_DOWN

IS_FALSE:
        XOR     AL,AL                   ; SETS ZF.
        RET

ROUND_UP?:
        CMP     AL,RND_UP
        JNE     IS_FALSE                ; BRANCH IF NOT UP

        AND     AH,AH
        JZ      IS_FALSE                ; BRANCH IF +RND_UP

IS_TRUE:
        OR      AL,TRUE                 ; CLEARS ZF.

EXIT_ROUND_TEST:
        RET

SPECIAL_ROUND_TEST      ENDP

PAGE
;""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
;                       DIRECTED_ROUND_TEST:
;                       """""""""""""""""""
;       FUNCTION:
;               TEST FOR ROUND CONTROL (UP OR DOWN).
;
;       INPUTS:
;               NONE
;
;       OUTPUTS:
;               RETURNS TRUE (IN AL) AND CLEARS ZF IF THE ROUND CONTROL
;               IS RND_UP OR RND_DOWN.  OTHERWISE,RETURNS FALSE AND SETS ZF.
;
;       DATA ACCESSED:
;
;       DATA CHANGED:
;
;       PROCEDURES CALLED:
;               GET_RND_CONROL
;
;"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
DIRECTED_ROUND_TEST     PROC

        CALL    GET_RND_CONTROL
        CMP     AL,RND_DOWN
        JE      IS_TRUE

        CMP     AL,RND_UP
        JE      IS_TRUE

        JMP     IS_FALSE

DIRECTED_ROUND_TEST     ENDP

EMULATOR_CODE ends

        END
