本文主要是介绍用8086汇编语言写新春祝福,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
本篇目录
一、前言
1.创作背景
2.最终效果
3.必要的准备
二、实现步骤
1.程序框架
2.使程序暂停一段时间的子程序
3.显示一朵烟花的子程序
(1)参数
(2)地址转换
(3)显示花柄
(4)清除花柄
(5)显示花瓣
3.显示一段字符的子程序
(1)参数
(2)显示字符
三、完整代码
一、前言
1.创作背景
马上就要到龙年春节了,作为热爱编程的人,我决定动手写个8086汇编语言的小程序作为新春祝福,顺道也复习一下汇编语言。本文一步步展示了程序的实现过程,最终效果如下,完整代码放在最后。
祝大家龙年大吉,健康快乐! 2024.2.1
2.最终效果
(烟花及标语都是动态的,但是在CSDN传视频比较麻烦,担心视频失效,这里就先放张图片。)
新春祝福-8086汇编语言
3.必要的准备
硬件和软件
我的电脑是64位win10系统,软件用的是VSCode(使用教程见 《使用VSCode编写汇编程序》 )。
也可以用记事本写代码,然后在DosBox中运行,也一样。只要能运行汇编程序应该都可以。
编程基础
主要涉及8086汇编语言的字符显示、子程序设计、循环语句等内容。目前汇编方面我学过《汇编语言》(王爽)教材,足够完成这个小项目了。
二、实现步骤
1.程序框架
这个程序的主要功能,是在屏幕指定位置动态地显示绽放的烟花以及祝福标语。烟花不只一朵,因此就写一个子程序show_fireworks来实现“显示一朵烟花”的功能,然后在主程序中多次调用,只要设置不同的参数即可。祝福标语的显示也写一个子程序show_area来实现。这个显示标语的功能,其实就是在指定的屏幕起始位置,按照指定的行数、列数来显示指定的字符串,这是比较通用的功能,所以单独写一个子程序来方便以后使用。最后,要想实现动态显示的效果,就要让程序在显示完一个字符后停顿一定的时间,因此还要写一个show_pause子程序来实现这个功能。
由此得程序的整体框架如下。
assume cs:codd,ss:stack,ds:datadata segment ;数据段dw 0
data ends stack segment ;栈段 code segment ;代码段start: ;程序入口call show_fireworks ;在屏幕指定位置显示一朵烟花call show_area ;在屏幕指定位置显示祝福标语mov ax,4c00H ;程序返回
int 21Hshow_fireworks: ;在屏幕指定位置显示一朵烟花show_area: ;在屏幕指定位置显示祝福标语show_pause: ;让程序暂停一会儿code ends
end start
2.使程序暂停一段时间的子程序
由简入繁,先来写这个最简单的子程序。需要的参数只有一个,就是想要暂停的时间,用cx来传递。这里的程序暂停,是依靠循环执行nop指令来实现的。我是用内外双循环来实现这个功能的,这样相当于可以实现O(n^2)的暂停时间(暂停时间与参数的平方成正比)。只用单层循环来实现也没问题。代码如下。
;功能:产生一段时间的暂停(时间与参数的平方成正比)
;参数:cx 暂停的时间
;返回:无
show_pause: ;子程序show_pause开始push cx ;将用到的寄存器压入栈push axmov ax,cx ;将接收的时间参数传送给ax,以ax为内层循环的次数show_pause_s:push cx ;将外层循环的次数压入栈mov cx,ax ;设置内层循环的参数show_pause_s0:nop ;空指令,产生一段时间的暂停loop show_pause_s0 ;内层循环pop cx ;将外层循环的次数pop出栈loop show_pause_s ;外层循环pop ax ;将用到的寄存器pop出栈pop cxret ;show_pause子程序返回
3.显示一朵烟花的子程序
(1)参数
在这个程序中,所有烟花的结构都是相同的,由花柄和8个花瓣组成。构成烟花的字符、字符属性、花柄的长度、花瓣的长度、烟花绽放的速度、指定的显示位置等都是子程序的参数。由于参数比较多,用寄存器来传递参数不太现实,因此就在数据段中存储参数,然后把参数列表的初始地址ds:si 作为参数传递给子程序就可以了。在数据段的参数列表中存储的参数,相当于是这些参数的初始值。
一些烟花通用的参数,比如字符属性,用data段中的标号show_fireworks来标识。另外,烟花的花瓣是要从花心出发向周围八个方向伸展,因此在数据段中要存储字符的偏移地址相对于花心的位移量,这部分数据用标号show_fireworks_addr来标识。当然也可以改变花瓣的形状,我这里只是画出了比较经典的八瓣花形状,相对花心位置的偏移量我是用Excel计算的。
因为要显示多朵烟花,所以,在主程序中进行调用的时候,可以设置不同的参数,这样就可以让烟花具有不同的颜色、大小、绽放速度、构成字符等属性,视觉效果相对丰富一些。
以下是data段(数据段)中show_fireworks子程序的参数列表。
data segment;show_fireworks子程序的参数列表;地址 参数含义; 00 屏幕起始显示位置的行号; 01 屏幕起始显示位置的列号; 02 烟花花柄长度(>1); 03 字符的属性字节; 04 用于构成烟花的单个字符; 05 烟花花瓣的长度; 06 显示花径每个字符(或花瓣每一组8个字符)后停顿的时间 低字节; 07 显示花径每个字符(或花瓣每一组8个字符)后停顿的时间 高字节; 08 清除花径每个字符后停顿的时间 低字节; 09 清除花径每个字符后停顿的时间 高字节; 0A-2AH 以字单元依次指明要显示字符的相对地址,8个为一组args_show_fireworks db 15H,12H,0BH,04H,'*',5H,0B0H,0H ;00-07字节db 0C0H,00H ;08-09字节args_show_fireworks_addr dw 0FF60Hdw 0FF62H,4H,0A2H,0A0H,9EH,0FFFCH,0FF5EH ;第一层(最靠近花心)dw 0FEC0H,0FEC4H,8H,144H,140H,13CH,0FFF8H,0FEBCH ;第二层dw 0FE20H,0FE26H,0CH,1E6H,1E0H,1DAH,0FFF4H,0FE1AH ;第三层dw 0FD80H,0FD88H,10H,288H,280H,278H,0FFF0H,0FD78H ;第四层dw 0FCE0H,0FCEAH,14H,32AH,320H,316H,0FFECH,0FCD6H ;第五层data ends
(2)地址转换
花柄要从指定的行号和列号处开始显示,因此这里首先要做的一步工作就是,将指定的行号和列号转换为显示缓冲区的偏移地址。这个功能很常用,接下来的show_area(显示祝福标语)子程序中也会用到。但是这步转换一共只需要八行代码,为此写一个子程序实在不值当,毕竟汇编语言的子程序还要写一堆push 和pop。代码如下。
;设置es段指向显示缓冲区段mov ax,0B800H mov es,ax;设置es:di指向显示缓冲区指定显示位置的首地址mov al,0A0H ;每行80个字符,每个字符占2字节,所以每行共80*2=160=A0H个字节mul byte ptr ds:[si+0] ;计算指定行的起始地址(al * 行号 → ax)mov di,ax ;将乘法结果存放在di中(di = al * 行号)mov al,02H ;每个字符占2个字节mul byte ptr ds:[si+1] ;计算目标行在指定位置前的字节数(al * 列号 → ax)add di,ax ;计算指定显示位置在es段的偏移地址(di + ax → di)
(3)显示花柄
在这个程序中,花柄是竖直向上、逐个字符显示的。长度、颜色等由参数指定,参数要从data段的参数列表中获取。每显示一个字符后,就调用show_pause子程序来让程序停顿一会儿,这样就有了动态效果。
这里还有一个小细节。在完成行号、列号→偏移地址的转换后,es:di指向屏幕指定的起始显示地址。之后,在显示花柄的过程中会改变di的值。但是接下来清除花柄的过程,还要从起始位置开始进行,因此这个起始位置是要保留的,程序中就用bx=di来暂存。
显示花柄的代码如下。
;显示烟花的花柄mov al,ds:[si+4] ;获取参数:构成烟花的单个字符mov ah,ds:[si+3] ;获取参数:字符的属性字节mov bx,di ;使用bx暂存di,es:di指向显示起始位置mov ch,0 ;获取参数:花柄长度mov cl,ds:[si+2] ;以花柄长度作为循环次数show_fireworks_s1: ;显示花柄mov es:[di],al ;将字符传送到显存区mov es:[di+1],ah ;将字符的属性字节传送到显存push cxmov cx,ds:[si+6] ;设置程序暂停子程序的时间参数call show_pause ;调用子程序,使程序暂停一会儿pop cxsub di,0A0H ;ds:di指向行号-1的地址loop show_fireworks_s1 ;循环结束,花柄显示完毕
(4)清除花柄
花柄在显示完成之后,还要从下到上逐渐消失,这是模拟真实烟花的效果。这其实是从下到上显示了一个空格字符。在花柄显示过程中,所用字符是从数据段的参数列表中获取的,而在花柄消失(清除花柄)的过程中,这个空格字符是在程序中定义的。
另外,烟花个人认为还是保留花心比较好看,因此在这个花柄消失过程中,就把花柄长度-1作为循环次数,这样就能保留花心。其余部分和上面显示花柄的代码基本相同。
在清除花柄的过程结束之后,es:di再次指向花心,这为接下来显示花瓣提供了便利。代码如下。
;清除烟花的花柄mov al,' ' ;要显示的字符是一个空格mov ah,0 ;字符的属性字节mov ch,0 ;获取参数:花柄长度mov cl,ds:[si+2] ;以花柄长度-1作为循环次数,保留花心dec clmov di,bx ;从bx中恢复di,es:di指向显示起始位置show_fireworks_s2: ;清除花柄mov es:[di],al ;将空格字符传送到显存区mov es:[di+1],ah ;将字符的属性字节传送到显存push cxmov cx,ds:[si+8] ;设置程序暂停子程序的时间参数call show_pause ;调用子程序,使程序暂停一会儿pop cxsub di,0A0H ;es:di指向行号-1的地址loop show_fireworks_s2 ;循环结束,花柄清除完毕;至此es:di指向花心
(5)显示花瓣
接下来是花瓣,这里用的全部是八瓣花,所以“8”这个数值是硬编码在程序中的。显示花瓣的策略有很多,我这里用的是内外双循环,外循环负责每次将花瓣(八个方向的)长度+1,内循环的循环次数为8,负责在8个方向各显示一个字符。内循环执行过程中不设置停顿,因此视觉效果就是,八个方向的花瓣是同时长度+1的。而外循环中包含一次对show_pause子程序的调用,视觉效果就是,烟花是从花心向外一层层逐渐绽放的(而不是一下子全都显示出来)。代码如下。
;显示烟花的花瓣(八瓣花)mov ah,ds:[si+3] ;获取参数:字符的属性字节mov al,ds:[si+4] ;获取参数:构成烟花的单个字符mov cl,ds:[si+5] ;获取参数:烟花花瓣的长度mov si,offset args_show_fireworks_addr ;获取参数:相对地址列表首地址mov ch,0 ;设置外层循环次数为花瓣的长度show_fireworks_s:push cx ;将外层循环次数压入栈mov cx,08H ;设置内层循环的次数为8,硬编码,因为是八瓣花show_fireworks_s0: ;内层循环,显示8个方向的字符mov bx,ds:[si] ;获取参数:显存区字符的相对地址mov es:[di+bx],al ;将字符传送到显存区mov es:[di+bx+1],ah ;将字符的属性字节传送到显存区add si,2 ;ds:si指向下一个字符显示的相对地址loop show_fireworks_s0 ;显示完8个方向的各一个字符push cxmov cx,0200H ;设置时间参数call show_pause ;调用子层序,使程序暂停一会儿pop cx pop cx ;将外层循环的循环次数pop出栈loop show_fireworks_s ;八瓣花显示完毕
3.显示一段字符的子程序
(1)参数
这个子程序的功能是,在指定的起始位置,按照指定的行数、列数(可以理解为要显示一个矩形区域的字符)显示一段字符,那么必然要指定起始起始位置的行号和列号、指定显示的行数和列数、要显示的字符及字符属性等。由此可知,这个子程序要用到的参数也比较多,那么同样我们就在data段中开辟一段空间来存放参数列表,然后把参数列表的首地址作为参数传递给这个子程序就可以了。
这个祝福标语,从视觉效果来看,显示的字符只有一行。但是,我用了橙底青字来突出显示,并且在祝福语周围加了一圈橙色边框(这是模拟在红纸上写祝福语,毕竟过春节嘛)。所以其实程序中要显示的字符行数为三行,只不过第一行和第三行显示的字符全都是空格。要显示的祝福语字符串,在data段(数据段)中用标号data_str来标识。
除了祝福标语外,我还在屏幕右下角显示了一行字符。个人认为,如果不写这行字符的话,整个画面会有点左右失衡,加上这么一行字符,视觉效果更好。字符的内容可以任意修改,不过要注意,在子程序中调用的时候,要设置好相应的行数、列数参数。要在右下角显示的字符串,在数据段中使用标号data_author来标识。
以下是数据段中show_area子程序的参数列表。
data segment;show_area子程序的参数列表;地址 参数含义; 00 屏幕起始显示位置的行号; 01 屏幕起始显示位置的列号; 02 待显示字符的行数; 03 待显示字符的列数; 04 待显示字符的属性字节; 05 显示一行后停顿的时间 低字节; 06 显示一行后停顿的时间 高字节; 07-0FH 备用args_show_area db 10H dup (0) ;show_area子程序 待显示的祝福标语字符串data_str db 26 dup (' ')db ' Happy Spring Festival! 'db 26 dup (' ');show_area子程序 待显示的作者信息data_author db 'Dr.Cheese 2024 'data ends
(2)显示字符
在开始显示字符之前,首先同样要将给定的起始行号、列号转换为显示缓冲区段的偏移地址,这一步在show_fireworks子程序中已经写过了,这里不再赘述。
接下来就要按照指定的行数、列数来显示字符了。我使用内外双循环来实现,外层循环每次负责显示一行字符,内层循环每次负责显示当前行中的一个字符。这里要注意的就是,循环次数使用cx寄存器来存放,因此设置内层循环的循环次数之前要先将外层循环的循环次数push压入栈,等内循环结束,再pop出来。
另外,我还在这个子程序中增加了一点动态效果,就是每显示完一行字符就调用show_pause子程序来使程序停顿一会儿,这样做的视觉效果就是,字符串(或者说矩形的字符区域)是由上到下逐渐展开的。(有点类似于PPt中的“展开”动画效果。)
这一部分的实现代码如下。
;通过循环来显示字符串mov ch,0 ;设置外层循环次数为待显示字符的行数mov cl,ds:[bx+2] show_area_s: ;外层循环开始push cx ;将外层循环次数压入栈mov ch,0 ;设置内存循环次数为待显示字符的列数mov cl,ds:[bx+3] show_area_s1: ;内层循环开始mov al,ds:[si] ;将ds:si指向的字节传送给es:dimov es:[di],al inc di ;es:di指向显存区下一个字节(当前字符的属性字节)mov al,ds:[bx+4] ;将字符的属性字节传送给es:dimov es:[di],alinc si ;ds:si指向下一个data段中要显示的字符inc di ;es:di指向显存区下一个字节(下一个字符的ASCII码字节)loop show_area_s1 ;内层循环mov cx,ds:[bx+5] ;设置程序暂停循环的次数call show_pause ;调用子程序产生一段时间的暂停add di,0A0H ;设置es:di指向显存区下一行mov al,ds:[bx+3] mov ah,0sub di,ax sub di,axpop cx ;将外层循环的次数pop出栈loop show_area_s ;外层循环
三、完整代码
最后是这个程序的完整代码,在本机测试通过。
;程序功能:在屏幕上显示烟花绽放动画及新春祝福标语
assume cs:code, ds:data ,ss:stack
stack segment ;定义栈段dw 32 dup (0)
stack endsdata segment ;定义数据段;show_fireworks子程序的参数列表;地址 参数含义; 00 屏幕起始显示位置的行号; 01 屏幕起始显示位置的列号; 02 烟花花柄长度(>1); 03 字符的属性字节; 04 用于构成烟花的单个字符; 05 烟花花瓣的长度; 06 显示花径每个字符(或花瓣每一组8个字符)后停顿的时间 低字节; 07 显示花径每个字符(或花瓣每一组8个字符)后停顿的时间 高字节; 08 清除花径每个字符后停顿的时间 低字节; 09 清除花径每个字符后停顿的时间 高字节; 0A-2AH 以字单元依次指明要显示字符的相对地址,8个为一组args_show_fireworks db 15H,12H,0BH,04H,'*',5H,0B0H,0H ;00-07字节db 0C0H,00H ;08-09字节args_show_fireworks_addr dw 0FF60Hdw 0FF62H,4H,0A2H,0A0H,9EH,0FFFCH,0FF5EH ;第一层(最靠近花心)dw 0FEC0H,0FEC4H,8H,144H,140H,13CH,0FFF8H,0FEBCH ;第二层dw 0FE20H,0FE26H,0CH,1E6H,1E0H,1DAH,0FFF4H,0FE1AH ;第三层dw 0FD80H,0FD88H,10H,288H,280H,278H,0FFF0H,0FD78H ;第四层dw 0FCE0H,0FCEAH,14H,32AH,320H,316H,0FFECH,0FCD6H ;第五层;show_area子程序的参数列表;地址 参数含义; 00 屏幕起始显示位置的行号; 01 屏幕起始显示位置的列号; 02 待显示字符的行数; 03 待显示字符的列数; 04 待显示字符的属性字节; 05 显示一行后停顿的时间 低字节; 06 显示一行后停顿的时间 高字节; 07-0FH 备用args_show_area db 10H dup (0) ;show_area子程序 待显示的祝福标语字符串data_str db 26 dup (' ')db ' Happy Spring Festival! 'db 26 dup (' ');show_area子程序 待显示的作者信息data_author db 'Dr.Cheese 2024 'data endscode segment ;定义代码段
start: ;程序入口;设置段地址mov ax,stack ;设置SS:SP指向栈顶mov ss,axmov sp,40Hmov ax,data ;设置ds指向data段mov ds,ax ;设置参数,并调用show_fireworks子程序,显示第一朵烟花mov si,offset args_show_fireworks ;ds:bx指向show_fireworks子程序的参数列表首地址mov byte ptr ds:[si+0],16H ;设置参数:烟花显示起始位置行号mov byte ptr ds:[si+1],11H ;设置参数:烟花显示起始位置列号mov byte ptr ds:[si+2],0BH ;设置参数:烟花花柄长度mov byte ptr ds:[si+3],03H ;设置参数:字符属性字节,青色mov byte ptr ds:[si+4],'z' ;设置参数:构成烟花的单个字符mov byte ptr ds:[si+5],05H ;设置参数:花瓣的长度add word ptr ds:[si+6],0001H ;设置参数:显示花径每个字符(或花瓣每一组8个字符)后停顿的时间add word ptr ds:[si+8],0001H ;设置参数:清除花径每个字符后停顿的时间call show_fireworks ;调用子程序显示一朵烟花;设置参数,并调用show_fireworks子程序,显示第二朵烟花mov si,offset args_show_fireworks ;ds:bx指向show_fireworks子程序的参数列表首地址mov byte ptr ds:[si+0],12H ;设置参数:烟花显示起始位置行号mov byte ptr ds:[si+1],3EH ;设置参数:烟花显示起始位置列号mov byte ptr ds:[si+2],0BH ;设置参数:烟花花柄长度mov byte ptr ds:[si+3],0CH ;设置参数:字符属性字节mov byte ptr ds:[si+4],'o' ;设置参数:构成烟花的单个字符mov byte ptr ds:[si+5],05H ;设置参数:花瓣的长度add word ptr ds:[si+6],0004H ;设置参数:显示花径每个字符(或花瓣每一组8个字符)后停顿的时间add word ptr ds:[si+8],0004H ;设置参数:清除花径每个字符后停顿的时间call show_fireworks ;调用子程序显示一朵烟花;设置参数,并调用show_fireworks子程序,显示第三朵烟花mov si,offset args_show_fireworks ;ds:bx指向show_fireworks子程序的参数列表首地址mov byte ptr ds:[si+0],11H ;设置参数:烟花显示起始位置行号mov byte ptr ds:[si+1],22H ;设置参数:烟花显示起始位置列号mov byte ptr ds:[si+2],0BH ;设置参数:烟花花柄长度mov byte ptr ds:[si+3],0DH ;设置参数:字符属性字节mov byte ptr ds:[si+4],'$' ;设置参数:构成烟花的单个字符mov byte ptr ds:[si+5],05H ;设置参数:花瓣的长度add word ptr ds:[si+6],-006H ;设置参数:显示花径每个字符(或花瓣每一组8个字符)后停顿的时间add word ptr ds:[si+8],-006H ;设置参数:清除花径每个字符后停顿的时间call show_fireworks ;调用子程序显示一朵烟花;设置参数,并调用show_fireworks子程序,显示第四朵烟花mov si,offset args_show_fireworks ;ds:bx指向show_fireworks子程序的参数列表首地址mov byte ptr ds:[si+0],17H ;设置参数:烟花显示起始位置行号mov byte ptr ds:[si+1],2FH ;设置参数:烟花显示起始位置列号mov byte ptr ds:[si+2],07H ;设置参数:烟花花柄长度mov byte ptr ds:[si+3],0EH ;设置参数:字符属性字节mov byte ptr ds:[si+4],'x' ;设置参数:构成烟花的单个字符mov byte ptr ds:[si+5],05H ;设置参数:花瓣的长度add word ptr ds:[si+6],0001H ;设置参数:显示花径每个字符(或花瓣每一组8个字符)后停顿的时间add word ptr ds:[si+8],0001H ;设置参数:清除花径每个字符后停顿的时间call show_fireworks ;调用子程序显示一朵烟花;程序暂停一会儿mov cx,01C0H ;参数:暂停的时间call show_pause ;调用子程序,使程序暂停一会儿;设置参数,并调用show_area子程序,显示祝福标语mov si,offset data_str ;参数:ds:si指向待显示字符的首地址mov bx,offset args_show_area ;参数:ds:bx指向show_area子程序的参数列表首地址mov byte ptr ds:[bx+0],09H ;参数:屏幕显示起始行号mov byte ptr ds:[bx+1],1AH ;参数:屏幕显示起始列号mov byte ptr ds:[bx+2],03H ;参数:待显示字符的行数mov byte ptr ds:[bx+3],1AH ;参数:待显示字符的列数mov byte ptr ds:[bx+4],4BH ;参数:待显示字符的属性 橙底青色mov word ptr ds:[bx+5],0170H ;参数:显示一行后暂停的时间call show_area ;调用show_area子程序;程序暂停一会儿mov cx,01C0H ;参数:暂停的时间call show_pause ;调用子程序,使程序暂停一会儿;设置参数,并调用show_fireworks子程序,显示第五朵烟花mov si,offset args_show_fireworks ;ds:bx指向show_fireworks子程序的参数列表首地址mov byte ptr ds:[si+0],18H ;设置参数:烟花显示起始位置行号mov byte ptr ds:[si+1],1FH ;设置参数:烟花显示起始位置列号mov byte ptr ds:[si+2],07H ;设置参数:烟花花柄长度mov byte ptr ds:[si+3],0AH ;设置参数:字符属性字节mov byte ptr ds:[si+4],'#' ;设置参数:构成烟花的单个字符mov byte ptr ds:[si+5],03H ;设置参数:花瓣的长度add word ptr ds:[si+6],0001H ;设置参数:显示花径每个字符(或花瓣每一组8个字符)后停顿的时间add word ptr ds:[si+8],0001H ;设置参数:清除花径每个字符后停顿的时间call show_fireworks ;调用子程序显示一朵烟花;设置参数,并调用show_area子程序,显示创作者信息mov si,offset data_author ;参数:ds:si指向待显示字符的首地址mov bx,offset args_show_area ;参数:ds:bx指向show_area子程序的参数列表首地址mov byte ptr ds:[bx+0],15H ;参数:屏幕显示起始行号mov byte ptr ds:[bx+1],3AH ;参数:屏幕显示起始列号mov byte ptr ds:[bx+2],01H ;参数:待显示字符的行数mov byte ptr ds:[bx+3],10H ;参数:待显示字符的列数mov byte ptr ds:[bx+4],03H ;参数:待显示字符的属性 青色mov word ptr ds:[bx+5],0100H ;参数:显示一行后暂停的时间call show_area ;调用show_area子程序;程序暂停一会儿mov cx,01C0H ;参数:暂停的时间call show_pause ;调用子程序,使程序暂停一会儿mov ax,4c00H ;程序返回int 21H;功能:在屏幕指定起始位置,以指定颜色,以指定行数和列数显示指定的字符串
;参数: ;ds:si 指向data段字符串的首地址;ds:bx 指向参数列表的首地址;show_area子程序的参数列表如下:;地址 参数含义; 00 屏幕起始显示位置的行号; 01 屏幕起始显示位置的列号; 02 待显示字符的行数; 03 待显示字符的列数; 04 待显示字符的属性字节; 05 显示一行后停顿的时间 低字节; 06 显示一行后停顿的时间 高字节
;返回:无
show_area: ;show_area子程序开始;将子程序用到的寄存器压入栈push ax push espush dipush cxmov ax,0B800H ;设置es指向显示缓冲区段mov es,ax;设置es:di指向显示缓冲区指定显示位置的首地址mov al,0A0H ;每行80个字符,每个字符占2字节,所以每行共80*2=160=A0H个字节mul byte ptr ds:[bx+0] ;计算第dh行起始地址(al * 行号 → ax)mov di,ax ;将乘法结果存放在di中(di = al * 行号)mov al,02H ;每个字符占2个字节mul byte ptr ds:[bx+1] ;计算目标行在指定位置前的字节数(al * 列号 → ax)add di,ax ;计算指定显示位置在es段的偏移地址(di + ax → di);通过循环来显示字符串mov ch,0 ;设置外层循环次数为待显示字符的行数mov cl,ds:[bx+2] show_area_s: ;外层循环开始push cx ;将外层循环次数压入栈mov ch,0 ;设置内存循环次数为待显示字符的列数mov cl,ds:[bx+3] show_area_s1: ;内层循环开始mov al,ds:[si] ;将ds:si指向的字节传送给es:dimov es:[di],al inc di ;es:di指向显存区下一个字节(当前字符的属性字节)mov al,ds:[bx+4] ;将字符的属性字节传送给es:dimov es:[di],alinc si ;ds:si指向下一个data段中要显示的字符inc di ;es:di指向显存区下一个字节(下一个字符的ASCII码字节)loop show_area_s1 ;内层循环mov cx,ds:[bx+5] ;设置程序暂停循环的次数call show_pause ;调用子程序产生一段时间的暂停add di,0A0H ;设置es:di指向显存区下一行mov al,ds:[bx+3] mov ah,0sub di,ax sub di,axpop cx ;将外层循环的次数pop出栈loop show_area_s ;外层循环;将子程序用到的寄存器都pop出栈pop cxpop dipop espop axret ;子程序show_area返回;功能:产生一段时间的暂停(时间与参数的平方成正比)
;参数:cx 暂停的时间
;返回:无
show_pause: ;子程序show_pause开始push cx ;将用到的寄存器压入栈push axmov ax,cx ;将接收的时间参数传送给ax,以ax为内层循环的次数show_pause_s:push cx ;将外层循环的次数压入栈mov cx,ax ;设置内层循环的参数show_pause_s0:nop ;空指令,产生一段时间的暂停loop show_pause_s0 ;内层循环pop cx ;将外层循环的次数pop出栈loop show_pause_s ;外层循环pop ax ;将用到的寄存器pop出栈pop cxret ;show_pause子程序返回;功能:在屏幕指定位置使用指定字符来显示一个烟花
;参数:;ds:si指向参数列表首地址,参数列表含义如下;地址 参数含义; 00 屏幕起始显示位置的行号; 01 屏幕起始显示位置的列号; 02 烟花花柄长度(>1); 03 字符的属性字节; 04 用于构成烟花的单个字符; 05 烟花花瓣的长度; 06 显示花径每个字符(或花瓣每一组8个字符)后停顿的时间 低字节; 07 显示花径每个字符(或花瓣每一组8个字符)后停顿的时间 高字节; 08 清除花径每个字符后停顿的时间 低字节; 09 清除花径每个字符后停顿的时间 高字节; 0A-2AH 以字单元依次指明要显示字符的相对地址,8个为一组
;返回:无
show_fireworks: ;子程序show_fireworks开始;将子程序用到的寄存器压入栈push axpush espush bxpush cxpush sipush di;设置es段指向显示缓冲区段mov ax,0B800H mov es,ax;设置es:di指向显示缓冲区指定显示位置的首地址mov al,0A0H ;每行80个字符,每个字符占2字节,所以每行共80*2=160=A0H个字节mul byte ptr ds:[si+0] ;计算指定行的起始地址(al * 行号 → ax)mov di,ax ;将乘法结果存放在di中(di = al * 行号)mov al,02H ;每个字符占2个字节mul byte ptr ds:[si+1] ;计算目标行在指定位置前的字节数(al * 列号 → ax)add di,ax ;计算指定显示位置在es段的偏移地址(di + ax → di);显示烟花的花柄mov al,ds:[si+4] ;获取参数:构成烟花的单个字符mov ah,ds:[si+3] ;获取参数:字符的属性字节mov bx,di ;使用bx暂存di,es:di指向显示起始位置mov ch,0 ;获取参数:花柄长度mov cl,ds:[si+2] ;以花柄长度作为循环次数show_fireworks_s1: ;显示花柄mov es:[di],al ;将字符传送到显存区mov es:[di+1],ah ;将字符的属性字节传送到显存push cxmov cx,ds:[si+6] ;设置程序暂停子程序的时间参数call show_pause ;调用子程序,使程序暂停一会儿pop cxsub di,0A0H ;ds:di指向行号-1的地址loop show_fireworks_s1 ;循环结束,花柄显示完毕;清除烟花的花柄mov al,' ' ;要显示的字符是一个空格mov ah,0 ;字符的属性字节mov ch,0 ;获取参数:花柄长度mov cl,ds:[si+2] ;以花柄长度-1作为循环次数,保留花心dec clmov di,bx ;从bx中恢复di,es:di指向显示起始位置show_fireworks_s2: ;清除花柄mov es:[di],al ;将空格字符传送到显存区mov es:[di+1],ah ;将字符的属性字节传送到显存push cxmov cx,ds:[si+8] ;设置程序暂停子程序的时间参数call show_pause ;调用子程序,使程序暂停一会儿pop cxsub di,0A0H ;es:di指向行号-1的地址loop show_fireworks_s2 ;循环结束,花柄清除完毕;至此es:di指向花心;显示烟花的花瓣(八瓣花)mov ah,ds:[si+3] ;获取参数:字符的属性字节mov al,ds:[si+4] ;获取参数:构成烟花的单个字符mov cl,ds:[si+5] ;获取参数:烟花花瓣的长度mov si,offset args_show_fireworks_addr ;获取参数:相对地址列表首地址mov ch,0 ;设置外层循环次数为花瓣的长度show_fireworks_s:push cx ;将外层循环次数压入栈mov cx,08H ;设置内层循环的次数为8,硬编码,因为是八瓣花show_fireworks_s0: ;内层循环,显示8个方向的字符mov bx,ds:[si] ;获取参数:显存区字符的相对地址mov es:[di+bx],al ;将字符传送到显存区mov es:[di+bx+1],ah ;将字符的属性字节传送到显存区add si,2 ;ds:si指向下一个字符显示的相对地址loop show_fireworks_s0 ;显示完8个方向的各一个字符push cxmov cx,0200H ;设置时间参数call show_pause ;调用子层序,使程序暂停一会儿pop cx pop cx ;将外层循环的循环次数pop出栈loop show_fireworks_s ;八瓣花显示完毕;将子程序用到的寄存器pop出来pop dipop sipop cxpop bxpop espop axret ;show_fireworks子程序返回code endsend start ;整个汇编程序结束
这篇关于用8086汇编语言写新春祝福的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!