掌握常用汇编语言指令时学好汇编语言的关键,一下是按照汇编指令进行分类后的6大类常用汇编指令和对于的英文全称,功能和使用示例汇总。包括数据传送指令, 算术运算指令, 逻辑运算指令, 控制转移指令,串操作指令和处理器控制指令等。

一、数据传送指令

1. MOV(Move)传送

  • 功能:这是最基本的数据传送指令,用于将源操作数(可以是立即数、寄存器或者内存单元)传送到目标操作数(寄存器或者内存单元)。它可以实现寄存器与寄存器之间、寄存器与内存之间、立即数与寄存器或内存之间的数据传送。
  • 示例
    • MOV AX, BX:将寄存器 BX 中的内容传送到寄存器 AX 中。
    • MOV AL, [SI]:将由 SI 寄存器指向的内存单元中的字节数据传送到 AL 寄存器。
    • MOV [DI], 10H:将十六进制数 10H 传送到由 DI 寄存器指向的内存单元中。

2. LEA(Load Effective Address)加载有效地址

  • 功能:用于将内存操作数的有效地址加载到指定的寄存器中。与 MOV 指令不同,它获取的是操作数的地址而不是操作数的值。
  • 示例
    • LEA SI, [BX + DI]:将 BX 和 DI 相加后的有效地址加载到 SI 寄存器中。这个指令常用于获取数组元素的偏移地址,例如在访问数组ARRAY[BX + DI]时,可以先用LEA指令获取其有效地址并存入 SI,然后通过 SI 来访问该数组元素。

3. PUSH(Push onto the stack) 入栈

  • 功能:将操作数(可以是寄存器或者内存单元)压入堆栈。在执行压栈操作时,堆栈指针(SP)会根据操作数的大小自动调整,对于 16 位操作数,SP 减 2;对于 32 位操作数,SP 减 4,以此类推。
  • 示例
    • PUSH AX:将寄存器 AX 中的值压入堆栈。

4. POP - 英文全称:Pop from the stack。 出栈

  • 功能:从堆栈中弹出一个字(在 16 位系统中)或双字(在 32 位系统中)到指定的操作数(寄存器或者内存单元),同时堆栈指针(SP)会相应地增加。
  • 示例
    • POP BX:从堆栈中弹出一个值到寄存器 BX 中。

5. XCHG(Exchange)交换

  • 功能:用于交换两个操作数(可以是寄存器与寄存器之间、寄存器与内存之间)的内容。
  • 示例
    • XCHG AX, BX:交换寄存器 AX 和 BX 中的内容。
    • XCHG AL, [SI]:交换 AL 寄存器中的字节内容和由 SI 指向的内存单元中的字节内容。

6. XLAT(Translate)转换

  • 功能:用于将 AL 寄存器中的值作为索引,在由 BX 指向的字节表中查找对应的字节数据,并将找到的数据传送到 AL 寄存器中。这个指令通常用于简单的代码转换或者查表操作
  • 示例
    • 假设BX指向一个 ASCII 码转换表(例如将数字 0 - 9 的 ASCII 码转换为对应的十六进制数的表),AL中存放需要转换的数字的 ASCII 码(如30H表示数字 0),执行XLAT指令后,AL中将存放转换后的十六进制数(如00H)。

7. LDS(Load Far Pointer into DS) 将指针加载到数据段寄存器

  • 功能:把由操作数指定的内存单元开始的 4 个字节(在 16 位实模式下)的内容加载到一个通用寄存器(如 SI 等)和数据段寄存器(DS)中。其中,前两个字节(偏移量)装入通用寄存器,后两个字节(段地址)装入 DS 寄存器。这用于处理远指针(包含段地址和偏移地址)相关的操作,方便访问内存中不同段的数据。
  • 示例
    • 假设内存地址1000H处存放着一个远指针。LDS SI, [1000H]指令会将1000H1001H单元中的偏移量加载到SI寄存器,将1002H1003H单元中的段地址加载到 DS 寄存器。之后,就可以通过DS:SI的方式来访问由这个远指针指向的数据。

8. LES(Load Pointer into ES)将指针加载到附加段寄存器

  • 功能:和 LDS 类似,不过是将内存单元开始的 4 个字节(在 16 位实模式下)的内容加载到一个通用寄存器(如 DI 等)和附加段寄存器(ES)中。前两个字节装入通用寄存器作为偏移量,后两个字节装入 ES 寄存器作为段地址,主要用于使用附加段来操作内存数据。
  • 示例

9. PUSHF(Push the Flags)将标志寄存器内容压入堆栈

  • 功能:将标志寄存器(FLAGS)的内容压入栈顶。在程序调用子程序、进入中断服务程序或者其他需要保存当前标志状态的情况下使用,以确保在返回时能够恢复原始的标志位状态,保证程序的正确执行。
  • 示例
    • 在调用一个子程序之前,为了保存当前的标志位状态,使用PUSHF指令(如PUSHF)。在 16 位模式下,执行此指令后,栈指针会自动减 2,然后将标志寄存器的内容存入栈顶。这样,在子程序返回后,可以使用POPF指令恢复标志位。

