20192411何张榕汇编语言程序设计学习笔记(1~4章)

第一章

1.1 汇编语言的一般概念

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

1.机器语言:

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

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

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

2.高级语言:

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

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

3.汇编语言:

  • 定义

    ​ 使用字母和符号来表示机器语言的命令,用十进制数或者十六进制数来表示数据,这样的计算机程序设计语言就成为汇编语言。

  • 汇编语言程序和机器语言的关系

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

  • 不同类型计算机有不同的机器指令系统和汇编语言描述

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

  1. 学习和使用汇编语言可以从根本上认识、理解计算机的工作过程。

  2. 在计算机系统中,某些功能必须用汇编语言程序来实现。(如:机器自检、系统初始化、实际的输入输出设备的操作等。)

  3. 汇编语言程序的效率高于高级语言。

    效率的含义是程序的目标代码长度和运行的速度

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

  • 进位计数制:使用一定个数的数码的组合来表示数字,即进位计数制。根据使用的数码的个数,就产生了不同的进位计数制。

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

  • 基数:每个数位上能使用的不同数码的个数成为基数。

    每个数位能取的最大数码值=基数-1。如十进制为10-1=9

  • 在书写不同进位计数制数时,为了区别,常在数的尾部用一个字母来表示。

    字母 进位计数制
    B(Binary) 二进制数
    O(Octal)或Q 八进制数
    D(Decimal) 十进制数
    H(Hexadecimal) 十六进制数
    无字母 默认十进制数
  • 各种数制间的相互转换

    • 十进制整数转换为二进制数

      (1)减权定位法:从二进制数高位起,依次用待转换的十进制数与各位权值进行比较。如果够减,则该数位系数Ki=1,同时减去该位权值,余数作为下一次比较的值;如果不够减,则Ki=0。

      例:将325转化为二进制数,直到余数为0。

      首先确定二进制数的最高位

      因为29(512)>325>28(256),因此从K8位开始比较。

      减数比较 Ki 对应二进制数
      325-256=69 K8 1
      69<128 K7 0
      69-64=5 K6 1
      5<32 K5 0
      5<16 K4 0
      5<8 K3 0
      5-4=1 K2 1
      1<2 K1 0
      1-1=0 K0 1

      所以 325D=101000101B

      (2)除基取余法:将十进制数除以基数2,其余数为二进制数的最低位,再用其商除2,其余数为次低位,反复做下去,直到商0.

      除基 余数 Ki
      325/2 1 K0
      162/2 0 K1
      81/2 1 K2
      40/2 0 K3
      20/2 0 K4
      10/2 0 K5
      5/2 1 K6
      2/2 0 K7
      1/2 1 K8

      这种转换方法同样适合于其他进制数之间的转换。

    • 十进制小数转换为二进制数

      (1)减权定位法

      例:将十进制数0.645转换为二进制数

      减权比较 Ki 对应二进制数
      0.645-0.5=0.145 K-1 1
      0.145<0.25 K-2 0
      0.145-0.125=0.02 K-3 1
      0.02<0.0625 K-4 0
      0.02<0.03125 K-5 0
      0.02-0.015625 K-6 1

      所以0.0645D=0.101001B

      (2)乘基取整法

      例:将0.8125D转换为二进制数

      乘以基数 Ki 整数部分
      0.8125*2=1.625 K-1 1
      0.625*2=1.25 K-2 1
      0.25*2=0.5 K-3 0
      0.5*2=1 K-4 1
    • 二进制整数转换为十进制数

      (1)按权相加法

      (2)逐次乘基相加法

    • 二进制小数转换为十进制数

      (1)按权相加法

      (2)逐次除基相加法

    • 二进制与八进制和十六进制间的转换

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

      例如:

      10100010B=10 (2) 100 (4) 010 (2),所以 10100010B=242Q

      10100010B=1010 (A) 0010 (2),所以 10100010B=A2 H

1.4 带符号数的表示

在一般算术表示中使用“+”和“-”来表示正数与负数,而在计算机中使用“0”和“1”来表示正数和负数。前者叫真值,后者叫机器数。

