⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 zlfsnakev1.00.asm

📁 用汇编语言实现贪吃蛇游戏 直接编译连接即可
💻 ASM
字号:

; 常量定义
EXITKEY = 1BH		; 退出键 ESC 的ASCII码
TOP 	= 4			; 左上角行坐标
BUTTOM  = 19		; 右下角行坐标
LEFT    = 16		; 左上角列坐标
RIGHT   = 64		; 右下角列坐标
SnakeLen = 64		; 蛇长,暂时没有用,但可供以后作为判断胜得的条件

SNODE STRUC			; 声明一个蛇结点结构
	nextY DB 0		; 行坐标
	nextX DB 0		; 列坐标
	char  DB '*'	; 字符
	color DB 0AH	; 颜色
SNODE ENDS

; 数据段
DSEG SEGMENT
	colorBox DB 01H,02H,03H,04H,0EH,07H,05H		; 颜色盒
	colorNum DB 7								; 颜色数,供产生随机颜色函数用
	currLen  DB 4								; 当前有效长度,初始为4个节点
	currPos  DB 4DH								; 初始方向,向右
	
sHead SNODE <11,41,'O',0BH>						; 蛇头
	  SNODE <11,40,'*',04H>
	  SNODE <11,39,'*',02H>
	  SNODE <11,38,'*',01H>
	  SNODE 60 DUP(<>)
	  
sTail SNODE <0,0,' ',0>							; 蛇尾,用于覆盖最后一个节点
food  SNODE <>									; 食物点
	
DSEG ENDS


; 代码段
CSEG    SEGMENT     'code'
        ASSUME CS:CSEG, DS:DSEG

START:
	MOV AX,DSEG									; 哎,居然会忘记写这两行
	MOV DS,AX									; 害我惨死了这么久
	
	MOV CH,3									; 开一个红色窗口
	MOV CL,15									; 作边框
	MOV DH,20
	MOV DL,65
	MOV BH,4CH
	MOV AL,0
	MOV AH,6
	INT 10H
	
	MOV CH,TOP									; 开一个黑色窗口
	MOV CL,LEFT									; 作为界面
	MOV DH,BUTTOM
	MOV DL,RIGHT
	MOV BH,07H
	MOV AL,0
	MOV AH,6
	INT 10H
	
	CALL setFood								; 抛食
moving:
	CALL setSnake								; 设置各节点将要出现的位置
	CALL drawSnake								; 画出各节点
	CALL checkIfEat								; 检查是否吃到食物
	CALL delay									; 延时
	JMP moving									; 循环




;===================================================================

; 子程序名: drawSnake
; 功能: 在屏幕上绘出蛇各节点
; 入口参数: 无
; 出口参数: 无

drawSnake PROC
	PUSH SI					; 保护寄存器
	PUSH AX
	PUSH BX
	PUSH CX
	PUSH DX

	XOR CX,CX				; CX赋当前有效长度
	MOV CL,currLen
	MOV SI,OFFSET sHead		; SI指向蛇头
draw:
	MOV BH,0				; 设置光标位置
	MOV DH,[SI].nextY
	MOV DL,[SI].nextX
	MOV AH,2
	INT 10H
	
	PUSH CX					; 保护好CX,LOOP draw要用到
	MOV BH,0				; 在光标位置画出相应节点
	MOV AL,[SI].char
	MOV BL,[SI].color
	XOR CX,CX
	MOV CX,1
	MOV AH,9
	INT 10H
	POP CX
	CALL checkIfSelfMurder	; 检查蛇头是否咬中蛇身
	ADD SI,TYPE SNODE		; SI指向下一个节点
	LOOP draw

drawTail:					; 画出蛇尾,覆盖原来的痕迹
	MOV BH,0
	MOV DH,sTail.nextY
	MOV DL,sTail.nextX
	MOV AH,2
	INT 10H
	
	MOV AL,sTail.char
	MOV BL,sTail.color
	XOR CX,CX
	MOV CX,1
	MOV AH,9
	INT 10H
		
	POP DX					; 恢复寄存器
	POP CX
	POP BX
	POP AX
	POP SI
	RET
drawSnake ENDP



;========================================================

; 子程序名: setNextPos
; 功能: 确定下一次移动的方向,检查是否需要退出
; 入口参数: 无
; 出口参数: 无

