I can see how 6.001 was a bit like jumping into the deep end to learn to swim.
Starting from, "To design a register machine, we must design its data paths (registers and operations) and the controller that sequences these operations." In less than 5,000 words it proceeds to implementing recursive procedure calls in what amounts to assembly:
(controller
(assign continue (label fact-done)) ; set up final return address
fact-loop
(test (op =) (reg n) (const 1))
(branch (label base-case))
(save continue) ; Set up for the recursive call
(save n) ; by saving n and continue.
(assign n (op -) (reg n) (const 1)) ; Set up continue so that the
(assign continue (label after-fact)) ; computation will continue
(goto (label fact-loop)) ; at after-fact when the
after-fact ; subroutine returns.
(restore n)
(restore continue)
(assign val (op *) (reg n) (reg val)) ; val now contains n(n - 1)!
(goto (reg continue)) ; return to caller
base-case
(assign val (const 1)) ; base case: 1! = 1
(goto (reg continue)) ; return to caller
fact-done)
A translation of:
(define (factorial n)
(if (= n 1)
1
(* (factorial (- n 1)) n)))
It's quite a jump in knowledge, but explained very clearly through a series of guided examples. It's close to equivalent code in x86 or arm, but much easier to read.
Starting from, "To design a register machine, we must design its data paths (registers and operations) and the controller that sequences these operations." In less than 5,000 words it proceeds to implementing recursive procedure calls in what amounts to assembly:
A translation of: It's quite a jump in knowledge, but explained very clearly through a series of guided examples. It's close to equivalent code in x86 or arm, but much easier to read.