汇编语言设计

一 概述

1.1 汇编代码和目标代码

汇编代码非常接近与计算器机执行的实际机器代码。与目标代码的二进制格式相比,它的主要特色在于它采用的是更加易读的文本格式。通过阅读这些汇编代码,我们能够理解编译器的优化能力,并分析出代码中潜在的低效率。

1.2 反汇编

使用命令:objdump -d 文件,可以查看改文件的汇编代码。
这个文件可以是.o文件(目标代码文件),也可以是可执行文件。

1.3 几个常用的术语

字节:8位
字:16位,也就是两个字节
双字:32位,也就四个字节

二 寄存器

2.1 16位微处理器(8086为例)

16位处理器的寄存器都是16位的。8086是小端模式,高地址存放高位字节,低地址存放地位字节。

寄存器主要有14个,分为下面几类:

1、4个通用寄存器:AX、BX、CX、DX
2、4个专用寄存器:SP、BP、SI、DI
3、4个段地址寄存器:CS、DS、SS、ES
4、指令指针寄存器:IP
5、状态寄存器:PSW

1) 4个通用寄存器

四个通用寄存器可以也可以分为两个可独立使用的8位寄存器。

16位    高8位     低8位
AX      AH        AL
BX      BH        BL
CX      CH        CL
DX      DH        DL           

2) 4个专用寄存器

SP:堆栈指针寄存器
BP:基址变址寄存器
SI:源变址寄存器
DI:目的变址寄存器

3) 4个段地址寄存器

CS:代码段寄存器
DS:数据段寄存器
SS:堆栈段寄存器,存放段地址,SP存放的是段内偏移。SS:SP指向栈顶元素!
DS:附加段寄存器

4) 指令指针寄存器IP

这个寄存器的主要的存在意义就是和CS寄存器一起,确定下一条指令的存储位置。(CS:IP就是指向下一条指令位置的指针,相当于简化模型中的那个PC)

5)标志寄存器PSW

同样是16位的一个寄存器。这是用于反应CPU内部状态以及用于控制CPU某些不见的行为而专门设置的。里面一个位就代表一个状态。常使用到的状态有下面几种:

CF:进位标志
PF:奇偶标志
AF:辅助进位标志
ZF:零标志
SF:符号溢出
OF:溢出标志

2.2 32位处理器中的寄存器(IA32架构处理器)

IA32架构的处理器提供三种基本的模式,实模式、保护模式、系统管理模式。其中实模式是为了兼容16位处理器而提供的,这个模式没有MMU。保护模式是IA32的原生模式,支持MMU。

寄存器主要有:

4个32位通用寄存器- EAX、EBX、ECX、EDX
4个32位专用寄存器- ESP、EBP、ESI、EDI
一个32位指令指针寄存器-EIP
6个32位段寄存器- CS、DS、ES、SS、FS、GS
一个标志寄存器-EFLAGS

这些寄存器是和8086CPU的功能作用是一样的,只不过拓展成了32位。但是某些寄存器仍然可以访问他的高16位或者是低16位或者是低16位中的高8位或者是低8位。

EAX,EAX的低16位AX,AX的高8位AH和低8位AL。
EBX,同EAX
ECX,同EAX
EDX,同EAX

ESI,ESI的低16位SI。
EDI,EDI的低16位DI。
EBP,EBP的低16位BP。
ESP,ESP的低16位SP。

1)通用寄存器(数据寄存器)

寄存器用途
EAX累加器,使用频率较高
EBX基地址寄存器,可以作为存储器指针来使用
ECX计数寄存器
EDX数据寄存器

2) 专用寄存器

两个指针寄存器ESP和EBP,主要存放的是在栈段的偏移量,都是用于访问栈内的存储单元:

指针寄存器用途
EBP基址指针寄存器,
ESP堆栈指针寄存器,用它只可以访问栈顶

两个变址寄存器ESI和EDI,它们主要用于存放存储单元在段内的偏移量, 它们可作一般的存储器指针使用:

变址寄存器用途
ESI源变址寄存器
EDI目的变址寄存器

3)段寄存器(32机器下也是16位的)

需要说明的是,在32位机器下,只有在实地址模式下段寄存器才是存放内存段的段基址,在保护模式下,段寄存器存放的是内存中描述符表的位置。

