;       SCCSID = @(#)divmul.asm	6.1 92/01/29
title   divmul.asm
page    55,132
;***********************************************************************
;
;                       M U L D I V . 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:
;               PERFORMS FLOATING-POINT DIVIDE OF UNPACKED
;               NON-ZERO, VALID NUMBERS.
;               PERFORMS FLOATING-POINT MULTIPLY OF UNPACKED
;               NON-ZERO, VALID NUMBERS.
;
;       PUBLIC PROCEDURES:
;               MULX                    MULT
;               DIVX                    ACCEL_DIVX              DIVID
;
;****************************************************************************
;
;...OCTOBER 30, 1986...
;
; 01-06-92  mth  removes use16 and intel def's
;
;---------------------------------------------

                NAME    A?MDM
                .386P
                .XLIST
                INCLUDE E80387.INC
                include  icmacros.inc
                .LIST
;
        EXTRN   STICKY_RIGHT_SHIFT:NEAR, ONE_LEFT_NORMALIZE:NEAR
        EXTRN   CLEAR_6W:NEAR, TEST_6W:NEAR, TEST_4W:NEAR, SET_6W:NEAR
        EXTRN   GET_PRECISION:NEAR
        EXTRN   LEFT_SHIFT_RESULT_CL:NEAR
;
        PUBLIC  ACCEL_DIVX, DIVX, LOG_DIVX, DIVID, MULX, MULT
;

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

EMULATOR_CODE    SEGMENT dword  public  'CODE'
                ASSUME CS:FLAT


QUOTIENT_LENGTH         DB      28,36,57,68     ; incremented and changed to
LOG_QUOTIENT_LENGTH     DB      28,36,60,68     ; a byte table on 12/02/82.
                                                ; FOR UNKNOWN REASONS, DIVX
                                                ; DOESN'T WORK WITH 53-BIT
                                                ; PRECISION FROM LOG FUNCTION

LOW_QUOTIENT_BYTE       DB      OFFSET_RESULT + 6,OFFSET_RESULT + 4
                        DB      OFFSET_RESULT + 2,OFFSET_RESULT + 1

PAGE
;"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
;                       ACCEL_DIVX:
;                       """"""""""
;       FUNCTION:
;                FRACTIONAL DIVIDE. RESULT_FRAC <-- FRAC1/FRAC2.
;
;       INPUTS:
;               FRAC2 IS ASSUMED TO BE NORMALIZED AND NON-ZERO.
;
;       OUTPUTS:
;               THE STICKY BIT IS SET FOR RESULT_FRAC
;               THE REMAINDER FOR ((FRAC1)/2)MOD(FRAC2) IS LEFT IN FRAC1
;
;       DATA ACCESSED:
;               - DWORD_FRAC1           OFFSET_OPERAND2
;               - DWORD_FRAC2           RESULT_DWORD_FRAC
;               - LSB_RESULT            OFFSET_OPERAND1
;
;       DATA CHANGED:
;               - DWORD_FRAC1           RESULT_DWORD_FRAC
;               - LSB_RESULT
;
;       PROCEDURES CALLED:
;               STICKY_RIGHT_SHIFT      GET_PRECISION
;
;"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
DIVISOR_DWORDS          DB   1, 2, 2, 2
QUOTIENT_DWORDS         DB   1, 2, 2, 3

R_APPRX_J       EQU DWORD PTR [EBP].EXTRA_DWORD_REG
Q_APPRX_J       EQU DWORD PTR [EBP].EXTRA_DWORD_REG + 4
CARRY           EQU DWORD PTR [EBP].EXTRA_DWORD_REG + 8

ACCEL_DIVX              PROC

CLR_QUO:
        MOV    EDI,OFFSET_RESULT
        CALL   CLEAR_6W                 ; CLEARS EAX AS WELL.
        MOV    EDI,OFFSET_OPERAND1      ; SHIFT FRAC1 (HERE, THE DIVIDEND)
        MOV    CL,1                     ; RIGHT BY 1 BIT
        CALL   STICKY_RIGHT_SHIFT
        CALL   GET_PRECISION
        CMP    DL,PREC64
        JE     PREC_TO_BASE

        MOV    CL,BYTE PTR [EBP].DWORD_FRAC1 + 3
        MOV    LSB_RESULT, CL

PREC_TO_BASE:

        MOVZX  EBX,DL
        MOV    EDI,A?MEDOFFSET QUOTIENT_DWORDS  ; [EDI] has OFFSET QUOTIENT_DWORDS' TABLE
        MOVZX  ECX,BYTE PTR CS:[EBX + EDI] ; Q holds the number of DWORDS
        MOV    Q,CL                        ; to be produced for the quotient.
        LEA    ESI,[EBP].DWORD_FRAC1 + FRAC64
        PUSH   DWORD PTR SS:[ESI + 4]

        MOV    EDX,ESP          ; EDX holds the offset relative to SS of the
                                ; interim partial remainder's HIGHEST dword.
        DEC    ECX
        JNZ    PUSH_DVDND

        INC    ECX

PUSH_DVDND:

        PUSH   DWORD PTR SS:[ESI]
        SUB    ESI,4
        LOOP   PUSH_DVDND

SET_UP_PUSH_DVSR:

        MOV    EAX,ECX                          ; ECX is 0 here.
        MOV    EDI,A?MEDOFFSET DIVISOR_DWORDS

        MOV    CL,CS:[EBX + EDI]                ; BIT_CT holds the number of
        MOV    BIT_CT,CL                        ; DWORDS in the divisor.

PUSH_ZEROES:

        PUSH   EAX
        LOOP   PUSH_ZEROES
        MOV    EBX,EDX          ; EBX holds the offset relative to SS of the
                                ; interim partial remainder's HIGHEST word.
        MOV    EDI,EAX          ; EDI is index to words of quotient.
        MOV    CL,Q

MAIN_LOOP:

        PUSH   ECX
        MOV    EDX,DWORD PTR SS:[EBX]
        CMP    EDX,[EBP].DWORD_FRAC2 + FRAC32   ; IS DVSR(1) = PREM(j)?
        JNE    DO_DIVIDE

        MOV    EAX,0FFFFFFFFH           ; EAX <-- APPRX_Q(j) = 2**32 - 1
        MOV    EDX,DWORD PTR SS:[EBX-4] ; EDX <-- PREM(j+1)
        JMP    SHORT GET_R_APPRX_J

DO_DIVIDE:

                                 ;(EDX,EAX) <-- PREM(j) * 2**32 + PREM(j+1)
        MOV    EAX,DWORD PTR SS:[EBX-4]
        DIV    DWORD PTR [EBP].DWORD_FRAC2 + FRAC32
        MOV    Q_APPRX_J,EAX
        MOV    R_APPRX_J,EDX
        JMP    SHORT TEST_Q_APPRX_J

DEC_Q_APPRX_J:

        MOV    EAX,Q_APPRX_J
        DEC    EAX
        MOV    EDX,R_APPRX_J

GET_R_APPRX_J:

        MOV    Q_APPRX_J,EAX
        ADD    EDX,[EBP].DWORD_FRAC2 + FRAC32   ; R_APPRX_J <-- EDX + DVSR(1)
        JC     ADJST_PREM

        MOV    R_APPRX_J,EDX

TEST_Q_APPRX_J:

        MOV    EAX,Q_APPRX_J
                                           ; (EDX,EAX) <-- DVSR(2)*Q_APPRX_J
        MUL    DWORD PTR [EBP].DWORD_FRAC2 + FRAC64
        CMP    EDX,R_APPRX_J
        JB     ADJST_PREM
        JA     DEC_Q_APPRX_J

        CMP    EAX,DWORD PTR SS:[EBX-8]         ; IS EAX > PREM(j+2)?
        JA     DEC_Q_APPRX_J

ADJST_PREM:

        XOR    EAX,EAX
        MOV    CARRY,EAX
        MOV    AL, BIT_CT
        MOV    ECX,EAX
        SHL    EAX,2
        NEG    EAX
        MOV    ESI,EAX

PREM_LOOP:

        MOV    EAX,DWORD PTR [EBP + ESI].SIGN2
        MUL    Q_APPRX_J
        SUB    DWORD PTR SS:[EBX + ESI],EAX
        JNC    SBTRCT_CARRY

        INC    EDX

SBTRCT_CARRY:

        MOV    EAX,CARRY
        SUB    DWORD PTR SS:[EBX + ESI], EAX
        JNC    NEXT_CARRY

        INC    EDX

NEXT_CARRY:

        MOV    CARRY,EDX
        ADD    ESI,4
        LOOP   PREM_LOOP
;
        SUB    DWORD PTR SS:[EBX + ESI], EDX    ; HERE, ESI = 0.
        JNC    NEXT_J

        DEC    Q_APPRX_J
        MOV    CL, BIT_CT
        MOV    EAX,ECX
        SHL    EAX,2
        NEG    EAX
        MOV    ESI,EAX
        CLC

FIX_PREM_LOOP:

        MOV    EAX,DWORD PTR [EBP + ESI].SIGN2
        ADC    DWORD PTR SS:[EBX + ESI],EAX
        ADD    ESI,4
        LOOP   FIX_PREM_LOOP
;
        ADC    DWORD PTR SS:[EBX + ESI],ECX

NEXT_J:

        MOV    EAX,Q_APPRX_J
        MOV    DWORD PTR [EBP+EDI].RESULT_DWORD_FRAC + FRAC32,EAX
        POP    ECX
        DEC    ECX
        JZ     GET_STICKY_BIT

        SUB    EBX,4
        SUB    EDI,4
        JMP    MAIN_LOOP

GET_STICKY_BIT:
        MOV    EAX,ECX
        MOV    ESI,EAX
        MOV    CL,BIT_CT
        SHL    CL,1
STICKY_LOOP:


        OR     AX,  WORD PTR SS:[EBX + ESI - 2]

        SUB    ESI,2
        LOOP   STICKY_LOOP
        OR     AH,AL
        OR     LSB_RESULT,AH
        MOV    ESI,ECX
        MOV    CL,BIT_CT

STOR_RMNDR:

;       MOV    EAX,DWORD PTR SS:[EBX + ESI - 4] bug in asm386 hard coded
        DB     36H,8BH,44H,33H,0FCH

        MOV    [EBP + ESI].DWORD_FRAC1 + FRAC32, EAX
        SUB    ESI,4
        LOOP   STOR_RMNDR
;
        MOV    CL,Q
        ADD    CL,BIT_CT
        CMP    CL,2
        JNE    GET_STACK_DWORDS

        INC    ECX

GET_STACK_DWORDS:

        SHL    ECX,2
        ADD    ESP,ECX                  ;   RESTORE STACK
;
        RET

ACCEL_DIVX     ENDP

PAGE
;"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
;                       DIVX:
;                       """"
;       FUNCTION:
;                FRACTIONAL DIVIDE. RESULT_FRAC <-- FRAC1/FRAC2.
;
;       INPUTS:
;               FRAC2 IS ASSUMED TO BE NORMALIZED AND NON-ZERO.
;
;       OUTPUTS:
;               THE STICKY BIT IS SET FOR RESULT_FRAC
;               THE REMAINDER IS LEFT IN FRAC1
;
;       DATA ACCESSED:
;               - DWORD_FRAC1           OFFSET_OPERAND2
;               - DWORD_FRAC2           RESULT_DWORD_FRAC
;               - LSB_RESULT
;
;       DATA CHANGED:
;               - DWORD_FRAC1           RESULT_DWORD_FRAC
;               - LSB_RESULT
;
;       PROCEDURES CALLED:
;               STICKY_RIGHT_SHIFT              GET_PRECISION
;
;"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
DIVX            PROC

        PUSH   A?MEDOFFSET QUOTIENT_LENGTH           ; SET NORMAL ENTRY PTR

        JMP    SHORT CLEAR_QUOTIENT

LOG_DIVX:

        PUSH   A?MEDOFFSET LOG_QUOTIENT_LENGTH      ; SET LOG ENTRY PTR

CLEAR_QUOTIENT:

        MOV    EDI,OFFSET_RESULT
        CALL   CLEAR_6W
        MOV    EDI,OFFSET_OPERAND2              ; SHIFT FRAC2 (DIVISOR)
        MOV    CL,1                             ; RIGHT BY 1 BIT
        CALL   STICKY_RIGHT_SHIFT
        CALL   GET_PRECISION
        MOVZX  EBX,DL
        POP    EDI                   ; RETRIEVE OFFSET QUOTIENT LENGTH TABLE
        MOV    AL,CS:[EBX + EDI]                ; BIT_CT holds
        MOV    BIT_CT,AL                        ; the quotient bits
        MOVZX  EAX,LOW_QUOTIENT_BYTE[EBX]       ; [EDI] POINTS TO LOW-
                                                ; ORDER BYTE OF QUOTIENT
        MOV    EDI,EAX
        MOV    EAX,[EBP].DWORD_FRAC1            ; LOAD DIVIDEND (FRAC1)
        MOV    EBX,[EBP].DWORD_FRAC1 + FRAC64   ; INTO EAX, EBX, & EDX
        MOV    EDX,[EBP].DWORD_FRAC1 + FRAC32
        SHRD   EAX,EBX,2
        SHRD   EBX,EDX,2
        SHR    EDX,2                            ; SHIFT DVDND RT 2 BITS
        JMP    SHORT SUBTRACT_DIVISOR

FRAC_DIVIDE_LOOP:

        SHL    EAX,1                            ; SHIFT DVDND (PARTIAL
        RCL    EBX,1                            ; REMAINDER) LFT ONE BIT
        RCL    EDX,1
        JC     QUOTIENT_BIT_0                   ; JUMP IF CY FROM SHIFT

        OR     BYTE PTR [EBP+EDI + FRAC80],20H  ; "SHIFT IN" A 1-BIT

SUBTRACT_DIVISOR:

        SUB    EAX,[EBP].DWORD_FRAC2            ;SUBTRACT DIVISOR FROM
        SBB    EBX,[EBP].DWORD_FRAC2 + FRAC64   ;PARTIAL REMAINDER
        SBB    EDX,[EBP].DWORD_FRAC2 + FRAC32
        JMP    SHORT SHIFT_QUOTIENT_LEFT

QUOTIENT_BIT_0:

        CALL   ADD_DIVISOR                      ; ADD DIVISOR TO PREM

SHIFT_QUOTIENT_LEFT:

        CALL   SHIFT_RESULT_LEFT
        DEC    BIT_CT                           ; decrement bit count
        JNZ    FRAC_DIVIDE_LOOP                 ; next quotient bit

        AND    EDX,EDX                          ; branch if remainder
        JS     ADJUST_REMAINDER                 ; is negative

        OR     BYTE PTR [EBP+EDI + FRAC80],20H  ;"SHIFT IN" LAST 1-BIT
        JMP    SHORT STORE_REMAINDER

ADJUST_REMAINDER:

        CALL   ADD_DIVISOR                      ; ADD DIVISOR TO PREM

STORE_REMAINDER:

        MOV    [EBP].DWORD_FRAC1,EAX            ; STORE PARTIAL RMNDR
        MOV    [EBP].DWORD_FRAC1 + FRAC64,EBX   ; IN FRAC1
        MOV    [EBP].DWORD_FRAC1 + FRAC32,EDX
        OR     EAX,EBX                          ; SET STICKY BITS IF
        OR     EAX,EDX                          ; PARTIAL RMNDR NON-ZERO
        SHLD   EDX,EAX,16
        OR     AX,DX
        OR     AH,AL
        MOV    LSB_RESULT, AH
        RET

ADD_DIVISOR:
        ADD    EAX,[EBP].DWORD_FRAC2            ;ADD DIVISOR TO PARTIAL
        ADC    EBX,[EBP].DWORD_FRAC2 + FRAC64   ;REMAINDER
        ADC    EDX,[EBP].DWORD_FRAC2 + FRAC32
        RET

SHIFT_RESULT_LEFT:
        SHL    [EBP].RESULT_DWORD_FRAC,1
        RCL    [EBP].RESULT_DWORD_FRAC + FRAC64,1
        RCL    [EBP].RESULT_DWORD_FRAC + FRAC32,1
        RET

DIVX            ENDP

PAGE
;"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
;                       DIVID:
;                       """"""
;       FUNCTION:
;               FLOATING-POINT DIVIDE.
;
;       INPUTS:
;               ASSUMES OPERANDS ARE UNPACKED, NON-ZERO, AND VALID
;
;       OUTPUTS:
;               CALCULATES UNPACKED RESULT AND RETURNS WITH AL
;               SET TO TRUE IF UNDERFLOW IS POSSIBLE, FALSE IF
;               OVERFLOW IS POSSIBLE.  THE QUOTIENT IS LEFT IN
;               THE RESULT, AND THE REMAINDER IS LEFT IN FRAC1.
;
;       DATA ACCESSED:
;               - DWORD_EXPON1                  DWORD_EXPON2
;               - MSB_FRAC2                             OFFSET_RESULT
;               - RESULT_SIGN                   RESULT_DWORD_EXPON
;               - RESULT_DWORD_FRAC
;
;       DATA CHANGED:
;               - RESULT_SIGN                   RESULT_DWORD_EXPON
;               - RESULT_DWORD_FRAC
;
;       PROCEDURES CALLED:
;               ACCEL_DIVX                      ONE_LEFT_NORMALIZE
;               GET_PRECISION                   LEFT_SHIFT_RESULT_CL
;
;"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
DIVID           PROC

        MOV    EAX,DWORD_EXPON1
        SUB    EAX,DWORD_EXPON2
        ADD    EAX,EXPONENT_BIAS        ;FORM BIASED EXPONENT
        MOV    DWORD_RESULT_EXPON,EAX
        MOV    AL,[EBP].SIGN1           ; SIGN = '+' IF SIGN1 = SIGN2
        XOR    AL,[EBP].SIGN2
        MOV    [EBP].RESULT_SIGN,AL

FRACTIONAL_DIVIDE:

        CALL    ACCEL_DIVX

; The following five lines are not to be used when accel_divx does the
; the divison instead of divx:
;      CALL    GET_PRECISION
;      CMP     DL,PREC53      ; IF DOUBLE PRECISION,
;      JNE     NORM_QUOTIENT  ; SHIFT QUOTIENT LEFT 3 BITS
;      MOV     CL,3
;      CALL    LEFT_SHIFT_RESULT_CL

NORM_QUOTIENT:

        MOV    EDI,OFFSET_RESULT        ; NORMALIZE BY 1 LEFT

        JMP    ONE_LEFT_NORMALIZE       ; SHIFT, IF UNNORMALIZED

DIVID           ENDP

PAGE
;""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
;                       MULT:
;                       """"
;       FUNCTION:
;               FLOATING POINT MULTIPLY.
;
;       INPUTS:
;               ASSUMES OPERANDS ARE UNPACKED, VALID, NON-ZERO.
;
;       OUTPUTS:
;               CALCULATES UNPACKED RESULT AND RETURNS WITH AL SET
;               TO TRUE IF UNDERFLOW IS POSSIBLE, FALSE IF OVERFLOW
;               IS POSSIBLE.
;
;       DATA ACCESSED:
;               - SIGN1                 DWORD_EXPON1
;               - SIGN2                 DWORD_EXPON2
;               - OFFSET_RESULT         RESULT_SIGN
;               - DWORD_RESULT_EXPON
;
;       DATA CHANGED:
;               - RESULT
;
;       PROCEDURES CALLED:
;               MULX                    ONE_LEFT_NORMALIZE
;
;""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
MULT            PROC

        MOV    EAX,DWORD_EXPON1         ; FORM DOUBLY-BIASED EXPONENT
        ADD    EAX,DWORD_EXPON2
        SUB    EAX,EXPONENT_BIAS - 1    ; FORM SINGLY-BIASED EXPONENT + 1
        MOV    DWORD_RESULT_EXPON,EAX
        MOV    AL,[EBP].SIGN1           ; RESULT SIGN = SIGN1 XOR SIGN2
        XOR    AL,[EBP].SIGN2
        MOV    [EBP].RESULT_SIGN,AL
        CALL   MULX
        MOV    EDI,OFFSET_RESULT        ; NORMALIZE BY 1 LEFT

        JMP    ONE_LEFT_NORMALIZE       ; SHIFT, IF UNNORMALIZED

MULT    ENDP

PAGE
;""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
;                       MULX:
;                       """""
;       FUNCTION:
;               FRACTIONAL MULTIPLY. RESULT_FRAC <-- FRAC1 * FRAC2.
;
;       INPUTS:
;               ASSUMES THE OPERANDS ARE UNPACKED, VALID, NON-ZERO.
;
;       OUTPUTS:
;               PRODUCT IN RESULT_FRAC (STICKY INDICATOR LEFT IN LOW BIT)
;
;       DATA ACCESSED:
;               - DWORD_FRAC1                   OFFSET_OPERAND1
;               - DWORD_FRAC2                   OFFSET_OPERAND2
;               - EXTRA_DWORD_REG               LSB_RESULT
;               - OFFSET_RESULT
;
;       DATA CHANGED:
;               - EXTRA_DWORD_REG               OFFSET_RESULT
;
;       PROCEDURES CALLED:
;               CLEAR_6W                        SET_6W
;               TEST_4W                         TEST_6W
;
;""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
MULX            PROC

        PUSH   DS                             ; SAVE A?MSR
        MOV    EDI,OFFSET EXTRA_DWORD_REG     ; SS:EBP+EDI => EXTRA_DWORD_REG
        PUSH   SS
        POP    DS
        LEA    EBX,[EBP+EDI]            ; DS:EBX also => EXTRA_DWORD_REG
        CALL   CLEAR_6W                 ; CLEAR EXTRA_DWORD_REG
        MOV    EDI,OFFSET_RESULT        ; CLEAR RESULT_FRAC
        CALL   SET_6W
        MOV    ECX,FRAC32               ; load s.p. offset
        MOV    EDI,OFFSET_OPERAND2      ; EDI => TO MULTIPLIER
        CALL   TEST_4W                  ; IF LOW 3 WORDS <> ZERO,
        JNZ    EXAMINE_FRAC1            ; branch if non single

        ADD    EDI,ECX                  ; FRAC2 IS S. P.,
        ADD    EBX,ECX                  ; SO ADJUST POINTERS

EXAMINE_FRAC1:

        MOV    ESI,OFFSET_OPERAND1      ; ESI POINTS TO MULTIPLICAND
        MOV    EAX,[EBP + ESI + FRAC64] ; IF LOW 3 WORDS = ZERO,
        OR     EAX,[EBP + ESI]          ; THEN SINGLE PRECISION
        JNZ    DO_FRAC_MULTIPLY

        ADD    ESI,ECX                  ; FRAC1 IS S. P.,
        ADD    EBX,ECX                  ; SO ADJUST POINTERS

DO_FRAC_MULTIPLY:

        PUSH   EDI                      ; SAVE FRAC2 OFFSET
        MOV    EDI,[EBP + EDI]          ; LOAD MULTIPLIER
        XOR    ECX,ECX                  ; CLEAR ECX
        MOV    EAX,[EBP + ESI]          ; get multiplicand's first dword
        MUL    EDI                      ; multiply by current multiplier
        ADD    [EBX],  EAX              ; ADD TO PARTIAL PRODUCT
        ADC    ECX,EDX                  ; ECX initially 0
        CMP    ESI,OFFSET_OPERAND1
        JE     MULT_SCND

        MOV    [EBX + 4],ECX            ; MULTIPLICAND IS S. P., SO
        JMP    SHORT END_OF_MUL_LOOP    ; GO TO MULTIPLIER'S NEXT WORD

MULT_SCND:

        MOV    EAX,[EBP + ESI + 4]      ; get multiplicand's second dword
        MUL    EDI                      ; multiply by current multiplier
        ADD    EAX,ECX
        ADC    EDX,0
        XOR    ECX,ECX
        ADD    [EBX + 4],EAX            ; ADD TO PARTIAL PRODUCT
        ADC    ECX,EDX
        MOV    EAX,[EBP + ESI + 8]      ; get multiplicand's third dword
        MUL    EDI                      ; multiply by current multiplier
        ADD    EAX,ECX
        ADC    EDX,0
        ADD    [EBX + 8],EAX            ; ADD TO PARTIAL PRODUCT
        ADC    EDX,0
        MOV    [EBX + 12],EDX           ; FINAL WORD TO PRODUCT

END_OF_MUL_LOOP:
        POP    EDI                      ; RELOAD FRAC2 OFFSET
        ADD    EBX,4                    ; ADJUST POINTERS FOR NEXT ITERATION
        ADD    EDI,4
        CMP    EDI,OFFSET_OPERAND2 + 12
        JNE    DO_FRAC_MULTIPLY

        MOV    EDI,OFFSET EXTRA_DWORD_REG  ; SET STICKY BIT IF ANY EXTRA
        CALL   TEST_6W                     ; REG WORDS ARE NONZERO
        JZ     FRAC_MULT_DONE

        OR     LSB_RESULT,01H

FRAC_MULT_DONE:

        POP    DS                       ; RESTORE A?MSR
        RET

MULX    ENDP

EMULATOR_CODE ends
        END
