; --------------------------------------
; Borland C Compiler Support Routines
;
; This code is included with the toolkit
; in order to make it possible to build
; TSRs that require longword arithmetic
; as well as structure copy and string
; copy.  Please adhere to the copyright
; notice.  This code is not considered a
; part of this toolkit.
; --------------------------------------

;[]-----------------------------------------------------------------[]
;|      F_LXMUL.ASM -- long multiply routine                         |
;[]-----------------------------------------------------------------[]

;
;       C/C++ Run Time Library - Version 5.0
; 
;       Copyright (c) 1987, 1992 by Borland International
;       All Rights Reserved.
; 

; calls to this routine are generated by the compiler to perform
; long multiplications.

; There is no check for overflow.  Consequently, the same routine
; is used for both signed and unsigned long multiplies.

;
; in:
;       (dx:ax) - 32bit arg1
;       (cx:bx) - 32bit arg2
; out:
;       (dx:ax) - 32bit product
;
; reg use: bx,cx destroyed, all others preserved or contain result.
;
; hi(result) := lo(hi(arg1) * lo(arg2)) +
;               lo(hi(arg2) * lo(arg1)) +
;               hi(lo(arg1) * lo(arg2))
; lo(result) := lo(lo(arg1) * lo(arg2))
;
;

        public  LXMUL@
        public  F_LXMUL@

LXMUL@          PROC    FAR
F_LXMUL@:
                push    si
                xchg    si,ax           ; save lo1
                xchg    ax,dx
                test    ax,ax           ; skip mul if hi1==0
                jz      nohi1
                mul     bx              ; hi1 * lo2

nohi1:          ; if we jumped here, ax==0 so the following swap works
                jcxz    nohi2           ; skip mul if hi2==0
                xchg    cx, ax          ; result <-> hi2
                mul     si              ; lo1 * hi2
                add     ax, cx          ; ax = hi1*lo2 + hi2*lo1
nohi2:
                xchg    ax,si
		mul     bx              ; lo1 * lo2
		add     dx,si           ; hi order result += partials
		pop     si
		ret
LXMUL@          ENDP

;[]-----------------------------------------------------------------[]
;|      F_SCOPY.ASM -- far struct copy routine                       |
;[]-----------------------------------------------------------------[]

;
;       C/C++ Run Time Library - Version 5.0
;
;       Copyright (c) 1987, 1992 by Borland International
;       All Rights Reserved.
;

; calls to this routine are generated by the compiler to copy
; one "struct" value to another
;
; On entry:
;
;       CX      = Number of bytes to copy

		public  SCOPY@
		public  F_SCOPY@

SCOPY@:
F_SCOPY@:
		push    bp
		mov     bp,sp
		push    si
		push    di
		push    ds
                lds     si,dword ptr 6[bp]
                les     di,dword ptr 10[bp]
                cld
                shr     cx, 1
                rep     movsw
                adc     cx, cx
                rep     movsb
                pop     ds
                pop     di
                pop     si
		pop     bp
		retf    8

;[]-----------------------------------------------------------------[]
;|      H_LDIV.ASM -- long division routine                          |
;[]-----------------------------------------------------------------[]

;
;       C/C++ Run Time Library - Version 5.0
;
;       Copyright (c) 1987, 1992 by Borland International
;       All Rights Reserved.
;

	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

;[]-----------------------------------------------------------------[]
;|      H_LLSH.ASM -- long shift left                                |
;[]-----------------------------------------------------------------[]

;
;       C/C++ Run Time Library - Version 5.0
; 
;       Copyright (c) 1987, 1992 by Borland International
;       All Rights Reserved.
; 

	public  LXLSH@
	public  F_LXLSH@
	public  N_LXLSH@

N_LXLSH@:
	pop     bx                      ;fix up for far return
        push    cs
        push    bx
LXLSH@:
F_LXLSH@:
        cmp     cl,16
        jae     lsh@small
        mov     bx,ax                   ; save the low bits
        shl     ax,cl                   ; now shift each half
        shl     dx,cl