10. POPF(Pop the Flags)从堆栈弹出内容到标志寄存器

  • 功能:从栈顶弹出数据并传送到标志寄存器(FLAGS),用于恢复之前保存的标志位状态,通常与PUSHF成对使用,以保证程序在经过一些可能改变标志位的操作后能够正确地恢复之前的状态。
  • 示例
    • 假设之前使用PUSHF保存了标志位状态,在从子程序返回或者中断服务程序返回时,使用POPF指令(如POPF)来恢复标志位。在 16 位模式下,它会将栈顶的数据(之前保存的标志寄存器内容)传送到标志寄存器,然后栈指针自动加 2。

11. PUSHFD(Push the EFLAGS)和 POPFD(Pop the EFLAGS) 将扩展标志寄存器内容压入堆栈和从堆栈弹出内容到扩展标志寄存器

  • 功能PUSHFD用于将 32 位的扩展标志寄存器(EFLAGS)的内容压入栈顶,POPFD则是从栈顶弹出数据并传送到 EFLAGS 寄存器,恢复之前保存的标志位状态。主要用于 32 位及以上的保护模式程序中,和PUSHFPOPF类似,但是针对 32 位的扩展标志寄存器操作。
  • 示例
    • 在 32 位保护模式程序中,进入一个关键代码段之前,使用PUSHFD保存 EFLAGS 寄存器的状态(如PUSHFD)。当从这个代码段退出时,使用POPFD来恢复标志位状态(如POPFD)。这可以确保在代码段执行过程中,即使标志位被修改,也能在退出时恢复到之前的状态。

后面的2个LAHF, SAHF 这2条指令在80x86中基本不使用,仅为了与8080/8085兼容

12. LAHF(Load AH from Flags)将标志寄存器低字节加载到 AH 寄存器

  • 功能:把标志寄存器(FLAGS)的低 8 位(包括符号标志 SF、零标志 ZF、辅助进位标志 AF、奇偶标志 PF 和进位标志 CF)传送到 AH 寄存器。这样就可以方便地对标志位进行保存或者后续处理,例如在程序的不同部分检查之前运算后的标志状态。
  • 示例:
    • 执行完一些算术运算(如加法或减法)后,想要保存当前标志位的状态。使用LAHF指令,如LAHF,执行后,标志寄存器中的 SF、ZF、AF、PF 和 CF 位的值就会被复制到AH寄存器的相应位上。例如,如果运算结果使得零标志位ZF = 1(表示运算结果为 0),那么在AH寄存器中对应的位也会被设置为 1。

13.SAHF(Store AH into Flags)将 AH 寄存器内容存储到标志寄存器低字节

  • 功能:与 LAHF 相反,把 AH 寄存器的内容传送到标志寄存器(FLAGS)的低 8 位,从而恢复之前保存的标志位状态或者设置新的标志位状态,用于控制程序流程或者影响后续运算的标志判断。
  • 示例:
    • 假设之前通过LAHF保存了标志位状态到AH寄存器。之后在适当的时候,使用SAHF指令(如SAHF)可以将AH寄存器中的内容复制回标志寄存器的低 8 位,恢复之前的标志状态或者根据AH寄存器中的内容设置新的标志状态。

二、算术运算指令

1. 加法指令

  • ADD(Addition)加法运算
    • 功能:用于执行两个操作数的加法运算. 操作数可以是寄存器、内存单元或立即数。加法运算的结果将存储在目标操作数中。
    • 示例
      • ADD AX, BX:将寄存器 AX 和 BX 中的值相加,结果存放在 AX 中。
      • ADD AL, 10H:将 AL 寄存器中的值与十六进制数 10H 相加,结果存放在 AL 中。
      • ADD [SI], CX:将由 SI 寄存器指向的内存单元中的值与 CX 寄存器中的值相加,结果存回该内存单元。
  • ADC(Add with Carry)带进位加法指令
    • 功能:在执行加法运算时,将进位标志位 CF(Carry Flag)的值一起加入运算。通常用于多字节或多字的加法运算,用于处理高位字节或高位字相加时的进位情况。
    • 示例
      • 假设要进行两个 32 位无符号数相加,低 16 位分别存于 AX 和 BX,高 16 位分别存于 DX 和 CX。首先进行低 16 位相加:ADD AX, BX,然后进行高 16 位相加,考虑低 16 位相加可能产生的进位:ADC DX, CX
  • INC(Increment)
    • 功能:将操作数的值加 1。操作数可以是寄存器或内存单元。它不影响进位标志 CF(Carry Flag),主要用于循环计数等简单的递增操作。
    • 示例
    • INC CX:将 CX 寄存器中的值加 1。
    • INC BYTE PTR [SI]:将由 SI 寄存器指向的内存单元中的字节值加 1。

