jack语言 计算机系统要素,计算机系统要素(3)— Machine Language

这是计算机组成元素:从零开始构建计算机的第三篇总结,对应着书本的第四章,该章节主要讲述了 Machine Language,即汇编语言。这里我们主要讨论汇编是如何通过 processor 和 registers 来处理 memory。

首先,我们先分别来看下 memory,processor 和 register 是什么。

Memory:我们把所有能存储数据与指令的设备都称为 memory。有点类似我们寄存器的结构,就是由一个一个的字组成,每个字有一个地址。一般该地址所指向的值我们用 Memory[address] 来表示。

Processor:一般我们称为 CPU。CPU 可以执行一些 operations,比如说,算术运算和逻辑运算,memory access,分支(类似 if-else 语句)。

Registers:这里的寄存器跟 memory 是类似的,只是这里特指 CPU 上的 Memory。因为 memory access 是一个相对耗时的操作,所以呢就在 CPU 上设置一些存储空间,这样可以满足 CPU 即想存储一些值,又能快速取得这些值的需求。我想 这里的 register 和 Memory 结构肯定有所不同,只是细的我还没有去研究,所以暂时还不知道区别在那里。

接下来我们讲讲汇编。汇编本质上是一串 01 的数字,只是规定了一些要求,让这串数字的可读性大大提高。比如说一串16位的指令,前几位代表操作符,中间几位代表操作数,等等这样类似的规则。这些根据不同的硬件和汇编语法规则,具体的要求都会有所差别。

有了规定,都是数字还是非常难以让人理解啊。所以呢有出现了 符号 助记符。举例来说,我又一个寄存器,它的地址是0,那我同时又可以设定 R0  也代表这个寄存器。那这个寄存器就有两种表示方法了,一个是地址0,另一个就是R0,哪一种更容易阅读是非常显而易见的。

计算机中,将这种汇编语言转化为最终计算机执行的二进制码的是汇编器,我们后面几章将会讨论如何实现一个汇编器。不同的硬件设备就有可能有不同的汇编语言的语法。这一章我们讨论的都是一些通用的汇编指令。

1. 算术和逻辑运算

就是加减法,与或非这几种操作。ADD R2, R1, R3 //  R2

AND R1, R2, R3

2. Memory Access

Memory Access 有两种方式:

(1)第一种我们已经看到了,就是上面 ADD R2,R1,R3 。进行加法运算的时候,我们获取了 R2 和 R1 中的值,并将结果存进了 R3。这是一种 memory access。这是操作于寄存器上的 memory access。

(2)第二种就是利用 load 和 store command。这种指令用于在寄存器和 memory 之间移动数据的。这种类型的指令会使用几种不同的寻址模式,即如何确定我们想要找的字在 memory 中的地址,我们刚说过一个 字对应一个地址。不同的计算机可能会提供不同的寻址模式,但是有三种是都支持的。

—  直接寻址:比如说我们已经知道了地址是 67,那直接 LOAD R1,67 // R1

—  立即寻址:这种寻址模式是用于加载常量,这里的常量不是数据常量,而是某个指令的地 址。比如说:LOADI R1,67       // R1

—  间接寻址:这种寻址模式常见于指针。即寄存器拥有的是指向某一块地址的指针。比如说:LOAD* R2, R1       //   R2

3. Flow of Control

程序通常是顺序执行的,一条接着一条执行的。但是有时候会程序会出现分支,出现分支的原因一般有如下三种:

(1)循环

(2)conditional execution(if-else)

(3)subroutine calling (调用函数)

为了满足这种分支,所有的汇编语言提供了跳转的命令(包括有条件和无条件的跳转)。这里举两个跳转命令的例子,详细的我们后面会介绍。JMP 是无条件跳转的一种,只要你确定了要跳转的地址,它就直接跳转了。JNG 是带条件判断的一种,只有条件为真才去跳转到指定的地址。

4. Hack Computer

刚我们说过,不同的计算机会有不同的汇编语言,我们这里引进 Hack Computer,它跟我们普通的计算机没什么不同,只是它简化了很多东西,同时这个理想化的计算机,可以为我们的练习提供方便,第五章我们要实现的就是这样一个计算机。

先来简单介绍下硬件组成:16位的机器,一个CPU,两块独立的存储空间,一块存指令,一块存数据,一个键盘和一个屏幕。

先来介绍下存储空间的分配。刚说了,一块指令的 memory 和另一块 data memory。这两块的 memory 都是 16 位宽,用 15 位地址表示。CPU只能执行在指令存储区的程序,但是指令存储区是一块只能读取的区域,所以程序只能用外部手段,而且只能 load 一次。

接下来说下 寄存器。Hack Programmer 有两类寄存器。一类是 D型寄存器,另一类是 A型寄存器。两者的寄存器用作不同的目的,D型寄存器只是用来存储数据的,而 A型寄存器既可以存数据也可以存储地址,这里的地址包括数据的地址或者是一条指令的地址。

说完这些,就可以讲讲 Hack Computer 上汇编语言的语法。我们刚讲过汇编语言都支持三种类型的指令,Hack Computer 上的当然也支持。

首先是逻辑与算术运算。没有用 ADD 这样的符号去代替,而是依旧 采用 + ,- 等数学公式上常用的符号(乘和除还没有这本书到现在为止都还没讲过,不过这章用的解决的一个方法是,累加代替乘)。

