汇编网首页登录博客注册
youthangel的学习博客
博客首页博客互动【做检测题】论坛求助

我的博客

个人首页 |  我的文章 |  我的相册 |  我的好友 |  最新访客 |  文章收藏 |  论坛提问 |  友情链接 |  给我留言  
图片载入中
学习动态
文章收藏

[2012-11-08 20:15] 手把手教你写汇编版贪吃蛇 第2期

Hello!欢迎大家回来,下面我们继续上期的内容。你把墙体和食物画出来了吗?不知道你画没画,反正我画了!
        先说计时显示和计分显示,不知道你看了程序以后有没有发现:食物和蛇都是到不了256列—320列这个区域的。有的朋友要问了:为什么呀?我说:你先别看答案自己去测试一下,看我说的对不对,也许在测试过程中你就会发现其中的缘由。细心的读者会发现,在rec(画矩形)的子程序中,我传参用的是(ah,al)。也就是说纵坐标最大为255(在计算机中以左上角为原点,向右为X轴向下为Y轴),而屏幕是320*200的,所以256列—320列是不能用来作为蛇的活动范围的。那我们正好可以利用这60多列来显示计时和计分。接下来,我们的研究对象又变成了“如何在图形模式下在指定位置显示字符串”了。看课本,看《bios、dos中断调用》都没有,那找“度娘”吧。在百度搜了一通倒是有结果了,但实现起来很麻烦。于是笔者想大胆的尝试一下强硬(其实就是在0b800段写字符)的用dos中断在屏幕上输出了一串字符。意外发生了,居然显示出来了。只是位置不容易确定,因为字符串是一点阵的形式显示的。于是乎又是一通乱试,终于确定位置了
        为了美观,我又给上面的区域加了个红框,到此位置主体的显示我们已经解决。
        说明:现在的整体效果是:左边是一个255*200的蓝框(颜色可以改),框内是蛇和食物。右边是一个红框,框内上半部是计时显示,下半部是计分显示。
2、数据结构(存储的核心)
        先分析蛇,有的朋友可能马上就想到了指针、结构体、链表……但那是C语言,有的朋友又说了:汇编中也可以用struct关键字定义结构啊,但那属于汇编的高级版本。我们现在要在8086上用王爽老师写的这本书里的内容去解决这个问题。我们在看一眼rec这个子程序,发现原来每个蛇节是靠一个(ah,al)坐标控制的。那么,我们只需要在内存里记录下这一系列的坐标,当要显示蛇身的时候在取出来。数据当然要放在数据段啦,我们在data段申请一批空间(可以试试动态申请),现在我们把上期的那三个蛇节的位置存入内存了,在用ds:[bx]取出来
        接下是食物的产生,只需要确定左上角的坐标(ah,al)就可以了。难点是必须产生两个随机值。于是,rand、random等又浮现在脑海……还是老规矩,看课本查手册,解决不了。找度娘,这次好像度娘也没给出特别乐观的答案。这时候就得靠经验(以前做过关于随机数的程序)或者参考一下其他网友的程序了(某人的汇编程序跟随机数有关),总之是借鉴。
        笔者正好以前做过这方面的东西,8086PC有很多外设,其中有个控制计数器/定时器的8253芯片,这个芯片的跟cpu交换的编号为40h的端口是某个计数器的一部分。计数器一直在计数,40h中的数也一直在变。也就是说我们可以从40h中获得随机数。那这个口输出的是8位的还是16位的呢?查看相关书籍得知识16位的
        只需用in        ax,40h,我们就可以在ax中得到一个随机数,但我们没法保证这个数不超出256或者199。可以模除256或者199,但肯定不是256和199,请读者自行思考为什么不是256和199。我们的代码变成了
        ;#mode=dos
.model        small
data        segment        
snack:        db        15,15,15,20,15,25,'$';以行列的方式存,方便调用
food:        db        8 dup(0)
times:        db        "Time",'$'
marks:        db        "mark",'$'
time:        db        "00:00",'$';上限99s
mark:        db        "0000",'$';一节一分,60分过关
data ends
.code
        start:          
              mov       ax,0013h
             int       10h         ;设置图形显示模式320*200 256色VGA
                    
                    call        init                                                                
        ;暂停           
              mov       ah,0
              int       16h
        ;返回16色文本模式      
              mov       ax,0003h
              int       10h
        ;退出              
              mov       ax,4c00h
              int       21h

