逆向笔记(1)———寄存器与汇编指令
寄存器
定义:CPU内部的储存元件,包括通用寄存器,专用寄存器和控制寄存器(每个CPU内核都有对应的一套寄存器)
CPU缓存:寄存器,一级缓存,二级缓存(寄存器用来暂时储存数据而缓存用来储存CPU常用指令)
速度:寄存器>一>二 大小:寄存器<一<二
PS:这里来科普一下,一个字节(Byte)由8个二进制位(bit)组成,每个八进制数字可以表示3个二进制位,一个十进制数字(0-9)需要用 4个二进制位 来表示,每个十六进制数表示4个二进制位,即一个字节可以表示两个十六进制数(4个二进制位可以表示的范围是 2**4(16) 个值,但2的3次方不够表示十进制数字,所以十进制也是4个)
寄存器类别
在CPU中至少要有六类寄存器:指令寄存器(IR)、程序计数器(PC)、地址寄存器(AR)、数据寄存器(DR)、累加寄存器(AC)、程序状态字寄存器(PSW)。这些寄存器用来暂存一个计算机字,其数目可以根据需要进行扩充。
x86寄存器一般以E开头
x64寄存器一般以R开头
通用寄存器
EAX:累加器,用于加乘
ECX:循环计数
EDX:存放整数除法的余数
EBX:在内存寻址时存放基地址
ESP:保存当前栈顶的下一个地址(但通常被说指向栈顶)
EBP:保存当前栈底的地址,还可以加上一个偏移量从而访问栈内数据(如数组)
ESI:在字符串操作时指向源字符串
EDI:在字符串操作时指向目标字符串
(在储存偏移量时优先这两个寄存器)
EIP:存放下一条要执行的指令地址
主要用途
1. EAX
- 主要用途:
- 函数返回值:通常用于存储函数返回的值(如
int或指针)。
- 算术运算:参与乘法(
MUL)、除法(DIV)等操作,保存结果。
- 系统调用号:在Linux系统调用中,
EAX存储调用号(如sys_write)。
2. EBX
- 主要用途:
- 基址寄存器:在内存寻址中作为基地址(如
[ebx + offset])。
- Linux系统调用:存储系统调用的第一个参数(如文件描述符)。
- 全局数据指针:某些编译约定用它保存全局变量地址。
3. ECX
- 主要用途:
- 循环计数器:在
LOOP指令或显式循环中存储计数(如for (int i=0; ...))。
this指针:在C++成员函数中,存储指向当前对象的指针(this)。
- 字符串/内存操作:与
REP前缀指令配合(如REP MOVSB)。
4. EDX
- 主要用途:
- 辅助算术寄存器:在乘除法中配合
EAX(如MUL结果的高32位存EDX)。
- 系统调用参数:Linux系统调用中存储第二个参数(如
write的缓冲区地址)。
- I/O端口操作:与
IN/OUT指令配合使用。
5. ESI
- 主要用途:
- 源数据指针:在字符串/内存操作中(如
MOVSB)指向源地址。
- 数组遍历:作为源数组的索引。
6. EDI
- 主要用途:
- 目标数据指针:在字符串/内存操作中(如
MOVSB)指向目标地址。
- 数组/结构体操作:存储写入的目标地址。
7. ESP
- 主要用途:
- 栈顶指针:始终指向当前栈的顶部,用于
PUSH/POP操作。
- 函数调用:保存返回地址、局部变量和参数。
8. EBP
- 主要用途:
- 栈帧基址:在函数中标记栈帧的起始位置(如
[ebp + 8]访问参数)。
- 调试支持:方便回溯调用链(编译器优化时可能被省略)。
特殊指令依赖:如CPUID依赖EAX,RDRAND结果存EAX等
AX: | 15 - 8 | 7 - 0 |
AH AL
• AX(16 位):EAX 的低 16 位。
• AH(8 位):AX 的高 8 位。
• AL(8 位):AX 的低 8 位。
EAX: | 31 - 16 | 15 - 8 | 7 - 0 |
AH AL
(AX)
• EAX(全 32 位):完整寄存器,包含所有 32 位。
• AX(低 16 位):EAX 的低 16 位,包含 AH 和 AL。
• AH(高 8 位):AX 的高 8 位。
• AL(低 8 位):AX 的低 8 位。
RAX: | 63 - 32 | 31 - 16 | 15 - 8 | 7 - 0 |
EAX AH AL
| |
AX (16位)
• RAX(64 位):完整寄存器,包含所有 64 位数据。
• EAX(32 位):RAX 的低 32 位。
• AX(16 位):EAX 的低 16 位。
• AH(8 位):AX 的高 8 位。
• AL(8 位):AX 的低 8 位。


高八位与低八位:高八位和低八位是指在计算机中,一个16位的数或值用8位表示高位,8位表示低位。例如,对于一个16位的内存地址,前8位是高八位,后8位是低八位(在平时使用时一般都是低位用起来方便,因为有专门的名称,如果想要使用高八位则要使用移位的方式)(high与low)
大端规则与小端规则:大端规则就是把数值的高位字节放在内存的低位地址上,把数值的地位字节放在内存的高位地址上,而小端规则把数值的高位字节放在内存的低位地址上,把数值的地位字节放在内存的高位地址上。(不管是大端法还是小端法存储,计算机在内存中存放数据的顺序都是从低地址到高地址,所不同的是首先取低字节的数据存放在低地址还是取高字节数据存放在低地址,也就是说大端法或小端法是数据在存储时的表现,而不是在寄存器中参与运算时的表现)
打个比方:
假设我们要存储数字 0x12345678,这个数字是 4 个字节的(不够四个则自动补0),分别是:
在小端法下存储(低字节在前):
复制代码地址: 0x100 0x101 0x102 0x103
数据: 78 56 34 12
在大端法下存储(高字节在前):
复制代码地址: 0x100 0x101 0x102 0x103
数据: 12 34 56 78
标志寄存器(EFLAG)
大小:标志寄存器占两个字节,x64虽然是64位的,但也实际只占两个字节
作用:
•用来储存相关指令的某些执行结果
•用来控制CPU的相关工作方式
•用来为CPU执行相关指令提供行为依据
种类:
• 状态标志(CF/ZF/SF/PF/OF/AF):记录程序运行结果
• 控制标志(DF/IF/TF):控制执行指令的方式
==每一个寄存器都有全部的标志寄存器==
CF:进位标志(有进位或借位时为1)(例如1111 1111 + 1 = 1 0000 0000 但是会发生数据截断所以这个1存储到CF中,数据变为0000 0000,没有截断/溢出CF不会发生改变)(WORD操作时好像不会触发)
ZF:零标志(结果为0时为1)
SF:结果最高位为1,SF为1(二进制数据的符号位)
PF:奇偶标志(结果的最低有效字节(十六进制数的后两位)中1的个数为偶数时为1)
OF:溢出标志(结果有溢出时为1)(CF针对无符号整数,OF针对有符号整数)(OF = CF XOR MSB[最高有效位])
(正数+正数=负数 -> 发生溢出 ; 负数+负数=正数 -> 发生溢出 ; 正数+负数=任意 -> 不会发生溢出)
AF:辅助进位标志(WORD操作时是低字节向高字节进/借位为1,DWORD操作时低八位向高八位进/借位为1)

