购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.1 汇编系统基本原理

2.1.1 机器语言与汇编语言

每一种特定型号的计算机系统都有自己特定的机器指令集合,集合中每条指令都代表一项具体的操作,例如从内存取数据到寄存器。这个机器指令集合就是机器语言,由机器语言编写的程序就称为机器程序。机器指令本质上是一个特定长度的二进制串,特定的位表示操作码,而另外的位表示操作数。

由于机器程序都是由二进制的机器指令组成,在编写机器程序的时候,不仅要记住特定操作码的二进制表示,还需要记下各个数据的地址的二进制表示。这是十分不方便的,而且容易出错,程序也很难读懂。于是人们就开始使用助记符(汇编指令)代表机器指令的操作码,并且使用伪指令(即不对应任何机器指令,只用于助记)和标号帮助确定数据或代码的位置,这就是汇编语言了。由于汇编指令和机器指令是相对应的,所以每种特定型号的计算机系统都有自己的汇编指令集合。

由汇编指令编写的程序就是汇编源程序,计算机是不能直接执行汇编源程序的,而必须由一个特殊程序根据伪指令的控制把汇编源程序转化为对应的机器语言程序。这个特殊的程序就是汇编程序。

2.1.2 汇编程序

如前面所述,汇编程序的基本工作包括:

(1)将每一条可执行的汇编指令转换成对应的机器指令;

(2)处理源程序中出现的伪指令。

这一工作通常需要对汇编程序进行超过一次的扫描。

前面的分析已经指出,形成操作数地址的各个部分有可能出现符号,而符号会是稍后语句的标号:

为了计算各汇编语句中标号的地址,我们在汇编程序中设立单元地址计数器LC,其初值一般为0。以后每处理完一条可执行的汇编语句和与存储分配有关的伪指令(如定义常数语句、定义存储语句),LC的值就增加相应的长度,这样LC的值始终是下一个存储单元的相对地址。当处理一条汇编语句标号时,就将LC当时的值定义为标号值。由于符号的使用可能出现在符号定义之前,整个汇编程序工作要通过对源程序进行二次扫描才能完成。

第一次扫描主要工作是定义符号的值。除了设置单元计数器LC外,我们设立机器指令表MOT1。由于本次扫描并不具体生成机器指令,MOT1的每一元素只需两个域:机器指令记忆码和机器指令长度。在扫描过程中,我们将符号及其值记录在符号表ST中。此外,在第一次扫描中,还需要对与定义符号值有关的伪指令进行处理。为了叙述方便,不妨设立伪指令表POT1,POT1表的每一个元素只有两个域:伪指令记忆码和相应处理子程序入口。下面是对第一次扫描的描述:

(1)单元计数器LC置初值0。

(2)打开源程序文件。

(3)反复执行以下操作。

①从源程序文件读下一条语句。

②如果该语句有标号,则将标号和LC当时的值送到符号表ST。

③根据语句操作码,执行:

● 如果是可执行汇编语句,K是查MOT1表所得机器指令的长度,则LC=LC+K。

● 如果是伪指令记忆码,则调用POT1表相应元素所规定的子程序。

● 如果是非法记忆码,则调用出错子程序。

直至语句操作码是END为止。

(4)关闭源程序文件。

第二次扫描的目的是产生目标程序。除了前一次扫描所生成的符号表ST外,我们需要建立机器指令表MOT2,该元素包含下面区域:机器指令记忆码、机器指令的二进制操作码(binary_code)、格式指示(type)和长度(length)。我们还设立第二次扫描的伪指令表POT2,它的每一元素仍是两个区域:伪指令记忆码和相应处理子程序入口。所不同的是,在第二次扫描中,伪指令有着完全不同的处理。

在第二次扫描中,可执行汇编语句应被翻译成对应的二进制代码机器指令。这一工作涉及两个方面:把机器指令记忆码转换成二进制机器指令操作码,以及求出操作数区各操作数的值(用二进制表示)。在此基础上,可以装配出二进制代码的机器指令。对于第一部分工作,只要根据机器指令记忆码查机器指令表MOT2,就可以获得相应二进制表示的机器指令操作码。从求值的角度来说,第二部分工作并不复杂。由于形成内存操作数地址的各个部分都以表达式的形式出现,我们统一定义一个过程eval-expr(index,value)。调用时,只要将表达式在汇编语句缓冲区S开始位置通过index传递给此过程,该过程就通过value返回此表达式的值。例如,虚拟计算机COMET的机器指令可归属与“X”型指令,其汇编语句为:

