20192430谢志琛汇编语言程序设计1~4章学习笔记

汇编语言程序设计

第一章 基础知识

1.汇编语言的一般概念

1.1 汇编语言的一般概念

计算机程序设计语言可分为机器语言、高级
语言和汇编语言三类

(1). 机器语言

机器语言就是把控制计算机的命令和各种数据直接用二进制数码表示的一种程序设计语言。

机器指令中既包含了指示运算功能的代码,又给出了参加运算的操作数据

优点:机器语言最直接地表示了计算机内部的基本操作,用它编制的程序在计算机中运行的效率最高。即运行速度最快,程序长度最短。

缺点:用二进制数表示的内容既不便于记忆又难于阅读。

(2). 高级语言

高级语言将计算机内部的操作细节屏蔽起来,用户不需要知道计算机内部数据的传送和处理的细节,使用类似于自然语言的一些语句来编制程序,完成指定的任务。

特点:程序设计简单,但程序效率较机器语言低。

(3). 汇编语言

为了便于记忆和阅读,使用字母和符号来表示机器语言的命令,用十进制数或十六进制数来表示数据,这样的计算机程序设计语言就称为汇编语言。

一条汇编语言的语句与一条机器语言指令对应,汇编语言程序与机器语言程序效率相同。

为了学习和使用某种计算机的汇编语言,必须熟悉计算机的内部组成结构。但并非要掌握计算机系统的全部硬件组成,只需掌握用汇编语言编制程序时所涉及到的那些硬件的结构和功能。

对一台计算机来说,机器语言的执行主要取决于该计算机的中央处理器CPU。


1.2 学习和使用汇编语言的目的

通过用汇编语言编制程序可以更清楚地了解计算机是如何完成各种复杂的工作。在此基础上,程序设计人员能更充分地利用机器硬件的全部功能,发挥机器的长处。

在计算机系统中,某些功能必须用汇编语言程序来实现。

汇编语言程序的效率高于高级语言程序。实时过程控制、智能仪器仪表等,常常用汇编语言来编制程序。


1.3 进位计数制及其相互转换

一. 进位计数制

如十进制数,用0、1~9十个数码的组合来表示数字。每个数码排在不同位置,所表示的数值大小不相同。

各个位置上所表示的基本数值称为位权, 简称权。

每个数位上能使用不同数码的个数称为基数。

例如十进制有十个数码0~9,基数为10,二进制基数为2。

每个数位能取的最大数码值=基数-1。

在计算机中数据表示一般采用二进制数,因为它在计算机中最容易表示和存储,且适合于逻辑值的表达与运算。

对人来说二进制不便于书写和阅读,因此书写时常使用8进制和16进制。

3位二进制数与一位8进制数对应,4位二进制数与一位16进制数对应。

B(Binary)--二进制数
O (Octal)或Q--八进制数
D (Decimal)--十进制数
H (Hexadecimal)--十六进制数。
如未使用任何字母,则默认表示是十进制数。

二. 各种数制间的相互转换

1.十进制整数转换为二进制数
(1)减权定位法
  1. 从二进制数高位起,依次用待转换的十进制数与各位权值进行比较;
  2. 如够减,则该数位系数Ki=1,同时减去该位权值,余数作为下一次比较的值;
  3. 如不够减,则Ki=0 。
(2) 除基取余法
  1. 将十进制数除以基数2,其余数为二进制数的最低位,再用其商除2,其余数为次低位,反复做下去,直到商0.
2.十进制小数转换为二进制数
(1) 减权定位法
(2) 乘基取整法
3. 二进制整数转换为十进制数
(1) 按权相加法
(2) 逐次乘基相加法
4. 二进制小数转换为十进制数
(1) 按权相加法
(2) 逐次除基相加法
5. 二进制与八进制和十六进制间的转换

三位二进制数对应一位八进制数,四位二进制数对应一位十六进制数。


1.4 带符号数的表示

在一般算术表示中使用”“+”和“-”来表示正数与负数,而在计算机中使用“0”和“1”来表示正数和负数。

一、原码表示

二进制数的最高位表示符号,0表示正,1表示负。数值 部分用二进制数绝对值表示。

二、补码的表示

1.补码的定义
[X]补=M+X (Mod M)

用补码表示的机器数,符号位仍然表示数的符号:0为正,1为负。对于正数,补码与原码相同,对于负数需要进行变换。

2.由真值、原码变换为补码

负数的真值变换为补码的方法:将各位变反(0变1,1变0)然后在最低位加1.

负数的原码变换为补码:保持符号位不变,其余各位变反,最低位加1。

3.补码数的表示范围

当位数为8时
最大补码为

01111111=[+127]补

最小补码为

10000000=[-128]补

0的补码只有一个

[0]补=00000000
10000000=[-128]补
11111111=[-1]补
4. 补码的加减运算
[X+Y]补=[X]补+[Y]补
[X-Y]补=[X]补-[Y]补=[X]补+[-Y]补
  • 其中[-Y]补是对[Y]补执行一次求补运算。

1.5 字符的表示

目前最广泛采用的是ASCII码

标准ASCII码为一字节,其中用低七位表示字符编码,用最高位表示奇偶数验位。

标准ASCII码共有128个,可分为两类:

  • 非打印ASCII码,共33个,用于控制操作, 如BEL(响铃07H),DEL(删除7FH),CR(回车,0DH), LF(换行,0AH).

  • 可打印ASCII码共有95个,如数字符0~9,大小写字母等。


1.6 基本逻辑运算

1. “与”运算(AND)

F=A ∧ B 或 F=A · B
0 ∧ 0 =0 
0 ∧ 1 = 0
1 ∧ 0 = 0
1 ∧ 1 =1

2. “或”运算(OR)

F=A ∨B 或F=A +B
0 ∨0=0
0 ∨1=1
1 ∨0=1 
1 ∨1=1

3. “非”运算

逻辑“非”运算通常是在逻辑变量上方加一横线表示。

4. “异或”运算(XOR)

1 ⊕ 1=0
1 ⊕ 0=1
0 ⊕ 1=1
0⊕ 0=0


第二章 IBM-PC微机的功能结构

2.1 IBM-PC微机基本结构

一. 微机的一般构成

一般计算机应包括五大部件:运算器、控制器、存储器、输入设备和输出设备。

微机的主要特点是其体积很小。

将运算器和控制器两大部件集成在一个集成电路芯片上,称为中央处理器,简称CPU,也叫微处理器。

