From Nand To Tetris 从与非门到俄罗斯方块

29、P1 W4 U4.8 HACK编程3(待深入)

2019-08-10  本文已影响0人  shazizm

视频:
如果本次课程对应的 Coursera 的视频打不开,可以点击下面链接
P1W4U4.8 - Hack Programming part 3

上节课,我们学会了 判断语句,变量使用,循环判断。
这节课,讲 指针 和 输入输出



一、指针 Pointer

如下图:

注:for 循环: 是编程里最常用的一种类似上节课最后的循环判断。
意思就是,设 i = 0,当i < n 时,执行{}里语句(此时i=0),最后 i + 1。然后在去判断 i + 1 后,是否 < n , 如果依然小于n,再去执行{}里语句(此时 i=i+1)。直到大于等于n时,循环退出。

这个循环判断不是重点(我假设你学过计算机简单的编程,如果不明白,搜索学习一下for循环吧),根据上节课学习的,琢磨一下可以写出来。

重点是 arr[i],编过程序的知道这是一个数组,也听说过数组的变量名(arr)代表的就是指针。

那么从最底层的语言,来看看这个指针(下图的arr)是什么工作的。

用指针实现一个任务

如下图:老师设计了一个任务来讲解指针的使用。
任务是将RAM[100]开始往后10个寄存器,都设置成-1。当然我们是通过用指针的方式实现。

首先

1.初始化Pointer指针

初始化分别实现3步:

// 伪代码 
arr = 100 
n = 10 
i = 0
// 为什么是这三个,去看for循环的完整语句,这3个是for循环里重要的三个变量

也就是如上图里
RAM[16] 写入 100,RAM[17] 写入 10,RAM[18] 写入 0。
(为什么是16、17、18,第六周做汇编器的时候就知道了,总之就是汇编器这么设计的,当然我们写代码时不用关心,这是汇编器自动分配的。)

对应HACK的汇编语言如下:

// 初始化Pointer指针

// 第1步:arr = 100
@100 //将A寄存器 放入100
D=A 
@arr //声明一个arr 变量,并将M寄存器对应此变量。
M=D //将D里的100 赋值给arr对应的M寄存器

// 第2步:n = 10
@10
D=A
@n
M=D

// 第3步:i = 0
@i
M=0

解释:把100存入RAM[arr]。中间用D寄存器传递了一下数值100,另外@arr是汇编器从RAM第16个开始找到的空闲寄存器的“门牌号”,图里就是16,也就是给 RAM[16] 写入100。

初始化 变量

2.写循环代码

这里老师也让停下来。最好自己把最终代码想出来,在和下图左边对比一下。

这里唯一值得注意的是。在之前我们只用 @value 对 A寄存器操作过。从没有直接 像下图里 红色 A = D + M 这样操作过。

(LOOP)
  // 判断 i 是否已循环 n 次
  @i
  D=M
  @n
  D=D-M
  @END
  D;JEQ //如果判读i-n=0,那么就跳转到END结束for循环。

  // 执行循环操作( 赋值 -1)
  @arr
  D=M
  @i
  A=D+M  //如果你看A寄存器被放左边这么使用,那通常就是在做指针操作。
  //上面4行就是在用指针操作来确定要被赋值的M
  M=-1 //执行赋值-1

  // 记录循环次数 ( i 加 1)  
  @i
  M=M+1

  @LOOP
  0;JMP //无条件跳转

(END) //程序结束
  @END
  0;JMP

注:在接下来 访问输入输出设备时,就会经常用到指针。

最终代码



二、输入输出 Input/Output

之前讲过 I/O设备的 存储映射,如果忘了,回顾U4.6

下图给出了 Hack小电脑实际的映射图,当然由老师提前架构设计好的。

图中可以看出,HACK的RAM里一开始是 16k个寄存器,应该是数据存储区。
然后紧跟着 8k个寄存器的 屏幕映射区,然后是1个寄存器的键盘映射。

之前U4.6也提到了,有两个“别名”,方便使用:

SCREEN = 16384
KBD = 24576

在汇编器 预处理时,会把“别名”分别转换成这两个数。

HACK小电脑RAM 的 功能区域划分

1.屏幕 Screen

1.1 屏幕定义:
256 行,每行由 32个寄存器 代表。
512 列,
左上角为 0行0列。

1.2 设置一个任务:
写一个程序,加载到CPU Emulator,程序操作屏幕映射存取区,使对应屏幕里显示一个矩形(矩形 长边 50像素,短边16像素)。

