FreeDOS/kernel/ludivmul.inc

142 lines
4.4 KiB
PHP
Raw Normal View History

2012-10-15 02:08:06 +02:00
; this one adapted from elks, http://elks.sourceforge.net
; multiply cx:bx * dx:ax, result in dx:ax
%macro LMULU 0
push si
push cx
mov si, ax ; save _ax in si
mov ax, bx ; cx:ax = _cx:_bx
mul dx ; dx:ax = _bx*_dx (forget dx)
xchg cx, ax ; cx = low(_dx*_bx)
mul si ; dx:ax = _cx*_ax (forget dx)
add cx, ax ; cx = low(_cx*_ax + _dx*_bx)
mov ax, si ; restore _ax
mul bx ; dx:ax = _bx*_ax
add dx, cx ; dx = high(_bx*_ax)+low(_cx*_ax + _dx*_bx)
pop cx
pop si
ret
%endmacro
; divide dx:ax / cx:bx, quotient in dx:ax, remainder in cx:bx
%macro LDIVMODU 0
; this one is adapted from an assembly gem:
; gem writer: Norbert Juffa, norbert.juffa@amd.com
; Dividing 64-bit unsigned integers Assembler / 80386
; Here is a division routine for dividing two 64-bit unsigned integers.
; I derived it by modifying some old
; 16-bit code for dividing 32-bit integers that I did several years ago for a
; Turbo-Pascal replacement library.
; If a 64-bit signed integer division is needed, appropriate shell code for
; this routine can easily be written.
;
; (adapted back to 32-bit by Bart Oldeman ;-))
;
; __U4D divides two unsigned long numbers, the dividend and the divisor
; resulting in a quotient and a remainder.
;
; input:
; dx:ax = dividend
; cx:bx = divisor
;
; output:
; dx:ax = quotient of division of dividend by divisor
; cx:bx = remainder of division of dividend by divisor
;
; destroys:
; flags
;
%ifdef STDCALL
push bp
mov bp, sp
mov ax, [bp+6]
mov dx, [bp+8]
mov bx, [bp+10]
mov cx, [bp+12]
pop bp
%endif
2012-10-15 02:08:06 +02:00
test cx, cx ; divisor > 2^16-1 ?
jnz %%big_divisor ; yes, divisor > 2^16-1
cmp dx, bx ; only one division needed ? (cx = 0)
2012-10-15 02:08:06 +02:00
jb %%one_div ; yes, one division sufficient
xchg cx, ax ; save dividend-lo in cx, ax=0
xchg ax, dx ; get dividend-hi in ax, dx=0
div bx ; quotient-hi in ax
2012-10-15 02:08:06 +02:00
xchg ax, cx ; cx = quotient-hi, ax =dividend-lo
%%one_div:
div bx ; ax = quotient-lo
mov bx, dx ; bx = remainder-lo
mov dx, cx ; dx = quotient-hi(quotient in dx:ax)
xor cx, cx ; cx = remainder-hi (rem. in cx:bx)
ret
%%big_divisor:
push si ; save temp
push di ; variables
push dx ; save
push ax ; dividend
mov si, bx ; divisor now in
mov di, cx ; di:si and cx:bx
2012-10-15 02:08:06 +02:00
%%shift_loop:
shr dx, 1 ; shift both
rcr ax, 1 ; dividend
shr cx, 1 ; and divisor
2012-10-15 02:08:06 +02:00
rcr bx, 1 ; right by 1 bit
jnz %%shift_loop ; loop if di non-zero (rcr does not touch ZF)
div bx ; compute quotient dx:ax>>x / cx:bx>>x (stored in ax; remainder in dx not used)
2012-10-15 02:08:06 +02:00
pop bx ; get dividend lo-word
mov cx, ax ; save quotient
mul di ; quotient * divisor hi-word (low only)
pop dx ; dividend high
sub dx,ax ; dividend high - divisor high * quotient, no overflow (carry/borrow) possible here
push dx ; save dividend high
2012-10-15 02:08:06 +02:00
mov ax, cx ; ax=quotient
mul si ; quotient * divisor lo-word
sub bx, ax ; dividend-lo - (quot.*divisor-lo)-lo
2012-10-15 02:08:06 +02:00
mov ax, cx ; get quotient
pop cx ; restore dividend hi-word
sbb cx, dx ; subtract (divisor-lo * quot.)-hi from dividend-hi
2012-10-15 02:08:06 +02:00
sbb dx, dx ; 0 if remainder > 0, else FFFFFFFFh
and si, dx ; nothing to add
and di, dx ; back if remainder positive di:si := di:si(cx:bx) & dx:dx
add bx, si ; correct remainder cx:bx += di:si
adc cx, di ; and
add ax, dx ; quotient if necessary ax += dx
xor dx, dx ; clear hi-word of quot (ax<=FFFFh) dx := 0
2012-10-15 02:08:06 +02:00
pop di ; restore temp
pop si ; variables
ret
%endmacro
%macro LSHLU 0
pop bx
popargs {dx,ax},cx
push bx
jcxz %%ret
%%loop: shl ax, 1
rcl dx, 1
loop %%loop
%%ret: ret
%endmacro
%macro LSHRU 0
pop bx
popargs {dx,ax},cx
push bx
jcxz %%ret
%%loop: shr dx, 1
rcr ax, 1
loop %%loop
%%ret: ret
%endmacro