1. 二进制数运算
(1)题目的具体要求,将原始数据及存放结果的内存单元分别定义成字变量或字节变量。
(2)编辑程序,把编写好的源程序建立为汇编语言源程序文件并存盘。
(3)对源程序文件进行汇编、连接,且修改至无错误,然后运行程序。
(4)利用调试程序观察运行结果。如果有错,分析并找出错误原因,改写程序并重新调试。
运行示例
.386
.model flat, stdcall
option casemap:none
include windows.inc
include kernel32.inc
include user32.inc
include masm32.inc
includelib kernel32.lib
includelib user32.lib
includelib masm32.lib
.data
A DW 23579
B DW 7684
bC DB 0A5H
D DB 34H
E DW ?
G DB ?
fmt db "23579 + 7684 = %d", 13, 10
db "0A5H + 34H = %02XH", 13, 10, 0
buffer db 128 dup(0)
.code
start:
MOV ax,A
ADD ax,B
MOV E,ax
MOV AL,bC
ADD AL,D
MOV G,AL
;结果格式化输出
movzx EAX,G
push EAX
movzx EAX,E
push EAX
push offset fmt
push offset buffer
call wsprintf
add esp,16
push 0
push offset caption
push offset buffer
push 0
call MessageBoxA
push 0
call ExitProcess
caption DB "二进制运算结果",0
end start

我们发现编译为乱码,排查发现是源代码文件编码与系统期望的编码不匹配,更改文件编码后解决。

调试器进行验证
MOV ax,A
ADD ax,B
MOV E,ax

MOV AL,bC
ADD AL,D
MOV G,AL
经验证结果一致。
2. 多字节加法
(1)在FIRST和SECOND开始的内存单元中建立各为10位的压缩型BCD码的数据区,数据要选择得当。要考虑位间有进位和最高位也要有进位等不同情况。数据从低字节开始存放。
(2)把编写好的源程序,用编辑程序建立为汇编语言程序源文件并存盘。
(3)注意多字节运算加法指令的选择以及相应状态(如CF位)位的设置。进行BCD码运算时,要选择合适的十进制调节调整指令;运算结果的最高位进位情况要处理。
(4)汇编、连接源程序,且修改至无错误,然后运行程序。
知识回顾
多精度加法
多精度加法用于处理超过32位的整数运算(eg:64,128位甚至高精度整数)
因为寄存器最多为32位,因此需要分字节或分字处理,并手动处理CF(进位标志)
概念 | 说明 |
---|---|
DAA 指令 |
加法后调整 AL 为压缩 BCD 格式(仅限 AL 寄存器) |
ADC 指令 |
带进位加法,用于多字节加法 |
小端序 | 低位字节存在低地址,符合 x86 内存布局 |
压缩BCD
每字节含两位BCD数字
高4bit = 十位,低4bit = 个位,范围0-99(即0x00-0x99) • 示例:46H = 01000110b
表示BCD码46
运算后某位>9或半进位AC=1,必须立即DAA/DAS调整,否则报错。
- 两个 10 位压缩 BCD 数(共 5 字节),低位在前
- 相加后结果存入
THIRD
,最多 11 位(6 字节,含进位)
x86 提供的十进制调整指令:
DAA ------ 加法后把 AL 调整为压缩 BCD(仅影响 AL,且只对 ADD/ADC 有效)。
DAS ------ 把AL调整成合法BCD,并减6校正。
这两条指令只认 AL,因此循环必须以字节为单位处理。
ADD后必须紧跟DAA,中间不能插入任何改变AL或标志的指令。
若CF=1表示向高位产生了进位(100以上)
运行示例
include io32.inc
.data
FIRST DB 46H,58H,32H,71H,66H,0H
SECOND DB 75H,21H,49H,23H,82H,0H
THIRD DB 6 DUP(?)
.code
start:
MOV ECX,6
MOV ESI,0
CLC
LLO:
MOV AL,SECOND[ESI]
ADC AL,FIRST[ESI]
DAA
MOV THIRD[ESI],AL
INC ESI
LOOP LLO
exit 0
end start

调试器验证
MOV CX,6
MOV ESI,0
CLC

存储清零
MOV AL,SECOND[ESI]
ADC AL,FIRST[ESI]
DAA
MOV THIRD[ESI],AL
INC ESI
LOOP LLO
第一次循环相加中出现了进位CF=1

第一次循环结束后cx-1,EIP进入第二次循环位置
此后循环依次执行,我们发现该代码并没有直接退出循环,问题为ECX≠0,我们只把CX置0,修改后程序正常6此后退出循环,得到我们相加后的数值
最终结果为014894818021,验证成功
3. 二进制乘法
(1)选择相应的数据存放于内存单元中。为了考查多种情况,数据选择要合理。对于8位无符号数应使其在0~255的范围内,并使得积大于8位无符号数的范围;对于16位有符号数的选择应兼顾到正、负数的情况。注意乘法指令的选择要考滤有符号乘法和无符号乘法的不同情况;有符号乘数如果为负数,则数据在内存中以二进制补码形式存放。
知识回顾
乘法指令一览
无符号
MUL r/m8 ; AX ← AL * r/m8
MUL r/m16 ; DX:AX ← AX * r/m16
MUL r/m32 ; EDX:EAX ← EAX * r/m32
有符号
IMUL r/m8 ; AX ← AL * r/m8
IMUL r/m16 ; DX:AX ← AX * r/m16
IMUL r/m32 ; EDX:EAX ← EAX * r/m32
规则:
- 源操作数 × 累加器(AL/AX/EAX)→ 双倍宽度结果。
- 结果高半部永远放在 AH/DX/EDX,低半部放在 AL/AX/EAX。
- CF/OF 置位表示"高半部非 0(无符号)或符号扩展失败(有符号)",其他标志未定义。
8 位无符号要点
被乘数必须预先装 AL,乘数可为内存字节或寄存器。
结果 16 位,在 AX 中,直接 mov [mem],ax 即可。
16 位带符号要点
被乘数必须预先装 AX,乘数可为内存字或寄存器。
结果 32 位,高 16 位在 DX,低 16 位在 AX。由于 Win32 内存按 32 位对齐方便,可一次把 DX:AX 存到连续 4 字节。
符号扩展与高位丢弃若只关心完整 32 位结果,无需理会 CF/OF;若需 64 位结果,可用 32 位 IMUL 产生 EDX:EAX。
MASM32 内存声明字节用 db,字用 dw,双字用 dd;带符号十进制字面量直接写,如 -12345,MASM 会自动按 16 位补码编码。
运行示例
include io32.inc
.data
DATA1 DB 245
DATA2 DB 43
DATA3 DW ?
FIRST DW 6405H
SECOND DW 0FFFEH
THIRD DW ?
THIRD1 DW ?
.code
start:
MOV AL,DATA1
MUL DATA2
MOV DATA3,AX
MOV AX,FIRST
IMUL SECOND
MOV THIRD,AX
MOV THIRD1,DX
exit 0
end start

调试器验证
无符号数
MOV AL,DATA1
MUL DATA2

我们可以看出ax值正确

有符号乘法
MOV AX,FIRST
IMUL SECOND
我们可以看出结果正确
