2.6 指令指针寄存器和简单控制转移
2.6.1 指令指针寄存器
2.6.2 常用条件转移指令
2.6.3 比较指令和数值大小比较
2.6.4 简单无条件转移指令
2.6.1 指令指针寄存器
有一个
32
位的指令指针寄存器
EIP
.
由
CS
和
EIP
确定所取指令的存储单元地址
指令指针寄存器
EIP
给出偏移
.IA-32系列CPU有一个32位的指令指针寄存器EIP
.它是早先8086CPU指令指针寄存器IP的扩展
.由CS和EIP确定所取指令的存储单元地址。段寄存器CS
给出当前代码段的段号,指令指针寄存器EIP给出偏移
.如果代码段起始地址是0,则EIP给出的偏移,或者说有
效地址,直接决定所取指令的存储单元地址
.实方式下,段的最大范围是64K,EIP中高16位必须是0,
相当于只有低16位的IP起作用
.指令指针寄存器
2.6.1 指令指针寄存器
自动调
整指令指针寄存器
EIP
的值
,
使其指向下一条指令
顺序执行指令
.CPU执行代码(程序)就是一条接一条地执行机器指令。
可以把CPU执行指令的过程看作一条处理指令的流水线,
其第一步是从存储器中取出指令。
.在取出一条指令后,会根据所取指令的长度,自动调
整指令指针寄存器EIP的值,使其指向下一条指令。这样,
就实现了顺序执行指令。
.顺序执行指令的过程
2.6.1 指令指针寄存器
非自动顺序调整
EIP
.所谓转移指,非自动顺序调整EIP内容
.控制转移指令就是专门用于改变EIP内容的指令
.多种控制转移指令:
.条件转移指令
.无条件转移指令
.循环指令
.函数调用及返回指令等
.各种控制转移指令用于根据不同的情形改变EIP内容,
从而实现转移
.控制转移指令
2.6.2 常用条件转移指令
.
标志寄存器中的状态标志被用于表示条件
.所谓条件转移指,当某一条件满足时,发生转移,否则
继续顺序执行。换句话说,当某一条件满足时,就改变
EIP的内容,从而实施转移,否则顺序执行
.标志寄存器中的状态标志被用于表示条件。绝大部分
条件转移指令根据某个标志或者某几个标志来判断条件
是否满足
.条件转移
条件转移
类似于高级语言的
分支
条件转移
类似于高级语言的
分支
2.6.2 常用条件转移指令
.条件转移
.根据一个标志判别
.根据两个标志判别
.根据三个标志判别
J
C
LAB1 ;
Jump if carry ( CF=1 )
J
BE
LAB2 ; Jump if below or equal ( CF=1
或
ZF=1 )
J
LE
LAB3 ;
Jump if less or equal ( ZF=1
或
SF≠OF )
JC LAB1 ; Jump if carry ( CF=1 )
JBE LAB2 ; Jump if below or equal ( CF=1 或 ZF=1 )
JLE LAB3 ; Jump if less or equal ( ZF=1 或 SF≠OF )
.演示程序dp213
#include <
stdio.h
>
int
arri
[] = {23, 56, 78, 82, 77, 35, 22, 18, 44, 67};
int
main( )
{
int
sum; //
用于存放累加和
//
嵌入汇编
_
asm
{
.. .. ..
.. .. ..
}
printf
("sum=%d
\
n", sum); //
显示为
sum=502
return 0;
}
#include <stdio.h>
int arri[] = {23, 56, 78, 82, 77, 35, 22, 18, 44, 67};
int main( )
{
int sum; //用于存放累加和
//嵌入汇编
_asm {
.. .. ..
.. .. ..
}
printf("sum=%d\n", sum); //显示为sum=502
return 0;
}
2.6.2 常用条件转移指令
计算整型数组arri中
10个元素值之和
.演示程序dp213
_
asm
{
MOV EAX, 0 //
用于存放累加和
MOV ESI, 0 //
作为数组的下标(索引)
MOV ECX, 10 //
作为计数器
LEA EBX,
arri
//
得到数组首元素的有效地址
NEXT:
ADD EAX, [EBX+ESI*4] //
累加某个元素值(由索引确定)
INC ESI //
调整下标
DEC ECX
//
计数器减
1
(该指令会影响状态标志)
JNZ NEXT
//
当
ECX
不为
0
,则从
NEXT
处继续执行
;
MOV sum, EAX //
保存累加和
}
_asm {
MOV EAX, 0 //用于存放累加和
MOV ESI, 0 //作为数组的下标(索引)
MOV ECX, 10 //作为计数器
LEA EBX, arri //得到数组首元素的有效地址
NEXT:
ADD EAX, [EBX+ESI*4] //累加某个元素值(由索引确定)
INC ESI //调整下标
DEC ECX //计数器减1(该指令会影响状态标志)
JNZ NEXT //当ECX不为0,则从NEXT处继续执行
;
MOV sum, EAX //保存累加和
}
2.6.2 常用条件转移指令
计算整型数组arri中
10个元素值之和
.Jcc指令的一般格式
Jcc LABEL
.符号cc表示各种条件缩写,LABEL代表源程序中的标号。
.当条件满足时,转移到标号LABEL处;否则继续顺序执行。
.条件转移指令(Jcc)
2.6.2 常用条件转移指令
条件转移指令是使用得最多的控制转移指令。
条件转移指令是使用得最多的控制转移指令。
.注意:同一指令,可能有多个助记符
.条件转移指令(Jcc)
2.6.2 常用条件转移指令
JB LABEL3 ;Jump if below
JNAE LABEL3 ;Jump if not above or equal
JC LABEL3 ;Jump if carry
JB LABEL3 ;Jump if below
JNAE LABEL3 ;Jump if not above or equal
JC LABEL3 ;Jump if carry
2.6.2 常用条件转移指令
.条件转移指令(Jcc)
指令格式
指令格式
转移条件
转移条件
转移说明
转移说明
其他说明
其他说明
JZ 标号
JE 标号
ZF=1
同上
等于0转移(Jump if zero)
相等转移(Jump if equal)
单个标志
JNZ 标号
JNE 标号
ZF=0
同上
不等于0转移(Jump if not zero)
不相等转移(Jump if not equal)
单个标志
JB 标号
JNAE 标号
JC 标号
CF=1
同上
同上
低于转移
不高于等于转移
进位位被置转移
单个标志
(无符号数)
JNBE 标号
JA 标号
(CF或ZF)=0
同上
不低于等于转移
高于转移
两个标志
(无符号数)
JLE 标号
JNG 标号
((SF异或OF)或ZF)=1
同上
小于等于转移
不大于转移
三个标志
(有符号数)
JNLE 标号
JG 标号
((SF异或OF)或ZF)=1
同上
不小于等于转移
大于转移
三个标志
(有符号数)
SUB ECX, 0 ;
在不改变
ECX
值的同时,根据
ECX
的值影响标志
MOV EAX, 0 ;
先假设
ECX
值为
0
JZ OVER ;
如果
ZF=1
(表示确实为
0
),转移到标号
OVER
处
MOV EAX, 1 ;
再假设
ECX
值为正
JNS OVER ;
如果
SF=0
(表示确实为正),转移
MOV EAX,
-
1 ;
至此,
ECX
值为负
OVER
:
SUB ECX, 0 ;在不改变ECX值的同时,根据ECX的值影响标志
MOV EAX, 0 ;先假设ECX值为0
JZ OVER ;如果ZF=1(表示确实为0),转移到标号OVER处
MOV EAX, 1 ;再假设ECX值为正
JNS OVER ;如果SF=0(表示确实为正),转移
MOV EAX, -1 ;至此,ECX值为负
OVER:
.条件转移指令(Jcc)
2.6.2 常用条件转移指令
把寄存器
ECX
中的值视为有符号数。如下指令片段的功能:
当
ECX
中的值为
0
时,使
EAX
为
0
;
当
ECX
为正数时,使
EAX
为
1
;否则使
EAX
为
-
1
。
把寄存器ECX中的值视为有符号数。如下指令片段的功能:
当ECX中的值为0时,使EAX为0;
当ECX为正数时,使EAX为1;否则使EAX为-1。
2.6.2 常用条件转移指令
.
条件转移指令本身不影响标志
条件转移的转移目的地仅限于同
一个代码段内
.条件转移指令本身不影响标志。
.条件转移指令在条件满足的情况下,只改变指令指针
寄存器EIP。也就是说,条件转移的转移目的地仅限于同
一个代码段内。这种不改变代码段寄存器CS,仅改变
EIP的转移被称为段内转移。
.条件转移指令可以实现向前方转移,也可以实现向后
方转移。
.说明
今后将进一步介绍条件转移指令
2.6.3 比较指令和数值大小比较
.比较指令(CMP)
.根据DEST – SRC的差影响标志寄存器中的各状态标志,
但不把作为结果的差值送的目的操作数DEST。
.比较指令的一般格式
CMP DEST,SRC
除了不把结果送到目的操作数外,这条指令与
SUB
指令一样。
两个操作数的尺寸必须一致
。
除了不把结果送到目的操作数外,这条指令与SUB指令一样。
两个操作数的尺寸必须一致。
2.6.3 比较指令和数值大小比较
.比较指令(CMP)
CMP EDX,
-
2 ;
把
EDX
与
-
2
比较
CMP ESI, EBX ;
把
ESI
与
EBX
比较
CMP AL, [ESI] ;AL
与由
ESI
所指的字节存储单元值作比较
CMP [EBX+EDI*4+5], DX ;
由
EBX+EDI*4+5
所指字存储单元值与
DX
作比较
CMP EDX, -2 ;把EDX与-2比较
CMP ESI, EBX ;把ESI与EBX比较
CMP AL, [ESI] ;AL与由ESI所指的字节存储单元值作比较
CMP [EBX+EDI*4+5], DX ;由EBX+EDI*4+5所指字存储单元值与DX作比较
.使用举例
2.6.3 比较指令和数值大小比较
.比较数值大小
根据零标志
ZF
是否置位,判断两者是否相等;
一套适用于无符号数之间的比较,另一套适用于有符
号数之间的比较
有符号数间的次序关系称为大于
(G)
、等于
(E)
和小于
(L)
无符号数间的次序关系称为高于
(A)
、等于
(E)
和低于
(B)
。
.为了比较两个数值的大小,一般使用比较指令。
根据零标志ZF是否置位,判断两者是否相等;
如果两者是无符号数,可根据进位标志CF判断大小;
如果两者是有符号数,要同时根据符号标志SF和溢出标志OF判断大小。
.为了方便进行数值大小比较,IA-32系列CPU提供两套以数值大小为条
件的条件转移指令,一套适用于无符号数之间的比较,另一套适用于有符
号数之间的比较。这两套条件转移指令判断的标志是不同的。
有符号数间的次序关系称为大于(G)、等于(E)和小于(L);
无符号数间的次序关系称为高于(A)、等于(E)和低于(B)。
在使用时要注意区分它们,不能混淆。
2.6.3 比较指令和数值大小比较
.比较数值大小
如果这两个数是有符号数,则代码片段如下:
CMP ECX, EDX
J
GE
OK
;
有符号数
比较大小转移(判断
SF
和
OF
)
XCHG ECX, EDX
OK
:
如果这两个数是无符号数,则代码片段如下:
CMP ECX, EDX
J
AE
OK
;
无符号数
比较大小转移(判断
CF
)
XCHG ECX, EDX
OK
:
如果这两个数是有符号数,则代码片段如下:
CMP ECX, EDX
JGE OK ;有符号数比较大小转移(判断SF和OF)
XCHG ECX, EDX
OK:
如果这两个数是无符号数,则代码片段如下:
CMP ECX, EDX
JAE OK ;无符号数比较大小转移(判断CF)
XCHG ECX, EDX
OK:
设
ECX
和
EDX
含有两个数,现要求把较大者保存在
ECX
中,
较小者保存在
EDX
中。
设ECX和EDX含有两个数,现要求把较大者保存在ECX中,
较小者保存在EDX中。
2.6.3 比较指令和数值大小比较
.比较数值大小
int
_
fastcall
cf214(
int
x,
int
y)
{
int
z = 1;
if ( x >= 13 && y <= 28 )
z = 2;
return z;
}
int _fastcall cf214(int x, int y)
{
int z = 1;
if ( x >= 13 && y <= 28 )
z = 2;
return z;
}
调用约定 _fastcall
寄存器传递参数
查看如下函数
cf214
的目标代码:
查看如下函数cf214的目标代码:
2.6.3 比较指令和数值大小比较
.比较数值大小
mov
eax
, 1
cmp
ecx
, 13 ;x
与
13
比较
jl
SHORT LN1cf214
;
小于,则转移
cmp
edx
, 28 ;y
与
28
比较
jg
SHORT LN1cf214
;
大于,则转移
mov
eax
, 2
LN1cf214:
ret ;
返回到调用者,函数
cf23
结束返回
mov eax, 1
cmp ecx, 13 ;x与13比较
jl SHORT LN1cf214 ;小于,则转移
cmp edx, 28 ;y与28比较
jg SHORT LN1cf214 ;大于,则转移
mov eax, 2
LN1cf214:
ret ;返回到调用者,函数cf23结束返回
符号“SHORT”表示转移目的地就在附近
ECX
传递
x , EDX
传递
y
EAX
作为变量
z
ECX传递x , EDX传递y
EAX作为变量z 函数
cf214
的目标代码:
函数cf214的目标代码:
2.6.4 简单无条件转移指令
无条件转移指令
类似于高级语言的
goto
语句
无条件转移指令
类似于高级语言的
goto语句
2.6.4 简单无条件转移指令
.无条件转移指令(JMP)
.指令使控制无条件地转移到标号LABEL位置处。
.所谓无条件是指没有任何前提,肯定实施转移。
. 无条件转移指令的一般格式
JMP LABEL
段内
直接
无条件 段内 直接 转移指令
2.6.4 简单无条件转移指令
.无条件转移指令(直接、段内)
CMP ECX, 3 ;
比较
ECX
和
3
JAE LAB1 ;
如
ECX>=3
,转移到标号
NEXT
处
MOV EAX, 7 ;
否则,
ECX=7
JMP LAB2
;
无条件转移到
LAB2
处
LAB1:
MOV EAX, 5
LAB2:
CMP ECX, 3 ;比较ECX和3
JAE LAB1 ;如ECX>=3,转移到标号NEXT处
MOV EAX, 7 ;否则,ECX=7
JMP LAB2 ;无条件转移到LAB2处
LAB1:
MOV EAX, 5
LAB2:
把寄存器
ECX
中的值视为无符号数。
当
ECX
中的值大于等于
3
时,使
EAX
为
5
;否则使
EAX
为
7
。
把寄存器ECX中的值视为无符号数。
当ECX中的值大于等于3时,使EAX为5;否则使EAX为7。
效率上,这样的代码片段并不好;
可读性,可以接受
效率上,这样的代码片段并不好;
可读性,可以接受
2.6.4 简单无条件转移指令
.示例
int
_
fastcall
cf215(
int
x,
int
y)
{
int
z;
if ( x > 10 ) //
语句
A
z = 3*x+4*y+7;
else
z = 2*x+7*y
-
12;
if ( y <= 20 ) //
语句
B
z = 4*z+3;
return z; //
语句
C
}
int _fastcall cf215(int x, int y)
{
int z;
if ( x > 10 ) //语句A
z = 3*x+4*y+7;
else
z = 2*x+7*y-12;
if ( y <= 20 ) //语句B
z = 4*z+3;
return z; //语句C
}
查看如下函数
cf215
的目标代码:
查看如下函数cf215的目标代码:
2.6.4 简单无条件转移指令
.示例
;
函数
cf215
目标代码(使速度最大化)
cmp
ecx
, 10 ;x
与
10
比较
jle
SHORT LN3cf215
;
当小于等于
10
时转
lea
eax
, DWORD PTR [
ecx+ecx
*2] ;
计算表达式
3*x+4*y+7
lea
eax
, DWORD PTR [
eax+edx
*4+7]
jmp
SHORT LN2cf215
;
无条件转(
if
-
else
语句结束)
LN3cf215:
lea
eax
, DWORD PTR [
edx
*8] ;
计算表达式
7*y+2*x
-
12
sub
eax
,
edx
lea
eax
, DWORD PTR [
eax+ecx
*2
-
12]
LN2cf215:
cmp
edx
, 20 ;y
与
20
比较
jg
SHORT LN1cf215
;
当大于
20
时转
lea
eax
, DWORD PTR [
eax
*4+3] ;
计算
4*z+3LN1cf215:
ret ;
函数
cf24
结束返回
;函数cf215目标代码(使速度最大化)
cmp ecx, 10 ;x与10比较
jle SHORT LN3cf215 ;当小于等于10时转
lea eax, DWORD PTR [ecx+ecx*2] ;计算表达式3*x+4*y+7
lea eax, DWORD PTR [eax+edx*4+7]
jmp SHORT LN2cf215 ;无条件转(if-else语句结束)
LN3cf215:
lea eax, DWORD PTR [edx*8] ;计算表达式7*y+2*x-12
sub eax, edx
lea eax, DWORD PTR [eax+ecx*2-12]
LN2cf215:
cmp edx, 20 ;y与20比较
jg SHORT LN1cf215 ;当大于20时转
lea eax, DWORD PTR [eax*4+3] ;计算4*z+3LN1cf215:
ret ;函数cf24结束返回
ECX
传递
x , EDX
传递
y
EAX
作为变量
z
ECX传递x , EDX传递y
EAX作为变量z
2.7 堆栈和堆栈操作
2.7.1 堆栈
2.7.2 堆栈操作指令 汇编语言中的
堆栈,
就是高级语言中的
栈
汇编语言中的堆栈,
就是高级语言中的
栈
2.7.1 堆栈
栈底
栈顶
.程序的运行与堆栈有密切关系:
.CPU在运行程序期间往往需要利用堆栈保存某些
关键信息
.程序自身也经常会利用堆栈临时保存一些数据
.所谓堆栈其实就是一段内存区域,只是对它的访问
操作仅限于一端进行。地址较大的一端被称为栈底,
地址较小的一端被称为栈顶。
.堆栈
2.7.1 堆栈
.
堆栈操作遵守“后进先出”的原则,所有数据的存入和
取出都在栈顶进行
.堆栈操作遵守“后进先出”的原则,所有数据的存入和
取出都在栈顶进行。
.把存入数据的操作称为进栈操作,把取出数据的操作称
为出栈操作。进栈操作也称为压栈操作,出栈操作也称
为弹出操作。
.堆栈
2.7.1 堆栈
.
堆栈指针寄存器
ESP
含有栈顶的偏移
ESP
指向栈顶
.堆栈段寄存器SS含有当前堆栈段的段号,SS指示堆栈所
在内存区域的位置
.堆栈指针寄存器ESP含有栈顶的偏移(有效地址),ESP
指向栈顶
.堆栈
ESPSSESPSSESPSS(a)堆栈初始情形(b)进栈后的情形(c)部分出栈后的情形
进栈方向出栈方向
堆栈底部堆栈底部堆栈底部
2.7.1 堆栈
.堆栈有如下所列的主要用途:
(1)保护寄存器内容或者保护现场;
(2)保存返回地址;
(3)传递参数;
(4)安排局部变量或者临时变量。
.堆栈的用途
2.7.2 堆栈操作指令
. 进栈指令PUSH
. 出栈指令POP
. 通用寄存器全进栈指令和全出栈指令
2.7.2 堆栈操作指令
.进栈指令PUSH
.
ESP
总是指向栈顶
.指令把源操作数SRC压入堆栈。
.源操作数SRC可以是32位通用寄存器、16位通用寄存器和段寄存器,
也可以是双字存储单元或者字存储单元,还可以是立即数。
.把一个双字数据压入堆栈时,先把ESP减4,然后再把双字数据送
到ESP所指示的存储单元。
.把一个字数据压入堆栈时,先把ESP减2,再把字数据送到ESP所指
示的存储单元。
.ESP总是指向栈顶。
.进栈指令的一般格式
PUSH SRC
2.7.2 堆栈操作指令
.进栈指令PUSH
PUSH EAX ;
把
EAX
的内容压入堆栈
PUSH DWORD PTR [ECX] ;
把
ECX
指示的双字存储单元的内容压入堆栈
PUSH BX ;
把
BX
的内容压入堆栈
PUSH WORD PTR [EDX] ;
把
EDX
指示的字存储单元的内容压入堆栈
PUSH EAX ;把EAX的内容压入堆栈
PUSH DWORD PTR [ECX] ;把ECX指示的双字存储单元的内容压入堆栈
PUSH BX ;把BX的内容压入堆栈
PUSH WORD PTR [EDX] ;把EDX指示的字存储单元的内容压入堆栈
符号“DWORD PTR”表示双字存储单元
符号“WORD PTR” 表示字存储单元
至少进栈一个字!
.使用举例
2.7.2 堆栈操作指令
.进栈指令PUSH
MOV EAX, 12345678H
PUSH EAX ;ESP=0013FA70H
PUSH AX ;ESP=0013FA6EH
MOV EAX, 12345678H
PUSH EAX ;ESP=0013FA70H
PUSH AX ;ESP=0013FA6EH
ESPESP(a)堆栈初始情形(b)进栈后的情形
1234567856780013FA740013FA740013FA700013FA6E00000000000000000013FA72
为节省篇幅,假设每一格为
一个字存储单元(16位)
.使用举例
2.7.2 堆栈操作指令
.出栈指令POP
.指令从栈顶弹出一个双字或者字数据到目的操作数DEST。
.目的操作数可以是32位通用寄存器、16位通用寄存器和段寄存器,
也可以是双字存储单元或者字存储单元。如果目的操作数是双字
的,那么就从栈顶弹出一个双字数据;否则,从栈顶弹出一个字
数据。
.出栈指令的一般格式
POP DEST
注意:
出栈指令操作数不可以是立即数,
也不能是代码段寄存器
CS
。
注意:
出栈指令操作数不可以是立即数,
也不能是代码段寄存器CS。
2.7.2 堆栈操作指令
.出栈指令POP
.
ESP
总是指向栈顶
.从栈顶弹出一个双字数据时,先从ESP所指示的存储单元中取出一
个双字送到目的操作数,然后把ESP加4。
.从栈顶弹出一个字数据时,先从ESP所指示的存储单元中取出一个
字送到目的操作数,然后把ESP加2。
.ESP总是指向栈顶。
.出栈指令的一般格式
POP DEST
2.7.2 堆栈操作指令
.出栈指令POP
POP ESI ;
从堆栈弹出一个双字到
ESI
POP DWORD PTR [EBX+4] ;
从堆栈弹出一个双字到
EBX+4
所指示存储单元
POP DI ;
从堆栈弹出一个字到
DI
POP WORD PTR [EDX+8] ;
从堆栈弹出一个字到
EDX+8
所指示的存储单元
POP ESI ;从堆栈弹出一个双字到ESI
POP DWORD PTR [EBX+4] ;从堆栈弹出一个双字到EBX+4所指示存储单元
POP DI ;从堆栈弹出一个字到DI
POP WORD PTR [EDX+8] ;从堆栈弹出一个字到EDX+8所指示的存储单元
符号“DWORD PTR”表示双字存储单元
符号“WORD PTR” 表示字存储单元
至少出栈一个字!
.使用举例
2.7.2 堆栈操作指令
.示例
#include <
stdio.h
>
int
main( ) {
int
varsp1, varsp2, varsp3, varsp4, varsp5; //
用于存放
ESP
值
int
varr1, varr2; //
用于存放
EBX
值
_
asm
{
//
嵌入汇编
。。。。。。
}
printf
("ESP1=%08XH
\
n",varsp1); //
显示为
ESP1=0013FA74H
printf
("ESP2=%08XH
\
n",varsp2); //
显示为
ESP2=0013FA70H
printf
("ESP3=%08XH
\
n",varsp3); //
显示为
ESP3=0013FA6EH
printf
("ESP4=%08XH
\
n",varsp4); //
显示为
ESP4=0013FA72H
printf
("ESP5=%08XH
\
n",varsp5); //
显示为
ESP5=0013FA74H
printf
("EBX1=%08XH
\
n",varr1); //
显示为
EBX1=56785678H
printf
("EBX2=%08XH
\
n",varr2); //
显示为
EBX2=56781234H
return 0;
}
#include <stdio.h>
int main( ) {
int varsp1, varsp2, varsp3, varsp4, varsp5; //用于存放ESP值
int varr1, varr2; //用于存放EBX值
_asm { //嵌入汇编
。。。。。。
}
printf("ESP1=%08XH\n",varsp1); //显示为ESP1=0013FA74H
printf("ESP2=%08XH\n",varsp2); //显示为ESP2=0013FA70H
printf("ESP3=%08XH\n",varsp3); //显示为ESP3=0013FA6EH
printf("ESP4=%08XH\n",varsp4); //显示为ESP4=0013FA72H
printf("ESP5=%08XH\n",varsp5); //显示为ESP5=0013FA74H
printf("EBX1=%08XH\n",varr1); //显示为EBX1=56785678H
printf("EBX2=%08XH\n",varr2); //显示为EBX2=56781234H
return 0;
}
演示程序
dp216
及其嵌入汇编代码片段,
演示堆栈操作和堆栈指针寄存器变化。
演示程序dp216及其嵌入汇编代码片段,
演示堆栈操作和堆栈指针寄存器变化。
2.7.2 堆栈操作指令
.示例
_
asm
{
MOV EAX, 12345678H //
初值
MOV varsp1, ESP //
保存演示之初的
ESP
(假设为
0013FA74H
)
;
PUSH EAX
//
把
EAX
压入堆栈
MOV varsp2, ESP //
保存当前
ESP
(
0013FA70H
)
;
PUSH AX
//
把
AX
压入堆栈
MOV varsp3, ESP //
保存当前
ESP
(
0013FA6EH
)
;
POP EBX
//
从堆栈弹出双字到
EBX
MOV varsp4, ESP //
保存当前
ESP
(
0013FA72H
)
MOV varr1, EBX
;
POP BX
//
从堆栈弹出字到
BX
MOV varsp5, ESP //
保存当前
ESP
(
0013FA74H
)
MOV varr2, EBX
}
_asm {
MOV EAX, 12345678H //初值
MOV varsp1, ESP //保存演示之初的ESP(假设为0013FA74H)
;
PUSH EAX //把EAX压入堆栈
MOV varsp2, ESP //保存当前ESP(0013FA70H)
;
PUSH AX //把AX压入堆栈
MOV varsp3, ESP //保存当前ESP(0013FA6EH)
;
POP EBX //从堆栈弹出双字到EBX
MOV varsp4, ESP //保存当前ESP(0013FA72H)
MOV varr1, EBX
;
POP BX //从堆栈弹出字到BX
MOV varsp5, ESP //保存当前ESP(0013FA74H)
MOV varr2, EBX
}
仅仅是示例!
仅仅是示例!
2.7.2 堆栈操作指令
.示例
PUSH EBP
;保护
EBP
PUSH ESI
;保护
ESI
PUSH EDI
;保护
EDI
.........
;其他操作
.........
;其间会破坏
EBP
、
ESI
和
EDI
的原有值
POP EDI
;恢复
EDI
POP ESI
;恢复
ESI
POP EBP
;恢复
EBP
PUSH EBP ;保护EBP
PUSH ESI ;保护ESI
PUSH EDI ;保护EDI
......... ;其他操作
......... ;其间会破坏EBP、ESI和EDI的原有值
POP EDI ;恢复EDI
POP ESI ;恢复ESI
POP EBP ;恢复EBP
必须充分注意堆栈操作“后进先出”
同时确保堆栈平衡!
演示堆栈用途之一,保护寄存器内容。
演示堆栈用途之一,保护寄存器内容。
2.7.2 堆栈操作指令
.
16
位通用寄存器全进栈
、
出栈指令
.
32
位通用寄存器全进栈
、
出栈指令
.16位通用寄存器全进栈、出栈指令
.32位通用寄存器全进栈、出栈指令
有时需要把多个通用寄存器压入堆栈,以保护这些通用寄存器中的值。
为了提高效率,从
Intel 80186
开始,提供了通用寄存器全进栈指令
和全出栈指令。
有时需要把多个通用寄存器压入堆栈,以保护这些通用寄存器中的值。
为了提高效率,从Intel 80186开始,提供了通用寄存器全进栈指令
和全出栈指令。
2.7.2 堆栈操作指令
.16位通用寄存器全进栈、出栈指令
.PUSHA指令将8个16位通用寄存器的内容压入堆栈,压入顺序:
AX、CX、DX、BX、SP、BP、SI、DI
.POPA指令从堆栈弹出内容,以PUSHA相反的顺序送通用寄存器
PUSHA
.16位通用寄存器全进栈指令
POPA
.16位通用寄存器全出栈指令
2.7.2 堆栈操作指令
.32位通用寄存器全进栈、出栈指令
.PUSHAD指令将8个16位通用寄存器的内容压入堆栈,压入顺序:
EAX、ECX、EDX、EBX、ESP、EBP、ESI、EDI
.POPAD指令从堆栈弹出内容,以PUSHA相反的顺序送通用寄存器
PUSHAD
.32位通用寄存器全进栈指令
POPAD
.32位通用寄存器全出栈指令
2.7.2 堆栈操作指令
.示例
#include <
stdio.h
>
int
buff[8]; //
全局数组,存放从堆栈中取出的各寄存器之值
int
main( )
{
_
asm
{
//
嵌入汇编
。。。。。。
}
//
依次显示数组
buff
各元素之值,从中观察
PUAHAD
指令压栈的效果
int
i
;
for (
i
=0;
i
<8;
i
++)
printf
("buff[%d]=%u
\
n",
i
, buff[
i
]);
return 0;
}
#include <stdio.h>
int buff[8]; //全局数组,存放从堆栈中取出的各寄存器之值
int main( )
{
_asm { //嵌入汇编
。。。。。。
}
//依次显示数组buff各元素之值,从中观察PUAHAD指令压栈的效果
int i;
for (i=0; i<8; i++)
printf("buff[%d]=%u\n", i, buff[i]);
return 0;
}
程序
dp217
及其嵌入汇编代码,演示
PUSHAD
指令的执行效果,
还演示另一种访问堆栈区域存储单元的方法。
程序dp217及其嵌入汇编代码,演示PUSHAD指令的执行效果,
还演示另一种访问堆栈区域存储单元的方法。
2.7.2 堆栈操作指令
.示例
_
asm
{//
嵌入汇编
PUSH EBP //
先保存
EBP
!!
;
MOV EAX, 0 //
给各通用寄存器赋一个特定的值
MOV EBX, 1
MOV ECX, 2
MOV EDX, 3
; //
决不能随意改变
ESP
!!
MOV EBP, 5
MOV ESI, 6
MOV EDI, 7
;
PUSHAD
//
把
8
个通用寄存器之值全部推到堆栈
;
_asm {//嵌入汇编
PUSH EBP //先保存EBP!!
;
MOV EAX, 0 //给各通用寄存器赋一个特定的值
MOV EBX, 1
MOV ECX, 2
MOV EDX, 3
; //决不能随意改变ESP!!
MOV EBP, 5
MOV ESI, 6
MOV EDI, 7
;
PUSHAD //把8个通用寄存器之值全部推到堆栈
;
保护重要寄存器!
2.7.2 堆栈操作指令
.示例
MOV EBP, ESP
//
使得
EBP
也指向堆栈顶
LEA EBX, buff //
把数组
buff
首元素的有效地址送到
EBX
MOV ECX, 0 //
设置计数器(下标)初值
NEXT:
MOV EAX, [
EBP
+ECX*4] //
依次从堆栈中取
MOV [EBX+ECX*4], EAX //
依次保存到数组
buff
INC ECX //
计数器加
1
CMP ECX, 8 //
是否满
8
JNZ NEXT //
没有满
8
个,继续处理下一个
;
POPAD
//
恢复
8
个通用寄存器
POP EBP
//
恢复开始保存的
EBP
}
MOV EBP, ESP //使得EBP也指向堆栈顶
LEA EBX, buff //把数组buff首元素的有效地址送到EBX
MOV ECX, 0 //设置计数器(下标)初值
NEXT:
MOV EAX, [EBP+ECX*4] //依次从堆栈中取
MOV [EBX+ECX*4], EAX //依次保存到数组buff
INC ECX //计数器加1
CMP ECX, 8 //是否满8
JNZ NEXT //没有满8个,继续处理下一个
;
POPAD //恢复8个通用寄存器
POP EBP //恢复开始保存的EBP
}
缺省SS
堆栈!
恢复被保护寄存器,
同时确保堆栈平衡!
2.7.2 堆栈操作指令
.示例
MOV EBP, ESP
LEA EBX, buff
MOV ECX, 0
NEXT:
MOV EAX, [
EBP
+ECX*4]
MOV [EBX+ECX*4], EAX
INC ECX
CMP ECX, 8
JNZ NEXT
MOV EBP, ESP
LEA EBX, buff
MOV ECX, 0
NEXT:
MOV EAX, [EBP+ECX*4]
MOV [EBX+ECX*4], EAX
INC ECX
CMP ECX, 8
JNZ NEXT
EBPbuff(a)堆栈情形
EBPEAXECXEDXEBXEDIESPEBPESIEBP+28EBP+4EBP+8EBP+24EBP+20EBP+16EBP+12ESPEBX+28
栈底
(b)数组buffEBX
高端
EBX+4EBX+8EBX+24EBX+20EBX+16EBX+12==>
从堆栈到数组图示
从堆栈到数组图示