系统采用总线结构,具有较大的灵活性和扩展性。
(https://qr.maitube.com/img/GDBHDB)

1、中央处理器CPU

分析从主存储器取来的各条指令的功能,控制计算机各部件完成指定功能的各项操作。

2、主存储器

主存储器是用来存放程序和数据的部件。它由若干个存储单元构成。存储单元的多少表示存储器的容量。每个存储单元使用一个唯一的编号来标识,称为存储单元的地址。对每个存储单元内容的存和取是按照地址进行访问的。由于它的位置是在主机之外,因此也叫做外存。

3、输入输出设备及接口

输入设备将外部信息(程序、数据和命令)送入计算机。包括键盘、鼠标等。

输出设备将计算机处理后的结果转换为人或其它系统能识别的信息形式向外输出。如显示器、打印机等。

由于I/O设备的工作速度、工作原理以及所处理的信息格式等与主机相差很大,因此I/O设备要通过I/O接口才能与系统总线连接。

I/O接口是主机与I/O设备之间设置的逻辑控制部件。通过它实现主机与I/O设备间的信息传送。

4、系统总线

系统总线包括地址总线、数据总线和控制总线三组。它们分别用于传送不同的信息。

二、 Intel 8086/8088 CPU的功能结构

指令是构成汇编语言程序的最基本单位,就象高级语言中的语句。

CPU执行指令序列就是重复执行从存储器中取指令和执行指令规定的操作。

这两个步骤的执行又分为串行方式和指令流水线方式。

1. 串行方式

当CPU在指令执行阶段,不需要占用系统总线,但此时总线也不工作,因此系统总线的空闲时间比较多。

在从存储器取指令、取数据或存数据时,总线处于忙状态,其所占用的时间也较长。而CPU却只需要花很短的时间去处理,因此大部分时间处于闲置状态。

2.指令流水线方式

采用指令流水线工作方式的计算机具有较高的工作效率。CPU内部采用了一种先进的指令流水线结构,这种结构可以有效而充分地利用各主要硬件资源。

要实现指令流水线方式,从CPU组成结构上要划分成多个单元。8086/8088CPU被划分成两个单元。
(https://qr.maitube.com/img/GDBHDD)

(1)执行单元EU

EU的主要任务是分析与执行指令,具体包括:

  1. 从指令队列中取出指令代码,由控制器译码后产生相应的控制信号,控制各部件完成指令规定的操作。

  2. 对操作数执行各种指定的算术或逻辑运算。

  3. 向总线接口单元BIU发送访问主存或I/0的命令,并提供相应的地址和传送的数据。

(2)总线接口单元BIU

BIU负责CPU与存储器、I/O的信息传送。具体包括:

  1. 取指令——根据CS寄存器和指令指针IP形成20位的物理地址,从相应的存储器单元中取出指令,暂存到指令队列中,等待EU取走并执行。

  2. 存取数据——在EU执行指令的过程中,需要与存储器或I/O端口传送数据时,由EU提供的数据和地址,结合段寄存器,通过外部总线与存储器或I/0进行数据的存取。


2. 2 Intel 8086/8088CPU寄存器结构及其用途

一、通用寄存器

Intel 8086/8088有8个16位通用寄存器,它们具有良好的通用性,并且还可以用作某个特定的功能,可以由程序设计人员进行编程访问。

1. 数据寄存器

它包括AX、BX、CX和DX四个寄存器。它们中的每一个既可以是16位寄存器,也可以分成两个8位寄存器使用。即可以当作8个独立的8位寄存器使用。

数据寄存器既可以用来存放参加运算的操作数,也可以存放运算的结果。在多数情况下,使用这些寄存器时必须在指令中明确指示。

在有些指令中,不需要明确指出使用的寄存器名,即隐念使用了某寄存器,称为隐含使用。

个别指令对寄存器有特定的使用,并且又必须在指令中指明它的名字,这类寄存器的使用称为特定使用。

2. 指针寄存器

SP指针——在进行堆栈操作时,被隐含使用,被用来指向堆栈顶部单元。

BP指针——被用来指向堆栈段内某一存储单元。BP除用作地址指针外也可以象数据寄存器一 样,存放参加运算的操作数和运算的结果。

3. 变址寄存器

有两个16位的变址寄存器SI和DI,一般被用来作地址指针。

  • SI——源变址寄存器
  • DI——目的变址寄存器

二、段寄存器

8086/8088CPU在使用存储器时,将它划分成若干个段。

每个段用来存放不同的内容,如程序代码、数据等等。

每个存储段用一个段寄存器来指明该段的起始位置(也叫段基址)。

CPU在访问存储器时必须指明两个内容:
(1)所访问的存储单元属于哪个段,即指明使用的段寄存器。
(2)该存储单元与段起始地址(段基址)的相距多少,即偏移量。

三、指令指针IP

CPU在从存储器取指令时,以段寄存器CS作为代码段的基址指针,以IP的内容作为偏移量,共同形成一条指令的存放地址。

当CPU从内存中取出一条指令后,IP内容自动修改为指向下一条指令。

IP的内容不能被直接访问,既不能用指令去读IP的值,也不能用指令给它赋值。但是可以通过某些指令的执行而自动修改IP的内容。

四、标志寄存器

标志寄存器是用来反映CPU在程序运行时的某些状态,如是否有进位、奇偶性、结果的符号、结果是否为零等等。

1. 进位标志位CF

在进行算术运算时,若最高位(对字操作是第15位,字节操作是第7位)产生进位或借位时CF被自动置“1”,否则置“0”

在移位类指令中,CF也被用来存放从最高位(左移时)或最低位(右移时)移出的数值(0或1)。

2. 奇偶标志位PF

当指令操作结果的低8位中含有1的个数为偶数时,则PF被置1,否则PF被置0。

注意:PF只反映操作结果的低8位的奇偶性,与指令操作数的长度无关。

3. 辅助进位标志位AF

在进行算术运算时,若低字节的低四位向高4位产生进位或借位,即第3位产生进位或借位时,AF位被置1,否则置0。AF标志位用于十进制运算的调整。

4. 零值标志位ZF

若运算结果各位全为0,则ZF被置1,否则置0。

5. 符号标志位SF

将运算结果视为带符号数,当运算结果为负数时SF被置1,为正数时,则置0。

由于第7位是字节操作数的符号位,而第15位是字操作数的符号位,因此,SF位与运算结果的最高位(第7位或第15位)相同。

6. 溢出标志位OF

当运算结果超过机器用补码所能表示数的范围时,则OF置1,否则置0.

字节数据,机器用补码所能表示的数范围为-128~+127。字数据的表示范围为:-32768~+32767

注意:溢出与进位是两个完全不同的概念,不能相互混淆。

7. 单步标志位TF(Trace Flag)

单步标志也叫跟踪位,该标志为控制标志位。单步标志位供调试程序使用。

当TF位被设置为1时,每执行一条指令后,CPU暂停运行,即产生单步中断。

8. 中断允许标志位IF

该标志位为控制标志位。当IF被设置为1时,CPU可以响应可屏蔽中断,否则不允许响应可屏蔽中断。

9. 方向标志位DF

DF也是控制标志位。它被用来规定串操作指令的增减方向。

当DF=0时,串操作指令自动使变址寄存器(SI和DI)的内容递增。当DF=1时,串操作指令自动使变址寄存器的内容递减。


2.3 存储器组织结构

一、存储器的组成

1. 存储器是由若干个存储单元构成

存储单元的多少就表示了存储器的容量。

2. 每个存储单元存放相同长度的二进制数

一个存储单元的长度一般为8位二进制数,即一个字节。

3. 每个存储单元有一个唯一的地址编号——地址

8086/8088CPU有20根地址线,即它可以产生20位的地址码,它的存储器寻址能力为1兆字节空间。

4. 任何两个相邻字节单元就构成一个字单元

一个字存储单元(WORD)的长度为16位二进
制数,即两个字节。字单元的地址为两个字节单元中较小地址字节单元的地址。

16位长数据的存放规则是低8位放在较低地址字节单元中,高8位放在较高地址字节单元中。

5. 在定义一个地址时必须指出是字节或字类型属性

由于存储单元可分为字单元和字节单元,因此8086/8088CPU访问内存的指令中,分为字节访问和字访问两种指令。

二、存储器的段结构

由于8086/8088可寻址的存储空间为1MB,需要提供20位长的地址码。而CPU内部的寄存器长度只有16位。能够直接访问的最大地址空间是64KB。

8086/8088系统的存储器段结构具有以下几个特点:

1. 8086/8088CPU将1MB的存储空间划分成若干个段,每个段最大长度为64K(65536)个字节单元组成。

在8086/8088的汇编语言源程序中,用户可以根据自己需要来设定段的个数、各个段长度和每个段的用途。并且代码或数据可以存放在段内任意单元中。

2. 每个段的基址(段基址)必须是一个小节的首址。

段基址——一个段的起始地址。

在存储器中规定从0地址开始,每16个字节单元称为一个小节(Paragraph)。因此,1MB内存就可划分为64K个小节。

3. 逻辑段在物理存储器中可以是邻接的、间隔的、部分重叠的和完全重叠的等4种情况。

逻辑段是指在汇编语言源程序中设置的段
内存中的一个物理存储单元可以映象到一个或多个逻辑段中

4. 在任一时刻,一个程序只能访问4个当前段中的内容。

4个段分别是代码段、数据段、堆栈段和附加段,称为当前段。

4个段寄存器CS、DS、SS和ES分别保存了它们段基址的高16位地址,称为段基值。段基址的最低4位为0。(小节首址的低4位为全0 )。

三、 逻辑地址与物理地址及对应关系

1. 物理地址

在1MB的存储空间中,每个存储单元的物理地址是唯一的,它就是该存储单元的20位地址。

8086/8088的物理地址范围:00000H~0FFFFFH

CPU与存储器之间的任何信息交换都使用物理地址。

2. 逻辑地址

在程序设计中,为了便于程序的开发和对存储器进行动态管理,使用了逻辑地址。

一个逻辑地址包括两个部分:段基值和偏移量

段基值:存放在某一个段寄存器中,是一个逻辑段的起始单元地址(段基址)的高16位。

偏移量:表示某个存储单元与它所在段的段基址之间的字节距离。

逻辑地址的表示方法

段基值:偏移量

3. 逻辑地址转换为物理地址

当CPU要访问存储器时,需要由总线接口单元BIU将逻辑地址转换成物理地址。

转换方法:将逻辑地址的段基值左移4位,形成20位的段基址(低位为0)然后与16位的偏移量相加,结果即为20位的物理地址。

4. 逻辑地址的来源

在程序的执行过程中,CPU根据不同操作类型访问存储器,其逻辑地址中段基值和偏移量的来源是不一样的。下表是各种操作类型所对应的逻辑地址的来源。
https://qr.maitube.com/img/GDBHEB

(1)允许替代来源也叫做段超越,它表示了段基值除使用隐含的段寄存器外是否可以指定其它段寄存器来提供。

(2)有效地址EA,它表示根据指令所采用的寻址方式计算出来的段内偏移量。


2.4 堆栈及其操作方法

堆栈的用途:主要用于暂存数据以及在过程调用或处理中断时保存断点信息。

一、堆栈的构造

专用堆栈存储器:
按堆栈的工作方式专门设计的存储器

软件堆栈:
由程序设计人员用软件在内存中划出的一块存储区作为堆栈来使用。8086/8088采用这种方式。

堆栈的一端是固定的,称为栈底。栈底是堆栈存储区的最大地址单元。

另一端是浮动的,称为栈顶。在任何时刻,栈顶是最后存入信息的存储单元。栈顶是随着堆栈中存放信息的多少而改变。

为了指示现在堆栈中存放数据位置,通常设置一个寄存器来指示栈顶位置。其内容就象一个指针一样,因此被称为堆栈指针SP(Stack Pointer)。

SP的内容始终指向栈顶单元
堆栈中数据进出都由SP来控制

在堆栈中存取数据的规则是:“先进后出FILO”(First-In Last-Out)。即最先送入堆栈的数据要到最后才能取出,而最后送入堆栈的数据,最先取出。

二、8086/8088堆栈的组织

在8086/8088微机中堆栈是由堆栈段寄存器SS指示的一段存储区。

顶由堆栈指针SP指示。SP中内容始终表示堆栈段基址与栈顶之间的距离(字节数)。当SP内容为最大(初始)值时,表示堆栈为空。而当(SP)=0时,表示堆栈全满.

当SP被初始化时,指向栈底+2单元,其值就是堆栈的长度。由于SP是16位寄存器,因此堆栈长度 64K字节。

数据在堆栈中的存放格式是:以字为单位存放,数据的低8位放在较低地址单元,高8位放在较高地址单元。

当用户程序中要求的堆栈长度超过一个堆栈段的最大长度64KB时,可以设置几个堆栈段。

通过改变堆栈段寄存器SS的内容,即可改变到另一个堆栈段,当改变了堆栈段寄存器SS的内容后,必须紧接着赋予SP新值。

三、 堆栈操作

1.设置堆栈

设置堆栈主要是对堆栈段寄存器SS和堆栈指针SP赋值。

当程序经过汇编、连接并装入内存时,系统将自动为其分配一个存储区作为堆栈段,将这个段的段基址的高16位送入SS中,将程序指定的字节单元数100赋值给SP。

2.进栈PUSH

进栈就是把数据存入堆栈。由指令PUSH或者由机器自动实现,可以将通用寄存器、段寄存器或字存储单元的内容压入堆栈顶部。

进栈的执行过程:
(1)首先将堆栈指针SP减2,即指向一个空的堆栈字单元

SP<=(SP)-2

(2)将要储存的内容(寄存器或存储单元的内容)送入SP指向的字单元中。

(SP)<=数据
3.出栈POP

出栈操作由POP指令或机器自动实现,它从堆栈顶部弹出一个字到通用寄存器、段寄存器或字存储单元。

出栈的操作过程:
(1)将SP指向的字单元(即栈顶字单元)内容送往指定的寄存器或存储器。

寄存器/存储器<=((SP))

(2)堆栈指针SP内容加2

SP<=(SP)+2


第三章 寻址方式与指令系统

3.1 寻址方式

一条指令通常由两大部分构成:
操作码:表示该指令应完成的具体操作,如加法、减法、乘法、移位等等。在汇编语言中使用一定的符号来表示,称为助记符。如ADD 、PUSH、POP、MOV等等。

操作数: 表示该指令的操作对象。如移位操作的被移位数,加法操作的加数等等。它可以是一个操作数,也可以是多个操作数。这取决于操作码部分的具体需要。

寻址方式:寻找指令中所需操作数的各种方法,也就是提供指令中操作数的存放信息的方式。

Intel 8086/8088各指令中提供操作数的方法有以下四种:

(1)立即数操作数——操作数在指令代码中提供
(2)寄存器操作数——操作数在CPU的通用寄存器或段寄存器中
(3)存储器操作数——操作数在内存的存储单元中
(4)I/O端口操作数——操作数在输入/输出接口的寄存器中

1.立即数寻址

立即数寻址方式的指令中,所需操作数直接包含在指令代码中,这种操作数称为立即数。

由于在指令执行过程中,立即数作为指令的一部分直接从BIU的指令队列中取出,它不需另外占用总线周期,因此这种寻址方式执行速度快。

注意:立即数只能作为源操作数,而不能作为目的操作数。

2.寄存器寻址

寄存器寻址方式是指指令中所需的操作数在CPU的某个寄存器中。寄存器可以是8位或16位通用寄存器,或者是段寄存器。如:AH、AL、AX、CX、DS、ES等。

由于存取寄存器操作数完全在CPU内部进行,不需要总线周期,所以执行速度很快。

一个存储单元逻辑地址表示形式:

段基值:偏移量

段基值由某个段寄存器提供.

偏移量表示了该存储单元与段起始地址之间的距离,也叫做有效地址EA。

有效地址EA是以下三个地址分量的几种组合,由CPU的执行单元EU计算出来的。
(1)位移量:位移量是指令中直接给出的一个8位或16位数。一般源程序中以操作数名字(变量名或标号)的形式出现。
(2)基址:由基址寄存器BX或基址指针BP提供的内容。
(3)变址:由源变址寄存器SI或目的变址寄存器DI提供的内容

位移量、基址和变址三个地址分量组合时,若有两个或两个以上分量时,将进行以2
的16次方为模的十六位加法运算。

3.直接寻址

在直接寻址方式的指令中,操作数的有效地址EA只有位移量地址分量。

在汇编语言源程序中,直接寻址方式用符号或常数来表示。

(1)用符号表示
例:

MOV BX,VAR=>MOV BX,DS:VAR

它表示将数据段中,偏移了VAR个字节距离的字单元内容送到寄存器BX中。

MOV AL,DATA+2=> MOV AL, DS: DATA+2

它表示将数据段偏移了DATA+2的字节单元内容送入AL中。

(2)用常数表示

MOV AX,DS:[64H]

它表示从当前数据段开始,偏移100个字节的字单元内容送到AX中。不能写为:

MOV AX,64H

注意:用常数表示时,必须用方括号括起来。段寄存
器不能省略。

4.寄存器间接寻址

操作数有效地址EA直接从基址寄存器(BX或BP)或变址寄存器(SI或DI)中获得。

寄存器间接寻址就是事先将偏移量存放在某个寄存器(BX、BP、SI或DI)中,这些寄存器就如同一个地址指针。

在程序运行期间,只要对寄存器内容进行修改,就可以实现用同一条指令实现对不同存储单元进行操作。

指示存储器所在段的段寄存器可以省略,当指令中使用的是BP寄存器,则隐含表示使用SS段寄存器,其余情况则隐含使用DS段寄存器。

例如:

MOV AX,[BX]=> MOV AX,DS:[BX]
MOV BH,[BP]=> MOV BH,SS:[BP]
MOV [DI],BX=> MOV DS:[DI],BX

5.基址寻址/变址寻址

操作数的有效地址EA等于基址分量或变址分量加上指令中给出的位移量。

指令中使用BX或BP时为基址寻址。指令中使用SI或DI时为变址寻址。

段寄存器的隐含使用规则与寄存器间接寻址方式相同

例:

MOV AX,10H [SI]=> MOV AX,DS:10H [SI]
MOV TABLE [DI],AL=>MOV DS:TABLE [DI],AL

注意:当位移量为常数时,不能加方括号。

由于这两种寻址方式中寄存器中的内容是相对于由位移量指定的初始单元。因此也叫寄存器相对寻址。

6.基址变址寻址

操作数的有效地址是三个地址分量之和,即:

EA=基址+变址+位移量

例如:

MOV CX,100H[BX][DI]
MOV TABLE[BX][DI],AX

在基址变址寻址方式中,程序运行期间有两个地址分量可以修改。因此它是最灵活的一种寻址方式,可以方便地对二维数组进行访问。

存储器操作数寻址方式中地址形成小结
(https://qr.maitube.com/img/GDBHEH)

7.串操作寻址方式

8086/8088设置有专门用于串操作的指令,这些指令的操作数虽然也在存储器中,但它们不使用前面介绍的各种寻址方式,而隐含地使用变址寄存器SI和DI专门指示。

在寻找源操作数时,隐含使用SI作为地址指针。

在寻找目的串时,隐含使用DI作为地址指针。

在串操作完成之后,自动对SI和DI进行修改,使它们指向下一个操作数。

8. I/O端口寻址

存储器编址方法:
将I/O端口视为存储器的一个单元,对端口的访问就如同访问存储单元一样。访问存储器的指令和各种寻址方式同样适用对I/O端口的访问。

特点: 程序设计灵活,但需要占用存储地址空间。

I/O端口编址方法:
I/O端口的地址与存储器地址分开,并使用专门的输入指令和输出指令。

(1) 直接端口寻址

在指令中直接给出端口地址,端口地址一般采用2位十六进制数,也可以用符号表示。

直接端口寻址可访问的端口数为0~255个。

(2)寄存器间接端口寻址

寄存器间接端口寻址:把I/O端口的地址先送到DX中,用DX作间接寻址寄存器。

如果访问的端口地址值大于255,则必须用I/O端口的间接寻址方式。


3.2 指令系统

一种计算机所能执行的各种类型的指令的集合称为该计算机的指令系统。
Intel8086/8088CPU指令系统的指令可以分为六大类:
1.传送类指令
2.算术运算类指令
3.位操作类指令
4.串操作类指令
5.程序转移类指令
6.处理器控制类指令

从指令的格式划分,一般可以分为三种:
1.双操作数指令:OPR DEST SRC
2.单操作数指令:OPR DEST
3.无操作数指令:OPR

对于无操作数指令,包含两种情况:
(1)指令不需要操作数,如暂停指令HLT。
(2)在指令格式中,没有显式地指明操作数,但是它隐含指明了操作数的存放地方,如指令PUSHF。

一、传送类指令

传送类指令的作用是将数据信息或地址信息传送到一个寄存器或存储单元中,可以分为以下四种情况。

1.通用数据传送指令

指令格式:MOV DEST,SRC

作用:将源操作数指定的内容传送到目的操作数,即DEST<=(SRC)。

当指令执行完后,目的操作数原有的内容被源操作数内容覆盖,即目的操作数和源操作数具有相同内容。

MOV指令对标志寄存器的各位无影响

MOV指令可以是字节数据传送也可以是字数据传送,但是源操作数和目的操作数的长度必须一致。

MOV指令可以分为以下几种情况:
(1)立即数传送到通用寄存器或存储单元
注意:立即数只能作为源操作数,立即数不能传送给段寄存器。

(2)寄存器之间的传送
注意:段寄存器CS只能作源操作数,不能作目的操作数。

(3)寄存器与存储单元之间传送

综合起来,MOV指令在使用时需注意以下几个问题:
(1)立即数只能作源操作数,且它不能传送给段寄存器。
(2)段寄存器CS只能作源操作数,段寄存器之间不能直
接传送。
(3)存储单元之间不能直接传送数据
(4)MOV指令不影响标志位

2.交换指令

指令格式:XCHG DEST,SRC

作用:源操作数和目的操作数两者内容相互交换,即:

(DEST)<=>(SRC)

指令对标志寄存器各位无影响

数据交换可以在寄存器之间或寄存器与存储器单元之间进行。但是不能在存储单元之间直接进行数据交换。寄存器只能使用通用寄存器。

为了完成两个存储单元(DA_BYTE1和DA_BYTE2)之间的数据交换可以使用以下三条指令来实现。

MOV AL,DA-BYTE1; AL<=(DA_BYTE1)
XCHG AL,DA-BYTE2 ; (AL)<=>(DA-BYTE2)
XCHG AL,DA-BYTE1 ; (AL)<=>(DA-BYTE1)

MOV DA-BYTE1,AL;(DA_BYTE1)<=(AL)
3.标志传送指令

对标志寄存器进行存取的指令有4条,它们都是无操作数指令,即指令隐含指定标志寄存器、AH寄存器或堆栈为操作数。

(1)取标志寄存器指令

指令格式:LAHF

作用:将标志寄存器的低8位送入AH寄存器,即将标志SF、ZF、AF、PF和CF分别送入AH的第7、6、4、2、0位,而AH的第5、3、1位不确定。

指令执行对标志寄存器各位无影响,即标志寄存器各位不变.

(2)存储标志寄存器指令

指令格式:SAHF

作用:将寄存器AH中的第7、6、4、2、0位分别送入标志寄存器的SF、ZF、AF、PF和CF各标志位。而标志寄存器高8位中的各标志位不受影响。

(3)标志进栈指令

指令格式:PUSHF

作用:先将堆栈指针SP减2,使其指向堆栈顶部的空字单元,然后将16位标志寄存器的内容送SP指向的字单元。

(4)标志出栈指令

指令格式:POPF

作用:将由SP指向的堆栈顶部的一个字单元的内容送入标志寄存器,然后SP的内容加2.

4.地址传送指令

这类指令有3条,它们的作用是将存储单元的地址送寄存器。

(1)装入有效地址

格式:LEA DEST,SRC

其中:源操作数SRC必须是一个字节或字存储器操作数(地址),DEST必须是一个16位通用寄存器。

作用:将SRC存储单元地址中的偏移量,即有效地址EA传送到一个16位通用寄存器中。

指令执行对标志寄存器各位无影响。

(2)装入地址指针指令

其中:DEST是任意一个16位通用寄存器。SRC必须是一个存储器操作数。

作用:把SRC存储单元开始的4个字节单元的内容(32位地址指针)送入DEST通用寄存器和段寄存器DS(LDS指令)或ES(LES指令),其中低字单元内容为偏移量送通用寄存器,高字单元内容为段基值送DS或ES。

二、算术运算类指令

8086/8088指令系统中有加、减、乘、除指令,这些指令可以对字节数据或字数据进行运算。

参加运算的数可以是无符号数,也可以是带符号数。带符号数用补码表示。

参加运算的数可以是二进制数,也可以是十进制数(以BCD码表示)。

1.加法指令

指令格式:ADD DEST,SRC

功能:目的操作数和源操作数相加,其和存放到目的操作数中,而源操作数内容保持不变,即:

DEST<=(DEST)+(SRC)

根据相加的结果将影响到标志寄存器的CF、PF、AF、ZF、SF和OF。

DEST只能是通用寄存器或存储器操作数。不能是立即数。

SRC可以是通用寄存器、存储器或立即数操作数

DEST和SRC不能都为存储器操作数。

ADD指令可以是字节操作数相加,也可以是字操作数相加。

2.带进位加法指令

指令格式:ADC DEST,SRC

该指令的功能与ADD基本相同,所不同的是其结果还要加上进位标志CF的值,即:

DEST<=(DEST)+(SRC)+CF

根据相加的结果设置标志寄存器中的CF、PF、AF、ZF、SF和OF

注意:参加运算的进位CF是本条指令执行之前的值。用ADC指令可实现数据长度大于16位的两数相加。

3.加1指令

指令格式:INC DEST

该指令为单操作数指令,其功能是将目的操作数加1,并送回到目的操作数,即:

DEST<=(DEST)+1

目的操作数可以是任意的8位、16位通用寄存器或存储器操作数。目的操作数被视为带符号二进制数

根据指令执行结果设置PF、AF、ZF、SF和OF标
志,但不影响CF。INC指令主要用于某些计数器的计数和修改地址指针。

4.减法指令

指令格式:SUB DEST,SRC

功能:目的操作数的内容减去源操作数的内容,结果送入目的操作数,源操作数中内容保持不变。即:

E = mc^2DEST<=(DEST)-(SRC)

操作结果将影响标志位CF、PF、AF、ZF、SF和OF。

目的操作数DEST和源操作数SRC可以是8位或16位的通用寄存器、存储器操作数,但两者不能同时为存储器操作数。立即数只能作源操作数。

注意:减法指令对借位标志的影响,若采用变减为加的运算方法,则产生的进位与CF标志结果相反。

5.带借位减法

指令格式:SBB DEST,SRC

该指令的功能与SUB指令基本相同,不同的是在两个操作数相减后再减去进位标志CF的值。即:

DEST<=(DEST)-(SRC)-CF

注意:该CF的值是本条指令执行前的结果。

SBB指令在使用上与ADC类似,主要用于长度大于16位的数相减,即将低16位相减的结果引入高位部分的减法中。

根据指令执行结果设置PF、AF、ZF、SF 、OF和CF。

6.减1指令

指令格式:DEC DEST

该指令为单操作数指令,将目的操作数的内容减1后,送回到目的操作数。即:

DEST<=(DEST)-1

DEST可以是8位或16位的通用寄存器存储器操作数,该指令将DEST看作是带符号二进制数。

根据指令执行结果设置PF、AF、ZF、SF和OF,
但不影响CF。

DEC指令的使用类似INC指令。主要用于计数和修改地址指针,计数方向与INC指令相反。

7.求负数指令

指令格式:NEG DEST

功能:用零减去目的操作数的内容,并送回目的操作数,即:

DEST<=0-(DEST)

DEST可以是任意一个8位或16位的通用寄存器或存储器操作数,被视为带符号的操作数。

由于机器中带符号数用补码表示,求操作数的负数就是求补操作。因此,NEG指令也叫取补指令。

NEG指令将影响标志PF、AF、ZF、SF、CF和OF。

只有当操作数为零时,进位标志CF被置零,其它情况都被置1.

当字节操作数为-128,或字操作数为-32768时,执行NEG指令的结果操作数将无变化,但溢出标志OF被置1.

三、位操作类指令

1.逻辑运算指令

逻辑运算指令共有4条,它们的指令格式分别是:
逻辑“与”指令 AND DEST,SRC
逻辑“或”指令 OR DEST,SRC
逻辑“异或”指令 XOR DEST,SRC
逻辑“非”指令 NOT DEST

DEST和SRC可以是8位或16位的通用寄存器或存储器操作数,但两者不能同时为存储器操作数,SRC可以为立即数。

逻辑指令对标志位的影响:
NOT指令对标志无影响。而其余三条指令将根据结果影响SF、ZF和PF,而CF和OF总 是置0,AF为不确定。

逻辑运算指令除用来实现各种逻辑运算之外,还常用于对字节或字数据的某些位的组合、分离或位设置。

2.测试指令

指令格式:TEST DEST,SRC

该指令的功能与AND指令相似,实现源操作数与目的操作数进行按位“逻辑与”运算,对标志位的影响与AND指令相同,但运算的结果不送入目的操作数,即目的操作数内容也将保持不变。

TEST指令主要用于测试某一操作数的一位或几
位的状态。

3.移位/循环移位指令
(1)算术移位

算术左移 SAL DEST,COUNT
算术右移 SAR DEST,COUNT

(2)逻辑移位

逻辑左移 SHL DEST,COUNT
逻辑右移 SHR DEST,COUNT

逻辑左移SHL与算术左移SAL功能相同。

(3)循环移位

小循环:
循环左移 ROL DEST,COUNT
循环右移 ROR DEST,COUNT

大循环:
带进位循环左移 RCL DEST,COUNT
带进位循环右移 RCR DEST,COUNT

这8条指令具有以下几个共同点:
(1)DEST为操作对象,它可以是字节或字操作数,可以是通用寄存器或存储器操作数。

(2)COUNT用来决定移位/循环的位数,即确定移位的次数。

当移位次数为1时,使用常数1或寄存器CL。
当移位次数大于1时,必须使用寄存器CL。

(3)在执行移位时,根据指令不同,每移位一次,最高位(左移)或最低位(右移)都要送到进位标志CF。

(4)前4条移位指令根据移位结束后修改标志位CF、PF、ZF、SF和OF,而AF不确定。而后4条循环移位指令根据移位结束后的结果仅修改CF和OF

对溢出标志位OF的影响:
移位次数为1时,移位前后操作数的符号位发生变化,则OF被置1,否则置0。移位次数大于1时,OF不确定。

指令SAL和SAR当移位次为n时,其作用相当于乘以2的n次方或除以2的n次方,因此被叫做算术移位指令。

为了保持其算术运算结果的正确性,移位后的结果不能发生溢出。

对于多字节或多字数据的移位,需要使用带进位循环移位指令。

四、处理器控制类指令

处理器控制类指令包括以下三种情况。

1.标志位操作指令

它们都是无操作数指令,操作数隐含为标志寄存器的某个标志位。能直接操作的标志位有CF、IF和DF。

(1)清除进位标志

CLC ;置CF为0

(2)置1进位标志

STC ;置CF为1

(3)进位标志取反

CMC ;CF的值取反

(4)清除方向标志

CLD;置DF为0

(5)置1方向标志

STD;置DF为1

(6)清除中断标志

CLI;置IF为0

(7)置1中断标志

STI;置IF为1

2、与外部事件同步的指令

HLT ;暂停指令
WAIT ;等待指令
ESC ;外部协处理器指令前缀
LOCK ;总线锁定指令

3、空操作指令 NOP

执行一次NOP占用CPU三个时钟周期,它不改变任何寄存器或存储单元内容,主要用于延时。


3.3 指令编码

汇编:将汇编语言程序转换为机器语言程序的过程

汇编程序:在计算机中实现汇编过程的系统程序

Intel8086/8088汇编指令的编码格式有四种基本格式。

1.双操作数指令编码格式
2.单操作数指令编码格式
3.与AX或AL有关的指令编码格式
4.其它指令编码格式

一、双操作数指令编码格式

对于象MOV、ADD、AND等双操作数指令,操作数可以是以下两种情形:
一个操作数在寄存器中,另一操作数在寄存器或存储器中。

目的操作数在寄存器或存储器中,源操作数是立即数。

这类指令的机器目标代码长度为2~6个字节

整个指令编码可以包含4个部分,但其中某些部分在一些指令的编码中可以没有。

1.操作特征部分

这部分为指令编码的首字节,它又分为以下三个段

(1)OPCODE:操作码字段

该字段长度为6bit。它表示了该指令所执行的功能和两个操作数的来源。

如果指令的源操作数是立即数,则需要使用指令编码的第2字节中REG字段作辅助操作码。

(2)方向字段d

该字段与第2部分寻址特征一起来决定源操作数和目的操作数的来源。

注意:当源操作数为立即数Imm时,d字段无效,它被并入 操作码字段。

(3)字/字节字段W

当W=1时,表示两操作数长度为字;当W=0时,表示两操作数长度为字节。

2.寻址特征部分

它与操作特征部分的方向字段d结合,指定两个操作数分别使用什么寻址方式,及使用哪个寄存器。

它包括MOD、REG和R/M三个字段,REG字段确定一个操作数,而MOD和R/M字段确定另一个操作数。

当d=1时,则目的操作数由REG字段确定,而源操作数由MOD和R/M字段确定。

当d=0时,则目的操作数由MOD和R/M字段确定,而源操作数由REG字段确定。

(1)REG字段

由REG字段确定的一个操作数是某一通用寄存器的内容,即使用的是寄存器寻址方式。

第一部分中的W字段决定操作数是字或是字节。

(2)寻址方式字段MOD和寄存器/存储器字段R/M

这两个字段共同确定一个操作数。该操作数可以在寄存器中,也可以在存储器中

MOD、R/M和W字段共同确定操作数的寻址方式和所使用的寄存器,如下表所示。
(https://qr.maitube.com/img/GDBHFA)
注意:在表中没有使用BP作寄存器间接寻址方式,如果在指令中使用了[BP],则将其汇编为[BP+0],即基址寻址。

3.位移量部分

根据寻址特征中MOD和R/M字段确定的有效地址计算方法,位移量可以是以下三种情况之一:
没有位移量
1字节位移量disp8
2字节位移量disp16

4.立即数部分

如果指令的源操作数为立即数,则指令编码中包含有该部分。它总是位于指令编码的最后1~2字节。

二、单操作数指令编码格式

这种编码格式适用于只有一个操作数的指令,如INC、DEC、移位/循环等指令。指令编码为2~3字节。

操作特征部分:
包括OPCODE、V和W三个字段,其中V字段只有移位/循环指令中才有该字段。其它指令中没有该字段。

V=0时,指令中使用常数1作为移位或循环次数。
V=1时,指令中使用寄存器CL作移位次数。

由于单操作数指令中只有一个操作数,因此寻址特征部分就不需要REG字段,而该字段被用作辅助操作码。

三、与AX或AL有关的指令编码格式

这种编码格式用于隐含指定AX/AL作为一个操作数的双操作数指令

采用这种编码格式的指令,除一个操作数隐含指定为AX/AL外,另一个操作数可以是立即数或存储单元。

立即数:则编码中应有1~2字节的立即数

存储单元:只能使用直接寻址方式,位移量由disp字段给出.

四、其它指令编码格式

除上述三种编码格式外,还有一些指令的编码格式更简单。如标志位操作指令、堆栈操作指令等。这些指令的编码格式一般只有一个字节。

例如 CLC清进位标志,该指令的编码只有一个字节的操作码

在有些单字节指令的编码中,将该字节划出部分位作为REG字段。



第四章 汇编语言程序格式

4.1 汇编语言语句种类及其格式

汇编语言的语句可以分为指令语句和伪指令语句

一、指令语句

每一条指令语句在汇编时都要产生一个可供CPU执行的机器目标代码,它又叫可执行语句。

指令语句的一般格式为:

标号:指令助记符-操作数(,);注释

一条指令语句最多可以包含4个字段

1.标号字段

标号是可选字段,它后面必须有“:”。标号是一条指令的符号地址,代表了该指令的第一个字节存放地址。

标号一般放在一个程序段或子程序的入口处,控制程序的执行转到该程序位置。

在转移指令或子程序调用指令中,可直接引用这个标号。

2.指令助记符字段

该字段是一条指令的必选项,它表示这条语句要求CPU完成什么具体操作,如MOV、ADD、SHL等。

有些指令还可以在指令助记符的前面加上前缀,实现一定的附加操作。

3.操作数字段

注释字段为可选项,该字段以分号“;”开始。

它的作用是为阅读程序的人加上一些说明性内容

注释字段不会产生机器目标代码,它不会影响程序和指令的功能。

注释字段可以是一条指令的后面部分,也可以是整个语句行。

二、伪指令语句

伪指令语句又叫命令语句。

伪指令本身并不产生对应的机器目标代码。它仅仅是告诉汇编程序对其后面的指令语句和伪指令语句的操作数应该如何处理。

一条伪指令语句可以包含四个字段。如下所示:

符号名-伪指令符-操作数(,);注释
1.符号名字段

该字段为可选项。根据伪指令的不同,符号名可以是常量名、变量名、过程名、结构名和记录名等等。

一条伪指令语句的符号名可以作其它伪指令语句或指令语句的操作数,这时它表示一个常量或存储器地址

注意:符号名后面没有冒号“:”,这是与指令语句的重要区别。

2.伪指令符字段

该字段是伪指令语句的必选项,它规定了汇编程序所要完成的具体操作。

3.操作数字段

该字段是否需要,以及需要几个是由伪指令符字段来决定。

操作数可以是一个常数(二进制、十进制、十六进制等)、字符串、常量名、变量名、标号和一些专用符号(如BYTE、FAR、PARA等)。

4.注释字段

注释字段为可选项,该字段必须以分号开始。其作用与指令语句的注释字段相同。

三、标识符

指令语句中的标号和伪指令语句中符号名统称为标识符。标识符是由若干个字符构成的。

标识符构成规则:
1.字符的个数为1~31个;
2.第一个字符必须是字母、问号、@或下划线“__”这4种字符之一;
3.从第二个字符开始,可以是字母、数字、@ 、“_”或问号“?”;
4.不能使用属于系统专用的保留字。

保留字: CPU中各寄存器名(如AX、CS等),指令助记符(如MOV、ADD),伪指令符(如SEGMENT、DB)、表达式中的运算符(如GE、EQ)以及属性操作符(如PTR、OFFSET等)


4.2 汇编语言数据

数据是指令和伪指令语句中操作数的基本组成部分。一个数据由数值和属性两部分构成。

在说明数据时不仅要指定其数值,还需说明它的属性,比如是字节数据还是字数据。

在汇编语言中常用的数据形式有:常数、变量和标号。

一、常数

常数在汇编期间其值已完全确定,并且在程序运行过程中,其值不会发生变化。

常数有以下几种形式:
1.二进制数:以字母B结尾,如01001001B
2.八进制数:以字母O或Q结尾,如631Q 254O
3. 十进制数:以字母D结尾,或者没有结尾字母。如2007D、2007。
4. 十六进制数:以字母H结尾,如3FEH,如果常数的第一个数字为字母,为了与标识符加以区别,必须在其前面冠以数字“0”。
5. 实数。一般格式为:

±整数部分• 小数部分E ±指数部分(前两部分合称尾数)

汇编程序在汇编源程序时,可以把实数转换为4字节、8字节或10字节的二进制数形式存放。

6.字符串常数:用引号(单引号或双引号)括起来的一个或多个字符,这些字符以它的ASCII码值存储在内存。

常数在程序中可以用在以下几种情况:
(1)作指令语句的源操作数
(2)在指令语句的直接寻址方式、变址(基址)寻址方式或基址变址寻址方式中作位移量。
(3)在数据定义伪指令中使用

二、变量

变量用来表示存放数据的存储单元,这些数据在程序运行期间可以被改变。

程序中以变量名的形式来访问变量,因此,可以认为变量名就是存放数据的存储单元地址。

1.变量的定义与预置

定义变量就是给变量在内存中分配一定的存储单元。也就是给这个存储单元赋与一个符号名,即变量名,同时还要将这些存储单元预置初值。

定义变量使用数据定义伪指令 DB、DW、DD、DQ和DT等。

变量定义的一般格式:

变量名(DB/DW/DD/DQ/DT)表达式1,表达式2......;定义字节变量/定义字变量/定义4字节变量/定义8字节变量/定义10字节变量

其中表达式1、表达式2是给存储单元赋的初值

当变量被定义后,就具有了以下三个属性:

(1)段属性

它表示变量存放在哪一个逻辑段中。

(2)偏移量属性(OFFSET)

它表示变量所在位置与段起始点之间的字节数。

段属性和偏移量属性就构造了变量的逻辑地址

(3)类型属性

它表示变量占用存储单元的字节数。其中DB伪指令定义的变量为字节,DW定义的变量为字,DD定义的为双字(4字节),DQ定义的为4字,DT定义的为5字。

在变量的定义语句中,给变量赋初值的表达式可以使用下面4种形式:

(1)数值表达式
(2)?表达式

不带引号的问号“?”表示可以预置任意内容。

(3)字符串表达式

对于DB伪指令,字符串为用引号括起来的不超过255个字符。给每一个字符分配一个字节单元。字符串按从左到右,将字符的ASCII编码值以地址递增的排列顺序依次存放。

对于DW伪指令可以给两个字符组成的字符串分配两个字节存储单元。
注意:两个字符的存放顺序是前一个字符放在高地址,后一字符放低地址单元。

对于DD伪指令,只能给两个字符组成的字符串分配4个字节单元。
两个字符存放在较低地址的两个字节单元中。存放顺序与DW伪指令相同,而较高地址的两个字节单元存放0。

注意:DW和DD伪指令不能用两个以上字符构成的字符串赋初值,否则将出错。

(4)DUP表达式

DUP称为重复数据操作符。
使用DUP表达式的一般格式为:

变量名(DB/DW/DD) 表达式1 DUP (表达式2)

其中:表达式1是重复的次数,表达式2是重复的内容。

DUP还可以嵌套使用,即表达式2又可以是一个带DUP的表达式。

2.变量的使用

(1)在指令语句中引

在指令语句中直接引用变量名就是对其存储单元的内容进行存取

当变量出现在变址(基址)寻址或基址变址寻址的操作数中时表示取用该变量的偏移量。

(2)在伪指令语句中引用

三、标号

标号写在一条指令的前面,它就是该指令在内存的存放地址的符号表示,也就是指令地址的别名。

标号主要用在程序中需要改变程序的执行顺序时,用来标记转移的目的地,即作转移指令的操作数

每个标号具有三属性:

(1)段属性(SEG)

它表示该标号所代表的地址在哪个逻辑段中,即段基值。

(2)偏移量属性(OFFSET)

它表示该标号所代表的地址在段内与段起点间的字节数,即地址的偏移量。

(3)距离属性(也叫类型属性)

它表示该标号可以被段内还是段间的指令调用。

NEAR(近):该标号只能作段内转移,也就是说只能是与该标号所指指令同在一个逻辑段的转移指令和调用指令才能使用它。

FAR(远):该标号可以被非本段的转移和调用指令使用。标号的距离属性可以有两种方法来指定:
a.隐含方式
当标号加在指令语句前面时,它隐含为NEAR属性。

b.用LABEL伪指令给标号指定距离属性
类型为NEAR或FAR。该语句应与指令语句连用。

SUB1_FAR与SUB1两个标号具有相同的段属性和偏移量属性,即相同的逻辑地址。被转移指令或调用指令访问时,是指同一个入口地址,但SUB1-FAR可以被其它段的指令调用。

LABEL伪指令还可以用来定义变量的属性,即改变一个变量的属性,如把字变量的高低字节作为字节变量来处理。


4.3 符号定义语句

一、等值语句

语句格式:符号名 EQU 表达式

功能:用符号名来表示EQU右边的表达式。后面的程序中一旦出现该符号名,汇编程序将把它替换成该表达式。

表达式可以是任何形式,常见的有以下几种情况。
1.常数或数值表达式

COUNT EQU 5
NUM EQU COUNT+5

2.地址表达式

ADR1 EQU DS:[BP+14]

ADR1被定义为在DS数据段中以BP作基址寻址的一个存储单元。

3.变量、寄存器名或指令助记符

CREG EQU CX;在后面的程序使用CREG就是使用CX
CBD EQU DAA;DAA为十进制调整指令。

二、等号语句

格式:符号名=表达式

等号语句与等值语句具有相同的作用。但等号语句可以对一个符号进行多次定义。

等号语句不能为助记符定义别名

注意:等值语句与等号语句都不会为符号分配存储单元。因此所定义的符号没有段、偏移量和类型等属性。


4.4 表达式与运算符

表达式是指令或伪指令语句操作数的常见形式。它由常数、变量、标号等通过操作运算符连接而成。

表达式是指令或伪指令语句操作数的常见形式。它由常数、变量、标号等通过操作运算符连接而成。

8086/8088宏汇编语言中的操作运算符非常丰富,可以分为以下五类。

一、算术运算符

+、—、*、 / 、MOD、SHL、SHR、[ ]
1.运算符“+”和“-”也可作单目运算符,表示数的正负。
2.使用“+”、“-”、“*”、和“/”运算符时,参加运算的数和运算结果都是整数。
3.“/”运算为取商的整数部分,而“MOD”运算取除法运算的余数。
4. “SHR ”和“SHL ”为逻辑移位运算符

“SHR”为右移,左边移出来的空位用0补入。
“SHL”为左移,右边移出来的空位用0补入。

注意:移位运算符与移位指令区别。 移位运算符的操作对象是某一具体的数(常数),在汇编时完成移位操作。而移位指令是对一个寄存器或存储单元内容在程序运行时执行移位操作。

5.下标运算符“[ ]”具有相加的作用

一般使用格式: 表达式1 [表达式2]

作用:将表达式1与表达式2的值相加后形成一个存储器操作数的地址。

二、逻辑运算符

逻辑运算符有NOT、AND、OR和XOR等四个,它们执行的都是按位逻辑运算。

三、关系运算符

关系运算符包括:EQ(等于)、NE(不等于)、LT(小于)、 LE(小于等于)、GT(大于)、 GE(大于等于)

关系运算符用来比较两个表达式的大小。关系运算符比较的两个表达式必须同为常数或同一逻辑段中的变量。

如果是常量的比较,则按无符号数进行比较;如果是变量的比较,则比较它们的偏移量的大小。

关系运算的结果只能是“真”(全1)或“假”(全0)

四、数值返回运算符

该类运算符有5个,它们将变量或标号的某些特征值或存储单元地址的一部分提取出来。

1.SEG运算符

作用:取变量或标号所在段的段基值。

2.OFFSET运算符

该运算符的作用是取变量或标号在段内的偏移量。

3.TYPE运算符

作用:取变量或标号的类型属性,并用数字形式表示。对变量来说就是取它的字节长度。

4.LENGTH运算符

该运算符用于取变量的长度。

如果变量是用重复数据操作符DUP说明的,则LENGTH运算取外层DUP给定的值。
如果没有用DUP说明,则LENGTH运算返回值总是1。

5.SIZE运算符

该运算符只能作用于变量,SIZE取值等于LENGTH和TYPE两个运算符返回值的乘积。

五、属性修改运算符

1.PTR运算符

使用格式:类型 PTR 地址表达式

作用: 将地址表达式所指定的标号、变量或用其它形式表示的存储器地址的类型属性修改为 “类型”所指的值。

类型可以是BYTE、WORD、DWORD、NEAR和FAR。这种修改是临时的,只在含有该运算符的语句内有效。

2.HIGH/LOW运算符

使用格式:
HIGH 表达式
LOW 表达式
这两个运算符用来将表达式的值分离出高字节和低字节。

如果表达式为一个常量,则将其分离成高8位和低8位;如果表达式是一个地址(段基值或偏移量)时,则分离出它的高字节和低字节。

注意:HIGH/LOW运算符不能用来分离一个变量、寄存器或存储器单元的高字节与低字节。

3、THIS运算符

THIS运算符一般与等值运算符EQU连用,用来定义一个变量或标号的类型属性。所定义的变量或标号的段基值和偏移量与紧跟其后的变量或标号相同。

六、运算符的优先级

https://qr.maitube.com/img/GDBHFB
先执行优先级别高的运算,再算较低级别运算;
相同优先级别的操作,按照在表达式中的顺序,从左到右进行;
可以用圆括号改变运算的顺序。


4.5 程序的段结构

8086/8088在管理内存时,按照逻辑段进行划分,不同的逻辑段可以用来存放不同目的的数据。在程序中使用四个段寄存器CS,DS,ES和SS来访问它们。

在源程序设计时,使用伪指令来定义和使用这些逻辑段。

一、段定义伪指令

一般格式:

段名 SEGMENT [定位类型] [组合类型] [‘类别名']+本段语句序列+段名 ENDS
1、段名

段名是由用户自己任意选定的,符合标识符定义规则的一个名称。

最好选用与该逻辑段用途相关的名称。如第一个数据段为DATA1,第二个数据为DATA2等。

一个段的开始与结尾用的段名必须一致。

2、定位类型

定位类型用于决定段的起始边界,即第一个可存放数据的位置(不是段基址)。它可以有4种取值。

(1)PAGE:表示该段从一个页面的边界开始

由于一个页面为256个字节,并且页面编号从0开始,因此,PAGE定位类型的段起始地址的最后8位二进制数一定为0,即以00H结尾的地址。

(2)PARA:表示该段从一个小节的边界开始

如果用户未选定位类型,则缺省为PARA。

(3)WORD:表示该段从一个偶数字节地址开始,即段起始单元地址的最后一位二进制数一定是0。
(4)BYTE:表示该段起始单元地址可以是任一地址值。

注意:定位类型为PAGE和PARA时,段起始地址与段基址相同。定位类型为WORD和BYTE时,段起始地址与段基址可能不同。

3、组合类型

组合类型说明符用来指定段与段之间的连接关系和定位。它有六种取值选择。

(1)若未指定组合类型,表示本段与其它段无连接关系。在装入内存时,本段有自己的物理段,因此有自己的段基址
(2)PUBLIC:在满足定位类型的前提下,将与该段同名的段邻接在一起,形成一个新的逻辑段,共用一个段基址。段内的所有偏移量调整为相对于新逻辑段的段基址。
(3)COMMON:产生一个覆盖段。在多个模块连接时,把该段与其它也用COMMON说明的同名段置成相同的段基址,这样可达到共享同一存储区。共享存储区的长度由同名段中最大的段确定。
(4)STACK:把所有同名段连接成一个连续段,且系统自动对SS段寄存器初始化为该连续段的段基址。并初始化堆栈指针SP。

用户程序中应至少有一个段用STACK说明,否则需要用户程序自己初始化SS和SP。

(5)AT表达式:表示本段可定位在表达式所指示的小节边界上。表达式的值也就是段基值。
(6)MEMORY:表示本段在存储器中应定位在所有其它段之后的最高地址上。如果有多个用MEMORY说明的段,则只处理第一个用MEMORY说明的段。其余的被视为COMMON
4.类别名

类别名必须用单引号引起来。所用字符串可任意选定,但它不能使用程序中的标号、变量名或其它定义的符号。

类别名必须用单引号引起来。所用字符串可任意选定,但它不能使用程序中的标号、变量名或其它定义的符号。
例:
STACK1 SEGMENT PARA STACK 'STACK0'
.....
STACK1 ENDS
DATA1 SEGMENT PARA 'DATA'
......
DATA1 ENDS
STACK2 SEGMENT PARA 'STACK0'
......
STACK2 ENDS
CODE SEGMENT PARA MEMORY
ASSUME CS:CODE,DS:DATA1,SS:STACK1
MAIN: ......
......
CODE ENDS
DATA2 SEGMENT BYTE 'DATA'
......
DATA2 ENDS
END MAIN
在进行程序设计时,如果程序不大,一般只需要定义三个段就可以了。

二、段寻址伪指令

段寻址伪指令ASSUME的作用是告诉汇编程序,在处理源程序时,定义的段与哪个寄存器关联。

ASSUME并不设置各个段寄存器的具体内容,段寄存器的值是在程序运行时设定的。

一般格式:
ASSUME并不设置各个段寄存器的具体内容,段寄存器的值是在程序运行时设定的。

其中段寄存器名为CS,DS,ES和SS四个之一,段名是用SEGMENT/ENDS伪指令定义的段名。

在一个代码段中可以有几条ASSUME伪指令,对于前面的设置,可以用ASSUME改变原来的设置。

在一个代码段中可以有几条ASSUME伪指令,对于前面的设置,可以用ASSUME改变原来的设置。

可以使用关键字NOTHING将前面的设置删除。

ASSUME ES:NOTHING;删除前面对ES与某个定义段的关联
ASSUME NOTHING;删除全部4个段寄存器的设置

三、段寄存器的装入

段寄存器的初值(段基值)装入需要用程序的方法来实现。四个段寄存器的装入方法略有不同。

1、DS和ES的装入

在程序中,使用数据传送语句来实现对DS和ES的装入。
为了改正上述程序中的错误,可以在变量DBYTE2前加一个段前缀说明即可。即:

MOV ES:DBYTE2[2], AL
2、SS的装入

SS的装入有两种方法
(1)在段定义伪指令的组合类型项中,使用STACK参数,并在段寻址伪指令ASSUME语句中把该段与SS段寄存器关联。

(2)如果在段定义伪指令的组合类型中,未使用STACK参数,或者是在程序中要调换到另一个堆栈,这时,可以使用类似于DS和ES的装入方法。

3、CS的装入

CPU在执行指令之前根据CS和IP的内容来从内存中提取指令,即必须在程序执行之前装入CS和IP的值。因此,CS和IP的初始值就不能用可执行语句来装入。

装入CS和IP一般有下面两种情况。

(1)由系统软件按照结束伪指令指定的地址装入初始的CS和IP

任何一个源程序都必须以END伪指令来结束。

其格式为:END 起始地址

起始地址可以是一个标号或表达式,它与程序中第一条指令语句前所加的标号必须一致。

END伪指令的作用是标识源程序结束和指定程序运行时的起始地址。当程序被装入内存时,系统软件根据起始地址的段基值和偏移量分别被装入CS和IP中。

(2)在程序运行期间,当执行某些指令时,CPU自动修改CS和IP,使它们指向新的代码段。

4.6 过程定义伪指令(PROC/ENDP)

在程序设计过程中,常常将具有一定功能的程序段设计成一个子程序。在MASM宏汇编程序中,用过程(PROCEDURE)来构造子程序。

过程名是子程序的名称,它被用作过程调用指令CALL的目的操作数。它类同一个标号的作用。具有段、偏移量和距离三个属性。而距离属性使用NEAR和FAR来指定,若没有指定,则隐含为NEAR。

NEAR过程只能被本段指令调用,而FAR过程可以供其它段的指令调用。

每一个过程中必须包含有返回指令RET,其作用是控制CPU从子程序中返回到调用该过程的主程序。


4.7 当前位置计数器$与定位伪指令ORG(Origin)

汇编程序在汇编源程序时,每遇到一个逻辑段,就要为其设置一个位置计数器,它用来记录该逻辑段中定义的每一个数据或每一条指令在逻辑段中的相对位置。

在源程序中,使用符号\(来表示位置计数器的当前值。因此,\)被称为当前计数器。它位于不同的位置具有不同的值。

位置计数器$在使用上完全类似变量的使用

定位伪指令ORG--用来改变位置计数器的值。

格式:ORG 数值表达式

作用:将数值表达式的值赋给当前位置计数器$ 。ORG语句为其后的数据或指令设置起始偏移量。

表达式的值必须为正值。表达式中也可以包含有当前位置计数器的现行值$。


4.8 标题伪指令TITLE

语句格式:TITLE 标题名

作用:给所在程序指定一个标题。以便在列表文件的每一页的第一行都显示这个标题。其中标题是用户任意选用的字符串,字符个数不能超过60。


4.9 从程序返回操作系统的方法

为了使程序运行结束后,能够正确地返回到操作系统,需要在程序中加上一些必要的语句。一般有以下两种方法。

一、使用程序段前缀PSP(Program Segment Prefix)实现返回

DOS系统将一个.EXE文件(可执行文件)装入内存时,在该文件的前面生成一个程序段前缀PSP,其长度为100H字节。同时让DS和ES都指向PSP的开始,而CS指向该程序的代码段,即第一条可执行指令。

为了使程序执行完后,正确返回DOS,需要做以下三个操作:

  1. 将用户程序编制成一个过程,类型为FAR;
  2. 将PSP的起始逻辑地址压栈,即将INT 20H指令的地址压栈;
  3. 在用户程序结尾处,使用一条RET指令。执行该指令将使保存在堆栈中的PSP的起始地址弹出到CS和IP中。

程序结构:
DATA SEGMENT

DATA ENDS
STACK1 SEGMENT STACK

STACK1 ENDS
CODE SEGMENT
BEGIN PROC FAR
ASSUME CS:CODE,DS:DATA,SS:STACK1
PUSH DS
MOV AX,0
PUSH AX
MOV AX,DATA
MOV DS,AX

RET
BEGIN ENDP
CODE ENDS
END BEGIN

二、使用DOS系统功能调用实现返回

执行DOS功能调用4CH,也可以控制用户程序结束,并返回DOS操作系统。
在程序结束时,使用两条指令:

MOV AH,4CH
INT 21H

代码段的结构为:
CODE SEGMENT
ASSUME CS:CODE......
BEGIN:MOV AX,DATA
MOV DS,AX

MOV AH,4CH
INT 21H
CODE ENDS
END BEGIN

posted @ 2022-03-13 13:16  屿微  阅读(50)  评论(0编辑  收藏  举报