Σ(゚∀゚ノ)ノ✓参见龙王

x86汇编语言总结

X86处理器架构

微机基本结构

中央处理器CPU

CPU通过主板插槽的引脚与计算机的其他部分相连接

内存存储单元

内存存储单元是计算机程序运行时存放指令和数据的地方

输入/输出设备

I/O设备,如显示器、键盘、鼠标、硬盘等等

三大总线

指令执行周期

程序在开始执行之前必须装入内存

指令指针寄存器:存有要执行的下一条指令的地址

指令队列:存放着若干条要执行的指令

执行步骤

取指令、解码、执行

若指令使用内存操作数,还需取操作数、存储输出操作数

多级流水线

指令执行的每一步至少占用一个时钟周期,处理器可以采用流水线技术来实现多个部件间的并行处理,以提高系统的效率。

486采用6级流水线,每级流水线有其对应的执行部件

超标量体系结构

有两条及以上的执行流水线

使得同时可以执行多条指令

内存读取

系统的吞吐量通常依赖于内存的访问速度

CPU时钟频率很高,通过系统总线的内存访问速度往往偏低,二者之间速度不匹配。一般要求快速设备插入等待周期以等待慢速设备

内存操作周期

一个内存操作,往往需要多个时钟周期

缓存

由于内存与CPU速度相差巨大,目前一般是采取缓存的方式来提高速度

缓存的存取速度比普通内存快得多

程序在第一次读取某数据时,将此数据范围内的一块内存数据读入并存放到缓存中。以后再操作此块内存时,不进行真正的内存操作,而在缓存中进行。

32位X86处理器

操作模式
执行环境

X86内存管理

实地址模式

20位地址线

可以访问1M字节的内存

地址范围0-0FFFFFH

保护模式

保护模式是一个强大的易用的处理器模式

地址线32根,可寻址4G空间

地址范围0-0FFFFFFFFH

保护模式使用内存的方式又分成平坦分段模式和多段模式

Windows平台下采用平坦分段模式,使用一个32位整数就可以存放任何指令和变量的地址

输入输出系统

应用程序通常从键盘和文件读取输入,并将输出写到屏幕或文件中。输入输出(I/O)不必通过直接访问硬件就能完成,通过直接调用操作系统的功能函数就能完成。有三种基本的访问层次:

Windows汇编编程,操作系统为了保护系统资源,不允许普通应用程序直接访问硬件资源

汇编语言基础

基本元素

保留字
标识符

程序员命名的符号,用于标识变量、常量、过程名或代码标号

标识符长度1-247个字符,默认大小写不敏感

第一个字符可以是字母、@、?、$,不能是数字

不能与保留字同名

伪指令

由汇编器识别并在编译时执行相应动作的命令,不在运行时执行

伪指令可用于定义变量、过程、宏、定义段等

每个汇编器都有一套自己的伪指令,不同汇编器可能差异较大

指令

汇编语言中的指令是一条汇编语句,在程序被汇编后变成可执行的机器指令

一条汇编指令包括4个部分:标号(可选)、助记符、操作数、注释(可选)

汇编程序

编程者用文本编辑器创建一个ASCII文本文件,称作源文件

汇编器将源代码生成含机器码的文件,称作目标文件,目标文件不能执行(MASM)

链接器将目标文件连接生成可执行文件(LINK)。生成执行文件后,就可以按操作系统支持的方式启动执行

定义数据

内部数据类型

有无符号主要是方便程序员区分,汇编器并不区分有无符号

数据定义语句

数据定义语句在内存中给变量分配存储空间,可以指定名字

[变量名] 数据类型伪指令 初始值

变量名可选,代表变量地址,汇编后用地址来区分

初始值:定义变量时需指定初始值,如果不想指定初始值,可以用问号代替

DUP运算符

使用一个整数表达式作为计数器,为多个数据项分配存储空间

V1 BYTE 10 dup(0)
变量加法

小端顺序

Intel处理器使用小段顺序存储,变量的最低字节存储在最低地址单元,其他字节依次存储

如12345678h这个双字在内存中的存储情况

未初始化数据的声明

符号常量

通过为证书表达式或文本指定标识符来创建符号常量

符号不预留存储空间

相当于C语言里用define定义的宏

可以用 = 、 EQU 、 TESTEQU等三种方式进行创建