1.3 任务提示:
画这个矩形就是把 ,前50行 的 第一个 寄存器 全部 设为 -1(-1的二进制等于16个1)

U4.5 对屏幕操作 有详细说明

1.4 任务演示:

课程视频里有一段写好的代码,演示如何画出矩形,之后在详细说明代码。
从12分10 到 18分55

代码,加载后会在ROM窗口显示,代码不会完全一样,因为已经被预处理了。也就是那些帮助我们写代码的“别名”都变成数字了。这一步CPUEmulator,提前帮我们预处理了,另外也可以切换显示二进制的机器码。

如下图 红色箭头标注


之后在做汇编器Assembler时,都会一步一步实现现在CPUEmulator帮我们先做好的这些预处理 和 编译 的工作

然后点击 一步步执行,大概到 20行的时候,就会发现 屏幕区域 在第一行 最左侧画出一个 16 像素的横线。


一步步执行

点击下图两个红圈处,自动快速动画 执行全部代码,直到画完全部矩形。

自动快速执行

还有一种更快的办法,瞬间完成。如下图设置

无动画瞬间完成

那么看过演示实际的运行规律,分析一下代码如何写。

在写汇编程序时,老师建议的最佳实践,是先写出伪代码。如图下图左侧是伪代码。

1.5 代码分析:

(指针)初始化设置:
addr :SCREEN在RAM里的启示“门牌号”
n:要画的长方形多高(多少行),需要在CPUEmulator执行前,自行在RAM区域设置。

循环执行:
然后每行都把 第一个寄存器 设置成 -1 也就是二级制的16个1。
然后 addr 加 32,找到第二行第一个寄存器,同样 设置成 -1。
直到循环 n 次,最红画完长方形。

上图是伪代码。是为了自己在脑子里验证逻辑是否正确。然后人脑翻译成可运行的程序写出来。 一种辅助思考的手段,伪代码可读性更高。但实际上并不能在任何地方执行。就跟写文章,先打个草稿一个意思

最终 把伪代码 用人工脑力 转成 汇编语言,如下图:

红色高亮处 体现了 指针的作用。具体代码就不分析了。文章开始指针处已经讲过了

画长方形 的 汇编程序(左侧是指针初始化部分)(右侧是循环部分)

2.键盘 Keyboard

接下来看一下 输入设备 键盘,键盘映射 的 就是一个 单个寄存器。

RAM中 “门牌号”为 24576 的寄存器,有个“别名” KBD方便我们使用这个数。

当键盘没有按下时,这个键盘映射的寄存器,就是0

如果当按住一个键比如 k,那么对应 键盘映射的寄存器就会显示一个 对应这个键的 值,图中是75。

这个 键 和 值的对应表 在U4.5里也有给出。

那么某时刻 要想知道 键盘的输入,就是对这个KBD寄存器进行 读操作。

键盘讲的不多,下节课作业有一个相关的,再去实践代码吧。

这里我有两个疑问,一个是 物理上,键盘和映射的寄存器如何实现联动?一个是 键 和 值 的对应表,在哪里物理实现的?也许后面两周会说明。





最后题外话:

三、编译 compilation

HACK的汇编程序,是一种 低级语言,对应有高级语言,比如java。

高级语言 就像上面的 伪代码,可读性更高。

如果发明出一个高级语言 ,再做出 对应的 编译器 (compiler)。

这样理论上,我们就只用写 可读性更高的 高级语言(就像我们写伪代码那么容易),然后由 对应的编译器,来帮助我们翻译成 低级语言,例如汇编语言,当然,再然后才是 汇编器 (assembler),翻译成 机器语言 (0101xxx)

关于 编译器 和 对应Hack汇编语言 的 Hack高级语言,是在nand2tetris的Part2 软件部分讲授

高级语言 => 编译器 => 汇编语言

最后老师赞扬了一下 机器语言。

虽然低级。但却由很简单的东西组成。例如只有A和C两种指令。

(待深入)

这两种指令我随便想了想,很大部分由ALU决定。而ALU又是只用一个加法操作 和 与非门自带的逻辑运算组成(好像还有时序 => 时间),加法器也是一个逻辑单元,所有的又都可以用一种“与非门”组成,与非门再抽象是什么?逻辑?,目前抽象总结是 时间 和 逻辑 ,想想真奇妙。







the simple-minded people are impressed by sophisticated things. the sophisticated people are impressed by simple things.
上一篇下一篇

猜你喜欢

热点阅读