summaryrefslogtreecommitdiff
path: root/libc/bcc/__ldivmod.c
diff options
context:
space:
mode:
Diffstat (limited to 'libc/bcc/__ldivmod.c')
-rw-r--r--libc/bcc/__ldivmod.c195
1 files changed, 195 insertions, 0 deletions
diff --git a/libc/bcc/__ldivmod.c b/libc/bcc/__ldivmod.c
new file mode 100644
index 0000000..4cb6d77
--- /dev/null
+++ b/libc/bcc/__ldivmod.c
@@ -0,0 +1,195 @@
+/************************************************************************/
+/* This file contains the BCC compiler helper functions */
+/* Function ldivmod */
+
+#ifdef __AS386_16__
+#asm
+ .text
+ .even
+
+| ldivmod.s - 32 over 32 to 32 bit division and remainder for 8086
+
+| ldivmod( dividend bx:ax, divisor di:cx ) [ signed quot di:cx, rem bx:ax ]
+| ludivmod( dividend bx:ax, divisor di:cx ) [ unsigned quot di:cx, rem bx:ax ]
+
+| dx is not preserved
+
+
+| NB negatives are handled correctly, unlike by the processor
+| divison by zero does not trap
+
+
+| let dividend = a, divisor = b, quotient = q, remainder = r
+| a = b * q + r mod 2^32
+| where:
+
+| if b = 0, q = 0 and r = a
+
+| otherwise, q and r are uniquely determined by the requirements:
+| r has the same sign as b and absolute value smaller than that of b, i.e.
+| if b > 0, then 0 <= r < b
+| if b < 0, then 0 >= r > b
+| (the absoulute value and its comparison depend on signed/unsigned)
+
+| the rule for the sign of r means that the quotient is truncated towards
+| negative infinity in the usual case of a positive divisor
+
+| if the divisor is negative, the division is done by negating a and b,
+| doing the division, then negating q and r
+
+
+ .globl ldivmod
+
+ldivmod:
+ mov dx,di ! sign byte of b in dh
+ mov dl,bh ! sign byte of a in dl
+ test di,di
+ jns set_asign
+ neg di
+ neg cx
+ sbb di,*0
+set_asign:
+ test bx,bx
+ jns got_signs ! leave r = a positive
+ neg bx
+ neg ax
+ sbb bx,*0
+ j got_signs
+
+ .globl ludivmod
+ .even
+
+ludivmod:
+ xor dx,dx ! both sign bytes 0
+got_signs:
+ push bp
+ push si
+ mov bp,sp
+ push di ! remember b
+ push cx
+b0 = -4
+b16 = -2
+
+ test di,di
+ jne divlarge
+ test cx,cx
+ je divzero
+ cmp bx,cx
+ jae divlarge ! would overflow
+ xchg dx,bx ! a in dx:ax, signs in bx
+ div cx
+ xchg cx,ax ! q in di:cx, junk in ax
+ xchg ax,bx ! signs in ax, junk in bx
+ xchg ax,dx ! r in ax, signs back in dx
+ mov bx,di ! r in bx:ax
+ j zdivu1
+
+divzero: ! return q = 0 and r = a
+ test dl,dl
+ jns return
+ j negr ! a initially minus, restore it
+
+divlarge:
+ push dx ! remember sign bytes
+ mov si,di ! w in si:dx, initially b from di:cx
+ mov dx,cx
+ xor cx,cx ! q in di:cx, initially 0
+ mov di,cx
+ ! r in bx:ax, initially a
+ ! use di:cx rather than dx:cx in order to
+ ! have dx free for a byte pair later
+ cmp si,bx
+ jb loop1
+ ja zdivu ! finished if b > r
+ cmp dx,ax
+ ja zdivu
+
+| rotate w (= b) to greatest dyadic multiple of b <= r
+
+loop1:
+ shl dx,*1 ! w = 2*w
+ rcl si,*1
+ jc loop1_exit ! w was > r counting overflow (unsigned)
+ cmp si,bx ! while w <= r (unsigned)
+ jb loop1
+ ja loop1_exit
+ cmp dx,ax
+ jbe loop1 ! else exit with carry clear for rcr
+loop1_exit:
+ rcr si,*1
+ rcr dx,*1
+loop2:
+ shl cx,*1 ! q = 2*q
+ rcl di,*1
+ cmp si,bx ! if w <= r
+ jb loop2_over
+ ja loop2_test
+ cmp dx,ax
+ ja loop2_test
+loop2_over:
+ add cx,*1 ! q++
+ adc di,*0
+ sub ax,dx ! r = r-w
+ sbb bx,si
+loop2_test:
+ shr si,*1 ! w = w/2
+ rcr dx,*1
+ cmp si,b16[bp] ! while w >= b
+ ja loop2
+ jb zdivu
+ cmp dx,b0[bp]
+ jae loop2
+
+zdivu:
+ pop dx ! sign bytes
+zdivu1:
+ test dh,dh
+ js zbminus
+ test dl,dl
+ jns return ! else a initially minus, b plus
+ mov dx,ax ! -a = b * q + r ==> a = b * (-q) + (-r)
+ or dx,bx
+ je negq ! use if r = 0
+ sub ax,b0[bp] ! use a = b * (-1 - q) + (b - r)
+ sbb bx,b16[bp]
+ not cx ! q = -1 - q (same as complement)
+ not di
+negr:
+ neg bx
+ neg ax
+ sbb bx,*0
+return:
+ mov sp,bp
+ pop si
+ pop bp
+ ret
+
+ .even
+
+zbminus:
+ test dl,dl ! (-a) = (-b) * q + r ==> a = b * q + (-r)
+ js negr ! use if initial a was minus
+ mov dx,ax ! a = (-b) * q + r ==> a = b * (-q) + r
+ or dx,bx
+ je negq ! use if r = 0
+ sub ax,b0[bp] ! use a = b * (-1 - q) + (b + r) (b is now -b)
+ sbb bx,b16[bp]
+ not cx
+ not di
+ mov sp,bp
+ pop si
+ pop bp
+ ret
+
+ .even
+
+negq:
+ neg di
+ neg cx
+ sbb di,*0
+ mov sp,bp
+ pop si
+ pop bp
+ ret
+#endasm
+#endif