段寄存器用途
CS代码段寄存器
DS数据段寄存器
SS堆栈段寄存器
ES、FS、GS附加段寄存器

4) 指令指针寄存器-EIP

存放的是下次要执行的指令在代码段的偏移量!使用CS和EIP就能找到下一条指令的存放位置。

2.3 理清几种寄存器之间的角色分类和搭配

1)访问栈中元素的时候

SS+EBP:可以访问整个栈
SS+ESP:只能访问栈顶元素

2)下一条指令

CS+EIP

3)存储器指针

EBX+ESI
EBX+EDI 

2.4 IA32体系下的系统级体系结构资源

之前说到的都是基本执行环境下的一些基本的寄存器,实际上除此之外,还有一些其他的系统级别的资源,这些资源都是进行底层的一些控制的。

主要有如下的几种:

IO端口
控制寄存器
存储管理寄存器
调试寄存器
机器检查寄存器

下面主要展开讲一下存储管理寄存器,其余的那些可以不用看。

在保护模式下通过分段和分页机制进行逻辑地址到物理地址的转换,从而实现虚拟存储。存储管理寄存器就是用于实现这种机制。存储管理寄存器包括:

  • 全局描述符表寄存器GDTR
  • 中断描述符表寄存器IDTR
  • 局部描述符表寄存器LDTR
  • 任务寄存器TR

寄存器的表示:

GDTR和IDTR都是48位的寄存器,高32位分别存放全局描述符表和中断描述符表的线性基地址,低16位存放的是两种表的限长。

LDTR和TR是16位的寄存器。

三 指令系统

寻址方式主要有:

立即数寻址
寄存器寻址
直接寻址
寄存器间接寻址
寄存器相对寻址
基址变址寻址
相对变址寻址

3.1 立即数寻址

MOV AL,10H  # 将10H放到AL寄存器中
MOV EAX,12003400H  # 将12003400放到EAX寄存器中

3.2 寄存器寻址

MOV CL,AL  # 将AL寄存器中的内容放到CL中
MOV AX,BX

3.3 直接寻址方式

直接寻址方式,默认是在数据段上的,除非指定某一个段,

MOV AX,[1200H] # 将数据段中偏移地址为1200H和1201H两个单元的内容放到AX中。(因为AX是16位的,而一个内存单元只是存放8位,也就是一个字节)

MOV BX,ES:[2000H] # 将ES段(附加段)中偏移地址为2000H和2001两单元的内容放到BX中。

3.4 寄存器间接寻址

寄存器中放的是地址的值。当使用寄存器间接寻址的时候,如果没有指定特定的段,那么在以ESP、EBP进行间接寻址的时候,默认的段寄存器为SS。以其他寄存器进行间接寻址的时候,默认的段寄存器为DS。
(注:两个指针寄存器ESP和EBP,主要存放的是在栈段的偏移量,都是用于访问栈内的存储单元,见前)

MOV AX,[BX]  # 设BX=2000H,那么将在DS段(数据段)上偏移地址为2000H和2001H两单元中的内容放到AX中。

MOV AX,[BP] # 设BP=2000H,那么将栈段上偏移地址为2000H和2001H两单元中的内容放到AX中。

MOV ECX,[EDX] # 将DS段(数据段)上偏移地址为EDX中存放的内容的的后序4字节的存储单元中的内容放到ECX寄存器中。

3.5 寄存器相对寻址方式(直接变址寻址方式)

往后的寻址方式,需要熟悉ESI和EDI寄存器,一个是源变址寄存器,另一个是目的变址寄存器。在16位CPU的时代,直接变址寻址方式、间接变址寻址方式等的一些变址寻址方式中,只能使用SI或者DI,但是在32位的时代,不再仅限于ESI和EDI了。但是要知道的是SI、DI的出现就是在变址寻址这个场景下出现的。

在直接变址寻址方式中,默认的段,和寄存器间接寻址是一样的,即使用的寄存器是ESP、EBP、SP、BP的时候,默认的段是在栈段,其他的时候都是在DS段。

MOV BX,100H[SI] (也可表示成MOV BX,[SI+100H]) # 假设SI=2000H,那么将数据段中,偏移地址为2100H和2101H两个单元的内容放到BX中。

