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,下期见!
- [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