231 lines
5.2 KiB
NASM
231 lines
5.2 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. |
|
|
;[]-----------------------------------------------------------------[]
|
|
|
|
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
|
|
; cmp [DWORD PTR bp+10],0 ; divide-by-zero break code
|
|
; jne short mike ;
|
|
; mov [DWORD PTR bp+10],1 ;
|
|
;mike: ;
|
|
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
|