热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

汇编实现贪吃蛇(参考修改别人的)

前两天决定写个贪吃蛇,用汇编实现(其实是作业)。然后参考了几分代码,选择了其中一份进行了大改。原程序地址:http:blog.csdn.netliangfei1983articledetai

前两天决定写个贪吃蛇,用汇编实现(其实是作业)。然后参考了几分代码,选择了其中一份进行了大改。

原程序地址:http://blog.csdn.net/liangfei1983/article/details/7015441

我修改的部分在于:

1、添加延时子程序(原程序也有类似代码,但是有多个地方要设置延时,造成代码重复,我把他改成子程序可以随时调用)

2、添加制造新食物的子程序(这个纯粹是为了代码更美观。。。)

3、删掉了很多不必要,冗余的代码

4、在游戏中设置了障碍物,增加一点点难度

5、改了一些输出的文字和方式(无关紧要)

6、修改添加了一部分注释,是我理解后添加的,更方面理解

7、还有就是说明一下,程序可能在不同计算机上运行速度回不同,修改一下延时子程序的参数即可

游戏界面:

1)游戏运行

2)游戏暂停

3)游戏结束


源代码:

DATA SEGMENT PARA PUBLIC 'DATA'
X DW 40
Y DW 40
GEWEI DB 0 ;个位,计分用
SHIWEI DB 0 ;十位
BAIWEI DB 0 ;百位
QIANWEI DB 0 ;千位
S_HEAD_X DW ? ;蛇头X轴坐标
S_HEAD_Y DW ? ;蛇头Y轴坐标
S_TAIL_X DW ? ;蛇尾X轴坐标
S_TAIL_Y DW ? ;蛇尾Y轴坐标
S_LENGTH DW 100 ;蛇初始长度为100
EGG_X DW 50 ;初始化食物的X轴坐标
EGG_Y DW 150 ;初始化食物的Y轴坐标
EGG_X_D DW ?
EGG_Y_R DW ?
S_DIRTIN DB 4 ;蛇头的方向,上1(X--,Y),下2(X++,Y)
; 左3(X,Y--),右4(X,Y++)
SPEED DW 13
TEMP_DIR DB 4
TEMPX DW ? ;蛇尾X轴坐标的临时存放单元
TEMPY DW ? ;蛇尾Y轴坐标的临时存放单元
SUP EQU 48H ;上的扫描码
SDOWN EQU 50H ;下的扫描码
SLEFT EQU 4BH ;左的扫描码
SRIGHT EQU 4DH ;右的扫描码
SESC EQU 01H ;ESC的扫描码
SKEYN EQU 31H ;N的扫描码
SKEYQ EQU 10H ;Q的扫描码
POINT DB 'POINT:$'
NGAME DB 'New game',0AH,0DH,'$'
QGAME DB 'Quit game$'
CHAR DB 'BOOM!!Your brains turns into shit!!Want another life?(Y/N)$'
C_SPEED DB 'GAME SPEED:',0AH,0DH,'1.VERY HARD',0AH,0DH,'2.HARD',0AH,0DH,'3.NORMAL',0AH,0DH,'4.EASY$'
STATEMENT DB 'REWRITE BY HJ(CSDN BLOG:legendtroy)! SOURCE CODE DOWN FROM(CSDN BLOG:)$'
GMENU DW 8 DUP (?)
DATA ENDS

STACK SEGMENT PARA STACK 'STACK'
DW 256 DUP (0)
STACK ENDS

CODE SEGMENT PARA PUBLIC 'CODE'
ASSUME CS:CODE,DS:DATA,SS:STACK
START:
PUSH DS
MOV AX,0
PUSH AX
MOV AX,DATA
MOV DS,AX
AGAIN:
MOV GEWEI,0
MOV SHIWEI,0
MOV BAIWEI,0
MOV QIANWEI,0
MOV EGG_X,50
MOV EGG_Y,150
MOV S_DIRTIN,4
MOV TEMP_DIR,4

MOV AH,6
MOV BH,0
MOV AL,0
XOR CX,CX ;异或运算,相异为真,相同为假
MOV DX,184FH
INT 10H ;清屏

