What & How & Why

差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

两侧同时换到之前的修订记录前一修订版
后一修订版
前一修订版
cs:comp_n_arch:courses:fnti_i:week_4 [2025/05/09 08:22] – [Variables] codingharecs:comp_n_arch:courses:fnti_i:week_4 [2025/05/11 03:11] (当前版本) – [实例:keyboard] codinghare
行 175: 行 175:
   * 显示器通过对区域的内容进行持续刷新来更新输出的内容   * 显示器通过对区域的内容进行持续刷新来更新输出的内容
   * 输出的内容可以通过 coding 的方式来操控   * 输出的内容可以通过 coding 的方式来操控
-==Screnn memory map==+==Screen memory map==
   * 256 * 512, black / white   * 256 * 512, black / white
   * 通过一个 8k 的 16 bits 的 memory map 来作为其底层(256 * 512 == 8192 * 16),每个 bit 代表一个像素,1 / 0 代表 black / white   * 通过一个 8k 的 16 bits 的 memory map 来作为其底层(256 * 512 == 8192 * 16),每个 bit 代表一个像素,1 / 0 代表 black / white
行 354: 行 354:
     0;JMP     0;JMP
 </code> </code>
 +需要注意的是,变量被识别的前提是在程序中无法找到与其对应的 label 注释。这个是与 branch 的根本不同: 
 +  * Branch label 是 ''@name + (name)'' 的形式,分配位置与具体 label 所在位置有关 
 +  * 变量是 ''@name'' 的形式,分配位置从 ''RAM[16]'' 开始
 ===Iteration=== ===Iteration===
 +在 Hack 中写循环一般要确定两个入点:
 +  * STOP:入口放置于整个程序的末尾,跳转点放置于循环条件的后方
 +  * LOOP:入口放置于循环体的起始点,跳转点放置于循环体的末尾
 +下面是一个循环累加的例子:
 +<code bash>
 +# compute RAM[1] + RAM[2] ... + RAM[n]
 +# 初始化
 +@R0 
 +D=M
 +@n
 +M=D # 将 R0 的值用于 n 的初始化
 +@i
 +M = 1 # i = 1
 +@sum
 +M = 0 # sum = 0
 +
 +(LOOP)
 +    @i 
 +    D = M # 获取当前 i 值
 +    @n
 +    D = D - M # 获取 n 值,计算 i-n 的结果
 +
 +    @STOP
 +    D;JGT # 如果 i-n > 0,则循环超过上限,停止
 +
 +    @sum 
 +    D = M 
 +    @i 
 +    D = D+M # 否则,sum += i
 +    @sum # 覆写 sum
 +    M = D
 +
 +    @i 
 +    M = M + 1 # ++i
 +
 +    @LOOP # 再次循环
 +    0;JMP
 +
 +(STOP)
 +    @sum 
 +    D = M
 +    @R1 # 将 sum 值存储到 R1 中
 +    M = D
 +
 +(END)
 +    @END
 +    0;JMP
 +</code>
 +<WRAP center round box 100%>
 +  * 推荐先写伪代码,再翻译为 HACK program
 +  * 测试推荐使用 track table:跟踪每个迭代中每个值的变化,看是否符合预期
 +</WRAP>
 +===Pointers===
 +array 的形式在机器语言中是不存在的。取而代之的是,array 是以起始地址 + 偏移量的形式来进行访问的。我们将这种通过地址进行访问的形式称为指针(//Pointer//)。
 +实现上,我们会通过更新 A 来得到其对应的 M,再对 M进行更新操作。下面是一个例子:
 +<code cpp>
 +// 需要模拟的代码
 +n = 10;
 +for (i = 0; i < n; ++i)
 +{
 +    arr[i] = -1;
 +}
 +</code>
 +<code bash>
 +# 初始化
 +# 假设 array arr 的起始地址为 100
 +
 +# 初始化 arr
 +@100
 +D = A
 +@arr
 +M = D # 将 arr 的初始值设为 100
 +
 +# 初始化 n
 +@10
 +D = A 
 +@n
 +M = D # 将 n 的初始值设为 10
 +
 +# 初始化 i
 +@i
 +M = 0 # 将 i 的初始值设为 0
 +
 +# 开始进入循环
 +(LOOP)
 +    # 如果 i == n,循环结束
 +    @i
 +    D = M
 +    @n
 +    D = D - M # D 是判断条件,存储 i - n 的结果
 +    
 +    @END
 +    D;JEQ # i == n 则跳转至 END
 +    
 +    # 如果不相等,则循环继续
 +    
 +    # 获取 arr 的起始地址
 +    @arr
 +    D=M
 +    @i
 +    A = D+M # 计算偏移量,也就是指针的地址 arr + i
 +    # 更新对应的地址
 +    # 注意此时的 M 已经变化了,不再是 i 对应的寄存器,而是 A 对应的寄存器
 +    # A 更新后访问 M 的 side effect: M 会与 RAM[A] 关联
 +    M = -1 # RAM[arr+i] = -1
 +    
 +    @i
 +    M = M + 1 # ++i
 +    
 +    @LOOP # 循环结束
 +    0;JMP
 +
 +(END)
 +    @END
 +    0;JMP
 +</code>
 +===Input & Output===
 +这部分主要实现的是:
 +  * Screen
 +  * Keyboard
 +{{ :cs:comp_n_arch:courses:fnti_i:hack_memory_distribution.svg?150 |}}
 +==实例:drawing an rectangle==
 +  * 题目的要求是:画一个自定义 row(''n'') 的,宽度(列)为 16 bits 的矩形。
 +首先从高级语言考虑一下这个代码的循环:
 +<code cpp>
 +for (i=0; i < n; ++i)
 +{
 +    // print first black pixels at he beginning of row i
 +}
 +</code>
 +接下来以更机器语言的方式思考该伪代码:
 +<code bash>
 +# init
 +addr = SCREEN # 写入的首地址是 SCREEN 对应的内存起始点
 +n = RAM[0] # 从 R0 中获取 n 的初始值
 +i = 0 
 +
 +# Looping
 +LOOP:
 +    if i > n goto END # 循环结束条件是 i > n
 +    # draw pixels
 +    RAM[addr] = -1 # -1的二进制表示是 1111111111111111
 +    # update status
 +    addr += 32 # 换行,由于屏幕宽度是 512,每一行需要 32 个寄存器才可以完全表示,因此换行是直接去第 33 个寄存器
 +    i += 1 # 更新循环条件
 +    goto LOOP
 +    
 +END:
 +    goto END
 +</code>
 +实现如下:
 +<code bash>
 +@SCREEN 
 +D = A
 +@addr # 使用 SCREEN 地址初始化 addr, 16384 号寄存器
 +M = D
 +@R0
 +D = M
 +@n
 +M = D # 使用 R0 内容初始化 n
 +@i
 +M = 0 # 初始化 i 为 0
 +
 +(LOOP)
 +    @i
 +    D = M
 +    @n
 +    D = D - M
 +    @END
 +    D;JGT # 如果 i > n, 结束循环
 +
 +    @addr
 +    A = M 
 +    M = -1 # RAM[addr] = 1111 1111 1111 1111
 +
 +    # 更新循环状态
 +    @i 
 +    M = M + 1
 +
 +    @32
 +    D = A # 注意,所有的常量都需要用 A 类寄存器获取
 +    @addr 
 +    M = M + D
 +    
 +    @LOOP
 +    0;JMP
 +
 +(END)
 +    @END
 +    0;JMP
 +</code>
 +==keyboard==
 +  - 读取 RAM[24576] 内的内容
 +  - 如果内容为 ''0'' ,那么没有键被按下
 +  - 否则,搜寻对应值的 key 值