
;FUNC=	MulDiv	    D0/D1/D2
;FUNC=	UMulDiv     D0/D1/D2
;FUNC=	MulShift    D0/D1/D2
;FUNC=	UMulShift   D0/D1/D2

	    ;	MulDiv	(a ,b ,c)   32x32->64 64/32->32     (signed)
	    ;	MulShift(a ,b ,n)   32x32->64 64>>n->32     (signed)
	    ;		 D0,D1,D2
	    ;
	    ;	32x32->64 64/32->32
	    ;	32x32->64 64>>n->32
	    ;
	    ;	note:	n is a word

	    public  _MulDiv
	    public  _UMulDiv
	    public  _MulShift
	    public  _UMulShift

	    ;	Various connotations specifying which arguments are
	    ;	unsigned (you gain a bit when using unsigned arguments)

REGS	    eqr     D2-D7

_UMulDiv    movem.l REGS,-(sp)
	    clr.w   D7
	    bra     .md10
_MulDiv     movem.l REGS,-(sp)
	    clr.w   D7
	    tst.l   D0
	    bpl     .md2
	    neg.l   D0
	    tst.l   D1	    ; neg
	    bpl     .md1    ; neg
	    neg.l   D1
	    tst.l   D2	    ; neg neg
	    bpl     .md10   ; neg neg
	    neg.l   D2
	    bra     .md9    ; neg neg neg
.md1	    tst.l   D2	    ; neg
	    bpl     .md9    ; neg
	    neg.l   D2
	    bra     .md10   ; neg neg
.md2	    tst.l   D1
	    bpl     .md3
	    neg.l   D1
	    tst.l   D2	    ; neg
	    bpl     .md9    ; neg
	    neg.l   D2
	    bra     .md10   ; neg neg
.md3	    tst.l   D2
	    bpl     .md10
	    neg.l   D2
	    bra     .md9

_UMulShift  movem.l REGS,-(sp)
	    clr.w   D7
	    bra     .md6
_MulShift   movem.l REGS,-(sp)
	    clr.w   D7
	    tst.l   D0
	    bpl     .md4
	    neg.l   D0
	    tst.l   D1
	    bpl     .md5
	    neg.l   D0
	    bra     .md6
.md4	    tst.l   D1
	    bpl     .md6
	    neg.l   D1
.md5	    addq.w  #1,D7

.md6	    swap    D0
	    swap    D1
	    tst.w   D0
	    beq     .md601
	    bsr     .md0huge
	    beq     .md602a
	    bra     .md603
.md601	    tst.w   D1
	    beq     .md602
	    bsr     .md1huge
	    beq     .md602a
	    bra     .md603
.md602	    swap    D0
	    swap    D1
	    mulu    D1,D0
.md602a     lsr.l   D2,D0
	    bra     .mddone
.md603	    subq.w  #1,D2
	    bmi     .mddone
.md604	    lsr.l   #1,D3
	    roxr.l  #1,D0
	    dbf     D2,.md604
	    bra     .mddone


.md10	    swap    D0
	    swap    D1
	    tst.w   D0
	    beq     .md101
	    bsr     .md0huge
	    beq     .mdsdiv
	    bsr     div64
	    bra     .mddone
.md101	    tst.w   D1
	    beq     .md105
	    bsr     .md1huge
	    beq     .mdsdiv
	    bsr     div64
	    bra     .mddone
.md105	    swap    D0		; both word sized
	    swap    D1
	    mulu    D1,D0	; D1.WxD0.W -> D0.L
.mdsdiv     move.l  D2,D1
	    jsr     .divu#

	    ;
	    ;

.md9	    addq.w  #1,D7

	    ;	All elements now unsigned, D7.W determines whether to
	    ;	negate at end.

.mddone     btst.l  #0,D7
	    beq     .mddone2
	    neg.l   D0
.mddone2    movem.l (sp)+,REGS
	    rts


	    ;	NOTE:	entered with register halves reversed
	    ;
	    ;	D2 not effected
	    ;	Test D3 on return

.md0huge    tst.w   D1
	    beq     .md1hugex	; D0.L, D1.W
				; D0.L, D1.L	(long x long)
	    move.w  D0,D4	; save ah
	    move.w  D0,D3	; ah bh
	    mulu.w  D1,D3
	    swap    D0		; al bh
	    move.w  D0,D5
	    mulu.w  D1,D5
	    swap    D1
	    mulu.w  D1,D0	; al bl
	    mulu.w  D4,D1	; ah bl
	    add.l   D1,D5	; combine blah and bhal
	    bcc     .mud1
	    add.l   #$10000,D3
.mud1	    swap    D0		; LSB MSB
	    add.w   D5,D0
	    swap    D0
	    clr.w   D5
	    swap    D5
	    addx.l  D5,D3	;64 bit mul result: D3|D0
	    rts 		;Test D3

	    ; D2 not effected

.md1huge    exg     D0,D1
.md1hugex
	    ; D0.L, D1.W	al ah
	    ;			   bl

	    move.w  D0,D3
	    mulu.w  D1,D3	; D3 = bl x ah
	    swap    D0
	    mulu.w  D1,D0	; D0 = bl x al
	    swap    D0		; add lsb byte of D3 to msb byte of D0
	    add.w   D3,D0
	    swap    D0
	    clr.w   D3		; doesn't effect X
	    swap    D3		; msb of D3 actually lsb of second longword
	    moveq.l #0,D1
	    addx    D1,D3	; possible x(carry) from previous addition
				; 64 bit mul result: D3|D0
				; Test D3
	    rts

	    ;	DIV64
	    ;
	    ;	64/32->32   D3|D0 / D2 -> D0

div64:
	    move.l  #0,A0	;Divide!    D1 into D3|D0, D2 = cntr, A0 = rslt
	    move.w  #31,D2	;(no initial compare).  31 + 1 iterations
.mud10	    adda.l  A0,A0	;shift result left
	    asl.l   #1,D0	;Shift left
	    roxl.l  #1,D3
	    cmp.l   D1,D3
	    blo     .mud11	;if D3	< D1, skip
	    sub.l   D1,D3	;   D3 >= D1
	    addq.l  #1,A0	;result = result | 1
.mud11	    dbf     D2,.mud10

	    ;	If remainder (D3) larger than 1/2 C (D1 >> 1), then
	    ;	round up result.

	    lsr.l   #1,D1
	    cmp.l   D1,D3
	    blo     .mud12	; skip if remainder < 1/2C
	    addq.l  #1,A0
.mud12
	    move.l  A0,D0	;return result
	    rts

