王爽老师的汇编语言实验九主要是对循环的使用。
实验九 根据材料编程
给朋友们放上原题的描述哈:
*注:以上图片均来源于 《汇编语言》 – 王爽 P175 - 177
Solution
在此实验之前,原书第九章对各种转移指令进行了解析。刚看到这道题时还是有点压力的,放眼望去整整两篇描述,比起一般的算法题,这个篇幅确实有点吓人了,别慌,逐步分析之后本题难度也不是很高(指完成本题,笔者学识浅薄,可能没能领悟编者的深意)。
首先书中介绍了 80 x 25 彩色字符模式的显示缓冲区。
一块缓冲区大致就是这么个模样(这样的显示区分为8页,每页4KB,显示器可以显示任意一页的内容,一般情况下,显示第0页的内容,如图),每一行能显示八十个字符,每个字符需要占用两个字节,分别存放字符的ASCll 码和属性,因此每一行占用160个字节,显示区共25行,因此可以将这个缓冲区抽象成一个二维数组,二维数组中,根据每个元素的行列信息,可以计算从首元素到该元素一共占用的内存空间(字节数)。
题目的要求是在第据此,若想大致的将字符显示在显存中间,需要稍微进行计算:
- 行:一共25行,25 / 2 = 12行(向下取整)
- 列:要显示的字符串 ‘welcome to masm!’ 一共16字节,每一行可以显示160字节,所以第一个元素 w 的位置: 第(160 - 16) / 2 = 72字节。
可以计算得到首个字符在显示区中的偏移:
第 12 * 160 + 72 = 1992 = 07C8H 字节处。
显示一行字符串! welcome to masm!
将待打印的字符串放到数据段,为后续拷贝到显存处做准备:
data segment
db 'welcome to masm!'
data ends
ds段寄存器记录待拷贝的字符串,es段寄存器存入显存首地址B800H,在汇编中表示地址不可以以字母起始,所以需要添上一个0 -> 0B800H。
start: mov ax, data
mov ds, ax ;ds字符串区
mov ax, 0B800H ;显存地址
mov es, ax ;显存段基址
因为连续空间的字符串拷贝,都是相同的工作,因此使用循环是一个明智的选择,值得思考的是:完成拷贝循环多少次?
因为每个字符占用两个字节,低位是ASCll 码,高位存放属性,刚好是一个16位寄存器可以装下的,所以这里的安排是:寄存器bx低位bl存放字符,高位bh存放属性。
mov cx, 16 ;循环拷贝16次
mov di, 12 * 160 + 72 ;找到中间位置
mov si, 0 ;字符串区偏移
完整代码
assume cs:code, ds:data
data segment
db 'welcome to masm!'
data ends
code segment
start: mov ax, data
mov ds, ax ;ds字符串区
mov ax, 0B800H ;显存地址
mov es, ax ;显存段基址
mov cx, 16 ;循环拷贝16次
mov di, 12 * 160 + 72 ;找到中间位置
mov si, 0 ;字符串区偏移
s0: mov al, ds:[si]
mov ah, 01110001B ;设置字体属性: 白底蓝字
mov es:[di], ax ;写入到显存
inc si ;字符串向后偏移1
add di, 2 ;显存写入位置向后偏移两个字节
loop s0
mov ax, 4c00h
int 21h
code ends
end start
运行展示:
有了一次输出的经验,对于输出三种不同样式的字符串那还不是小菜一碟?(手动滑稽)
直接上来就是无脑循环接循环… …
assume cs:code, ds:data
data segment
db 'welcome to masm!'
data ends
code segment
start: mov ax, data
mov ds, ax
mov ax, 0b800h
mov es, ax
mov cx, 16 ;16个字母,拷贝十六次
mov si, 0 ;显存偏移
mov di, 12 * 160 + 80;显存首字符放置处
s: mov al, ds:[si]
mov ah, 01110001B ;白底蓝字
mov es:[di], ax
inc si
add di, 2
loop s
mov cx, 16
mov si, 0
mov di, 13 * 160 + 80
s0: mov al, ds:[si]
mov ah, 11000010B
mov es:[di], ax
inc si
add di, 2
loop s0
mov cx, 16
mov si, 0
mov di, 14 * 160 + 80
s1: mov al, ds:[si]
mov ah, 00000111B
mov es:[di], ax
inc si
add di, 2
loop s1
mov ax, 4c00h
int 21h
code ends
end start
运行效果
本次实验并没有用到最近学到的知识,更待后续钻研。