;----------------初始化子程序------------------------
;子程序的功能:
;        显示蛇、墙体、食物、计时、分数
;参数:无
;--------------------------------------------------
        init:
        ;显示time
                    mov        ah,2
                    mov        bh,0
                    mov        dh,2;行
                    mov        dl,34;列
                    int        10h
                    
                    mov        ax,data
                    mov        ds,ax
                    mov        dx,times
                    mov        ah,9
                    int        21h
                    
                    mov        ah,2
                    mov        bh,0
                    mov        dh,7;行
                    mov        dl,34;列
                    int        10h
                    
                    mov        ax,data
                    mov        ds,ax
                    mov        dx,time;时间值
                    mov        ah,9
                    int        21h
                    
                    ;显示mark
                    mov        ah,2
                    mov        bh,0
                    mov        dh,90;行
                    mov        dl,50;列
                    int        10h
                                        
                    mov        dx,marks
                    mov        ah,9
                    int        21h
                    
                    mov        ah,2
                    mov        bh,0
                    mov        dh,96;行
                    mov        dl,50;列
                    int        10h
                                        
                    mov        dx,mark;分值
                    mov        ah,9
                    int        21h
                    
                    call        show_snack
                call        show_food
                      call        bound
                ret
;----------------显示子程序------------------------
;子程序的功能:
;        显示蛇、墙体、食物、计时、分数
;--------------------------------------------------
        show:
                call        show_snack
                call        show_food             
                ret
;----------------显示蛇身子程序------------------------
;子程序的功能:
;        在屏幕上显示蛇身
;参数:
;        snack地址标号的坐标数据遇,'$'结束
;-----------------------------------------------------                                        
        show_snack:
                push        ax;保护现场
                push        ds
                push        bx
                push        cx
                
                mov        ax,data
                mov        ds,ax
                mov        bx,0
                mov        ch,2
        show_again:
                mov        ax,snack
                add        bx,ax
                mov        ax,[bx]
                cmp        al,'$'
                je        retsnack
                call        rec
                add        bx,2
                jmp        show_again
        retsnack:
                pop        cx
                pop        bx
                pop        ds;恢复现场
                pop        ax
                ret

;----------------显示边界线程序------------------------
;子程序的功能:
;        画出可用屏幕的边界线,蓝色为蛇的活动范围
;-----------------------------------------------------        
        bound:
                push        ax
                push        bx
                push        si
                push        di
                push        cx
                
                ;下
                mov        bx,0
                mov        ax,199;(0,199)
                mov        si,255
                mov        di,199;(255,199)
                mov        ch,1
                call        line
                                
                ;左
                mov        bx,0
                mov        ax,0;(0,0)
                mov        si,0
                mov        di,199;(0,199)
                call        line
                
                ;上
                mov        bx,0
                mov        ax,0;(0,0)
                mov        si,255
                mov        di,0;(255,0)
                call        line
                
                ;右
                mov        bx,255
                mov        ax,0;(255,0)
                mov        si,255
                mov        di,199;(255,199)
                call        line
                
        ;多出来的65pix
                ;右
                mov        bx,319
                mov        ax,0;(319,0)
                mov        si,319
                mov        di,199;(319,199)
                mov        ch,4
                call        line
                ;左
                mov        bx,256
                mov        ax,0;(255,0)
                mov        si,256
                mov        di,199;(256,199)
                call        line
                ;上
                mov        bx,256
                mov        ax,0;(256,0)
                mov        si,319
                mov        di,0;(319,0)
                call        line
                ;下
                mov        bx,256
                mov        ax,199;(256,119)
                mov        si,319
                mov        di,199;(319,199)
                call        line
                
                pop        cx
                pop        di
                pop        si
                pop        bx
                pop        ax
                ret
;----------------显示食物子程序------------------------
;子程序的功能:
;        在屏幕上随机显示一个食物(不能和蛇身重合)
;参数:无
;算法:
;        利用40端口产生随机数,并把这个坐标记录放在food的结构体中
;-----------------------------------------------------        
        show_food:
                push        ax
                push        bx;保护现场
                push        ds
                push        cx
                
                in        ax,40h
                mov        bl,190;再大就要超出屏幕了
                div        bl;得到余数做行坐标在ah中
                mov        bh,ah
                in        ax,40h                
                mov        bl,250;再大就要超出边界了
                div        bl;得到余数做列坐标在ah中
                mov        bl,ah
                mov        ax,bx;行列坐标放在bx中,接下来是查找是否是蛇身的某个节点
                mov        bx,data
                mov        ds,bx
                mov        bx,food
                mov        [bx],ax;保存食物的位置,也可以不保存
                mov        ch,3
                call        rec
                
                pop        cx
                pop        ds
                pop        bx
                pop        ax;恢复现场
                ret
                
;----------------画矩形子程序------------------------
;子程序的功能:
;        在指定左上角和右下角坐标画矩形
;参数:
;        左上角坐标(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
        好到这里我们把数据结构也解决了。下面就是让蛇动起来还要控制它的移动方向
        到这里,我们的程序也就出现了一个bug,比如食物的位置是随机的可以是(4,4),而蛇的坐标是5的倍数,那么蛇怎么遇到食物呢?请读者仔细阅读代码看你的慧眼能否识破其中的bug。我们将在下期解决这些bug,并完成我们的作品
        Bye,下期见!
评论次数(0)  |  浏览次数(349)  |  类型(冥思苦想) |  收藏此文  | 
 
 请输入验证码  (提示:点击验证码输入框,以获取验证码