带符号的机器数可以用原码、反码和补码三种不同码制来表示,一般计算机中大多采用补码表示。

  • 原码表示

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

    8位二进制数原码的最大数为01111111(+127),最小数为11111111(-127)

    8位二进制数表示范围:-127~+127

    0的原码有两种表示形式:00000000和10000000(+0和-0)

  • 补码的表示

    • 补码定义

      带符号数X的补码表示[X]补 定义为:[X]补=M+X (Mod M)

      其中模数M根据机器数的位数而定,如位数为8则M=2^8

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

    • 由真值、原码变换为补码

      • 正数:原码与补码相同。

      • 负数:

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

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

    • 补码数的表示范围

      当位数为8,最大补码为01111111=[+127]补,最小补码为10000000=[-128]补

      0的补码只有一个,[0]补=00000000,而10000000是[-128]补,11111111=[-1]补

      对于16位数,则补码表示范围为-32768~+32767

    • 补码的加减运算

      规则:[X+Y]补=[X]补+[Y]补

      ​ [X-Y]补=[X]补-[Y]补=[X]补+[-Y]补

      ​ 其中[-Y]补是对[Y]补执行一次求补运算

      ​ 求补运算是将原数连同符号位一起(不管正负)按位求反,再在最低位加1。

1.5 字符的表示

在计算机内部,各种字符都是按一定的方式编写成二进制信息,不同的计算机以及不同的场合采用的编码形式可能不同。目前最广泛采用的是ASCII码(American Standard Code for Information Interchange)

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

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

  1. 非打印ASCII码,共33个,用于控制操作,如BEL(响铃07H),DEL(删除7FH),CR(回车,0DH),LF(换行,0AH)
  2. 可打印ASCII码共有95个,如数字符0~9,大小写字母等。

1.6 基本逻辑运算

计算机内部采用二进制数表示信息,具有物理实现容易、可靠性高的优点,且由于状态0、1正好与逻辑运算中的真、假对应,因此可以用0和1来表示逻辑变量的取值,很容易地实现各种复杂的逻辑运算。

  • "与"运算(AND)

    也叫逻辑乘。

  • "或"运算(OR)

    也叫逻辑加。

  • "非"运算

    指对逻辑变量取相反的一个逻辑值。通常是在逻辑变量上方加一横线表示。

  • "异或"运算(XOR)

    通常用⊕表示,即F=A⊕B

    当A和B相同时(同时为1或同时为0),运算结果F为0,不同时F为1。

上述四种基本逻辑运算规则用真值表示为:

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

本章主要内容:

  • IBM-PC微机基本结构
  • 8086/8088寄存器结构及其用途
  • 8086/8088系统的存储器组织结构
  • 8086/8088系统的堆栈及其操作方法

2.1 IBM-PC微机基本机构

  1. 微机的一般构成

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

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

    • 系统采用总线结构,具有较大的灵活性和扩展性。

  • 中央处理器CPU

    微型计算机中的中央处理器也叫微处理器,包括运算器和控制器。

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

  • 主存储器

    • 用来存放程序和数据的部件,由若干个存储单元构成。

    • 存储单元的多少表示存储器的容量,每个存储单元使用一个唯一的编号来标识,成为存储单元的地址。

    • 对每个存储单元内容的存和取是按照地址进行访问的。

    • 计算机存储信息的基本单位是一个二进制位,一位可存储一个二进制数0或1,每8位组成一个字节(BYTE)。

      大多计算机中,存储器的组织都是以字节为基本单位。每一个基本单位成为一个存储单元。

      指示存储单元编号的地址长度决定了存储器的最大容量,例如一个10位二进制数表示的地址,可以用来区分2^10=1024=1K个单元。

    • 习惯上将CPU与主存储器合称为主机

      除了主存储器外,一般还配置有辅助存储器,简称辅存。由于它的位置是在主机之外,因此也叫做外存。

  • 输入输出设备及接口

    • 输入设备将外部信息(程序、数据和命令)送入计算机,包括键盘、鼠标等。
    • 输出设备将计算机处理后的结果转换为人或其他系统能识别的信息形式向外输出。如显示器、打印机等。
    • 有的设备既具有输入功能又具有输出功能。如磁盘、磁带、触摸显示屏等。
    • 由于I/O设备的工作速度、工作原理以及所处理的信息格式等与主机相差很大,因此I/O设备要通过I/O接口才能与系统总线连接。
    • I/O接口是主机与I/O设备之间设置的逻辑控制部件。通过它实现主机与I/O设备间的信息传送。
  • 系统总线

    • 系统总线将CPU、存储器和I/O设备连接起来,实现各大部件之间的各种信息传送。
    • 系统总线包括地址总线数据总线控制总线三组。它们分别用于传送不同信息。
  1. Intel 8086/8088 CPU的功能结构

    汇编语言是由一系列的指令(指令序列)构成。

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

    CPU执行指令序列就是重复执行以下两个步骤:

    • 从存储器中取指令
    • 执行指令规定的操作

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

  • 串行方式(采用串行工作方式的计算机其运行速度较慢)

    CPU 取指 执行 存数 取指 执行 取指 取数 执行
    系统总线

    特点

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

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

  • 指令流水线方式(采用指令流水线工作方式的计算机工作效率较高)

    指令流水线结构最先出现在Intel公司的8086/8088CPU中。

    8086/8088CPU被分成两个单元

    (1)执行单元EU:分析与执行指令

    (2)总线接口单元BIU:负责CPU与存储器、I/O的信息传送。

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