2. 减法指令

  • SUB(Subtraction)执行两个操作数的减法运算
    • 功能:从目标操作数中减去源操作数,结果存储在目标操作数中。
    • 示例
      • SUB CX, DX:将 CX 寄存器中的值减去 DX 寄存器中的值,差存放在 CX 中。
      • SUB [DI], 05H:将由 DI 寄存器指向的内存单元中的值减去 5,结果存回该内存单元。
  • SBB(Subtract with Borrow)带借位减法指令
    • 功能:在减法运算时,考虑借位标志位 CF(Carry Flag)的值。主要用于多字节或多字的减法运算,用于处理高位字节或高位字相减时的借位情况。
    • 示例
      • 类似 ADC 指令,在进行多字节减法运算时,先进行低字节减法,再进行高字节减法并考虑借位。例如,对于两个 32 位无符号数相减,低 16 位分别存于 AX 和 BX,高 16 位分别存于 DX 和 CX。先进行低 16 位相减:SUB AX, BX,然后进行高 16 位相减,考虑低 16 位相减可能产生的借位:SBB DX, CX
  • DEC(Decrement)
    • 功能:将操作数的值减 1。操作数可以是寄存器或内存单元。同样不影响进位标志 CF,常用于循环计数等递减操作。
    • 示例
    • DEC DX; 将 DX 寄存器中的值减 1。
      DEC WORD PTR [DI]; 将由 DI 寄存器指向的内存单元中的字值减 1。
  • NEG(Negate)求补指令
    • 功能:求操作数的相反数。即对操作数进行按位取反后再加 1 的操作。操作数可以是寄存器或内存单元。它会影响标志位,如 CF(Carry Flag)、SF(Sign Flag)、ZF(Zero Flag)等。
    • 示例
    • NEG AX ; 求 AX 寄存器中值的相反数。如果 AX = 0005H,执行 NEG 后,AX = FFFBH( - 5 的补码)。
    • NEG BYTE PTR [BX]; 求由 BX 寄存器指向的内存单元中的字节值的相反数
  • CMP(Compare) 用于比较两个操作数的大小

    • 功能

      • CMP 指令主要用于比较两个操作数。它执行减法操作,但不保存结果,只是根据减法的结果来设置标志寄存器(FLAGS)中的相应标志位。这些标志位包括零标志位(ZF)、符号标志位(SF)、进位标志位(CF)和溢出标志位(OV)等,通过检查这些标志位可以确定两个操作数的大小关系。
      • 具体来说:
        • 相等判断:如果两个操作数相等,那么减法的结果为 0,此时零标志位 ZF 会被置 1。
        • 大小判断
          • 当无符号数比较时,若被减数小于减数,会产生借位,进位标志位 CF 被置 1;若被减数大于等于减数,CF 为 0。
          • 当有符号数比较时,根据结果的正负来设置符号标志位 SF。如果结果为负,SF = 1;如果结果为正,SF = 0。同时,还要考虑溢出标志位 OV,当发生溢出时,OV 会被置 1,此时 SF 的值不能正确反映大小关系。
    • 示例

      • 无符号数比较示例
        • 假设我们要比较两个无符号数,在 8086 汇编语言中,有以下代码:
          MOV AX, 5   ; 将5存入AX寄存器
          MOV BX, 3   ; 将3存入BX寄存器
          CMP AX, BX  ; 比较AX和BX的值

          在这个例子中,执行 CMP 指令时,实际上执行了AX - BX的减法操作(但不保存结果)。在这里,AX - BX = 5 - 3 = 2,由于没有借位,进位标志位 CF = 0。这表明 AX 中的无符号数大于 BX 中的无符号数。

        • 有符号数比较示例  考虑比较两个有符号数,代码如下:

          MOV AL, -2  ; 将 - 2存入AL寄存器
          MOV BL, 1   ; 将1存入BL寄存器
          CMP AL, BL  ; 比较AL和BL的值

          执行 CMP 指令时,相当于执行AL - BL,即-2 - 1 = -3。结果为负,所以符号标志位 SF = 1。这表明 AL 中的有符号数小于 BL 中的有符号数。在这个简单的例子中没有溢出情况,但在更复杂的有符号数比较中,需要同时考虑溢出标志位 OV 来准确判断大小关系。

        • 结合条件跳转指令使用示例   在汇编程序中,CMP 指令经常与条件跳转指令一起使用,以实现根据比较结果进行不同的程序分支。例如:

          MOV CX, 10
          MOV DX, 10
          CMP CX, DX
          JE EQUAL   ; 如果ZF = 1(即CX和DX相等),跳转到EQUAL标签处的代码
          MOV AX, 1  ; 如果不相等,执行这一行,将1存入AX
          JMP END_PROG
          EQUAL:
          MOV AX, 0  ; 如果相等,将0存入AX
          END_PROG:

          在这个例子中,首先比较 CX 和 DX 的值。如果它们相等,零标志位 ZF 被置 1,程序会跳转到EQUAL标签处,将 0 存入 AX;如果不相等,程序会继续执行下一条指令,将 1 存入 AX,然后跳转到END_PROG结束程序。

