检测点 14.2
编程,用加法和移位指令计算 (ax)=(ax)*10。提示,(ax)*10=(ax)*2+(ax)*8。
具体代码如下:
1. 说明:AX 寄存器存储的初始数据不得超过 6553,因为 6553×10=65530 是 16 位寄存器 AX 能进行乘 10 计算后所存储的最大值,超过后就会由于 AX 寄存器的存储容量不足而导致溢出,所得的结果将是错误的。例如若计算 6554×10,结果为 65540,该数值在 AX 寄存器中实际存储的是其补码,即十进制的数值 4,因为 AX 寄存器最大能存储的无符号数为 65535。本程序代码只有计算功能,需通过 Debug 运行才能观察计算结果。
assume cs:code
code segment
start: ; 计算 (ax)×10
mov bx,53
mov ax,bx
mov cl,1
shl ax,cl
mov cl,3
shl bx,cl
add ax,bx ; 计算的最终结果存储在 AX 寄存器中
mov ax,4c00h
int 21h
code ends
end start
2. 说明:AX 寄存器存储的初始数据不得超过 6553,因为 6553×10=65530 是 16 位寄存器 AX 能进行乘 10 计算后所存储的最大值,超过后就会由于 AX 寄存器的存储容量不足而导致溢出,所得的结果将是错误的。例如,若计算 6554×10,结果为 65540,该数值在 AX 寄存器中实际存储的是其补码,即十进制的数值 4,因为 AX 寄存器最大能存储的无符号数为 65535。本程序代码既有计算功能,也有在屏幕显示计算结果的功能。
assume cs:code
data segment
db "6553 x 10 = ",0
data ends
; 由于 16 位寄存器 AX 能存储的最大无符号数是 65535,这是个 5 位数字,所以用于存储 5 位十进制数字的字符串空间只需 6 个字节(其中最后一个字节用于存储字符串结束符 0)。
stda segment
db 6 dup (0)
stda ends
code segment
start: mov ax,data
mov ds,ax
mov si,0 ; 令 ds:si 指向 data 段字符串的首地址
mov bx,2 ; (bh)=0,设置显示缓冲区第 0 页;(bl)=2,设置字符属性为绿色
mov dh,5 ; 在 BH 寄存器中存放行号
mov dl,12 ; 在 DL 寄存器中存放列号
call show
; 计算 (ax)×10
mov bx,6553
mov ax,bx
mov cl,1
shl ax,cl
mov cl,3
shl bx,cl
add ax,bx ; 计算的最终结果存储在 AX 寄存器中
; 转换 AX 寄存器中存储的数值数据为数字字符,并存入 stda 段中。
mov bx,stda
mov ds,bx
mov si,0 ; 令 ds:si 指向 stda 段字符串的首地址
call dtoc
mov si,0 ; 重置 SI 寄存器存储 0 值,以令 ds:si 指向 stda 段字符串的首地址
mov bx,2 ; (bh)=0,设置显示缓冲区第 0 页;(bl)=2,设置字符属性为绿色
call show
mov ax,4c00h
int 21h
; begin show
; show 子程序:通过调用 BIOS 的 int 10h 指令中断例程的 2 号和 9 号子程序,在屏幕输出计算结果
show: mov ah,2 ; 设置 int 10h 指令中断例程的子程序号为 2,功能为“设置光标位置”
int 10h
mov ah,9 ; 设置 int 10h 指令中断例程的子程序号为 9,功能为“在光标位置显示字符”
mov al,[si] ; 字符
cmp al,0
je showret ; 判断当前需要显示的字符是否为字符串的结束符 —— ASCII 码值为 0
mov cx,1 ; 字符重复个数
int 10h
inc si ; ds:si 指向下一个字符
inc dl ; 列数自增 1,在下一列输出字符
jmp short show
showret: ret
; end show
; begin dtoc
; 参数:(ax) = 数值数据的低 16 位 (dx) = 数值数据的高 16 位 (cx) = 转换用到的除数值 10 ds:si 指向字符串首地址
; 功能:将提供的数值参数(被除数)转换为字符数字字符串形式,并将转换后的数字字符串存储到 ds:si 指向的段字符串中。
; 说明:转换成的字符数字字符串表示的如果是 10 进制数字,则提供的 (cx) 参数为十进制数值 10;如果是 8 进制数字,则 (cx) 参数为十进制数值 8;如果是 2 进制数字,则 (cx) 为十进制数值 2;如果是 16 进制数字,则 (cx) 为十进制数值 16。
dtoc: push dx ; 将显示字符串的屏幕行、列信息压入栈中
mov dx,0 ; 设置高 16 位被除数 0,并存入 DX 寄存器 —— 低 16 位则在 AX 寄存器中
push dx ; 将数值 0 压栈,以作为 ok 标号处进行原序复制数字字符串的末尾结束符,以界定数字字符串的长度。在本子程序的 over 标号处将会通过 pop 操作将该 0 值出栈到 CX 寄存器
mca: cmp ax,0 ; 判断被除数的低 16 位值是否为 0
je mcd ; 如果为 0,则跳至标号 mcd 处,以对高 16 位值是否为 0 作进一步判断;如果不为 0,则跳至 cd 标号处调用 divdw 子程序
cd: mov cx,10 ; 设置求余时要用到的除数数值 10,并存入 CX 寄存器
call divdw ; 调用 divdw 子程序,解决“除法溢出”问题,并获得存储在 CX 寄存器的余数
add cx,30h ; 将数值转换为字符
push cx ; 将数值的数字字符压栈
jmp mca ; 跳至 mca 标号处,对下一轮除法运算的被除数进行是否为 0 的判断
mcd: cmp dx,0 ; 判断被除数的高 16 位值是否为 0
je over ; 如果为 0,则跳至 over 标号处,以恢复数字字符串正常顺序;如果不为 0,则通过其后的 jmp 指令跳至 cd 标号处,调用 divdw 子程序
jmp cd
over: ; 将倒序显示的数字字符串恢复为原来数值的数字字符串正常序列
pop cx ; 将数值的数字字符出栈存入 CX 寄存器
jcxz ok ; 如果出栈字符的码值为 0,则说明数值的数字字符串已全部出栈完毕,应该跳至 ok 标号处返回主调程序
mov [si],cl ; 将 CL 寄存器中存储的字符转存入目标位置 ds:si;注意,不是 CX 寄存器
inc si ; si 后移 1 位,指向 stda 段下一个字符的存储位置
jmp over; 跳至 over 标号处继续出栈操作,再次判断出栈字符的 ASCII 码值是否为 0
ok: pop dx
ret
; end dtoc
; begin divdw
; 参数:(ax) = 被除数的低 16 位 (dx) = 被除数的高 16 位
(cx) = 除数
; 返回:(ax) = 商数的低 16 位 (dx) = 商数的高 16 位
(cx) = 余数
divdw: ; 执行高 16 位被除数与 16 位除数的除法运算
mov bx,ax ; 将被除数的低 16 位暂存到 BX 寄存器
mov ax,dx ; 将被除数的高 16 位转存到 AX 寄存器
mov dx,0 ; 设置原被除数高 16 位进行的除法运算所需要使用的高 16 位被除数
div cx ; 执行高 16 位除法运算,计算结果的余数默认存储于 DX 寄存器
push ax ; 将高 16 位除法运算所得的商数入栈存储,将由下面的 pop dx 指令出栈转存
; 执行低 16 位被除数与 16 位除数的除法运算
mov ax,bx ; 将被除数的低 16 位传送回 AX 寄存器
div cx ; 执行低 16 位除法运算,计算结果的商数默认存储于 AX 寄存器
mov cx,dx ; 将低 16 位除法运算所得余数(也是最终除法运算的余数)转存到 CX 寄存器
pop dx ; 将高 16 位除法运算所得的商数出栈转存到 DX 寄存器
; 经 divdw 子程序上述操作后,AX、DX 和 CX 寄存器分别存储着商数的低 16 位、高 16 位和余数
ret
; end divdw
code ends
end start