# 实验10

实验10

  1. 显示字符串
  • 问题
    显示字符串是现实工作中经常要用到的功能,应该编写一个通用的子程序来实现这个功能、我们应该提供最灵活的调用接口,使调用者可以决定显示的位置(行和列)、内容和颜色

  • 子程序描述
    名称:show_str
    功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串。
    参数:(dh)= 行号(取值范围0~24),(dl)= 列号(取值范围0-79),
    (cl)= 颜色, ds : si 指向字符串的首地址
    返回:无
    应用举例:在屏幕的8行3列,用绿色显示data段中的字符串

assume cs:code
data segment
db 'welcome to masm!',0
data ends

code segment
start:
mov dh,8
mov dl,3
mov cl,2
mov ax,data
mov ds,ax
mov si,0
call show_str

mov ax,4c00h
int 21h

show_str:
mov ax,0b800h
mov es,ax
mov al,160; 一行160个字节
dec dh
mul dh; 计算所在行数
mov dh,0; 因为dl存着列数,另一个加数一个字大小,故将dl变为一个字
push ax
mov ax,0
mov al,2
dec dl
mul dl; 计算所在列的下标
mov dx,ax
pop ax
add ax,dx; 相加得到开始所在下标
mov bx,ax; 将偏移地址传给bx
mov ah,cl; 将颜色传给ah。因为cx要存储循环次数
mov cx,16; 共16个字符
s:
push cx; 因为cx要用作判断,故先要保存cx的值
mov ch,0
mov cl,[si]
jcxz ok; 判断循环结束条件,当前字符是否为0
mov al,[si]
mov es:[bx],ax; 只需要将ax低位不断改变即可,因为颜色不变
add bx,2
inc si
pop cx
loop s
ok:
ret

code ends
end start
  1. 解决除法溢出的问题

前面讲过,div指令可以做除法。当进行8位除法的时候,用al存储结果的商,ah存储结果的余数;进行16位除法的时候,用ax才能出结果的商,dx存储结果的余数,可是,现在有一个问题,如果结果的商大于alax所能存储的最大值,那么将如何?

比如下面的程序段:

Mov bh,1

Mov ax,1000

Div bh

进行的是8位除法,结果的商为1000,而1000al中放不下。

又比如,下面的程序段:

Mov ax,1000h

Mov dx,1

Mov bx,1

Div bx

进行的是16位除法,结果的商为11000h,而11000hal中放不下。

​ 我们在用div的指令做除法时,很可能会发生上面的情况:结果商过大,超出了寄存器所能存储的范围。当cpu执行div等除法指令的时候,如果发生这样的情况,将引发cpu内部的一个错误,这个错误被称为:除法溢出。我们可以通过特殊的程序来处理这个错误,但在这里我们不讨论这个错误的处理,这是后面的程序所涉及到的内容。

好了,我们已经清楚了问题的所在:用div指令做除法的时候可能会产生除法溢出。由于有这样的问题,在进行除法运算的时候要注意除数和被除数的值,比如1000000/10就不能用div指令来计算。那么怎么办呢?我们用下面的子程序divdw解决。

子程序描述

名称:divdw

功能:进行不会溢出的除法运算,被除数为dword型,除数为word型,结果为dword

参数:(ax=dword型数据的低16

dx=dword型数据的高16

cx=除数

返回:(dx=结果的高16位,ax=结果的低16

cx=余数

​ 应用举例:计算1000000/10F4240H/0AH

Mov ax,4240h

Mov dx,000fh

Mov cx,0ah

Call divdw

​ 结果:dx=0001hax=86a0hcx=0

提示:

给出一个公式:

X:被除数,范围:[0ffffffff]

N:除数,范围:[0ffff]

H:X16位,范围:[0ffff]

L:X16位,范围:[0ffff]

Int():描述性运算符,取商,比如,int38/10=10

Rem():描述性运算符,取余数,比如,rem38/10=8

公式:X/N=int(H/N)*65536+[rem(H/N)*65536+L]/N

assume cs:code
code segment
start:
mov ax,4240h
mov dx,000fh
mov cx,0ah
call divdw

mov ax,4c00h
int 21h

divdw:
mov bx,ax; bx寄存器空着,因为要做除法,会占用ax,故先将ax的值保存
mov ax,dx; 要做高位除法,将被除数先存进ax中
mov dx,0; dx清零,保存余数
div cx; 进行(H/N)
push ax; 将商的高16位保存,因为还有一次除法,dx正好可以直接用到,因为*65536相当于左移16位,低16位加上L正好组成被除数
mov ax,bx; bx就是低16位
div cx
mov cx,dx; dx存的余数,赋值给cx
pop dx; dx存的商高16位
; ax正好已经存了商的低16位
ret; 返回


code ends
end start
  1. 问题:
    编程,将data段中的数据以十进制的形式显示出来。
    data segment
    dw 123,12666,1,8,3,38
    data ends

子程序描述:
名称:dtoc
功能:将word型数据转变为表示十进制数的字符串,字符串以0位结尾符。
参数:(ax)=word型数据 ds:si指向字符串的首地址
返回:无
应用举例:编程,将数据12666以十进制的形式在屏幕的8行3列,用绿色显示出来。在显示时我们调用本次实验中的第一个子程序show_str。

assume cs:code

data segment
db 10 dup(0)
data ends

code segment
start:
mov ax,12666
mov bx,data
mov ds,bx
mov si,0
mov bx,0
call dtoc

mov dh,8
mov dl,3
mov cl,2
call show_str

mov ax,4c00h
int 21h

dtoc:
mov dx,0; 除法dx保存高16位,必须清零,不然程序会出错,具体表现为不知道会跳转到哪里
mov cx,10; 除数
div cx
add dx,30h; 存该数字代表的ASCII码
mov [bx],dx; 存进data段中
mov cx,ax; 判断商是否为0,需要借助cx判断
add bx,2; 因为商是一个字大小,所以要加2个字节
inc si; 存一共多少位
jcxz ok
jmp dtoc

show_str:
mov cx,si; si存着数字一共多少位
mov di,cx; 因为要进行两次循环,si后面会用到,所以用di存
mov al,dh; 做乘法,获得该行所在的下标
dec al
mov ah,160
mul ah
mov bx,ax
mov al,dl; 做乘法,获得列所在的下标
mov ah,2
mul ah
add bx,ax; 行加列,获得下标
mov ax,0b800h; 显存段号
mov es,ax
mov si,0; 存储data段偏移量

s1:; 因为data段的结果与实际结果相反,故可以存入栈中再输出,得到正确的顺序
mov al,[si]
mov ah,2; 组合,低位ASCII码,高位颜色
push ax
add si,2
loop s1

mov cx,di; 将数字的位数传给cx
s2:
pop ax
mov es:[bx],ax; 赋值给显存,bx存着之前算得的下标
add bx,2
loop s2
ret

ok:
ret

code ends
end start
posted @   三年、  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App
点击右上角即可分享
微信分享提示