MOV DL,ES:BUF[SI]  # 将ES段中,偏移地址为[SI]+BUF中的内容放到DL中。

3.6 基址变址寻址方式

直接变址寻址方式中,默认的段,和寄存器间接寻址是一样的,即使用的寄存器是ESP、EBP、SP、BP的时候,默认的段是在栈段,其他的时候都是在DS段。

MOV AX,[BX][SI] # 假设BX=100H,SI=2000H,那么将数据段中偏移地址为2100H和2101H中的内容放到AX中。

MOV EAX,[EBX][EBP] # 将堆栈段上偏移地址为[EBP+EBX]之后4字节的内容放到EAX上。EBX和EBP都是存放的地址值

3.7 相对基址变址寻址方式

默认的段,和寄存器间接寻址是一样的,即使用的寄存器是ESP、EBP、SP、BP的时候,默认的段是在栈段,其他的时候都是在DS段。

MOV AX,MASK[BX][SI] (也可写成 MOV AX,MASK[SI+BX]或MOV AX,[SI+BX+MASK] )# BX和SI中都是地址值,将数据段中偏移地址为SI+BX+MASK的之后两个单元的位置放到AX中。

3.8 比例变址寻址方式

默认的段,和寄存器间接寻址是一样的,即使用的寄存器是ESP、EBP、SP、BP的时候,默认的段是在栈段,其他的时候都是在DS段。

MOV EAX,COUNT[ESI*4] 

3.9 基址比例变址寻址方式

默认的段,和寄存器间接寻址是一样的,即使用的寄存器是ESP、EBP、SP、BP的时候,默认的段是在栈段,其他的时候都是在DS段。

MOV ECX,[EAX][EDX*8]

3.10 相对基址比例变址寻址方式

默认的段,和寄存器间接寻址是一样的,即使用的寄存器是ESP、EBP、SP、BP的时候,默认的段是在栈段,其他的时候都是在DS段。

MOV,EAX TABLE[EBP][EDI*4]

四 IA32的基本指令集

4.1 数据传送指令

几个示例:

MOV AX,1200H
MOV BX,[BP+SI]
MOV [DI],AX  # DI寄存器中存放的是地址,将AX寄存器中的内容,放到数据段中偏移地址为[DI]的存储单元中

需要注意的一个示例是,源操作数和目的操作数类型必须匹配,如果目的操作数为内存单元,而源操作数为立即数,则必须使用PTR运算符说明目的操作数的属性:

MOV BYTE PTR [SI],24H
MOV WORD PTR BUF[BX][SI],1234H  # word说明是字操作,那么这是将1234H放到指定地址后面的两个连续的存储单元中

4.2 堆栈操作指令

栈是从高地址从低地址增长的。
一个16位数或者是32位的数据进栈规律是,高位字节存入高地址单元,低位字节存入低地址单元;
一个16位数或者是32位的数据出栈规律是,低地址字节存入目的操作数低位,高地址字节存入目的操作数高位;

堆栈的段地址放在SS寄存器中,并且栈顶指针寄存器ESP或SP存放栈顶的有效地址(偏移地址)。

进栈指令:PUSH SRC

当SRC是16位的时候,执行的操作是,先将SP-2,然后将SRC低位字节放到SP指向的存储单元中,将SRC的高位字节放到SP+1指向的存储单元中。

当SRC是32位的时候,执行的操作是,先将ESP-4,然后将SRC放到ESP、ESP+1、ESP+2、ESP+3这是个存储单元中。

出栈指令:POP DST

当DST是32位寄存器的时候,执行的操作是,先将ESP、ESP+1、ESP+2、ESP+3指向的存储单元放到DST中,然后调节栈顶指针,也就是ESP=ESP+4

对于POP的使用,如果内存的操作数不是采用直接寻址,则必须用PTR运算符说明其属性:

POP AX  # 正常的使用
POP DWORD PTR[SI]  # 将ESP指向的地址的开始的的四个单元中的内容放到数据段中有效地址为SI的4个存储单元中,然后将ESP=ESP+4

4.3 数据交换指令

1、交换指令:XCHG DST,SRC
将源操作数与目的操作数内容进行交换。

XCHG AL,BL
XCHG [2350],CX  # 将CX中的内容和数据段偏移地址为2350和2351中的内容进行交换

