TAOCP/1

I bought a boxed set of Knuth's Art of Computer Programming last week. As you may or may not know, TAOCP demonstrates programs in assembly for a mythical computer known as the MIX 1009. MIX would not have been out of place in 1965, but is hopelessly outdated today. Nonetheless, I learned enough MIX to write the following program in the MIX Assembly Language.

* factorial.mixal
*
* calculate n factorial in MIXAL
*
* loops on rI1 from n down to 0
*
* n! must fit into a 30-byte word, or it overflows
* n must fit into a 12-byte index register,
* n must be positive.
*
* if n! overflows, it will throw an error.
*
* Tested with GNU MDK 1.2.3.
*
* assemble with:
*	mixasm factorial.mixal
* execute with:
*	mixvm --run factorial.mix
*
* This program is dedicated to the public domain.
*      ---Hal Canary, https://halcanary.org/
*
n	EQU	11	* Argument
pp	EQU	1000	* pointer to a memory address.
*			* We make use of pp and pp+1
term    EQU     19	* terminal
        ORIG    3000
start	ENTA	1
	ENTX	0
	ENT1	n
loop	ST1	pp
	MUL	pp	* pp! == pp * (pp-1)!
	CMPA	0	* Check for overflow
	JNE	ovrflw
	SLAX	5	* Shift rX back to rA.
	DEC1	1	* k <- k-1
	J1P	loop	* if (k > 0) loop back
	CHAR	0	* rAX <- sprintf("%i", rA)
	STX	pp+1
	STA	pp
	OUT	pp(term)	* output pp and pp+1
        HLT
ovrflw	OUT	error0(term)
	HLT
error0	ALF	OVERF
	ALF	LOW
        END     start

Notice how I had to write to memory in order to multiply two numbers together! Crazy.

(Factorial calculations are a common assignment in computer science 101, as they are a simple exercise in a language's looping constructs.)

Here's the same program, reimplemented for the Emmix 2009:

* factorial.mmo
*
* Calculate factorial(NUMBER) in MMIXAL.
* If it overflows, it exits with no output.
*
* Tested with Donald E. Knuth's MMIXware (v20060918).
*
* Assemble with:
*	mmixal factorial.mms
* Execute with:
*	mmix factorial.mmo
*
* This program is dedicated to the public domain.
*	---Hal Canary, https://halcanary.org/

NUMBER	IS	12

	LOC	Data_Segment
buf	BYTE	0		this buffer will be 21
	LOC	buf+21			   bytes long
pbuf	GREG	@		pointer to the end of
num	OCTA	NUMBER			   the buffer
pnum	GREG	num
n	IS	pnum		the number
r	GREG	0		the remainder
k	IS	r		counter
ov	GREG	0

	LOC	#100
Main	LDO	n,pnum,0	load number from memory
	SUB	k,n,1
1H	MULU	n,n,k
	GET	ov,rH		check for overflow
	BP	ov,overflow
	SUB	k,k,1
	PBP	k,1B

* Print out n, followed by a newline.
	SET	r,0		'\0' character
	STBU	r,pbuf
	SUB	pbuf,pbuf,1
	SET	r,10		'\n' char
	STBU	r,pbuf
2H	SUB	pbuf,pbuf,1
	DIVU	n,n,10
	GET	r,rR		final digit is the remainder
	INCL	r,'0'		convert digit to ASCII char
	STBU	r,pbuf		store digit char in buffer
	PBP	n,2B		if there is more chars, loop
	SET	$255,pbuf	set $255 to point to the
	TRAP	0,Fputs,StdOut		most signif. digit.
	TRAP	0,Halt,0

overflow TRAP	0,Halt,0

This processor required me to (gasp) rewrite 'printf("%u",n);', which was the hardest part of the program.