指令系统

  • 寻址方式
    • 数据
      • 立即寻址:指令中使用立即数作为操作数。
        • 立即数只能在源操作数位置。
      • 寄存器寻址:使用寄存器值作为操作数。
        • 寄存器可以是通用寄存器或段寄存器,但不可以是 CS
      • 直接寻址:指令中保存内存地址(有效地址),使用地址指向的单元作为操作数。
        • 如果地址直接写出,则用 [<ADDR>] 表示。
        • 如果是定义的变量,则直接使用变量名 <VAR>
        • 默认在 DS 中寻址,可以用 <SEG>:<VAR> 来制定不同的段(段超越)。
        • 同一条指令只能访问一个内存单元。
      • 寄存器间接寻址:使用寄存器中保存的值作为地址,指向的内存作为操作数。
        • 使用 [<REGISTER>] 表示。
        • 16 位寻址时,只能使用 BXBPSIDI。32 位寻址时,可以用任意通用寄存器。
        • EBPESP 默认在 SS 寻址,其他寄存器在 DS 寻址。
      • 寄存器相对寻址:使用寄存器中保存的值与常数偏移量相加作为地址,指向的内存作为操作数。
        • 使用 [<DISP-CONSTANT> + <REGISTER>]<DISP-CONSTANT>[<REGISTER>] 表示。
        • 寄存器使用规则同上。
        • 一般用于访问一维数组,偏移量是数组起始地址,寄存器对应下标。
      • 基址变址寻址:使用基址寄存器和变址寄存器的值相加作为地址,指向的内存作为操作数。
        • 使用 [<BASE-REGISTER> + <INDEX-REGISTER>][<BASE-REGISTER>][<INDEX-REGISTER>] 表示。
        • 16 位寻址时,基址寄存器只能使用 BXBP,变址寄存器只能用 SIDI
        • 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> 可以是 1248
    • 指令
      • 段内直接寻址:跳转指令中带有偏移量,当前 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> 包括 BYTEWORDDWORDQWORD
        • 使用立即数 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> 只有 ALAXEAXRAXAH 不是累加器。
      • 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>
        • ALAXEAX<SRC> 中的数视为无符号数进行相乘。
        • 8 位时,结果放在 AX,16 位时,结果放在 DX:AX,32 位时,结果放在 EDX:EAX
        • 源操作数 <SRC> 只能是寄存器或内存单元,不能是立即数。
        • 溢出结果以是否超出 AX 为准,即如果 DX 为全 0,则 CFOF 为 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>
        • 设置 CFOF0,正常设置 SFZFPF,其他标志不定。
      • OR <DST>, <SRC>
        • 按位或 <DST><SRC>,结果写入 <DST>
        • 设置 CFOF0,正常设置 SFZFPF,其他标志不定。
      • XOR <DST>, <SRC>
        • 按位异或 <DST><SRC>,结果写入 <DST>
        • 设置 CFOF0,正常设置 SFZFPF,其他标志不定。
      • 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 设置为最后移出的一位,SFZFPF 正常设置。
        • <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 设置为最后移入的一位,SFZFPF 正常设置。
        • <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>
        • CXECX0 时,跳转到 <LABEL>
        • 只能进行短转移。
    • 循环
      • LOOP <LABEL>
        • 递减 CXECX,如果不为 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> 个中断的处理程序。
        • 执行指令时,把当前的标志寄存器、CSIP 压入栈,再执行中断处理程序。
      • IRET/IRETD
        • 从栈上取出标志寄存器、CSIP 的保存值并恢复,从中断处理程序返回。
    • 块操作
      • 块操作主要用于操作连续内存,完成数据传送、比较等操作的同时,自动操作 SIDI 寄存器。
      • 块操作包括一个指令主体,和一个可选的重复前缀,重复前缀与 LOOP 类似。
      • 指令主体:
        • 如果操作数是内存,所有源操作数使用数据段,所有目的操作数使用扩展段。
        • 执行指令后,自动增加或减少 SIDI(只有指令需要使用才修改),按照 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) 为栈顶。
    • 指令
      • 杂项
        • 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.0ST(0)
        • FLDZ:加载 0.0ST(0)
        • FLDPI:加载 π\piST(0)
        • FLDL2T:加载 log210\log_2 10ST(0)
        • FLDL2E:加载 log2e\log_2 eST(0)
        • FLDLG2:加载 lg2\lg 2ST(0)
        • FLDLN2:加载 ln2\ln 2ST(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) 出栈。