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.