2、字节交换指令 :BSWAP DST

该指令的DST必须要是32位的寄存器,实现的功能是将DST中3124与70位交换,2316与158位交换

4.4 累加器专用传送指令

这组指令只限于使用累加器EAX、AX、或AL传送信息。包括输出、输出、换码指令.(换码指令先不看了,下面只讲IN、OUT)

使用IN、OUT的场景:

IO端口的地址和内存单元是相互独立的,访问IO端口不能用普通的访问内存的指令来访问其信息。所以就有了输入输出指令(IN、OUT)来完成累加器EAX与IO端口之间的数据传送功能。

使用的注意的地方:

如果IO端口地址为一个字节,即在0~255之间,那么可以采用直接寻址方式,即端口地址直接在输入\输出中直接给出,最多访问256个端口。
如果IO端口地址为大于两个字节,即端口地址>=256,那么要进行间接寻址的方式,即先把端口的地址放在寄存器DX中,然后再进行IN或OUT。
IN和OUT一次传送多少字节,受外设端口宽度的影响,比如,如果端口宽度为8位,那么一次只能传送一个字节!!

示例:

IN AX,20H  # 将端口20H和21H的内容送到AX。(是假设端口宽度是8位的前提下的,也就是一个端口上只有8位的数据,放满AX需要两个端口上的数据)
MOV [20H],AX  # 也就是说,要使用IO端口中的数据,必须要使用EAX、AX和IN、OUT作为中介,不能直接从端口MOV到内存中。

MOV DX,180H
IN EAX,DX # 从180端口读一个双字到EAX中!端口号大于255,要使用DX作为中介存放端口号!(DX代表的是地址)

OUT 70H,AX  # 如果端口宽度是8位,那么将AX中的内容输出到端口70H和71H上

MOV DX 1A8H
MOV AL,40H
OUT DX,AL # 这三条指令是将40H输出到端口1A8H上去。(DX代表的是地址)

4.5 地址传送指令

主要用于将存储器操作数地址(段地址、偏移地址)传送给指定的寄存器,他包含6条指令:LEA、LDS、LES、LFS、LGS、LSS

这些指令就是用法简单,这里不再展开

4.6 标志寄存器传送指令

标志寄存器传送指令用来传送标志寄存器FLAGS的内容,方便对各个标志位的直接操作。标志位传送指令有四条指令:LAHG、SAHF、PUSHF、POPF

1)读取标志指令LAHF

将标志位寄存器的低8位送入AH寄存器。该指令的执行对标志位无影响。

2)设置标志指令SAHF

将AH寄存器的内容送到标志寄存器的低8位,高8位不变。这是用来设置或恢复SF、ZF、AF、PF、CF五个标志位。

3) 标志寄存器进栈指令PUSHF/PUSHFD

功能是将标志寄存器中的内容进栈。
PUSHF是将SP中的内容进栈,也就是先将SP=SP-2,然后再把FLAGS的内容放到SP和SP+1指向的内存单元上。
PUSHFD是将ESP中的内容进栈,也就是先将ESP=ESP-4,然后再把FLAGS中的内容放到那四个内存单元上。

4)标志寄存器出栈指令POPF/POPFD

将栈顶内容放到标志寄存器中。
POPF是先将SP和SP+1的内容放到FLAGS中,然后再把SP=SP-2
POPFD和POPF相同,只不过操作的是ESP

4.7 类型转换指令

1)字节拓展到字CBW

2)字拓展到双字CWD

3)字拓展到双字CWDE

4)双字拓展到四字CDQ

4.8 算数运算指令

4.8.1 加法指令

1)ADD DST,SRC

将目的操作数与源操作数相加,送到目的操作数,源操作数不变。并设置响应的标志位(如进位标志和就标志)
ADD AL,20H
ADD EAX,ECX # 将ECX中的内容和EAX中的内容放在EAX中
ADD BYTE PTR [BX],12H  # 将数据段中偏移地址为[BX]的存储单元中的字节和12H相加,结果送回该单元
ADD WORD PTR [BX],12H  # 将数据段中偏移地址为[BX]和[BX+1]的内容和12H相加,结果送回这两个单元

2)ADC DST,SRC

将目的操作数加源操作数再加进位标志CF,结果送到目的操作数中,并设置相应的标志位。

