wolf3d/H_LDIV.ASM
1995-07-21 00:00:00 +00:00

227 lines
6.4 KiB
NASM

;[]-----------------------------------------------------------------[]
;| 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