3. 乘法指令

  • MUL(Multiply)
    • 功能:用于无符号数的乘法运算。如果是字节乘法,操作数默认是 AL,结果存放在 AX 中;如果是字乘法,操作数默认是 AX,结果的高字存放在 DX 中,低字存放在 AX 中。
    • 示例
      • 字节乘法:MOV AL, 05HMOV BL, 03HMUL BL,结果 AX = 000FH(,十六进制为 0F,存放在 AX 中)。
      • 字乘法:MOV AX, 1000HMOV BX, 0200HMUL BX,结果 AX = 20000H,DX = 0002H(,高字 0002H 存放在 DX 中,低字 20000H 存放在 AX 中)。
  • IMUL(Integer Multiply)
    • 功能:用于有符号数的乘法运算。运算规则和 MUL 类似,但会考虑操作数的符号位,结果也是有符号数。
    • 示例
      • 假设 AL = - 2(十六进制为 FEH),BL = - 3(十六进制为 FDH),IMUL BL,结果 AX = 0006H(因为 - 2× - 3 = 6)。

4. 除法指令

  • DIV(Divide)
    • 功能:用于无符号数的除法运算。如果是字节除法,被除数默认是 AX,除数是源操作数,商存放在 AL 中,余数存放在 AH 中;如果是字除法,被除数是 DX 和 AX 组成的双字,除数是源操作数,商存放在 AX 中,余数存放在 DX 中。 注意除法不产生有效的标志位转态(除法会影响标志位,但是在某些情况下是0还是1不确定),既在除法指令执行后标志位转态就不再有意义了。
    • 示例
      • 字节除法:MOV AX, 0010H(十进制为 16),MOV BL, 03HDIV BL,结果 AL = 05H(商为 5),AH = 01H(余数为 1)。
      • 字除法:MOV DX, 0000HMOV AX, 0100H(此时 DX:AX 表示十进制为 256),MOV CX, 0010H(十进制为 16),DIV CX,结果 AX = 0010H(商为 16),DX = 0000H(余数为 0)。
  • IDIV(Integer Divide)
    • 功能:用于有符号数的除法运算。运算规则和 DIV 类似,但会考虑被除数和除数的符号位,商和余数也是有符号数。
    • 示例
      • 假设 AX = - 10(十六进制为 FFF6H),BL = 2,IDIV BL,结果 AL = - 5(商为 - 5),AH = 00H(余数为 0)。

5. 其他指令

  • CBW  (Convert Byte to Word)字节转换为字
    •  功能:用于将字节数据转换为字数据。CBW 指令用于将寄存器 AL 中的有符号字节数据扩展为 AX 中的有符号字数据。具体规则是:
      • 如果 AL 中的最高位(符号位)为 0,表示正数,那么扩展后的 AX 的高字节(AH)被填充为 0,AL 中的内容不变。
      • 如果 AL 中的最高位为 1,表示负数,那么扩展后的 AX 的高字节(AH)被填充为全 1(即 FFH),AL 中的内容不变。这种扩展方式称为符号扩展,确保了在将字节数据转换为字数据后,数据的符号和数值大小的表示在有符号数运算体系下是正确的。
    • 示例

      • 正数示例
        • 假设 AL 寄存器中存储着一个有符号字节数 7(二进制为 00000111)。当执行 CBW 指令时,由于 AL 的最高位是 0,表示正数,所以扩展后的 AX 高字节 AH 被填充为 0,AL 不变。因此,AX 中的值变为 0000000000000111(即 0007H)。
          MOV AL, 7   ; 将7存入AL寄存器
          CBW         ; 执行字节到字的转换
        • 负数示例
          • 假设 AL 寄存器中存储着一个有符号字节数 - 7(二进制为 11111001)。执行 CBW 指令时,因为 AL 的最高位是 1,表示负数,所以扩展后的 AX 高字节 AH 被填充为 11111111(FFH),AL 不变。此时,AX 中的值变为 1111111111111001(即 FFF9H)。
            MOV AL, -7  ; 将 - 7存入AL寄存器
            CBW         ; 执行字节到字的转换
          • 在运算中的应用示例
            • 在进行有符号数的算术运算时,可能会遇到操作数类型不一致的情况。例如,要将一个字节类型的有符号数与一个字类型的有符号数相加,就需要先将字节数转换为字数。
              MOV AL, -3  ; 存储一个有符号字节数 - 3
              CBW         ; 将字节 - 3转换为字
              MOV BX, 5   ; 存储一个有符号字数5
              ADD AX, BX  ; 将转换后的AX与BX相加

              在这个例子中,先通过 CBW 指令将 AL 中的有符号字节数 - 3 转换为有符号字数据存储在 AX 中,然后与 BX 中的有符号字数据 5 相加,得到正确的运算结果。

  • CWD(Convert Word to Double - word)字转换为双字
    • 功能
      • CWD 指令用于将寄存器 AX 中的有符号字数扩展为 DX:AX 中的有符号双字数。如果 AX 中的最高位(符号位)为 0(表示正数),则将 DX 寄存器设置为 0,AX 保持不变;如果 AX 中的最高位为 1(表示负数),则将 DX 设置为 0FFFFH,AX 保持不变。这同样是一种符号扩展方式,目的是在字到双字的数据转换过程中,保证有符号数的符号和数值正确性。
    • 示例
      • 假设 AX 寄存器中存放着一个字数据-3(二进制表示为11111101)。
        MOV AX, -3
        CWD

        执行 CWD 指令后,因为 AX 的最高位为 1,所以 DX 被设置为 0FFFFH,AX 保持为11111101,DX:AX 中的数据就变为 0FFFFFDH,表示一个有符号双字数据-3。

