
作者:逆境不可逃
技术永无止境
希望我的内容可以帮助到你!!!!
大家吼 ! 我是 逆境不可逃 今天给大家带来文章
《 一篇速通 汇编程序语言设计之 8086 汇编核心指令 》
近期文章 欢迎阅读
【学Git看这篇文章就够了】Git 终极避坑指南 + 全命令速查表-CSDN博客
【学Git看这篇文章就够了】远程仓库 AND 企业协作进阶-CSDN博客
【学Git看这篇文章就够了】基础入门之本地单机 Git-CSDN博客

好,废话不多说,进入正题
一开始接触汇编,看到满屏的英文缩写指令都会觉得头大
其实这些指令就像 CPU 能听懂的「基础动词」
掌握了它们,你就能给 CPU 发号施令
让它完成数据搬运、数值计算、批量处理等各种操作
今天我们把最常用的四大类指令 ------ 数据传送、算术运算、逻辑操作、串处理
用通俗的比喻和直白的用法讲清楚,零基础也能快速理解
一、数据传送指令:CPU 的「搬运小分队」
数据传送是汇编里最常用的操作,就像现实里的搬家、储物、互换位置。它的核心是把数据在寄存器、内存、外设之间挪来挪去,绝大多数情况下不会改变数据本身,只改变数据的存放位置。
1. 通用数据传送指令:最基础的搬运操作
这是日常开发中使用频率最高的一组,包含 4 条指令:MOV、PUSH、POP、XCHG
-
MOV(Move):数据复制传送 相当于电脑里的「复制粘贴」 把源位置的数据复制一份放到目标位置,源位置的数据保持不变 ,格式:
MOV 目标操作数, 源操作数,示例:MOV AX, BX------ 把 BX 寄存器里的数值,复制一份放到 AX 寄存器中, 注意:不能直接把一段内存数据传到另一段内存,也不能直接修改段寄存器,需要通过通用寄存器中转。 -
PUSH / POP:栈操作指令 你可以把栈理解成一个「后进先出」的箱子:PUSH 是往箱子最上面放东西(压栈),POP 是从箱子最上面拿东西(弹栈)。
PUSH AX:把 AX 的值放到栈顶,栈指针自动上移POP AX:把栈顶的值取出放到 AX,栈指针自动下移 常用于保存临时数据、调用子程序时保存现场。
-
XCHG(Exchange):数据交换 相当于「互换位置」,不用中间变量,直接交换两个位置的数据。 示例:
XCHG AX, BX------ 执行完成后,AX 里是原来 BX 的值,BX 里是原来 AX 的值。
2. 累加器专用传送指令:和外设打交道
这组指令专门通过累加器(AX/AL)和外部设备端口(比如键盘、串口)交互,包含IN、OUT、XLAT
-
IN:从端口读取数据 从指定的外设端口读取数据,存到 AL(8 位)或 AX(16 位)中。 示例:
IN AL, 20H------ 从 20 号端口读取一个字节,放到 AL 寄存器。 -
OUT:向端口写入数据 把 AL 或 AX 里的数据,写入到指定的外设端口。 示例:
OUT 20H, AL------ 把 AL 里的字节数据,写入 20 号端口。 -
XLAT(Translate):查表转换 俗称「换码指令」,用来快速查表取值。比如你提前在内存里存了一张 ASCII 码对照表,用 XLAT 就能根据索引快速取出对应的值,结果自动存入 AL。 用法:先把表的首地址存入 BX,索引值存入 AL,执行 XLAT 即可得到表中对应位置的数值。
3. 地址传送指令:拿地址,不拿数据
这组指令传送的不是数据本身,而是数据在内存中的地址,包含LEA、LDS、LES
-
LEA(Load Effective Address):取有效地址 把内存操作数的偏移地址,送到指定的通用寄存器。注意它取的是「地址坐标」,不是地址里存储的数值。 示例:
LEA AX, [BX+SI]------ 把 BX+SI 计算出的偏移地址放到 AX,而不是把这个地址里的数据放到 AX。 通俗理解:MOV 是拿盒子里的东西,LEA 是拿盒子的位置坐标。 -
LDS / LES:加载远地址 一次性加载「段地址 + 偏移地址」:LDS 会把段地址存入 DS 数据段寄存器,LES 会把段地址存入 ES 附加段寄存器,偏移地址统一存入指定的通用寄存器。 示例:
LDS SI, [1000H]------ 把内存 1000H 处的偏移地址给 SI,后续的段地址给 DS。
4. 标志寄存器传送指令:操作状态标志
标志寄存器保存着 CPU 运算的各种状态(比如是否进位、结果是否为 0),这组指令专门用来读写标志寄存器,包含LAHF、SAHF、PUSHF、POPF
- LAHF:把标志寄存器的低 8 位(SF、ZF、AF、PF、CF 等状态标志),复制到 AH 寄存器。
- SAHF:和 LAHF 相反,把 AH 里的值写回标志寄存器的低 8 位,用来修改标志位。
- PUSHF / POPF:对整个 16 位标志寄存器做栈操作。PUSHF 把标志寄存器整体压栈保存,POPF 把栈顶数据弹出恢复标志位,常用于子程序中保存和恢复运算状态。
5. 类型转换指令:自动扩展数据长度
做乘除法运算时,经常需要把短数据扩展成长数据,这两条指令会自动按照符号位扩展,保证数值大小不变,包含CBW、CWD
-
CBW(Convert Byte to Word):字节转字 把 AL 里的 8 位有符号数,扩展成 16 位有符号数,结果存在 AX 中。扩展规则:AL 最高位是 0 则 AH 全填 0,最高位是 1 则 AH 全填 1。
-
CWD(Convert Word to Double word):字转双字 把 AX 里的 16 位有符号数,扩展成 32 位有符号数,高 16 位存在 DX,低 16 位存在 AX,同样按符号位自动扩展。
二、算术指令:CPU 的「自带计算器」
算术指令就是负责加减乘除运算,和我们日常使用的计算器逻辑基本一致,运算的同时会同步修改标志寄存器的状态位。
1. 加法指令
包含ADD、ADC、INC三条
-
ADD:普通加法 两个数相加,结果存回目标操作数。 格式:
ADD 目标, 源示例:ADD AX, BX------ 等价于 AX = AX + BX -
ADC(Add with Carry):带进位加法 相加时会额外加上进位标志 CF 的值,专门用来计算超过 16 位的大数。比如计算 32 位加法,先算低 16 位,再用 ADC 算高 16 位,把低 16 位的进位包含进去。
-
INC(Increment):自增 1 给目标操作数加 1,等价于
目标 = 目标 + 1,常用在循环中给计数器累加。 示例:INC CX------ CX 的数值加 1
2. 减法指令
包含SUB、SBB、DEC、NEG、CMP五条
-
SUB:普通减法 目标操作数减去源操作数,结果存回目标。 示例:
SUB AX, BX------ 等价于 AX = AX - BX -
SBB(Subtract with Borrow):带借位减法 相减时会额外减去借位标志 CF 的值,和 ADC 对应,用于实现大数减法。
-
DEC(Decrement):自减 1 给目标操作数减 1,等价于
目标 = 目标 - 1,循环中非常常用。 -
NEG(Negate):取负指令 对操作数求补码,也就是正数变负数、负数变正数,等价于
操作数 = 0 - 操作数。 -
CMP(Compare):比较指令 用目标操作数减去源操作数,但不保存运算结果,只修改标志位 。它是分支判断的核心:执行完 CMP 后,我们就能通过标志位判断两个数是否相等、谁大谁小。 比如执行
CMP AX, BX后,如果 ZF 标志为 1,说明 AX 和 BX 数值相等。
3. 乘法指令
包含MUL、IMUL,分别对应无符号数和有符号数乘法
-
MUL(Multiply):无符号乘法
- 8 位乘法:一个乘数默认在 AL,另一个是指定的 8 位操作数,结果存在 AX(AH 存高 8 位,AL 存低 8 位)
- 16 位乘法:一个乘数默认在 AX,另一个是指定的 16 位操作数,结果存在 DX:AX(DX 存高 16 位,AX 存低 16 位)
-
IMUL(Integer Multiply):有符号乘法 用法和 MUL 完全一致,专门用于有符号数乘法,保证符号位计算正确。
4. 除法指令
包含DIV、IDIV,同样区分无符号和有符号
-
DIV(Divide):无符号除法
- 8 位除法:被除数默认在 AX(16 位),除以指定的 8 位操作数;商存在 AL,余数存在 AH
- 16 位除法:被除数默认在 DX:AX(32 位),除以指定的 16 位操作数;商存在 AX,余数存在 DX
-
IDIV(Integer Divide):有符号除法 用法和 DIV 一致,专门处理有符号数除法,余数的符号和被除数保持一致。
5. 十进制调整指令
计算机内部用二进制运算,但很多场景需要用十进制(BCD 码)计算,这组指令的作用就是把二进制运算结果,修正为正确的十进制格式。
- 压缩 BCD 调整:DAA(加法调整)、DAS(减法调整) 压缩 BCD 指一个字节存两位十进制数,运算后用这两条指令修正结果。
- 非压缩 BCD 调整:AAA(加法调整)、AAS(减法调整)、AAM(乘法调整)、AAD(除法调整) 非压缩 BCD 指一个字节只存一位十进制数,对应四种运算的结果修正。
三、逻辑指令:CPU 的「位操作工具箱」
逻辑指令以二进制的「位」为单位进行操作,是汇编里非常灵活的一类指令,常用于控制某一位的开关、实现快速乘除。
1. 逻辑运算指令
包含AND、OR、NOT、XOR、TEST
-
AND:按位与 两个二进制位都为 1,结果才是 1,否则为 0。 典型用法:把某几位清零(屏蔽位)。比如
AND AL, 0FH------ 把 AL 的高 4 位清零,低 4 位保持不变。 -
OR:按位或 两个二进制位只要有一个是 1,结果就是 1。 典型用法:把某几位置 1。比如
OR AL, 80H------ 把 AL 的最高位设为 1,其余位不变。 -
NOT:按位取反 把每一位的 0 变 1、1 变 0,是单操作数指令。注意它不会影响标志位。
-
XOR:按位异或 两个位相同结果为 0,不同结果为 1。 典型用法:① 给寄存器快速清 0,比如
XOR AX, AX,执行后 AX 一定为 0,比 MOV 指令更高效;② 翻转某几位的数值。 -
TEST:位测试指令 和 AND 一样执行按位与运算,但不保存结果,只修改标志位 。 用来检测某一位是否为 0。比如
TEST AL, 01H,如果执行后 ZF=1,说明 AL 的最低位是 0。
2. 移位指令
把二进制数整体向左或向右移动,分为普通移位、算术移位、循环移位三类,共 8 条:SHL、SHR、SAL、SAR、ROL、ROR、RCL、RCR
逻辑移位
- SHL(Shift Left):逻辑左移 所有位整体左移,最低位补 0,最高位移入进位标志 CF。左移一位等价于无符号数乘以 2。
- SHR(Shift Right):逻辑右移 所有位整体右移,最高位补 0,最低位移入 CF。右移一位等价于无符号数除以 2。
算术移位
- SAL(Shift Arithmetic Left):算术左移 效果和 SHL 完全一致,左移低位补 0,不影响符号位。
- SAR(Shift Arithmetic Right):算术右移 右移时最高位(符号位)保持不变,保证有符号数的正负属性不变,适合有符号数除以 2。
循环移位
-
ROL / ROR:不带进位循环移位 ROL 循环左移:最高位移到最低位,同时送入 CF; ROR 循环右移:最低位移到最高位,同时送入 CF。 所有数据位在内部循环,不会丢失数据。
-
RCL / RCR:带进位循环移位 把进位标志 CF 也加入循环圈,相当于多了一位参与循环。RCL 是带进位左移,RCR 是带进位右移。
四、串处理指令:CPU 的「批量处理流水线」
这里的「串」指内存中连续的一段数据(比如字符串、数组),串处理指令可以批量操作一整段数据,不用手动写循环逐个处理,执行效率非常高。
1. 方向标志指令:控制处理方向
串处理是按地址逐个推进的,方向标志决定了地址是递增还是递减:
- CLD:清除方向标志,地址从低到高推进(从串头到串尾)
- STD:设置方向标志,地址从高到低推进(从串尾到串头)
2. 核心串处理指令
所有串指令都有字节(后缀 B)和字(后缀 W)两个版本,默认规则:源串位于 DS:SI,目标串位于 ES:DI,每执行一次,SI/DI 会自动增减 1(字节)或 2(字)。
-
MOVSB / MOVSW:串传送 把源串的一个字节 / 字,复制到目标串位置,然后自动修改 SI 和 DI。 配合重复前缀可以实现「批量复制内存块」,是最常用的串指令。
-
STOSB / STOSW:串存储 把 AL(字节)或 AX(字)里的值,写入目标串的位置,然后自动修改 DI。 常用于给一段内存批量填充同一个值(比如批量清 0、填充空格)。
-
LODSB / LODSW:串读取 把源串的一个字节 / 字,读取到 AL 或 AX 中,然后自动修改 SI。 通常配合循环使用,逐个读取串内数据进行处理。
-
CMPSB / CMPSW:串比较 将源串和目标串的对应位置数据相减,不保存结果,只修改标志位,然后自动修改 SI 和 DI。 用来比较两个字符串 / 数组是否完全相同。
-
SCASB / SCASW:串扫描 用 AL/AX 里的值,和目标串的对应位置数据做比较,只修改标志位,然后自动修改 DI。 用来在字符串中查找某个特定字符。
3. 重复前缀:让指令自动循环
串指令本身只执行一次,加上重复前缀后,就会自动重复执行;重复次数存放在 CX 寄存器中,每执行一次 CX 减 1,直到 CX 为 0 时停止。
-
REP:无条件重复 只要 CX 不为 0 就持续重复,常和 MOVS、STOS 搭配,实现批量复制、批量填充。 示例:
REP MOVSB------ 连续复制 CX 个字节,从源串复制到目标串。 -
REPE / REPZ:相等时重复 除了 CX 不为 0,还要求比较结果相等(ZF=1)才继续重复。常和 CMPS、SCAS 搭配,用来查找第一个不相等的位置。
-
REPNE / REPNZ:不相等时重复 CX 不为 0,且比较结果不相等(ZF=0)时才继续重复。常和 SCAS 搭配,用来在串中查找第一个匹配的字符。
写在最后
以上就是 8086 汇编最核心的四大类指令,它们是编写汇编程序的「基础词汇」:
- 搬运数据用数据传送指令
- 数值计算用算术指令
- 按位操作用逻辑指令
- 批量处理内存用串指令
刚开始不用死记硬背,写代码的时候多查多用,慢慢就能熟练掌握。等把这些指令吃透,你就能看懂绝大多数基础汇编代码,也能独立写出简单的汇编程序了。
附参考程序
; ========== 数据段:定义程序用到的变量和数据 ==========
DATA SEGMENT
; 1. 算术运算测试用变量
num_byte DB 12H ; 8位字节变量
num_word DW 1234H ; 16位字变量
result DW ? ; 预留结果存储位置
; 2. 串操作测试用数据
src_str DB 'Hello, Assembly!', 0 ; 源字符串,共16字节
dest_str DB 20 DUP(0) ; 目标缓冲区,20字节初始化为0
fill_buf DB 10 DUP(?) ; 批量填充测试缓冲区
search_ch DB 's' ; 待查找的字符
DATA ENDS
; ========== 代码段:编写指令逻辑 ==========
CODE SEGMENT
ASSUME CS:CODE, DS:DATA, ES:DATA ; 声明各段对应的段寄存器
START:
; 初始化数据段和附加段(串操作需要ES寄存器,这里和DS共用同一段)
MOV AX, DATA
MOV DS, AX
MOV ES, AX
; ----------------------------------------------------------------------
; 第一部分:数据传送指令演示
; ----------------------------------------------------------------------
; 1. MOV 基本数据传送:内存 → 寄存器
MOV AL, num_byte ; 把num_byte的值(12H)复制到AL寄存器
MOV BX, num_word ; 把num_word的值(1234H)复制到BX寄存器
; 2. XCHG 数据交换:交换AX和BX的内容
XCHG AX, BX ; 执行后 AX=1234H,BX=0012H
; 3. PUSH / POP 栈操作:保存和恢复寄存器
PUSH AX ; 将AX压入栈顶保存
PUSH BX ; 将BX压入栈顶保存
POP AX ; 弹出栈顶数据到AX(得到原BX的值0012H)
POP BX ; 弹出栈顶数据到BX(得到原AX的值1234H)
; 4. LEA 取有效地址:获取变量的内存偏移地址
LEA SI, src_str ; SI = src_str在数据段中的偏移地址
LEA DI, dest_str ; DI = dest_str在数据段中的偏移地址
; 5. CBW 字节转字:有符号数符号扩展
MOV AL, 80H ; AL = 80H(有符号数 -128)
CBW ; 扩展为16位,AX = FF80H,数值保持不变
; 6. CWD 字转双字:16位扩展为32位
MOV AX, 8000H ; AX = 8000H(有符号数 -32768)
CWD ; 扩展为32位,高16位DX=FFFFH,低16位AX=8000H
; ----------------------------------------------------------------------
; 第二部分:算术指令演示
; ----------------------------------------------------------------------
; 1. ADD 普通加法
MOV AX, 1000H
ADD AX, 2000H ; AX = AX + 2000H = 3000H
; 2. ADC 带进位加法:实现32位加法
; 计算 0001FFFFH + 00020001H = 00040000H
MOV AX, 0FFFFH ; 被加数低16位
MOV DX, 0001H ; 被加数高16位
ADD AX, 0001H ; 低16位相加,产生进位 CF=1
ADC DX, 0002H ; 高16位相加 + 进位,DX = 0001+0002+1 = 0004H
; 3. INC 自增1
MOV CX, 0
INC CX ; CX = CX + 1 = 1
; 4. SUB 普通减法
SUB AX, 1000H ; AX = AX - 1000H
; 5. DEC 自减1
DEC CX ; CX = CX - 1 = 0
; 6. NEG 取负(求补码)
MOV AX, 5
NEG AX ; AX = -5(补码表示为 FFFBH)
; 7. CMP 比较指令:只修改标志位,不改变操作数
MOV AX, 10
CMP AX, 10 ; 两数相等,ZF标志位置1
CMP AX, 20 ; AX < 20,SF标志位置1
; 8. MUL 无符号8位乘法:AL × 操作数 → AX
MOV AL, 10H
MOV BL, 20H
MUL BL ; AX = 10H × 20H = 0200H
; 9. DIV 无符号8位除法:AX ÷ 操作数 → 商在AL,余数在AH
MOV AX, 0200H
MOV BL, 10H
DIV BL ; AL = 20H(商),AH = 00H(余数)
; ----------------------------------------------------------------------
; 第三部分:逻辑指令演示
; ----------------------------------------------------------------------
; 1. AND 按位与:清零高4位,保留低4位
MOV AL, 0ABH
AND AL, 0FH ; AL = 0BH,高4位被强制清零
; 2. OR 按位或:最高位置1
OR AL, 80H ; AL = 8BH,最高位变为1
; 3. XOR 按位异或:快速清零寄存器(比MOV更高效)
XOR AX, AX ; AX = 0000H
; 4. TEST 位测试:检测最低位是否为1
MOV AL, 03H
TEST AL, 01H ; 结果非零,ZF=0,说明最低位是1
; 5. SHL 逻辑左移:无符号数 ×2
MOV AX, 3
SHL AX, 1 ; AX = 6,左移1位等价于乘以2
; 6. SAR 算术右移:有符号数 ÷2,符号位保持不变
MOV AX, 0FFFCH ; -4 的补码
SAR AX, 1 ; AX = 0FFFEH(-2),最高位保持1
; 7. ROL 循环左移:最高位移到最低位
MOV AL, 11000000B
ROL AL, 1 ; AL = 10000001B
; ----------------------------------------------------------------------
; 第四部分:串处理指令演示
; ----------------------------------------------------------------------
; 1. 串传送:批量复制字符串(src_str → dest_str)
CLD ; 清除方向标志,地址从低到高递增
LEA SI, src_str ; 源串地址 → SI
LEA DI, dest_str ; 目标串地址 → DI
MOV CX, 16 ; 复制长度:16个字节
REP MOVSB ; 重复执行MOVSB,直到CX=0,完成批量复制
; 2. 串存储:批量填充内存(把fill_buf全部填0)
LEA DI, fill_buf
MOV CX, 10 ; 填充10个字节
MOV AL, 0 ; 填充的数值
REP STOSB ; 重复执行STOSB,10个字节全部变为0
; 3. 串扫描:在dest_str中查找字符's'
LEA DI, dest_str
MOV AL, search_ch ; 待查找的字符 → AL
MOV CX, 20 ; 最大扫描长度
CLD
REPNE SCASB ; 不相等就继续找,找到匹配字符后停止
; 找到后:DI指向匹配字符的下一位,CX为剩余未扫描长度
; ----------------------------------------------------------------------
; 程序结束:正常返回DOS系统
; ----------------------------------------------------------------------
MOV AH, 4CH ; DOS功能号:程序退出
INT 21H ; 调用DOS中断
CODE ENDS
END START
