用8086汇编语言写新春祝福

2024-02-07 02:12

本文主要是介绍用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汇编语言写新春祝福的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/686237

相关文章

《x86汇编语言:从实模式到保护模式》视频来了

《x86汇编语言:从实模式到保护模式》视频来了 很多朋友留言,说我的专栏《x86汇编语言:从实模式到保护模式》写得很详细,还有的朋友希望我能写得更细,最好是覆盖全书的所有章节。 毕竟我不是作者,只有作者的解读才是最权威的。 当初我学习这本书的时候,只能靠自己摸索,网上搜不到什么好资源。 如果你正在学这本书或者汇编语言,那你有福气了。 本书作者李忠老师,以此书为蓝本,录制了全套视频。 试

汇编语言知识基础介绍

汇编语言是一种低级编程语言,直接与计算机硬件的指令集架构(ISA)对应。它在操作系统、设备驱动程序和性能关键的应用程序中起着重要作用。以下是汇编语言的基础介绍: 1. 基本概念 指令集架构(ISA):汇编语言与特定的处理器架构(如 x86、ARM)对应,每种架构有其特定的指令集。汇编指令:汇编语言的语法是处理器指令的符号表示,通常包括操作码(opcode)和操作数。操作码指定操作的类型,操作数

汇编语言输出“Hello World!“

1.软件 Nasmide64.exe(李忠老师编写) Fixvhdw64.exe(李忠老师编写) VirtualBox虚拟机(免费 开源) 2.过程 01.Fixvhdw64.exe输入以下代码: mov ax,0xb800mov ds,axmov byte [0x00],'H'mov byte [0x02],'e'mov byte [0x04],'l'mov byte [0

编译原理项目——C++实现C语言编译器输出为8086级汇编(代码/报告材料)

完整的材料 代码见文章末尾 以下为核心内容和部分结果 项目介绍 一个小型的c语言编译器,实现的功能如下: 可以定义多个变量,并且能初始化。可以支持基本的加减乘除运算。可以支持带括号的多个变量的四则混合运算。可以支持单行注释和多行注释。可以输出%d格式的整数。可以定义int 类型。可以输出字符串。可以输出2位整数可以支持简单的if{}else{}语句。(大于和小于比较条件)以上支持的语法可以混

ARM——结构体系(处理器工作模式,CPSR,立即数,汇编语言函数调用)

1、处理器工作模式          ARM有7个基本工作模式:User:非特权模式,大部分任务执行在这种模式FIQ:当一个高优先级(fast)中断产生时将会进入这种模式IRQ:当一个低优先级(normal)中断产生时将会进入这种模式Supervisor:当复位或软中断指令执行时将会进入这种模式Abort:当存取异常时将会进入这种模式Undef:当执行未定义指令时会进入这种模式Sy

ARM下汇编语言编程

一、ARM汇编语言程序格式 ARM汇编语言是以段(section)为单位来组织源文件的。段是相对独立的、具有特定名称的、不可分割的指令或者数据序列。 段又可以分为代码段和数据段,代码段存放执行代码,数据段存放代码运行时需要用到的数据。一个ARM源程序至少需要一个代码段,大的程序可以包含多个代码段和数据段。 二、ARM汇编语言中常用的伪操作 例如: area reset,cod

一个生成祝福短信的简单C++程序

正值大过年的,很多人对群发短信反感,我就灵机一动用Qt写了一个简单的祝福短信的程序,主要的代码如下: void Widget::printMsg() { int i = 0; int j = 0; bool rightPoint = false; int mack; int numBuf[10] = {0}; //词语导入//0位

汇编语言05——包含多个段的程序

整理自fishcc论坛 安全的空间 通常来说,0:200~0:300这段空间是相对安全的,操作系统通常不会去占用 但这只有256个字节,当需要更多空间时,可以向操作系统申请 问题: 编程计算以下8个数据的和,结果存在ax 寄存器中: 0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H 程序: assume cs

汇编语言04——[BX]和loop指令

整理自fishcc论坛课件 首先展示一个新的程序: assume cs:codesg codesg segment start: mov ax,2000H mov ds,ax mov al,[0] mov bl,[1] mov cl,[2] mov dl,[3] mov ax,4C00H int 21H codesg ends end start 使用masm进行

汇编语言03——第一个汇编程序

整理自鱼C论坛课件 汇编程序编写步骤 编写源代码->使用MASM.exe进行编译,产生目标文件->用LINK.exe,对目标文件进行链接,生成可执行文件 可执行文件中包含两部分内容 1,程序和数据 2,相关的描述信息(程序有多大,要占用多少内存空间等) 源程序: assume cs:abc abc segment mov ax,2 add ax,ax add