setNextPos PROC
	PUSH AX
	
again0:	
	MOV AH,1			
	INT 16H
	JZ  ggSNP					; 无数据可读,转
	
	MOV AH,0
	INT 16H

	CMP AL,EXITKEY				; 读到退出键,退出
	JZ  ggMain0
	
	CMP AH,4DH					; 右箭头(小键盘 6),转
	JZ  turnRight0

	CMP AH,4BH					; 左箭头(小键盘 4),转
	JZ  turnLeft0

	CMP AH,50H					; 上箭头(小键盘 8),转
	JZ  turnUp0

	CMP AH,48H					; 下箭头(小键盘 2),转
	JZ  turnDown0

	JMP again0					; 否则,重新读取

turnRight0:
	CMP currPos,4BH				; 当前方向是向左
	JZ  again0					; 转(不能逆行)
	MOV currPos,4DH				; 置方向为右
	JMP ggSNP

turnLeft0:						; 以下处理都同turnRight0
	CMP currPos,4DH
	JZ  again0
	MOV currPos,4BH
	JMP ggSNP

turnUp0:
	CMP currPos,50H
	JZ  again0
	MOV currPos,48H
	JMP ggSNP

turnDown0:
	CMP currPos,48H
	JZ  again0
	MOV currPos,50H
	JMP ggSNP
	
ggMain0:						; 退出程序
	MOV AH,4CH
	INT 21H

ggSNP:							; 退出子程序
	POP AX
	RET
setNextPos ENDP



;==========================================================

; 子程序名: setHead
; 功能: 设置蛇头下一次将要移动到的位置
; 入口参数: 无
; 出口参数: 无

setHead PROC
	PUSH DI
	
	MOV  DI,OFFSET sHead
	CALL setNextPos				; 设置前进方向

	CMP currPos,4DH				; 右向,转
	JZ  turnRight1
	
	CMP currPos,4BH				; 左向,转
	JZ  turnLeft1

	CMP currPos,48H				; 向上,转
	JZ  turnUp1
	
	CMP currPos,50H				; 向下,转
	JZ  turnDown1

turnRight1:
	INC [DI].nextX				; 列坐标++
	JMP ggSH

turnLeft1:	
	DEC [DI].nextX				; 列坐标--
	JMP ggSH

turnUp1:	
	INC [DI].nextY				; 这里好象反了,可是
	JMP ggSH					; 这样确实可以工作正常啊(???)

turnDown1:
	DEC [DI].nextY				; 好象也反了(???)
	JMP ggSH

ggSH:
	POP DI
	RET
setHead ENDP



;=================================================================

; 子程序名: setSnake
; 功能: 设置蛇身各节点下一次将要移动到的位置
; 入口参数: 无
; 出口参数: 无

setSnake PROC
	PUSH AX
	PUSH DI
	PUSH SI
	PUSH CX
	
	MOV DI,OFFSET sHead				; DI指向蛇头
	MOV SI,DI
	SUB SI,TYPE SNODE				; SI指向蛇头前一节
	
	XOR CX,CX
	MOV CL,currLen
	DEC CL	
scan0:
	ADD DI,TYPE SNODE				; DI指向蛇尾
	ADD SI,TYPE SNODE				; SI指向倒数第二个节点
	LOOP scan0
	
	MOV AL,[DI].nextX				; 设置sTail,以便覆盖
	MOV sTail.nextX , AL
	MOV AL,[DI].nextY
	MOV sTail.nextY , AL

	XOR CX,CX
	MOV CL,currLen
	DEC CL
ReScan0:							; 由后开始往前扫描,每个节点下一次要运行的
	MOV AL,[SI].nextX				; 位置就是它前一个节点当前所在位置
	MOV [DI].nextX , AL
	MOV AL,[SI].nextY
	MOV [DI].nextY , AL
		
	SUB DI,TYPE SNODE
	SUB SI,TYPE SNODE
	LOOP ReScan0
	
	CALL setHead					; 设置蛇头
	CALL checkIfOutside				; 检查是否越界,是,则从另一边出来

	POP CX
	POP SI
	POP DI
	POP AX
	RET
setSnake ENDP

;=======================================================