在接下来是 Memory Access。在 Hacker Computer 中,你可以直接获取到 D型寄存器的值和A型寄存器的值(如果 A 是作为数据存储的功能),比如说 D = xxxx 或者 A = xxxx。其次当 A 型寄存器是作为地址的是时候,要取得该地址指向的值,用的是 M。把两者结合起来举个例子,D = M + 1, 如果当寄存 A 的值是 516 时,这个例子其实就是 D = Memory[516] - 1(真是跟指针一摸一样)。我们刚说过 A型寄存器还有一个作用是作为指令的地址,这里主要是在跳转的时候要用到,Hacker Computer 的跳转指令首先要确定跳转到哪里,这个就是由 A型寄存器的值。所以一般来说,在我们做跳转之前,都要给 A型寄存器赋值,来告诉CPU跳转到哪,或者说下条要执行的指令的地址在哪里。

指令语法

现在给出指令的具体语法:

75b60633e59d?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

A-Instruction

A-instruction,又称 address instruction,是专门用来给 A 型寄存器设置值的(刚我们讲过 Hack Computer 的计算机设定了,用 15 位的来表示地址),第一位表示该指令是 A-instruction。

A-instruction:的目的有三个:

(1)这是唯一一个可以直接加载常量的指令。

(2)提供数据地址

(3)提供指令地址

接下来介绍最重要的一个指令

75b60633e59d?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

C-instruction

介绍下C-instruction 的用法和意思。可以看出二进制码对应着三个部分(不包括最前面的三面)。第一位 1 表示该指令是 C-instruction,后面两位是没有用到的,但是书上没说能否是0,所以我们还是用1去写。

接下来是 compution(comp) 部分。这部分的二进制码指定我们要计算谁,我们是去D型寄存器去那,还是去取 M 的值,要用什么操作,是加法还是减法等等。

75b60633e59d?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

comp 对照表

第二部分是 destination(dest) 部分。看这名字应该就知道这部分的指令是干什么的了,ALU 算出来的结果放哪去。

75b60633e59d?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

dest 对照表

第三部分就是 jump 部分。这里的要注意的是,这部分代码决定的是代码的跳转是有条件的还是无条件的,其次有条件的跳转是什么样的条件。要跳转去哪,是由 A型寄存器决定的。

75b60633e59d?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

jump 对照表

所以在做跳转指令的时候,一般都要利用下A-instruction,先加载下一条要执行的指令的位置是哪。

75b60633e59d?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

书上的例子

可以看到的是在写跳转指令的时候,最前面的 dest 部分是不写的,因为我们已经通过 A 型寄存器获取到了 destination。

有一点要注意,因为A既可以去做数据存储,也可以做地址存储,所以很容易分不清楚 A型寄存器到底是干什么的。为了防止混淆,在跟跳转有关的指令的时候,禁止写M。

符号

一个常量既可以用数字表示,也可以用符号表示。在 Hacker Computer 中有三种类型的 symbols。

(1)predefined symbols。先前计算机就定义好的,内置的符号。属于这种的有 :Virtual registers:R0 ~ R15,代表16个寄存器,和它们的地址Predefined pointers: SP,LCL,ARG,THIS,THAT 代表地址 0 ~4I/O pointers:SCREEN 和 KBD 表示 键盘和屏幕的地址

(2)Label symbols。当然我们也可以自定义。(XXX)  //  这就是语法了,这个你自己定义的 Symbol 就代表了某一个地址

需要注意的是,你不一定要先定义在使用,你可以先使用,在使用之后在去定义它,但是你只能定义一次,而不能重复使用。

(3)Variable symbols。任何你没定义过的 label 就是一个 Variable symbol,会被汇编器任意符一个地址,从16开始的(前面有 R1 - R15)。

输入输出的处理

我们之前提到过 Hacker Computer 有一个键盘和屏幕作为输入输出设备。Hacker Computer 与这两个设备的交互,其实是对这两个设备对应的 memory 作读写操作,这种处理方式叫做 memory map。我们来看下这两个设备的 memory。

(1)Screen:是一块黑白屏幕。分辨率是 256 * 512,每一行由 32 个 16位的字构成,所以是 一位 对应着 一个像素点。当某一位是 0 时,决定了该像素点显示白色,1 则显示黑色。在 Symbol 那一节,我们看到过系统有个预定义的符号,SCREEN。这个符号表示的就是屏幕最左上角的那个像素点对应的在内存中的地址,这个 symbol 值是 16384(0x4000)。例子如下:

75b60633e59d?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

书上例子

这里其实会把最左边的16个像素全部变为黑色。

(2)Keyboard:键盘也有一个符号表示,KBD。这个值是 24576(0x6000)。其实你算下,刚好就是就是在屏幕最后一个像素点的内存位置后面一位,256 * 32 + 16384 = 24576。然后 RAM[24576] 的值就是你敲的那个键的 ASCII 码。没有键按下,就是0了。

地址分配

每个存储在指令存储区的指令,都会分配一个地址。那汇编器是如何分配的呢?我们这里简单的就把指令所在的行数定义为该指令的地址。这里的行数是忽略空的行数的结果。我们写程序为了可读性把空行加上,汇编器不会管这个的。

总结

现在看下来,Hack Computer 上的汇编语言非常简单,总共就两类指令。但实际上一般的汇编语言肯定有更多的数据类型,更多的寄存器与指令格式。而且这两类指令其实非常接近高级语言了,也不是那么的难以读懂。但是很懂东西本质都是一样的,这样做只是利于我们更好的理解与学习。

文章有任何问题也欢迎指出,还有最重要的一点,所有的详细资料都在:

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值