等号伪指令
COUNT = 500
mov ax,COUNT	;
//将生成并编译为mov ax,500
EQU伪指令

数据传送寻址和算术运算

数据传送指令

操作数类型

直接内存操作数

变量名是该变量相对其所在的数据段开始处的偏移

.data
Var1 byte 10h

当指令使用内存操作数时,实际机器指令中使用的时操作数的地址(偏移量)

MOV指令
MOV dest,src

MOV指令有两个操作数,第一个为目的操作数,第二个为源操作数

其基本功能是将源操作数复制到目的操作数

规则

内存间的移动

单挑MOV指令不能把数据从一个内存位置直接移动到另一个内存位置,需要借助寄存器实现

.data
Var1 word ?
Var2 word ?

.code
Mov ax,Var1
Mov Var2,ax

复制整数常量到变量或寄存器时需要考虑该变量需要的最小字节数

.data
oneByte BYTE 78h
oneWord WORD 1234h
oneDWord DWORD 12345678h

.code
mov eax,0
mov al,oneByte
mov ax,oneWord
mov eax,oneDWord
mov ax,0
整数的0符号扩展

目的操作数不支持内存操作数,不支持段寄存器

LAHF/SAHF

两条指令均无操作数

XCHG指令

将两个操作数的内容交换,操作数不可以是立即数

xchg ax,bx
xchg ah,al
xchg var1,bx
xhchg eax,ebx

交换两个内存操作数

Mov ax,var1
xchg ax,var2
mov var1,ax
直接-偏移操作数

在变量名后加上一个偏移值,称作直接-偏移操作数

可以通过它来访问没有显示标号的内存地址

.data
arrayB byte 10h,20h,30,40

.code
mov al,arrayB	;al=10h
mov al,[arrayB+1]	;al=20h
mov al,arrayB+1	;

加法和减法

INC和DEC指令

INC加1,DEC减1

会影响到标志位(AF、OF、PF、SF、ZF),但不影响进位标志位(CF)

.data
myWord  WORD 1000h
myDword DWORD 10000000h
.code
	inc myWord 	; 1001h
	dec myWord	; 1000h
	inc myDword	; 10000001h

	mov ax,00FFh
	inc ax	; AX = 0100h
	mov ax,00FFh
	inc al	; AL=00h, AX = 0000h
ADD和SUB指令

将相同尺寸的源操作数和目的操作数相加,结果存到目的操作数

指令会修改CF、ZF、SF、OF、AF、PF

NEG指令

求数的补码(按位取反再加1)

求数的相反数(用0去减操作数)

指令会修改CF、ZF、SF、OF、AF、PF

.data
varB BYTE -1
varW WORD +32767

.code
	mov al,varB ;AL = -1
	neg al      ;AL = +1
	neg varW    ;varW = -32767
影响的标志位

AddSubTest

OFFSET操作符

返回数据标号的偏移地址,以字节为单位

ALIGN

将变量的地址按指定的大小对齐

大小可以指定为1,2,4,8,16(.386只支持1,2,4),默认大小为1

PTR

重载操作数声明的默认大小,类似于强制类型转换

.data
myDouble DWORD 12345678h

.code
	mov ax,word ptr myDouble

TYPE

返回按字节计算的变量的单个元素的大小

LENGTHOF

返回操作数的元素个数,只返回同一行的元素个数

如果声明的数组初值跨行,则只计算第一行的初值个数

如果初值的末尾是逗号,则会包含下一行的个数

SIZEOF

返回操作数所占的字节数总数(LENGTHOF和TYPE返回值的乘积)

LABEL伪指令

为其后定义的变量提供一个别名以及一个不同的尺寸属性

它不会分配存储空间,不再需要PTR操作符

.data
dwList LABEL DWORD
wordList LABEL WORD
intList BYTE 00h,10h,00h,20h

.code
mov eax,dwList ;20001000h
mov cx,wordList ;1000h
mov dl,intList ;00h

间接寻址

利用寄存器内的值作为地址,访问操作数的方式称作间接寻址

mov ax,[si]	//用si寄存器中的值作为地址,访问该内存单元,取出值存到ax中
间接操作数

用寄存器作为指针并操纵寄存器的值,称为间接寻址

操作数使用间接寻址时,称为间接操作数

任何一个32位通用寄存器加上括号就能构成一个间接操作数

数组应用

变址操作数

