BCD (Binary-Coded Decimal) 指令介绍
BCD (Binary-Coded Decimal) 指令介绍
BCD (Binary-Coded Decimal) 是一种二进制编码的十进制表示方法,相关的指令集主要用于处理十进制数的运算和调整。以下是关于BCD指令的详细介绍:
基本概念
BCD编码使用4位二进制数表示一个十进制数字(0-9),常见的两种形式:
- 压缩BCD:每个字节存储两个十进制数字
- 非压缩BCD:每个字节存储一个十进制数字(高4位通常为0)
常见的BCD指令
x86架构中的BCD指令
-
AAA (ASCII Adjust after Addition)
- 加法后的ASCII调整指令
- 用于调整非压缩BCD加法后的AL寄存器结果
-
AAS (ASCII Adjust after Subtraction)
- 减法后的ASCII调整指令
- 用于调整非压缩BCD减法后的AL寄存器结果
-
AAM (ASCII Adjust after Multiplication)
- 乘法后的ASCII调整指令
- 将二进制乘积转换为非压缩BCD格式
-
AAD (ASCII Adjust before Division)
- 除法前的ASCII调整指令
- 将非压缩BCD被除数转换为二进制格式
-
DAA (Decimal Adjust after Addition)
- 加法后的十进制调整指令
- 用于调整压缩BCD加法后的AL寄存器结果
-
DAS (Decimal Adjust after Subtraction)
- 减法后的十进制调整指令
- 用于调整压缩BCD减法后的AL寄存器结果
使用示例
DAA指令示例(压缩BCD加法)
MOV AL, 35h ; AL = 35h (十进制35的BCD表示)
ADD AL, 47h ; AL = 35h + 47h = 7Ch (二进制加法结果)
DAA ; 调整AL为82h (35+47=82的BCD表示)
AAA指令示例(非压缩BCD加法)
MOV AL, '8' ; AL = 38h (ASCII '8')
ADD AL, '4' ; AL = 38h + 34h = 6Ch
AAA ; 调整AL为02h, AH += 1 (进位)
OR AL, 30h ; AL = 32h (ASCII '2')
现代应用
在现代计算机系统中,BCD指令的使用已经减少,因为:
- 大多数计算现在直接使用二进制运算
- 需要十进制精确计算的场合可以使用软件库
- 这些指令在现代处理器上可能效率不高
然而,BCD运算仍然在某些特定领域有应用,如财务计算、某些嵌入式系统等,因为BCD可以提供精确的十进制表示而无需处理二进制浮点数的舍入误差。
其他架构
其他处理器架构如ARM、PowerPC等通常不提供专门的BCD指令,而是通过软件实现BCD运算。
演示BCD指令
以下是修改后的汇编代码,演示了BCD(二进制编码十进制)指令的使用,包括压缩和非压缩BCD运算:
; 设置处理器模式和内存模型
.586 ; 使用 586 指令集
.model flat, stdcall ; 平坦内存模型,stdcall 调用约定
option casemap:none ; 区分大小写; 引入库文件
includelib kernel32.lib ; Windows API 库
includelib msvcrt.lib ; C 运行时库.data ; 数据段定义; BCD测试数据packedBCD1 db 34h ; 压缩BCD: 34packedBCD2 db 27h ; 压缩BCD: 27packedResult db ? ; 压缩BCD结果unpackedBCD1 db 08h ; 非压缩BCD: 8 (高4位为0)unpackedBCD2 db 05h ; 非压缩BCD: 5 (高4位为0)unpackedResult db ? ; 非压缩BCD结果asciiNum1 db '9' ; ASCII数字asciiNum2 db '7' ; ASCII数字asciiResult db 2 dup(?) ; ASCII结果存储(十位和个位); 标志位检测CF_flag db ? ; 进位/借位标志OF_flag db ? ; 溢出标志SF_flag db ? ; 符号标志ZF_flag db ? ; 零标志.code ; 代码段
main proc; ---------------------------; 1. 压缩BCD加法演示 (DAA指令); ---------------------------mov al, packedBCD1 ; AL = 34h (BCD 34)add al, packedBCD2 ; AL = 34h + 27h = 5Bh (二进制加法结果)daa ; 调整AL为61h (BCD 61, 即34+27=61)mov packedResult, al; ---------------------------; 2. 压缩BCD减法演示 (DAS指令); ---------------------------mov al, packedBCD1 ; AL = 34hsub al, packedBCD2 ; AL = 34h - 27h = 0Dhdas ; 调整AL为07h (BCD 7, 即34-27=7); ---------------------------; 3. 非压缩BCD加法演示 (AAA指令); ---------------------------mov al, unpackedBCD1 ; AL = 08hadd al, unpackedBCD2 ; AL = 08h + 05h = 0Dhaaa ; 调整AL=03h, AH+=1 (进位)mov unpackedResult, al; ---------------------------; 4. 非压缩BCD减法演示 (AAS指令); ---------------------------mov al, 09h ; AL = 09hsub al, 05h ; AL = 04haas ; 调整AL=04h (无借位); ---------------------------; 5. ASCII数字加法演示 (AAA指令); ---------------------------mov al, asciiNum1 ; AL = '9' (39h)add al, asciiNum2 ; AL = 39h + 37h = 70haaa ; 调整AL=06h, AH+=1 (进位)or al, 30h ; AL = '6' (36h)mov asciiResult[0], ah ; 存储十位 (AH=1 → '1')mov asciiResult[1], al ; 存储个位 (AL='6'); 结果: '9' + '7' = "16" (存储在asciiResult中); ---------------------------; 6. 非压缩BCD乘法演示 (AAM指令); ---------------------------mov al, 09h ; AL = 9mov bl, 07h ; BL = 7mul bl ; AX = 09h * 07h = 003Fh (63)aam ; 调整AH=06h, AL=03h (BCD 63); ---------------------------; 7. 非压缩BCD除法准备 (AAD指令); ---------------------------mov ax, 0508h ; AH=5, AL=8 (BCD 58)aad ; 转换为二进制: AX = 5*10 + 8 = 003Ahmov bl, 09h ; BL = 9div bl ; AL=6, AH=4 (58/9=6余4); ---------------------------; 标志位存储演示; ---------------------------setc CF_flag ; 存储进位标志seto OF_flag ; 存储溢出标志sets SF_flag ; 存储符号标志setz ZF_flag ; 存储零标志; ---------------------------; 程序退出; ---------------------------xor eax, eax ; 返回码 0ret
main endpend main
代码说明
-
压缩BCD运算:
- 使用
DAA
(加法后十进制调整)和DAS
(减法后十进制调整)指令 - 处理每个字节存储两个BCD数字的情况
- 使用
-
非压缩BCD运算:
- 使用
AAA
(加法后ASCII调整)和AAS
(减法后ASCII调整)指令 - 处理每个字节存储一个BCD数字(高4位为0)的情况
- 使用
-
ASCII数字处理:
- 演示如何将ASCII字符转换为BCD进行运算
- 使用
OR AL, 30h
将数字转换回ASCII表示
-
乘法和除法:
AAM
指令用于乘法后的BCD调整AAD
指令用于除法前的BCD到二进制转换
-
标志位检测:
- 使用
setc
、seto
等指令存储标志位状态
- 使用
这段代码展示了BCD指令在十进制运算中的典型应用场景,包括压缩和非压缩格式的处理,以及ASCII数字的转换和运算。
以下是针对代码中每种BCD运算场景及其结果的详细解释:
1. 压缩BCD加法演示 (DAA指令)
mov al, packedBCD1 ; AL = 34h (BCD 34)
add al, packedBCD2 ; AL = 34h + 27h = 5Bh (二进制加法结果)
daa ; 调整AL为61h (BCD 61, 即34+27=61)
- 场景:两个压缩BCD数相加(每个字节存储两个十进制数字)。
- 运算过程:
- 直接二进制加法:
34h + 27h = 5Bh
(非法的BCD结果,因为低4位Bh
超出0-9范围)。 DAA
指令自动调整:- 检查低4位(
Bh
> 9),加06h
修正:5Bh + 06h = 61h
。 - 检查高4位(
6h
≤ 9),无需修正。
- 检查低4位(
- 直接二进制加法:
- 结果:
AL = 61h
(BCD格式的十进制数61)。 - 关键点:
DAA
会在加法后自动修正结果,确保每4位表示一个合法的BCD数字(0-9)。
2. 压缩BCD减法演示 (DAS指令)
mov al, packedBCD1 ; AL = 34h
sub al, packedBCD2 ; AL = 34h - 27h = 0Dh
das ; 调整AL为07h (BCD 7, 即34-27=7)
- 场景:压缩BCD数相减。
- 运算过程:
- 直接二进制减法:
34h - 27h = 0Dh
(低4位Dh
非法)。 DAS
指令调整:- 检查低4位(
Dh
> 9),减06h
修正:0Dh - 06h = 07h
。 - 检查高4位(
0h
≤ 9),无需修正。
- 检查低4位(
- 直接二进制减法:
- 结果:
AL = 07h
(BCD格式的十进制数7)。 - 关键点:
DAS
通过借位修正确保结果符合BCD规范。
3. 非压缩BCD加法演示 (AAA指令)
mov al, unpackedBCD1 ; AL = 08h
add al, unpackedBCD2 ; AL = 08h + 05h = 0Dh
aaa ; 调整AL=03h, AH+=1 (进位)
- 场景:非压缩BCD数相加(每个字节存储一个十进制数字,高4位为0)。
- 运算过程:
- 直接加法:
08h + 05h = 0Dh
(低4位Dh
非法)。 AAA
指令调整:- 若低4位 > 9 或 AF=1,则加
06h
修正:0Dh + 06h = 13h
。 - 高4位清零:
AL = 03h
,并设置AH += 1
(进位)。
- 若低4位 > 9 或 AF=1,则加
- 直接加法:
- 结果:
AL = 03h
(个位),AH
增加1(十位进位),组合结果为十进制13。 - 关键点:
AAA
处理非压缩BCD加法后的进位和修正。
4. 非压缩BCD减法演示 (AAS指令)
mov al, 09h ; AL = 09h
sub al, 05h ; AL = 04h
aas ; 调整AL=04h (无借位)
- 场景:非压缩BCD数相减。
- 运算过程:
- 直接减法:
09h - 05h = 04h
(合法BCD,无需修正)。 AAS
检查:若低4位 > 9 或 AF=1,则减06h
并借位(本例无调整)。
- 直接减法:
- 结果:
AL = 04h
(直接保留结果)。 - 关键点:
AAS
仅在需要时(借位或非法BCD)调整结果。
5. ASCII数字加法演示 (AAA指令)
mov al, asciiNum1 ; AL = '9' (39h)
add al, asciiNum2 ; AL = 39h + 37h = 70h
aaa ; 调整AL=06h, AH+=1 (进位)
or al, 30h ; AL = '6' (36h)
- 场景:ASCII字符数字相加(需先转换为数值)。
- 运算过程:
- ASCII加法:
'9' (39h) + '7' (37h) = 70h
(二进制结果)。 AAA
调整:70h
的低4位为0h
,但AF=1(因加法跨越十进制进位),故修正为06h
,AH += 1
。
OR AL, 30h
将数值06h
转回ASCII字符'6'
。
- ASCII加法:
- 结果:
AH = 1
(十位),AL = '6'
(个位),组合为字符串"16"
。 - 关键点:AAA配合
OR 30h
可实现ASCII数字的十进制运算。
6. 非压缩BCD乘法演示 (AAM指令)
mov al, 09h ; AL = 9
mov bl, 07h ; BL = 7
mul bl ; AX = 09h * 07h = 003Fh (63)
aam ; 调整AH=06h, AL=03h (BCD 63)
- 场景:非压缩BCD乘法。
- 运算过程:
- 直接二进制乘法:
9 * 7 = 63 (003Fh)
。 AAM
将二进制结果转换为非压缩BCD:- 除以10:
63 / 10 = 6
(商→AH),3
(余数→AL)。
- 除以10:
- 直接二进制乘法:
- 结果:
AH = 06h
(十位),AL = 03h
(个位),即BCD格式的63。 - 关键点:
AAM
将二进制乘积拆分为十位和个位。
7. 非压缩BCD除法准备 (AAD指令)
mov ax, 0508h ; AH=5, AL=8 (BCD 58)
aad ; 转换为二进制: AX = 5*10 + 8 = 003Ah
mov bl, 09h ; BL = 9
div bl ; AL=6, AH=4 (58/9=6余4)
- 场景:非压缩BCD除法前的转换。
- 运算过程:
AAD
将非压缩BCD(AH=5
,AL=8
)转换为二进制:5*10 + 8 = 58 (003Ah)
。- 直接二进制除法:
58 / 9 = 6
(商→AL),余数4
(→AH)。
- 结果:
AL = 06h
(商),AH = 04h
(余数)。 - 关键点:
AAD
是唯一在运算前(而非运算后)调整的BCD指令。
标志位的作用
- CF(进位标志):在BCD运算中表示无符号溢出(如加法后>99)。
- AF(辅助进位标志):用于
AAA
/AAS
,标记低4位是否需修正。 - OF/SF/ZF:通常不直接用于BCD运算,但可能受指令影响。
总结
- 压缩BCD:使用
DAA
/DAS
,直接处理字节内的两个十进制数字。 - 非压缩BCD:使用
AAA
/AAS
/AAM
/AAD
,处理单个数字(高4位为0)。 - ASCII数字:需先转换为数值,运算后用
OR 30h
转回ASCII。 - 调整指令:自动修正二进制运算结果,确保符合BCD规范。