ReC98/libs/BorlandC/H_LDIV.ASM

197 lines
6.1 KiB
NASM

; *Not* the original file, but an edit to turn it into an includable slice.
; Changes include:
; * removal of RULES.ASI to eliminate redundancy
; * removal of the 'CODE' segment declaration (for obvious reasons)
; * the @@ prefix on any local labels to avoid name collisions with master.lib
;[]-----------------------------------------------------------------[]
;| 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