32位汇编:实验5算数运算类指令使用
1. 二进制数运算
(1)题目的具体要求,将原始数据及存放结果的内存单元分别定义成字变量或字节变量。
(2)编辑程序,把编写好的源程序建立为汇编语言源程序文件并存盘。
(3)对源程序文件进行汇编、连接,且修改至无错误,然后运行程序。
(4)利用调试程序观察运行结果。如果有错,分析并找出错误原因,改写程序并重新调试。
运行示例
.386
.model flat, stdcall
option casemap:noneinclude windows.inc
include kernel32.inc
include user32.inc
include masm32.incincludelib kernel32.lib
includelib user32.lib
includelib masm32.lib.dataA DW 23579 B DW 7684 bC DB 0A5H D DB 34H E DW ? G DB ? fmt db "23579 + 7684 = %d", 13, 10db "0A5H + 34H = %02XH", 13, 10, 0buffer db 128 dup(0).code
start:MOV ax,AADD ax,BMOV E,axMOV AL,bCADD AL,D MOV G,AL;结果格式化输出movzx EAX,Gpush EAXmovzx EAX,Epush EAXpush offset fmtpush offset buffercall wsprintfadd esp,16push 0push offset captionpush offset bufferpush 0call MessageBoxApush 0call ExitProcesscaption DB "二进制运算结果",0end 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,6MOV ESI,0CLC
LLO: MOV AL,SECOND[ESI] ADC AL,FIRST[ESI] DAAMOV THIRD[ESI],ALINC ESILOOP LLOexit 0end 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.dataDATA1 DB 245 DATA2 DB 43 DATA3 DW ?FIRST DW 6405H SECOND DW 0FFFEH THIRD DW ?THIRD1 DW ?.code
start:MOV AL,DATA1MUL DATA2MOV DATA3,AX MOV AX,FIRSTIMUL SECONDMOV THIRD,AXMOV THIRD1,DXexit 0end start
调试器验证
无符号数
MOV AL,DATA1
MUL DATA2
我们可以看出ax值正确
有符号乘法
MOV AX,FIRST
IMUL SECOND
我们可以看出结果正确