指令系统
- 寻址方式
- 数据
- 立即寻址:指令中使用立即数作为操作数。
- 立即数只能在源操作数位置。
- 寄存器寻址:使用寄存器值作为操作数。
- 寄存器可以是通用寄存器或段寄存器,但不可以是
CS。
- 寄存器可以是通用寄存器或段寄存器,但不可以是
- 直接寻址:指令中保存内存地址(有效地址),使用地址指向的单元作为操作数。
- 如果地址直接写出,则用
[<ADDR>]表示。 - 如果是定义的变量,则直接使用变量名
<VAR>。 - 默认在
DS中寻址,可以用<SEG>:<VAR>来制定不同的段(段超越)。 - 同一条指令只能访问一个内存单元。
- 如果地址直接写出,则用
- 寄存器间接寻址:使用寄存器中保存的值作为地址,指向的内存作为操作数。
- 使用
[<REGISTER>]表示。 - 16 位寻址时,只能使用
BX、BP、SI、DI。32 位寻址时,可以用任意通用寄存器。 EBP、ESP默认在SS寻址,其他寄存器在DS寻址。
- 使用
- 寄存器相对寻址:使用寄存器中保存的值与常数偏移量相加作为地址,指向的内存作为操作数。
- 使用
[<DISP-CONSTANT> + <REGISTER>]或<DISP-CONSTANT>[<REGISTER>]表示。 - 寄存器使用规则同上。
- 一般用于访问一维数组,偏移量是数组起始地址,寄存器对应下标。
- 使用
- 基址变址寻址:使用基址寄存器和变址寄存器的值相加作为地址,指向的内存作为操作数。
- 使用
[<BASE-REGISTER> + <INDEX-REGISTER>]或[<BASE-REGISTER>][<INDEX-REGISTER>]表示。 - 16 位寻址时,基址寄存器只能使用
BX、BP,变址寄存器只能用SI、DI。 - 32 位寻址时,基址寄存器可以用任意通用寄存器,变址寄存器可以用除
ESP以外的任意通用寄存器。 - 段配合规则同上,根据基址寄存器确定。
- 使用
- 相对基址变址寻址:使用基址寄存器、变址寄存器、常数偏移量相加的值相加作为地址,指向的内存作为操作数。
- 使用
[<DISP-CONSTANT> + <BASE-REGISTER> + <INDEX-REGISTER>]或<DISP-CONSTANT>[<BASE-REGISTER>][<INDEX-REGISTER>]表示。 - 寄存器使用规则同上。
- 一般用于访问二维数组。
- 使用
- 比例变址寻址:使用变址寄存器的值乘以比例因子与基址寄存器的值相加作为地址,指向的内存作为操作数。
- 使用
[<DISP-CONSTANT> + <BASE-REGISTER> + <INDEX-REGISTER> * <SCALE>]或<DISP-CONSTANT>[<BASE-REGISTER>][<INDEX-REGISTER> * <SCALE>]表示。 - 寄存器使用规则同上。
- 比例因子
<SCALE>可以是1、2、4、8。
- 使用
- 立即寻址:指令中使用立即数作为操作数。
- 指令
- 段内直接寻址:跳转指令中带有偏移量,当前
IP与偏移量相加得到目标地址。 - 段内间接寻址:跳转指令使用目标寄存器或内存单元中的值作为目标地址。
- 段间直接寻址:
- 16 位:指令的地址部分第 0~1 字节表示偏移量,第 2~3 字节表示段基址。
- 32 位:指令的地址部分第 0~3 字节表示偏移量,第 4~5 字节表示段选择符。
- 段间间接寻址:
- 16 位:一个 32 位变量表示目标,低 16 位表示偏移量,高 16 位表示段基址。
- 32 位:一个 48 位变量表示目标,低 32 位表示偏移量,高 16 位表示段选择符。
- 段内直接寻址:跳转指令中带有偏移量,当前
- 数据
- 指令
- 数据传送
MOV <DST>, <SRC>- 立即数不能是目的操作数。
- 立即数不能直接赋值给段寄存器。
- 两个段寄存器之间不能直接赋值。
CS不能赋值。- 两个内存单元之间不能直接赋值。
- 立即数赋值给内存单元时,可能无法判断长度,需要在地址使用
<SIZE> PTR来确定。- 如果内存单元不是通过定义的变量给出,而是通过地址给出,则无法确定长度。
<SIZE>包括BYTE、WORD、DWORD、QWORD。
- 使用立即数
10的十六进制时,要写作0AH,防止与寄存器冲突。
MOVSX <DST>, <SRC>- 带符号扩展的
MOV。
- 带符号扩展的
MOVZX <DST>, <SRC>- 带零扩展的
MOV。
- 带零扩展的
PUSH <SRC>- 把
<SRC>压栈。 - 16 位时,
<SRC>不能是立即数。32 位无限制。 SP的变化量为当前字长。
- 把
POP <DST>- 把栈顶数据弹出到
<DST>。 <DST>可以是通用寄存器、内存单元、除了CS以外的段寄存器。SP的变化量为当前字长。
- 把栈顶数据弹出到
PUSHF/PUSHFD- 把标志寄存器的值压栈。
PUSHF用于 16 位,PUSHFD用于 32 位。
POPF/POPFD- 把栈顶数据弹出到标志寄存器。
POPF用于 16 位,POPFD用于 32 位。
XCHG <SRC1>, <SRC2>- 交换
<SRC1>和<SRC2>的值。 - 两个操作数都不能是立即数,必须有一个是寄存器。
- 交换
IN <ACR>, <PORT>- 从端口
<PORT>读取数据到累加器<ACR>。 - 如果端口号小于 256,可以使用立即数,否则必须使用
DX。 - 累加器
<ACR>只有AL、AX、EAX、RAX,AH不是累加器。
- 从端口
OUT <PORT>, <ACR>- 从累加器
<ACR>输出数据到端口<PORT>。 - 注意操作数顺序,与
IN相反,两者都是目的在前。 - 端口地址、寄存器的规则同
IN。
- 从累加器
LEA <DST>, <SRC>- 计算
<SRC>的有效地址,并存入<DST>。 <SRC>必须是指向内存单元,因为寄存器不使用内存地址。
- 计算
- 数据转换
CBW- 把
AL中的数据视为有符号数,扩展到AX。
- 把
CWD- 把
AX中的数据视为有符号数,扩展到DX:AX。
- 把
CWDE- 把
AX中的数据视为有符号数,扩展到EAX。
- 把
CDQ- 把
EAX中的数据视为有符号数,扩展到EDX:EAX。
- 把
- 算术运算
ADD <DST>, <SRC>- 把
<SRC>加到<DST>上,结果存入<DST>。 - 同时适用于无符号和有符号整数。
- 把
ADC <DST>, <SRC>- 与
ADD类似,但是会加上CF。
- 与
INC <DST>- 把
<DST>增加1。 - 不影响
CF,其他标志寄存器都会影响。
- 把
XADD <DST>, <SRC>- 类似
ADD,但是同时把原<DST>的值存入<SRC>。
- 类似
SUB <DST>, <SRC>- 把
<SRC>从<DST>中减去,结果存入<DST>。 - 同时适用于无符号和有符号整数。
- 把
SBB <DST>, <SRC>- 与
SUB类似,但是会减去CF。
- 与
DEC <DST>- 把
<DST>减少1。 - 不影响
CF,其他标志寄存器都会影响。
- 把
CMP <DST>, <SRC>- 不修改
<DST>,但是按照SUB的方式设置标志寄存器。
- 不修改
NEG <DST>- 把
<DST>按照补码取相反数。 - 行为与
0减<DST>一样,CF == 0当且仅当<DST> == 0。
- 把
MUL <SRC>- 把
AL、AX或EAX和<SRC>中的数视为无符号数进行相乘。 - 8 位时,结果放在
AX,16 位时,结果放在DX:AX,32 位时,结果放在EDX:EAX。 - 源操作数
<SRC>只能是寄存器或内存单元,不能是立即数。 - 溢出结果以是否超出
AX为准,即如果DX为全 0,则CF、OF为 0,否则为 1。
- 把
IMUL <SRC>- 类似
MUL,但是以有符号数运算。
- 类似
IMUL <DST-REGISTER>, <SRC>- 类似
IMUL <SRC>,但是不再默认使用AX,而是指定目标寄存器。 - 目标操作数只能是通用寄存器。
- 类似
IMUL <DST-REGISTER>, <IMM8>- 类似
IMUL <DST-REGISTER>, <SRC>,但是源原操作数使用 8 位立即数。 - 立即数只能 8 位,根据目标寄存器,自动符号扩展。
- 类似
IMUL <DST-REGISTER>, <SRC>, <IMM8>- 类似
IMUL <DST-REGISTER>, <IMM8>,但是额外指定源操作数,目标只存结果。
- 类似
DIV <SRC>- 按无符号数计算,把目的寄存器作为被除数,把
<SRC>作为除数,结果写回目的寄存器。 - 8 位时,结果放在
AX,16 位时,结果放在DX:AX,32 位时,结果放在EDX:EAX。 - 结果中,低位存放商,高位存放余数。
- 源操作数
<SRC>只能是寄存器或内存单元,不能是立即数。
- 按无符号数计算,把目的寄存器作为被除数,把
IDIV <SRC>- 与
DIV类似,但是按有符号数计算。
- 与
- 逻辑运算
NOT <DST>- 按位取反
<DST>。 - 不改变标志位。
- 按位取反
AND <DST>, <SRC>- 按位与
<DST>和<SRC>,结果写入<DST>。 - 设置
CF、OF为0,正常设置SF、ZF、PF,其他标志不定。
- 按位与
OR <DST>, <SRC>- 按位或
<DST>和<SRC>,结果写入<DST>。 - 设置
CF、OF为0,正常设置SF、ZF、PF,其他标志不定。
- 按位或
XOR <DST>, <SRC>- 按位异或
<DST>和<SRC>,结果写入<DST>。 - 设置
CF、OF为0,正常设置SF、ZF、PF,其他标志不定。
- 按位异或
BT <DST>, <SRC>- 用
<DST>的从低到高第<SRC>位(从0计数)设置CF,其他标志不定。 - 如果源操作数不是立即数,两个操作数要长度相等。
- 用
BTS <DST>, <SRC>- 类似
BT,但是在设置CF后,再把那个位设为1。
- 类似
BTR <DST>, <SRC>- 类似
BT,但是在设置CF后,再把那个位设为0。
- 类似
BTC <DST>, <SRC>- 类似
BT,但是在设置CF后,再把那个位取反。
- 类似
SHL <DST>, <CNT>- 把
<DST>逻辑左移<CNT>位。 - 如果
<CNT>为1,则用立即数1,否则可以用CL或立即数存。 CF设置为最后移出的一位,SF、ZF、PF正常设置。<CNT>为1时,如果符号位变化,OF设为1,否则设为0。<CNT>大于1时不确定。
- 把
SAL <DST>, <CNT>- 把
<DST>算术左移<CNT>位,实际上与逻辑左移相同。 CNT、标志寄存器规则与SHL相同。
- 把
SHR <DST>, <CNT>- 把
<DST>逻辑右移<CNT>位。 CNT、标志寄存器规则与SHL相同。
- 把
SAR <DST>, <CNT>- 把
<DST>算术右移<CNT>位。 CNT、标志寄存器规则与SHL相同。
- 把
ROL <DST>, <CNT>- 把
<DST>循环左移<CNT>位。 - 如果
<CNT>为1,则用立即数1,否则可以用CL或立即数存。 CF设置为最后移入的一位,SF、ZF、PF正常设置。<CNT>为1时,如果符号位变化,OF设为1,否则设为0。<CNT>大于1时不确定。
- 把
ROR <DST>, <CNT>- 把
<DST>循环右移<CNT>位。 CNT、标志寄存器规则与ROL相同。
- 把
RCL <DST>, <CNT>- 把
<DST>循环左移<CNT>位,额外使用CF作为最高位。 CNT、标志寄存器规则与ROL相同。
- 把
ROR <DST>, <CNT>- 把
<DST>循环右移<CNT>位,额外使用CF作为最低位。 CNT、标志寄存器规则与ROL相同。
- 把
- 跳转
JMP SHORT <LABEL>- 段内直接短转移,跳转到
<LABEL>所在代码,要求<LABEL>相对于此的偏移量小于 128。
- 段内直接短转移,跳转到
JMP <LABEL>- 段内直接转移,跳转到
<LABEL>所在代码。
- 段内直接转移,跳转到
JMP <SRC>- 段内间接转移,跳转到
<SRC>中存储的地址。
- 段内间接转移,跳转到
JZ/JNZ/JE/JNE/JC/JNC/JO/JNO/JP/JNP/JPE/JPO <LABEL>- 根据标志位条件跳转。
JA/JNA/JB/JNB/JAE/JNAE/JBE/JNBE/JL/JNL/JG/JNG/JLE/JNLE/JGE/JNGE <LABEL>- 根据标志位条件跳转,比较大小。
JCX/JECX <LABEL>- 当
CX或ECX为0时,跳转到<LABEL>。 - 只能进行短转移。
- 当
- 循环
LOOP <LABEL>- 递减
CX或ECX,如果不为0,则跳转到<LABEL>。 - 实现固定次数的循环,
LOOP一般放在循环体最后,在循环体前放标号。 - 需要提前排除
ECX是否是0,否则会发生溢出。
- 递减
LOOPZ/LOOPE <LABEL>- 与
LOOP相似,但跳转条件额外检查ZF == 1。
- 与
LOOPNZ/LOOPNE <LABEL>- 与
LOOP相似,但跳转条件额外检查ZF == 0。
- 与
- 调用
CALL <DST>- 与
JMP的各种形式类似,在栈上压入返回地址,分段内或段间、直接或间接。
- 与
RET- 弹栈取出返回地址,跳转回原来的调用位置。
RET <IMM16>- 与
RET类似,在跳转回去后,额外使SP增加<IMM16>。 - 无论是 16 位还是 32 位执行,都是 16 位立即数。
- 用于清理调用时在栈上传递的参数,若有
n个参数,则<IMM16> = n * <WORD-SIZE>。
- 与
INT <NUM>- 触发中断
<NUM>,执行中断向量表中第<NUM>个中断的处理程序。 - 执行指令时,把当前的标志寄存器、
CS、IP压入栈,再执行中断处理程序。
- 触发中断
IRET/IRETD- 从栈上取出标志寄存器、
CS、IP的保存值并恢复,从中断处理程序返回。
- 从栈上取出标志寄存器、
- 块操作
- 块操作主要用于操作连续内存,完成数据传送、比较等操作的同时,自动操作
SI、DI寄存器。 - 块操作包括一个指令主体,和一个可选的重复前缀,重复前缀与
LOOP类似。 - 指令主体:
- 如果操作数是内存,所有源操作数使用数据段,所有目的操作数使用扩展段。
- 执行指令后,自动增加或减少
SI、DI(只有指令需要使用才修改),按照DF确定方向,按指令后缀区分长度。 MOVSB/MOVSW/MOVSD:移动DS:[ESI]到ES:[EDI],行为类似MOV。CMPSB/CMPSW/CMPSD:比较DS:[ESI]和ES:[EDI],行为类似CMP。SCASB/SCASW/SCASD:比较累加器和ES:[EDI],行为类似CMP。STOSB/STOSW/STOSD:把累加器的值写入ES:[EDI],行为类似MOV。MOVSB/MOVSW/MOVSD:把DS:[ESI]读入累加器,行为类似MOV。
- 重复前缀:
REP:类似LOOP。REPZ:类似LOOPZ。REPNZ:类似LOOPNZ。
- 块操作主要用于操作连续内存,完成数据传送、比较等操作的同时,自动操作
- 数据传送
- 浮点数操作
- 寄存器
- 浮点数使用专用的寄存器,80 位,编号为
FPR0~FPR7。 - 浮点寄存器以栈的形式操作,引用使用
ST(i),其中i表示从栈顶开始从0计数的下标,ST(0)为栈顶。
- 浮点数使用专用的寄存器,80 位,编号为
- 指令
- 杂项
FINIT:初始化 FPU。使用 FPU 前都要调用。FLDCW <SRC>:从<SRC>加载 FPU 控制字。FSTCW <DST>:保存 FPU 控制字到<DST>。FCLEX:清楚浮点异常。FNOP:空操作。
- 传送
FLD <SRC>:把<SRC>加载到ST(0)。FST <DST>:把ST(0)存储到<DST>,ST(0)不出栈。FSTP <DST>:把ST(0)存储到<DST>,ST(0)随后出栈。
- 常数加载
FLD1:加载1.0到ST(0)。FLDZ:加载0.0到ST(0)。FLDPI:加载 到ST(0)。FLDL2T:加载 到ST(0)。FLDL2E:加载 到ST(0)。FLDLG2:加载 到ST(0)。FLDLN2:加载 到ST(0)。
- 运算
FADD [[<DST>, ]<SRC>]:相加。- 如果完全省略参数,则
ST(0)加到ST(1),随后ST(0)出栈。 - 如果只省略
<DST>保留<SRC>,则<SRC>加到ST(0),不出栈。 - 不省略时,则
<SRC>加到<DST>,不出栈。
- 如果完全省略参数,则
FADDP ST(i), ST(0):ST(0)加到ST(i),随后ST(0)出栈。FSUB/FSUBP/FMUL/FMULP/FDIV/FDIVP:类似FADD/FADDP。FPREM:计算ST(0)对ST(1)取模,保存在ST(0)。FABS:计算ST(0)的绝对值,保存在ST(0)。FCHS:计算ST(0)的相反数,保存在ST(0)。FSQRT:计算ST(0)的算术平方根,保存在ST(0)。FSCALE:计算 2 的ST(0)次幂,保存在ST(0)。FSIN:计算ST(0)的正弦,保存在ST(0)。FCOS:计算ST(0)的余弦,保存在ST(0)。FPTAN:计算ST(0)的正切,保存在ST(0)。FPATAN:计算ST(0)的反正切,保存在ST(0)。F2XM1:计算 2 的ST(0)次幂减 1,保存在ST(0)。
- 比较
FCOM [<SRC>]:比较ST(0)与<SRC>的大小,如果省略<SRC>,则用ST(1)。FCOMP [<SRC>]:类似FCOM,完成比较后ST(0)出栈。
- 杂项
- 寄存器