📄 zlfsnakev1.00.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 + -