MOV AH,0
MOV AL,12H
INT 10H ;设置显示模式为640*480,16色

REINPUT: ;这段代码设置游戏难度
MOV AH,2 ;置光标位置
MOV BH,0
MOV DH,0
MOV DL,0
INT 10H
MOV AH,9
MOV DX,OFFSET C_SPEED
INT 21H ;显示数据
MOV AH,2 ;置光标位置
MOV BH,0
MOV DH,8
MOV DL,0
INT 10H
MOV AH,9
MOV DX,OFFSET STATEMENT
INT 21H ;显示数据
MOV SPEED,13
MOV AH,1 ;键盘输入并回显,al=输入的内容
INT 21H
SUB AL,30H ;字符转成数字
CMP AL,1 ;如果为1
JAE JUDGE1 ;高于等于1转移到JUDGE1,用于无符号数进行转移,只有大于等于1才会执行
JMP REINPUT ;如果不是输入1---4,回去循环
JUDGE1:
CMP AL,4
JBE JUDGE2 ;低于等于时转移
JMP REINPUT ;跑回开始处
JUDGE2:
MOV CL,AL
SHL SPEED,CL ;设置游戏速度,speed左移cl

MOV AH,0
MOV AL,12H
INT 10H ;设置显示方式
MOV CX,29 ;以下代码设置游戏边框
MOV DX,29
LH1:
MOV AH,0CH
MOV BH,0
MOV AL,0fH ;色彩为白色
INT 10H ;写象素到屏幕上
INC DX ;DX++
CMP DX,449
JBE LH1 ;低于等于,从上到下,从29行29列一个一个地向449行画像素
MOV DX,29
ADD CX,500 ;列加500,继续从29行往449行写像素
CMP CX,640
JA LV1 ;大于转移
JB LH1 ;低于转移
LV1:
MOV DX,29
MOV CX,29
LV2: MOV AH,0CH ;同上
MOV BH,0
MOV AL,0FH
INT 10H
INC CX
CMP CX,529
JBE LV2
MOV CX,29
ADD DX,420
CMP DX,480
JB LV2 ;画边框,左上角坐标(29,29)右下角坐标(449,529)

WALL: ;以下代码生成两堵墙(障碍物),增加游戏难度
MOV DX,179 ;用像素点画出第一堵墙,实现方法与上类似
LP1:MOV CX,154
LP2:MOV AH,0CH
MOV AL,7 ;颜色是灰色
MOV BH,0
INT 10H
INC CX
CMP CX,404
JBE LP2
INC DX
CMP DX,199
JBE LP1
MOV DX,279 ;用像素点画出第二堵墙
LP3:MOV CX,154
LP4:MOV AH,0CH
MOV AL,7
MOV BH,0
INT 10H
INC CX
CMP CX,404
JBE LP4
INC DX
CMP DX,299
JBE LP3

INIT: ;初始化蛇的形状,位置
MOV S_HEAD_X,350
MOV S_HEAD_Y,150 ;初始化蛇头的坐标
MOV S_TAIL_X,350
MOV S_TAIL_Y,50 ;初始化蛇屁股的坐标
MOV DX,S_TAIL_X
MOV CX,S_TAIL_Y ;蛇屁股的坐标放进ds,cs
A: MOV AH,0CH
MOV AL,2 ;绿色的象素值
MOV BH,0
INT 10H
INC CX
CMP CX,150
JBE A

MOV DX,EGG_X ;这里的代码是一个for内层循环,画一个10*10的方框,很强大,后面多次用到
BCA: MOV CX,EGG_Y ;EGG_X,EGG_Y的初始坐标是(50,150),(行,列)
ABC: MOV AH,0CH ;初始食物的位置
MOV AL,0CH
MOV BH,0
INT 10H
INC CX
CMP CX,159
JBE ABC ;低于等于159时转到ABC,其实再往上经过一轮循环
INC DX
CMP DX,59
JBE BCA ;低于等于59时跳转回BCA

