;[]-----------------------------------------------------------------[]
;|      H_LDIV.ASM -- long division routine                          |
;[]-----------------------------------------------------------------[]

;
;       C/C++ Run Time Library - Version 5.0
; 
;       Copyright (c) 1987, 1992 by Borland International
;       All Rights Reserved.
; 

        INCLUDE RULES.ASI

_TEXT   segment public byte 'CODE'
        assume  cs:_TEXT
        public  LDIV@
        public  F_LDIV@
        public  N_LDIV@
        public  LUDIV@
        public  F_LUDIV@
        public  N_LUDIV@
        public  LMOD@
        public  F_LMOD@
        public  N_LMOD@
        public  LUMOD@
        public  F_LUMOD@
        public  N_LUMOD@

N_LDIV@:
        pop     cx                      ;fix up far return
        push    cs
        push    cx
LDIV@:
F_LDIV@:
        xor     cx,cx                   ; signed divide
        jmp     short common

N_LUDIV@:
        pop     cx                      ;fix up far return
        push    cs
        push    cx
LUDIV@:
F_LUDIV@:
        mov     cx,1                    ; unsigned divide
        jmp     short common

N_LMOD@:
        pop     cx                      ;fix up far return
        push    cs
        push    cx
LMOD@:
F_LMOD@:
        mov     cx,2                    ; signed remainder
        jmp     short   common

N_LUMOD@:
        pop     cx                      ;fix up far return
        push    cs
        push    cx
LUMOD@:
F_LUMOD@:
        mov     cx,3                    ; unsigned remainder

;
;       di now contains a two bit control value.  The low order
;       bit (test mask of 1) is on if the operation is unsigned,
;       signed otherwise.  The next bit (test mask of 2) is on if
;       the operation returns the remainder, quotient otherwise.
;
common:
        push    bp
        push    si
        push    di
        mov     bp,sp                   ; set up frame
        mov     di,cx
;
;       dividend is pushed last, therefore the first in the args
;       divisor next.
;
        mov     ax,10[bp]               ; get the first low word
        mov     dx,12[bp]               ; get the first high word
        mov     bx,14[bp]               ; get the second low word
        mov     cx,16[bp]               ; get the second high word

        or      cx,cx
        jnz     slow@ldiv               ; both high words are zero

        or      dx,dx
        jz      quick@ldiv

        or      bx,bx
        jz      quick@ldiv              ; if cx:bx == 0 force a zero divide
                                        ; we don't expect this to actually
                                        ; work

slow@ldiv:

        test    di,1                    ; signed divide?
        jnz     positive                ; no: skip
;
;               Signed division should be done.  Convert negative
;               values to positive and do an unsigned division.
;               Store the sign value in the next higher bit of
;               di (test mask of 4).  Thus when we are done, testing
;               that bit will determine the sign of the result.
;
        or      dx,dx                   ; test sign of dividend
        jns     onepos
        neg     dx
        neg     ax
        sbb     dx,0                    ; negate dividend
        or      di,0Ch
onepos:
        or      cx,cx                   ; test sign of divisor
        jns     positive
        neg     cx
        neg     bx
        sbb     cx,0                    ; negate divisor
        xor     di,4
positive:
        mov     bp,cx
        mov     cx,32                   ; shift counter
        push    di                      ; save the flags
;
;       Now the stack looks something like this:
;
;               16[bp]: divisor (high word)
;               14[bp]: divisor (low word)
;               12[bp]: dividend (high word)
;               10[bp]: dividend (low word)
;                8[bp]: return CS
;                6[bp]: return IP
;                4[bp]: previous BP
;                2[bp]: previous SI
;                 [bp]: previous DI
;               -2[bp]: control bits
;                       01 - Unsigned divide
;                       02 - Remainder wanted
;                       04 - Negative quotient
;                       08 - Negative remainder
;
        xor     di,di                   ; fake a 64 bit dividend
        xor     si,si                   ;
xloop:
        shl     ax,1                    ; shift dividend left one bit
        rcl     dx,1
        rcl     si,1
        rcl     di,1
        cmp     di,bp                   ; dividend larger?
        jb      nosub
        ja      subtract
        cmp     si,bx                   ; maybe
        jb      nosub
subtract:
        sub     si,bx
        sbb     di,bp                   ; subtract the divisor
        inc     ax                      ; build quotient
nosub:
        loop    xloop
;
;       When done with the loop the four register value look like:
;
;       |     di     |     si     |     dx     |     ax     |
;       |        remainder        |         quotient        |
;
        pop     bx                      ; get control bits
        test    bx,2                    ; remainder?
        jz      usequo
        mov     ax,si
        mov     dx,di                   ; use remainder
        shr     bx,1                    ; shift in the remainder sign bit
usequo:
        test    bx,4                    ; needs negative
        jz      finish
        neg     dx
        neg     ax
        sbb     dx,0                    ; negate
finish:
        pop     di
        pop     si
        pop     bp
        retf    8

quick@ldiv:
        div     bx                      ; unsigned divide
                                        ; DX = remainder AX = quotient
        test    di,2                    ; want remainder?
        jz      quick@quo
        xchg    ax,dx

quick@quo:

        xor     dx,dx
        jmp     short finish

_TEXT   ends
        end
