Featured image of post Nand2Tetris Project(4)

Nand2Tetris Project(4)

使用机器语言编程

Project 4: Machine Language

在本次Project里面,我们将从代码填空变为整段代码编程以实现一个乘法器和一个画图程序。我们将初步接触Hack language这种low-level编程语言,并且着重实现一些赋值、循环等等在high-level编程中很简单的功能。

Mult

使用循环累加,逻辑很简单,但是如果要转化为机器语言,初步想法需要以下几步:

  1. 给一个变量赋值为sum=0,用于累加记录
  2. 建立一个Loop:提前存好两个变量,num=RAM[0],time=RAM[1]
  • 用一个变量i来检测是否已经超过预定的循环次数
  • 累加到sum里面
  1. 把sum赋给RAM[2]
  2. 无限循环终止程序

但是上面的代码有一个问题,就是我们需要处理的不仅仅是正数乘法,还有负数乘法。这就需要我们同时判断两个乘数中负数的个数从而判断最终的结果的符号。

本题的难点不在思路上,而是在对于Hack机器语言的熟悉上,在第一遍实现的过程中,我遇到了以下的问题:

  • 注释要单开一行写
  • 要写课本表格里面有的表达式,只要是课本里没有的都无法通过编译
  • 不要自行多加分号,同样无法通过编译(C++受害者)
  • 把D视作一个临时储存器,把中间变量都存到这个里面

下面贴出代码,仅供参考。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
//record the number of negative numbers
@minusCount 
M=0

// record the total num
@sum 
M=0

// count of iterations
@count 
M=0

@R2
M=0
// D=RAM[0]
(MAKESIGN1)
@0
D=M 

@MAKEABS1
D;JGE 

//if ram[0] is negative,then minusCount+1,and we set the number to its abs
@minusCount
M=M+1

@MAKEABS2
0;JMP

(MAKEABS1)
@0
D=M
@a
M=D

@MAKESIGN2
0;JMP

(MAKEABS2)
@0
D=M
@a
M=-D

(MAKESIGN2)
@1
D=M

@MAKEABS3
D;JGE

@minusCount
M=M+1

@MAKEABS4
0;JMP

(MAKEABS3)
@1
D=M
@b
M=D

@ODDEVEN
0;JMP

(MAKEABS4)
@1
D=M
@b
M=-D


//if the minusCount is odd,the final result need to be negative
(ODDEVEN)
@minusCount
M=M-1

// we set isNegative to 0 at first
@isNegative
M=0

// if minusCount=0,,which means it is odd before minusing 1,jump to odd
@minusCount
D=M
@ODD
D;JEQ

//else, jump to even
@EVEN
0;JMP

(ODD)
@isNegative
M=1

@LOOP
0;JMP

(EVEN)
@isNegative
M=0

(LOOP)
    //we use a as base and b as iterations
    // D = count
    @count
    D=M        

    // D = b - count
    @b
    D=M-D     

    // IF b=count,then finish the loop
    @CHANGESYMBOL
    D;JEQ      

    @count
    M=M+1

    @a
    D=M
    @sum
    D=M+D
    M=D

    @LOOP
    0;JMP

(CHANGESYMBOL)
    @isNegative
    D=M
    @GIVE2
    D;JEQ

    @sum
    M=-M

(GIVE2)
    @sum
    D=M
    @2
    M=D

(END)
    @END
    0;JMP

fill

尝试实现设备的I/O交互。

处理输入

Whenever a key is pressed on the physical keyboard, its 16-bit ASCII code appears inRAM[24576]. When no key is pressed, the code 0 appears in this location. 因此我们应该使用KBD读取键盘map的地址,然后对它进行操作。显然这里我们只需要判断它是不是0就可以了。

处理输出

  • 读取SCREEN地址
  • 设置一个待绘制的指针
  • 如果键盘被按下,那么所有像素应该是绘制为黑色(-1),反之则绘制为白色(1),这里是一个关键点,我们需要保证先按下后松起之后屏幕重新变成白色。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// set 8192 as the total number of ram spaces to be drawn
// here we need to know that if we want to set a specific number to a variable
// rather than the specific number at that address
// we need to do it like follows
@8192
D=A
@total
M=D


(CHECK)
    //so do the specific number of address of screen
    @SCREEN
    D=A
    @addr
    M=D

    @KBD
    D=M

    @WHITE
    D;JEQ

    @BLACK
    0;JMP


    (WHITE)
        @i
        M=0

        (LOOP1)
            @total
            D=M
            @i
            D=D-M
            @DRAW0
            D;JGT
            @CHECK
            0;JMP

            //here is an example of set a specific address
            (DRAW0)
            @addr
            A=M
            M=0

            @addr
            M=M+1

            @i
            M=M+1
            @LOOP1
            0;JMP

    @CHECK
    0;JMP
        

    (BLACK)
        @i
        M=0

        (LOOP2)
            @total
            D=M
            @i
            D=D-M
            @DRAW1
            D;JGT
            @CHECK
            0;JMP

            (DRAW1)
            @addr
            A=M
            M=-1

            @addr
            M=M+1

            @i
            M=M+1
            @LOOP2
            0;JMP

    @CHECK
    0;JMP

参考代码如上。要记得在CPU simulator里面把对应的模式改成No animation。

这部分内容写起来还是比较恶心的,因为它反直觉,需要我们把操作掰开揉碎了喂给电脑。但是通过查阅课程代码以及“面向报错和AI编程”,还是可以完成的。

我之所以选择Nand2Tetris,是因为我不满足于平日写的一些high-level编程代码,而希望可以更深入地了解计算机运行的底层机制。然而通过本节课繁琐的机器语言的代码实战,让我意识到了high-level代码的意义,真可谓“真让你学了你又不乐意了”。这也正体现了计算机知识体系本身强烈的层次性。

接下来我们将进一步接触计算机架构和编译器,继续加油吧。

comments powered by Disqus