; 子程序名: checkIfSelfMurder
; 功能: 检查蛇头是否咬到蛇身
; 入口参数: 无
; 出口参数: 无

checkIfSelfMurder PROC
	PUSH AX
	PUSH DX
	
	MOV DL,sHead.nextX
	MOV DH,sHead.nextY
	
	CALL checkIfBodyNode		; 检查蛇头是否是蛇身的一部分
	CMP AX,0
	JZ ggMain1					; 是,结束程序
	JMP ggCISM
	
ggMain1:
	MOV AH,4CH
	INT 21H

ggCISM:
	POP DX
	POP AX
	RET
checkIfSelfMurder ENDP 
	

;=======================================================	

; 子程序名: Delay
; 功能: 延时(进行AX * CX次循环)
; 入口参数: 无
; 出口参数: 无

delay PROC

	PUSH CX
	PUSH AX
	MOV  AX,0000100111111111B
;	MOV  AX,0FFFFH
	
MoreTick:	
	MOV  CX,1111111111111111B
OneTick:
	LOOP OneTick
	DEC AX
	JNZ MoreTick
	
	POP  AX
	POP  CX
	RET
delay ENDP

;======================================================================

; 子程序名: checkIfOutside
; 功能: 检查是否越界
; 入口参数: 无
; 出口参数: 无

checkIfOutside PROC
	CMP sHead.nextY,TOP-1
	JNA fromButtom				; 超过顶线,从下面出来

	CMP sHead.nextY,BUTTOM+1
	JNB fromTop					; 超过底线,从上面出来
	
	CMP sHead.nextX,LEFT-1
	JNA fromRight				; 超过左线,从右边出来
	
	CMP sHead.nextX,RIGHT+1
	JNB fromLeft				; 超过右线,从左边出来

	JMP ggCIO
	
fromButtom:
	MOV sHead.nextY,BUTTOM		; 头结点从最下边开始出来
	JMP ggCIO
	
fromTop:
	MOV sHead.nextY,TOP			; 同上
	JMP ggCIO
	
fromRight:
	MOV sHead.nextX,RIGHT
	JMP ggCIO

fromLeft:
	MOV sHead.nextX,LEFT
	JMP ggCIO
		
ggCIO:
	RET
checkIfOutside ENDP


;=========================================================

; 子程序名: zlfRand
; 功能: 产生一个随机数
; 入口参数: AL = 下限, BL = 上限
; 出口参数: AL = 随机数
; 公式: (种子 * 23 + 89) % (上限 - 下限) + 下限

zlfRand  PROC
	PUSH BX
	PUSH CX
	PUSH DX
	PUSH AX
	
	MOV AH,2CH				; 获取系统时间,做为种子
	INT 21H
	
;	MOV AL,DL				; 这里本来用百秒会达到更加随机的效果
	MOV AL,DH				; 但总是会溢出,就先用秒来做吧,AL = 种子
	MOV CL,23
	MUL CL					; AL * 23,结果在AX中
	ADD AX,89
	MOV CL,BL
	POP BX					; BL为下限
	SUB CL,BL				
	INC CL					; CL = 上限 - 下限
	DIV CL					; 除,余数在AH中
	ADD AH,BL
	MOV AL,AH
	
	POP DX
	POP CX
	POP BX
	RET
zlfRand  ENDP


;=================================================================

; 子程序名: checkIfBodyNode
; 功能: 检查是否是蛇身上的一个点(不包括蛇头)
;		(因为在检查是否自噬的时候会用的这里,所以不能包括蛇头)
; 入口参数: DL = X(列), DH = Y(行)
; 出口参数: AX = 1(不是), AX = 0(是)

checkIfBodyNode PROC
	PUSH SI
	PUSH CX
	
	MOV SI,OFFSET sHead
	ADD SI,TYPE SNODE

	XOR CX,CX
	MOV CL,currLen
	DEC CL
	DEC CL
nextNode:					; 逐个扫描,比较每个节点的nextX,nextY是否与
	CMP DL,[SI].nextX		; DL,DH完全相同
	JNZ unEqual
	CMP DH,[SI].nextY
	JNZ unEqual
	XOR AX,AX
	JMP ggCIBN
	
unEqual:
	ADD SI,TYPE SNODE
	LOOP nextNode
	XOR AX,AX
	MOV AX,1
	