图中三种情况时第一个与第二个数相加然后得出第三个数时AF的反应:
反映了一个事情WORD与DWORD的判断逻辑有相同的地方,如果在低八位的低字节的两个十六进制数(前四个bit与后四个bit)没有发生进位,那么AF不会发生变化
DF:方向标志(DF=1,地址由高向低,DF=0,地址由低向高)
IF:中断允许标志(IF=1,CPU可以响应中断申请,IF=0则不能)
TF:TF=1,CPU每执行一条指令产生一个单步中断请求


翻译:
- ID 标志(ID)
- 虚拟中断挂起(VIP)
- 虚拟中断标志(VIF)
- 对齐检查 / 访问控制(AC)
- 虚拟 8086 模式(VM) or 保护模式
- 恢复标志(RF)
- 嵌套任务(NT)
- I/O 特权级(IOPL)
- 溢出标志(OF)
- 方向标志(DF)
- 中断使能标志(IF)
- 陷阱标志(TF)
- 符号标志(SF)
- 零标志(ZF)
- 辅助进位标志(AF)
- 奇偶标志(PF)
- 进位标志(CF)
- S 表示状态标志
- C 表示控制标志
- X 表示系统标志
- 保留的位位置。不要使用。
- 始终设置为先前读取的值。

转换时十六进制数可以直接写成二进制数(就是把二进制数每四个隔一段就是一个十六进制数字)
指令指针寄存器
指令指针寄存器 ---- EIP
EIP存放下一条要执行的指令地址
段寄存器
段:内存中一段连续区域用于储存特定的数据或代码
CS:代码段寄存器
DS:数据段寄存器
ES:附加段寄存器
FS:FS寄存器主要用于指向当前活动线程的TEB结构(线程环境块),在内核态(R0)和用户态(R3)下的指向有所不同(线程进入内核之后就会指向KPCR)
SS,SP,BP:栈段寄存器(SS储存基地址,SP储存及地址相对于栈顶的偏移量,BP与SP类似,访问时SS+SP(BP)即可得到目标地址)
==通常默认的数据段寄存器为DS,但在进行字符串操作时变为ES==
段选择子与段描述符
段基地址:是在计算机的内存管理体系中,表示一个段(Segment)在内存中的起始位置。它是由段寄存器(如 CS, DS, SS 等)提供的值,定义了该段的开始位置。段基地址是整个段的起始地址,从这个地址开始,偏移地址(Offset)可以用来访问该段内的具体数据。(但是在 x86 架构中的实模式下,段寄存器存储的是段基地址的“段基值”,它表示的是段在内存中的位置,单位是 16 字节。因此,我们需要将段寄存器中的值乘以 16(物理地址的计算公式为:物理地址=段地址×16+偏移地址物理地址=段地址×16+偏移地址)(别问,问就是历史遗留问题),才能得到段的实际物理起始地址,但是的但是在64位计算机中已经采用虚拟内存不需要将段寄存器的值乘以 16 来计算物理地址)(这些都是段模式的问题,在页模式并没有这个东西)
举例说明
假设 DS 寄存器的值为 0x1000,而要访问数据的偏移地址为 0x200。则物理地址为:
物理地址 = DS × 16 + 偏移地址
= 0x1000 × 16 + 0x200
= 0x10000 + 0x200
= 0x10200
使用示例
MOV AX, DS:[0x200] ; 从 DS 指向的段的偏移地址 0x200 处读取数据到 AX
MOV DS:[0x200], AX ; 将 AX 寄存器的值写入 DS 指向的段的偏移地址 0x200
MOV DS:[eax],AL ; 将eax的值作为一个偏移地址
CS:IP寄存器
CS:IP 两个寄存器指示了 CPU 当前将要读取的指令的地址,其中 CS 为代码段寄存器,而 IP 为指令指针寄存器
什么叫做指示了 CPU 当前将要读取的指令呢?在 8086 CPU 中,为什么 CPU 会自动的执行指令呢?
这些指令肯定是存放在内存中的,但是 CPU 怎么知道这些指令存放在内存的那个位置呢?
比如,我有下面的两条指令要执行:
MOV AX,1234H
MOV BX,AX
而假设这两条指令在内存中存放为:

很显然, 1000H:0000H 指向的是 MOV AX,1234H 的首地址,
如果 CPU 要读取到我的指令的话,很显然,必须要知道地址 1000H:0000H ,
然后 CPU 就可以根据这个首地址,将汇编指令 MOV AX,1234H 所对应的机器码读入到 CPU 的指令寄存器中,
最后便可以在 CPU 中进行处理了。
但关键是 CPU 如何知道我的 1000H:0000H 这个首地址?其实这就需要使用到 CS:IP 这个寄存器组了 。
当我们运行一个可执行文件时,很明显,我们需要另外一个程序来将这个可执行文件加载到内存当中,
关于这个加载可执行文件的程序,我们在这里不管他,点一下即可,
一般是通过操作系统的外壳程序(也就是传说中的 Shell 程序),
Shell 将可执行文件加载到内存中以后,就会设置 CPU 中的两个寄存器,**即设置 CS:IP 两个寄存器指向可执行文件的起始地址,此后 CPU 便从这个起始地址开始读取内存中的指令,并且执行。**
比如我们在写汇编程序时,通常会使用 START 标记,其实这个标记就是用来标记起始地址的,
当将一个汇编程序编译,连接成可执行文件以后,再通过操作系统的 Shell 程序将可执行文件加载到内存中以后,
这个 START 所标记处的地址就是整个可执行文件的起始地址了 。
也就是说,当一个可执行文件加载到内存中以后,CS:IP 两个寄存器便指向了这个可执行文件的起始地址,
然后 CPU 就可以从这个起始地址开始往下读取指令,
当读取完指令后,CS:IP 将会自动的改变,基本上是改变 IP ,从而指向下一条要读取的指令,这样就可以执行这个可执行文件了 。
最后再对 CS:IP 总结一下:
- 你想让 CPU 执行哪行指令,你就让 CS:IP 指向保存有指令的那块内存即可。
- 任何时候,CS:IP 指向的地址中的内容都是 CPU 当前执行的指令。
指令
指令=操作码+操作数
指令和数据的寻址方式
寻址方式:确定本条指令的数据地址或下一条要执行的指令地址的方法。

