; r = MulDiv(a,b,c) long a,b,c; ; r = MulDivU(a,b,c) unsigned long a,b,c; ; ; result.32 = a.32 * b.32 / c.32 ; ; 32x32->64 64/32->32 multiply-divide combination section CODE xdef _MulDiv xdef _MulDivU _MulDiv: movem.l D2-D4,-(sp) ; save D2/D3 (generic compiler compatibility) moveq.l #0,D4 ; clear negate flag lea 16(sp),A0 ; address of 'a' move.l A0,A1 ; save A1 for muldivu call tst.l (A0) ; test a bpl .mp neg.l (A0)+ ; n-- tst.l (A0) ; test b bpl .mnp neg.l (A0)+ ; nn- tst.l (A0) ; test c bpl muldivu ; nnp CALL, RESULT POSITIVE .mppn neg.l (A0) ; nnn .mpnp .mnpp moveq.l #1,D4 ; set negate flag bra muldivu .mp addq.l #4,A0 ; p-- tst.l (A0) ; test b bpl .mpp neg.l (A0)+ ; pn- tst.l (A0) ; test c bpl .mpnp neg.l (A0) ; pnn bra muldivu ; jmp to routine .mpp addq.l #4,A0 ; pp- tst.l (A0) bpl muldivu ; ppp RESULT POSITIVE bra .mppn ; RESULT NEGATIVE (negate (A0) befor call) .mnp addq.l #4,A0 ; np- tst.l (A0) ; test c bpl .mnpp ; RESULT NEGATIVE neg.l (A0) ; npn bra muldivu ; RESULT POSITIVE ; MulDivU(a,b,c) AH 4(sp) AL 6(sp) BH 8(sp) BL 10(sp) ; unsigned long a @4(sp) ; unsigned long b @8(sp) ; unsigned long c @12(sp) _MulDivU: movem.l D2-D4,-(sp) ; save D2-D4 moveq.l #0,D4 ; clear negate flag lea 16(sp),A1 ; address of 'a' muldivu lea 4(A1),A0 move.w (A0)+,D3 ; bh ah 8(sp) A0 8->10 mulu.w (A1)+,D3 ; 4(sp) A1 4->6 move.w (A0),D0 ; bl al 10(sp) A0 10 mulu (A1),D0 ; 6(sp) A1 6 move.w (A0),D1 ; bl ah 10(sp) A0 10 mulu.w -(A1),D1 ; 4(sp) A1 now 4 move.w -(A0),D2 ; bh al 8(sp) A0 now 8 mulu.w -(A0),D2 ; 6(sp) A0 now 6 add.l D1,D2 ; combine blah and bhal bcc .mud1 add.l #$10000,D3 .mud1 swap D0 ; LSB MSB add.w D2,D0 swap D0 swap D2 and.l #$FFFF,D2 addx.l D2,D3 ;64 bit mul result: D3|D0 ;64 bit by 32 bit division. 32 bit result. move.l 6(A0),D1 ;D1 = c A0 WAS 6 beq .mdfail sub.l A0,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 bcs .mud11 ;if D3 < D1, skip (blo) 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. REMOVED ; ;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 tst.l D4 ;D4 non-zero means negate result beq .mud13 neg.l D0 .mud13 movem.l (sp)+,D2-D4 ;restore D2-D4 rts .mdfail moveq.l #-1,D0 bra .mud13 END