通用寄存器8个:累加器AX(AH,AL)、基址寄存器BX(BH,BL)、计数寄存器CX(CH,CL)、数据寄存器DX(DH,DL)、堆栈寄存器SP、基址指针BP、源变址寄存器SI、目的变址寄存器DI;

控制寄存器2个:指令指针IP、标志寄存器FLAGS;

段寄存器4个:代码段寄存器CS、数据段寄存器DS、附加段寄存器ES、堆栈段寄存器SS。

  • 通用寄存器

    • 数据寄存器

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

      • MOV AX,BX;将BX的内容送到AX中

        ADD CH,DH;将DH和CH的内容相加,结果送到CH

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

      • 例如,在循环指令LOOP中,CX被隐含指定作循环次数计数用。

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

      • 例如,移位指令 SHL AX,CL;CL被固定用作移位次数。
    • 指针寄存器

      指针寄存器有堆栈指针SP和基址指针BP;它们一般用来存放16位地址,在形成20位的物理地址时常被作为偏移量使用。

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

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

    • 变址寄存器

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

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

      同BP寄存器一样,SI和DI也可以用作通用数据寄存器存放操作数和运算结果。

  • 段寄存器

    • 8086/8088CPU在使用存储器时,将它划分成若干个段。
    • 每个段用来存放不同的内容,如程序代码、数据等等。
    • 每个存储段用一个段寄存器来指明该段的起始位置(也叫段基址)。

    CPU在访问存储器时必须指明两个内容:

    (1)所访问的存储单元属于哪个段,即指明使用的段寄存器。

    (2)该存储单元与段起始地址(段基址)的相距多少,即偏移量。

    ——在程序设计中,一个程序将存储器划分成多少个存储段是任意的。但在程序运行的任何时刻最多只有4个段,分别由CS、DS、ES和SS指定。

  • 指令指针IP

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

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

    • IP内容不能被直接访问,即不能用指令去读IP的值,也不能用指令给它赋值。但是可以通过某些指令的执行而自动修改IP的内容。
      • 例如下面两条指令就可以自动改变IP寄存器的内容。
        • 转移指令将指令中的目的地址的偏移量送入IP
        • 子程序调用指令CALL,将IP原有内容自动压入堆栈,而将子程序的入口地址偏移量自动送入IP,而返回指令RET,又自动从堆栈中弹回原有IP的内容。
  • 标志寄存器

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

    8086/8088CPU中标志寄存器长度为16位,但只定义了其中的9位。

    15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
    OF DF IF TF SF ZF AF PF CF
    溢出位 方向位 中断允许位 单步标志位 符号位 零值位 辅助进位位 奇偶位 进位位

    标志位分别为:

    • 状态标志:CF,PF,AF,ZF,SF,OF
    • 控制标志:TF,DF,IF
    1. 进位标志位CF

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

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

    2. 奇偶标志位PF

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

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

    3. 辅助进位标志位AF

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

      注意:AF只反映运算结果低8位,与操作数长度无关。

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