汇编代码":"的含义:
DS:偏移地址
数据寻址方式说明
目的操作数和源操作数均可采用不同的寻址方式(你可以让源和目的分别来自不同的地方源可以是内存、寄存器、立即数……目的可以是寄存器、内存……)
两个操作数的类型必须一致。
AX表示16位寄存器,AH、AL表示其高低字节,AX 可以拆分成两个 8 位寄存器 AH 和 AL 单独使用。
立即寻址
操作数就在指令中,紧跟在操作码后面,作为指令一部分存放在内存的代码段中,该操作数为立即数,这种寻址方式称为立即寻址方式。数据通常采用补码的形式存放。常用于给寄存器赋初值(作用);
注意
①立即数可以送到寄存器、一个存储单元(8位)、两个连续的存储单元(16位)中去;
②立即数只能作源操作数,不能作目的操作数;
③以A~F打头的数字,前面必须加数字0。
如下指令
MOV AL,3400H ×,前后格式不匹配
MOV AX,34H √,等价于 MOV AX,0034H
MOV 87H,BX ×,立即数不能作为目的操作数
MOV AX,F98AH ×,应为 MOV AX,0F98AH
设16位计算机中,存储器宽度为16位,一条指令占据一个机器字。
已知操作码为6位,目的操作数为寄存器编号,占4位;
源操作数寻址方式为 立即数寻址 ,以补码整数形式给出,
则该指令立即数寻址方式的数值范围是多少?

操作码占6位,目的操作数据占4位,那么立即数(源操作数)占6位,按补码的形式,第一位为符号位,所以数值范围为 -32~31 。
立即寻址方式的
优点:指令已经提供操作数,无需再次访问存储器。提供操作数最快。
缺点
①操作数为指令一部分,不能修改,适用于给某一寄存器或存储单元赋初值等操作。
②指令中A的位数限制了这类指令所表述的立即数的范围。、
直接寻址
存储单元的有效地址EA(即:操作数的有效地址)直接由指令给出。
作用:实现对存储单元的读/写操作。
特点:
访问存储器次数较少;
操作数地址在指令中,灵活性较差。
操作数A的位数限制了操作数的寻址范围
(存储器)间接寻址
操作数所在内存单元的地址通过存储器间接给出。
优点:实现简便,对编程带来较大的灵活性,当操作数地址改变时,只需修改间接地址指示器的单元内容,而不必修改指令,原指令的功能照样实现。这给编程带来很大方便。
多次访问内存,增加了指令的执行时间;占用主存储器单元多。
寄存器寻址
操作数包含在寄存器中,寄存器的名称由指令指定。
常用于寄存器之间传递数据。优势是速度快。注意源操作数的长度必须与目的操作数一致。
寄存器间接寻址
操作数所在内存单元的地址通过寄存器间接给出。
作用:有效地址可以存放在寄存器中。
相对地址
操作数的有效地址是程序计数器PC的内容与n位位移量。
位移量的概念:在寄存器间接寻址给出的偏移地址上,加上一个相对偏移量。位移量是一个带符号的补码机器数。
可有效缩短地址的长度 。
兼具灵活性,只与PC相对位置有关,与绝对地址无关。
相对基址寻址
这种寻址方式下,EA是由两部分组成的,基址寄存器BX或BP的内容加上偏移量DISP。(EA=BX(BP)+DISP(偏移量))
相对变址寻址
这种寻址方式下,EA是由两部分组成的,变址寄存器的内容加上偏移量DISP。
与基址寻址类似。基址寻址,往往变化的是形式地址;变址寻址,往往形式地址不变,变化的是变址寄存器。(EA=SI(DI)+DISP(偏移量))
相对基址变址寻址
这种寻址方式下,EA是由三部分组成的,基址寄存器BX或BP的内容加上变址寄存器的内容,以及位移量的和。(EA=BX(BP)+SI(DI)+DISP(偏移量))
常用操作指令
数据传输指令
mov
mov指令:最常见的数据传输指令,相当于赋值操作(当操作数中有内存地址时需要加[])(AX ,CX ,DX ,SP不能用来指定内存地址)
mov 目的操作数,源操作数
mov可以实现寄存器与寄存器之间,寄存器与内存之间,寄存器与立即数之间,内存立即数之间的内存传递,但内存与内存之间是无法直接传输数据的,目的操作数不能为立即数,且两个操作数的宽度一致
在对于地址赋值时可以通过BYTE,WORD,DWORD来控制取多少字节(例如MOV EAX,DWORD PTR DS:[0x12345678] 就是从12345678开始到1234567B的数据存储到EAX)

