What & How & Why

差别

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

到此差别页面的链接

两侧同时换到之前的修订记录前一修订版
后一修订版
前一修订版
cs:comp_n_arch:courses:fnti_i:week_4 [2025/05/09 10:28] – [Screnn memory map] codingharecs:comp_n_arch:courses:fnti_i:week_4 [2025/05/11 03:11] (当前版本) – [实例:keyboard] codinghare
行 364: 行 364:
 <code bash> <code bash>
 # compute RAM[1] + RAM[2] ... + RAM[n] # 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
  
-@R0 # n+(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 > 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 D = M
 +@n
 +M = D # 使用 R0 内容初始化 n
 @i @i
-M = 1 +M = 0 # 初始化 i 为 0 
-@sum + 
-M=0+(LOOP) 
 +    @i 
 +    D = M 
 +    @n 
 +    D = D - M 
 +    @END 
 +    D;JGT # 如果 i > n, 结束循环 
 + 
 +    @addr 
 +    A = M  
 +    M = -# RAM[addr] = 1111 1111 1111 1111 
 + 
 +    # 更新循环状态 
 +    @ 
 +    M = M + 1 
 + 
 +    @32 
 +    D = A # 注意,所有的常量都需要用 A 类寄存器获取 
 +    @addr  
 +    M = M + D 
 +     
 +    @LOOP 
 +    0;JMP
  
-@LOOP+(END) 
 +    @END 
 +    0;JMP
 </code> </code>
 +==keyboard==
 +  - 读取 RAM[24576] 内的内容
 +  - 如果内容为 ''0'' ,那么没有键被按下
 +  - 否则,搜寻对应值的 key 值