我们可以写出下面处理“X”型指令的程序段(假定index已指向操作数在缓冲区S的首址):

其他类型指令的处理操作数的程序段都可以类似写出。设当前可执行汇编语句的操作记忆码在MOT2表的索引值为i,则整个可执行汇编语句的处理可以描述如下:

将形成指令送往输出区。

在第二次扫描中,DS伪指令的主要目的是保留存储空间。我们不妨设立一个工作单元k,用以累计以字节为单位的存储空间大小,k的初值为0。从DS伪指令的操作数区求出k的大小后,就向输出区送k个空格以达到保留所规定存储单元的目的。DC伪指令处理和DS伪指令类似,只不过向输出区送的是所转换得到的常量。最后,START伪指令工作可能是输出目标程序开始的标准信息,而END伪指令则可能是输出目标程序结束的标准信息,这些信息都是为装配程序提供的。

2.1.3 装配程序

装配程序也称为连接程序。该程序主要完成以下两个任务。

(1)装入:装入是指读入可重定位的机器代码,修改重定位的地址,把修改后的指令和数据放在内存的适当位置或者形成可执行文件。

(2)连接:连接是把几个可重定位的机器代码文件连接成一个可执行程序,这些文件可以是分别汇编得到的,也可以是系统提供的程序库机器代码。

这种装配称为相对装配。装配程序从操作系统得到整个用户程序的装入起始地址,汇编程序第一次扫描结束时,本程序段长度已经求出。因此,可以求出每个程序段的起始装入地址,并进一步求出由PUBLIC伪指令定义的每一符号的实际存储地址。另一方面,汇编程序虽然无法知道外名(如SUM)的实际地址,但它却可以知道本程序哪一个存储单元(如ASUM)需要修改,并通过伪指令EXTERN说明哪些外名的值是本程序段修改地址常数时需要的。

为了能使相对装配程序获得必要信息,汇编程序对每一程序段输出下面次序的目标块:

(1)段名定义块:该块的信息包括程序段段名和程序段长度。

(2)整体符号定义块:程序块中定义的入口名及它在程序段中的相对地址。

(3)正文块:其信息包括正文第一个字节装入的相对地址、正文字节数和正文本身。

(4)地址常数块:地址常数所依赖的外名,地址常数在程序段中的相对地址。

(5)结束块:其信息为启动地址或0。

装配程序的工作通过对各程序段的目标块进行二次扫描来完成。在第一次扫描中,它只处理段名定义块和整体符号定义块,为段名和各段的入口名分配地址,建立整体符号表。在第二次扫描中,当正文全部复写到所分配的内存后,就可以根据地址常数块的信息对地址常数所分配单元中的值进行修正。最后根据结束块中的启动地址,执行装配好的目标程序。

2.1.4 宏指令

用汇编语言进行程序设计时,用户经常会书写完全相同或类似的语句,为了程序设计的方便,汇编程序往往向用户提供宏指令技术。按照这一技术,程序员可以相当自由地将一组汇编语句定义成一条新指令——宏指令。宏指令一经定义,用户就可以在程序段的其他地方书写这条指令,而将宏指令替换成原来指令序列的工作留给宏指令处理程序去完成。

在使用宏指令技术时,用户应该先进行宏定义,将宏指令和一串指令序列联系起来。各种汇编语言在宏指令定义开始和结束的规定上存在着微小的差异。IBM公司PC上宏指令定义的开始语句是:

宏指令名 MACRO 哑元表

而宏指令定义的结束语句是:

ENDM

其间是一个指令序列。宏指令定义开始语句的哑元表可以处于默认状态。宏指令确定以后,用户就可以直接在程序中书写宏指令,称为宏调用。如果宏指令定义带有哑元表,则用户在宏调用时必须提供替换相应哑元的变量信息。宏指令处理程序对宏指令定义开始语句和结束语句之间的语句一一进行必要的变量信息替换,然后依次插入宏指令调用处,这一过程称为宏指令展开。

宏指令处理程序的实现并不复杂,可建立宏定义表MDT,用来专门保存宏指令定义开始和结束之间的语句序列。另外,再设立宏定义名表MNT,用来建立宏指令名和它在MDT表第一条语句的对应关系。在宏指令展开时,只要根据宏指令名查找宏定义名表MNT,就可以得到第一条语句的位置,并从这一条语句开始,对宏定义中所有语句依次进行变量替换,然后送到相应位置上。 nhysiXJVcO4feOwNCbdb1tMkB5mAT0c3u5g76WgRxbehPOhUyLRNO/f7IxAEclXK

点击中间区域
呼出菜单
上一章
目录
下一章
×