;
;                       We now have a hole in DX where the upper bits of
;                       AX should have been shifted.  So we must take our
;                       copy of AX and do a reverse shift to get the proper
;                       bits to be or'ed into DX.
;
	neg     cl
	add     cl,16
	shr     bx,cl
	or      dx,bx
	retf
lsh@small:
	sub     cl,16                   ; for shifts more than 15, do this
					; short sequence.
	xchg    ax,dx
	xor     ax,ax                   ; We have now done a shift by 16.
	shl     dx,cl                   ; Now shift the remainder.
	retf

;[]-----------------------------------------------------------------[]
;|      H_LRSH.ASM -- long shift right                               |
;[]-----------------------------------------------------------------[]

;
;       C/C++ Run Time Library - Version 5.0
; 
;       Copyright (c) 1987, 1992 by Borland International
;       All Rights Reserved.
; 

	public  LXRSH@
	public  F_LXRSH@
	public  N_LXRSH@

N_LXRSH@:
	pop     bx                      ;fix up for far return
	push    cs
	push    bx
LXRSH@:
F_LXRSH@:
	cmp     cl,16
	jae     _lsh@small
        mov     bx,dx                   ; save the high bits
        shr     ax,cl                   ; now shift each half
        sar     dx,cl
;
;                       We now have a hole in AX where the lower bits of
;                       DX should have been shifted.  So we must take our
;                       copy of DX and do a reverse shift to get the proper
;                       bits to be or'ed into AX.
;
        neg     cl
        add     cl,16
        shl     bx,cl
        or      ax,bx
        retf
_lsh@small:
        sub     cl,16                   ; for shifts more than 15, do this
                                        ; short sequence.
        xchg    ax,dx                   ;
        cwd                             ; We have now done a shift by 16.
	sar     ax,cl                   ; Now shift the remainder.
        retf


;[]-----------------------------------------------------------------[]
;|      H_LURSH.ASM -- long shift right                              |
;[]-----------------------------------------------------------------[]

;
;       C/C++ Run Time Library - Version 5.0
;
;       Copyright (c) 1987, 1992 by Borland International
;       All Rights Reserved.
;

	public  LXURSH@
        public  F_LXURSH@
        public  N_LXURSH@

N_LXURSH@:
        pop     bx                      ;fix up far return
	push    cs
	push    bx
LXURSH@:
F_LXURSH@:
	cmp     cl,16
	jae     __lsh@small
        mov     bx,dx                   ; save the high bits
        shr     ax,cl                   ; now shift each half
        shr     dx,cl
;
;                       We now have a hole in AX where the lower bits of
;                       DX should have been shifted.  So we must take our
;                       copy of DX and do a reverse shift to get the proper
;                       bits to be or'ed into AX.
;
        neg     cl
        add     cl,16
	shl     bx,cl
	or      ax,bx
	retf
__lsh@small:
	sub     cl,16                   ; for shifts more than 15, do this
					; short sequence.
	xchg    ax,dx
	xor     dx,dx                   ; We have now done a shift by 16.
	shr     ax,cl                   ; Now shift the remainder.
	retf

;[]-----------------------------------------------------------------[]
;|      H_SPUSH.ASM -- struct argument routine                       |
;[]-----------------------------------------------------------------[]

;
;       C/C++ Run Time Library - Version 5.0
; 
;       Copyright (c) 1987, 1992 by Borland International
;       All Rights Reserved.
; 

; calls to this routine are generated by the compiler to copy
; a "struct" argument to the stack
;
; DX:AX = address of struct to copy to stack
; CX    = size of struct, in bytes
;

		PUBLIC  SPUSH@
		PUBLIC  F_SPUSH@
                PUBLIC  N_SPUSH@

N_SPUSH@:
                pop     bx              ;fetch return address off stack
                sub     sp,cx           ;make room on stack for struct
                push    cs              ;save return address back on stack
                jmp     short PUSHIT
SPUSH@:
F_SPUSH@:
                pop     bx              ;fetch return address off stack
                pop     es
                sub     sp,cx           ;make room on stack for struct
                push    es              ;save return address back on stack
