5.5 Exercise 5.5
5.5.1 Factorial machine
(controller (assign continue (label fact-done)) fact-loop (test (op =) (reg n) (const 1)) (branch (label base-case)) (save continue) (save n) (assign n (op -) (reg n) (const 1)) (assign continue (label after-fact)) (goto (label fact-loop)) after-fact (restore n) (restore continue) (assign val (op *) (reg n) (reg val)) (goto (reg continue)) base-case (assign val (const 1)) (goto (reg continue)) fact-done)
After each instruction, if any register values have changed, they all will be shown in a table, like this:
Register |
| Value |
n |
| 2 |
The stack will also be shown when it is updated, like this:
Stack value |
<end> |
(assign continue (label fact-done))
Register |
| Value |
n |
| 2 |
continue |
| (label fact-done) |
(test (op =) (reg n) (const 1)) (branch (label base-case))
This evaluates to false, so we don’t branch.
(save continue) (save n)
This adds two values to the stack:
Stack value |
2 |
(label fact-done) |
<end> |
(assign n (op -) (reg n) (const 1)) (assign continue (label after-fact)) (goto (label fact-loop))
This updates the values of n and continue for the next recursive call, and jumps to the beginning of the loop.
Register |
| Value |
n |
| 1 |
continue |
| (label after-fact) |
(test (op =) (reg n) (const 1)) (branch (label base-case))
In this case, we do take this branch, moving to the base-case label.
(assign val (const 1)) (goto (reg continue))
Register |
| Value |
n |
| 1 |
continue |
| (label after-fact) |
val |
| 1 |
At this point, we have first assigned to the val register where the final result will be stored, and will jump to the instruction currently pointed at by the continue register – that is, the after-fact label.
(restore n) (restore continue)
Register |
| Value |
n |
| 2 |
continue |
| (label fact-done) |
val |
| 1 |
Stack value |
<end> |
(assign val (op *) (reg n) (reg val)) (goto (reg continue))
We assign the final val and will presently jump to fact-done, ending the procedure.
Register |
| Value |
n |
| 2 |
continue |
| (label fact-done) |
val |
| 2 |
5.5.2 Fibonacci machine
(controller (assign continue (label fib-done)) fib-loop (test (op <) (reg n) (const 2)) (branch (label immediate-answer)) (save continue) (assign continue (label afterfib-n-1)) (save n) (assign n (op -) (reg n) (const 1)) (goto (label fib-loop)) afterfib-n-1 (restore n) (restore continue) (assign n (op -) (reg n) (const 2)) (save continue) (assign continue (label afterfib-n-2)) (save val) (goto (label fib-loop)) afterfib-n-2 (assign n (reg val)) (restore val) (restore continue) (assign val (op +) (reg val) (reg n)) (goto (reg continue)) immediate-answer (assign val (reg n)) (goto (reg continue)) fib-done)
Our register values will start out like this:
Register |
| Value |
n |
| 3 |
And our stack is empty:
Stack value |
<end> |
(assign continue (label fib-done))
Register |
| Value |
n |
| 3 |
continue |
| (label fact-done) |
(test (op <) (reg n) (const 2)) (branch (label immediate-answer))
This is false, so we don’t branch.
(save continue)
Stack value |
(label fib-done) |
<end> |
(assign continue (label afterfib-n-1))
Register |
| Value |
n |
| 3 |
continue |
| (label afterfib-n-1) |
(save n)
Stack value |
3 |
(label fib-done) |
<end> |
(assign n (op -) (reg n) (const 1)) (goto (label fib-loop))
We decrement n and move on to the next loop.
Register |
| Value |
n |
| 2 |
continue |
| (label afterfib-n-1) |
(test (op <) (reg n) (const 2)) (branch (label immediate-answer))
This is false, so we don’t branch.
(save continue)
Stack value |
(label afterfib-n-1) |
3 |
(label fib-done) |
<end> |
(assign continue (label afterfib-n-1))
This happens to do nothing.
Register |
| Value |
n |
| 2 |
continue |
| (label afterfib-n-1) |
(save n)
Stack value |
2 |
(label afterfib-n-1) |
3 |
(label fib-done) |
<end> |
(assign n (op -) (reg n) (const 1)) (goto (label fib-loop))
We decrement n and move on to the next loop.
Register |
| Value |
n |
| 1 |
continue |
| (label afterfib-n-1) |
(test (op <) (reg n) (const 2)) (branch (label immediate-answer))
This is now true, so we will branch.
(assign val (reg n)) (goto (reg continue))
We write the base case value into the val register and will presently jump to afterfib-n-1.
Register |
| Value |
n |
| 1 |
continue |
| (label afterfib-n-1) |
val |
| 1 |
(restore n) (restore continue)
Register |
| Value |
n |
| 2 |
continue |
| (label afterfib-n-1) |
val |
| 1 |
Stack value |
3 |
(label fib-done) |
<end> |
(assign n (op -) (reg n) (const 2))
Register |
| Value |
n |
| 0 |
continue |
| (label afterfib-n-1) |
val |
| 1 |
(save continue)
Stack value |
(label afterfib-n-1) |
3 |
(label fib-done) |
<end> |
(assign continue (label afterfib-n-2))
We prepare to jump into the other recursive branch for the first time.
Register |
| Value |
n |
| 0 |
continue |
| (label afterfib-n-2) |
val |
| 1 |
(save val) (goto (label fib-loop))
We also save the value of Fib(1) – we’re going to need it later.
Stack value |
1 |
(label afterfib-n-1) |
3 |
(label fib-done) |
<end> |
After this, we will begin looping again.
(test (op <) (reg n) (const 2)) (branch (label immediate-answer))
This is true, so we branch.
(assign val (reg n)) (goto (reg continue))
We write the base case value into the val register (overwriting the value that we saved to the stack earlier) and will presently jump to afterfib-n-1.
Register |
| Value |
n |
| 0 |
continue |
| (label afterfib-n-2) |
val |
| 0 |
(assign n (reg val))
This happens to do nothing.
Register |
| Value |
n |
| 0 |
continue |
| (label afterfib-n-2) |
val |
| 0 |
(restore val) (restore continue)
Register |
| Value |
n |
| 0 |
continue |
| (label afterfib-n-1) |
val |
| 1 |
Stack value |
3 |
(label fib-done) |
<end> |
(assign val (op +) (reg val) (reg n)) (goto (reg continue))
This also happens to do nothing. After this, we will jump to afterfib-n-1 for another recursive go-around.
Register |
| Value |
n |
| 0 |
continue |
| (label afterfib-n-1) |
val |
| 1 |
(restore n) (restore continue)
Register |
| Value |
n |
| 3 |
continue |
| (label fib-done) |
val |
| 1 |
Stack value |
<end> |
(assign n (op -) (reg n) (const 2))
Register |
| Value |
n |
| 1 |
continue |
| (label fib-done) |
val |
| 1 |
(save continue)
Notice how we have popped this label off of the stack and immediately put it back.
Stack value |
(label fact-done) |
<end> |
(assign continue (label afterfib-n-2))
Register |
| Value |
n |
| 1 |
continue |
| (label afterfib-n-2) |
val |
| 1 |
(save val) (goto (label fib-loop))
Stack value |
1 |
(label fact-done) |
<end> |
(test (op <) (reg n) (const 2)) (branch (label immediate-answer))
This is true, so we will branch.
(assign val (reg n)) (goto (reg continue))
This happens to do nothing.
Register |
| Value |
n |
| 1 |
continue |
| (label afterfib-n-2) |
val |
| 1 |
(assign n (reg val))
This happens to do nothing.
Register |
| Value |
n |
| 1 |
continue |
| (label afterfib-n-2) |
val |
| 1 |
(restore val) (restore continue)
Register |
| Value |
n |
| 1 |
continue |
| (label fact-done) |
val |
| 1 |
Stack value |
<end> |
(assign val (op +) (reg val) (reg n)) (goto (reg continue))
With this final addition, we reach our final answer and exit the procedure.
Register |
| Value |
n |
| 1 |
continue |
| (label fact-done) |
val |
| 2 |