三、逻辑运算指令

1. 逻辑与指令

  • AND(Logical AND)
    • 功能:对两个操作数进行按位逻辑与运算,只有当两个操作数对应的位都为 1 时,结果位才为 1,否则为 0。运算结果存放在目标操作数中,同时会根据结果更新标志位,如零标志位 ZF(Zero Flag)、符号标志位 SF(Sign Flag)和奇偶标志位 PF(Parity Flag)等。
    • 示例
      • AND AL, 0FH:将 AL 寄存器中的值与十六进制数 0F 进行逻辑与运算,例如 AL 初始值为 3CH(二进制为 00111100),运算后 AL 的值变为 0CH(二进制为 00001100)。
      • AND AX, BX:将 AX 和 BX 寄存器中的值进行逻辑与运算,结果存放在 AX 中。

2. 逻辑或指令

  • OR(Logical OR)
    • 功能:对两个操作数进行按位逻辑或运算,只要两个操作数对应的位中有一个为 1,结果位就为 1;只有当两个位都为 0 时,结果位才为 0。结果存放在目标操作数中,并且会更新相关标志位。
    • 示例
      • OR AH, 80H:将 AH 寄存器中的值与十六进制的 80 进行逻辑或运算。假设 AH 初始值为 01H,运算后 AH 的值变为 81H(二进制为 10000001)。
      • OR CX, DX:将 CX 和 DX 寄存器中的值进行逻辑或运算,结果存放在 CX 中。

3. 逻辑异或指令

  • XOR(Exclusive OR)
    • 功能:对两个操作数进行按位逻辑异或运算,当两个操作数对应的位不同时,结果位为 1;当两个位相同时,结果位为 0。运算结果存放在目标操作数中,同时也会影响标志位。
    • 示例
      • XOR BL, 01H:将 BL 寄存器中的值与十六进制的 01 进行逻辑异或运算。例如 BL 初始值为 02H,运算后 BL 的值变为 03H(因为 0010 XOR 0001 = 0011)。
      • XOR SI, DI:将 SI 和 DI 寄存器中的值进行逻辑异或运算,结果存放在 SI 中。

4. 逻辑非指令

  • NOT(Logical NOT)
    • 功能:是单操作数指令,对操作数进行按位逻辑非运算,即将操作数的每一位取反。它不影响标志位(除了结果影响标志位 ZF 等间接情况)。
    • 示例
      • NOT CX:将 CX 寄存器中的值按位取反。例如 CX 初始值为 1234H,运算后 CX 的值变为 EDCBH(因为 1234H 二进制为 0001001000110100,取反后为 1110110111001011,即 EDCBH)。

5. 测试指令

  • TEST(Test)
    • 功能:对两个操作数进行逻辑与运算,但不保存运算结果,只根据结果设置标志位,如 ZF、SF 和 PF 等。通常用于检查操作数的某些位是否为 0 或 1,而不需要改变操作数本身的值。
    • 示例
      • TEST AL, 80H:对 AL 寄存器中的值和十六进制数 80H 进行逻辑与运算,通过检查零标志位 ZF 可以判断 AL 的最高位是否为 0。如果 ZF = 1,说明 AL 的最高位为 0;如果 ZF = 0,说明 AL 的最高位为 1。

四、控制转移指令

1. 无条件转移指令

  • JMP(Jump)
    • 功能:无条件地改变程序的执行顺序,跳转到指定的标号(Label)或内存地址。它可以实现短跳转、近跳转和远跳转,分别适用于不同的跳转范围。
    • 示例
      • JMP LABEL:跳转到名为 LABEL 的标号处继续执行程序。例如在循环结构中,可以使用 JMP 指令跳回到循环的开始部分,实现循环执行。
      • JMP DWORD PTR [BX]:跳转到由 BX 寄存器指向的内存单元中存放的双字(4 字节)地址处。这种方式可以根据内存中的数据动态地确定跳转目标。