MOV AH,2 ;设置光标位置
MOV BH,0
MOV DH,3 ;行
MOV DL,68 ;列
INT 10H
MOV AH,9
MOV DX,OFFSET NGAME ;显示菜单栏的文字
INT 21H
MOV AH,2
MOV BH,0
MOV DH,6
MOV DL,68
INT 10H
MOV AH,9
MOV DX,OFFSET QGAME
INT 21H ;在右上角显示菜单

MOV AH,2 ;在左上角打印POINT:0000字样
MOV BH,0
MOV DH,0
MOV DL,0
INT 10H
MOV AH,9
MOV DX,OFFSET POINT ;输出POINT
INT 21H
MOV AH,2
MOV BH,0
MOV DH,0
MOV DL,8
INT 10H
MOV AH,2
MOV DX,30H ;输出千位0
INT 21H
MOV AH,2
MOV BH,0
MOV DH,0
MOV DL,9
INT 10H
MOV AH,2
MOV DX,30H
INT 21H ;输出百位0
MOV AH,2
MOV BH,0
MOV DH,0
MOV DL,10
INT 10H
MOV AH,2
MOV DX,30H
INT 21H ;输出十位0
MOV AH,2
MOV BH,0
MOV DH,0
MOV DL,11
INT 10H
MOV AH,2
MOV DX,30H
INT 21H ;输出个位0

