20192425苏祺汇编程序设计前四章学习笔记

第一次作业 缓冲区溢出之汇编程序设计基础

1.1.1前言

1.1.2汇编语言的一般概念

汇编语言(Assembly Language)是任何一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言,亦称为符号语言。在汇编语言中,用助记符代替机器指令的操作码,用地址符号或标号代替指令或操作数的地址。在不同的设备中,汇编语言对应着不同的机器语言指令集,通过汇编过程转换成机器指令。特定的汇编语言和特定的机器语言指令集是一一对应的,不同平台之间不可直接移植。

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

  • 嵌入式驱动开发

  • 反汇编源程序理解其本质

  • 在应对恶意代码时,需要反编译,看汇编代码

  • 可以轻松的读取存储器状态以及硬件I/O接口情况

  • 编写的代码因为少了很多编译的环节,可以能够准确的被执行

  • 作为一种低级语言,可扩展性很高


汇编语言的主体是汇编指令。

汇编指令和机器指令的差别在于指令的表示方法上。汇编指令是机器指令便于记忆的书写格式。

汇编指令是机器指令的助记符。


IBM-PC微机的功能结构

IBM-PC微机基本结构

微机的一般构成

  • 微机的一般构成:运算器、控制器、存储器、输入设备和输出设备

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

  • 系统采用总线结构,系统总线分为地址总线、数据总线、控制总线三类

  • 主存储器:用来存放程序和数据的部件,由若干存储单元组成,对每个存储单元按照地址进行访问

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

  • 除主存外,计算机一般还配置辅助存储器,也成为外存

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

  • 输入输出设备及接口

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


Intel 8086/8088 CPU的功能结构

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

CPU执行指令序列就是重复“取指令——执行指令”两个步骤

  • 串行方式

    特点:系统总线空闲时间较多

  • 指令流水线方式

    执行单元(EU)与总线接口单元(BIU)

    特点:减少了系统总线的空闲时间


通用寄存器

数据寄存器

包括AX、BX、CX、DX四个寄存器,它们既可以是16位寄存器,也可以当作8个独立的8位寄存器使用。存放操作数或运算的结果。

例如:MOV AX,BX; 将BX的内容送到AX中。

ADD CH,DH; 将DH和CH的内容相加,结果放入CH。

在某些指令中,不需要明确指出使用的寄存器名,隐含使用对应寄存器。例如在循环指令LOOP中,CX被隐含指定作循环次数计数用;移位指令SHL AX,CL;CL被固定用作移位次数。

指针寄存器

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

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

BP指针——被用来指向堆栈段内某一存储单元。

变址寄存器

有两个16位的变址寄存器SI(源变址寄存器)和DI(目的变址寄存器),一般被用来作地址指针,也可以作为通用数据寄存器存放操作数和运算结果


段寄存器

代码段,数据段,堆栈段,附加段

地址增长方向:从上往下

CPU在访问存储器时必须指明:

·所访问的存储单元属于哪个段

·该存储单元与段基址的偏移量

指令指针IP

指令指针寄存器IP(X86型CPU)相当于ARM型CPU中的程序计数器PC,用于控制程序中指令的执行顺序。

正常运行时,IP中含有BIU要取的下一条指令(字节)的偏移地址,一般情况下,每从内存中存取一次指令码,IP就自动加1,从而保证指令的顺序执行。

IP实际上是指令机器码存放内存单元的地址指针,IP的内容可以被转移类指令(如JMP)强迫改写,以改变程序执行的顺序。

注意!我们编制的程序不能直接访问IP,即不能用指令去取出IP的值或给IP设定值(如不能用mov指令给IP赋值)。

标志寄存器

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

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

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

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

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

  • 立即数操作数——操作数在指令代码中提供。立即数可以是8位,也可以是16位
  • 寄存器操作数——操作数在CPU的通用寄存器或段寄存器中
  • 存储器操作数——操作数在内存的存储单元中
  • I/O端口操作数—— 操作数在输入/输出接口的寄存器中

存储器存储单元的逻辑地址由端基值和偏移量组成。

  • 位移量:指令中直接给出的一个8位或16位数,一般源程序中以操作数名字(变量名或标号)的形式出现。

  • 基址:由基址寄存器BX或基址指针BP提供的内容。

  • 变址:由源变址寄存器SI或目的变址寄存器DI提供的内容。


立即数寻址

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

寄存器寻址

寄存器寻址方式是指指令中所需的操作数在CPU的某个 寄存器中。寄存器可以是8位或16位通用寄存器,或者是段寄存器。

直接寻址

指令由操作码+操作数组成,操作数的有效地址EA只有位移量地址分量。

