FASM 第二章 - 2.1 x86 体系指令
Author: 徐艺波 From: xuyibo.org Updated: 2008-04-17
官方论坛 | 本站软件反馈、软件开发交流。 | ||
邮件通知 | 当更新时自动发送邮件通知。 | ||
评论本文 | 有什么建议或评论,可以贴一下。 | ||
捐助 | 你的支持,让我们做的更好。 |
- 2.1 x86 体系指令
-
2.1.1 数据传送指令
2.1.2 类型转换指令
2.1.3 二进制算术指令
2.1.4 十进制算术指令
2.1.5 逻辑指令
2.1.6 控制转移指令
2.1.7 I/O 指令
2.1.8 字符串操作指令
2.1.9 标志控制指令
2.1.10 条件操作指令
2.1.11 其他指令
2.1.12 系统指令
2.1.13 FPU 指令
2.1.14 MMX 指令
2.1.15 SSE 指令
2.1.16 SSE2 指令
2.1.17 SSE3 指令
2.1.18 AMD 3DNOW!指令
2.1.19 x86-64长模式指令
2.1 x86 体系指令
这一章讲述了汇编语言指令语法和功能。更多的技术信息可以阅读Intel软件开发者手册。
汇编指令有助记符(指令名称)和0到3个操作符组成。如果有大于两个的操作符,通常第一个为目的操作符第二个为源操作符。每个操作符都可以为寄存器,内存或立即数(操作符语法见1.2节)。每条指令描述后悔有操作符不同用法的例子。
一些指令用作前缀,可以和其他指令放在同行一起使用,一行也允许有多个前缀。段寄存器也是指令助记符前缀,但推荐在方括号中段重写来替代这些前缀。
2.1.1 数据传送指令
mov从源操作符传送字节,字或双字道目的操作符。它可以在通用寄存器之间,通用寄存器到内存,或从内存到通用寄存器间传送数据,但不能在内存间传送数据。它也可以在立即数到通用寄存器或内存,段寄存器到通用寄存器或内存,通用寄存器或内存到段寄存器,控制或调试寄存器到通用寄存器以及通用寄存器到控制或调试寄存器间传送数据。只有当源操作符和目的操作符大小相同时mov才能被汇编。下面是一些例子:
mov bx,ax ; 通用寄存器到通用寄存器
mov [char],al ; 通用寄存器到内存
mov bl,[char] ; 内存到通用寄存器
mov dl,32 ; 立即数到通用寄存器
mov [char],32 ; 立即数到内存
mov ax,ds ; 段寄存器到通用寄存器
mov [bx],ds ; 段寄存器到内存
mov ds,ax ; 通用寄存器到段寄存器
mov ds,[bx] ; 内存到段寄存器
mov eax,cr0 ; 控制寄存器到通用寄存器
mov cr3,ebx ; 通用寄存器到控制寄存器
xchg置换两个操作数内容。它可以用来置换两个字节、字或者双字操作数。操作数的顺序并不重要。操作数可以为两个通用寄存器,或者通用寄存器同内存。例如:
xchg ax,bx ; 置换两个通用寄存器
xchg al,[char] ; 寄存器和内存置换
push递减堆栈指针(esp寄存器),然后传送操作数到esp执行的栈顶。操作数可以为内存,通用寄存器,段寄存器或字、双字立即数。如果操作数为没有指定大小的立即数时,汇编器在16位模式下默认将当作16位值,在32位模式下将当作32位值。pushw和pushd助记符为push指令的变种,分别用来压入16位,32位大小值到堆栈。如果同行后指定了更多参数(空格分隔,而非逗号),将汇编为一串push指令。下面为带有单一操作符的例子:
push ax ; 压入通用寄存器到堆栈
push es ; 压入段寄存器
pushw [bx] ; 压入内存值
push 1000h ; 压入立即数
pusha压入8个通用寄存器的内容到堆栈,这条指令没有操作数。这条指令有两个版本,一个16位的和一个32位的,汇编器自动根据当前模式生成正确的版本,但也可以使用pushaw或pushad助记符重写为只为16位或32位版本。16位版本的这条指令将以以下顺序压入通用寄存器:ax,cx,dx,bx,压入ax前的sp值,bp,si和di。32为版本将以相同顺序压入等价的32位通用寄存器。
pop传送当前栈顶的字或双字到目的操作符,然后递增esp指向新的栈顶。操作符可以为内存,通用寄存器或段寄存器。popw和popd助记符为pop指令的变种,分别用来弹出字或双字。如果同行后指定了更多参数(空格分隔,而非逗号)将汇编为一串pop指令。
pop bx ; 弹出栈顶数据到通用寄存器
pop ds ; 弹出到段寄存器
popw [si] ; 弹出到内存
popa弹出堆栈中由pusha指令压入的寄存器,将忽略其中保存的sp(或esp)值。使用popaw或popad助记符来强制汇编16位或32位版本的这条指令。
2.1.2 类型转换指令
类型转换指令转换字节为字,字为双字,双字为四字。这些转换可以为符号扩展或零扩展的。符号扩展将用符号位来填充,而零扩展将使用0来填充。
cwd和cdq分别用来扩展ax和eax大小,并将额外位存储到dx和edx中。转换将使用符号扩展。这些指令没有操作数。
cbw符号扩展al的值到ax,cwde符号扩展ax到eax。这些指令也没有操作数。
movsx使用符号扩展将字节转换为字或双字,字转换为双字。movzx类似,只是它使用0扩展。源操作数可以为通用寄存器或内存,目的操作数必须为通用寄存器。例如:
movsx ax,al ; 字节寄存器转换为字寄存器
movsx edx,dl ; 字节寄存器转换为双字寄存器
movsx eax,ax ; 字寄存器转换为双字寄存器
movsx ax,byte [bx] ; 字节内存之后为字寄存器
movsx edx,byte [bx] ; 字节内存转换为双字寄存器
movsx eax,word [bx] ; 字内存转换为双字寄存器
2.1.3 二进制算术指令
add替换目的操作数的值为源操作数和目的操作数的和,并且在溢出时设置CF标志。操作数可以为字节,字或双字。目的操作数可以为通用寄存器或内存,源操作数可以为通用寄存器或立即数,如果目的操作数为寄存器也可以为内存。
add ax,bx ; add 寄存器到寄存器
add ax,[si] ; add 内存到寄存器
add [di],al ; add 寄存器到内存
add al,48 ; add 立即数到寄存器
add [char],48 ; add 立即数到内存
adc和add类似,只是如果设置CF的话结果还将递增1。add后跟着多个adc指令能用来计算大于32位值的和。
inc将操作数值递增1,它不影响CF。操作数可以为通用寄存器或内存,操作数大小可以为字节,字或双字。
inc ax ; 寄存器值递增1
inc byte [bx] ; 内存值递增1
sub用目的操作数值减去源操作数,并且用结果替换目的操作数。如果需要借位,将设置CF。操作数规则和add指令相同。
sbb和sub类似,只是如果设置CF的话结构还将递减1。操作数规则和add质量相同。sub后跟着多个sbb指令能用来计算大于32位值的差。
dec将操作数值递减1,它不影响CF。操作数规则和inc指令相同。
cmp用目的操作数减去源操作数,类似sub指令更新标志值,但它不改变源和目的操作符。操作数规则和sub指令相同。
neg用0减去带符号的整数操作数。这条指令的效果是将带符号的操作数从正数变为负数或者从负数变为正数。操作数规则和inc指令相同。
xadd交换目的和源操作数,然后载入两个值的和到目的操作数。操作数规则和add指令相同。
所有上面的二进制算术指令都将更新SF,ZF,PF和OF标志。SF被设置为结果符号位的值,ZF当结果为0时设置为1,PF当低8位存在偶数个1时设置,OF在结果对于正数太大或对于负数太小(超过符号位)以放到目的操作数中时设置。
mul计算无符号操作数和累加器的积。如果为8位操作数,将和al计算积,16位结果返回到ah和al中。如果4,为16位操作数,将和ax计算积,32位结果返回到dx和ax中。如果为32位操作数,将和eax计算积,64位结果返回到edx和eax中。当结果高半部分不为零时将设置标志CF和OF,否则将清除该标志。操作数规则和inc指令相同。
imul执行符号乘法运算。这条指令有3种用法。第一种允许一个操作数,和mul指令类似。第二种有两个操作数,此时将计算目的操作数和源操作数的积,并将结果替换目的操作数。目的操作数可以为16位或32位通用寄存器,源操作数可以为通用寄存器,内存或立即数。第三种有3个操作数,目的操作数必须为16位或32位通用寄存器,源操作数可以为通用寄存器或内存,第三种操作数必须为立即数。源操作数乘以立即数并将结果保存到目的寄存器。所有上面三种形式都将计算出双倍大小的结构,并当结果高半部分不为零时设置标志CF和OF。所以第二种和第三种形式也能用作无符号操作数,因为,无论操作数是否为有符号无符号,结果的低半部分是相同的。下面的所有三种形式的乘法指令使用例子:
imul bl ; 累加器和寄存器
imul word [si] ; 累加器和内存
imul bx,cx ; 寄存器和寄存器
imul bx,[si] ; 寄存器和内存
imul bx,10 ; 寄存器和立即数
imul ax,bx,10 ; 寄存器,立即数,值到寄存器
imul ax,[si],10 ; 内存,立即数,值到寄存器
div计算操作数和累加器无符号运算的商。被除数(累加器)为两倍大小的除数(操作数),商和余数和除数有相同尺寸。如果除数为8位,被除数为ax,商和余数分别保存到al和ah中。如果除数为16位,被除数的商的高半部分从dx获取,低半部分从ax获取,商和余数分别保存到ax和dx中。如果除数为32位,被除数的高半部分从edx获取,低半部分从eax获取,商和余数分别保存到eax和edx中。操作数规则和mul指令相同。
idiv计算操作数和累加器有符号运算的商。它使用和div指令相同的寄存器,操作数的规则也是一样的。
2.1.4 十进制算术指令
十进制算术指令用来调整上一节的二进制算术操作以生成有效的压缩或未压缩十进制结果,或调整输入为一个二进制算术操作序列以使该操作能生产一个有效的压缩或解压缩十进制结果。
daa调整al中两个有效压缩十进制操作数和的值。daa必须跟着两对压缩十进制数(每半字节一个点)的和来得到一对有效压缩十进制数字结果。如果需要进位将设置CF标志。这条指令没有操作数。
das调整al中两个有效压缩十进制操作数差的值。das必须跟着两对压缩十进制数(每半个字节一个点)的差来得到一对有效压缩十进制数字结果。如果如要进位将设置CF标志。这条指令没有操作数。
aaa修改al中的内容为有效的未压缩十进制数字,并将高四位清零。aaa必须跟着al中两个有效未压缩十进制操作数和。如果需要进位将设置CF标志并递增ah的值。这条指令没有操作数。
aas修改al的值为一个有效的未压缩十进制数据,并将高四位清零。aas必须跟着al中两个有效未压缩十进制操作数差。如果需要进位将设置CF标志并递减ah的值。这条指令没有操作数。
aam修正两个有效未压缩十进制数的积。aam必须跟着两个十进制数字的积来生成一个有效的十进制结果。数字的高位在ah中,低位在al中。这条指令用来调整ax来生成两个任何基数的未压缩数字。标志版本的这条指令没有操作数,另一种有一个操作数 - 一个指定创建数字基数的立即数。
aad修改ah保存的分子和ah和ah中两个有效未压缩十进制操作数的商,所以计算的商将为一个未压缩十进制数据。ah为高位,al为低位。这条指令调整al的值,结果也在al中,而ah内容为0.这条指令用来调整任何基数的两个未压缩数字。操作数规则和aam质量相同。
2.1.5 逻辑指令
not将指定操作数求反。它不影响标志。操作数规则和inc指令相同。
and,or和xor质量执行标准的逻辑操作。它们更新标志SF,ZF和PF。操作数规则和add指令相同。
bt,bts,btr和btc指令只能处理一个在内存或寄存器中的位。该位位置由操作数的低位指定。偏移有第二个操作数指定,它可以为字节立即数或一个通用寄存器。这些指令首先将选择的位送到标志CF。bt指令不会做更多操作,bts设置选择位为1,btr将选择为置为0,btc修改将改位值求反。第一个操作数可以为字或双字。
bt ax,15 ; 测试寄存器中的位
bts word [bx],15 ; 测试并设置内存值中的位
btr ax,cx ; 测试并重置寄存器中的位
btc word [bx],cx ; 测试并求反内存值中的位
bsf和bsr质量扫描字或双字第一个为1的位,并将改为索引保存到必须为通用寄存器的目的操作数。源操作数可以为通用寄存器或内存。当整个串为0时设置ZF标志;否则将置为0。如果没有找到为1的位,谜底寄存器的值为未定义的。bsf从低位到高位扫描(从位索引0开始)。bsr从高位到低位扫描(16位时从第15位,32位时从31位开始)。
bsf ax,bx ; 向前扫描寄存器
bsr ax,[si] ; 逆向扫描内存值
shl左移目的操作数为第二个操作数指定的位数。目的操作数可以为字节,字,或双字通用寄存器或内存。第二个操作数可以为立即数或cl寄存器。左移的最后一位将被放到标志CF中。sal和shl为相同指令。
shl al,1 ; 左移寄存器一位
shl byte [bx],1 ; 左移内存值一位
shl ax,cl ; 左移寄存器为cl中的值
shl word [bx],cl ; 左移内存值为cl中的值
shr和sar右移目的操作数为第二个参数指定的位数。操作数规则和shl指令相同。shr右移的最后一位将放到标志CF中。sar保留操作数符号位,如果操作数为正数用0左移,否则用1左移。
shld左移目的操作数(第二个操作数)为第三个操作数指定的位数,lefto do。目的操作数为字或双字通用寄存器或内存,源操作数必须为通用寄存器,第三个操作数可以为立即数或cl寄存器。
shld ax,bx,1 ; 左移寄存器1位
shld [di],bx,1 ; 左移内存值一位
shld ax,bx,cl ; 左移寄存器为cl中的位数
shld [di],bx,cl ; 左移内存值为cl中的位数
shrd右移目的操作数,lefto to do。不修改源操作数。操作数规则和shld指令相同。
rol和rcl左转字节,字或双字目的操作数为第二个操作数指定的位数。对于每次转动,左转出来的位数将成为这个值新的低位。rcl指令还将把高位放到标志CF中。操作数规则和shl指令相同。
ror和rcr右转字节,字或双字目的操作数为第二个操作数指定的位数。对于每次转东,右转出来的位数将成为这个值新的高位。rcr指令还将把低位放到标志CF中。操作数规则和shl质量相同。
test执行和and指令相同的操作,但它不会修改目的操作数的值,只更新标志。操作数规则和and指令相同。
bswap翻转32位通用寄存器:0到7位翻转为23到31位,8到15位翻转为16位到23位。这条指令用来转换little-endian值为big-endian格式,反之亦然。
bswap edx ; 翻转寄存器值
2.1.6 控制转移指令
jmp无条件转移控制到目的位置。目的地址可以直接在指令中指定或间接通过寄存器或内存,允许的地址大小取决于跳转类型为near或far(通过在操作数前指定near或far操作数来指定)以及指令是否为16位或32位。对于16位指令near跳转操作数为16位,32位指令为32位。16位far跳转操作数大小为32位,32位指令为64位。一个直接jmp指令 包括作为指令一部分的目的地址(可以包含short,near或far操作符),指定地址的操作数对于near或短跳转为数值表达式,对于far跳转为两个用冒号分隔的数值表达式。第一个指定段选择子,第二个为段中偏移。pword操作符可强制为32位far调用,dword强制为16位far调用。间接jmp指令间接从寄存器或指针变量中获取目的地址,操作数应为通用寄存器或内存。细节见1.2.5节。
jmp 100h ; 直接near跳转
jmp 0FFFFh:0 ; 直接far跳转
jmp ax ; 间接near跳转
jmp pword [ebx]; 间接far跳转
call转移控制到过程,保存call后指令地址到堆栈,稍后将被ret(返回)指令使用。操作数规则和jmp指令相同,但call没有直接种类因此它不是最优的。
ret,retn和retf指令结束过程执行将转移控制给堆栈中call指令压入的地址。ret等价于retn,retn从near调用过程返回,而retf从far调用过程返回。这些指令默认地址大小和当前代码设置适合,但也可以使用retw,retnw和retfw助记符来强制大小为16位,使用retd,retnd和retfd助记符强制大小为32位。这些指令可可选的指定一个立即数操作数,用它和堆栈指针相加,它的作用是在执行call指令之前移除调用程序压入堆栈的参数。
iret返回控制到中断过程。它不同于ret的地方是它还将弹出堆栈中的标志到标志寄存器。这个标志是由中断机制保存的。它默认返回地址为当前代码设置,但也可以使用iretw或iretd助记符来强制使用16位或32位。
条件转移指令根据指令执行时CPU标志状态来决定是否转移控制。条件跳转助记符可以通过j助记符后面跟着表格2.1列出的条件助记符来得到,例如jc指令在CF设置时转移控制。条件跳转可以为short或near,仅能直接跳转,并且能优化(见1.2.5),操作数为指定目的地址的立即数。
loop指令为使用cx(或ecx)中指定软循环次数的条件跳转。所有loop指令自动递减cx(或ecx),并且在cx(或ecx)为0时结束循环。使用cx还是ecx取决于当前代码设置为16位还是32为,但也可以使用loopw助记符强制使用cx或使用loopd助记符强制使用ecx。loope和loopz是相同指令,用作标准loop,但它也在ZF为1时结束循环。loopew和loopzw强制使用cx寄存器,looped和loopzd强制使用ecx寄存器。loopne和loopnz是相同指令,用作标准loop,但它也在ZF为0时结束循环。loopnew和loopnzw助记符强制使用cx寄存器,loopned和loopnzd强制使用ecx寄存器。每一个loop指令都需要一个立即数值来指定目的地址,它只能为短调整(跟在loop指令前128字节和指令后127字节范围)。
jcxz在cx值为0时跳到指定标号,jecxz类似,但在ecx为0时跳到指定标号。操作数规则和loop指令类似。
int激活操作数指定的中断服务过程,中断号范围在0到255之间。中断服务过程以iret指令结束,返回控制给int后的指令。int3助记符为短格式的调用中断3的指令。into指令当OF为1的话调用中断。
bound检查指定寄存器中的符号值是否在指定范围内。如果不在这个范围将产生中断5。它需要两个参数,第一个操作数为要测试的寄存器,第二个操作数为范围。操作数大小为word或dword。
bound ax,[bx] ; 检查word数据边界
boudn eax,[esi]; 检查dword数据边界
助记符 | 测试条件 | 描述 |
o | OF=1 | 溢出 |
no | OF=0 | 不溢出 |
c b nae |
CF=1 | 进位 小于 不大于 |
nc ae nb |
CF=0 | 不进位 不大于 不小于 |
e z |
ZF=1 | 相等 零 |
ne nz |
ZF=0 | 不相等 不为0 |
be na |
CF或ZF=1 | 小于或等于 不大于 |
a nbe |
CF或ZF=0 | 大于 不小于不等于 |
s | SF=1 | 有符号 |
ns | SF=0 | <无符号/td> |
p pe |
PF=1 | 偶校验 |
np po |
PF=0 | 奇校验 |
l nge |
SF异或OF=1 | 小于 不大于不等于 |
ge nl |
SF异或OF=0 | 大于或等于 不小于 |
le ng |
(SF异或OF)或ZF=1 | 小于或等于 不大于 |
g nle |
(SF异或OF)或ZF=0 | 大于 不小于不等于 |
表2.1:条件
2.1.7 I/O 指令
in从输入端口传输字节,字或双字到al,ax,或eax。I/O端口可以用与指令一起编码的字节立即数直接寻址,或间接使用dx寄存器。目的操作数为al,ax或eax寄存器。源操作数应当为0到255之间的立即数,或dx寄存器。
in al,20h ; 从端口20h输入字节
in ax,dx ; 从dx寻址的端口输入字
out传送字节,字,或者双字到al,ax,或eax指定的输出端口。程序可以使用与in指令相同的方法指定端口号。目的操作数应当为0到255之间的立即数,或dx寄存器。源操作数应为al,ax或eax寄存器。
out 20h,ax ; 输出字道端口20h
out dx,al ; 输出字节到dx寻址的端口
2.1.8 字符串操作指令
字符串操作针对字符串的一个元素。字符串元素可以为字节,字或双字。字符串元素用si和di(或esi和edi)寻址。每次字符串操作后si和或di(或esi和或edi)自动更新指向字符串中后一个元素。如果DF(方向标志位)为0,将递增索引寄存器,否则将递减。取决于字符串元素的大小在递增或递减大小为1,2或4。每一个字符串操作指令都有不使用任何操作数的简短格式,在16位下使用si或和di,在32位下使用esi或和edi。si和esi默认从ds段中定位数据,di和edi默认从es段中定位数据。当字符串操作助记符后跟着指定字符串元素大小的字母时将使用短格式,“b”为字节元素,“w”为字元素,“d”为双字元素。字符串操作完整格式需要指定尺寸操作符和内存地址的操作数,操作数可以为跟有任何段前缀的si或esi,di或edi通常和es段前缀使用。
movs传送si(或esi)指向的字符串元素到di(或edi)指向的地址。操作数尺寸可以为byte,word或dword。目的操作数应当为di或edi寻址的内存,源操作数应当为跟着任何段前缀si或esi寻址的内存。
movs byte [di],[si] ; 传送字节
movs word [es:di],[ss:si] ; 传送字
movsd ; 传送双字
cmps用目的字符串元素减去源字符串元素并更新标志AF,SF,PF,CF和OF,但它不会修改任何比较元素。如果字符串元素相当,ZF设置为1,否则为0。第一个操作数为带有任何段前缀的si或esi定位的源字符串元素,第二个操作数为di或edi定位的目的字符串。
cmpsb ; 比较字节
cmps word [ds:si],[es:di] ; 比较字
cmps dword [fs:esi],[edi] ; 比较双字
scas用al,ax,或eax(取决于字符串元素的尺寸)减去目的字符串元素并更新标志AF,SF,ZF,PF,CF和OF。如果值相等,ZF将设置为1,否则为0.操作数因为di或edi定位的目的字符串元素。
scas byte [es:di] ; scan 字节
scasw ; scan 字
scas dword [es:edi] ; scan 双字
lods载入字符串元素到al,ax,或eax。操作数因为带有任何段前缀的si或esi寻址的字符串元素。
lods byte [ds:si] ; load 字节
lods word [cs:si] ; load 字
lodsd ; load 双字
stos将al,ax,或eax的值放到目的字符串元素。字符串规则和scas指令相同。
ins从dx寻址的输入端口传送一个字节,字或者双字到目的字符串元素。目的操作数应当为di或edi寻址的内存,源操作数应当为dx寄存器。
insb ; input 字节
ins word [es:di],dx ; input 字
ins dword [edi],dx ; input 双字
outs传送源字符串元素到dx寄存器寻址的输出端口。目的操作数应当dx寄存器,源操作数应当为带有可带有任何段前缀的si或esi寻址的内存。
outs dx,byte [si] ; output 字节
outsw ; output 字
outs dx,dword [gs:esi] ; output 双字
重复前缀rep,repe/repz,和repne/repnz指定重复字符串操作。当一个字符串操作指令包含重复前缀时,操作将重复执行,每一次将使用不同的字符串元素。当前缀指定的一个条件满足时结束重复。每次操作后所有3个前缀自动递减cx或ecx寄存器(取决于是否字符串操作指令使用16位或32位寻址),并且重复指定的操作指导cx或ecx为0。repe/repz和repne/repnz仅和scas和cmps指令使用(下面讲述的)。当使用这些前缀时,取决于ZF标志重复后面的指令,此外,当ZF为0时repe和repz结束执行,当ZF为1时,repne和repnz结束执行。
rep movsd ; 传送多个双字
repe cmpsb ; 比较字节直到不相等
2.1.9 标志控制指令
标志控制指令用来直接修改标志寄存器中的状态位。这节讲述的所有指令都没有操作数。
stc设置进位标志CF为1,clc清零CF,cmc逆反CF的值。std设置方向标志DF为1,cld清零DF,sti设置中断标志IF为1以允许中的,cli清零IF以禁止中断。
lahf拷贝SF,ZF,AF,PF,和CF到ah寄存器的位7,6,4,2,和0。其余位将不受影响。 标志位保持不变。
sahf将ah的位7,6,2,和0传送到SF,ZF,AF,PF,和CF。
pushf将esp值递减2或4,压入低16位或32位的符号寄存器到堆栈,压入数据大小取决于当前代码设置。pushfw强制压入16位,pushfd强制压入32位。
popf从栈顶弹出16位或32位数据到符号寄存器,然后递减esp值为2或4,递减大小取决于当前代码设置。popfw强制弹出16位,popfd强制弹出32位。
2.1.10 条件操作指令
这些指令有set助记符,条件助记符(见表2.1)组成,如果条件为true设置一个字节为1否则为置为0。操作数必须为8位的通用寄存器或内存中字节。
setne al ; 如果ZF为0设置al
seto byte [bx] ; 如果溢出设置byte
salc指令当CF为0时设置al的所有位为1,否则都置为0.这条指令没有参数。
cmov助记符后面跟着条件助记符组成的指令,仅当条件满足时传送通用寄存器中的word或dword到通用寄存器。目的操作数必须为通用寄存器,源操作数可以为通用寄存器或内存。
cmove ax,bx ; 当ZF为1时传送
cmovnc eax,[ebx] ; 当CF为0时传送
cmpxchg比较al,ax或eax和目的操作数。如果两个值相等,源操作数载入到目的操作数,否则目的操作数载入到al,ax或eax寄存器。目的操作数可以为通用寄存器或内存,源操作数必须为通用寄存器。
cmpxchg dl,bl ; 和寄存器比较并交换
cmpxchg [bx],dx ; 和内存比较并交换
cmpxchg8b比较edx和eax组成的64位值和目的操作数比较。如果值相等,ecx和ebx中64位值将保存到目的操作数。否则目的操作数值保存到edx和eax寄存器。目的寄存器必须为内存中的qword。
cmpxchg8b [bx] ; 比较并交换8字节
2.1.11 其他指令
nop指令占用一个字节但除了指令指针外没有任何作用。这条指令没有操作数,不会执行任何操作。
ud2指令生成一个无效的指令异常。这条指令用作软件测试来显式字生成一个无效指令。这条指令没有操作数。
xlat替换al寄存器字节为bx或ebx寻址的转换表中al索引的字节。操作数必须为可带有任何段前缀的bx或ebx寻址的内存中一个字节。这条指令有一个没有任何操作数的短格式xlatb,它使用ds段寄存器中bx或ebx(取决于当前代码设置)中的地址。
lds转移源操作数中的指针变量到ds和目的寄存器。源操作数必须为内存操作数,目的寄存器必须为通用寄存器。ds寄存器接受段选择子,目的寄存器接受指针偏移部分。les,lfs,lgs和lss操作和lds类似,只是它们分别使用es,fs,gs和ss寄存器,而不是ds寄存器。
lds bx,[si] ; 载入指针到ds:bx
lea传输源操作数偏移(而不是值)到目的寄存器。源操作数必须为内存操作数,目的寄存器必须为同一寄存器。
lea dx,[bx+si+1] ; 载入有效地址到dx
cpuid返回处理器标识和特性信息到eax,ebx,ecx和edx寄存器。指令执行前eax寄存器为参数。该指令没有操作数。
pause指令延迟指定时间执行下一条指令。它可用来提高死等效率。这条指令没有操作数。
enter创建堆栈框架,可用作实现块结构高级语言的范围规则。leave指令在过程结束后和过程开头的enter一起用来简化堆栈管理,并用作嵌套过程中控制访问变量。enter指令有两个参数。第一个指定堆栈中要分配的动态存储字节大小。第二个参数为相应嵌套层数,范围为0到31。指定层数决定了多少堆栈框架指针从前面一个框架中拷贝新的堆栈框架。堆栈框架通常叫做显示。显示的第一个word(当代码为32位时为dword)为最后的堆栈框架。这个指针允许leave指令通过废弃上一个堆栈帧来逆向前面的enter指令动作。当enter为过程创建一个新的显示后,通过递减esp为指定字节来分配需要的动态存储空间。允许过程寻址显示,enter保留bp(后ebp)指向新堆栈框架。如果嵌套层数为0,enter压入bp(或ebp),拷贝sp到bp(或esp到ebp),然后esp递减第一个操作数大小。对于嵌套层数大于0的,处理器在调整堆栈指针前压入额外的框架指针。
enter 2048,0
2.1.12 系统指令
lmsw载入操作数到机器状态字(CR0的0到15位),而smsw保存机器状态字到目的操作数。这两条指令操作数可以为16位通用寄存器,对于smsw还可以为32位通用寄存器。
lmsw ax ; 从寄存器载入机器状态字
smsw [bx] ; 载入机器状态字到内存
lgdt和lidt指令分别用来载入操作数中的值到全局描述表寄存器和中断描述表寄存器。sgdt和sidt分别用来保存全局描述表或中断描述表寄存器到目的操作数。操作数必须为内存中的6个字节。
lgdt [ebx] ; 载入全局描述表
lldt载入操作数到局部描述表寄存器的选择子,sldt保存局部描述表寄存器段选择子到操作数。ltr载入操作数到任务寄存器段选择子,str保存任务寄存器选择子到操作数。操作数规则和lmsw,smsw指令相同。
lar载入源操作数指定的选择子对应的段描述符访问权限到目的操作数,并设置ZF标志。目的操作数可以为16位或32为通用寄存器。源操作数必须为16位通用寄存器或内存。
lar ax,[bx] ; 载入访问权限到word
lar eax,dx ; 载入访问权限到dword
lsl从源操作数选择子指定的段描述符的段限制到目的操作数并设置ZF标志。操作数规则和lar指令相同。
verr和verw检查操作数指定代码或数据段是否能以当前特权级上读或写。操作数必须为word,可以为通用寄存器或内存。如果可用段并且可读(对于verr)或可写(对于verw),将设置ZF为1,否者ZF置为0。操作数规则和lldt指令相同。
arpl比较两个段选择子的RPL(请求特权级)。第一个操作数包含一个段选择子,第二个包含另一个。如果目的操作数RTL小于源操作数的RPL,ZF置为1,否则置为0,这条指令不影响目的操作数。目的操作数可以为16位通用寄存器或内存,源操作数必须为通用寄存器。
arpl bx,ax ; 调整寄存器选择子RPL
arpl [bx],ax ; 调整内存选择子RPL
clts清零CR0寄存器的任务切换TS位。这条指令没有操作数。
lock前缀导致处理器在执行该指令期间断言总线锁定信号,总线锁定信号保证处理器在信号断言期间独占使用任何共享内存。lock前缀只能用在以下指令,并且目的操作数为内存:add,adc,and,btc,btr,bts,cmpxchg,cmpxchg8b,dec,inc,neg,not,or,sbb,sub,xor,xadd和xchg。如果lock前缀和上面其中一指令使用并且源操作数为内存,可能会产生未定义指令异常。一个未定义指令异常也可能在lock和不在上面列出的指令一起使用的时候产生。xchg指令常用来断言总线锁定信号无论是否使用lock前缀。
invlpg无效(写)操作数指定的转换后援缓冲项TLB。处理器定位这些地址包含的页并为这些页回写TLB项。
rdmsr载入64位MSR(model specific register)ecx中的地址到edx和eax。wrmsr写edx和eax到ecx寄存器指定的64位MSR。rdtsc从64位MSR载入当前处理器时间戳到edx和eax寄存器。处理器每一时钟周期递增MSR时间戳,每次处理器重置时重置时间戳为0。rdpmc载入edx寄存器指定的40位性能监视计数器到edx和eax。这些指令没有操作数。
wbinvd回写处理器内部缓冲中所有修改的缓冲行到主内存,并且无效内部缓冲。然后创建一个特殊函数总线周期来指导外部缓冲也回写外部修改数据以及另一个时钟周期来标识外部缓冲无效。这条指令没有操作数。
rsm从系统管理模式返回到当处理器接受SMM中断时所处的模式。这条指令没有操作数。
sysenter执行到ring 0系统过程的快速调用,sysexit执行到ring 3的快速返回。这些指令是否可用有MSR相关位标识。这些指令没有操作数。
2.1.13 FPU 指令
浮点单元FPU指令操作三种格式的浮点数据:单精度(32位),双精度(64位)和扩展双精度(80位)。FPU寄存器构成一个堆栈,并且它们都是扩展双精度的。当从堆栈中压入或弹出一些值时,FPU寄存器移动,所以st0一直为FPU堆栈栈顶的值,st1为栈顶下的第一个值。st0和st是同义词。
fld压入浮点数据到FPU寄存器堆栈。操作数可以为32位,64位或80位内存地址或FPU寄存器,其值将稍后载入到FPU寄存器堆栈栈顶(也就是st0寄存器),并且自动转换为扩展双精度格式。
fld dword [bx] ; 从内存载入单精度浮值。
fld st2 ; 压入st2的值到寄存器堆栈
fld2,fldz,fldl2t,fldl2e,fldpi,fldlg2和fldln2载入常用的常量到FPU寄存器堆栈。载入的常量分别为:+1.0,+0.0,log2|10,log2|e,pi,log10|2和ln2。这些指令没有操作数。
fild转换一个源操作数整数为扩展双精度浮点格式,并将结果压入FPI寄存器堆栈。源操作数可以为16位,32位,或64位内存地址。
fild qword [bx] ; 从内存载入64位整数
fst拷贝st0寄存器的值到目的操作数,目的操作数可以为32位或64位内存地址或另一个FPU寄存器。fstp执行和fst相同的操作,只是它还将弹出寄存器堆栈。fstp执行和fst相同的操作,只是它还将压入一个80位内存中的值。
fst st3 ; 拷贝st0值到st3寄存器
fstp tword [bx] ; 存储内存中值并弹出堆栈
fist转换st0值为一整数,并保存结果到目的操作数。操作数可以为61位或32位内存地址。fistp执行相同操作,但很将弹出寄存器堆栈,并能存储值到64位内存,操作数规则和fild指令相同。
fbld转换压缩BCD整数为扩展双精度浮点格式并压入值到FPU堆栈。fbstp转换st0中的值为18个数字压缩BCD整数,保存结果到目的操作数并弹出寄存器堆栈。操作数应为80位内存地址。
fadd计算目的和源操作数的和并保存结果到目的操作数。目的操作数一直为FPU寄存器,如果源操作数为内存地址,目的操作数为st0寄存器并且只指定源操作数。内存操作数可以为32位或64位值。
fadd qword [bx] ; 计算扩展双精度和st0的和
fadd st2,st0 ; 计算st0和st2的和
faddp计算目的和源操作数的和,并保持结果到目的位置,然后弹出堆栈。目的操作数必须为FPU寄存器,源操作数必须为st0。当没有指定操作数时,将使用st1作为目的操作数。
faddp ; 计算st0和st1的和并弹出堆栈
faddp st2,st0 ; 计算st0和st2的和并弹出堆栈
fiadd指令转换源操作数整数为扩展双精度浮点数,并和目的操作数相加。操作数必须为16位或32位内存地址。
fiadd word[bx] ; word整数和st0相加
fsub,fsubr,fmul,fdiv,fdivr指令和fadd类似,操作数规则和fadd相同。fsub计算目的操作数和源操作数的差,fsubr计算源操作数和目的操作数的差,fmul将目的和源操作数相乘。fdivr计算目的操作数和源操作数的差,fdivr计算源操作数和目的操作数的差。fsubp,fsubrp,fmulp,fdivp,fidivr在转换源操作数整数为浮点数据后执行这些操作,它们操作数的规则和fiadd指令相同。
fsqrt计算st0寄存器中值的开方。fsin计算值的sin,fabs清除符号位来得到绝对值,frndint根据当前四舍五入模式来得到最接近的整数值。f2xm1计算2的以st0为幂的指数,并减去1.0,st0的值的范围必须在-1.0和+1.0之间。所有这些指令保存结果到st0并且没有操作数。
fsincos计算st0值的sin和cos,保存sin结果到st0,压入cos值到FPU寄存器堆栈。fptan计算st0的tag值,保存结果到st0,并压入值1.0堆栈。fpatan计算st1的arctag,并和st0相除,保存结果到st1并弹出寄存器堆栈。fyl2x计算st0的二进制算术结果,乘以st1值,保存结果到st1,然后弹出FPU寄存器堆栈。fyl2xp1执行相同操作,但它在计算对数前和1.0相加,保存结果到st0。fprem计算st0和st1相除的余数到st1,结果到st0。fprem1执行和fprem相同的操作,但它计算IEEE标志754指定的余数。fscale截去st1的值并和st0值相加。fxtract分隔st0值为指数和有效数字,保存指数到st0,压入有效数字到寄存器堆栈。fnop不执行任何操作。这些指令没有操作数。
fxch交换st0和另一个FPU寄存器的内容。这个操作数必须为FPU寄存器,不用指定操作数,st0和st1内容将被交换。
fcom和fcomp比较st0和源操作数,并格局结构设置FPU状态字标志。fcomp执行操作后还将弹出寄存器堆栈。操作数可以为内存中单精度或双精度浮点或FPU寄存器。当没有指定源操作数时将使用st1.
ficom word [bx] ; 16位整数和st0比较
fcomi,fcomip,fucomi,fucomip用st0和另一个FPU寄存器比较并根据结果设置标志ZF,PF和CF。fcomip和fucomip还将在执行操作后弹出寄存器堆栈。fcmov助记符后面跟着表2.2列出的FPU条件助记符组成的指令如果给定测试条件为true时传送指定FPU寄存器到st0寄存器。这些指令有两种不同语法,一种是跟着指定源FPU寄存器的单一操作数,另一种带有两个操作数,此时目的操作数为st0,第二个操作数为源FPU寄存器。
fcomi st2 ; 比较st0和st2并设置标志
fcmovb st0,st2 ; 如果小于传送st2到st0
助记符 | 测试条件 | 描述 |
b | CF=1 | 小于 |
e | ZF=1 | 等于 |
be | CF或ZF=1 | 不大于 |
u | PF=1 | 无序的 |
nb | CF=0 | 不小于 |
ne | ZF=0 | 不相等 |
nbe | CF且ZF=0 | 大于 |
nu | PF=0 | 有序的 |
表2.2:FPU条件
ftst比较st0和0.0并根据结果设置FPU状态字标志。fxam检查st0内容并设置FPU状态字来标识该寄存器值类型。这些指令没有操作数。
fstsw和fnstsw保存当前FPU状态字到目的位置。目的操作数可以为16位内容或ax寄存器。fstsw在保持状态字前检查未知的没有屏蔽的FPU异常,而fnstsw不这么做。
fstcw和fnstcw保存当前FPU状态字到指定的内存中目的地址。fstcw在保持状态字前检查未知没有屏蔽的FPU异常,而fnstcw不这样。fldcw载入操作数到FPU控制字。操作数必须为16位内存地址。
fstenv和fnstenv保存当前FPU操作环境到目的操作数指定的内存地址,然后屏蔽所有FPU异常。fstenv在处理前检查待处理的未屏蔽的FPU异常,fnstenv将不检查。flden从内存中载入完整的操作环境到FPU。fsave和fnsave保存当前FPU状态(操作环境和寄存器堆栈)到内存中定制的目的地址并重新初始化FPU。fsave在处理前检查待处理的非屏蔽FPU异常,fnsave不检查。frstor从指定内存位置载入FPU状态。所有这些指令都需要一个内存位置操作数。
finit和fninit设置FPU操作环境到默认状态。finit在处理前检查待处理非屏蔽FPU异常,而fninit不检查。fclex和fnclex清除FPU状态字中FPU异常标志。fclex在处理前检查待处理非屏蔽FPU异常,fnclex不检查。wait和fwait为相同指令,将导致处理器检查待处理的非屏蔽FPU异常并在处理前处理它们。这些指令没有操作数。
ffree设置和指定FPU寄存器相关的tag为0。操作数必须为一个FPU寄存器。
fincstp和fdecstp翻转FPU堆栈为1或栈顶指针减1.这些指令没有操作数。
2.1.14 MMX 指令
MMX指令 操作压缩整数或MMX寄存器,MMX寄存器为80位FPU寄存器的低64位。因为此MMX指令不能和FPU指令一起使用。他们可以操作压缩字节(八个8位整数),压缩字(四个16位整数)或压缩双字(两个32位整数),使用压缩格式允许一次对多个数据执行操作。
movq从源操作数拷贝8字节到目的操作数。至少一个操作数必须为MMX寄存器,第二个可以为MMX寄存器或64位内存地址。
movq mm0,mm1 ; 寄存器到寄存器移动8字节
movq mm2,[ebx] ; 内存到寄存器移动8字节
movd从源操作数移动双字到目的操作数。其中一个操作数必须为MMX寄存器,第二个可以为通用寄存器或32位内存地址。只使用MMX寄存器的低双字。
所有通用MMX操作有两个操作数,目的操作数应当为MMX寄存器,源操作数可以为MMX寄存器或64位内存地址。对源和目的操作数执行相应操作并保存数据单元到目的操作数。paddb,paddw和paddd计算压缩字节,压缩字,压缩双字的和。paddsb,paddsw,psubsb和psubsw执行压缩字节或压缩字的带符号saturation的和。paddusb,paddusw,psubusb,psubusw类似,但将计算无符号saturation。pmulhw和pmullw符号乘压缩字,保存结果的高位或低位到目的操作数。pmaddwd乘压缩字,加上四个立即双字对来生成压缩双字结果。pand,por和pxor执行qword逻辑操作。pcmpeqb,pcmpeqw和pcmpeqd比较压缩字节,压缩字或压缩双字是否相等。如果某对数据元素相等,目的操作数中相应数据元素将填充为1,否则填充0.pcmpgtb,pcmpgtw和pcmpgtd执行相同操作,但它们用来检查是否目的操作数中数据元素大于源操作数中数据元素。packsswb转换带压缩符号字为压缩带符号字节,使用saturation来处理溢出。packuswb转换压缩符号字道压缩无符号字节。源操作数中转换后的数据单元存储到目的操作数的低部分,目的操作数中转换后的数据单元存储到高半部分。punpckhbw,punpckhwd和punpckhdq从源操作数和目的操作数高半部分插入数据单元并保持结果到目的操作数。punpcklbw,punpcklwd和punpckldq执行相同操作,但它们使用源和目的操作数的低半部分。
paddsb mm0,[esi] ; 计算压缩字节符号saturation和
pcmpeqw mm3,mm7 ; 比较压缩字是否相当
psllw,pslld和psllq对压缩字,压缩双字或目的操作数中的一个qword执行逻辑左移,左移位数由源操作数指定。pswlw,psrld和psrlq对压缩字,压缩双字或目的操作数中的一个qword执行逻辑右移。psraw和psrad对压缩字或双字执行算术右移。目的操作数因为MMX寄存器,而源操作数可以为MMX寄存器,64位内存为孩子,或8位立即数。
psslw mm2,mm4 ; 逻辑左移word
psrad mm4,[ebx] ; 算术右移dword
emms是得FPU寄存器可用。如果使用了MMX指令,它必须在使用FPU指令前使用。
2.1.15 SSE 指令
SSE扩展增加了更多MMX指令,并且能操作压缩单精度浮点数。128位压缩单浮点格式由4个单精度浮点数组成。128位SSE寄存器设计用来操作这种数据类型。
movapshemovups传送源操作数中一个包含单进度值的双qword操作数到目的操作数。至少一个操作数必须为SSE寄存器,第二个操作数可以为SSE寄存器或128位内存地址。movaps指令的内存操作数必须对齐在16位字节边界,movups指令操作数不需要对齐。
movups xmm0,[ebx] ; 传送未对其双qword
movlps在内存和SSE寄存器低qword之间移动两个压缩单精度数据。movhps在内存和SSE寄存器高qword之间移动两个压缩单精度数据。其中一个操作数必须为SSE寄存器,另一个必须为64位内存地址。
movlps xmm0,[ebx] ; 移动内存到xmm0低qword
movhps [esi],xmm7 ; 移动xmm7高qword到内存
movlhps从源寄存器的低qword移动压缩的两个浮点数据到目的寄存器。movhlps从源寄存器高qword移动两个压缩单浮点数到目的寄存器的低qword。这两个操作数都必须为SSE寄存器。
movmskps传送SSE寄存器中4个单浮点数据的最高位到一个通用寄存器的低4位。源操作数必须为SSE寄存器,目的操作数必须为通用寄存器。
movss在源和目的操作数(只传送低dword)传送单浮点数据。至少一个操作数必须为SSE寄存器,第二个操作数可以为SSE寄存器或32位内存地址。
movss [edi],xmm3 ; 移动xmm3低dword到内存
每一个SSE算术操作都有两种。当助记符以ps结尾时,源操作数可以为128位内存地址或SSE寄存器,目的操作数必须为SSE寄存器,操作压缩的四个浮点数据,对于对应数据元素对,结果保存在目的寄存器。当助记符以ss结尾时,源操作数可以为32位内存地址或SSE寄存器,目的操作数必须为SSE寄存器,操作于单浮点数据,此时只适用SSE寄存器的低dword。addps和addss计算和,mulps和mulss计算积,divps和divss计算目的值和源值的商,rcpps和rcpss计算源操作数的近似倒数,sqrtps和sqrtss计算源操作数的开放,rsqrtps和rsqrtss计算源值的开方的近似倒数,maxps和maxss比较源和目的值并返回大的值,minps和minss计算源和目的值并返回小的值。
mulss xmm0,[ebx] ; 乘以单浮点数据
addps xmm3,xmm7 ; 加上压缩单浮点数据
andps,andnps,orps和xorps对压缩单精度数据执行逻辑操作。源操作数可以为128位内存地址或SSE寄存器,目的操作数必须为SSE寄存器。
cmpps比较压缩单精度数并返回结果掩码到目的操作数,目的操作数只能为SSE寄存器。源操作数可以为128位诶从地址或SSE寄存器,第三个参数必须为表2.3中列出的8个比较条件操作数立即数。cmpss对单浮点数据执行相同的操作,但它只影响目的寄存器的低dowrd,此时源操作数可以为32位内存地址或SSE寄存器。这两个指令也包含只有两个操作数和条件编码的助记符。这些助记符有cmp助记符后跟着表2.3列出的助记符,以及ps或ss构成。
cmpps xmm2,xmm4,0 ; 比较压缩单精度值
cmpltss xmm0,[ebx] ; 比较单精度数据
comiss和ucomiss比较单精度并设置标志ZF,PF和CF来表示结果。目的操作数必须为SSE寄存器,源操作数可以为32位内存地址或SSE寄存器。
代码 | 助记符 | 描述 |
0 | eq | 相等 |
1 | lt | 小于 |
2 | le | 小于后等于 |
3 | unord | 无序 |
4 | neq | 不等 |
5 | nlt | 不小于 |
6 | nle | 不小于不等于 |
7 | ord | 有序 |
shufps从目的操作数移动任何两个四单精度数据到目的操作数的低qword,源操作数中4个值的任何两个到目的操作数的高qword。目的操作数必须为SSE寄存器,源操作数可以为128位内存地址或SSE寄存器,第三个操作数必须8位立即数来指定选择移动那些数据到目的操作数。位0和1选择移动目的操作数到结果的低dword,位2和3移动目的操作数到第二个dword,位4和5移动源操作数的到结果的第三个dword,位6和7移动源操作数到结果的高dword。
shufps xmm0,xmm0,10010011b ; 搅乱dword
unpckhps执行从源和目的操作数高部分插入的未压缩数据,并保存结果到目的操作数。源操作数可以为128位内存地址或SSE寄存器。unpcklps执行从源和目的操作数低部分插入的未压缩数据,并保持结果到目的操作数,操作数规则相同。
cvtpi2ps转换压缩的2dword整数到压缩的2单浮点数据,并保存结果到目的操作数的低qword,目的操作数应为SSE寄存器。源操作数可以为64位内存地址或MMX寄存器。
cvtpi2ps xmm0,mm0 ; 整合为单精度数
cvtsi2ss转换dword整数位单精度浮点数并保存结果到目的操作数的低dword,目的操作数必须为SSE寄存器。源操作数可以为32位内存地址或32位通用寄存器。
ctsi2ss xmm0,eax ; 整合为但精度数
cvtps2pi转换2单精度浮点数为压缩2dword整数并保存结果到目的操作数,目的操作数必须为通用寄存器。源操作数可以为64位内存地址或SSE寄存器,只适用SSE寄存器的低qword。cvttps2pi操作结果类似,除了截去近似为整数,操作数规则相同。
cvtps2pi mm0,xmm0 ; 单精度数到整数
cvtss2si转换2单精度浮点数为压缩2dword整数并保存结果到目的操作数,目的操作数必须为32位通用寄存器。源操作数可以为32位内存地址或SSE寄存器,只适用SSE寄存器的低qword。cvttss2pi操作结果类似,除了截去近似为整数,操作数规则相同。
cvtss2pi eax,xmm0 ; 单精度数到整数
pextrw拷贝第三个操作数指定的源操作数word到目的操作数。源操作数必须为MMX寄存器,目的操作数必须为32位通用寄存器(仅影响低word),第三个操作数必须为8位立即数。
pextrw eax,mm0,1 ; 取word到eax
pinsrw插入第三个操作数指定的word到目的操作数中第三个操作数指定的位置,第三个操作数必须为8位立即数。目的操作数必须为MMX寄存器,源操作数可以为16位内存地址或32位通用寄存器(只适用寄存器的低word)。
pinsrw mm1,ebx,2 ; 从ebx插入word
pavgb和pavgw计算压缩字节或字平均值。pmaxub返回压缩无符号字节的最大值,pminub返回压缩无符号字节的最小值,pmaxsw返回压缩无符号字的最大值,pminsw返回压缩无符号字的最小值。pmulhuw执行无符号压缩字乘法并保存结果到目的操作数的高word。psadbw计算压缩无符号字节绝对差别,汇总不同点,并保存汇总到目的操作数的低word。所有这些指令操作数规则和上一节讲述的MMX操作相同。
pmovmskb创建源操作数每一个字节的自高位掩码,并保存结果到目的操作数的低byte。源操作数必须为MMX寄存器,目的操作数必须为32位通用寄存器。
pshufw插入word源操作数到目的操作数中第三个操作数指定的位置。目的操作数必须为MMX寄存器,源操作数可以为64位内存地址或MMX寄存器,第三个操作数必须8位立即数用来选择那些值将移动到目的操作数,和shufps指令第三个操作数相同方式。
ovntq使用非临时缓冲提示以最小缓冲损失方式从源操作数移动qword到内存。源操作数必须为MX寄存器,目的寄存器应为64位内存地址。movntps使用非临时提示从SSE寄存器保存压缩单精度数据到内存。源操作数必须为SSE寄存器,目的操作数必须为128位内存地址。maskmovq使用非临时提示方式保存第一个操作数指定的字节到64位内存地址。两个操作数都必须为MMX寄存器,第二个操作数决定源操作数中那些字节将写到内存中。内存地址由DS段中DI(或EDI)寄存器指向。
prefetcht0,prefetcht1,prefetcht2和prefetchnta获取操作数指定的字节所处内存数据行到指定位置。操作数应为8位内存地址。
sfence同步所有在它之前所有创建指令操作。这条指令没有操作数。
ldmxcsr载入32位内存操作数到MXCSR寄存器。stmxcsr保存MXCSR内容到32位寄存器。
fxsave保存FPU,MXCSR寄存器当前状态,和所有FPU和SSE寄存器内容到512字节目的操作数指定的内存地址。fxrstor重新载入前面用fxsave指令保存的512字节内存地址。这两条指令内存操作数必须对齐在16字节边界上,它不能声明任何指定大小操作数。
2.1.16 SSE2指令
SSE2扩展用来操作压缩双精度浮点数据,扩展MMX指令语法,并且增加了新的指令。
movapd和movupd从源操作数传送包含压缩双精度数据的双qword操作数到目的操作数。这些指令类似movaps和movups,操作数规则也相同。
movmskpd传送SSE寄存器两个双精度数最高位到通用寄存器低两位。这条指令和movmskps类似并有相同操作数规则。
movsd在源和目的操作数之间传送双精度数(值传送低qword)。其中至少一个操作数为SSE寄存器,第二个可以为SSE寄存器或64位内存地址。
双精度值算术操作有:addpd,addsd,subpd,subsd,mulpd,mulsd,divpd,divsd,sqrtpd,sqrtsd,maxpd,maxsd,minpd,minsd,它们和上一节讲述的单浮点算术操作类似。当助记符以pd而不是ps结尾时,操作针对于压缩的2双精度数,但操作数规则相同。当助记符以sd而不是ss结尾时,源操作数可以为64位内存地址或SSE寄存器,目的寄存器必须为SSE寄存器并且操作于双精度数,此时只适用SSE寄存器的低qword。
andpd,andnpd,orpd和xorpd对压缩双精度值执行逻辑操作。它们和针对单精度的逻辑操作类似并且有相同的操作数规则。
cmppd比较压缩双精度数并返回掩码结果到目的操作数。这条指令和cmpps类似,并且有相同的操作数规则。cmpsd对双精度数据执行相同操作,但它只影响目的寄存器的低qword。接受两个操作数的指令由cmp助记符,表2.3列出的条件助记符和pd或sd组成。
comisd和ucomisd比较双精度操作数并设置标志ZF,PF和CF来表示结果。目的操作数必须为SSE寄存器,源操作数可以为128位内存地址或SSE寄存器。
shufpd从目的操作数移动任何两个双精度数到目的操作数的低qword,源操作数任何两个值值到目的寄存器的高qword。这条指令和shufps类似并且有相同的操作数规则。第三个操作数位0指定将移动到目的操作数的值,位1选择将从源操作数移动的值,其他位为保留的必须为0。
unpckhpd在源和目的操作数之间执行压缩高qword,unpcklpd在源和目的操作数之间执行未压缩低qword。它们是unpckhps和unpcklps类似,并且有相同的操作数规则。
cvtps2pd转换压缩2单精度浮点数据为两个压缩的双精度浮点数据,目的操作数必须为SSE寄存器,源操作数可以为64位内存地址或SSE寄存器。 cvtpd2ps转换压缩2扩展双精度浮点数据为压缩2单精度浮点数据,目的操作数必须为SSE寄存器,源操作数可以为128位内存地址或SSE寄存器。cvtss2sd转换单精度浮点数据为双精度浮点数据,目的操作数必须为SSE寄存器,源操作数可以为32位内存地址或SSE寄存器。cvtsd2ss转换扩展双精度数据为单精度浮点数据,目的操作数必须为SSE寄存器,源操作数可以为64位内存地址或SSE寄存器。
cvtpi2pd转换压缩2 dword整数位压缩双精度浮点数据,目的操作数必须为SSE寄存器,源操作数可以为64位内存地址或MMX寄存器。cvtsi2sd转换一个dword整数为双精度浮点数据,目的操作数必须为SSE寄存器,源操作数可以为32位内存地址或32位通用寄存器。cvtpd2pi转换压缩双精度浮点数据为压缩2 dword整数,目的操作数应为MMX寄存器,源操作数可以为128位内存地址或SSE寄存器。cvttpd2pi执行类似操作,除了它将源值截断到整数,操作数规则也一样。cvtsd2si转换双精度浮点数据为dword整数,目的操作数应为32位通用寄存器,源操作数可以为64为内存地址或SSE寄存器。cvttsd2si执行相同吃哦啊在,除了将源值截为整数,操作数规则也一样。
cvtps2dq和cvttps2dq转换压缩单精度浮点数据为压缩4 dword整数,保存它们的值到目的操作数。cvtpd2dq和cvttpd2dq转换压缩双精度浮点数据为压缩2 dword整数,保存结果到目的操作数的低qword。cvtdq2ps转换压缩4dword帧数为压缩单精度浮点数据。cvtdq2pd从源操作数低qword转换压缩2 dword整数为压缩双精度浮点数据。所有这些指令目的操作数必须为SSE寄存器,源操作数可以为128位内存地址或SSE寄存器。
movdqa和movdqu传送源操作数中双qword大小的压缩整数为目的操作数。至少其中一个操作数必须为SSE寄存器,第二个可以为SSE寄存器或128位内存地址。movdqa指令内存操作数必须16字节对齐,movdqu指令操作数不需要对齐。
movq2dq移动MMX源寄存器内容到目的SSE寄存器低qword。movdq2q传送源SSE寄存器低qword到目的MMX寄存器。
movq2dq xmm0,mm1 ; MMX寄存器传送到SSE寄存器
movdq2q mm0,xmm1 ; SSE寄存器传送到MMX寄存器
所有MMX指令操作的64位压缩整数(用p开头的助记符)扩展能操作SSE寄存器中128位压缩整数。left to do。pshufw指令另外,它不需要扩展语法,但有两种新的变种:pshufhw和pshuflw,他们只允许扩展语法,并且分别针对操作数高或低qword执行和pshufw相同操作。此外pshufd为新增指令,用来执行和pshufw相同的操作,但它操作dword而不是word,它只允许扩展语法。
psubb xmm0,[esi] ; 减16压缩字节
pextrw eax,xmm0,7 ; 提取最高word到eax
paddq执行两个压缩qword的和,psubq执行两个压缩qword的差,puludq执行无符号乘法每一个对应qword的低dword,并返回结果到压缩qword。这些指令和2.1.14讲述的通用MMX操作有相同规则。
pslldq和psrldq执行逻辑左或右移双dqword目的操作数,移动位数由源操作数指定。目的操作数必须为SSE寄存器,源操作数应为8位立即数。
punpckhqdq源操作数高qword和目的操作数高qword,并将结果写到目的SSE寄存器中。punpcklqdq插入源操作数低qword和目的操作数低qword,并将结果写到目的SSE寄存器中。源操作数可以为128位内存地址或SSE寄存器。
movntdq使用非临时提示从SSE寄存器保存压缩整数数据到内存。源操作数应为SSE寄存器,目的操作数应为128位内存地址。movntpd使用非临时提示从SSE寄存器保存压缩双精度数据到内存。源操作数应为32位通用寄存器,目的操作数应为32位内存地址。maskmovdqu使用非临时提示从第一个参数保存选择位到128位内存地址。这两条指令操作数都应为SSE寄存器,第二个操作数选择了那些字节将从源操作数写到目的操作数。内存地址有DS段中DI(或EDI)寄存器指定,不需要对齐。
clflush写并且无效指定操作数地址字节的缓冲行,指定操作数必须为8位内存位置。
lfence执行载入同步。mfence执行访问同步。所以它组合了sfence(上一节讲述的)和lfence指令功能。这些指令没有任何操作数。
2.1.17 SSE3指令
Prescott技术发明了新的指令来提高SSE和SSE2性能 - 称为SSE3。
fisttp行为和fistp指令相似,并且允许相同操作数,唯一区别是它总是会截操作,无论当前的舍入模式。
movshdup载入目的操作数为原值同样尺寸用两个重复的高dword填充每一个qword的128位值。movsldup执行相同动作,除了它拷贝低dword。目的操作数应为SSE寄存器,源操作数可以为SSE寄存器或128位内存地址。
movddup载入64为源值,赋值它到目的操作数的高和低qword。目的操作数应当为SSE寄存器,源操作数可以为SSE寄存器或64位内存地址。
lddqu是和movdqu执行等价功能的指令,但能在源操作数跨缓冲行边界时提高性能。目的操作数必须为SSE寄存器,源操作数必须为128位内存地址。
addsubps执行第二和第四组单精度和,第一和第三组单精度差。addsupd执行第二组双精度和,第一组双精度差。haddps执行源和目的操作数每个qword的两个单精度和,保存结果到目的操作数低qword,源操作数结果到目的操作数高qword。haddpd对每个操作数执行两个双精度值和,并保存目的操作数中结果到目的操作数的低qword,源操作数结果到目的操作数高qword。所有这些指令都需要SSE寄存器为目的操作数,源操作数可以为SSE寄存器或128位内存地址。
monitor创建回写地址行监视。它需要三个有顺序的操作数EAX,ECX和EDX。mwait等待回写到monitor创建的地址区域。它使用带有额外参数的两个操作数,第一个为EAX,第二个为EDX。
2.1.18 AMD 3DNOW!指令
3DNow!扩展增加新的2.1.14列出MMX指令,并且能操作64位压缩浮点数据,每一个有两个单精度浮点数据组成。
这些指令规则和通用MMX操作相同,目的操作数必须为MMX寄存器,源操作数可以为MMX寄存器或64位内存地址。pawgusb计算压缩无符号字节平均值。pmulhrw执行带符号压缩字的乘积,舍入每一个dword结果高半部分到目的操作数。pi2fd转换压缩dword整数为压缩浮点数。pf2id使用舍入转换压缩浮点数据为压缩dword整数。pi2fw转换压缩字整数到压缩浮点数,只是哟个源操作数中每个dword的低word。pf2iw转换压缩浮点数据为压缩word整数,结果使用符号扩展扩展为压缩浮点数。pfadd计算压缩浮点数的和。pfsub和pfsubr计算压缩浮点数的差,第一个用目的值减去源值,第二个用源值减去目的值。pfmul计算压缩浮点数的积。pfacc计算目的操作数地和高浮点数的和,保存结果到目的操作数的低dword,并且计算源操作数的低和高dword的和,保存结果到目的寄存器的高dword。pfnacc用目的操作数的高浮点数减去低浮点数,保存结果到目的操作数的低dword,并结算源操作数的高和低dword的差,保存结果到目的操作数的高dword。pfpnacc用目的操作数的高浮点数据减去低浮点数据,保存结果到目的操作数的低dword,并计算源操作数低和高浮点数的和,保存结果到目的操作数的高dword。pfmax和pfmin计算浮点数的最大和最小值。pswapd翻转源操作数的高低dword。pfrcp返回原操作数的近似浮点值倒数。pfrsqrt返回原操作数的开方的近似倒数。pfrcpit1执行第一步Newton-Raphson迭代开方。pfrcpit2计算第二步Newton-Raphson迭代开发。pfcmpeq,pfcmpge和pfcmpgt比较压缩浮点数并根据比较结果设置目的操作数中相应数据元素的位(全部置为1或置为0),第一个检查值是否相等,第二个检查目的值是否大于或等于源之,第三个检查是否目的值大于源值。
prefetch和prefetchw从内存载入 包含操作数指定字节的数据行,prefetchw指令必须当缓冲行中数据被修改时使用,否则应使用prefetch指令。操作数必须为8位内存地址。
femms执行快速清除MMX状态。它没有操作数。
2.1.19 x86-64长模式指令
AMD64和EM64T体系(我们将使用x86-64作为通用名称)扩展x86指令集以用作64位处理。而原始和兼容模式使用相同的寄存器和指令集。新的长模式扩展x86操作64位,并且发明了一个新的寄存器。你可以使用use64伪指令来生成这个模式的代码。
每一个通用寄存器都被扩展为64位,增加了8个新的通用寄存器和8个新的SSE寄存器。表2.4列出了新增的这些寄存器。通用寄存器的小的尺寸为大的值的低部分。你仍然可以在长模式下访问ah,bh,ch和dh寄存器,但你不能在新的指令中使用任何新的寄存器。
通常x86体系中任何指令,允许16位或32位操作数尺寸,在长模式下还允许64位操作数。64位操作数必须在长模式下寻址,也允许32位寻址,但不能使用基于16位寄存器的地址。下面为长模式中的mov指令例子:
mov rax,r8 ; 传送64位通用寄存器
mov al,[rbx] ; 传送通过64位寄存器寻址的内存
类型 | 通用寄存器 | SSE | |||
位 | 8 | 16 | 32 | 64 | 128 |
rax | |||||
rcx | |||||
rdx | |||||
rbx | |||||
spl | rsp | ||||
bpl | rbp | ||||
sil | rsi | ||||
dil | rdi | ||||
r8b | r8w | r8d | r8 | xmm8 | |
r9b | r9w | r9d | r9 | xmm9 | |
r10b | r10w | r10d | r10 | xmm10 | |
r11b | r11w | r11d | r11 | xmm11 | |
r12b | r12w | r12d | r12 | xmm12 | |
r13b | r13w | r13d | r13 | xmm13 | |
r14b | r14w | r14d | r14 | xmm14 | |
r15b | r15w | r15d | r15 | xmm15 |
表2.4:长模式新寄存器
长模式也使用基于地址的指令指针,你可以手动用RIP指定,但这些地址也能自动由FASM生成,因此在长模式下没有64位绝对地址。你可以通过中括号中dword尺寸重写地址来强制汇编器使用32位绝对地址。也有一个使用64位绝对寻址的例外,它为mov后跟着其中一个为累加器,第二个为内存操作数的情况。使用qword来强制汇编器使用64位绝对寻址。当没有指定尺寸操作符时,汇编器自动生成最佳格式。
mov [qword 0],rax ; 绝对64位寻址
mov [dword 0],r15d ; 绝对32位寻址
mov [0],rsi ; 自动RIP相对寻址
mov [rip+3],sil ; 手动RIP相对寻址
作为64位操作立即数只可能为32位数,唯一例外是带有64位通用寄存器目的操作数的mov指令。试图其他指令使用64位立即数将导致错误。
如果在长模式下操作32位通用寄存器的指令,64位寄存器的高32位填充为0.这不同于16位或32位那些指令操作,它们保留高位。
新增三条类型转换指令。cdqe符号扩展EAX中dword到qword并保持结果到RAX。cqo符号扩展RAX qword为双qword并保存额外位到RDX寄存器。这些指令没有操作数。movsxd符号扩展dword源操作数到64位目的操作数,源操作数可以为32为寄存器或内存,目的操作数必须为寄存器。没有零扩展类似指令,因为它自动由32位寄存器完成,上一段中说明的那样。movzx和movsx指令遵守通常规则,可以使用64位目的操作数,允许扩展字节或字到qword。
所有二进制算术和逻辑指令提升以允许在长模式下操作64位操作数。在长模式下禁止使用十进制算术指令。
堆栈操作,比如push和pop在长模式下默认为64位操作数,不能使用32位操作数。pusha和popa在长模式下不可用。
间接near调整和调用在长模式下默认为64位操作数,它不能使用32位操作数。另外,间接far调整和调用允许任何x86体系允许的操作数,也允许使用80位内存操作数(仅EM64T实现了),80位内存操作数有第一个定义偏移的8字节和指定选择子的最后两个字节组成。长模式下不允许直接far调整和调用。
I/O指令,in,out,ins和outs为例外指令,它们不允许在长模式下操作qword操作数。但其他串操作可以。他们有新的段格式movsq,cmpsq,scasq,lodsq和stosq。RSI和RDI寄存器默认用来寻址这些串元素。
lfs,lgs和lss用来扩展以接受80位元内存操作数和64位目的寄存器(仅EM64T实现了)。lds和les不能在长模式下使用。
系统指令,比如需要48位内存操作数的lgdt,在长模式下需要80为内存操作数。
cmpxchg16b为64位的cmpxchg8b等价指令,它使用双qword内存操作数和64为寄存器来执行类似操作。
swapgs为新增指令,它置换GS寄存器和KernelGSbase MSR寄存器的内容(MSR地址为0C0000102h)。
syscall和sysret为新增指令,用来在长模式下提供和sysenter和sysexit相似功能的指令,而sysenter和sysexit在长模式下不允许使用。
详情 jpg 转 rar