51 单片机的程序状态字寄存器PSW(属于 SFR,地址 0D0H)中,与算术运算相关的标志位包括:
| 标志位 | 含义 | 受影响的指令 |
|---|---|---|
| C (Cy) | 进位 / 借位标志 | ADD/ADDC/SUBB(加法有进位置 1,减法有借位置 1) |
| AC | 半进位标志 | ADD/ADDC/SUBB(低 4 位向高 4 位进位 / 借位时置 1) |
| OV | 溢出标志 | ADD/ADDC/MUL/DIV(有符号数溢出 / 乘法超 255 / 除法除数为 0 时置 1) |
| P | 奇偶标志 | 所有改变 A 的指令(A 中 1 的个数为奇数时置 1) |
1. 加法 (ADD / ADDC)
加法有两种:不带进位的( ADD )和带进位的( ADDC )。1.1 普通加法 (ADD)最常用的加法。
-
格式 : ADD A, <源操作数>
-
含义 :A = A + 源操作数
-
影响标志位 : C (进位), AC (半进位), OV (溢出)代码样例:
MOV A, #30H ; A = 48 (十进制)
ADD A, #10H ; A = 48 + 16 = 64 (0x40)
; 结果:A = 40H
1.2 带进位加法 (ADDC)用于多字节相加 。比如两个 16 位数相加,先加低 8 位,再加高 8 位时,高 8 位必须用 ADDC ,因为它要把低位产生的进位(Cy)也加进去。
-
格式 : ADDC A, <源操作数>
-
含义 :A = A + 源操作数 + C (进位标志)代码样例(16 位数相加):
; 目标:R0R1 (高低) + R2R3 (高低) -> 结果存回 R0R1
; 1. 先加低 8 位
MOV A, R1
ADD A, R3 ; 低位相加
MOV R1, A ; 保存低位结果 (如果溢出,C 会置 1); 2. 再加高 8 位 (必须用 ADDC)
MOV A, R0
ADDC A, R2 ; 高位相加 + 刚才的进位 C
MOV R0, A ; 保存高位结果
2. 减法 (SUBB)
注意:51 单片机只有带借位的减法 (SUBB),没有不带借位的 SUB 指令! 所以,做第一次减法前, 必须手动清零进位标志 C 。
-
格式 : SUBB A, <源操作数>
-
含义 :A = A - 源操作数 - C
-
新手坑点 :忘记写 CLR C 。代码样例:
MOV A, #50H ; A = 80
CLR C ; 【重要】先清零 C,防止之前的进位干扰
SUBB A, #20H ; A = 80 - 32 - 0
; 结果:A = 30H (48)
3. 乘法 (MUL)
51 单片机有硬件乘法器,虽然慢(4 个周期),但很方便。
- 格式 : MUL AB (操作数必须固定是 A 和 B)
- 含义 :A × B
- 结果存放 :
-
低 8 位 放在 A
-
高 8 位 放在 B
-
如果积大于 255 (FFH),溢出标志 OV 会置 1。代码样例:
MOV A, #10 ; A = 10
MOV B, #20 ; B = 20
MUL AB ; 10 * 20 = 200 (0xC8)
; 结果:A = C8H, B = 00H (因为结果没超过 255)
-
4. 除法 (DIV)
同样使用硬件除法器。
- 格式 : DIV AB (操作数必须固定是 A 和 B)
- 含义 :A ÷ B
- 结果存放 :
-
商 放在 A
-
余数 放在 B
-
如果 B = 0 (除数为 0),溢出标志 OV 会置 1。代码样例:
MOV A, #13 ; A = 13
MOV B, #5 ; B = 5
DIV AB ; 13 ÷ 5 = 2 ... 3
; 结果:A = 02H (商), B = 03H (余数)
-
5. 快速查表总结
| 运算 | 指令 | 格式 | 结果存放 | 备注 |
|---|---|---|---|---|
| 加法 | ADD | ADD A, R0 | A | 最常用 |
| 带进位加 | ADDC | ADDC A, R0 | A | 做 16 位加法时,高位运算用它 |
| 减法 | SUBB | SUBB A, R0 | A | 必须先 CLR C |
| 乘法 | MUL | MUL AB | A (低), B (高) | 只能用 A 和 B |
| 除法 | DIV | DIV AB | A (商), B (余) | 只能用 A 和 B |
6 位运算 (逻辑运算)
位运算是按位进行的,每一位互不干扰。1.1 与运算 (ANL) - "清零神器"规则:只有两个都是 1 才是 1,否则为 0。 用途:把某些位强制清零,保留其他位不变。
-
格式 : ANL A, #<mask>
-
口诀 : 想清零谁,就给谁 0;想保留谁,就给谁 1。样例:保留低 4 位,清零高 4 位
MOV A, #0F5H ; A = 1111 0101
ANL A, #0FH ; A = A & 0000 1111
; 结果:A = 05H (0000 0101) -> 高 4 位被砍掉了
1.2 或运算 (ORL) - "置位神器"规则:只要有一个是 1 就是 1。 用途:把某些位强制置 1,保留其他位不变。
-
格式 : ORL A, #<mask>
-
口诀 : 想置 1 谁,就给谁 1;想保留谁,就给谁 0。样例:把高 4 位置 1,低 4 位不变
MOV A, #05H ; A = 0000 0101
ORL A, #0F0H ; A = A | 1111 0000
; 结果:A = F5H (1111 0101) -> 高 4 位全变 1 了
1.3 异或运算 (XRL) - "翻转神器"规则:相同为 0,不同为 1。 用途:把某些位取反(翻转),保留其他位不变。
-
格式 : XRL A, #<mask>
-
口诀 : 想翻转谁,就给谁 1;想保留谁,就给谁 0。样例:把 P1 口的所有灯状态取反
MOV A, P1 ; 读状态
XRL A, #0FFH ; 全 1 掩码,每一位都翻转
MOV P1, A ; 写回去
| 指令类型 | 指令 | 是否必须含 A/C | 核心语法(合法格式) | 示例 |
|---|---|---|---|---|
| 字节操作 | ANL | 含 A 或 直接地址(无需 C) | ANL A, #dataANL direct, #data | ANL A, #0F0H(含 A)ANL P1, #0FEH(直接地址,无 A/C) |
| 字节操作 | ORL | 含 A 或 直接地址(无需 C) | ORL A, directORL direct, A | ORL A, 30H(含 A)ORL P3, #00FH(直接地址,无 A/C) |
| 字节操作 | XRL | 含 A 或 直接地址(无需 C) | XRL A, @RiXRL direct, #data | XRL A, @R0(含 A)XRL 40H, #55H(直接地址,无 A/C) |
| 位操作 | ANL | 必须含 C | ANL C, bitANL C, /bit | ANL C, P1.0(含 C)ANL C, /00H(含 C) |
| 位操作 | ORL | 必须含 C | ORL C, bitORL C, /bit | ORL C, TCON.0(含 C)ORL C, /P3.1(含 C) |
| 位操作 | XRL | 无位操作版 | - | 无(XRL 不能操作 C 位) |