		ORG	$0
		DC.L	$8000		Stack pointer value after a reset
		DC.L	START		Program counter value after a reset

START:		MOVE.L	#12,-(SP)	push the argument on the stack
		JSR	FAC		call factorial
		MOVE.L	(SP)+,D0	pop the result from the stack
		BREAK

		MOVE.L	#13,-(SP)
		JSR	FAC
		MOVE.L	(SP)+,D0
		BREAK


********************************************************************************
*
* function:   (SP) FAC( (SP))
*
* description:
*   computes the factorial of the number that was top of the runtime stack
*   right before calling the function. The result is returned via the very
*   same stack location.
*
* registers affected: none
*

FAC:		LINK	A6,#0		save A6 and set it to its stack location
		MOVEM.L	D0-D1,-(SP)	save the registers used
		MOVE.L	8(A6),D0	fetch n

		CMPI	#0,D0		n == 0?
		BNE	ELSE
		MOVE.L	#1,D0		res (D0) = 1
		BRA	DONE
ELSE:		MOVE.L	D0,D1
		SUBI	#1,D1
		MOVE.L	D1,-(SP)	push (n-1)
		JSR	FAC		call factorial
		MOVE.L	D0,-(SP)
		JSR	MULU_L
		MOVE.L	(SP)+,D0	higher long of the result
		BEQ	OK
		MOVE.L	#0,D0		signalize an error
		BRA	DONE
OK:		MOVE.L	(SP)+,D0	lower long of the result

DONE:		MOVE.L	D0,8(A6)	save the result in the argument frame
		MOVEM.L	(SP)+,D0-D1	restore the registers used
		UNLK	A6		restore A6
		RTS


********************************************************************************
*
* function:   (SP1), (SP2) MULU_L( (SP1), (SP2))
*
* description:
*   multiplies two long formats given in (SP1),(SP2). The result is returned
*   in (SP2),(SP1), which hold the higher and the lower long format of the result,
*   respectively.
*
* registers affected: none
*

MULU_L:		LINK	A6,#0
		MOVEM.L D0-D7,-(SP)
		MOVE.L	12(A6),D0
		MOVE.L	8(A6),D1

		MOVE.L	D0,D4
		SWAP	D4		D4 = HW( D0)
		MOVE.L	D1,D5
		SWAP	D5		D5 = HW( D1)
		MOVE.L	D4,D2
		MULU	D5,D2		D2 = HW( D0) * HW( D1)
		MOVE.L	D1,D3
		MULU	D0,D3		D3 = LW( D0) * LW( D1)

		MULU	D1,D4		D4 = HW( D0) * LW( D1)
		MULU	D0,D5		D5 = LW( D0) * HW( D1)
		ADD.L	D5,D4
		BCC	NO_INC_HW	do we have a carry affecting r0?
		ADD.L	#$10000,D2	increment r0
NO_INC_HW:	SWAP	D4
		MOVE.L	D4,D5
		ANDI.L	#$FFFF,D4	D4 = $0000     HW(D4+D5)
		ANDI.L	#$FFFF0000,D5	D5 = LW(D4+D5) $0000
		ADD.L	D5,D3		LL( D0.L * D1.L)
		ADDX.L	D4,D2		HL( D0.L * D1.L)

		MOVE.L	D2,8(A6)	store D2
		MOVE.L	D3,12(A6)	store D3
		MOVEM.L	(SP)+,D0-D7
		UNLK	A6
		RTS