(1)用符号表示
例:MOV BX, VAR =>MOV BX, DS: VAR 它表示将数据段中,偏移了VAR个字节距离的字单元 内容送到寄存器BX中。

(2)用常数表示
例:MOV AX, DS: [64H]
它表示从当前数据段开始,偏移100个字节的字单元内容 送到AX中。
注意:用常数表示时,必须用方括号括起来。段寄存器不能省略。

寄存器间接寻址

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

基址寻址和变址寻址

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

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

基址变址寻址

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

当基址选用BX时隐含使用段寄存器DS,而选用BP时 则隐含使用段寄存器SS。

串操作寻址方式

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

I/O端口寻址

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

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

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

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

    寄存器间接端口寻址
    寄存器间接端口寻址:把I/O端口的地址先送到DX中,用 DX作间接寻址寄存器。
    如果访问的端口地址值大于255,则必须用I/O端口的间 接寻址方式。

指令系统

一种计算机所能执行的各种类型的指令的集合称为该计算机的指令系统。


Intel8086/8088CPU指令系统的指令可以分为六大类:

1.传送类指令

2.算术运算类指令

3.位操作类指令

4.串操作类指令

5.程序转移类指令

6.处理器控制类指令


从指令的格式划分,一般可以分为三种:

1.双操作数指令:OPR DEST SRC

2.单操作数指令:OPR DEST

3.无操作数指令:OPR

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