BG: ;游戏开始的地方,之前都是图像的初始化
INC X ;X,Y是用来产生食物的随机位置
CMP X,400 ;随机原理是XY相当于时间计数器,XY会自增长(在40~400循环),相当于随机数
JBE CONT1 ;这里小于等于是用来限制新生成的食物的位置(40MOV X,40
CONT1: INC Y
CMP Y,500
JBE CONT2
MOV Y,40

CONT2:
CMP S_DIRTIN,1 ;方向上
JZ S_UP
CMP S_DIRTIN,2 ;方向下
JZ S_DOWN
CMP S_DIRTIN,3 ;方向左
JZ S_LEFT
CMP S_DIRTIN,4 ;方向右
JZ S_RIGHT

S_UP:
CMP TEMP_DIR,2 ;temp_dir是当前方向,如果当前方向向下,那按向上键不会改变蛇方向
JNZ GOUP ;如果当前方向不为下,转至GOUP
MOV S_DIRTIN,2;
JMP S_DOWN ;如果当前方向向下,按下上键时只会继续向下
GOUP: CALL DELAY ;调用延时子程序,用来控制游戏的速度
DEC S_HEAD_X
CMP S_HEAD_X,29 ;行坐标,框框的上行坐标是29,撞到就gameover
JBE OVER
JMP NEXT ;否则接着跑

S_DOWN:
CMP TEMP_DIR,1
JNZ GODOWN
MOV S_DIRTIN,1
JMP S_UP
GODOWN: CALL DELAY
INC S_HEAD_X
CMP S_HEAD_X,449
JAE OVER
JMP NEXT

S_LEFT:
CMP TEMP_DIR,4
JNZ GOLEFT
MOV S_DIRTIN,4
JMP S_RIGHT
GOLEFT: CALL DELAY
DEC S_HEAD_Y
CMP S_HEAD_Y,29
JBE OVER
JMP NEXT

S_RIGHT:
CMP TEMP_DIR,3
JNZ GORIGHT
MOV S_DIRTIN,3
JMP S_LEFT
GORIGHT: CALL DELAY
INC S_HEAD_Y
CMP S_HEAD_Y,529
JAE OVER
JMP NEXT

OVER:JMP GMOVER ;有些人会不解为什么不直接跳转到GMOVER,而要通过OVER过度
;本人在百度以后大概了解:程序跳转范围不能太大,直接跳转到GMOVER超出跳转范围
NEXT:
MOV AH,0DH ;读特定点的象素
MOV BH,0
MOV DX,S_HEAD_X ;行坐标,
MOV CX,S_HEAD_Y ;列坐标
INT 10H
CMP AL,2 ;AL=象素值,如果前面行坐标的象素等于本身的象素,就是蛇咬到自己了
JZ OVER
CMP AL,0CH ;如果前面的象素是食物的象素,则跳转到ADDTAIL1代码
JZ ADDTAIL1
CMP AL,7 ;如果碰到我之前加的那两堵墙就死翘翘啦
JZ OVER

PHEAD:
MOV AH,0CH
MOV AL,2
MOV BH,0
MOV DX,S_HEAD_X ;写像素,就是蛇往上(下左右)走了一步
MOV CX,S_HEAD_Y
INT 10H

JMP PTAIL
ADDTAIL1:JMP ADDTAIL

PTAIL: ;以下是消去蛇尾部的像素,蛇身跟着往前走
MOV AX,S_TAIL_X
MOV TEMPX,AX
MOV AX,S_TAIL_Y
MOV TEMPY,AX
DEC S_TAIL_X
MOV AH,0DH ;这里消去蛇尾部像素的实现方法有点复杂,要先判断前进方向
MOV BH,0
MOV DX,S_TAIL_X
MOV CX,S_TAIL_Y
INT 10H
CMP AL,2
JZ NEXT1;
INC S_TAIL_X
INC S_TAIL_Y
MOV AH,0DH
MOV BH,0
MOV DX,S_TAIL_X
MOV CX,S_TAIL_Y
INT 10H
CMP AL,2
JZ NEXT1
INC S_TAIL_X;
DEC S_TAIL_Y;
MOV AH,0DH
MOV BH,0
MOV DX,S_TAIL_X
MOV CX,S_TAIL_Y
INT 10H
CMP AL,2
JZ NEXT1
DEC S_TAIL_X
DEC S_TAIL_Y
JMP NEXT1

NEXT1:
MOV AH,0CH ;这里就是把尾部像素涂黑,即消掉
MOV AL,0
MOV BH,0
MOV DX,TEMPX
MOV CX,TEMPY
INT 10H
JMP MOVEON

ADDTAIL: ;当蛇吃到食物就会跳转到这里
CALL DISPNUM ;设置左上角数字的子程序,+1
MOV AX,EGG_X ;EGG_X行坐标,egg_y列坐标
MOV EGG_X_D,AX
ADD EGG_X_D,10 ;食物的大小是10*10,所以这里加10
MOV AX,EGG_Y
MOV EGG_Y_R,AX;
ADD EGG_Y_R,10 ;同上
MOV DX,EGG_X ;从这里到
ZYX: MOV CX,EGG_Y ;|
XYZ: MOV AH,0CH ;|
MOV BH,0 ;|
MOV AL,0 ;|
INT 10H ;|
INC CX ;|
CMP CX,EGG_Y_R ;|
JB XYZ ;|
INC DX ;|
CMP DX,EGG_X_D ;|
JB ZYX ;这里!!!代码目的是把原来被吃了的食物消去
CALL CREATE_FOOD ;调用生成新食物的子程序!!!!!!!!!!!!
CMP S_DIRTIN,1 ;判断当前方向,跳转到不同代码段
JZ EATUP
CMP S_DIRTIN,2
JZ EATDOWN
CMP S_DIRTIN,3
JZ EATLEFT
CMP S_DIRTIN,4
JZ EATRIGHT

EATUP: ;在蛇头处往前进方向增加十个像素点,吃了个10*10的食物当然要长身子啊!
MOV BL, 09H
EU:MOV AH,0CH
MOV AL,2
MOV BH,0
MOV DX,S_HEAD_X
MOV CX,S_HEAD_Y
INT 10H
DEC S_HEAD_X
DEC BL
JNZ EU
JMP PHEAD ;长完身子后继续前进,调回PHEAD段

EATDOWN: ;同上
MOV BL, 09H
ED:MOV AH,0CH
MOV AL,2
MOV BH,0
MOV DX,S_HEAD_X
MOV CX,S_HEAD_Y
INT 10H
INC S_HEAD_X
DEC BL
JNZ ED
JMP PHEAD

EATLEFT: ;同上
MOV BL, 09H
EL:MOV AH,0CH
MOV AL,2
MOV BH,0
MOV DX,S_HEAD_X
MOV CX,S_HEAD_Y
INT 10H
DEC S_HEAD_Y
DEC BL
JNZ EL
JMP PHEAD

EATRIGHT: ;同上
MOV BL, 09H
ER:MOV AH,0CH
MOV AL,2
MOV BH,0
MOV DX,S_HEAD_X
MOV CX,S_HEAD_Y
INT 10H
INC S_HEAD_Y
DEC BL
JNZ ER
JMP PHEAD

MOVEON: ;这段代码用来识别输入的字符,没有输入时自动前进
MOV AH,S_DIRTIN
MOV TEMP_DIR,AH ;TEMP_DIR 是当前方向
MOV AH,1
INT 16H ;al有字符,zf=0,无字符zf=1
JZ CONTINUE ;非零跳转,即无字符输入时跳到CONTINUE段
MOV AH,0 ;读取走键盘敲入的字符
INT 16H
CMP AH,SUP ;判段敲入的是什么字符
JZ SU
CMP AH,SDOWN
JZ SD
CMP AH,SLEFT
JZ SL
CMP AH,SRIGHT
JZ SR
CMP AH,SESC
JZ PAUSE
JMP BG ;如果敲入的不是有效键,则继续前进,相当于没有输入
SU: MOV S_DIRTIN,1
JMP BG
SD: MOV S_DIRTIN,2
JMP BG
SL: MOV S_DIRTIN,3
JMP BG
SR: MOV S_DIRTIN,4
JMP BG

CONTINUE: ;调用延迟子程序,用于控制游戏速度,之后回到游戏开始地方
CALL DELAY
JMP BG

PAUSE: ;PAUSE段代码
MOV DX,29 ;从这里~~~~~~~~~~~~~~(方法与上面初始化类似)
MENUB2:
MOV CX,539
MENUB1:
MOV AH,0CH
MOV AL,1
MOV BH,0
INT 10H
INC CX
CMP CX,629
JB MENUB1
ADD DX,100
CMP DX,130
JB MENUB2

MOV CX,539
MENUA2:
MOV DX,29
MENUA1:
MOV AH,0CH
MOV AL,1
MOV BH,0
INT 10H
INC DX
CMP DX,130
JB MENUA1
ADD CX,90
CMP CX,639
JB MENUA2 ;到这里~~~~~~~~是用来生成一个蓝色框框,框住菜单栏,意味选中菜单栏暂停

MOV AH,0 ;用来接收键盘输入
INT 16H
CMP AH,SESC ;判断键盘输入的字符,如果是Esc则推出暂停,N则重新开始游戏,Q则推出
JZ QUIT_PAUSE
CMP AH,SKEYN
JZ NEWGAME
CMP AH,SKEYQ
JZ QUIT
JMP PAUSE

QUIT_PAUSE: ;推出暂停的代码段,目的就是消去那个蓝色小框框
MOV DX,29
MENUB4:
MOV CX,539
MENUB3:
MOV AH,0CH
MOV AL,0
MOV BH,0
INT 10H
INC CX
CMP CX,629
JB MENUB3
ADD DX,100
CMP DX,130
JB MENUB4

MOV CX,539
MENUA4:
MOV DX,29
MENUA3:
MOV AH,0CH
MOV AL,0
MOV BH,0
INT 10H
INC DX
CMP DX,130
JB MENUA3
ADD CX,90
CMP CX,639
JB MENUA4

JMP BG ;消去蓝色框框之后就重新回到游戏啦

NEWGAME: ;重置整个游戏,即所有东西都回复到原点
JMP AGAIN

GMOVER: ;游戏结束代码段
MOV AH,2 ;先设置光标位置
MOV BH,0
MOV DH,14
MOV DL,6
INT 10H
MOV AH,9
MOV DX,OFFSET CHAR
INT 21H ;显示那段字符串,意思是gameover
ASK: ;GAMEOVER后询问是否重新游戏
MOV AH,0
INT 16H
CMP AL,59H
JZ NEWGAME
CMP AL,79H
JZ NEWGAME
CMP AL,4EH
JZ QUIT
CMP AL,6EH
JZ QUIT
JMP ASK
QUIT:
MOV AH,0
MOV AL,3
INT 10H

DELAY PROC NEAR ;延时子程序
PUSH AX ;程序开始前先储存各个寄存器
PUSH BX
PUSH CX
PUSH DX
MOV BX, SPEED ;实现方法其实就是弄两个循环,消耗cpu时间
DL1: MOV CX, 1000
DL2: LOOP DL2
DEC BX
JNZ DL1
POP DX ;还原各个寄存器
POP CX
POP BX
POP AX
RET
DELAY ENDP

CREATE_FOOD PROC NERA
PUSH AX
PUSH BX
PUSH CX
PUSH DX
REMOVE:
MOV AX,X;
MOV EGG_X,AX
ADD AX,10
MOV EGG_X_D,AX
MOV AX,Y
MOV EGG_Y,AX
ADD AX,10
MOV EGG_Y_R,AX
MOV DX,EGG_X
BBA: MOV CX,EGG_Y
ABB: MOV AH,0DH
MOV BH,0
INT 10H
CMP AL,2 ;判断现在将要生成新食物的范围有没于有没有与蛇身重叠
JZ JUG
CMP AL,7 ;有没有与墙重叠
JNZ NEXT4
JUG:ADD X,10 ;如果EGG_X和EGG_Y上有象素,即有重叠,则x和Y加10
ADD Y,10
CMP X,400 ;判断(X,Y)是否在设定范围内
JA ZZZ
CMP Y,500
JBE REMOVE
ZZZ:
MOV X,40
MOV Y,40
JMP REMOVE
NEXT4: ;如果这一像素点没有重叠则跳到此处
INC CX
CMP CX,EGG_Y_R
JB ABB
INC DX
CMP DX,EGG_X_D
JB BBA ;循环判断其他像素点,判断完后就跳出
MOV AX,X ;此段代码在(X,Y)开始生成一个新的食物,实现方法与初始那里差不多
MOV EGG_X,AX
ADD AX,10
MOV EGG_X_D,AX
MOV AX,Y
MOV EGG_Y,AX
ADD AX,10
MOV EGG_Y_R,AX
MOV DX,EGG_X
HJK:
MOV CX,EGG_Y
KJH:
MOV AH,0CH
MOV AL,0CH
MOV BH,0
INT 10H
INC CX
CMP CX,EGG_Y_R
JB KJH
INC DX
CMP DX,EGG_X_D
JB HJK;
POP DX
POP CX
POP BX
POP AX
RET
CREATE_FOOD ENDP ;子程序结束

DISPNUM PROC NEAR ;显示左上角分数的子程序,有计分功能
PUSH AX
PUSH BX
PUSH CX
PUSH DX
INC GEWEI ;吃到食物时才会调用改子程序,所以分数需要加一
CMP GEWEI,9
JBE DISP
MOV GEWEI,0
INC SHIWEI
CMP SHIWEI,9
JBE DISP
MOV SHIWEI,0
INC BAIWEI
CMP BAIWEI,9
JBE DISP
MOV BAIWEI,0
INC QIANWEI
CMP QIANWEI,9
JBE DISP
MOV QIANWEI,0
DISP:
MOV AH,2 ;设置光标
MOV BH,0
MOV DH,0
MOV DL,8
INT 10H
MOV AH,2 ;显示字符, DL=输出字符,千位!
MOV DL,QIANWEI
ADD DL,30H
INT 21H
MOV AH,2 ;设置光标
MOV BH,0
MOV DH,0
MOV DL,9
INT 10H
MOV AH,2 ;输出字符,百位!
MOV DL,BAIWEI
ADD DL,30H
INT 21H
MOV AH,2 ;设置光标
MOV BH,0
MOV DH,0
MOV DL,10
INT 10H
MOV AH,2 ;输出字符,十位!
MOV DL,SHIWEI
ADD DL,30H
INT 21H
MOV AH,2 ;设置光标
MOV BH,0
MOV DH,0
MOV DL,11
INT 10H
MOV AH,2 ;输出字符,个位
MOV DL,GEWEI
ADD DL,30H
INT 21H
POP DX
POP CX
POP BX
POP AX
RET
DISPNUM ENDP ;子程序结束

CODE ENDS
END START ;代码段结束



推荐阅读
author-avatar
胸膛舒畅_736
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有