2. 条件转移指令(基于标志位的条件跳转)

  • JZ(Jump if Zero)/ JE(Jump if Equal)
    • 功能:当标志位 ZF(Zero Flag)为 1,即上一个运算结果为零(或比较结果相等)时跳转。这两个指令在功能上是等价的,只是不同的助记符表示方式。
    • 示例
      • CMP AX, BX; JZ EQUAL:先比较 AX 和 BX 的值,如果相等(比较结果为零),则跳转到 EQUAL 标号处。这在判断两个数是否相等的程序逻辑中经常使用。
  • JNZ(Jump if Not Zero)/ JNE(Jump if Not Equal)
    • 功能:当标志位 ZF 为 0,即上一个运算结果不为零(或比较结果不相等)时跳转。这两个指令在功能上是等价的。
    • 示例
      • CMP CX, DX; JNZ DIFFERENT:比较 CX 和 DX 的值,如果不相等(比较结果不为零),则跳转到 DIFFERENT 标号处。
  • JS(Jump if Sign)和 JNS(Jump if Not Sign)
    • 功能:JS 指令在标志位 SF(Sign Flag)为 1,即结果为负数时跳转;JNS 指令在 SF 为 0,即结果为正数或零时跳转。这些指令用于根据运算结果的符号进行跳转。
    • 示例
      • MOV AX, -1; JS NEGATIVE_NUMBER:如果 AX 中的值为负数(在这个例子中是 - 1),则跳转到 NEGATIVE_NUMBER 标号处。
  • JO(Jump if Overflow)和 JNO(Jump if No Overflow)
    • 功能:JO 指令在标志位 OF(Overflow Flag)为 1,即发生溢出时跳转;JNO 指令在 OF 为 0,即未发生溢出时跳转。这些指令在进行有符号数运算时,用于检查是否发生溢出并进行相应的处理。
    • 示例
      • ADD AX, BX; JO OVERFLOW_HAPPENED:在进行 AX 和 BX 相加后,如果发生溢出,则跳转到 OVERFLOW_HAPPENED 标号处。

3. 循环控制指令

  • LOOP(Loop)
    • 功能:用于循环控制。它会先将 CX 寄存器(在 16 位模式下)或 ECX 寄存器(在 32 位模式下)的值减 1,然后判断 CX/ECX 是否为 0。如果不为 0,则跳转到指定的标号处继续执行循环;如果为 0,则结束循环,继续执行 LOOP 指令后面的程序。
    • 示例: 以下代码实现了一个简单的循环,将 AL 寄存器中的值累加到 BX 寄存器中,循环次数为 10 次。
MOV CX, 10
    MOV AL, 5
    MOV BX, 0
    LABEL:
        ADD BX, AL
        LOOP LABEL
  • 开始时,CX 被设置为 10,每次循环执行ADD BX,AL后,LOOP LABEL指令会将 CX 减 1,并检查 CX 是否为 0。当 CX 减到 0 时,循环结束,此时 BX 寄存器中存储的是 AL 值的 10 倍(即 50)。

        

  • LOOPE/LOOPZ(Loop While Equal/Loop While Zero)
    • 功能:这两个指令是等价的,它们在 CX/ECX 不为 0 且标志位 ZF(Zero Flag)为 1(即上一个比较或运算结果为相等或为零)时跳转,继续循环;否则结束循环。主要用于在循环过程中根据条件是否满足来决定是否继续循环。
    • 示例:假设有两个字节数组,分别存放在ARRAY1ARRAY2中,要比较这两个数组中相同位置的元素,直到找到第一个不相等的元素或者比较完所有元素。
MOV CX, COUNT   ; COUNT是数组元素个数
    MOV SI, 0
    LABEL:
        MOV AL, ARRAY1[SI]
        MOV BL, ARRAY2[SI]
        CMP AL, BL
        LOOPE LABEL
  • 在这个例子中,每次循环比较两个数组中相同位置的元素(通过SI索引)。如果比较结果相等,ZF 标志位为 1,并且 CX 不为 0,就会继续循环;如果找到不相等的元素,ZF 为 0,循环结束。

  • LOOPNE/LOOPNZ(Loop while Not Equal/Loop while Not Zero)

        功能:LOOPNE/LOOPNZ这两个指令也是等价的,它们在 CX/ECX 不为 0 且标志位 ZF 为 0(即上一个比较或运算结果为不相等或不为零)时跳转,继续循环;否则结束循环。

        示例:以下代码用于在一个字节数组ARRAY中查找第一个非零元素。

MOV CX, 10
MOV AL, 10H
LABEL:
  CMP BYTE PTR [SI], AL
  LOOPE LABEL

在这个例子中,每次循环会比较由 SI 指向的内存单元中的字节和 AL 中的值(10H),如果比较结果相等(ZF = 1)且 CX 不为 0,就会继续循环。


MOV CX, COUNT   ; COUNT是数组元素个数
MOV SI, 0
LABEL:
    MOV AL, ARRAY[SI]
    CMP AL, 0
    LOOPNE LABEL
  • 每次循环检查数组中的一个元素是否为 0。如果元素不为 0(比较结果 ZF 为 0),并且 CX 不为 0,就继续循环;如果找到零元素或者已经检查完所有元素(CX 为 0),循环结束。