3)INC OPR

将目的操作数加1,送到目的操作数中。
主要是用于对计数器加1或者是对地址指针加1,所以不影响进位标志CF!
INC BX
INC BYTE PTR [BX+DI+500]

4)交换并相加指令 XADD DST,SRC

先交换,然后再相加
XADD BL,CL # 如果BL=12H,CL=02H,那么操作后BL=14H,CL=12H

4.8.2 减法指令

1)SUB DST,SRC

将DST中的内容减去SRC,结果放到DST中。并设置相应的标志位。(产生进位或者借位的时候,CF都会置1)
示例:
SUB BX,3440H
SUB [BP+2],CL
SUB WORD PTR [DI],1000H # 将数据段中有效地址为DI的存储单元之后的两个字节减去1000H

2)SBB DST,SRC

将DST减去SRC再减去CF,结果放到DST中。并会设置相应的标志位。

3)DEC OPR

将目的操作数减去1。和INC的用途是一样的,所以也不会设置相应的标志位。

4) 求补指令:NEG OPR

对目的操作数进行求补运算。

5) 比较指令:CMP OPR1,OPR2

将目的操作数减去源操作数,但是结果不会送回目的操作数,而是只会影响标志位。
所以可以根据标志位判断两个操作数的大小。

4.8.3 乘法指令

1)无符号乘法指令:MUL SRC
2)有符号数乘法指令:IMUL SRC

两个指令的使用都是相同的:

如果SRC是8位的寄存器,那么执行的是字节乘法:AX=AL*SRC
如果SRC是16的位寄存器,那么执行的是字乘法:(DX,AX)=AX*SRC  ,相乘的高位放在DX,地位放在AX
如果SRC是32位的寄存器,那么执行的是双字乘法:(EDX,EAX)= EAX*SRC ,结果的高位放在EDX,低位放在EAX

值得注意的是:

- MUL和IMUL的不同就是对SRC中的数据的解释方式不一样,MUL将其解释成无符号的,IMUL将其解释成无符号的。
- 乘法指令对CP和OF有影响,对其他标志位无定义(也就是说影响任意,不可预测,可以理解为无影响)。

4.8.4 除法指令

执行两个二进制的除法运算。

1)无符号除法指令DIV SRC

2)有符合除法指令IDIV SRC

4.9 逻辑指令

1)逻辑与指令:AND DST,SRC

将目的操作数和源操作数进行按位与,结果送到目的操作数中。
示例:
AND AL,77H
AND AX,BX

2) 逻辑或指令:OR DST,SRC

按位或

3) 逻辑非运算 NOT OPR

对目的操作数进行按位取反。

4)异或指令:XOR DST,SRC

将目的操作数和源操作数进行按位异或,结果放在目的操作数中

5)位测试并修改指令

测试目的操作数中由源操作指定的那一位,将测试位的值送CF。

- 位测试指令:BT DST,SRC  #目的操作数不变
- 位测试并置1指令: BTS DST,SRC # 将测试结果送CF后,将测试位置1
- 位测试并置0指令:BTR DST,SRC # 将测试结果送CF后,将测试位置0
- 位测试并变反指令:BTC DST,SRC # 将测试结果送CF后,将测试位置取反

示例:
BT AX,0 # 将AX的第0位送CF
BTR EAX,31  #将EAX的31位送CF,并将其第31位置1

6)位扫描指令

- 正向位扫描指令:BSF REG,SRC ,从低位往高位开始扫描,将遇到的第一个1的位序号存入目的寄存器中,并将ZF置0。若源操作数为0,则将ZF置1,目的寄存器内容不变。
- 反向位扫描指令:BSR REG,SRC ,和BSF一样,只是扫描顺序不一样。

示例:
MOV EAX,23456780H 
BSF EBX,EAX # (EBX)=7,EAX不变,ZF=0

7)移位指令

4.10 串处理指令

先略

4.11 控制转移指令

控制转移指令就是通过修改CS和IP或EIP中的值来控制程序的执行流程的。
控制转移指令包含5类指令:无条件转移指令、条件转移指令、循环指令、子程序调用指令、返回指令、中断和中断返回指令。

五 总结

上面的只是简单的进行了片面的总结,后序还需要对其中的很多东西进行补充、更新。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值