把寄存器的值加上一个常量作为地址,访问内存操作数的方式,称作变址寻址

JMP和LOOP指令

默认情况下,CPU按顺序执行指令

汇编语言通过控制转移改变语句的执行顺序,有两类控制转移指令

JMP指令
JMP 目的地址

接着从目的地址开始执行指令

LOOP指令
LOOP 目的地址

目的地址和当前地址只能相距-128-127之间,循环可以嵌套

数组求和

复制字符串

过程

堆栈操作

堆栈是CPU直接管理的内存数组,使用到两个寄存器:SS和ESP

保护模式下,SS段寄存器内存放的是用于寻找栈段的段描述符选择子,不应修改

ESP是指向栈的特定位置的一个32位偏移值,指向最后压入到栈的数据

堆栈只有一个出口,即当前栈顶,用堆栈指针寄存器ESP指定

栈顶是地址较小的一端,栈底不变

堆栈的特点
PUSH

进栈指令先使堆栈指针ESP减4

将要压入的32位值拷贝到ESP指向的内存

PUSH r/m16
PUSH r/m32
PUSH imm32

对于32位操作数,ESP减4,存到栈中的内容为双字

对于16位操作数,ESP减2,存到栈中的内容为字

保护模式的立即数总是32位的

POP

将ESP指向的内存中的内容取出来,放到操作数中

将ESP+4

POP r/m16
POP r/m32

对于32位操作数,先从栈中拷贝双字到操作数中,然后ESP加4

对于16位操作数,先从栈中拷贝字到操作数中,然后ESO加2

PUSHFD和POPFD
PUSHAD和POPAD
字符串反转

过程的定义和使用

将经常用到的应用问题编写成一个通用子程序

使用子程序可以使程序的结构更为清晰,程序的维护更为方便,有利于分工合作

子程序指令
向过程的传递参数

参数传递方法:寄存器、变量、堆栈

参数具体内容:数据本事、数据的存储地址

USES操作符

在定义过程时,可以在porc后跟uses操作符,然后再跟要保护的寄存器列表

指示汇编器在过程开始时添加压栈指令;在过程结束时加出栈指令

寄存器保护原则
堆栈平衡

一个程序模块压入堆栈多少字节的数据,最终则应该弹出多少字节的数据,使得堆栈指针ESP的值不变

主程序CALL指令将返回地址压入堆栈,子程序RET指令将返回地址弹出堆栈

只有堆栈平衡,才能保证执行RET指令时,当前栈顶刚好是返回地址

链接到外部库

链接库是一种文件,包含了已经汇编为机器代码的过程

链接库开始是一个或多个源文件,这些文件再被汇编成目标文件

在汇编阶段,不再指定call指令的目标地址;在链接阶段,先在链接库中寻址WriteString,并把库中适当的机器指令复制到程序的可执行文件中

Irvine链接库

文件操作类
控制台窗口属性设置类
调试信息输出类
输入输出类
输入检查、转换类
其他函数

条件处理

布尔和比较指令

CMP指令

目的操作数减去源操作数,不修改操作数的值,但会修改标志位的值

CMP的结果

置位和清除单个CPU标志位

条件跳转

Jcond destination

相当于 jmp if cond

当条件为真时,分支转移到目的地址处执行;条件为假时执行后面的指令

基于特定标志位跳转

基于操作数是否相等跳转

基于无符号操作数比较结果

基于有符号操作数比较结果

条件循环指令

LOOPZ指令
LOOPZ 目的地址

ECX不等于0且ZF标志位为1时跳转

目的地址与当前指令的距离在-128-127之间

LPPONZ指令

ECX不等于0且ZF标志位为0时跳转

IF语句
x dword ?
y dword ?

cmp x,y
jb S1
jmp S2
S1:statement-1
jmp:quit
S2:statement-2
jmp:quit
quit:

整数运算

移位和循环移位

位操作的指令分为移位指令和逻辑指令

移位的含义是将操作数向左或向右移动

IA-32共有十条移位指令,会影响溢出标志位和进行标志位

逻辑移位和算术移位

指令

乘法和除法指令

乘除法运算对于有符号数和无符号数使用不同的指令

无符号乘法
MUL reg/mem8
MUL reg/mem16
MUL reg/mem32

指令中只有一个操作数,另一个操作数是隐含的

mov al,30h
mov bl,4h
mul b1 ;AX = 0C0H,CF = 0,OF = 0

