;[]-----------------------------------------------------------------[]
;|      H_LDIV.ASM -- long DIVision routine                          |
;|                                                                   |
;|      C/C++ Run Time Library        Version 4.0                    |
;|                                                                   |
;|      Copyright (c) 1987, 1991 by Borland International Inc.       |
;|      All Rights Reserved.                                         |
;[]-----------------------------------------------------------------[]
.model medium
	INCLUDE RULES.ASI
.386C   ;JAB - we use 386 instructions

_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

;       JAB
;
;       If we're using a 386 or better, the two instructions above get patched
;               to be NOP's (4 of them). So, instead of using the looping CODE,
;               we use the 386's long DIVide instruction.
;
;       The stack after setting up the stack frame:
;               12[bp]: DIVisor (high word)
;               10[bp]: DIVisor (low word)
;                8[bp]: DIVidend (high word)
;                6[bp]: DIVidend (low word)
;                4[bp]: return CS
;                2[bp]: return IP
;                0[bp]: previous bp
;
	IDEAL

	push bp
	mov     bp,sp   ;Save bp, and set it equal to stack

	mov     eax,[Dword PTR bp+6]
	cdq
	iDIV [Dword PTR bp+10]
	mov     edx,eax
	shr     edx,16

	pop     bp              ;Restore bp
	retf    8       ;Return to original caller

	MASM

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