movzx
把较小数据单位的数据移动到较大数据单位的寄存器中,并且移动的时候将高位设置为0,但是它只适用于无符号整数
大致有以下三种格式
movzx 32位通用寄存器, 8位通用寄存器/内存单元
movzx 32位通用寄存器, 16位通用寄存器/内存单元
movzx 16位通用寄存器, 8位通用寄存器/内存单元
xchg
xchg指令:交换两个操作数据
xchg 目的操作数,源操作数
同样不允许内存与内存交换数据
lea
LEA指令用于计算地址,将有效地址加载到寄存器中,不会访问内存。
LEA 目标寄存器, [源操作数]
目标寄存器:存放计算出的地址。
源操作数:通常是一个内存地址表达式(如偏移量或基址+索引)
标志位操作指令(CLC,STC,CLD,STD,CLI,STI,CMC)
| 指令 |
全称 |
功能 |
| CLC |
clear carry flag |
CF清零 |
| STC |
set carry flag |
CF置位1 |
| CMC |
complement carry flag |
CF取反 |
| CLD |
clear direction flag |
DF清零 |
| STD |
set direction flag |
DF置位1 |
| CLI |
clear interrupt endable flag |
IF清零,关闭中断 |
| STI |
set interrupt endable flag |
IF置位1,打开中断 |
算数运算指令
add
add:加
ADD 目标寄存器, [源操作数](结果储存在目的操作数中)
影响标志位:OF ,SF ,ZF ,AF ,CF ,PF
adc
adc:加,与add类似
ADC 目标寄存器, [源操作数](结果储存在目的操作数中)
add与adc的区别:
ADD:目标 = 目标 + 源
ADC:目标 = 目标 + 源 + CF(进位寄存器)
inc
inc:自增
inc 目标操作数
将目标操作数加1,同时保持CF标志的状态不变
sub
sub:减
SUB 目标寄存器, [源操作数](结果储存在目的操作数中)
影响标志位:OF ,SF ,ZF ,AF ,CF ,PF
sbb
sbb:减,与sub类似
SBB 目标寄存器, [源操作数](结果储存在目的操作数中)
sub与sbb的区别:
SUB:目标 = 目标 - 源
SBB:目标 = 目标 - 源 - CF(进位寄存器)
dec:自减
DEC 目标操作数
位运算
xor
xor:异或
| A |
B |
XOR |
| 0 |
0 |
0 |
| 0 |
1 |
1 |
| 1 |
0 |
1 |
| 1 |
1 |
0 |
其他逻辑运算指令
|
1+1 |
0+0 |
0+1 |
1+0 |
| AND |
1 |
0 |
0 |
0 |
| OR |
1 |
0 |
1 |
1 |
| XOR |
0 |
0 |
1 |
1 |
| NOR |
0 |
1 |
0 |
0 |
| XNOR |
1 |
1 |
0 |
0 |
| XAND |
0 |
1 |
0 |
0 |
堆栈操作指令
push
push:入栈(压栈)
push reg/mem/imm ; 将寄存器、内存或立即数压入栈中
pop
pop:出栈
pop reg/mem ; 从栈中弹出数据到寄存器或内存
pushad
pushad:将所有32 位通用寄存器按顺序压入栈中,保存程序当前寄存器状态,常用于子程序调用或中断处理。
压入顺序:
EAX → ECX → EDX → EBX → 原ESP → EBP → ESI → EDI
==*sp会增加48,因为每个寄存器都占4字节空间**==
popad
popad:按上面相反的顺序将寄存器的值弹出并依次恢复这些寄存器的原始值
pushfd
pushfd:将标志寄存器的内容压入堆栈
popfd
popfd:将栈堆顶部的32位内容弹入标志寄存器
顺序说明(从低位到高位)
| 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
| ID | VIP| VIF| AC | VM | RF | NT | IOPL | OF | DF | IF | TF | SF | ZF | 0 | AF |
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| 0 | RF | 0 | NT | IOPL | OF | DF | IF | TF | SF | ZF | 0 | AF | 0 | PF | CF |
逐位解释(从低位到高位)
| 位 |
标志位 |
名称 |
说明 |
| 0 |
CF |
进位标志 |
加/减法运算的进位或借位 |
| 1 |
0 |
保留位 |
始终为 0 |
| 2 |
PF |
奇偶标志 |
结果低 8 位中 1 的数量是否为偶数 |
| 3 |
0 |
保留位 |
始终为 0 |
| 4 |
AF |
辅助进位标志 |
半字节运算的进位或借位(BCD 运算) |
| 5 |
0 |
保留位 |
始终为 0 |
| 6 |
ZF |
零标志 |
运算结果是否为零 |
| 7 |
SF |
符号标志 |
结果符号是否为负 |
| 8 |
TF |
陷阱标志 |
启用单步调试 |
| 9 |
IF |
中断使能标志 |
控制中断的开启与关闭 |
| 10 |
DF |
方向标志 |
字符串操作方向 |
| 11 |
OF |
溢出标志 |
运算是否溢出 |
| 12-13 |
IOPL |
I/O 特权级 |
当前任务的 I/O 特权级 |
| 14 |
NT |
嵌套任务标志 |
控制多任务嵌套 |
| 15 |
0 |
保留位 |
始终为 0 |
| 16 |
RF |
恢复标志 |
控制调试时的指令响应 |
| 17 |
VM |
虚拟 8086 模式标志 |
是否启用虚拟 8086 模式 |
| 18 |
AC |
对齐检查标志 |
控制内存对齐检查 |
| 19 |
VIF |
虚拟中断标志 |
反映虚拟中断的状态 |
| 20 |
VIP |
虚拟中断挂起 |
表示是否有虚拟中断挂起 |
| 21 |
ID |
ID 标志 |
是否支持 CPUID 指令 |
| 22-31 |
0 |
保留位 |
始终为 0 |
具体示例
假设当前 EFLAGS 寄存器的值为:
EFLAGS = 0x00000286 (二进制: 0000 0000 0000 0000 0000 0010 1000 0110)
- CF = 0 (第 0 位)
- PF = 1 (第 2 位)
- AF = 0 (第 4 位)
- ZF = 1 (第 6 位)
- SF = 0 (第 7 位)
- TF = 0 (第 8 位)
- IF = 1 (第 9 位)
- DF = 0 (第 10 位)
- OF = 0 (第 11 位)
压入堆栈后的堆栈结构:
栈顶 -> 0000 0000 0000 0000 0000 0010 1000 0110 (低地址)
栈底 -> (高地址)
转移指令(jcc指令)
jmp
jmp:只要遇见直接跳转执行(只有一个操作数)
jmp指令后面跟四个十六进制数,所以一个jmp指令的偏移一般是5
JMP 寄存器/内存地址/立即数
条件跳转
<img src="D:\Typora\image\005.jpg" style="zoom:150%;" />
JCC搭配用法
test
test:用于进行按位与操作(AND)并更新标志寄存器,但不保存结果
test 目的操作数,源操作数
常见用途:
-
检查某些位是否为1: 通过 test 指令,你可以检查某些特定的比特位是否为 1。例如:
test al, 1 ; 检查 AL 寄存器最低位(LSB)是否为 1
-
判断两个值是否相等: 如果两个操作数相等,则它们的按位与结果不为 0,test 指令可以用来判断这一点:
test eax, ebx ; 检查 eax 和 ebx 是否有相同的位
jz equal ; 如果相等,跳转
-
检查特定位: test 可以与位掩码配合使用,用来检查某些特定的标志位:
test eax, 0x4 ; 检查 eax 寄存器的第 3 位是否为 1
cmp
cmp:用于对两个操作数执行 减法,并根据结果更新标志寄存器,但不保存结果
cmp 目的操作数,源操作数
常见用法:
-
判断两个值是否相等: 通过 cmp 可以检查两个值是否相等,如果它们相等,零标志(ZF)将被设置为 1。
cmp eax, ebx ; 比较 eax 和 ebx 的值
je equal ; 如果相等,跳转到 "equal"
-
判断大小关系: cmp 可以用来判断两个操作数的大小关系,根据结果设置相应的标志位,从而通过条件跳转指令(如 jg、jl、jge、jle)做出决策。
cmp eax, ebx ; 比较 eax 和 ebx
jg greater ; 如果 eax > ebx,跳转到 "greater"
jl less ; 如果 eax < ebx,跳转到 "less"
-
判断某个值是否为零或负数: cmp 可以用来检查一个值是否为零或负数。
cmp eax, 0 ; 比较 eax 和 0
je is_zero ; 如果 eax == 0,跳转到 "is_zero"
js is_negative ; 如果 eax < 0,跳转到 "is_negative"
Loop
loop:循环
LOOP 目标地址
工作原理:
LOOP 指令会做两件事:
- 递减
CX 寄存器:LOOP 指令会将 CX 寄存器(或在 32 位模式下是 ECX)的值减 1。
- 判断是否跳转:如果
CX(或 ECX)的值不为零,程序跳转到指定的目标地址。否则,程序继续执行后续的指令。
例子
假设我们需要执行一个循环,重复执行某个任务 5 次:
MOV CX, 5 ; 将循环次数放入 CX 寄存器
loop_start:
; 循环体的代码(这里是伪代码,表示你要执行的任务)
LOOP loop_start ; 递减 CX,若不为零则跳回 loop_start 继续执行
调用与返回
call
call:用于调用子程序(函数),它将程序的控制权转移到子程序的入口地址,并在子程序执行完毕后返回到调用处继续执行
CALL 目标地址(也可以调用寄存器的地址)
工作原理
当执行 CALL 指令时,CPU 会进行以下操作:
- 保存返回地址:
CALL 指令会将下一条指令的地址(即 CALL 指令的下一条指令的地址)压入栈中。这个返回地址是程序在子程序执行完毕后跳回的地址。
- 跳转到目标地址:
CALL 会将程序计数器(EIP 或 IP)设置为目标地址,跳转到子程序的开始执行。
- 返回:子程序执行完毕后,会通过
RET 指令从栈中弹出返回地址,跳回到 CALL 指令的下一条指令继续执行。
ret
ret:用于从子程序(函数)返回到调用该子程序的位置
RET
工作原理
当执行 RET 指令时,CPU 会从栈中弹出返回地址,并将程序计数器(EIP 或 IP)设置为该地址,从而使程序跳转回 CALL 指令后的位置继续执行。
RET 指令的变种
-
带操作数的 RET(清理栈):RET n 指令用于清理由调用方传递给被调用者的参数。n 是一个立即数,表示需要从栈中弹出的字节数。
RET 8 ; 弹出 8 字节,通常用于清理传入的参数
示例:CALL 与 RET 的配合
section .text
global _start
_start:
CALL my_function ; 调用函数
; 在此处继续执行
my_function:
; 子程序代码
RET ; 从子程序返回
- 当程序执行到
CALL my_function 时,CALL 将当前指令的下一条地址压入栈中,跳转到 my_function。
- 在
my_function 执行完毕后,RET 指令会将栈中的返回地址弹出,并跳转回到 CALL 后的地址。
int
int 中断号
中断号:一个 8 位的立即数(0 到 255),用于指定要触发的中断
MOVS
movs:字符串传送指令,用于将一段内存中的数据块传送到另一段内存中,按 字节、字或双字 进行传送(通俗来讲就是复制)
指令格式:
MOVSB ; 传送一个字节 (Byte)
MOVSW ; 传送一个字 (Word, 2 字节)
MOVSD ; 传送一个双字 (Double Word, 4 字节)
MOVSQ ; 传送一个四字 (Quad Word, 8 字节,仅 64 位)
操作方式:
DS:SI 指向源地址(源字符串的起始地址)
ES:DI 指向目标地址(目标字符串的起始地址)
DS和ES为段寄存器,SI和DI为偏移地址
-
每次执行后,SI 和 DI 自动更新,指向下一个字节/字/双字的位置。
-
方向由 DF(方向标志位)决定:
DF=0 时,SI 和 DI 自增,从低地址到高地址传输(正向传输)。
DF=1 时,SI 和 DI 自减,从高地址到低地址传输(逆向传输)。
-
CLD(Clear Direction Flag):使 DF=0(正向传输)
-
STD(Set Direction Flag):使 DF=1(逆向传输)
示例:
- 按字节传送 100 个字节的数据:
CLD ; 设置方向标志为0 (正向)
MOV CX, 100 ; 传送100个字节
MOV SI, OFFSET SRC ; 源地址
MOV DI, OFFSET DEST ; 目标地址
REP MOVSB ; 重复传送,直到CX=0
解释:
REP 会重复执行 MOVSB,直到 CX 变为 0,每次传送 1 字节,SI 和 DI 分别自增 1。
示例:MOVS 逐字节传送
假设内存如下:
SRC: 41 42 43 44 45 ; 'ABCDE' 的 ASCII 码
DEST: 00 00 00 00 00 ; 目标地址初始为空
汇编代码:
CLD ; 确保正向传送 (从低地址到高地址)
MOV SI, OFFSET SRC ; 源地址
MOV DI, OFFSET DEST ; 目标地址
MOVSB ; 传送1个字节 ('A')
MOVSB ; 传送1个字节 ('B')
MOVSB ; 传送1个字节 ('C')
MOVSB ; 传送1个字节 ('D')
MOVSB ; 传送1个字节 ('E')
STOPS
stops:用于将寄存器中的数据存储到内存
指令格式:
示例:存储单个字节
MOV AX, 2000h ; 目标段地址
MOV ES, AX ; 设置 ES = 2000h
MOV DI, 1000h ; 目标地址偏移
MOV AL, 'A' ; 要存储的字符 'A'
CLD ; 清方向标志,DI 正向增加
STOSB ; 存储 AL ('A') 到 ES:DI (2000:1000)
结果:
内存地址 2000:1000 存储字符 'A',DI 自动增加1。
REP
rep:用于重复执行字符串操作指令的前缀
常见用法:
指令格式:
REP MOVSB ; 重复移动字节
REP MOVSW ; 重复移动字
REP STOSB ; 重复存储字节
REP SCASB ; 重复扫描字节
db
db 是汇编语言中的一条伪指令(Pseudo-Instruction),用于定义字节(Define Byte)数据。它通常用于在程序中声明和初始化一个或多个字节的数据
(db 是汇编器的伪指令,不会被 CPU 直接执行,汇编器会将 db 定义的数据转换为二进制形式,并存储在生成的目标文件中)
RESB
RESB 是汇编语言中的一条伪指令(Pseudo-Instruction),用于在程序中预留指定数量的未初始化字节空间
(RESB 指令本身不会被 CPU 直接执行,而是由汇编器(Assembler)处理,生成相应的目标文件)
基本语法:
RESB count
NASK
nask与RESB差不多,不同的是nask会在空出来的地址上自动填充上0x00
ptr
ptr -- pointer (既指针)得缩写, 汇编里面 ptr 是规定 的 字 (既保留字),是用来临时指定类型的 (可以理解为,ptr是临时的类型转换,相当于C语言中的强制类型转换)
mov ax,word ptr [bx]; 是把内存地址等于“BX寄存器的值”的地方所存放的数据,赋予ax。由于只是给出一个内存地址,不知道希望赋予ax的,是byte还是word,所以可以用word明确指出;如果不用,既(mov ax, [bx]; )则在8086中是默认传递一个字,既两个字节给ax(防止当两个操作数的宽度不一样)
offset
OFFSET为属性操作符,表示应该把其后跟着的符号地址的值(不是内容)作为操作数
示例:
.data
myVar db 10 ; 定义一个字节变量 myVar,初始值为 10
mov eax, OFFSET myVar ; 将 myVar 的地址加载到寄存器 eax 中
如果你想获取 myVar 的地址,而不是 myVar 的值,可以使用 OFFSET 操作符
在这个例子中:
myVar 是一个符号,代表内存中的一个字节,其值为 10。
OFFSET myVar 返回的是 myVar 在内存中的地址,而不是 10。
mov eax, OFFSET myVar 将 myVar 的地址加载到 eax 寄存器中(如果不使用 OFFSET,直接使用 myVar,则会操作 myVar 的内容,即将 myVar 的值(10)加载到 al 寄存器中)
push data(数据段的变量?)push的是这个东西的地址,而不是他的内容
word/byte ptr
指明了指令访问的内存单元是一个字/字节单元
mov eax,byte ptr [esi] #表示从esi指向的地方读取一个字节到eax中
mov byte ptr ds:[0], 1 #将数字1移动到数据段(DS)偏移地址为0的内存单元中,并且byte ptr表明这个内存单元是一个字节大小
在没有寄存器参与的内存单元访问指令中,用word ptr或byte ptr显性地指明所要访问的内存单元的长度是很有必要的。否则,CPU无法得知所要访问的单元是字单元,还是字节单元
neg
neg:将操作数(目标操作数)的值替换为它的二进制补码
neg 目标操作数(只能寄存器)(即用0减操作数,并将求得的结果存入指定的寄存器或内存单元(把操作数按位取反,末位加1))
如果源操作数为0,则CF标志清除为0;否则设置为1,OF ,SF ,ZF ,AF ,CF标志根据结果设置(0的补码为自身,且不会影响CF标志位)
| 表示 |
正数 |
负数 |
| 原码 |
数据本身的二进制表示(符号位为0) |
数据本身的二进制表示(符号位为1) |
| 反码 |
和原码一样 |
符号位不变,其余各位按位取反(1变0,0变1) |
| 补码 |
和原码一样 |
反码加1 |
伪指令
汇编语言源程序必须翻译成机器语言才能被计算机运行,而翻译通常是由计算机通过汇编程序来实现,翻译过程称为汇编。在翻译过程中需要汇编语言源程序向汇编程序提供相应的编译信息,而这些信息是通过在汇编语言源程序中加入伪指令实现的。也就是说伪指令是放在汇编语言源程序中用于指示汇编程序如何对源程序进行汇编的指令
org
-
作用:指定程序或数据在内存中的起始地址(加载地址)。
-
用途:常用于裸机编程或引导加载程序(Bootloader),告诉汇编器代码将来会被加载到内存的哪个位置。
-
语法:
org <address>
-
示例:
org 0x7C00 ; 典型的 Bootloader 起始地址(BIOS 加载地址)
-
注意:
org 会影响后续所有标签和数据的地址计算。
- 如果没有指定
org,默认起始地址可能是 0。
section
-
作用:将代码或数据划分为逻辑段(如代码段、数据段、栈段等),便于链接器处理。
-
用途:现代汇编(如 NASM、GAS)中用于模块化程序结构,支持分段内存模型或分页机制。
-
语法:
section <name> [attributes]
-
常见段名:
.text:代码段(可执行指令)。
.data:已初始化数据段。
.bss:未初始化数据段(Block Started by Symbol)。
-
示例(NASM):
section .text
mov eax, 1 ; 代码段
section .data
msg db "Hello", 0 ; 数据段
-
属性(可选):
exec:可执行。
write:可写。
read:可读。
bits
-
作用:指定当前汇编代码的目标指令模式(16 位、32 位或 64 位)。
-
用途:在混合模式代码(如 Bootloader 或模式切换代码)中显式声明处理器模式。
-
语法:
asm
复制
bits 16 ; 16 位模式(实模式)
bits 32 ; 32 位模式(保护模式)
bits 64 ; 64 位模式(长模式)
-
示例:
asm
复制
bits 16
mov ax, 0x1234 ; 16 位指令
bits 32
mov eax, 0x12345678 ; 32 位指令
-
注意:
bits 不会改变实际 CPU 模式,需配合真正的模式切换指令(如 cr0 设置)。
- 在 NASM 中,
[BITS 16] 或 [BITS 32] 需放在代码段前。
DB
格式:[标号:] DB 项或项表
作用:用于定义字节数据,可以定义一个字节也可以定义多个字节。定义多个字节时,两两之间用逗号隔开,定义时多个字节是在存储器中连续存放的。
例: ORG 3000H
TAB1: DB 12H,34H
DB '5','A','abc'
DW
格式:[标号:] DW 项或项表
作用:与DB相似,但用于定义字数据。项或项表所定义的一个字在存储器中占两个字节。汇编时,机器自动按高字节在前、低字节在后存放,即高字节存放在低地址单元,低字节存放在高地址单元。
例: ORG 3000H
TAB2:DW 1234H, 5678H
DS
格式:[标号:] DS 数值表达式
作用:用于在存储器中保留一定数量的字节单元。保留存储单元空间是为了以后存放数据使用。保留的字节单元数由表达式的值决定。
例: ORG 3000H
TAB1:DB 12H, 34H
DS 4H
DB '5'
EQU
格式:EQU 项
作用:将指令中项的值赋予EQU前面的符号。项可以是常数、地址标号或表达式。
例:
TAB1 EQU 1000H
TAB2 EQU 2000H
结果:TAB1的值为1000H,TAB2的值为2000H。
DATA
格式:符号 DATA 直接字节地址
作用:用于给片内RAM字节单元地址赋予DATA前面的符号,符号以字母开头,同一单元地址可以赋予多个符号。赋值后可用该符号代替DATA后面的片内RAM字节单元地址(有点像C里面的define)
例:
RESULT DATA 60H (RESULT代表片内RAM的60H单元)
....
MOV RESULT , A
XDATA
格式:符号 XDATA 直接字节地址
作用:与DATA基本相同,不同点是对片外RAM的字节单元
bit
格式:符号 bit 位地址
作用:用于给位地址赋予符号,经赋值后可用该符号代替bit后面的位地址。
例:
PLG bit F0
AL bit P1.0
END
格式:放于程序最后位置,指明程序的结束位置。
ENTRY
语法格式:
ENTRY:
ENTRY伪指令用于指定汇编程序的入口点。在一个完整的汇编程序中至少要有一个ENTRY(也可以有多个,当有多个ENTRY时,程序的真正入口点由链接器指定),但在一个源文件里最多只能有一个ENTRY(可以没有)。 (类似于C的main)
使用示例:
AREA Init ,CODE ,READONLY
ENTRY ; 指定应用程序的入口点
AREA
语法格式:
AREA 段名 属性1 ,属性2 ,……
AREA伪指令用于定义一个代码段或数据段。其中,段名若以数字开头,则该段名需用“|”括起来,如:|1_test| 。
属性字段表示该代码段(或数据段)的相关属性,多个属性用逗号分隔。常用的属性如下:
— CODE 属性:用于定义代码段,默认为READONLY 。
— DATA 属性:用于定义数据段,默认为READWRITE 。
— READONLY 属性:指定本段为只读,代码段默认为READONLY 。
— READWRITE 属性:指定本段为可读可写,数据段的默认属性为READWRITE 。
— ALIGN 属性:使用方式为ALIGN表达式。在默认时,ELF(可执行连接文件)的代码段和数据段是按字对齐的,表达式的取值范围为0~31,相应的对齐方式为2表达式次方。
— COMMON 属性:该属性定义一个通用的段,不包含任何的用户代码和数据。各源文件中同名的COMMON段共享同一段存储单元。
一个汇编语言程序至少要包含一个段,当程序太长时,也可以将程序分为多个代码段和数据段。
使用示例:
AREA Init ,CODE ,READONLY ; 该伪指令定义了一个代码段,段名为Init ,属性为只读。
global
global关键字用来让一个符号对链接器可见,可以供其他链接对象模块使用。
global _start 让_start符号成为可见的标示符,这样链接器就知道跳转到程序中的什么地方并开始执行。linux寻找这个 _start标签作为程序的默认进入点。
在汇编和C混合编程中,在GNU ARM编译环境下,汇编程序中要使用.global伪操作声明汇编程序为全局的函数,意即可被外部函数调用,同时C程序中要使用extern声明要调用的汇编语言程序。
extern
extern XXXX 说明xxxx为外部函数,调用的时候可以遍访所有文件找到该函数并且使用它。
补充
.586 ;指定使用的指令集.386也可以
.model flat, stdcall ;MODEL [内存模式], [调用约定]
;保护模式 -- 每为个进程都有独立的虚拟内存
;FLAT - 平坦模式 - 代码和数据段上限都是4GB
;.model flat == ASSUME CS:FLAT DS:FLAT SS:FLAT ES:FLAT FS:ERROR GS:FLAT
;调用约定 _cdecl,stdcall,fastcall,thiscall
option casemap:none ;大小写敏感检查
includelib User32.lib ;导入库文件
MessageBoxA PROTO hwnd:dword, LPTEXT:byte, LPCAPTION:byte, UTYPE:dword;函数声明
;函数名 PROTO 参数名:类型, 参数名:类型...(参数名可以随便写)
;代码段 - .code
;数据段 - .data(已知要初始化的数据) , .data?(不知道要初始化什么的数据) , .const(常量数据) .......
;堆栈段 - .stack
;data? 与可以写在data中,值改为?即可
;段那可以重复定义
.data ;数据段
; 变量名 数据类型 大小
.code ;代码段[.code-- end] code开头 end结尾
main proc ; 过程 proc 开始 endp结尾
push 0
push 0
push 0
push 0
call MessageBoxA
ret ; return
main endp
end
;标号:如果不手动设置程序入口点可以用start: + end start
不能直接赋值和参与运算的寄存器
在 x86/x86-64 架构中,以下类型的寄存器通常不能直接通过通用指令赋值或参与算术/逻辑运算,需要特殊操作方式或权限:
一、段寄存器
- DS(Data Segment)
- ES(Extra Segment)
- FS、GS(Extended Segments)
- SS(Stack Segment)
- CS(Code Segment)
限制:
-
不能直接用 MOV 赋值立即数,必须通过通用寄存器中转:
MOV AX, 0x10 ; 先赋值给通用寄存器
MOV DS, AX ; 再转到段寄存器
-
不能直接参与算术/逻辑运算(如 ADD DS, AX 非法)。
二、控制寄存器
- CR0(控制操作模式,如分页/保护模式)
- CR2(页故障线性地址)
- CR3(页表基址)
- CR4(扩展功能控制)
- CR8(x86-64 中用于任务优先级)
限制:
-
需要 MOV 指令操作,但只能在特权级 0(内核模式)下修改。
-
不能直接参与算术运算,需通过通用寄存器中转:
MOV EAX, CR0
OR EAX, 0x80000000 ; 修改值
MOV CR0, EAX
三、调试寄存器
- DR0-DR3(硬件断点地址)
- DR6(调试状态)
- DR7(调试控制)
限制:
- 需通过
MOV 指令访问,但需要内核权限。
- 用户态程序无法直接操作。
四、测试寄存器
五、任务寄存器
限制:
六、全局描述符表寄存器和中断描述符表寄存器
- GDTR(全局描述符表基址)
- IDTR(中断描述符表基址)
限制:
七、标志寄存器
限制:
- 不能直接用
MOV 修改,需通过 POPF/PUSHF 或 STC/CLC 等专用指令。
- 用户态程序可能无法修改某些敏感标志位(如 IF 中断标志)。
八、浮点和 SIMD 寄存器
- FPU 寄存器(如 ST0-ST7)
- XMM/YMM/ZMM 寄存器(SSE/AVX)
限制:
- 必须通过专用指令(如
FADD, MOVAPS, VADDPS)操作,不能直接参与通用整数运算。
关于__emit
asm _emit 0x0F 和 __asm _emit 0x31是什么意思?
在C语言中,允许直接插入汇编代码,甚至直接插入机器码。
插入汇编代zhuan码可以用asm关键字来表示,如:
asm {
mov eax, ebx ; 插入mov 指令
xor eax,edx ; 插入xor 指令
}
也可以直接插入机器码,用可用emit宏来表示,
如直接插入机器码0x50(机器指令为:PUSH EAX),可写为:
emit(0x50);
就是直接插入0X50,具体的指令要结合其它数据才能确定。
x86中
jmp相对跳转
EB (目标地址 - JMP地址 - 指令长度) 相对短跳转8位
E9 (目标地址 - JMP地址 - 指令长度) 相对跳转16位
E8也有call
x64中
绝对远跳转: FF 25 00 00 00 00 XX XX XX XX XX XX XX XX
FF 25: 是jmp
四个00: 不用管
八个XX: 是目标绝对地址
| 00 |
ADD |
reg8/mem8, reg8 |
| 操作码 |
助记符 |
操作数 |
| 01 |
ADD |
reg16/mem16, reg16 |
| 02 |
ADD |
reg8, reg8/mem8 |
| 03 |
ADD |
reg16, reg16/mem16 |
| 04 |
ADD |
AL, immed8 |
| 05 |
ADD |
AX, immed16 |
| 06 |
PUSH |
ES |
| 07 |
POP |
ES |
| 08 |
OR |
reg8/mem8, reg8 |
| 09 |
OR |
reg16/mem16, reg16 |
| 0A |
OR |
reg8, reg8/mem8 |
| 0B |
OR |
reg16, reg16/mem16 |
| 0C |
OR |
AL, immed8 |
| 0D |
OR |
AX, immed16 |
| 0E |
PUSH |
CS |
| 0F |
Not used |
- |
| 10 |
ADC |
reg8/mem8, reg8 |
| 11 |
ADC |
reg16/mem16, reg16 |
| 12 |
ADC |
reg8, reg8/mem8 |
| 13 |
ADC |
reg16, reg16/mem16 |
| 14 |
ADC |
AL, immed8 |
| 15 |
ADC |
AX, immed16 |
| 16 |
PUSH |
SS |
| 17 |
POP |
SS |
| 18 |
SBB |
reg8/mem8, reg8 |
| 19 |
SBB |
reg16/mem16, reg16 |
| 1A |
SBB |
reg8, reg8/mem8 |
| 1B |
SBB |
reg16, reg16/mem16 |
| 1C |
SBB |
AL, immed8 |
| 1D |
SBB |
AX, immed16 |
| 1E |
PUSH |
DS |
| 1F |
POP |
DS |
| 20 |
AND |
reg8/mem8, reg8 |
| 21 |
AND |
reg16/mem16, reg16 |
| 22 |
AND |
reg8, reg8/mem8 |
| 23 |
AND |
reg16, reg16/mem16 |
| 24 |
AND |
AL, immed8 |
| 25 |
AND |
AX, immed16 |
| 26 |
SEG OVERRIDE |
ES: |
| 27 |
DAA |
- |
| 28 |
SUB |
reg8/mem8, reg8 |
| 29 |
SUB |
reg16/mem16, reg16 |
| 2A |
SUB |
reg8, reg8/mem8 |
| 2B |
SUB |
reg16, reg16/mem16 |
| 2C |
SUB |
AL, immed8 |
| 2D |
SUB |
AX, immed16 |
| 2E |
SEG OVERRIDE |
CS: |
| 2F |
DAS |
- |
| 30 |
XOR |
reg8/mem8, reg8 |
| 31 |
XOR |
reg16/mem16, reg16 |
| 32 |
XOR |
reg8, reg8/mem8 |
| 33 |
XOR |
reg16, reg16/mem16 |
| 34 |
XOR |
AL, immed8 |
| 35 |
XOR |
AX, immed16 |
| 36 |
SEG OVERRIDE |
SS: |
| 37 |
AAA |
- |
| 38 |
CMP |
reg8/mem8, reg8 |
| 39 |
CMP |
reg16/mem16, reg16 |
| 3A |
CMP |
reg8, reg8/mem8 |
| 3B |
CMP |
reg16, reg16/mem16 |
| 3C |
CMP |
AL, immed8 |
| 3D |
CMP |
AX, immed16 |
| 3E |
SEG OVERRIDE |
DS: |
| 3F |
AAS |
- |
| 40–47 |
INC |
reg16 (AX, CX, DX, BX, SP, BP, SI, DI) |
| 48–4F |
DEC |
reg16 (AX, CX, DX, BX, SP, BP, SI, DI) |
| 50–57 |
PUSH |
reg16 (AX, CX, DX, BX, SP, BP, SI, DI) |
| 58–5F |
POP |
reg16 (AX, CX, DX, BX, SP, BP, SI, DI) |
| 60 |
PUSHA |
- |
| 61 |
POPA |
- |
| 62 |
BOUND |
reg16/mem16, reg16 |
| 63–67 |
Not used |
- |
| 68 |
PUSH |
immed16 |
| 69 |
IMUL |
reg16/mem16, immed16 |
| 6A |
PUSH |
immed8 |
| 6B |
IMUL |
reg8/mem8, immed8 |
| 6C |
INSB |
- |
| 6D |
INSW |
- |
| 6E |
OUTSB |
- |
| 6F |
OUTSW |
- |
| 70–7F |
Jcc |
immed8(各种条件跳转,如JO,JNO,JB,JNB,JZ,JNZ,JBE,JA,JS,JNS,JP,JNP,JL,JNL,JLE,JG) |
| 80–83 |
Table2 |
多种运算与立即数组合,具体取决于 ModR/M 字节 |
| 84 |
TEST |
reg8/mem8, reg8 |
| 85 |
TEST |
reg16/mem16, reg16 |
| 86 |
XCHG |
reg8, reg8 |
| 87 |
XCHG |
reg16, reg16 |
| 88 |
MOV |
reg8/mem8, reg8 |
| 89 |
MOV |
reg16/mem16, reg16 |
| 8A |
MOV |
reg8, reg8/mem8 |
| 8B |
MOV |
reg16, reg16/mem16 |
| 8C |
MOV |
reg16/mem16, segReg |
| 8D |
LEA |
reg16, reg16/mem16 |
| 8E |
MOV |
segReg, reg16/mem16 |
| 8F |
POP |
reg16/mem16 |
| 90 |
NOP |
- |
| 91–97 |
XCHG |
AX 与其他 reg16 |
| 98 |
CBW |
- |
| 99 |
CWD |
- |
| 9A |
CALL |
immed32 |
| 9B |
WAIT |
- |
| 9C |
PUSHF |
- |
| 9D |
POPF |
- |
| 9E |
SAHF |
- |
| 9F |
LAHF |
- |
| A0 |
MOV |
AL, [mem8] |
| A1 |
MOV |
AX, [mem16] |
| A2 |
MOV |
[mem8], AL |
| A3 |
MOV |
[mem16], AX |
| A4 |
MOVSB |
- |
| A5 |
MOVSW |
- |
| A6 |
CMPSB |
- |
| A7 |
CMPSW |
- |
| A8 |
TEST |
AL, [mem8] |
| A9 |
TEST |
AX, [mem16] |
| AA |
STOSB |
- |
| AB |
STOSW |
- |
| AC |
LODSB |
- |
| AD |
LODSW |
- |
| AE |
SCASB |
- |
| AF |
SCASW |
- |
| B0–B7 |
MOV |
reg8, immed8 |
| B8–BF |
MOV |
reg16, immed16 |
| C0–C1 |
Table1 |
各种位移操作,按ModR/M确定 |
| C2 |
RET |
immed16 |
| C3 |
RET |
- |
| C4 |
LES |
reg16/mem16, mem16 |
| C5 |
LDS |
reg16/mem16, mem16 |
| C6 |
MOV |
reg8/mem8, immed8 |
| C7 |
MOV |
reg16/mem16, immed16 |
| C8 |
ENTER |
immed16, immed8 |
| C9 |
LEAVE |
- |
| CA |
RET |
immed16 |
| CB |
RET |
- |
| CC |
INT |
3 |
| CD |
INT |
immed8 |
| CE |
INTO |
- |
| CF |
IRET |
- |
| D0–D3 |
Table1 |
位移相关,具体操作数见 ModR/M |
| D4 |
AAM |
- |
| D5 |
AAD |
- |
| D6 |
Not used |
- |
| D7 |
XLAT |
[BX] |
| D8–DF |
ESC |
immed8 |
| E0–E2 |
LOOP |
条件循环,目标为 immed8 |
| E3 |
JCXZ |
immed8 |
| E4 |
IN |
AL, immed8 |
| E5 |
IN |
AX, immed8 |
| E6 |
OUT |
AL, immed8 |
| E7 |
OUT |
AX, immed8 |
| E8 |
CALL |
immed16 |
| E9 |
JMP |
immed16 |
| EA |
JMP |
immed32 |
| EB |
JMP |
immed8 |
| EC |
IN |
AL, DX |
| ED |
IN |
AX, DX |
| EE |
OUT |
AL, DX |
| EF |
OUT |
AX, DX |
| F0 |
LOCK |
- |
| F1 |
Not used |
- |
| F2 |
REPNE |
- |
| F3 |
REP |
- |
| F4 |
HLT |
- |
| F5 |
CMC |
- |
| F6–F7 |
Table3 |
多种1/2操作,见ModR/M |
| F8 |
CLC |
- |
| F9 |
STC |
- |
| FA |
CLI |
- |
| FB |
STI |
- |
| FC |
CLD |
- |
| FD |
STD |
- |
| FE–FF |
Table4 |
见ModR/M定义的指令 |