本 Wiki 开启了 HTTPS。但由于同 IP 的 Blog 也开启了 HTTPS,因此本站必须要支持 SNI 的浏览器才能浏览。为了兼容一部分浏览器,本站保留了 HTTP 作为兼容。如果您的浏览器支持 SNI,请尽量通过 HTTPS 访问本站,谢谢!
这里会显示出您选择的修订版和当前版本之间的差别。
两侧同时换到之前的修订记录前一修订版后一修订版 | 前一修订版 | ||
cs:comp_n_arch:courses:fnti_i:week_4 [2025/05/09 08:26] – [Variables] codinghare | cs:comp_n_arch:courses:fnti_i:week_4 [2025/05/11 03:11] (当前版本) – [实例:keyboard] codinghare | ||
---|---|---|---|
行 175: | 行 175: | ||
* 显示器通过对区域的内容进行持续刷新来更新输出的内容 | * 显示器通过对区域的内容进行持续刷新来更新输出的内容 | ||
* 输出的内容可以通过 coding 的方式来操控 | * 输出的内容可以通过 coding 的方式来操控 | ||
- | ==Screnn | + | ==Screen |
* 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 | ||
行 358: | 行 358: | ||
* 变量是 '' | * 变量是 '' | ||
===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 | ||
+ | </ | ||
+ | <WRAP center round box 100%> | ||
+ | * 推荐先写伪代码,再翻译为 HACK program | ||
+ | * 测试推荐使用 track table:跟踪每个迭代中每个值的变化,看是否符合预期 | ||
+ | </ | ||
+ | ===Pointers=== | ||
+ | array 的形式在机器语言中是不存在的。取而代之的是,array 是以起始地址 + 偏移量的形式来进行访问的。我们将这种通过地址进行访问的形式称为指针(// | ||
+ | 实现上,我们会通过更新 A 来得到其对应的 M,再对 M进行更新操作。下面是一个例子: | ||
+ | <code cpp> | ||
+ | // 需要模拟的代码 | ||
+ | n = 10; | ||
+ | for (i = 0; i < n; ++i) | ||
+ | { | ||
+ | arr[i] = -1; | ||
+ | } | ||
+ | </ | ||
+ | <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 | ||
+ | </ | ||
+ | ===Input & Output=== | ||
+ | 这部分主要实现的是: | ||
+ | * Screen | ||
+ | * Keyboard | ||
+ | {{ : | ||
+ | ==实例:drawing an rectangle== | ||
+ | * 题目的要求是:画一个自定义 row('' | ||
+ | 首先从高级语言考虑一下这个代码的循环: | ||
+ | <code cpp> | ||
+ | for (i=0; i < n; ++i) | ||
+ | { | ||
+ | // print first black pixels at he beginning of row i | ||
+ | } | ||
+ | </ | ||
+ | 接下来以更机器语言的方式思考该伪代码: | ||
+ | <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 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 | ||
+ | </ | ||
+ | ==keyboard== | ||
+ | - 读取 RAM[24576] 内的内容 | ||
+ | - 如果内容为 '' | ||
+ | - 否则,搜寻对应值的 key 值 |