传送类指令

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

  • 通用数据传送指令

    指令格式:MOV DEST,SRC

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

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

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

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

    MOV指令可以分为以下几种情况:

      (1)立即数传送到通用寄存器或存储单元
      例:MOV AH,10H
      注意:立即数只能作为源操作数,立即数不能传送给段寄存器。
    
      (2)寄存器之间的传送
      例:MOV AH,CH
      MOV AX,CS;正确
      MOV CS,AX;错误
      注意:段寄存器CS只能作源操作数,不能作目的操作数。
      
      (3)寄存器与存储单元之间传送
    

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

      (1)立即数只能作源操作数,且它不能传送给段寄存器。
      (2)段寄存器CS只能作源操作数,段寄存器之间不能直接传送。
      (3)存储单元之间不能直接传送数据
      (4)MOV指令不影响标志位
    
  • 交换指令

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

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

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

  • 标志传送指令

    (1)取标志寄存器指令

    指令格式: LAHIF

    作用:将标志寄存器的低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.

  • 地址传送指令

    将存储单元的地址送寄存器。

    (1)装入有效地址

    格式:LEA,DEST,SRC

    源操作数必须是一个地址,目的是一个16位通用寄存器。作用是将SRC存储单元地址中的偏移量,即有效地址EA传送到一个16位通用寄存器中。指令执行对标志寄存器各位无影响。

    (2)装入地址指针指令

    格式:LDS DEST,SRC/LES DEST,SRC

  • 算术运算类指令

    1)加法指令:指令格式:ADD
    DEST,SRC。DEST只能是通用寄存器或存储器操作数,不能是立即数。
    DEST和SRC不能都为存储器操作数。可以是字节操作数相加,也可以是字操作数相加。

    2)带进位加法指令:指令格式:ADC DEST,SRC。与ADD不同的是结果要加上进位标志CF的值。注意CF是本条指令执行之前的值。

    3)加1指令:指令格式:INC
    DEST,目的操作数可以是任意的8位16位通用寄存器或存储器操作数,它不影响CF,INC指令主要用于某些计数器的计数和修改地址指针。

    4)减法指令:指令格式:SUB
    DEST,SRC,,目的-源,再放入目的地址。不能同时为存储器操作数减法指令对借位标志的影响,若采用变减为加的运算方法,则产生的进位与CF标志结果相反。

    5)带借位减法:指令格式:SBB DEST,SRC,其实就是多减了一个CF和带进位加法类似。

    6)减1指令:指令格式:DEC DEST

    7)求负数指令:指令格式:NEG
    DEST,也叫做取补指令。对进位CF的影响:只有当操作数为0的时候,CF才被置0。当字节操作数为-128,或字操作数为-32768时,执行NEG指令的结果操作数将无变化,但OF被置1.
    加减乘除指令,这些指令可以对字节数据或字数据进行运算,参加运算的可以是无符号数,也可以是带符号数,带符号数用补码表示。

  • 位操作类指令

    (1)逻辑运算指令,逻辑运算指令共有4条,它们的指令格式分别是逻辑“与”指令:AND
    DEST,SRC

    逻辑“或”指令:OR DEST,SRC

    逻辑“异或”指令:XOR DEST,SRC

    逻辑“非”指令:NOT DEST

    这四条指令都是执行按位逻辑运算。

    逻辑指令对标志位的影响:NOT对标志无影响,而其余三条指令将根据结果影响SF、ZF和PF,而CF和OF总是置0,AF为不确定。可以对一些数分离其高位或低位,或者把最低位置1,最高位置0,取反等等。

    (2)测试指令:TEST
    DEST,SRC。与AND类似,但是运算的结果不送入目的操作数。主要用于测试某一操作数的一位或几位的状态。

    (3)移位/循环移位指令

  • 处理器控制类指令

    (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中断标志:CTI

    (2)与外部事件同步的指令:ESC,HLT,WAIT,LOCK

    (3)空操作指令:NOP,相当于延时,三个CPU的时钟周期。

指令编码

双操作数指令编码格式

操作特征部分

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

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

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

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


寻址特征部分
当d=l时,则目的操作数由REG字段确定,而源操作 数由MOD和R/M字段确定。
当d=0时,则目的操作数由MOD和R/M字段确定,而 源操作数由REG字段确定。
位移量部分

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

  • 没有位移量
  • 1字节位移量disp8
  • 2字节位移量displ6
立即数部分

如果指令的源操作数为立即数,则指令编码中包含 有该部分。它总是位于指令编码的最后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字段给岀。

其它指令编码格式

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


第四章 汇编语言程序格式

通用数据传送指令

汇编语言由以下3类组成

  • 汇编指令(机器码的助记符)
  • 伪指令(由编译器执行)
  • 其他符号(由编译器识别)

内部总线实现CPU内部各个期间之间的联系

外部总线实现CPU和主板上其他器件的联系


汇编语言程序格式

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


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

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


伪指令是由编译器来执行的指令,编译器根据伪指令来进行相关的编译工作。


segment和ends是一对成对使用的伪指令。

end是一个汇编程序的结束标记。


程序返回:

```
mov ax,4c00H
int 21H
```

汇编语言数据

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

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


常数在程序中可以用在以下几种情况

  • 作指令语句的源操作数

  • 在指令语句的直接寻址方式、变址(基址)寻址方式或基址变址寻址方式中作位移量

  • 在数据定义伪指令中使用


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

可以认为变量名就是存放数据的存储单元地址

变量的使用   

(1)在指令语句中引用
当变量出现在变址(基址)寻址或基址变址寻址的操作数中时表示取用该变量的偏移量。(2)在伪指令语句中引用
它表示取变量地址的偏移量

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

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

符号定义语句

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


等值语句

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

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

1.常数或数值表达式
2.地址表达式
3.变量、寄存器名或指令助记符

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


等号语句

格式:符号名=表达式

可以对一个符号进行多次定义。 不能为助记符定义别名


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


表达式与运算符

任何表达式的值在程序被汇编的过程中进行计算确定,而不是到程序运行时才计算。

一、算术运算符

1.运算符“+”和“-”也可作单目运算符,表示数的正负。

2.使用“+”、“-”、“*”、和“/”运算符时,参加运算的数和运算结果都是整数。

3.“/"运算为取商的整数部分,而“MOD”运算取除法运算的余数。

4.“SHR ”和“SHL”为逻辑移位运算符

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

5.下标运算符“[]”具有相加的作用
一般使用格式:表达式1[表达式2]

二、逻辑运算符

1.NOT
2.AND
3.OR
4.XOR

都是按位逻辑运算。

三、关系运算符

包括:EQ(等于)、NE(不等于)、LT(小于)、LE(小于等于)、GT(大于)、GE(大于等于)

关系运算符比较的两个表达式必须同为常数或同一逻辑段中的变量。如果是常量的比较,则按无符号数进行比较;如果是变量的比较,则比较它们的偏移量的大小。

四、数值返回运算符

它们将变量或标号的某些特征值或存储单元地址的一部分提取出来。

五、属性修改运算符

这一类运算符用来对变量、标号或存储器操作数的类型属性进行修改或指定。

六、运算符的优先级

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

程序的段结构

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

段定义伪指令

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

段寻址伪指令

段寻址伪指令ASSUME的作用是告诉汇编程序,在处理源程序时,定义的段与哪个寄存器关联。ASSUME并不设置各个段寄存器的具体内容,段寄存器的值是在程序运行时设定的

段寄存器的装入

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

DS和ES的装入:

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

SS的装入:

在段定义伪指令的组合类型项中,使用STACK参数,并在段寻址伪指令ASSUME语句中把该段与SS段寄存器关联,如果在段定义伪指令的组合类型中,未使用STACK参数,或者是在程序中要调换到另一个堆栈,这时,可以使用类似于DS和ES的装入方法

CS的装入:

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

posted @ 2022-03-13 21:32  20192425  阅读(228)  评论(0编辑  收藏  举报