计算的要素-第六章小结

What’s in a name?That which we call a rose by any other name would smell as sweet

经历了前五个小节的学习,我们终于从硬件脱身了,请让我松一口气,毕竟熟悉各种各样的硬件细节可不是我的爱好…
好了,现在硬件对于我们来说就是一层抽象了,工程上的优势体现了出来,抽象屏蔽使得我们在写内存管理的时候不用去思考电信号:)
接下来我会总结一些关于第六章汇编器的知识,然后尽力的额外扩充一些知识,以后”有时间”去填坑

1 背景

1.1 机器语言

机器语言分为两类,一类称作符号型(symbolic)和二进制型(binary),当然硬件实际执行的指令是二进制型号,那么为什么需要符号型呢,答案很简单,因为让机器读懂指令很容易,但让程序员读懂二进制型的机器指令(特别还不是他自己写的)就很困难了,可以想象如果有成千上百条不同的指令,那么对直接读取二进制的程序员来说想必并不舒服。此时,针对硬件可执行的二进制机器语言的抽象产生了,说是抽象其实是增加了可读性,此时可以用Load代表10000000(假定指令长度8位)的前5位,这种等效替换也出现在了marco,各种设计模式中,本质是对可读性的考虑。

1.2 符号解析

利用符号表进行符号解析

1.3 汇编编译器

汇编编译器的输入是一串汇编命令,然后产生一串等价的二进制指令作为输出。生成的代码被加载到计算机的内存中然后被硬件执行。可见,汇编编译器主要是个文本处理程序,设计目的是用来提供翻译服务。编写汇编编译器的程序员必须有完整的汇编语法说明文档和相应的二进制代码。有了这样的约定(通常称为机器语言规范),就不难编写程序,让其对每个符号命令执行下面的任务(顺序无关)

  • 解析出符号命令内在的域
  • 对每个域,产生机器语言中相应的位域
  • 用内存单元的数字地址来替换所有的符号引用
  • 将二进制码汇编成完整的机器指令

2 指令

Hack的指令集比较IA-32的指令可以说是很简单了,hack的指令分为寻址指令(A-指令,Addressing Instruction)和计算指令(C-指令,Comput Instruction)

A指令的作用是将value读入A寄存器,比如你要访问内存地址为12的值,步骤如下

  1. @12 // 将12这个地址值读入A寄存器
  2. M // M的默认行为是将A寄存器中的值作为地址访问该地址存储的值

3 符号定义

符号分为3种,分别是:

3.1 预定义符号

预定义符号指代的是对应的符号在数据内存中的位置(RAM)

3.2 标签符号

标签符号中符号对应的是指令内存中的位置(ROM),且每个标签只能被定义一次,可以在程序的任何地方使用,包括在定义位置之前,这让我联想到了JS的预定义,估计也是先扫了一遍符号表,这里的符号表和编译器前端parser的符号表不是一个概念,虽然都是对符号的某种映射,但汇编器的符号表更加底层,它将符号映射到地址,而parser的符号表是一种上下文环境,这个在后面总结parser的时候会再提到。

标签符号例子:
(LOOP) 它的作用仅仅是更新符号表,其值为下一条指令的地址,这个在x86_64的反汇编也同样,可以看到标签语句是没有对应的二进制值的

3.3 变量符号

任何出现在汇编程序中的符号Xxx,不是预定义符号或标签符号,就将他作为变量符号处理,变量符号的地址默认从RAM[16]开始逐一分配,是的你没看错,逐一分配意味着在一个程序中已经废弃的变量也会永久持有内存,在第13章我会尝试用GC来解决部分堆内存问题,但这里的分配问题应该是指令集层面的考量所以除非修改分配策略否则无解。

变量例子:
@i 这里的i可以代表循环变量,默认变量从RAM地址16处开始分配

4 Q&A

  • Q:如何完成交叉汇编程序以及生成的代码如何读取.asm的文本输入以及如何维护其符号表?
    A:这些在这门课都没有涉及,感兴趣的同学可以学习Assemblers And Loaders.

  • Q:实际计算机中的汇编程序如何工作?我确信它不会使用高级语言将.asm文件转换为二进制文件……它是如何做到的呢?使用机器代码本身?
    A1:40年前,许多大型计算机程序仍然用汇编语言编写,因为每个字节都计算在内。例如,IBM 370型号145是IBM的第一台带有半导体内存的大型机,它的最大配置为512 千字节RAM。基本配置仅为112 KB。存储器周期速率约为3 MHz。 随着内存越来越大,速度越来越快,我们可以不再担心程序的大小,而是使用更高级别的语言来使编程更快,更不容易出错。 现代计算机的装配工几乎肯定是用高级语言编写的,很可能是’C’。例如,参见http://en.wikipedia.org/wiki/GNU_Assembler。
    A2:与任何其他科学领域一样,计算机科学领域由众多科学家和研究者建造,每个科学家和研究者都贡献了一小部分。所以,有很多英雄。 每种编程语言,无论是低级汇编语言还是高级现代语言,都是一种抽象。抽象是由语言设计师创造的,他们是一些有想法的人。
    为了实现抽象,即将语言从正式规范转换为实用工具,您必须能够将用该语言编写的程序翻译成我们已经知道要执行的另一种语言。根据我们希望翻译的语言的抽象级别,此翻译代理称为“编译器”,“VM翻译器”,“汇编程序”等。无论名称如何,它总是翻译一个文本文件(例如包含C代码)到另一个文本文件(例如包含汇编代码)。因为这个翻译器本质上是非常精美的文本处理程序,所以它可以用你选择的任何语言编写。
    尽管第一个汇编程序是用机器语言编写的,但你是正确的。但是一旦实现了第一步,生成的汇编程序就可以用来翻译符号程序,所以从那时起就不再需要用二进制代码编写代码了。剩下的就是历史……“智能是制造人造物品的能力,尤其是制造工具的工具。” (Henry Bergson,1859-1941)

  • Q:如果实际计算机中的汇编程序本身是用机器代码编写的,那么我是否可以使用hack汇编语言本身实现“项目6”?为什么要使用高级语言?
    A:使用Hack汇编语言编写汇编程序的限制因素是计算机模拟器没有I / O工具来访问主机上的文件。(但各种高级语言有I/O库)对于项目6,您需要处理存储在计算机上的.ASM文件,并编写将加载到模拟器中的.HACK文件。

  • Q:我理解架构以及ROM中的二进制值如何工作,但我不知道我们如何从具有机器代码(.hack)的文本文件转到ROM中的实际二进制值。 因为它是文本文件,所以每个“1”和“0”都是ASCII字符。 那么,我们如何从硬件上的ASCII转换为真正的二进制值?
    A:将汇编程序的输出视为另一种中间“语言”。还有另一种工具可以读取该语言并将其加载到可以执行该语言的计算机中。
    在像PC这样的通用计算机中,可执行文件由称为加载器的操作系统的一部分加载到存储器中。在基于ROM的计算机中,使用称为ROM编程器的硬件工具来编写ROM。
    Hack CPU Emulator上的“加载程序”命令是模拟ROM programmer。
    汇编器输出和可执行文件之间的软件链中通常还有另一个步骤。一个连接器 结合了多个汇编目标文件 和库 创建可执行文件。为了支持链接,目标文件包含额外的信息,告诉他们需要哪些外部函数以及外部函数可以调用的函数。