视频:
如果本次课程对应的 Coursera 的视频打不开,可以点击下面链接
P1W4U4.10 - Perspectives
-
Hack的机器语言,和普通我们使用的电脑有什么区别呢?
Hack是专为这个教学用的,它的机器语言的指令非常精简,但能清晰传达计算机核心概念。普通电脑为了提高效率会有很多提高计算性能的设计,比如会把 乘法 和 除法 从硬件层面 设计出对应的指令。Hack也可以实现乘法和除法,这要在之后的 nand2tetris Part2 软件部分涉及。 -
取地址,然后存数。其它电脑的机器语言也是这样操作吗?
一般都会这样。普通电脑不像Hack指令,只有16位。有的位数多的,是可以在一个指令里完成上面两步操作。
3.Hack的机器语言跟其他电脑的机器语言 语法上区别大吗?
还好,自己看吧
例如
Hack:D = D+M
其它:ADD D ,M
又例如
Hack:D = M
其它:LOAD D,M
- 真的有必要写这些机器语言吗?
一般还真不写这种底层的机器语言,之后有了编译器就都写高级语言了,然后用编译器和汇编器,来翻译成机器语言。
作业:
Mult.asm
Fill.asm
写作业前。我基本忘了这周都讲啥了。回顾一下
1、回顾工具:
CPU Emulator CPU 仿真器 。跟之前的硬件模拟器放在一个目录里nand2tetris/tools/CPUEmulator.xxx,Mac、Linux用户找到CPUEmulator.sh运行。Windows用户双击CPUEmulator.bin
2、回顾U4.3(A指令和C指令)
3、回顾U4.6(D、A、M、寄存器操作)
代码最后要有无条件循环,
@END
0;JMP
4、回顾U4.7
虚拟寄存器:R0-R15、SCREEN、KBD
符号LABEL:()定义,@使用
5、回顾U4.8
指针的初始化,和循环用法。
6、工作流:
工作流
7、作业提示:
能用 “别名” 和 变量 表示存储器的“门牌号”。就千万不要用具体的地址数字。
别名 和 变量,起名字的时候要能让人看懂啥意思。
变量用小写。
别名用大写。
用缩进?使排版美观。
写汇编前,可以打打草稿(伪代码)。
一、Mult.asm
写一个程序 使 R0 乘 R1 的结果写入 R2
提示:
循环 和 加法。
R0*R1=R2
循环R1次相加
既然需要 循环,可参考回顾U4.8
1、初始化部分:
(R0=3,R1=4,R2=0)
// 假设做一个 R0*R1 => 3*4
// R0 = 3
@3
D=A
@R0
M=D //测试发现R0、R1、寄存器没法直接赋值(0和1除外)。
// R1 = 4
@4
D=A
@R1
M=D
// R2 = 0
@R2
M=0
// 记录当前循环次数
@i
M=0
2、循环加部分:
(LOOP)
// 判断 i 是否已循环 R1 次
@i
D=M
@R1
D=D-M
@END
D;JEQ //如果判读i-R1=0,那么就跳转到END结束for循环。
// 执行循环操作( R2 + R0)
@R0
D=M
@R2
M=D+M
// 记录循环次数 ( i 加 1)
@i
M=M+1
@LOOP
0;JMP //无条件跳转
(END) //程序结束
@END
0;JMP
上面代码,可以点击单步运行,一步步查看计算结果。
也可以把 Mult.asm 修改成可测试代码(把R0和R1的初始化去掉):
3、最终可测试代码:
// This file is part of www.nand2tetris.org
// and the book "The Elements of Computing Systems"
// by Nisan and Schocken, MIT Press.
// File name: projects/04/Mult.asm
// Multiplies R0 and R1 and stores the result in R2.
// (R0, R1, R2 refer to RAM[0], RAM[1], and RAM[2], respectively.)
// Put your code here.
//初始化
// R0 赋值
//@3
//D=A
//@R0
//M=D
// R1 赋值
//@4
//D=A
//@R1
//M=D
// R0 和 R1 不用赋值,跑 Mult.tst 测试代码的时候会赋值
@R2
M=0
@i
M=0
//循环加(乘法)
(LOOP)
// 判断 i 是否已循环 R1 次
@i
D=M
@R1
D=D-M
@END
D;JEQ //如果判读i-R1=0,那么就跳转到END结束for循环。
// 执行循环操作( R2 + R0)
@R0
D=M
@R2
M=D+M
// 记录循环次数 ( i 加 1)
@i
M=M+1
@LOOP
0;JMP //无条件跳转
(END) //程序结束
@END
0;JMP
测试:
Mult.tst如果报错说找不到“Mult.hack”。就改成“Mult.asm”。
// This file is part of www.nand2tetris.org
// and the book "The Elements of Computing Systems"
// by Nisan and Schocken, MIT Press.
// File name: projects/04/mult/Mult.tst
load Mult.hack, //修改成 load Mult.asm(要被测试的文件名)
测试成功。
二、Fill.asm
写一个程序 当键盘上,任意键按下时,就黑屏。抬起键,就白屏。
提示:写一个循环,一直监听键盘,然后对应做出反应。黑屏,就是把所有屏幕映射区的寄存器,全部写-1。
1、监听键盘:
写一个无限循环,读KBD寄存器,判断如果不是0,就白屏。(貌似每次都循环写白屏,比较浪费计算资源。可以设置一个标志寄存器。来记录当前屏幕状态)
//循环
(LOOP)
...读KBD寄存器
@LOOP
0;JMP
//读KBD寄存器
@KBD
D=M
@BLACK
D;JNE //不等于0,跳转到@BLACK
@WHITE
0;JMP //等于0,跳转到@WHITE
//设 FLAG = 0 表示 白屏。FLAG = 1 表示 黑屏。
@FLAG
M=0
// 初始化屏幕指针
// 第1步:arr = @SCREEN
@SCREEN //将SCREEN的寄存器地址 存入D
D=A
@arr //声明一个arr 变量,并将M寄存器对应此变量。
M=D //将D里的SCREEN地址赋值给arr对应的M寄存器
// 第2步:屏幕:256 行,每行由 32个寄存器 代表。n = 256*32 = 8192
@8192
D=A
@n
M=D
// 第3步:i = 0
@i
M=0
2、设置屏幕:
设置黑,就是循环就是把SCREEN的所有寄存器,的8bit位都赋值1。也就是给 M = -1。
设置白,反之,M = 0。
(BLACK)
@FLAG
D=M-1
@LOOP
D;JEQ //如果现在状态是黑,就不再设置屏幕了,跳回LOOP处继续扫描键盘
//否则,修改FLAG 为1(黑屏)
@FLAG
M = 1
//把i回0
@i
M=0
//然后设置 黑屏
(SETBLACKLOOP)
// 判断 i 是否已循环 n 次
@i
D=M
@n
D=D-M
@LOOP //
D;JEQ //如果判读i-n=0,就代表设置完屏幕颜色了。跳回继续扫描键盘
// 执行循环操作( 赋值 -1)
@arr
D=M
@i
A=D+M //如果你看A寄存器被放左边这么使用,那通常就是在做指针操作。
//上面4行就是在用指针操作来确定要被赋值的M
M=-1 //执行赋值-1,设置黑屏
// 记录循环次数 ( i 加 1)
@i
M=M+1
@SETBLACKLOOP
0;JMP //无条件跳转
(WHITE)
@FLAG
D=M
@LOOP
D:JEQ //如果现在状态是白,就不再设置屏幕了,跳回LOOP处继续扫描键盘
//否则,修改FLAG 为0(白屏)
@FLAG
M = 0
//把i回0
@i
M=0
//然后设置 白屏
(SETWHITELOOP)
// 判断 i 是否已循环 n 次
@i
D=M
@n
D=D-M
@LOOP //
D;JEQ //如果判读i-n=0,就代表设置完屏幕颜色了。跳回继续扫描键盘
// 执行循环操作( 赋值 -1)
@arr
D=M
@i
A=D+M //如果你看A寄存器被放左边这么使用,那通常就是在做指针操作。
//上面4行就是在用指针操作来确定要被赋值的M
M=0 //执行赋值0 ,设白屏
// 记录循环次数 ( i 加 1)
@i
M=M+1
@SETWHITELOOP
0;JMP //无条件跳转
3、最终完整代码:
// This file is part of www.nand2tetris.org
// and the book "The Elements of Computing Systems"
// by Nisan and Schocken, MIT Press.
// File name: projects/04/Fill.asm
// Runs an infinite loop that listens to the keyboard input.
// When a key is pressed (any key), the program blackens the screen,
// i.e. writes "black" in every pixel;
// the screen should remain fully black as long as the key is pressed.
// When no key is pressed, the program clears the screen, i.e. writes
// "white" in every pixel;
// the screen should remain fully clear as long as no key is pressed.
// Put your code here.
//初始化FLAG
//设 FLAG = 0 表示 白屏。FLAG = 1 表示 黑屏。
@FLAG
M=0
//初始化 用来循环刷新屏幕寄存器的循环指针
// 第1步:arr = @SCREEN
@SCREEN //将SCREEN的寄存器地址 存入D
D=A
@arr //声明一个arr 变量,并将M寄存器对应此变量。
M=D //将D里的SCREEN地址赋值给arr对应的M寄存器
// 第2步:屏幕:256 行,每行由 32个寄存器 代表。n = 256*32 = 8192
@8192
D=A
@n
M=D
// 第3步:i = 0
@i
M=0
// 开始循环扫描键盘
(LOOP)
//读KBD寄存器
@KBD
D=M
@BLACK
D;JNE //不等于0,跳转到@BLACK
@WHITE
D;JEQ //等于0,跳转到@WHITE
@LOOP
0;JMP
// 循环设置黑屏 和 白屏
(BLACK)
@FLAG
D=M-1
@LOOP
D;JEQ //如果现在状态是黑,就不再设置屏幕了,跳回LOOP处继续扫描键盘
//否则,修改FLAG 为1(黑屏)
@FLAG
M=1
//把i回0
@i
M=0
//然后设置 黑屏
(SETBLACKLOOP)
// 判断 i 是否已循环 n 次
@i
D=M
@n
D=D-M
@LOOP //
D;JEQ //如果判读i-n=0,就代表设置完屏幕颜色了。跳回继续扫描键盘
// 执行循环操作( 赋值 -1)
@arr
D=M
@i
A=D+M //如果你看A寄存器被放左边这么使用,那通常就是在做指针操作。
//上面4行就是在用指针操作来确定要被赋值的M
M=-1 //执行赋值-1,设置黑屏
// 记录循环次数 ( i 加 1)
@i
M=M+1
@SETBLACKLOOP
0;JMP //无条件跳转
(WHITE)
@FLAG
D=M
@LOOP
D;JEQ //如果现在状态是白,就不再设置屏幕了,跳回LOOP处继续扫描键盘
//否则,修改FLAG 为0(白屏)
@FLAG
M=0
//把i回0
@i
M=0
//然后设置 白屏
(SETWHITELOOP)
// 判断 i 是否已循环 n 次
@i
D=M
@n
D=D-M
@LOOP //
D;JEQ //如果判读i-n=0,就代表设置完屏幕颜色了。跳回继续扫描键盘
// 执行循环操作( 赋值 -1)
@arr
D=M
@i
A=D+M //如果你看A寄存器被放左边这么使用,那通常就是在做指针操作。
//上面4行就是在用指针操作来确定要被赋值的M
M=0 //执行赋值0 ,设白屏
// 记录循环次数 ( i 加 1)
@i
M=M+1
@SETWHITELOOP
0;JMP //无条件跳转
3、测试
测试成功
网友评论