ggCIBN:
	POP CX
	POP SI
	RET
checkIfBodyNode ENDP


;===================================================================

; 子程序名: setFood
; 功能: 设置食物点
; 入口参数: 无
; 出口参数: 无

setFood PROC
	PUSH AX
	PUSH BX
	PUSH DX
	PUSH SI

again1:						; 开始撒种,呵呵
	XOR AX,AX
	XOR BX,BX
	XOR DX,DX
		
	MOV AL,TOP				; 设置入口参数:上限
	MOV BL,BUTTOM			; 设置入口参数:下限
	CALL zlfRand			; 获取随机行号
	MOV DH,AL				; 行号
	
	MOV AL,LEFT				; 设置入口参数
	MOV BL,RIGHT
	CALL zlfRand			; 获取随机列号
	MOV DL,AL				; 列号

	CALL checkIfBodyNode	; 检查是否撒到蛇身上了
	CMP AX,0				; 是,再撒过
	JZ again1

	CMP DL,sHead.nextX		; 检查是否撒到蛇头上了
	JNZ noHead
	CMP DH,sHead.nextY
	JNZ noHead
	JMP again1				; 是,再撒过

noHead:
	MOV food.nextY , DH
	MOV food.nextX , DL
	
	MOV AL,0				; 获取一种随机颜色
	MOV BL,colorNum
	CALL zlfRand			; 随机数在AL中
	
	MOV SI,OFFSET colorBox
	XOR AH,AH
	ADD SI,AX	
	MOV AH,BYTE PTR [SI]	; 这里不能直接赋mov food.color, [si],
	MOV food.color , AH		; 变量之间不能直接赋值
	
drawFood:					; 把食物点画出来
	MOV BH,0
	MOV DH,food.nextY
	MOV DL,food.nextX
	MOV AH,2
	INT 10H
	
	MOV AL,food.char
;	MOV AL,'*'
	MOV BL,food.color
	MOV CX,1
	MOV AH,9
	INT 10H

	POP SI
	POP DX
	POP BX
	POP AX
	RET
setFood ENDP


;=================================================================
	
; 子程序名: addToTail
; 功能: 在蛇尾加上一个节点
; 入口参数: 无
; 出口参数: 无

addToTail PROC
	PUSH AX
	PUSH BX
	PUSH CX
	PUSH DX
	PUSH DI
	PUSH SI
	
	MOV SI,OFFSET sHead
	MOV DI,SI
	ADD DI,TYPE SNODE

	XOR CX,CX
	MOV CL,currLen
	DEC CL
scan1:
	ADD DI,TYPE SNODE		; DI指向最后一个的下一个
	ADD SI,TYPE SNODE		; SI最后一个
	LOOP scan1
	
	MOV AL,[SI].nextX		; 新添加节点下一次的位置
	MOV [DI].nextX , AL		; 就是原来的最后的一个节点
	
	MOV AL,[SI].nextY
	MOV [DI].nextY , AL
	
	MOV AL,food.color		; 颜色就是食物的颜色
	MOV [DI].color , AL
		
	MOV AL,food.char		; 字符就是食物的字符
	MOV [DI].char , AL
	
	INC currLen

drawNewTail:				; 画出新添加的节点
	MOV DH,[DI].nextY
	MOV DL,[DI].nextX
	MOV BH,0	
	MOV AH,2
	INT 10H

	MOV AL,[DI].char
	MOV BL,[DI].color
	MOV CX,1
	MOV AH,9
	INT 10H

	POP SI
	POP DI
	POP DX
	POP CX
	POP BX
	POP AX
	RET
addToTail ENDP


;================================================================

; 子程序名: checkIfEat
; 功能: 检查是否吃到食物
; 入口参数: 无
; 出口参数: 无

checkIfEat PROC
	PUSH AX
	
	MOV AL,food.nextX			; 其实就是判断蛇头坐标与
	CMP sHead.nextX , AL		; 食物坐标是否重合而已
	JNZ noEat
	MOV AL,food.nextY
	CMP sHead.nextY , AL
	JNZ noEat
	
	CALL addToTail				; 吃到食物了,添加到蛇尾
	CALL setFood				; 重新撒种
noEat:
	POP AX
	RET
checkIfEat ENDP

;==================================================================

CSEG ENDS
END  START

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -