; long arithmetic routines
;
; written by Kai-Uwe Bloem (I5110401@dbstu1.bitnet).
;
; Names changed and converted from MIT to Motorola syntax by Dave Gymer for SC

	.text
	.even
	.globl	lmul,lmulu
	.globl	ldiv,ldivu
	.globl	lrem,lremu

ldiv:
	move.l	d2,a0		; save registers
	move.l	d3,a1
	clr.w	-(sp)		; sign flag
	clr.l	d0		; prepare result
	move.l	10(sp),d2	; get divisor
	beq	L9		; divisor = 0 causes a division trap
	bpl	J0		; divisor < 0 ?
	neg.l	d2		; negate it
	not.w	(sp)		; remember sign
J0:	move.l	6(sp),d1	; get dividend
	bpl	J1		; dividend < 0 ?
	neg.l	d1		; negate it
	not.w	(sp)		; remember sign
J1:
;== case 1) divident < divisor
	cmp.l	d2,d1		; is divident smaller then divisor ?
	bcs	L8		; yes, return immediately
;== case 2) divisor has <= 16 significant bits
	tst.w	10(sp)
	bne	L2		; divisor has only 16 bits
	move.w	d1,d3		; save dividend
	clr.w	d1		; divide dvd.h by dvs
	swap	d1
	beq	J2		; (no division necessary if dividend zero)
	divu	d2,d1
J2:	move.w	d1,d0		; save quotient.h
	swap	d0
	move.w	d3,d1		; (d0.h = remainder of prev divu)
	divu	d2,d1		; divide dvd.l by dvs
	move.w	d1,d0		; save quotient.l
	clr.w	d1		; get remainder
	swap	d1
	bra	L8		; and return
;== case 3) divisor > 16 bits (corollary is dividend > 16 bits, see case 1)
L2:
	moveq	#31,d3		; loop count
L3:
	add.l	d1,d1		; shift divident ...
	addx.l	d0,d0		;  ... into d0
	cmp.l	d2,d0		; compare with divisor
	bcs	J3
	sub.l	d2,d0		; big enough, subtract
	add.w	#1,d1		; and note bit into result
J3:
	dbra	d3,L3
	exg	d0,d1		; put quotient and remainder in their registers
L8:
	tst.w	6(sp)		; must the remainder be corrected ?
	bpl	J4
	neg.l	d1		; yes, apply sign
; the following line would be correct if modulus is defined as in algebra
;	add.l	6(sp),d1	; algebraic correction: modulus can only be >= 0
J4:	tst.w	(sp)+		; result should be negative ?
	bpl	J5
	neg.l	d0		; yes, negate it
J5:
	move.l	a1,d3
	move.l	a0,d2
	rts
L9:
	divu	d2,d1		; cause division trap
	bra	L8		; back to user

lrem:
	move.l	8(sp),-(sp)	; push divisor
	move.l	8(sp),-(sp)	; push dividend
	bsr	ldiv
	lea	8(sp),sp
	move.l	d1,d0		; return the remainder in d0
	rts

ldivu:
	move.l	d2,a0		; save registers
	move.l	d3,a1
	clr.l	d0		; prepare result
	move.l	8(sp),d2	; get divisor
	beq	N9		; divisor = 0 causes a division trap
	move.l	4(sp),d1	; get dividend
;== case 1) divident < divisor
	cmp.l	d2,d1		; is divident smaller then divisor ?
	bcs	N8		; yes, return immediately
;== case 2) divisor has <= 16 significant bits
	tst.w	8(sp)
	bne	N2		; divisor has only 16 bits
	move.w	d1,d3		; save dividend
	clr.w	d1		; divide dvd.h by dvs
	swap	d1
	beq	K0		; (no division necessary if dividend zero)
	divu	d2,d1
K0:	move.w	d1,d0		; save quotient.h
	swap	d0
	move.w	d3,d1		; (d1.h = remainder of prev divu)
	divu	d2,d1		; divide dvd.l by dvs
	move.w	d1,d0		; save quotient.l
	clr.w	d1		; get remainder
	swap	d1
	bra	N8		; and return
;== case 3) divisor > 16 bits (corollary is dividend > 16 bits, see case 1)
N2:
	moveq	#31,d3		; loop count
N3:
	add.l	d1,d1		; shift divident ...
	addx.l	d0,d0		;  ... into d0
	cmp.l	d2,d0		; compare with divisor
	bcs	K1
	sub.l	d2,d0		; big enough, subtract
	add.w	#1,d1		; and note bit in result
K1:
	dbra	d3,N3
	exg	d0,d1		; put quotient and remainder in their registers
N8:
	move.l	a1,d3
	move.l	a0,d2
	rts
N9:
	divu	d2,d1		; cause division trap
	bra	N8		; back to user

lremu:
	move.l	8(sp),-(sp)	; push divisor
	move.l	8(sp),-(sp)	; push dividend
	bsr	ldivu
	lea	8(sp),sp
	move.l	d1,d0		; return the remainder in d0
	rts

lmul:
	move.l	d2,a0		; save registers
	move.l	d3,a1
	movem.w	4(sp),d0-d3	; get the two longs. u = d0-d1, v = d2-d3
	move.w	d0,-(sp)		; sign flag
	bpl	J6		; is u negative ?
	neg.w	d1		; yes, force it positive
	negx.w	d0
J6:	tst.w	d2		; is v negative ?
	bpl	J7
	neg.w	d3		; yes, force it positive ...
	negx.w	d2
	not.w	(sp)		;  ... and modify flag word
J7:
	ext.l	d0		; u.h <> 0 ?
	beq	M1
	mulu	d3,d0		; r  = v.l * u.h
M1:	tst.w	d2		; v.h <> 0 ?
	beq	M2
	mulu	d1,d2		; r += v.h * u.l
	add.w	d2,d0
M2:	swap	d0
	clr.w	d0
	mulu	d3,d1		; r += v.l * u.l
	add.l	d1,d0
	move.l	a1,d3
	move.l	a0,d2
	tst.w	(sp)+		; should the result be negated ?
	bpl	M3		; no, just return
	neg.l	d0		; else r = -r
M3:	rts

lmulu:
	move.l	d2,a0		; save registers
	move.l	d3,a1
	movem.w	4(sp),d0-d3	; get the two longs. u = d0-d1, v = d2-d3
	ext.l	d0		; u.h <> 0 ?
	beq	O1
	mulu	d3,d0		; r  = v.l * u.h
O1:	tst.w	d2		; v.h <> 0 ?
	beq	O2
	mulu	d1,d2		; r += v.h * u.l
	add.w	d2,d0
O2:	swap	d0
	clr.w	d0
	mulu	d3,d1		; r += v.l * u.l
	add.l	d1,d0
	move.l	a1,d3
	move.l	a0,d2
	rts
