Published on

nand2tetris Series 02 - Computing Sum of 1 to 10

Authors
  • avatar
    Name
    Morphy Chan
    Twitter

The complete program is shown below, largely following the example from 1.

// Adds 1+...+10
    @i
    M=1
    @sum
    M=0
(LOOP)
    @i
    D=M
    @10
    D=D-A
    @END
    D;JGT
    @i
    D=M
    @sum
    M=D+M
    @i
    M=M+1
    @LOOP
    0;JMP
(END)
    @END
    0;JMP

The program structure is straightforward, consisting of three parts: variable initialization, a loop for addition, and an infinite loop as termination.

Before diving into the specific instructions, let's first look at a dynamic view of the program running:

sum10

Now let's analyze the program instructions in detail.

1. Variable Initialization

Two variables are initialized here: i and sum. Since variables are stored in memory, we need to learn how instructions declare and initialize variables in memory.

First, the @ instruction — as introduced previously, @value is called the A-Instruction, which sets the A register to value. The value can be a non-negative decimal number or a symbol referring to such a number.

Next is the M instruction, which assigns a value to memory — effectively M[A].

The Hack ALU is designed to compute a fixed set of functions on the D, A, and M registers (where M stands for Memory[A]).

When the CPU operates on memory, it first needs to know the memory address. The CPU interacts directly with registers, so the address must be stored in a register. In the hack computer's instruction design, the A register is used for this purpose. Therefore, M=1 actually means M[A]=1 — using the A register's value as the memory address and assigning it the value 1.

With these two instructions understood, the initialization code becomes clear:

    @i
    M=1
    @sum
    M=0
  1. Declare variable i: set the A register to i, then use the M instruction to initialize M[i] to 1.
  2. Declare variable sum: set the A register to sum, then use the M instruction to initialize M[sum] to 0.

This is equivalent to the following in a high-level language:

i=1;
sum=0;

But there's a question: both i and sum are symbols that should refer to non-negative decimal numbers, yet there's no code initializing the symbols themselves.

In practice, when the assembly code is compiled into hack computer byte-code (this happens automatically when loading the program into the CPU emulator), the compiler automatically replaces symbols i and sum with non-negative decimal numbers. In the actual running byte-code, there's no concept of symbols. For example, when this assembly code is loaded into the emulator:

var-symbol-subs

i is replaced with 16, and sum with 17. After the first four instructions execute, memory address 16 (variable i) holds the value 1, and memory address 17 (variable sum) holds 0.

2. Loop Addition

In a high-level language, this logic would be:

for(;i<=10;i++) {
    sum += i;
}

Let's see how hack computer assembly implements this.

(LOOP)
    @i
    D=M
    @10
    D=D-A
    @END
    D;JGT
    @i
    D=M
    @sum
    M=D+M
    @i
    M=M+1
    @LOOP
    0;JMP
  1. @i: set the A register to i (which is 16).
  2. D=M: i.e., D=M[16], assign the value at memory address 16 to the D register — equivalent to D=i. After execution, D equals 1.
  3. @10: set the A register to 10 — this is the loop condition.
  4. D=D-A: D is 1, A is 10. The ALU performs subtraction on the D and A registers. After execution, D equals -9. Before execution:

    D_minus_A-before

    After execution:

    D_minus_A-after

  5. @END: set the A register to END (replaced by the compiler with 18), preparing for the next jump instruction.
  6. D;JGT: check if D is greater than 0. If so, jump to the END address — this sets the loop exit condition. Since D (-9) is less than 0, no jump occurs.
  7. @i and D=M: similar to instructions 1 and 2, assign the value at memory address 16 to D. After execution, D equals 1.
  8. @sum and M=D+M: retrieve the value of sum (memory address 17, currently 0), add D to it (this is the first addition to sum — adding 1), and store the result back to memory address 17, which now becomes 1.

    M_plus_D-after

  9. @i and M=M+1: retrieve the value of i and increment by 1 — equivalent to i++.
  10. @LOOP: set the A register to LOOP (replaced by the compiler with 4), preparing for the next jump instruction.
  11. 0;JMP: unconditional jump to the LOOP position, i.e., start executing from instruction at PC counter 4, beginning the next loop iteration.

    unconditional_jump-after

This completes the first iteration of the loop. The loop runs 10 times in total to compute the sum of 1 to 10.

3. Program Termination

After 10 iterations, sum (memory address 17) equals 55, and i (memory address 16) is 11. Continuing execution to instruction 7 (D=D-A), with D=11 and A=10, D becomes 1 after execution. Then instruction 9 (the jump instruction) triggers because D>0, jumping to the END position (instruction 18), which enters an infinite loop as termination — as introduced in the previous post.

Footnotes

  1. chapter 4