Skip to content

nand2tetris系列02-sum10

  • by

本文承接上一篇博文,继续介绍使用hack computer的指令(下文简称指令)实现sum 10的汇编程序。

完整程序如下,基本完整参考了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

整个程序结构很清晰,分为3个部分:初始化变量、循环做加法和以无限循环作为结束。

在分析具体指令代码之前,先来看一段程序动态运行状态:

下面分析具体程序指令代码。

1.初始化变量

这里初始化了2个变量:isum,我们知道变量是存放在内存里,所以这里学习指令代码如何在内存中声明一个变量,并做初始化。

首先用到了@指令,前文已经介绍过,@value叫做A-Instruction(A指令),赋值A寄存器的值为value,value可以是一个非负数的十进制数或者symbol(该symbol refer这样类型的数)。

接下来是一个新的指令,M指令,给内存赋值,实际意义相当于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]).

我们知道,CPU在对内存进行操作的时候,首要知道内存地址,而CPU是和寄存器直接交互的,所以地址肯定要先存放在某个寄存器里。而hack computer指令的设计是使用A寄存器,所以,M=1实际上是M[A]=1,把A寄存器的值作为内存地址,并赋为1。

理解了这2条指令,再来看初始化代码就很清晰了:

    @i
    M=1
    @sum
    M=0
  1. 声明变量i:把A寄存器的值赋值为i,然后使用M指令,把内存M[i]的值初始化为1;
  2. 声明变量sum:把A寄存器的值赋值为sum,然后使用M指令,把内存M[sum]的值初始化为0。

相当于高级语言:

i=1;
sum=0;

但这里有个问题,isum都是symbol,按照要求必须refer一个非负的十进制数,但是在代码中并没有初始化symbol本身的代码?

实际情况是,在汇编代码被编译为hack computer byte-code(字节码)的时候(该过程在把汇编程序载入CPU模拟器时自动发生),编译器会为自动的为symbol i和sum替换为一个非负的十进制数。也就是在实际运行的字节码中,是没有symbol这个概念的,例如当这段汇编代码被载入模拟器:

i被替换为了16,sum被替换为17,而在前4条指令执行完毕以后,内存地址16(变量i)的值为1,内存地址17(变量sum)的值为0。

2.循环做加法

如果使用高级语言,那这里代码逻辑相当于:

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

下面看使用hack computer的汇编代码如何实现以上逻辑。

(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指令,把A寄存器的值赋值为i,这里是16
  2. 接下来D=M,即是D=M[16],把内存地址16的值赋值给D寄存器,相当于D=i。执行完该条指令之后,D寄存器的值为1
  3. @10把A寄存器的值赋值为10,10是循环条件了。
  4. D=D-A:此时D的值为1,A为10,ALU对D和A寄存器进行减法运算,执行完该条指令后D为-9,如下图
    执行指令之前:

    执行指令之后:
  5. @END:设置A寄存器的值为END(被编译器替换为18),为下一条的跳转指令做准备
  6. D;JGT:判断D寄存器值是否大于0,如果是则跳转到END的地址执行,相当于设置跳出循环的条件,这里由于D寄存器(-9)小于0,不执行跳转。
  7. @iD=M:和1、2条指令类似,把内存16的值赋值给D,执行完之后D值为1
  8. @sumM=D+M:取出sum(内存地址17的值,此时为0),与D相加(这里相当于执行sum的第1次加法了,加1),结果再放入内存地址17中,此时值改变为1,如下图
  9. @iM=M+1:取i的值,并+1,相当于i++
  10. @LOOP:设置A寄存器为LOOP(被编译器替换为4),为下一条跳转指令做准备
  11. 0;JMP:无条件跳转到LOOP位置,即PC counter为4的指令开始执行,准备开始下一轮循环,如下图

这样就完成了第1次的循环,这样的循环一共要进行10轮,最后完成sum10。

3.程序结束

10轮循环完成后,此时sum值(内存地址17)为55,i值(内存地址16)应为11。继续执行到第7条指令D=D-A,D为11,A为10,所以执行完毕后D=1。然后第9条指令跳转指令,因为D>0,发生跳转到END位置(即第18条指令),进入无限循环作为结束,无限循环在前文已经介绍过。

Leave a Reply

Your email address will not be published. Required fields are marked *