《网络与系统攻防技术与汇编语言》学习笔记一
首先简单描述以下汇编语言存在的意义:
汇编语言介于机器语言与高级语言之间
高级语言相对来说方便程序猿(or程序媛)操作,程序设计相对简单,语句也易懂好记。但相应的代价为,高级语言将计算机操作细节藏匿了起来,臭写代码的并不知道(也不需要知道)计算机内部数据如何传送、储存、处理的,同时运行效率相对较低。相当于在计算机和操作者中间加了一个翻译员。
机器语言则是一种将命令和数据直接用二进制码表示的一种程序设计语言。学习强国中举了个例子,将寄存器AH中的内容和数10相加,再将结果送回AH寄存器中,需这样表示:1011 0100 0000 1010
(若使用十六进制表示则为:B40A
),所以需要臭写代码的明白当下操作的数据和存放地点
,也需要明白操作指令码。但明显用二进制(or十六进制)来写代码不太现实,效率太过低下,因此引出汇编语言~
汇编语言存在的目的,就是为了帮助我们既能直接像机器语言一样直接地统筹处理计算机资源,又方便操作者阅读,提高程序设计的效率,可见汇编语言既保留了机器运行效率,又保证了人员的设计效率。同样使用机器语言中的例子,如果我需要将AH寄存器中的内容和10相加,再将结果存放回AH寄存器中,大概需要将语句写成这样:ADD AH 10
(大概是这样,不对也是类似的),根据这个例子我们可见汇编语言的特点。
汇编语言的特点
我认为汇编语言的特点主要有二:
- 一是对机器语言直接翻译,一句机器语言指令对应一句汇编语言指令,这保证了汇编语言转机器语言时的效率,所以学会了汇编语言,其实也就是学会了机器语言。(至少能读懂部分的机器码了吧?)
- 二是和硬件相配套,由于不同的机器内部的机器语言不同,导致汇编语言也会发生改变,这导致我们在学习汇编语言时必须明白当下计算机系统的内部组成结构。
学习汇编语言的目的
课程中所介绍的学习汇编语言的目的大多为帮助我们发挥计算机的性能。但在志强老师前几节课的讲解中,我认识到了汇编语言是我们这门课不可或缺的一部分,在课程中我们需要学习计算机系统全方位的攻防,上到应用层,中有网络层系统层,下至物理层,而汇编语言则是我们在系统层最有力的助手,因此,学好汇编语言对于这门课的收益是巨大的~
一些基础知识
进位计数制及其相互转换 (1.3内容)
进位计数制
作为一个计算机方面专业的大三学生,这部分内容属实有点不太想看(偷偷吐槽),既然课程中讲了,那就简单介绍一下吧~
一般常用的计数制有四种:十进制1234567890
、八进制01234567
、二进制01
、十六进制0123456789ABCDEF
,他们保持着逢十(八、二、十六)进一的规则,也就是按位进的原则进行计数。其中每个进制中所写的字符为他们的基数,而计数制表示的数字在哪一位都有自己的权值,例如十六进制表示的62A
的每位权值表示分别为:6
为162(16的2次方)、2
为161、A
为160。
各个数值之间的转换: 减权定位法和除基取余法(乘积取整法)
首先课程中介绍了十进制转为二进制时所用的减权定位法,这也是我将十进制转为二进制的常见方式~
减权定位法:从二进制数高位起,依次用待转换的十进制数与各位权值进行比较,若足够减去,则将该位设为1(即书中所说的·该数位系数Ki=1),书中举例使用325,在这里我选则324(我滴生日)
324-256=68; 68<128; 68-64=4; 4<32; 4<16; 4<8; 4-4=0; 0<2; 0<1 最终得到324的二进制为101000100
上述方法也适用于小数部分,例如我们选择0.345作为例子,则:
0.345<0.5; 0.345-0.25=0.095; 0.095<0.125; 0.095-0.0625=0.0325; 0.0325-0.03125=0.00125; ……(往后懒得算了)
最终得到0.345的二进制约为0.01011……
上述方式仅适合十进制向二进制转化,如果需要十进制向八进制或十六进制转换,则可优先使用该方法将十进制转化为二进制,在将二进制转化为八进制(十六进制)。
而除基取余法则适用于各种进制之间的转换,书中举例使用十进制向二进制转换:
除基取余法:将需要转化的数除以2(二进制首位权值为2,因此可以推出其他进制之间转换时的除数和被除数),运算过后所得余数为最低为的值,同时所得结果(商值)继续向下进行除运算(也就是除以2),得到的结果为次低位。持续该运算,知道商值为0~这里我依然用324举例
324÷2=162……0; 162÷2=81……0; 81÷2=40……1; 40÷2=20……0; 20÷2=10……0; 10÷2=5……0; 5÷2=2……1; 2÷2=1……0; 1÷2=0……1
得到324的二进制为101000100
该方法的小数处理则为乘积取整法,简单来说就是进行累乘(乘数为该进制最低位权值),当结果整数部分出现1时,需将结果减1并将结果中的该位赋值为1(结果从最高位开始)。
除了这两种方法,课程中还讲了二进制转为十进制时所用的按权相加法、逐次乘积相加法(逐次除基相加法),相对简单,就不一一描述了,这里放上课件中的图:
逐次乘积相加法
逐次除基相加法
带符号的数表示(1.4内容)
原码、反码、补码
因为之前专业课都讲过,就不按照课件的顺序走了:
- 原码
原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值. 比如如果是8位二进制:
[+1]原 = 0000 0001
[-1]原 = 1000 0001
第一位是符号位. 因为第一位是符号位, 所以8位二进制数的取值范围就是:
[1111 1111 , 0111 1111]==>[-127 , 127]
- 反码
反码的表示方法是:
- 正数的反码是其本身
- 负数的反码是在其原码的基础上, 符号位不变,其余各个位取反.
[+1] = [00000001]原 = [00000001]反
[-1] = [10000001]原 = [11111110]反
- 补码
补码的表示方法是:
- 正数的补码就是其本身
- 负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)
[+1] = [00000001]原 = [00000001]反 = [00000001]补
[-1] = [10000001]原 = [11111110]反 = [11111111]补
相互转换
如下图:详细可参考点击我!!!
字符的表示: ASCII码 (1.5内容)
ASCII ((American Standard Code for Information Interchange): 美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言。它是最通用的信息交换标准,并等同于国际标准ISO/IEC 646。ASCII第一次以规范标准的类型发表是在1967年,最后一次更新则是在1986年,到目前为止共定义了128个字符。
标准ASCII码为一字节,其中用低七位表示字符编码,用最高位作为奇偶校验位
基本逻辑运算 (1.6内容)
计算机内部采用二进制数表示信息,而“0”和“1”正好对应逻辑TURE
和FLASE
两个状态。
- “与”运算(AND):又称逻辑乘,通常使用·或∧表示,举例
1∧1=1; 1∧0=0;
- “或”运算(OR) :又称逻辑加,通常使用+或∨表示
- “非”运算(NOT);对逻辑变量取相反的值,通常使用一条横线来表示
- “异或”运算(XOR):通常使用⊕来表示,该运算两值相同时返回
TURE
,否则FLASE
硬件组织结构部分 (2.X内容)
IBM-PC微机基本结构
一般结构概述
一般计算机应包括五大部件:运算器、控制器、存储器、输入设备、输出设备,其中运算器和控制器通常集成在一起,称为中央处理器,即我们的CPU。
而计算机不同部件之间还需要传输通道,即为我们的总线,总线又分为三类总线:地址总线、数据总线、控制总线。
下图则为我们一般结构的图展示:
下面让我们一一来认识他们~
- 中央处理器,CPU:分析从主存储器中取来的各项指令功能,控制计算机完成各部件的具体操作。
- 主存储器:存放程序和数据的部件,由若干个存储单元组成。每个存储单元使用唯一的编号来标识,称为存储单元的地址。指示存储单元编号的地址长度决定了存储器的最大容量。
- 输入输出设备及接口:I/O设备的工作速度、工作原理以及所处理的信息格式等与主机相差很大,因此I/O设备要通过I/O接口与系统总线连接
Intel 8086/8088 CPU的功能结构
CPU执行指令即为重复执行两个步骤:
- 从存储器中取指令
- 执行指令规定的操作
而执行上述两个步骤又分为两种方式:串行方式、指令流水线方式。
串行方式
CPU | 取指 | 执行 | 存数 | 取指 | 执行 | 取指 | 取数 | 执行 |
---|---|---|---|---|---|---|---|---|
系统总线 | 忙 | 闲 | 忙 | 忙 | 闲 | 忙 | 忙 | 闲 |
可见串行方式的特点为如下两点;
- 当CPU在指令执行阶段,不需要占用系统总线,但总线在此时也不工作,导致总线空闲时间较多
- 在从存储器中取指令、取数据或存数据时,总线处于忙状态,所占用时间也较长。但同时CPU仅需较短的时间去处理,因此CPU大多数时间处于闲置状态下。
指令流水线方式
采用指令流水线工作方式的计算机具有较高的工作效率。CPU内部采用了一种先进的指令流水线结构,这种结构可以有效而充分地利用各主要硬件资源。
要实现指令流水线方式,从CPU组成结构上要划分成多个单元。8086/8088CPU被划分成两个单元。
Intel 8086/8088 CPU的功能结构
我们说到Intel 8086/8088 CPU被划分为两个单元,即为我们的执行单元EU和总线接口单元BIU:
EU的主要任务是分析与执行指令,具体包括:
A、从指令队列中取出指令代码,由控制器译码后产生 相应的控制信号,控制各部件完成指令规足的操作。
B、对操作数执行各种指定的算术或逻辑运算。
C、向总线接口单元BIU发送访问主存或I/O的命令,并提供相应的地址和传送的数据。
BIU负责CPU与存储器、I/O的信息传送。具体包括:
A、取指令——根据CS寄存器和指令指针IP形成20位的物理地址,从相应的存储器单元中取出指令,暂存到指令队列中,等待EU取走并执行。
B、存取数据——在EU执行指令的过程中,需要与存储器或I/O端口传送数据时,由EU提供的数据和地址,结合段寄存器,通过系统总线与存储器或I/O进行数据的存取。
EU和BIU是既分工又合作的两个独立部分。它们的操作在一定程度上是并行工作的,分别完成不同的任务,从而大大加快了指令执行速度。
在这种结构下,Intel 8086/8088 在运行时运行过程大致如下:
BIU | 取指1 | 取指2 | 取指3 | 存数 | 取指4 | 取数 | …… |
---|---|---|---|---|---|---|---|
EU | 执行1 | 执行2 | 执行3 | 等待 | 执行4 | …… | |
系统总线 | 忙 | 忙 | 忙 | 忙 | 忙 | 忙 | 忙 |
Intel 8086/8088 寄存器结构及其用途
如上图所示(H表示高位,L表示低位),Intel 8086/8088 寄存器共有通用寄存器8个 + 控制寄存器2个 + 段寄存器4个。
通用寄存器
Intel 8086/8088有8个16位通用寄存器,它们具有良好的通用性,并且还可以用作某个特定的功能,可以由程序设计人员进行编程访问:
-
数据寄存器:它包括AX、BX、CX和DX四个寄存器。它们中的每一个既可以是16位寄存器,也可以分成两个8位寄存器使用。即可以当作8个独立的8位寄存器使用。数据寄存器既可以用来存放参加运算的操作数,也可以存放运算的结果。在多数情况下,使用这些寄存器时必须在指令中明确指示。
在有些指令中,不需要明确指出使用的寄存器名,即隐念使用了某寄存器,称为隐含使用。个别指令对寄存器有特定的使用,并且又必须在指令中指明它的名字,这类寄存器的使用称为特定使用。 -
指针寄存器:指针寄存器有堆栈指针SP和基址指针BP。它们一般被用来存放16位地址,在形成20位的物理地 址时常被作为偏移量使用。
SP指针——在进行堆栈操作时,被隐含使用,被用来指向堆栈顶部单元。
BP指针——被用来指向堆栈段内某一存储单元。BP除用作地址指针外也可以象数据寄存器一样,存放参加运算的操作数和运算的结果。 -
变址寄存器:有两个16位的变址寄存器SI和DL 一般被用来作地址指针。
SI——源变址寄存器
DI—— 目的变址寄存器
同BP寄存器一样,SI和DI也可以用作通用数据寄存器存放操作数和运算结果。
段寄存器
8086/8088CPU在使用存储器时,将它划分成若干个段。每个段用来存放不同目的内容。每个存储段用一个段寄存器来指明该段的起始位置(也叫段基址)。CPU在访问存储器时必须指明两个内容:
- 所访问的存储单元属于哪个段,即指明使用的段 寄存器。
- 该存储单元与段起始地址(段基址)相距多少, 即偏移量。
在程序设计中,一个程序将存储器划分成多少个存 储段是任意的。但在程序运行的任何时刻最多只有4个 段,分别由CS、DS、ES和SS指定。
指令指针IP
CPU在从存储器取指令时,以段寄存器CS作为代码段的基址指针,以IP的内容作为偏移量,共同形成一条指令的存放地址。当CPU从内存中取岀一条指令后,IP内容自动修改为指向下一条指令。注意:IP的内容不能被直接访问,既不能用指令去读IP的值,也不能用指令给它赋值。但是可以通过某些指令的执行而自动修改IP的内容。
标志寄存器
标志寄存器是用来反映CPU在程序运行时的某些状态, 如是否有进位、奇偶性、结果的符号、结果是否为零等等。
-
进位标志位CF:在进行算术运算时,若最高位(对字操作是第15位,字节操作是第7位)产生进位或借位时CF被自动置“1”,否则置“0”
在移位类指令中,CF也被用来存放从最高位(左移时)或最 低位(右移时)移岀的数值(0或1) -
奇偶标志位PF:当指令操作结果的低8位中含有1的个数为偶数时,贝IJPF 被置1,否则PF被置0。
注意:PF只反映操作结果的低8位的奇偶性,与指令操作数的长度无关。 -
辅助进位标志位AF:在进行算术运算时,若低字节的低四位向高4位产 生进位或借位,即第3位产生进位或借位时,AF位被置1, 否则置0。AF标志位用于十进制运算的调整。
注意:AF只反映运算结果低八位,与操作数长度无关。 -
零值标志位ZF:若运算结果各位全为0,贝IJZF被置1,否则置0。
-
符号标志位SF:将运算结果视为带符号数,当运算结果为负数时SF 被置1,为正数时,则置0。
由于第7位是字节操作数的符号位,而第15位是字操 作数的符号位,因此,SF位与运算结果的最高位(第7 位或第15位)相同。 -
溢岀标志位OF:当运算结果超过机器用补码所能表示数的范围时, 则OF置1,否则置0.
字节数据,机器用补码所能表示的数范围为-128〜+127。 字数据的表示范围为:-32768〜+32767
注意:溢岀与进位是两个完全不同的概念,不能相互混淆。 -
单步标志位TF:单步标志也叫跟踪位,该标志为控制标志位。单步标志 位供调试程序使用。
当TF位被设置为1时,每执行一条指令后,CPU暂停运 行,即产生单步中断。 -
中断允许标志位IF:该标志位为控制标志位。当IF被设置为1时,CPU可以 响应可屏蔽中断,否则不允许响应可屏蔽中断。
-
方向标志位DF:DF也是控制标志位。它被用来规定串操作指令的增减 方向。
当DF=O时,串操作指令自动使变址寄存器(SI和DI) 的内容递増。当DF=1时,串操作指令自动使变址寄存器的 内容递减。
存储器组织结构
在张健毅老师的计算机组成课中,我曾学习存储器组织结构。
存储单元的地址
8086/8088 是16位的cpu,因此由两个字节构成,由无符号的数构成
存储器结构
在8086中cpu具有1MB的寻址能力,但是地址总线只有16根
寻址方式
16位的带宽只能访问216,也就是64kb的空间。因此>16,也就是64kb的空间。因此 在cpu中采取了存储器分段的方法也就是将空间分为任意的存储段每个段的大小最大为64kb
这样就可以用基地址加偏移地址来获取地址 这里的基地址和偏移地址都是逻辑地址
计算方式
段地址 × 16d + 偏移地址 = 物理地址
在存储器中物理地址是唯一的 但是 逻辑地址不唯一
对应寄存器
如下图~
堆栈及其操作方法
堆栈是一个特定的存储区,访问该存储区一般需要按照专门的规则进行操作
堆栈的用途:主要用于暂存数据以及在过程调用或处理中断时保存断点信息
堆栈的构造
堆栈一般分为专用堆栈存储器和软件堆栈
堆栈的一端是固定的,称为栈底,是存储器的最大地址单元
另一端是浮动的,称为栈顶,随着堆栈中存放信息的多少而改变
为了确定栈顶的位置,通常使用堆栈指针SP指示栈顶
堆栈存取数据的规则:“先进后出FILO”
8086/8088堆栈的组织
当SP被初始化时,指向栈底+2单元,其值就是堆栈的长度
指向栈底+2单元的原因是当栈为空的时候,栈中没有元素,也就不存在栈顶元素,所以SP只能指向栈的最底部单元下面的单元,该单元的偏移地址为栈最底部的字单元的偏移地址+2
数据在堆栈中存放格式:以字(两字节)为单位存放,数据的低8位放在较低地址单元,高8位放在较高地址单元
堆栈操作
设置堆栈
STACK1 SEGMENT PARA STACK DB100 DUP (0)
STACK1 ENDS
进栈PUSH
PUSH AX;将寄存器AX的内容压入堆栈
PUSH DS;将段寄存器DS的内容压入堆栈
PUSHF;将标志寄存器内容压入堆栈
进栈过程:首先将堆栈指针SP-2,即指向一个空的堆栈字单元。之后将要储存的内容送入SP指向的字单元中
出栈POP
出栈操作由POP指令或机器自动实现,它从堆栈顶部弹出一个字到通用寄存器、段寄存器或字存储单元
POP AX;将栈顶字单元内容弹出到AX
POP DS;将栈顶字单元内容弹出到段寄存器DS
POPF;将栈顶字单元内容送回标志寄存器F
出栈过程:首先将SP指向的字单元内容送往指定的寄存器或存储器。之后将堆栈指针SP内容+2
寻址方式、指令系统、指令编码 (3.X内容)
8086/8088 指令寻址方式
操作数的种类
数据操作数
数据操作数是与数据有关的操作数,即指令操作的对象是数据。
对于数据操作数,有的指令有两个操作数,一个称为源操作数,在操作过程中,其值不发生改变;另一个称为目的操作数,操作后一般被操作结果所代替。
(1)立即数操作数:要操作的数据包含在指令中。
(2)寄存器操作数:要操作的数据存放在指定的寄存器中。
(3)存储器操作数:要操作的数据存放在存储单元中。
(4)I/O操作数:要操作的数据来自或送到I/O端口。
地址操作数
- 地址操作数是与程序转移地址有关的操作数,即指令中的操作对象不是数据,而是要转移的目标地址。它也分为立即数操作数、寄存器操作数和存储器操作数,即要转移的目标地址包含在指令中,或者寄存器,或者存储单元中。
- 对于地址操作数,指令只有一个目的操作数,它是一个提供程序转移的目标地址。
寻址方式
所谓寻址方式,就是指令中给出的寻找操作数的方法。8086/8088的寻址方式分为两类:数据寻址方式和地址寻址方式。
立即数寻址方式
立即数寻址方式又分为立即数寻址方式、寄存器寻址方式、存储器寻址方式和IO端口寻址方式四种类型。
1. 立即数寻址方式(immediate addressing)
MOV BL,80H
MOV AX,1090H
2. 寄存器寻址方式(register addressing)
MOV CL,DL
MOV AX,BX
3. 存储器寻址方式(memory addressing)
(1) 直接寻址方式:MOV AL,[1064H],数据段寄存器(DS)= 2000H,物理存储单元地址:2000H × 10H + 1064H = 21064H,将该地址内的数据存放到AL寄存器中。
(2)寄存器间接寻址方式:MOV AX,[SI],根据物理存储地址,将其值赋值为AX寄存器。
(3)寄存器相对寻址方式:MOV AL,[BP + TABLE],TABLE是一个8位或16位的位移量。
(4)基址变址寻址方式:MOV AH,[BP][SI],根据基址寄存器BP、变址寄存器SI运算的和,将其内容赋值到AH寄存器中。
(5)基址变址相对寻址方式:MOV AX,[BX + SI + COUNT],COUNT表示8位或16位常量。
4. IO端口寻址方式
(1)IO端口直接寻址方式:IN AL,21H。从地址为21H的端口读取数据送到AL中。
(2)IO端口间接寻址方式:OUT DX,AX。将AX的内容送到由DX寄存器内容所指定的端口号中。
地址寻址方式
地址寻址方式有以下四种:
- 段内直接寻址方式
- 段内间接寻址方式
- 段间直接寻址方式
- 段间间接寻址方式
8086/8088指令系统
8086/8088的指令系统可以分为六种类型:
(1)数据传送指令;
(2)算术运算指令;
(3)位操作指令;
(4)串操作指令;
(5)程序控制指令;
(6)处理器控制指令;
数据传送指令
通用数据传送指令
数据传送指令MOV
MOV dst, src //dst表示目的操作数,src表示源操作数
堆栈操作指令PUSH、POP
PUSH src //压栈指令,先将堆栈指针SP-2,再将src压入栈中
POP dst //出栈指令,先将SP指向的栈顶值弹出栈顶,在将堆栈指针SP+2
数据交换指令XCHG
XCHG dst, src //将源操作数和目的操作数进行交换
字节转换操作XLAT
XLAT src_table //根据表中的元素的序号,查出表中相应元素的内容
输入输出指令
输入指令IN
IN acc, port //从指定端口port中读入一个字节或一个字送AL或AX中
//端口地址小于0FFH
间接寻址的输入指令
IN acc, DX //由DX寄存器来指定端口号,将其端口号的8/16位数据送如AL/AX中
输出指令OUT
OUT port, acc //将AL或AX的8位/16位数据输出到指定的I/O端口,端口地址不大于FFH
间接寻址的输出指令
OUT DX, acc //由DX寄存器指定端口号,将AL或AX的8位/16位数据输出到该端口号中
目的地址传送指令
目的地址传送指令常常用于串操作时建立初始的地址指针:
取有效地址指令LEA(load effective address)
LEA reg16, mem //将一个近地址指针写入指定的寄存器中。目的操作数必须为16位通用寄存器
LEA BX, BUFFER
MOV BX, BUFFER
//LEA是将BUFFER的偏移地址赋值给BX,而MOV是将BUFFER的值赋值给BX,LEA等同于下面的操作
MOV BX, OFFSET BUFFER //OFFSET BUFFER表示存储器地址的偏移地址
地址指针装入DS指令LDS(load pointer into DS)
LDS reg16, mem32 //LDS用于传送一个32位的远地址指针,包括一个偏移地址和一个段地址。偏移地址送入reg16的目的寄存器,段地址送入数据段寄存器DS。
地址指针装入ES指令(load pointer into ES)
LES reg16, mem32 //LES与LDS类似,不过段地址送入附加段寄存器ES
标志传送指令
8086/8088 CPU有一标志寄存器FLAG,其中6个状态标志位和3个控制位:
取标志指令LAHF(load AH from Flags)
LAHF //LAHF指令将标志寄存器FLAG中的5个状态标志位SF,ZF,AF,PF,以及CF分别取出传送到累加器AH的对应位。LAHF 对状态标志没有影响。
置标志指令SAHF(store AH into Flags)
SAHF //SAHF指令与LAHF相反,将AH的对应位传送到标志寄存器的对应位。SAHF对部分状态标志位有影响。
标志压入堆栈指令PUSHF(PUSH Flags onto stack)
PUSHF //指令将SP-2,然后将标志寄存器FLAG的内容(16位)压入堆栈。不影响状态标志位。
标志弹出堆栈指令POPF(POP Flags off stack)
POPF //POPF指令操作与PUSHF相反,它将SP指向栈顶的内容弹出到寄存器,然后SP+2
算术运算指令
二进制数运算指令
加法指令
-
不带进位加法指令
ADD dst, src //将源操作数和目的操作数相加然后赋值给dst目的操作数 -
带进位加法指令ADC
ADC dst, src //将源操作数和目的操作数相加,再加上进位标志CF的内容,赋值给目的操作数。 -
加1指令
INC dst //将目的操作数+1赋值给目的操作数。
减法指令
-
不带借位减法指令
SUB dst, src //将目的操作数减源操作数,结果赋值给目的操作数。 -
带借位的减法指令
SBB dst, src //将目的操作数减源操作数,然后在减进位标志CF,结果赋值给目的操作数。 -
减1指令
DEC dst //将目的操作数减1,然后赋值给操作数。 -
求补指令
NEG dst //用“0” - dst,结果赋值给目的操作数。 -
比较指令CMP
CMP dst, src //用dst减src,结果影响标志位,不影响dst和src。
乘法指令
-
无符号乘法
MUL src //MUL指令的另一个操作数在累加器(8位AL,16位AX)中,是隐含的。 -
带符号的乘法指令IMUL
IMUL src //IMUL操作与MUL操作类似,只是src表示的范围是带符号的。
除法指令
-
无符号数除法指令DIV
DIV src //字节除法中,AX除以src,被除数16位,除数8位,商在AL,余数在AH中。字除法,DX:AX除以src,被除数32位,除数为16位。商在AX,余数在DX中。 -
带符号除法指令IDIV
IDIV src //如果除数为0,会发生类型为0的终端。 -
符号扩展指令
字节扩展指令CBW:将一个字节(8位)按其符号扩展为字(16位)。它隐含的操作数为寄存器AL和AH。
字扩展指令CWD:将一个字(16位)按其符号位扩展为双字(32位)。它隐含的操作数为寄存器AX和DX。
位操作指令
逻辑运算指令
逻辑与指令AND
AND dst, src //将目的操作数和源操作数按位进行逻辑与运算,并将结果赋值给目的操作数。
测试指令TEST
TEST dst, src //TEST指令操作根AND指令相似,只是,不将操作结果赋值给目的操作数,只将结果反应在状态标志位上。TEST指令和CMP指令作用相似,一个比较位,一个比较字或字节。
逻辑或指令OR
OR dst, src //将目的操作数和源操作数进行按位逻辑或运算,结果赋值给目的操作数。
OR指令和AND指令:如果将一个寄存器和本身进行运算,不改变寄存器的值,只影响状态标志位。
经常用寄存器与自身按位与AND或者按位或OR,根据状态标志位来判断数据是否为0,用CMP指令虽然能达到同样的效果,但是指令字节较多,执行速度慢。
逻辑异或运算指令XOR
XOR dst, src //将dst和src进行按位异或运算,结果赋值给dst。
XOR指令的一个用途是:将寄存器或存储器某些特定的位”求反“,使其余位不变。**求反的位与”1“异或,不变的位与”0“异或。
逻辑非运算NOT
NOT dst //操作数不能为一个立即数。
移位指令
逻辑左移/算术左移指令SHL/SAL
SHL dst, 1/CL //将dst逻辑左移1位或CL位
SAL dst, 1/CL //将dst算术左移1位或CL位
SHL和SAL操作完全相同,左移一次可以实现dst乘以2操作。
逻辑右移指令SHR
SHR dst, 1/CL //将dst向右移1位或CL寄存器指定的位数,最高位补0。右移一次可以实现除以2操作。
算术右移指令SAR
SAR dst, 1/CL //将dst向右移1位或CL寄存器指定的位数,最高位保持不变。
循环移位指令
循环左移指令ROL
ROL dst, 1/CL //将dst向左循环移动1位或者CL寄存器指定的位数。
循环右移指令ROR
ROR dst, 1/CL //将dst向右循环移动1位或者CL寄存器指定的位数。
带进位循环左移指令RCL
RCL dst, 1/CL //将dst与进位标志CF一起向左循环1位或者CL寄存器指定的位数。
带进位循环右移指令RCR
RCR dst, 1/CL //将dst与进位标志CF一起向右循环1位或者CL寄存器指定的位数。
串操作指令
串传送指令
MOVS dst_string, src_string //将一个字节或字从存储器的某个区域传送到另一个区域。
MOVSB //B代表字节串传送,有B这个标志,指令后不能出现操作数。
MOVSW //W代表字传送,有W这个标志,指令后不能出现操作数。
例子: SI和DI分贝是源变址寄存器和目的变址寄存器
LEA SI, BUFFER1 ;源串首地址指针
LEA DI, BUFFER2 ;目的串首地址指针
MOV CX, 200 ;字符串长度
CLD ;清除方向标志DF
REP MOVSB ;传送200字节 REP是重复前缀
HLT ;停止
串装入指令LODS
LODS src_string //将一个字符串中的字节或字逐个装入累加器AL或AX中。
LODSB
LODSW
串送存指令STOS
STOS dst_string //将累加器AL或AX的值送存到内存缓冲区某个位置。
STOSB
STOSW
例子:将字符”#“装入以AREA为首地址的100个字节中
LEA DI, AREA
MOV AX, '##'
MOV CX, 50
CLD
REP STOSW
HTL
串比较指令SCAS
SCAS dst_string //SCAS指令在一个字符串中搜索特定的关键字。字符串的起始地址只能放在(ES:DI)中,不能段超越,待搜索的关键字必须放在累加器AL或AX中。
SCASB
SCASW
控制转移指令
转移指令
将一种程序控制从一处改换到另一处的最直接的方法。在CPU的内部,转移是通过将目标地址传送给CS和IP(段间转移)或IP(段内转移)来实现的。
无条件转移指令JMP
- 段内直接转移
JMP near_label //代码段寄存器CS的内容不变,JMP指令计算出下一条指令的地址到目标地址之间的16位相对位移量disp,disp可正可负。范围-32768 - 32767
段内直接短转移
JMP short_label //相对位移量disp的范围为-128 - 127,属于短标号。
段内间接转移
JMP reg16/mem16 //用指定的寄存器或存储器的内容取代当前IP的值,以实现程序的转移。
段间直接转移
JMP far_label //操作数是一个远标号,该标号在另一个代码段内。IP和CS内容会被该代码段标号信息取代。
段间间接转移
JMP mem32 //将存储器的前两个字节赋值给IP,后两个字节赋值给CS。
条件转移指令
Jcc short_babel //”cc“表示条件,首先测试规定条件,如果满足则跳转到目标地址,否则,继续执行。条件可以是无符号位的高 | 低(Above | Below),带符号数的大 | 小(Great | Less)等等。
循环控制指令
LOOP
LOOP short_label //LOOP要求使用CX做寄存器的内容减一,结果不为零,则转到指令指定的短标号处。
LOOPE short_label //"E" 是相等equal
LOOPZ short_label //"Z" 是等于零zero
LOOPNE short_label //”NE“是不相等
LOOPNE short_label //”NZ“是不为零
过程调用与返回指令
过程调用指令CALL
段内直接调用
CALL near_proc //操作数是一个近过程,过程在本段内。
段内间接调用
CALL reg16/mem16 //操作数是一个16位的寄存器或存储器,先将IP入栈,再讲操作数内容赋值给IP。
段间直接调用
CALL far_proc //操作数是一个远过程,在另外的代码段。会先将CS和IP入栈,在更新其值为该代码段的信息。
段间间接调用
CALL mem32 //操作数是一个32位的存储器,先将CS和IP入栈,再见操作数的后两个字节给CS,前两个字节给IP。
过程返回指令RET
一般将堆栈之前保存的断点值弹出,程序返回原来调用的地方。RET允许带一个弹出只(pop_value),范围是0 - 64K大小,表示返回时从堆栈舍弃的字节数大小。
2.6 处理器控制指令
标志位操作指令
CLC:清进位标志
STC:置进位标志
CMC:对进位标志求反
CLD:清方向标志
STD:置方向标志
CLI:清中断允许位标志
STI:置中断允许标志
外部同步指令
- HLT //使CPU进入暂停状态
- WAIT //使CPU进入等待状态
- ESC //ESC ext_op, src 使其他处理器使用8086的寻址方式,并取得指令。
- LOCK //使CPU总线锁定低电平有效,直到执行完下一条指令。
空操作指令NOP
NOP指令不进行任何操作,但是会占用3个时钟周期。
汇编语言程序格式 (4.X内容)
汇编语言语句种类及其格式
汇编语言的语句可以分为指令语句和伪指令语句:
- 指令语句:每一条指令语句在汇编时都要产生一个可供CPU执 行的机器目标代码,它又叫可执行语句。一条指令语句最多可以包含4个字段:标号字段、指令助记符字段、操作数字段、注释字段
- 伪指令语句:又叫命令语句。伪指令本身并不产生对应的机器目标代码。它仅仅是告诉汇编程序对其后面的指令语句和伪指令语句的操作数应该如何处理。一条伪指令语句可以包含四个字段:符号名字段、伪指令符字段、操作数字段、注释字段
- 标识符:指令语句中的标号和伪指令语句中符号名统称为标识符。标识符是由若干个字符构成的
汇编语言数据
数据是指令和伪指令语句中操作数的基本组成部分。一个数据由数值和属性两部分构成
在汇编语言中常用的数据形式有:常数、变量和标号
常数:在汇编期间其值已完全确定,并且在程序运行过程中,其值不会发生变化
变量:变量用来表示存放数据的存储单元,这些数据在程序运行期间可以被改变
标号:标号写在一条指令的前面,它就是该指令在内存的存放地址的符号表示,也就是指令地址的别名
符号定义语句
在源程序设计中,使用符号定义语句可以将常数或表达式等内容用某个指定的符号来表示。
在8086/8088汇编语言中有两种符号定义语句:等值语句、等号语句
等值语句
- 语句格式:符号名 EQU 表达式
- 功能:用符号名来表示EQU右边的表达式。后面的程序中一旦出现该符号名,汇编程序将把它替换成该表达式。表达式可以是任何形式,如:常数或数值表达式、地址表达式、变量、寄存器名或指令助记符
等号语句
- 语句格式:符号名=表达式
- 功能:与等值语句具有相同的作用。但等号语句可以对一个符号进行多次定义
表达式与运算符
表达式是指令或伪指令语句操作数的常见形式。它由常数、变量、标号等通过操作运算符连接而成
8086/8088宏汇编语言中的操作运算符可以分为五类:
(1)算术运算符
(2)逻辑运算符
(3)关系运算符
(4) 数值返回运算符
(5)属性修改运算符
算术运算符
- 包括:+、—、*、 / 、MOD、SHL、SHR、[ ]
逻辑运算符
- 包括:NOT、AND、OR和XOR等
关系运算符
- 包括:EQ(等于)、NE(不等于)、LT(小于)、 LE(小于等于)、GT(大于)、GE(大于等于)
数值返回运算符
- 用于将变量或标号的某些特征值或存储单元地址的一部分提取出来
- 包括:SEG运算符、OFFSET运算符、TYPE运算符、LENGTH运算符、SIZE运算符
属性修改运算符
- 用来对变量、标号或存储器操作数的类型属性进行修改或指定
- 包括PTR运算符、HIGH/LOW运算符、THIS运算符
程序的段结构
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的初始值就不能用可执行语句来装入