4. 子程序调用和返回指令

  • CALL(Call a subroutine)和 RET(Return from a subroutine)
  • CALL 英文全称:Call a subroutine;RET 英文全称:Return from a subroutine。
  • 功能:CALL 指令用于调用子程序。它将下一条指令的地址(返回地址)压入堆栈,然后跳转到子程序的入口地址。RET 指令用于从子程序返回。它从堆栈中弹出返回地址,使程序回到调用子程序的位置继续执行。

示例

CALL SUB_PROC
; 调用SUB_PROC子程序后,这里的代码会在子程序返回后执行
SUB_PROC PROC
    ; 子程序代码
    RET
SUB_PROC ENDP

在这个例子中,当执行CALL指令时,程序会跳转到SUB_PROC子程序的代码部分执行,当遇到RET指令时,程序会返回CALL指令的下一条指令处继续执行。

五、串操作指令

1.  数据传送类串操作指令

  • MOVS(Move String)
    • 功能:用于将一个数据块(字节串或字串)从一个存储位置移动到另一个存储位置。可以在内存的不同段之间或者同一段内进行数据传送。
    • 示例
      • 假设要将数据段(DS)中由源变址寄存器(SI)指向的字节串传送到附加段(ES)中由目的变址寄存器(DI)指向的位置,代码如下:
      • MOVS BYTE PTR ES:[DI], DS:[SI](字节串传送)或者MOVS WORD PTR ES:[DI], DS:[SI](字串传送)。
      • 通常会与 REP 前缀一起使用,用于重复移动多个数据。例如:REP MOVS BYTE PTR ES:[DI], DS:[SI],此时 CX 寄存器中存放着重复次数,每传送一次字节(或字),CX 减 1,直到 CX 为 0 时停止传送。

2. 数据比较类串操作指令

  • CMPS(Compare String)
    • 功能:用于比较两个数据块(字节串或字串)。比较的是由源变址寄存器(SI)和目的变址寄存器(DI)指向的字节或字,比较结果反映在标志位中,如相等则零标志位 ZF 为 1,不相等则 ZF 为 0,同时还会影响其他标志位(如 SF、OF 等),可以根据标志位来判断两个串的大小关系等信息。
    • 示例
      • CMPS WORD PTR [SI], [DI](比较两个字串)或者CMPS BYTE PTR [SI], [DI](比较两个字节串)。
      • 常与 REPE/REPZ(相等 / 为零则重复)或 REPNE/REPNZ(不相等 / 不为零则重复)前缀一起使用。例如:REPE CMPS BYTE PTR [SI], [DI],在字节相等且 CX 不为 0 时继续比较,可用于查找两个串中第一个不相等的字节位置。

3. 数据扫描类串操作指令

  • SCAS(Scan String)
    • 功能:用于在一个字节串或字串中扫描特定的值。该字节串或字串是由目的变址寄存器(DI)指向的,扫描的值通常存放在 AL(字节扫描)或 AX(字扫描)寄存器中。扫描结果也会反映在标志位中,例如找到匹配值则 ZF 为 1,否则 ZF 为 0。
    • 示例
      • SCASB(扫描字节串)用于在字节串中扫描 AL 寄存器中的值,SCASW(扫描字串)用于在字串中扫描 AX 寄存器中的值。
      • 同样可以和 REPE/REPZ 或 REPNE/REPNZ 前缀一起使用。例如:REPNE SCASB,在字节串中扫描与 AL 寄存器中的值不相等的字节,只要不相等且 CX 不为 0 就继续扫描,可用于统计串中与给定值不相等的字节个数。

4. 数据存储类串操作指令

  • STOS(Store String)
    • 功能:将 AL(字节存储)或 AX(字存储)中的值存储到由目的变址寄存器(DI)指向的字节串或字串的连续单元中。
    • 示例
      • STOSB将 AL 中的字节值存储到字节串中,STOSW将 AX 中的字值存储到字串中。
      • 与 REP 前缀结合可以实现对一段内存区域的初始化。例如:REP STOSB,将 AL 中的字节值重复存储到由 DI 指向的字节串中,存储的次数由 CX 寄存器决定,可用于将一段内存区域初始化为特定的值。

5. 数据读取类串操作指令

  • LODS(Load String)
    • 功能:将由源变址寄存器(SI)指向的字节串或字串中的数据读取到 AL(字节读取)或 AX(字读取)寄存器中。
    • 示例
      • LODSB从字节串中读取一个字节到 AL 中,LODSW从字串中读取一个字到 AX 中。不过这个指令一般较少单独使用 REP 前缀,因为每次读取都会覆盖 AL 或 AX 中的原有值。

六、处理器控制指令