2.3 存储器组织结构

  • 存储器的组成

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

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

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

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

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

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

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

  • 存储器的段结构

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

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

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

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

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

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

      第1小节 00000H 00001H 00002H …… 0000FH
      第2小节 00010H 00011H 00012H …… 0001FH
      …… …… …… …… …… ……
      第65535小节 FFFE0H FFFE1H FFFE2H …… FFFEFH
      第65536小节 FFFF0H FFFF1H FFFF2H …… FFFFFH

      可以看出,每个小节的首地址最低位必为0(16进制数表示)。因此段基址只能是上述64K个小节首址之一。

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

      逻辑段——在汇编语言源程序中设置的段

      内存中的一个物理存储单元可以映象到一个或多个逻辑段中。

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

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

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

  • 逻辑地址与物理地址及其对应关系

    1. 物理地址

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

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

    2. 逻辑地址

      包括两个部分:段基值和偏移量

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

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

      逻辑地址表示方法——段基值:偏移量

      • 例如,3267H:0A0H;表示该逻辑单元位于段起始地址为32670H,段内偏移量为0A0H个字节。
    3. 逻辑地址转换为物理地址

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

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

      • 逻辑地址:段基值0915H,偏移量003AH;

        0915H左移4位→0915 0H,再与003AH相加→0918A H.

    4. 逻辑地址的来源

      在程序的执行过程中,CPU根据不同操作类型访问存储器,其逻辑地址中段基值和偏移量的来源是不一样的。

2.4 堆栈及其操作方法

堆栈是一个特定的存储区,访问该存储区一般需要按照专门的规则进行操作。

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

  • 堆栈的构造

    一般分为专用堆栈存储器软件堆栈

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

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

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

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

    • 为了指示现在堆栈中存放数据位置,通常设置一个寄存器来指示栈顶位置。其内容就像一个指针一样,因此被称为堆栈指针SP(Stack Pinter)。
      • SP的内容始终指向栈顶单元
      • 堆栈中数据进出都由SP来控制

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

  • 8086/8088堆栈的组织

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

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

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

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

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

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

  • 堆栈操作

    1. 设置堆栈

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

      • 例如:

        STACK1 SEGMEMT PARA STACK

        ​ DB 100 DUP (0)

        STACK1 ENDS

        第一行中的PARA STACK就是用来说明本段为堆栈段。

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

    2. 进栈PUSH

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

      • PUSH AX;将寄存器AX的内容压入堆栈

        PUSH DS;将寄存器DS的内容压入堆栈

        PUSH DATA-WORD;将字存储单元DATA-WORD压入堆栈

        PUSHF;将标志寄存器的内容压入堆栈

      • 进栈的执行过程

        1. 首先将堆栈指针SP减2,即指向一个空的堆栈字单元

          SP<=(SP)-2

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

          SP<=数据

    3. 出栈POP

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

      • 例如:

        POP AX;将栈顶字单元内容弹出到AX

        POP DS;将栈顶字单元内容弹出到DS

        POP DATA-WORD;将栈顶字单元内容弹出到DATA-WORD存储

        POPF;将栈顶字单元内容送回到标志寄存器F

      • 出栈的操作过程

        1. 将SP指向的字单元(即栈顶字单元)内容送往指定的寄存器或存储器。

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

        2. 堆栈指针SP内容加2,即:SP<=(SP)+2

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

本章主要内容:

  • 8086/8088的各种寻址方式
  • 8086/8088的传送类指令
  • 8086/8088的基本算术类指令
  • 8086/8088移位指令
  • 8086/8088逻辑指令
  • 8086/8088处理器控制类指令
  • 8086/8088指令编码

3.1 寻址方式

一条指令通常由两大部分构成:操作码、操作数

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

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

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

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

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

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

    • 例:

      • MOV AH,20H

        表示将8位立即数20H送入AH中

      • MOV AX,20A0H

        表示将16位立即数20A0H送入AX中

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

    • 注意:立即数只能作为源操作数,而不能作为目的操作数。
  • 寄存器寻址

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

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

后面几种寻址方式其操作数都是在存储器中,它们的主要区别就是操作数在内存中存放地址的形成方法不同。

