贪吃蛇,这个游戏相信大家都玩过了。现在我们要用汇编去实现它(当然是主要功能,界面不一定很漂亮),废话少说,下面进入正题(ps:不说废话,下面还是写了一大堆,耐心读下去!)
这个游戏我们采用的是自下而上,自上而下的设计方式,界面->数据结构->控制(好像是MVC的设计模式),这个分析过程很重要。避免后期胡子眉毛一把抓。忘了,最重要的一点了。开发环境:masmplus.运行环境:winXP或者dos。Win7不可以,因为win7不支持控制台全屏
1、 界面(徒有虚表,核心的在黑屏后面)
从界面上讲,我们要显示的有:①蛇②墙体(障碍物)③食物④计时显示⑤分数显示
好,我们又把任务细化了。先分析前三项①蛇②墙体(障碍物)③食物,我们可以用线代表墙,用N个小矩形代表蛇身,用一个小矩形代表食物。那么我们的问题进一步具体了,怎么画线,画矩形呢?相信有的朋友心里已有答案了,四条线是一个矩形,N个点是一条线。我们只需要画点就可以了
在介绍画点之前,我们还得对一些硬件的知识有所了解(毕竟汇编是面对的是硬件)。
每个显卡的厂商都会提供自己显卡接口标准,标准有很多,我们不用这种方式,太麻烦。我们采用最原始的方法,直接写显存,就想当初写0b800h那段内存一样。当然,这次我们不能通过写0b800h段显示一个点了。因为,显卡的显示方式分为文本模式、图形模式、视频模式……,我们以前写的0b800h段是文本显示模式,我们先在要让显示器以图形模式显示一个个的像素点。A000h是图形区的读写地址,而且图形模式还要分不同的分辨率。那我们怎么设置分辨率呢?先看课本有没有先关只是?no!那在查查dos、biso中断例程中有没有
通过查看手册,我们看到bios中断中int 10h (ah)=ch时可以修改分辨率,在此我们选择的是320*200 256色的显示方式。好,下面先试验一下!
;#mode=dos
.model small
.code
start:
mov ax,0013h
int 10h ;设置图形显示模式320*200 256色VGA
mov bx,20
mov ax,20;(20,20)
mov ch,2;颜色 绿色
call pixel
;暂停
mov ah,0
int 16h
;返回16色文本模式
mov ax,0003h
int 10h
;退出
mov ax,4c00h
int 21h
;--------------------------------------------------
;子程序的功能在指定坐标画点
;参数:坐标(bx,ax)列行,颜色值ch
;--------------------------------------------------
pixel:
push ax
push bx
push dx
push di
push es;保护现场,ax,bx本来不用的,为了方便以后的调用就保护了
mov dx,0a000h;图形的开始内存,相当于文本模式的0b800h
mov es,dx
mov dx,320
mul dx;算出第n行的内存地址dx:ax
add ax,bx;算出n行中的m列
mov di,ax
mov es:[di],ch;给这个单元赋颜色值
pop es;恢复现场
pop di
pop dx
pop bx
pop ax
ret
end start
看到没有,在(20,20)处有个点(O_O)
下面,解决画线的问题,根据两点一线原理我们只要得到开始和结束坐标就可以画线了。但由于笔者的图形学掌握的不是很好,在此只能画水平和垂直线啦。接下来我们画四条线
;#mode=dos
.model small
.data
.code
start:
mov ax,0013h
int 10h ;设置图形显示模式320*200 256色VGA
;左线
mov bx,10
mov ax,10;(10,10)
mov si,10
mov di,20;(10,20)
mov ch,2
call line
;下线
mov bx,10
mov ax,20;(10,20)
mov si,20
mov di,20;(20,20)
mov ch,2
call line
;上线
mov bx,10
mov ax,10;(10,10)
mov si,20
mov di,10;(20,10)
mov ch,2
call line
;右线
mov bx,20
mov ax,10;(20,10)
mov si,20
mov di,20;(20,20)
mov ch,2
call line
;暂停
mov ah,0
int 16h
;返回16色文本模式
mov ax,0003h
int 10h
;退出
mov ax,4c00h
int 21h
;----------------画线子程序------------------------
;子程序的功能:
; 在指定坐标画线,只能画水平和垂直的线
;参数:
; 起点坐标(bx,ax)列行,终点坐标(si,di)颜色值ch
;--------------------------------------------------
line:
push ax
push bx
push si
push di
push cx;保护现场
push dx
cmp di,ax
je level;水平画线
cmp si,bx;求差
je perp;垂直画线
level: ;水平线
sub si,bx;si代表要画的的总列数,bx代表但前要画的列(行在ax中),颜色在ch中
inc si;相减会少一
putl:
call pixel;调用子程序,显示一个点
inc bx;指向下一列
dec si;列数减一
jnz putl;重复调用画点,连成线
back:
pop dx;恢复现场
pop cx
pop di
pop si
pop bx
pop ax
ret
perp: ;垂直线
sub di,ax;di代表要画的的总列数,bx代表但前要画的列(行在ax中),颜色在ch中
inc di;相减会少一
putp:
call pixel;调用子程序,显示一个点
inc ax;指向下一列
dec di;列数减一
jnz putp;重复调用画点,连成线
jmp back
;----------------画点子程序------------------------
;画点子程序的功能:
; 在指定坐标画点
;参数:
; 坐标(bx,ax)列行,颜色值ch
;--------------------------------------------------
pixel:
push ax
push bx
push dx
push di
push es;保护现场,ax,bx本来不用的,为了方便以后的调用就保护了
mov dx,0a000h;图形的开始内存,相当于文本模式的0b800h
mov es,dx
mov dx,320
mul dx;算出第n行的内存地址dx:ax
add ax,bx;算出n行中的m列
mov di,ax
mov es:[di],ch;给这个单元赋颜色值
pop es;恢复现场
pop di
pop dx
pop bx
pop ax
ret
end start
看到四条线组成的矩形了吗?距离目标有近了一步
The next,我们进行进一步的封装,把上面那个程序封装成画正方形的子程序,参数:矩形左上角的坐标,大小为5pix
;#mode=dos
.model small
.data
.code
start:
mov ax,0013h
int 10h ;设置图形显示模式320*200 256色VGA
mov ah,15
mov al,15;(15,15)
mov ch,2
call rec
mov ah,20
mov al,15;(20,15) 、
call rec
mov ah,25
mov al,15
call rec
;暂停
mov ah,0
int 16h
;返回16色文本模式
mov ax,0003h
int 10h
;退出
mov ax,4c00h
int 21h
;----------------画矩形子程序------------------------
;子程序的功能:
; 在指定左上角和右下角坐标画矩形
;参数:
; 左上角坐标(ah,al)列行 < 右下角坐标(bl,bh)颜色值ch
; (bx,ax)列行 (si,di)
;--------------------------------------------------
rec:
push ax
push bx
push cx;保护现场
push dx
push si
push di
mov bl,ah
mov bh,al
add bx,0505h;算出右下角的坐标
;以左上角为主
push ax
push bx
;左线(ah,al) (ah,bh)
mov dx,bx;保存bx
mov bx,0
mov bl,ah
mov ah,0;开始坐标准备完毕
mov si,bx
push dx
mov cl,dh
mov dx,0
mov dl,cl
mov di,dx
pop dx;结束坐标准备完毕
push ax
push bx;下面要用ax,bx
call line
pop bx
pop ax
;上线
mov di,ax
mov dh,0
mov si,dx
call line
pop bx
pop ax;ax,bx恢复为刚进入子程序时的状态
;以右下角为主
;右线
mov dx,ax
mov ax,0
mov al,bh
mov bh,0
mov si,bx
mov di,ax;结束坐标完毕
mov bx,si
mov al,dl
push di
push si
call line
pop si
pop di
;下线
mov bl,dh
mov ax,di
call line
pop di;恢复现场
pop si
pop dx
pop cx
pop bx
pop ax
ret
;----------------画线子程序------------------------
;子程序的功能:
; 在指定坐标画线,只能画水平和垂直的线
;参数:
; 起点坐标(bx,ax)列行,终点坐标(si,di)颜色值ch
;--------------------------------------------------
line:
push ax
push bx
push si
push di
push cx;保护现场
push dx
cmp di,ax
je level;水平画线
cmp si,bx;求差
je perp;垂直画线
level: ;水平线
sub si,bx;si代表要画的的总列数,bx代表但前要画的列(行在ax中),颜色在ch中
inc si;相减会少一
putl:
call pixel;调用子程序,显示一个点
inc bx;指向下一列
dec si;列数减一
jnz putl;重复调用画点,连成线
back:
pop dx;恢复现场
pop cx
pop di
pop si
pop bx
pop ax
ret
perp: ;垂直线
sub di,ax;di代表要画的的总列数,bx代表但前要画的列(行在ax中),颜色在ch中
inc di;相减会少一
putp:
call pixel;调用子程序,显示一个点
inc ax;指向下一列
dec di;列数减一
jnz putp;重复调用画点,连成线
jmp short back
;----------------画点子程序------------------------
;画点子程序的功能:
; 在指定坐标画点
;参数:
; 坐标(bx,ax)列行,颜色值ch
;--------------------------------------------------
pixel:
push ax
push bx
push dx
push di
push es;保护现场,ax,bx本来不用的,为了方便以后的调用就保护了
mov dx,0a000h;图形的开始内存,相当于文本模式的0b800h
mov es,dx
mov dx,320
mul dx;算出第n行的内存地址dx:ax
add ax,bx;算出n行中的m列
mov di,ax
mov es:[di],ch;给这个单元赋颜色值
pop es;恢复现场
pop di
pop dx
pop bx
pop ax
ret
end start
看到没有,我们可以画出蛇身了,每次进步一点点!
到这里,我们就可以画出蛇身、墙体和食物了。墙体和食物请读者自行完成(就当是练习了)
至于计分显示和计时显示我们会在下期介绍,另外下期还会介绍我们的数据结构的分配
水平有限,如有不当之处还请指正,不胜感激。
我们下期再见!
- [cutebe] 相当牛,这个苦思冥想也值了。^_^ 11/30 00:00
- [parse] 如果忽略消息循环,那么操作系统加载的程序很快就执行完了,就像DOS程序一闪而过,所以CPU会空闲下来 06/30 09:04
- [游客] 楼主好厉害,挺一下! 01/19 08:43
- [游客] 很不错。 01/04 18:36
- [chinatree] 潜力贴留名,沙发。 11/08 12:58
- [youthangel] 恩,这次对了 10/30 18:56
- [fpamc] mov bx,18 在这条指令的上边是不是要加一条sub dx,dx? 10/30 10:03
- [fpamc] 对的 10/27 11:19
- [fpamc] 对的 10/27 09:00
- [fpamc] 哦,对不起,看错了。实验13也有一个7ch中断 10/27 08:52
- [游客] 现在急需一个汇编大作业。。。。。。可以么。。。。。如果今天之内看见留言 就加 1765496715 12/28 16:52
- [youthangel] 这算是对我学习的鼓励吗?谢谢!咱们这样交流就可以了 10/11 15:48
- [fpamc] 多日观察,你的学习积极性挺高的。可以来我们群了。群号:75916434 10/11 10:58