汇编实验(第3版)实验十:编写子程序
问题
显示字符串是现实工作中经常要用到的功能,应该编写一个通用的子程序来实现这个功能。我们应该提供灵活的调用接口,使调用者可以决定显示的位置(行、列)、内容和颜色。
提示
(1) 子程序的入口参数是屏幕上的行号和列号,注意在子程序内部要将它们转化为显存中的地址,首先要分析一下屏幕上的行列位置和显存地址的对应关系:
(2) 注意保存子程序中用到的相关寄存器:
(3) 这个子程序的内部处理和显存的结构密切相关,但是向外提供了与显存结构无关的接口。通过调用这个子程序,进行字符串的显示时可以不必了解显存的结构,为编程提供了方便。在实验中,注意体会这种设计思想。
子程序描述
名称:show_str
功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串。
参数:(dh)=行号(取值范围024),(dl)=列号(取值范围079),
(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: …
…
…
code ends
end start
思想:
题目说到子程序,则我们需要利用子程序来编写满足题目要求,即大部分实现出来的东西我们都需要子程序实现。用到子程序,则我们需要跳进子程序里面然后能够在执行完以后又能够走出子程序继续执行外部程序,这里我们就需要用到保护现场和恢复现场的知识——栈,那我们怎么通过外部程序进入子程序呢,可以用到转移地址指令–call指令,同时要能够找到出口继续执行外部程序,则我们需要配套使用另一个转移地址指令–ret指令(只把地址放入栈中,不转移cs,但retf和call word ptr指令是cs:ip都要放入栈中)
进入子程序中之后,接下来是实现题目要求的内容,即是在子程序中实现在指定的位置,用指定的颜色,显示一个用0结束的字符串。
接下来我们讲一讲怎么实现这个功能
首先:我们得清楚80(80个字符)25彩色字符模式显示缓冲区共有32kb的空间,向这个地址空间写入数据,写入的内容将立即出现在显示器上。对应的可以在显示器上显示25行,每行可以显示80个字符。
这样,
一个字符在显示缓冲区中就要占两个字节(这里有一个有趣的东西是在显示缓冲区一个字符占两个字节,而在data段中一个字节即是一个字节(因为题目就是按字节定义的),这两个地方的不同之处得分清楚,这是我理解的,若有不对请帮我指正,感谢!)
,分别存放字符的ASCII码和属性即00(DOSbox中一个字节由两个0构成)
其中我们需要注意的是在显示缓冲区中,偶地址存放字符的ASCII码,低地址存放的是字符的颜色属性(记得分清楚,很多时候进行列变化的时候容易把偶地址和奇地址里面的东西覆盖没了。
首先,彩色字符显示缓冲区是从地址B800h开始(在DOSbox中直接将地址赋予寄存器时,不能以字母开头,应该在字母的前面加上一个0才能够编译成功。),所以我们得先拿一个段关联这段内存,这里我们用es来关联0B800h,
然后还得用另一个段es来关联题目要求的已知字符。
接下来是存取问题,题目已经要求在显示器第8行3列显示该字符串,所以我们可以用一个寄存器指向第一行第一个字符,然后再通过计算1607即可跳转到第八行,然后就是列变化,这个有点特殊,因为一个字符占据两个字节,然后偶字节存放字符ASCII码,奇字节存放了属性,所以在进行列变化时,必须两个字节两个字节变化,而不能逐个变化,所以列变化用另一个寄存器来变化(因为行变化和列变化变化不一样)
因为题目说得用0作为内存中字符存储的结尾,所以我们可以联想到利用jcxz指令(判断cx是否为0,若为0,则跳转到ok标号的位置继续完成ok标号下的代码,所以可以把一次字符传递给寄存器cx然后判断cx是否为0,以此来跳出一直输出字符的代码(即是loop循环,之所以需要jcxz指令跳出,是因为把字符传递给cx后,cx-1一般不为0,不用jcxz判断cx存放的0字符的话跳不出循环),值得注意的是,题目中给定了cl存了颜色,可以到时候把cl的存储的数放进其他的寄存器。这里我们可以看到题目中同样给定的dh,dl,在实现行和列变化后,可以将行的地址和列的地址加起来放入一个寄存器(bx)中存储起来,然后就可以将dx空闲出来了,可以把cl中存储的值放进dx中。
然后还用到一个如di寄存器来进行在显示器上列变化,一次变化两个字节。
(附注:我们进行换行时需要用到乘法指令,同样分为16位乘和32位乘法,16位乘法的积放入ax中,32位乘法的积低位放入ax,高位放入dx。)
具体代码实现如下:
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 bx,0
mov di,0
mov ax,0B800h
mov es,ax
dec dh
mov al,dh
mov ah,0 ;保证ax中只有一个完整数
mov dh,160
mul dh
mov bx,ax
dec dl
mov al,dl
mov ah,0 ;保证ax中只有一个完整数
mov dh,2
mul dh
add bx,ax
mov dl,cl
s: mov cl,ds:[si]
mov ch,0 ;保证cx中只有一个完整数
jcxz ok
mov es:[bx+di],cl
mov es:[bx+di+1],dl
inc si
add di,2
loop s
ok: ret
code ends
end start