一个存储单元逻辑地址表示形式:段基值:偏移量

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

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

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

  • 直接寻址

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

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

    • 用符号表示

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

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

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

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

    • 用常数表示

      • MOV AX, DS: [64H]

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

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

  • 寄存器间接寻址

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

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

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

    • 指示存储器所在段的段寄存器可以省略,当指令中使用BP寄存器,则隐含使用SS段寄存器;其余情况隐含使用DS段寄存器。(BP对应SS,其余对应DS)
      • 例如:MOV AX, [BX] => MOV AX, DS: [BX]

        ​ MOV BH, [BP] => MOV BH, SS: [BP]

  • 基址寻址/变址寻址

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

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

    • 隐含使用规则和寄存器间接寻址方式相同

      • MOV AX, 10H [SI] => MOV AX, DS: 10H[SI]

        MOV TABLE [DI], AL => MOV DS: TABLE[DI], AL

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

    这两种寻址方式只需要通过改变寄存器的内容就可用一条指令访问不同存储单元,并且由于增加了一个位移量分量,因此它们能够很方便地访问数组和表格数据。

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

  • 基址变址寻址

    操作数的有效地址是三个地址分量之和,即:EA=基址+变址+位移量

    隐含使用规则同上:BP对应SS,其余对应DS

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

  • 串操作寻址方式

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

    • 在寻找源操作数时,隐含使用SI作为地址指针
    • 在寻找目的串时,隐含使用DI作为地址指针
    • 在串操作完成后,自动对SI和DI进行修改,使它们指向下一个操作数
  • I/O端口寻址

    分为两种:

    • 存储器编址方法

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

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

    • I/O端口编址方法

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

      8086/8088系统就是采用这种方式。可以最多访问64K个字节端口或32K个端口,用专门的IN指令和OUT指令访问。寻址方式有以下2种

      • 直接端口寻址

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

        直接端口寻址可访问的端口数为0~255个。例如:IN AL, 25H

      • 寄存器间接端口寻址

        把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)寄存器之间的传送

        • 例:

          MOV AH, CH

          MOV DS, AX

          MOV CS, AX(错误×)

          注意:段寄存器CS只能作为源操作数,不能作目的操作数。(CS涉及到代码)

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

      • 综合起来,MOV指令在使用时需注意以下几个问题:

        (1)立即数只能作源操作数,且它不能传送给段寄存器。

        (2)段寄存器CS只能作源操作数,段寄存器之间不能直接传送。

        (3)存储单元之间不能直接传送数据

        (4)MOV指令不影响标志位

    2. 交换指令

      指令格式:XCHG DEST,SRC

      作用:源操作数和目的操作数两者内容相互交换,即:(DEST)<=>(SRC)。

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

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

      • 例 XCHG AX,BX; XCHG AH,CH

      为了完成两个存储单元(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寄存器或堆栈为操作数。

      • 取标志寄存器指令

        指令格式:LAHF

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

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

      • 存储标志寄存器指令

        指令格式:SAHF

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

      • 标志进栈指令

        指令格式:PUSHF

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

      • 标志出栈指令

        指令格式:POPF

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

    4. 地址传送指令

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

      • 装入有效地址

        格式:LEA DEST,SRC

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

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

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

        • 例1:LEA AX,[BX] [SI]

          源操作数使用的是基址变址寻址方式,它所形成的有效地址就是BX的内容加上SI的内容。即
          AX<=(BX)+(SI)

          注意:它不是将BX和SI所寻址的存储单元的内容送入AX。

      • 装入地址指针指令

        格式:LDS DEST,SRC
        LES DEST,SRC

        其中: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指令可以是字节操作数相加,也可以是操作数相加。

      • 例 分析下列各指令功能

        (1) ADD AX,CX
        功能:将寄存器AX的内容与CX的内容相加,结果传送到AX中
        (2) ADD AH,DATA_BYTE
        功能:将由直接寻址方式所指示的存储单元的内容与AH内容相加,结果送回AH中。
        (3) ADD CX,10H
        功能:将常数10H加入到CX中。为操作数指令.
        (4) ADD AX, [BX] [SI]
        功能:将由基址变址寻址方式所指示的存储单元的内容加入到AX中。

    2. 带进位加法指令

      指令格式:ADC DEST,SRC

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

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

      注意:参加运算的进位CF是本条指令执行之前的值。

      用ADC指令可实现数据长度大于16位的两数相加

      • 例:计算12349678H+377425H

        MOV AX, 1234H
        MOV BX, 9678H
        ADD BX,7425H
        ADC AX,37H

        指令执行后,结果的高16位在AX,低16位在BX中。

    3. 加1指令

      指令格式:INC DEST

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

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

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

      • 例:INC CL
        INC SI
        INC COUNT
    4. 减法指令

      指令格式:SUB DEST,SRC

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

      即:DEST<=(DEST)-(SRC)

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

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

      • 例:SUB AX,BX
        SUB AH,10H
        SUB DX,DA-WORD
        SUB DA-BYTE,BL

      注意减法指令对借位标志的影响,若采用变减为加的运算方法,则产生的进位与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的影响:只有当操作数为零时,进位标志CF被置零,其它情况都被置1.

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

      • 例1 设AL中存放一个正数(AL)=25H,BL中存放一个负数:(BL)=-58H,求它们的相反数,即负数。

        NEG AL

        NEG BL

        指令执行后,(AL)=-25H=11011011B; (BL)= 58H=01011000B

      • 例2 一个32位带符号数存放在DAW开始的四个字节存储单元中,DAW字节单元存放最低字节。求该数的负数,并存入原存储单元中。

        NEG WORD PTR DAW

        MOV AX,0

        SBB AX,DAW+2

        MOV DAW+2,AX

        结果的低16位由指令NEG直接得到,而高16位还要考虑低16位产生的借位,因此使用了带借位的指令SBB。

  • 位操作类指令

    • 逻辑运算指令

      逻辑运算指令共有4条,它们的指令格式分别是:

      • 逻辑“与”指令 AND DEST,SRC
      • 逻辑“或”指令 OR DEST,SRC
      • 逻辑“异或”指令 XOR DEST,SRC
      • 逻辑“非”指令 NOT DEST

      这4条指令都是执行按位逻辑运算,如下表所示:

      DEST SRC AND OR XOR NOT
      0 0 0 0 0 1
      0 1 0 1 1 1
      1 0 0 1 1 0
      1 1 1 1 0 0

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

      逻辑指令对标志位的影响:

      • NOT指令对标志无影响。而其余三条指令将根据结果影响SF、ZF和PF,而CF和OF总 是置0,AF为不确定。
      • 逻辑运算指令除用来实现各种逻辑运算之外,还常用于对字节或字数据的某些位的组合、分离或位设置。

      例子:

      • AND AH,0F0H;分离出AH中的高4位.

        AND AH,0FH;分离出AH中的低4位

        OR AH,01H;将AH中最低位置1

        AND AL,7FH;将AL的最高位置0

        XOR AX,0FFH;将AX的低字节变反

        XOR BX,8000H;将BX的符号位变反

      • 下面的程序段将中断标志位IF清0,其它标志位保持不变。

        PUSHF; 将标志寄存器压栈

        POP AX; 将栈中的标志字送AX

        AND AX,0FDFFH; 将AX的第9位清0

        PUSH AX; 将第9位清0后的AX内容压栈

        POPF 将堆栈中的值返回到标志寄存器

    • 测试指令

      指令格式:TEST DEST,SRC

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

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

      例:

      • TEST AL,01

        JZ ZERO

        …….

        ZERO: …...

        该程序段检查AL寄存器的最低位是否为0,如果为0,则程序转移到ZERO处执行。

      • LAHF

        TEST AH,04H

        JZ TARGET

        该程序段检查标志寄存器的PF位(第2位)是否为0,如果为0,则执行后标志ZF为1。因此通过测试ZF标志即可。

    • 移位/循环移位指令

      这一类指令共有8条,分为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。

      例子:

      • SAL AX,1;将AX的内容左移1位,其中最高位移入CF中,而低位补0.
      • MOV CL,2
        SAR AX,CL;将AX的内容算术右移2位。

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

      • MOV AL,10010011B

        SHL AL,1 ;执行后CF标志为1

        SAR AL,1 ;执行后CF标志为0

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

      对溢出标志位OF的影响:

      移位次数为1时,移位前后操作数的符号位发生变化,则OF被置1,否则置0。移位次数大于1时,OF不确定。

      • MOV AL,11000000B;(AL)=-64

        MOV BL,01111111B; (BL)=127

        SAL AL, 1 ; (AL)=10000000B=-128,OF=0

        SAL BL, 1 ; (BL)=11111110B=-2, OF=1

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

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

      • 设AX中存放一个带符号数,若要实现(AX) ×5÷2,可由以下几条指令完成。

        MOV DX,AX

        SAL AX,1

        SAL AX,1

        ADD AX,DX

        SAR AX,1

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

      • 下面程序段对从存储单元M开始的三字数据执行左移一位。

        SAL M,1

        RCL M+2,1

        RCL M+4,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

    • 与外部事件同步的指令

      HLT ;暂停指令

      WAIT ;等待指令

      ESC ;外部协处理器指令前缀

      LOCK ;总线锁定指令

    • 空操作指令 NOP

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

3.3 指令编码

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

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

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

1.双操作数指令编码格式; 2.单操作数指令编码格式;

3.与AX或AL有关的指令编码格式; 4.其它指令编码格式;

  • 双操作数指令编码格式

    对于象MOV、ADD、AND等双操作数指令,操作数可以是以下两种情形:

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

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

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

    1. 操作特征部分

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

      (1)OPCODE:操作码字段

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

      • 例如

        操作码 指令 目的操作数 源操作数
        100010 MOV REG R/M
        1100011 MOV R/M Imm
        000000 ADD REG R/M
        001000 AND R/M REG
        1000000 AND R/M Imm
        1000000 OR R/M Imm

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

        前面例子中的最后两条指令,虽然其OPCODE字段相同,但它们的辅助操作码字段不同。

      (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字段决定操作数是字或是字节。

      ​ 它们配合使用可以有16种组合,也即可以分别指定16个寄存器之一。如下表所示:

      REG 000 001 010 011 100 101 110 111
      W=0 AL CL DL BL AH CH DH BH
      W=1 AX CX DX BX SP BP SI DI

      ​ 如果REG字段被用于指定段寄存器(用于MOV指令) ,则它的编码与指定的段寄存器如下。

      REG 000 001 010 011
      段寄存器 ES CS SS DS

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

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

      ​ MOD、R/M和W字段共同确定操作数的寻址方式和所使用的寄存器,如下表所示。

      MOD👉 存储器- 有效地址- 计算方法 寄存器- 方式(11)
      R/M👇 00 01 10 w=0 w=1
      000 (BX)+(SI) (BX)+(SI)+disp8 (BX)+(SI)+disp16 AL AX
      001 (BX)+(DI) (BX)+(DI)+disp8 (BX)+(DI)+disp16 CL CX
      010 (BP)+(SI) (BP)+(DI)+disp8 (BP)+(DI)+disp16 DL DX
      011 (BP)+(DI) (BP)+(SI)+disp8 (BP)+(SI)+disp16 BL BX
      100 (SI) (SI)+disp8 (SI)+disp16 AH SP
      101 (DI) (DI)+disp8 (DI)+disp16 CH BP
      110 disp16 (BP)+disp8 (BP)+disp16 DH SI
      111 (BX) (BX)+disp8 (BX)+disp16 BH DI

      注意:在表中没有使用BP作寄存器间接寻址方式,如果在指令中使用了[BP],则将其汇编为[BP+0],即基址寻址。

      ​ 当MOD=11时,操作数为16个寄存器之一的内容。

      ​ 当MOD=00,01,11时,操作数为存储器单元,可有24种有效地址EA计算方法。disp8和disp16分别为8位和16位位移量

    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字段给出。

  • 其它指令编码格式

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

第四章 汇编语言程序格式

本章主要内容:

  • 汇编语言语句种类及其格式
  • 汇编语言数据
  • 符号定义语句
  • 表达式与运算符
  • 程序的段结构
  • 过程定义伪指令
  • 当前位置计数器$与定位伪指令
  • 从程序返回操作系统的方法

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

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

  • 指令语句

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

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

    1. 标号字段

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

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

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

    2. 指令助记符字段

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

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

    3. 操作数字段

      一条指令可以有一个操作数、两个操作数或者无操作数。

      如ADD、MOV指令需要两个操作数,INC、NOT 指令只需一个操作数,而CLC指令不需要操作数。

    4. 注释字段

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

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

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

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

  • 伪指令语句

    伪指令语句又叫命令语句

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

    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等。

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

      (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)在伪指令语句中引用

    3. 标号

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

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

      每个标号具有三属性

      (1)段属性(SEG):它表示该标号所代表的地址在哪个逻辑段中,即段基值。

      (2)偏移量属性(OFFSET):它表示该标号所代表的地址在段内与段起点间的字节 数,即地址的偏移量。

      (3)距离属性(也叫类型属性):它表示该标号可以被段内还是段间的指令调用。

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

      FAR(远): 该标号可以被非本段的转移和调用指令使用。
      标号的距离属性可以有两种方法来指定:

      a.隐含方式:当标号加在指令语句前面时,它隐含为NEAR属性。

      b.用LABEL伪指令给标号指定距离属性

      ​ 格式: 标号名 LABEL 类型

      ​ 类型为NEAR或FAR。该语句应与指令语句连用。

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

4.3 符号定义语句

在源程序设计中,使用符号定义语句可以将常数或表 达式等内容用某个指定的符号来表示。在8086/8088汇编语 言中有两种符号定义语句。

  • 等值语句

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

    功能:用符号名来表示EQU右边的表达式。后面的程序中 一旦出现该符号名,汇编程序将把它替换成该表达式。
    表达式可以是任何形式,常见的有以下几种情况。

    1.常数或数值表达式

    2.地址表达式

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

    注意:在同一源程序中,同一符号不能用EQU定义多次。

  • 等号语句

    格式:符号名=表达式

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

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

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连用,用来定 义一个变量或标号的类型属性所定义的变量或标号的段基值和偏移量与紧跟其后的变量或标号相同

六、运算符的优先级

在一个表达式中如果存在多个运算符时,在计算时就有先
后顺序问题。不同的运算符具有不同的运算优先级别。

汇编程序在计算表达式时,按以下规则进行运算。

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

4.5 程序的段结构

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

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

一、段定义伪指令

伪指令SEGMENT和ENDS用于定义一个逻辑段。使
用时必须配对,分别表示定义的开始与结束。

一般格式:

段名 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. 类别名

    类别名为某一个段或几个相同类型段设定的类型名称。系 统在进行连接处理时,把类别名相同的段存放在相邻的存储 区,但段的划分与使用仍按原来的设定。

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

    在定义一个段时,段名是必须有的项,而定位类型、组 合类型和类别名三个参数是可选项。各个参数之间用空格分 隔。各参数之间的顺序不能改变。

二、段寻址伪指令

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

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

一般格式 :ASSUME 段寄存器名:段名,段寄存器名:段名,......

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

  • 在一个代码段中可以有几条ASSUME伪指令,对于前面的 设置,可以用ASSUME改变原来的设置。
  • 一条ASSUME语句不一定设置全部段寄存器,可以选择其中 一个或几个段寄存器。
  • 可以使用关键字NOTHING将前面的设置删除。

三、段寄存器的装入

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

  1. DS和ES的装入

    在程序中,使用数据传送语句来实现对DS和ES的装入。

  2. SS的装入

    SS的装入有两种方法

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

    SS将被自动装入STACK1段的段基值,堆栈指针SP也 将指向堆栈底部+2的存储单元。上例中(SP)=40H。

    (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)来构造子程序。

过程定义伪指令格式如下:

过程名 PROC [NEAR/FAR]
......
RET
......
过程名 ENDP

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

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

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

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

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

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

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

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

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

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

4.8 标题伪指令TITLE

语句格式:TITLE 标题名

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

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

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

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

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

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

  1. 将用户程序编制成一个过程,类型为FAR;

  2. 将PSP的起始逻辑地址压栈,即将INT 20H指令的地址压栈;

  3. 在用户程序结尾处,使用一条RET指令。执行该指令将使 保存在堆栈中的PSP的起始地址弹出到CS和IP中。

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

执行DOS功能调用4CH,也可以控制用户程序结束, 并返回DOS操作系统。

在程序结束时,使用两条指令:MOV AH,4CH

​ INT 21H

posted @ 2022-03-13 15:08  20192411  阅读(45)  评论(0编辑  收藏  举报