PUSHIT:
                push    bx

                push    di              ;save regs
                push    ds

                mov     di,sp           ;set up di for move
                add     di,8
                mov     bx,ss           ;es = ss for move
                mov     es,bx
                mov     ds,dx           ;set up ds for move
                xchg    ax,si           ;set up si for move (also saves si)

                cld
                shr     cx,1
                rep     movsw
                adc     cx,cx
                rep     movsb

                xchg    si,ax           ;restore si
                pop     ds
                pop     di
                retf

;[]-----------------------------------------------------------------[]
;|      N_LXMUL.ASM -- long multiply routine                         |
;[]-----------------------------------------------------------------[]

;
;       C/C++ Run Time Library - Version 5.0
;
;       Copyright (c) 1987, 1992 by Borland International
;       All Rights Reserved.
;

; calls to this routine are generated by the compiler to perform
; long multiplications.

; There is no check for overflow.  Consequently, the same routine
; is used for both signed and unsigned long multiplies.

;
; in:
;       (dx:ax) - 32bit arg1
;       (cx:bx) - 32bit arg2
; out:
;       (dx:ax) - 32bit product
;
; reg use: bx,cx destroyed, all others preserved or contain result.
;
; hi(result) := lo(hi(arg1) * lo(arg2)) +
;               lo(hi(arg2) * lo(arg1)) +
;               hi(lo(arg1) * lo(arg2))
; lo(result) := lo(lo(arg1) * lo(arg2))
;

	public  N_LXMUL@

N_LXMUL@        PROC    NEAR
                push    si
                xchg    si, ax          ; save lo1
                xchg    ax, dx
                test    ax, ax          ; skip mul if hi1==0
		jz      _nohi1
		mul     bx              ; hi1 * lo2

_nohi1:         ; if we jumped here, ax==0 so the following swap works
		jcxz    _nohi2          ; skip mul if hi2==0
		xchg    cx, ax          ; result <-> hi2
		mul     si              ; lo1 * hi2
		add     ax, cx          ; ax = hi1*lo2 + hi2*lo1
_nohi2:
		xchg    ax, si
                mul     bx              ; lo1 * lo2
                add     dx, si          ; hi order result += partials
                pop     si
		ret
N_LXMUL@        ENDP

;[]-----------------------------------------------------------------[]
;|      N_PCMP.ASM -- long pointer comparison                        |
;[]-----------------------------------------------------------------[]

;
;       C/C++ Run Time Library - Version 5.0
;
;       Copyright (c) 1987, 1992 by Borland International
;       All Rights Reserved.
;

; calls to this routine are generated by the compiler to compare
; long pointers.

;
;       PCMP@ - compares two pointers, sets the condition codes
;
;       ax,dx   left hand pointer
;       bx,cx   right hand pointer

        public  N_PCMP@

N_PCMP@         PROC    NEAR
                push    cx
                mov     ch,al
		mov     cl,4
		shr     ax,cl
		add     dx,ax
		mov     al,ch
		mov     ah,bl
		shr     bx,cl
		pop     cx
		add     cx,bx           ; right hand pointer segment
		mov     bl,ah
		and     ax,0fh
                and     bx,0fh
                cmp     dx,cx           ; compare segments
                jne     PCMPend
                cmp     ax,bx           ; compare offsets
PCMPend:
                ret
N_PCMP@         ENDP

;[]-----------------------------------------------------------------[]
;|      N_SCOPY.ASM -- near struct copy routine                      |
;[]-----------------------------------------------------------------[]

;
;       C/C++ Run Time Library - Version 5.0
;
;       Copyright (c) 1987, 1992 by Borland International
;       All Rights Reserved.
;

; calls to this routine are generated by the compiler to copy
; one "struct" value to another
;
; On entry:
;
;       CX      = Number of bytes to copy


		public  N_SCOPY@

N_SCOPY@:
		push    bp
		mov     bp,sp
		push    si
		push    di
		push    ds
		lds     si,dword ptr 4[bp]
		les     di,dword ptr 8[bp]
                cld
                shr     cx, 1
                rep     movsw
                adc     cx, cx
                rep     movsb
                pop     ds
                pop     di
                pop     si
                pop     bp
                ret     8