mov ax,2000h
mov bx,100h
mul bx ;DX:AX = 0200 0000h,CF = 1,OF = 1
有符号乘法

指令的操作数分成单操作数、双操作数、三操作数三种

单操作数

IMUL reg/mem8
IMUL reg/mem16
IMUL reg/mem32

若结果的高半部分不是低半部分的符号扩展,OF、CF置位

双操作数

IMUL r16, r/m16
IMUL r16, imm8
IMUL r16, imm16
IMUL r32, r/m32
IMUL r32, imm8
IMUL r32, imm32

两个操作数相乘,结果存到第一个操作数中

如果第一个操作数存不下结果,OF、CF置位

三操作数

IMUL r16, r/m16, imm8
IMUL r16, r/m16, imm16
IMUL r32. r/m32. imm8
IMUL r32, r/m32. imm32

两个操作数相乘,结果存到第一个操作数中

如果第一个操作数存不下结果,OF、CF置位

无符号除法
DIV r/m8	AX / op8  = AL(商) …AH(余数)
DIV r/m16	DX:AX/op16 = AX…DX
DIV r/m32	EDX:EAX/op32=EAX…EDX

实现整除运算,结果包括两项:一项为商,一项为余数

mov ax,83h
mov b1,2h
DIV b1 ;AL=42H,AH=1

mov dx,0
mov ax,8003h
mov cx,100h
DIV cx ;AX=008H,DX=3h
有符号除法

有符号在进行除法操作欠往往要进行符号扩展

共有三条符号扩展指令,其操作数在指令中不给出,均为隐含的寄存器

mov AL,-48
mov bl,5
cbw
IDIV bl ;AL=-9,AH=-3

mov ax,-5000
mov cx,256
cwd
IDIV cx ;AX=-19,DX=-136

在做除法时,如果商太大,目的操作数无法容纳,则会置位溢出位OF,引发中断

当除数为0时,也会引发中断,通常在做除法前,需要判断除数是否为0

扩展的加法和减法

ADC

带进位加

将两个操作数与进位位一起相加,结果存放到目的操作数中

SBB

带借位减

将目的操作数减去源操作数,再减进位位,结果存放到目的操作数中

高级过程

堆栈帧

CPU中用到堆栈的地方很多

堆栈帧是一块堆栈保留区域,它是为传递的参数、子程序的返回地址、局部变量和保存的寄存器保留的堆栈空间

给子过程传递参数有两种基本方式

创建步骤
  1. 如果有传递的参数,则压入堆栈
  2. 子程序被调用,子程序的返回地址压入堆栈
  3. 子程序开始执行时,EBP被压入堆栈
  4. EBP设为ESP的值,EBP就被作为寻址所有子程序参数的基址指针使用
  5. 如果有局部变量,ESP减去一个数值,以遍在堆栈上为局部变量保留空间
  6. 如果任何寄存器需要保存,则压入堆栈

串操作

基本串操作指令

每个指令因处理对象的宽度不同,各有三个后缀B、W、D

这些指令都使用各自隐含的寄存器,源操作数由DS:ESI指向,目的操作数由ES:EDI指向。有的指令涉及到累加器,因处理宽度不同分别对应AL、AX、EAX

在保护模式下,ES和DS的值相同,且程序不能修改

MOVS

MOVS指令把ESI指向的内存内容复制到EDI指向的内存单元中

相当于mov [edi],[esi]

ESI和EDI两个寄存器的值同时增加或减少,如DF位为0则增加,否则减少

CMPS

将ESI指向的内存同EDI指向的内存相比较

相当于cmp [esi],[edi]

同时修改ESI和EDI的值

SCAS

SCASB指令将AL的值与EDI指向的内存内容相比较

相当于cmp AL,[edi]

即做查找操作

SCASW是用AX作字查找,SCASD是用EAX作双字查找

STOS

将累加器的内容存储到edi指向的内存单元中,同时修改edi的值

LODS

将从esi指向的内存内容取出存到累加器中,同时修改esi的值

二维数组

基址变址寻址方式是把两个寄存器的值相加,得到的和作为偏移地址,两个寄存器第一个称作基址,另一个称作变址

在32位模式下,基址和变址可以使用任意通用32位寄存器

在16位模式下,基址只能是BX或BP,变址只能是SI或DI

寻址方式总结

退出移动版