1. 标志位操作指令

  • CLC(Clear Carry Flag)
    • 功能:将进位标志位 CF(Carry Flag)清零。进位标志位在算术运算(如加法、减法)和移位操作等过程中用于表示是否有进位或借位情况。清除 CF 位后,可用于重新开始一些不依赖于之前进位状态的运算。
    • 示例
      • 在进行多字节加法运算时,先清除进位标志,然后从最低字节开始相加。如在 16 位系统中计算两个 32 位数相加,先执行CLC,然后进行低 16 位相加,再考虑进位进行高 16 位相加。
  • STC(Set Carry Flag)
    • 功能:将进位标志位 CF 置为 1。这在一些需要手动设置进位状态的运算或者特定的位操作中很有用,比如在实现一些特殊的算术算法或者位运算算法时可能需要预先设置进位。
    • 示例
      • 在某些位运算算法中,可能需要先设置进位标志,然后通过带进位的循环左移(RCL)指令进行多字节数据的处理,此时就需要先执行STC来设置进位标志。
  • CLI(Clear Interrupt - Enable Flag)
    • 功能:清除中断允许标志位 IF(Interrupt - Enable Flag)。当 IF 位被清除后,CPU 将屏蔽外部可屏蔽中断,直到再次通过STI指令将 IF 位设置为 1。这在一些需要保证一段代码不受外部中断干扰的情况下很有用,例如在执行一些对时间要求严格或者数据一致性要求高的代码序列时。
    • 示例
      • 在进行一些关键的数据读写操作或者多步的内部设备初始化过程中,为了避免被外部中断打断,可以先执行CLI指令,等操作完成后再执行STI恢复中断响应。
  • STI(Set Interrupt - Enable Flag)
    • 功能:将中断允许标志位 IF 置为 1,允许 CPU 响应外部可屏蔽中断。这是恢复 CPU 对外部中断响应能力的指令,通常在完成一段需要屏蔽中断的关键代码后使用。
    • 示例
      • 假设在一段数据加密算法代码执行前执行了CLI指令屏蔽中断,当加密算法执行完毕后,执行STI指令允许中断,使 CPU 能够正常响应外部设备的中断请求。
  • CMC(Complement Carry Flag)
    • 功能:对进位标志位 CF 取反。即如果 CF 原来为 0,则变为 1;如果 CF 原来为 1,则变为 0。这在一些根据进位标志位状态进行灵活处理的算法中很有用。
    • 示例
      • 在一个循环进位加法算法中,根据前一步运算的进位标志位状态,使用CMC指令改变进位状态,然后进行下一步加法运算。

2. 外部同步指令

  • HLT(Halt)
    • 功能:使处理器进入暂停状态。在这种状态下,处理器停止执行指令,直到被外部中断或者复位信号唤醒。通常用于等待外部事件或者系统处于空闲状态时降低功耗等情况。
    • 示例
      • 在一个简单的嵌入式系统中,当系统完成了所有的任务并且在等待新的外部输入(如按键按下或者传感器触发)时,处理器可以执行HLT指令暂停,当有中断信号到来时,处理器从暂停状态恢复并处理中断。
  • WAIT(Wait)
    • 功能:使处理器进入等待状态。与HLT不同的是,WAIT指令主要用于等待处理器的测试引脚(如在 8086 中是 TEST 引脚)变为有效状态,同时处理器会不断检查这个引脚状态,一旦引脚有效就继续执行下一条指令。它通常用于与协处理器或者其他外部设备进行同步操作。
    • 示例
      • 在与数学协处理器(如 8087)配合工作时,主处理器执行WAIT指令等待协处理器完成复杂的数学运算,协处理器在运算完成后会使 TEST 引脚有效,从而唤醒主处理器继续执行后续指令。
  • ESC(Escape)
    • 功能:处理器交权给外部设备(如协处理器),用于协处理器操作。在指令流中,当执行ESC指令时,处理器会将指令流中的某些操作码和操作数传递给外部协处理器,由协处理器执行相应的操作。
    • 示例
      • 在包含数学协处理器(如 8087)的系统中,当需要执行浮点数运算时,主处理器通过ESC指令将浮点数运算指令发送给协处理器,协处理器执行这些指令并将结果返回给主处理器或者存储在指定的内存位置。

3. 总线锁定指令

  • LOCK(Lock)
    • 功能:在多处理器环境下,锁定总线,保证指令执行的原子性。当在指令前加上LOCK前缀时,处理器会在执行该指令期间独占总线,防止其他处理器访问共享的内存资源或者 I/O 设备,确保对共享资源的操作完整性。
    • 示例
      • 在一个多处理器系统中,当多个处理器可能同时访问和修改一个共享的内存变量(如全局计数器)时,在对这个变量进行读写操作的指令(如INC指令增加计数器的值)前加上LOCK前缀,如LOCK INC [COUNT_VARIABLE],可以确保每次只有一个处理器能够修改这个变量,避免数据不一致的情况。

总结: 汇编语言中指令众多,以上是一些常用的汇编语言指令,熟练掌握和使用上面的这些汇编指令,你的汇编语言水平就已经上了一个台阶了! 这6大类汇编指令基本上包括了常用的所有汇编指令,学习汇编语言的小伙伴可不要忘记收藏点赞哦 :)

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