8086汇编实现的五子棋小游戏
一、五子程序设计要求
汇编实现五子棋游戏。
首先显示空白棋盘,让玩家选择旗色,白子先行。
进入游戏对弈循环
只要有一方连成同色五子,即获胜。
事先写了一个普通的c++五子棋,然后将这个思路用汇编实现出来,代码逾500行,五子棋功能比较完善了,获胜逻辑什么的判断部分基本没有问题,花费了我很大的精力。选择旗色,先手玩家即为白色,后手玩家为黑色,分别以W,B代表黑白棋子。
二、设计思路
五子棋,首先需要显示棋盘,然后玩家输入落子坐标,白方落子,黑方落子,落子循环,其中每次落子都要判断一次落子点是否已经有棋子了,那就需要提醒玩家重新输入落子坐标,同理玩家输入的坐标超出棋盘边界的时候也要提醒玩家。
落子的部分解决了,就得考虑获胜的判定逻辑了,每次成功的落子,就要对该坐标进行判定,我的判定获胜的方法是这样的,对于落子坐标进行四类判定,一种是横向的,一种是纵向的,另外两类是左上到右下,与左下到右上的,这样就涵盖了所有的获胜的情况,并且具体怎么实现?设定一个变量COUNT计算同一线上的同色棋子数目,COUNT初始为1。从落子坐标出发,对于横向的,分两种搜索,一类向左搜索,遇到同类的棋子COUNT+1,一旦不是同色棋子或者是搜索到棋盘外便转到向右搜索的循环中去,每次COUNT+1后判断是否达到5,达到5了则当前落子方获胜,遇到非同色棋子或搜索到棋盘外跳出循环,重置COUNT为1后进入其它三类判定,其它三类的获胜判定与该法原理一致。原理简单易懂,但是在汇编上实现起来还是遇到了不少问题的。同时,每次有效落子的时候有个变量OVERFLOW需要+1,这个是计算和棋的变量,五子棋的棋盘是15*15大小的,当OVERFLOW达到225时还没有决出胜负,这时候便可以宣布和棋了。
一些实现上的问题,由于8086汇编的输入中断一次仅识别一个ASII字符,导致两位数的输入还得手撸,便设置了两个暂存量TI,TJ用于暂存输入,事后再赋值给真正的落子坐标I,J。I,J的作用主要是控制循环,在获胜逻辑判断内判定是否已经超出棋盘边界了。汇编语言没有什么数据结构,因此我的棋盘MP二维数组的所有元素访问都要以一维的形式进行,应用基址+偏移寻址来找到需要的元素进行修改。还有条件跳转的范围实在太小了,汇编代码写着写着就很长了,需要经过好几个中转的跳转来实现一个大的循环,每次找错需要非常认真一句一句认真看,不过最后还是做完了。
三、核心代码

1.显示棋盘部分:
……
MOV DX,OFFSET STR2 ;输入提示语句
MOV AH,9
INT 21h
MOV BX,OFFSET MP ;当前是第几个元素
MOV CH,0 ;行数
NICE:
MOV CL,0 ;列数
CMP CH,10 ;行数大于等于10不用加前缀0
JAE UPTEN
MOV AH,02H
MOV DL,'0' ;加0前缀
INT 21H
MOV AH,02H
MOV DL, CH
ADD DL,30H
INT 21H
……
JMP LINE ;小于10的数进入打印棋盘
UPTEN:
MOV AH,02H
MOV DL,'1' ;十位
INT 21H
MOV AH,02H
MOV DL,CH
ADD DL,38 ;个位
INT 21H
…… ;省略的是字符间隔调整部分
LINE:
MOV AH, 02H
MOV DL,[BX]
ADD DL,30H
INT 21H
MOV AH,02H
MOV DL, ' '
INT 21H
MOV AH,02H
MOV DL, ' '
INT 21H
ADD BX,1 ;往后一个元素
ADD CL,1
CMP CL,15
JB LINE ;当前行未到尾部,继续输出
MOV DL,0DH
MOV AH,2
INT 21H
MOV DL,0AH ;显示换行
MOV AH,2
INT 21H
ADD CH,1;显示当前行 自增到下一行
CMP CH,15
JAE FIN ;棋盘显示完毕,结束
JMP NICE
FIN:
POP DX ;结束棋盘输出 恢复现场
POP CX
POP BX
POP AX
RET ;返回原调用程序
SHOW ENDP
该段代码将棋盘MP的元素逐行输出,同时在左侧和上侧显示行数列数提示,便于用户确定落子坐标。
2.获胜逻辑判断部分:
其中BX是玩家选择的落子坐标的偏移地址,在主调用中给出
WIN PROC NEAR ;获胜逻辑判断子程序
PUSH AX
PUSH BX
PUSH CX
PUSH DX;保存现场
MOV AL,[BX] ;当前是哪一方落子 这个NOW是W/B的ASII码减去30H的值
MOV NOW,AL
MOV CL,J; 从左到右的判断 根据J当前值确定是否退出循环
LEFT_TO_RIGHT_LEFT: ;从左到右时count不刷新,进入新的一条线五子判断时才需要
DEC CL ;往左一个元素
CMP CL,0 ;小于0则跳入右判断
JB LEFT_TO_RIGHT_RIGHT
SUB BX,1 ;BX地址减1
CMP AL,[BX]
JNE LEFT_TO_RIGHT_RIGHT ;与落子的棋子不同
ADD COUNT,1 ;若相同,一条线上的棋子数+1
CMP COUNT,5 ;若COUNT达到5,说明有一方已经胜利了
JE FIVE_TO_WINN ;此处条件跳转可能超出 需要找个中继继续跳转
JMP LEFT_TO_RIGHT_LEFT ;循环继续
LEFT_TO_RIGHT_RIGHT:
MOV CL,J ;需要初始化一些数据
MOV BX,OFFSET MP ;重新定位到落子坐标
ADD BX,CNTW ;CNTW是落子坐标相对于MP的地址的16位偏移量
START_1:
INC CL ;往右一个元素
CMP CL,14 ;大于14则跳入下一个判断
JA UP_TIL_DOWN_UP
ADD BX,1
CMP AL,[BX]
JNE UP_TIL_DOWN_UP ;不等时跳入下一个判断区间
ADD COUNT,1 ;若相同,一条线上的棋子数+1
CMP COUNT,5 ;若COUNT达到5,说明有一方已经胜利了
JE FIVE_TO_WINN
JMP START_1 ;循环继续
……
后续的三类判断与此相似,此处便不赘叙,需要注意每次继续搜索判断的下一个元素地址上会相差±1,15±1,±15,这样。但原理是差不多的。
3.落子坐标输入部分:格式3 4#当后一位数仅有一位需要以#结尾
MOV AH, 1 ; ah设置成1模式,用于从键盘输入
INT 21H ; 调用系统中断,输入
CMP AL,30H
JB W_NOT
CMP AL,39H
JA W_NOT
JMP NEXTW ;第一个输入无误
W_NOT:
MOV DL,0DH
MOV AH,2
INT 21H
MOV DL,0AH ;显示换行
MOV AH,2
INT 21H
MOV DX,OFFSET STR6
MOV AH,9
INT 21h
JMP IN_WHITE ;返回白方落子循环处
NEXTW:
SUB AL, 30H ; 输入的结果默认是放在al的,减去30h是为了把字符转换成数字
MOV TI,AL ;暂存这个量
MOV AH, 1 ; ah设置成1模式,用于从键盘输入
INT 21H ; 调用系统中断,输入
CMP AL,20H
JE NEXTWW ;检测到空格 跳到接受第二个数的位置
CMP AL,30H
JB W_NOT
CMP AL,34H
JA W_NOT
CMP TI,1
JA W_NOT ;超出14的说明输入错误 否则TI为1
MOV TI,10
SUB AL, 30H ; 输入的结果默认是放在al的,减去30h是为了把字符转换成数字
MOV TJ,AL ;暂存这个量
NEXTWW:
MOV BL,TI
ADD BL,TJ
MOV I,BL ;保留行数据
MOV TI,0
MOV TJ,0 ;初始化,防止数据遗留出问题
SEC:
MOV AH, 1 ; ah设置成1模式,用于从键盘输入
INT 21H ; 调用系统中断,输入
CMP AL,20H
JE SEC ;检测到空格 返回一段
CMP AL,30H
JB W_NOT
CMP AL,39H
follow: JA W_NOT ;下面接力,超出跳转位置
SUB AL, 30H ; 输入的结果默认是放在al的,减去30h是为了把字符转换成数字
MOV TI,AL ;暂存这个量
MOV AH, 1 ; ah设置成1模式,用于从键盘输入
INT 21H ; 调用系统中断,输入
CMP AL,23H ;#结尾
JE ENDWW ;输入结束
CMP AL,30H
JB W_NOT
CMP AL,34H
JA W_NOT
CMP TI,1
JA follow ;超出14的说明输入错误 否则TI为1
MOV TI,10 ;转化成10便于操作。。因为只有10-14所以十位只有这个
SUB AL, 30H ; 输入的结果默认是放在al的,减去30h是为了把字符转换成数字
MOV TJ,AL ;暂存这个量
ENDWW:
MOV DL,0DH
MOV AH,2
INT 21H
MOV DL,0AH ;显示换行
MOV AH,2
INT 21H
MOV BL,TI
ADD BL,TJ
MOV J,BL ;保留列数据
这一块主要是把键盘输入的数据转化为需要落子的坐标,由于一次只识别一个ASII字符,需要进行多次判断,当前输入的是否是两位数,是否输入超出棋盘,会及时更正输入错误,提示玩家重新输入。
运行视频:
https://vkceyugu.cdn.bspapp.com/VKCEYUGU-imgbed/a864674a-c59a-492d-a63f-1ccdd1d12962.mp4
由于几乎是从零开始做,需要汇编环境的准备,去图书馆查阅相关汇编程序设计的书,在书里学到了一些基本的编程语句用法就开始做了。先用c++实现了五子棋pvp对弈的demo,然后打算把这个思路用汇编来实现。demo写出来没花多少时间,但是手动转成汇编可着实有些难办。因为汇编几乎没有什么数据结构,我设想的棋盘数组MP得靠基址+偏移寻址来访问元素,二维化一维,繁琐程度加重了一些,但是好歹费尽一番功夫解决了,期间花一下午排除bug终于弄明白了一些简单的知识,只有BX可以当基址寻址的指定寄存器,偏移地址是不准用变量来表示的,这些句子书里有但是缺乏实操的情况下很难注意到。
8086汇编语言的输入和输出实在是太简陋了,这个部分踩了不少坑,但是最后还是在各路查询资料以及做小的输入输出试验demo下完成了。
循环控制部分也比较麻烦,但是理清这里面的逻辑关系之后,认真写,写着写着反而也挺顺手了,不过总是要考虑每个寄存器的变量要怎么变化,每个细节都不能漏掉。
总体而言,踩过的坑变成了我吸取的教训,修过的bug变成了我难忘的经历,500多行的汇编代码,着实是一番不小的工程了。
下面是全部的500行代码:
1 SSEG SEGMENT STACK 2 STK DB 20 DUP(0) 3 SSEG ENDS 4 DSEG SEGMENT 5 MP DB 225 DUP(0) 6 I DB 0 ;横坐标 7 J DB 0 ;纵坐标 8 TI DB 0; 9 TJ DB 0;暂用变量 10 CNTW DW 0;基址偏移的16位量表示 11 CNT DB 0;基址偏移的8位量表示 12 FINISH DB 0 ;棋局是否完成 13 COUNT DB 1 ;连线上的棋子个数 14 OVERFLOW DB 0 ;棋盘是否满了 下满225次未有人胜利即后宣布和棋 15 NOW DB 0 16 STR1 DB 'Game start,either side can win by reaching five pieces on a line.',0DH,0AH,'$' 17 STR2 DB ' 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14',0DH,0AH,'$' 18 STR3 DB 0DH,0AH,'$' 19 STR4 DB 'White round, please enter two numbers, separated by spaces,end withs #',0DH,0AH,'$' ;各个游戏提示语句 20 STR5 DB 'Black round, please enter two numbers, separated by spaces,end withs #',0DH,0AH,'$' 21 STR6 DB 'The data is wrong, please re-enter it',0DH,0AH,'$' 22 STR7 DB 'White win.',0DH,0AH,'$' 23 STR8 DB 'Black win.',0DH,0AH,'$' 24 STR9 DB 'Game ends,draw.' 25 DSEG ENDS 26 CSEG SEGMENT 27 ASSUME CS:CSEG,DS:DSEG 28 ASSUME SS:SSEG 29 MAIN PROC 30 START: MOV AX,DSEG 31 MOV DS,AX 32 MOV AX,SSEG 33 MOV SS,AX 34 MOV SP,LENGTH STK 35 MOV DX,OFFSET STR1 36 MOV AH,9 37 INT 21h ;显示游戏开始 38 ;这是最开始的外圈循环 白方先手继而黑方 39 40 IN_WHITE: ;白方落子循环 此处黑白落子逻辑判断代码有大部分相似,但是避免跳转逻辑过于复杂因此黑白落子判断分开执行 41 CALL SHOW ;显示棋盘 42 MOV TI,0 43 MOV TJ,0 ;多一层防止数据错误 44 MOV DX,OFFSET STR4 ;白方回合提示 45 MOV AH,9 46 INT 21h 47 MOV AH, 1 ; ah设置成1模式,用于从键盘输入 48 INT 21H ; 调用系统中断,输入 49 CMP AL,30H 50 JB W_NOT 51 CMP AL,39H 52 JA W_NOT 53 JMP NEXTW ;第一个输入无误 54 W_NOT: 55 MOV DL,0DH 56 MOV AH,2 57 INT 21H 58 MOV DL,0AH ;显示换行 59 MOV AH,2 60 INT 21H 61 MOV DX,OFFSET STR6 62 MOV AH,9 63 INT 21h 64 JMP IN_WHITE ;返回白方落子循环处 65 NEXTW: 66 SUB AL, 30H ; 输入的结果默认是放在al的,减去30h是为了把字符转换成数字 67 MOV TI,AL ;暂存这个量 68 MOV AH, 1 ; ah设置成1模式,用于从键盘输入 69 INT 21H ; 调用系统中断,输入 70 CMP AL,20H 71 JE NEXTWW ;检测到空格 跳到接受第二个数的位置 72 CMP AL,30H 73 JB W_NOT 74 CMP AL,34H 75 JA W_NOT 76 CMP TI,1 77 JA W_NOT ;超出14的说明输入错误 否则TI为1 78 MOV TI,10 79 SUB AL, 30H ; 输入的结果默认是放在al的,减去30h是为了把字符转换成数字 80 MOV TJ,AL ;暂存这个量 81 NEXTWW: 82 MOV BL,TI 83 ADD BL,TJ 84 MOV I,BL ;保留行数据 85 MOV TI,0 86 MOV TJ,0 ;初始化,防止数据遗留出问题 87 SEC: 88 MOV AH, 1 ; ah设置成1模式,用于从键盘输入 89 INT 21H ; 调用系统中断,输入 90 CMP AL,20H 91 JE SEC ;检测到空格 返回一段 92 CMP AL,30H 93 JB W_NOT 94 CMP AL,39H 95 follow: JA W_NOT ;下面接力,超出跳转位置 96 SUB AL, 30H ; 输入的结果默认是放在al的,减去30h是为了把字符转换成数字 97 MOV TI,AL ;暂存这个量 98 MOV AH, 1 ; ah设置成1模式,用于从键盘输入 99 INT 21H ; 调用系统中断,输入 100 CMP AL,23H ;#结尾 101 JE ENDWW ;输入结束 102 CMP AL,30H 103 JB W_NOT 104 CMP AL,34H 105 JA W_NOT 106 CMP TI,1 107 JA follow ;超出14的说明输入错误 否则TI为1 108 MOV TI,10 ;转化成10便于操作。。因为只有10-14所以十位只有这个 109 SUB AL, 30H ; 输入的结果默认是放在al的,减去30h是为了把字符转换成数字 110 MOV TJ,AL ;暂存这个量 111 ENDWW: 112 MOV DL,0DH 113 MOV AH,2 114 INT 21H 115 MOV DL,0AH ;显示换行 116 MOV AH,2 117 INT 21H 118 MOV BL,TI 119 ADD BL,TJ 120 MOV J,BL ;保留列数据 121 MOV TI,0 122 MOV TJ,0 ;初始化,防止数据遗留出问题 123 ;获得相对MP的8位/16位的偏移量 124 MOV AL,I ;行数*15+列数才是当前元素的相对于MP的偏移量 125 MOV CL,15 126 MUL CL 127 MOV CNTW,AX 128 MOV DL,BYTE PTR CNTW 129 ADD DL,J 130 MOV CNT,DL ;保留相对于MP的偏移量 为byte 131 MOV AX,WORD PTR CNT 132 MOV CNTW,AX ;保留相对于MP的偏移量 为Word 133 MOV BX,OFFSET MP 134 ADD BX,CNTW 135 MOV AL,[BX] ;取到MP当前玩家准备落子的坐标 136 CMP AL,0 ;此处是否没有落子 137 JNE follow ;不等于0表示此处有落子 该次落子失败,需要重新选择落子处 138 ;成功落子,改变MP的坐标数据 139 ADD OVERFLOW,1 ;落子+1 140 MOV BYTE PTR[BX],27H ;以W代表白棋 B代表黑棋 因为棋盘输出时每个数字自动加30H转为字符,所以此处的W ASII码先减去30H 141 CALL WIN;调用判断获胜与否的子程序 142 CMP FINISH,1 143 JE EXIT_TEMP ;获胜条件已达成 144 145 IN_BLACK: ;黑方落子循环 146 CALL SHOW ;显示棋盘 147 MOV TI,0 148 MOV TJ,0 ;多一层防止数据错误 149 MOV DX,OFFSET STR5 ;黑方回合提示 150 MOV AH,9 151 INT 21h 152 MOV AH, 1 ; ah设置成1模式,用于从键盘输入 153 INT 21H ; 调用系统中断,输入 154 CMP AL,30H 155 JB B_NOT 156 CMP AL,39H 157 JA B_NOT 158 JMP NEXTB ;第一个输入无误 159 B_NOT: 160 MOV DL,0DH 161 MOV AH,2 162 INT 21H 163 MOV DL,0AH ;显示换行 164 MOV AH,2 165 INT 21H 166 MOV DX,OFFSET STR6 167 MOV AH,9 168 INT 21h 169 JMP IN_BLACK ;返回黑方落子循环处 170 ;中转处 171 EXIT_TEMP: 172 CMP FINISH,1 173 JE EXIT_TEMPP ;获胜条件已达成 174 NEXTB: 175 SUB AL, 30H ; 输入的结果默认是放在al的,减去30h是为了把字符转换成数字 176 MOV TI,AL ;暂存这个量 177 MOV AH, 1 ; ah设置成1模式,用于从键盘输入 178 INT 21H ; 调用系统中断,输入 179 CMP AL,20H 180 JE NEXTBB ;检测到空格 跳到接受第二个数的位置 181 CMP AL,30H 182 JB B_NOT 183 CMP AL,34H 184 JA B_NOT 185 CMP TI,1 186 JA B_NOT ;超出14的说明输入错误 否则TI为1 187 MOV TI,10 188 SUB AL, 30H ; 输入的结果默认是放在al的,减去30h是为了把字符转换成数字 189 MOV TJ,AL ;暂存这个量 190 NEXTBB: 191 MOV BL,TI 192 ADD BL,TJ 193 MOV I,BL ;保留行数据 194 MOV TI,0 195 MOV TJ,0 ;初始化,防止数据遗留出问题 196 THR: 197 MOV AH, 1 ; ah设置成1模式,用于从键盘输入 198 INT 21H ; 调用系统中断,输入 199 CMP AL,20H 200 JE THR ;检测到空格 返回一段 201 CMP AL,30H 202 JB B_NOT 203 CMP AL,39H 204 folloB: JA B_NOT ;下面接力,超出跳转位置 205 SUB AL, 30H ; 输入的结果默认是放在al的,减去30h是为了把字符转换成数字 206 MOV TI,AL ;暂存这个量 207 MOV AH, 1 ; ah设置成1模式,用于从键盘输入 208 INT 21H ; 调用系统中断,输入 209 CMP AL,23H ;#结尾 210 JE ENDBB ;输入结束 211 CMP AL,30H 212 JB B_NOT 213 CMP AL,34H 214 JA B_NOT 215 CMP TI,1 216 JA folloB ;超出14的说明输入错误 否则TI为1 217 MOV TI,10 ;转化成10便于操作。。因为只有10-14所以十位只有这个 218 SUB AL, 30H ; 输入的结果默认是放在al的,减去30h是为了把字符转换成数字 219 MOV TJ,AL ;暂存这个量 220 JMP ENDBB ;后面安排一个中转处 221 ;中转处 222 EXIT_TEMPP: 223 CMP FINISH,1 224 JE EXIT ;获胜条件已达成 225 ENDBB: 226 MOV DL,0DH 227 MOV AH,2 228 INT 21H 229 MOV DL,0AH ;显示换行 230 MOV AH,2 231 INT 21H 232 MOV BL,TI 233 ADD BL,TJ 234 MOV J,BL ;保留列数据 235 MOV TI,0 236 MOV TJ,0 ;初始化,防止数据遗留出问题 237 ;获得相对MP的8位/16位的偏移量 238 MOV AL,I ;行数*15+列数才是当前元素的相对于MP的偏移量 239 MOV CL,15 240 MUL CL 241 MOV CNTW,AX 242 MOV DL,BYTE PTR CNTW 243 ADD DL,J 244 MOV CNT,DL ;保留相对于MP的偏移量 为byte 245 MOV AX,WORD PTR CNT 246 MOV CNTW,AX ;保留相对于MP的偏移量 为Word 247 MOV BX,OFFSET MP 248 ADD BX,CNTW 249 MOV AL,[BX] ;取到MP当前玩家准备落子的坐标 250 CMP AL,0 ;此处是否没有落子 251 JNE folloB ;不等于0表示此处有落子 该次落子失败,需要重新选择落子处 252 ;成功落子,改变MP的坐标数据 253 ADD OVERFLOW,1 ;落子+1 254 MOV BYTE PTR[BX],12H ;以W代表白棋 B代表黑棋 因为棋盘输出时每个数字自动加30H转为字符,所以此处的W ASII码先减去30H 255 CALL WIN;调用判断获胜与否的子程序 256 CMP OVERFLOW,225 257 JE OVER_FLOW ;棋盘满了 258 CMP FINISH,1 259 JE EXIT ;获胜条件已达成 260 JMP NEAR PTR IN_WHITE ;游戏没有达到结束条件,继续 261 EXIT: ;棋局结束 262 CALL SHOW 263 MOV AL,NOW ;NOW是获胜时落子的一方 264 CMP AL,27H 265 JE WHITE_WIN 266 CMP AL,12H 267 JE BLACK_WIN 268 OVER_FLOW: 269 CMP FINISH,1 ;最后一子导致五子成线了,由于白子先行必定是白棋落的最后一子,所以此时会白棋胜 270 JE WHITE_WIN 271 MOV DX,OFFSET STR9 272 MOV AH,9 273 INT 21h ;棋盘满了没人获胜,和棋 274 JMP GAME_OVER 275 WHITE_WIN: 276 MOV DX,OFFSET STR7 277 MOV AH,9 278 INT 21h ;白棋获胜 279 JMP GAME_OVER 280 BLACK_WIN: 281 MOV DX,OFFSET STR8 282 MOV AH,9 283 INT 21h ;黑棋获胜 284 JMP GAME_OVER 285 GAME_OVER: 286 MOV AX,4C00H;退出 287 INT 21H 288 MAIN ENDP 289 290 WIN PROC NEAR ;获胜逻辑判断子程序 291 PUSH AX 292 PUSH BX 293 PUSH CX 294 PUSH DX;保存现场 295 MOV COUNT,1 296 MOV AL,[BX] ;当前是哪一方落子 这个NOW是W/B的ASII码减去30H的值 297 MOV NOW,AL 298 MOV CL,J; 从左到右的判断 根据J当前值确定是否退出循环 299 LEFT_TO_RIGHT_LEFT: ;从左到右时count不刷新,进入新的一条线五子判断时才需要 300 DEC CL ;往左一个元素 301 CMP CL,0 ;小于0则跳入右判断 302 JB LEFT_TO_RIGHT_RIGHT 303 SUB BX,1 ;BX地址减1 304 CMP AL,[BX] 305 JNE LEFT_TO_RIGHT_RIGHT ;与落子的棋子不同 306 ADD COUNT,1 ;若相同,一条线上的棋子数+1 307 CMP COUNT,5 ;若COUNT达到5,说明有一方已经胜利了 308 JE FIVE_TO_WINN ;此处条件跳转可能超出 需要找个中继继续跳转 309 JMP LEFT_TO_RIGHT_LEFT ;循环继续 310 LEFT_TO_RIGHT_RIGHT: 311 MOV CL,J ;需要初始化一些数据 312 MOV BX,OFFSET MP ;重新定位到落子坐标 313 ADD BX,CNTW 314 START_1: 315 INC CL ;往右一个元素 316 CMP CL,14 ;大于14则跳入下一个判断 317 JA UP_TIL_DOWN_UP 318 ADD BX,1 319 CMP AL,[BX] 320 JNE UP_TIL_DOWN_UP ;不等时跳入下一个判断区间 321 ADD COUNT,1 ;若相同,一条线上的棋子数+1 322 CMP COUNT,5 ;若COUNT达到5,说明有一方已经胜利了 323 JE FIVE_TO_WINN 324 JMP START_1 ;循环继续 325 UP_TIL_DOWN_UP: 326 MOV CL,I ;需要初始化一些数据 327 MOV BX,OFFSET MP ;重新定位到落子坐标 328 ADD BX,CNTW 329 MOV COUNT,1 ;count初始化 330 START_2: 331 DEC CL ;往上一个元素 332 CMP CL,0 ;小于0则跳入下判断 333 JB UP_TIL_DOWN_DOWN 334 SUB BX,15 ;BX地址减15 335 CMP AL,[BX] 336 JNE UP_TIL_DOWN_DOWN ;与落子的棋子不同 337 ADD COUNT,1 ;若相同,一条线上的棋子数+1 338 CMP COUNT,5 ;若COUNT达到5,说明有一方已经胜利了 339 FIVE_TO_WINN: JE FIVE_TO_WIN 340 JMP START_2 ;循环继续 341 UP_TIL_DOWN_DOWN: 342 MOV CL,I ;需要初始化一些数据 343 MOV BX,OFFSET MP ;重新定位到落子坐标 344 ADD BX,CNTW 345 START_3: 346 INC CL ;往下一个元素 347 CMP CL,14 ;大于14则跳入下判断 348 JA LEFTUP_TO_RIGHTDOWN_LEFTUP 349 ADD BX,15 ;BX地址加15 350 CMP AL,[BX] 351 JNE LEFTUP_TO_RIGHTDOWN_LEFTUP ;与落子的棋子不同 352 ADD COUNT,1 ;若相同,一条线上的棋子数+1 353 CMP COUNT,5 ;若COUNT达到5,说明有一方已经胜利了 354 JE FIVE_TO_WIN 355 JMP START_3 ;循环继续 356 LEFTUP_TO_RIGHTDOWN_LEFTUP: ;左上到右下 左上部分 357 MOV CL,I ;需要初始化一些数据 这是行 数据 358 MOV DL,J ;这是列 数据 359 MOV BX,OFFSET MP ;重新定位到落子坐标 360 ADD BX,CNTW 361 MOV COUNT,1 ;count初始化 362 START_4: 363 DEC CL ;往左上一个元素 364 DEC DL 365 CMP CL,0 ;小于0则跳入下判断 366 JB LEFTUP_TO_RIGHTDOWN_RIGHTDOWN 367 CMP DL,0 368 JB LEFTUP_TO_RIGHTDOWN_RIGHTDOWN 369 SUB BX,16 ;BX地址减16 370 CMP AL,[BX] 371 JNE LEFTUP_TO_RIGHTDOWN_RIGHTDOWN ;与落子的棋子不同 372 ADD COUNT,1 ;若相同,一条线上的棋子数+1 373 CMP COUNT,5 ;若COUNT达到5,说明有一方已经胜利了 374 FIVE_TO_WIN: 375 JE FIVE_WINNN 376 JMP START_4 ;循环继续 377 LEFTUP_TO_RIGHTDOWN_RIGHTDOWN: ;右下部分 378 MOV CL,I ;需要初始化一些数据 这是行 数据 379 MOV DL,J ;这是列 数据 380 MOV BX,OFFSET MP ;重新定位到落子坐标 381 ADD BX,CNTW 382 START_5: 383 INC CL ;往右下一个元素 384 INC DL 385 CMP CL,14 ;大于14则跳入下一个判断 386 JA LEFTDOWN_TO_RIGHTUP_LEFTDOWN 387 CMP DL,14 388 JA LEFTDOWN_TO_RIGHTUP_LEFTDOWN 389 ADD BX,16 390 CMP AL,[BX] 391 JNE LEFTDOWN_TO_RIGHTUP_LEFTDOWN ;不等时跳入下一个判断区间 392 ADD COUNT,1 ;若相同,一条线上的棋子数+1 393 CMP COUNT,5 ;若COUNT达到5,说明有一方已经胜利了 394 FIVE_WINNN: JE FIVE_WIN 395 JMP START_5 ;循环继续 396 LEFTDOWN_TO_RIGHTUP_LEFTDOWN: ;左下到右上 左下部分 397 MOV CL,I ;需要初始化一些数据 这是行 数据 398 MOV DL,J ;这是列 数据 399 MOV BX,OFFSET MP ;重新定位到落子坐标 400 ADD BX,CNTW 401 MOV COUNT,1 ;count初始化 402 START_6: 403 INC CL ;往左下一个元素 404 DEC DL 405 CMP CL,14 ;大于14则跳入下判断 406 JA LEFTDOWN_TO_RIGHTUP_RIGHTUP 407 CMP DL,0 408 JB LEFTDOWN_TO_RIGHTUP_RIGHTUP 409 ADD BX,14 ;BX地址加14 410 CMP AL,[BX] 411 JNE LEFTDOWN_TO_RIGHTUP_RIGHTUP ;与落子的棋子不同 412 ADD COUNT,1 ;若相同,一条线上的棋子数+1 413 CMP COUNT,5 ;若COUNT达到5,说明有一方已经胜利了 414 JE FIVE_WIN 415 JMP START_6 ;循环继续 416 LEFTDOWN_TO_RIGHTUP_RIGHTUP: ;右上部分 417 MOV CL,I ;需要初始化一些数据 这是行 数据 418 MOV DL,J ;这是列 数据 419 MOV BX,OFFSET MP ;重新定位到落子坐标 420 ADD BX,CNTW 421 START_7: 422 DEC CL ;往右上一个元素 423 INC DL 424 CMP CL,0 ;小于0则结束四个获胜判断 425 JB OK 426 CMP DL,14 427 JA OK 428 SUB BX,14 429 CMP AL,[BX] 430 JNE OK 431 ADD COUNT,1 ;若相同,一条线上的棋子数+1 432 CMP COUNT,5 ;若COUNT达到5,说明有一方已经胜利了 433 JE FIVE_WIN 434 JMP START_7 ;循环继续 435 FIVE_WIN: ;出现了获胜的棋面 结束游戏并显示胜利者 436 MOV FINISH,1 ;用这个变量判断胜利 437 OK: 438 MOV COUNT,1 439 POP DX ;结束胜负判断 恢复现场 440 POP CX 441 POP BX 442 POP AX 443 RET ;返回原调用程序 444 WIN ENDP 445 446 SHOW PROC NEAR ;屏幕显示棋盘子程序 447 PUSH AX 448 PUSH BX 449 PUSH CX 450 PUSH DX;保存现场 451 MOV DX,OFFSET STR2 452 MOV AH,9 453 INT 21h 454 MOV BX,OFFSET MP ;当前是第几个元素 455 MOV CH,0 ;行数 456 NICE: 457 MOV CL,0 ;列数 458 CMP CH,10 ;行数大于等于10不用加空格前缀 459 JAE UPTEN 460 MOV AH,02H 461 MOV DL,' ' ;加空格前缀 462 INT 21H 463 MOV AH,02H 464 MOV DL, CH 465 ADD DL,30H 466 INT 21H 467 MOV AH,02H 468 MOV DL, ' ' 469 INT 21H 470 MOV AH,02H 471 MOV DL, ' ' 472 INT 21H 473 JMP LINE ;小于10的数进入打印棋盘 474 UPTEN: 475 MOV AH,02H 476 MOV DL,'1' ;十位 477 INT 21H 478 MOV AH,02H 479 MOV DL,CH 480 ADD DL,38 ;个位 481 INT 21H 482 MOV AH,02H 483 MOV DL, ' ' 484 INT 21H 485 MOV AH,02H 486 MOV DL, ' ' ;字符间隔调整 487 INT 21H 488 LINE: 489 MOV AH, 02H 490 MOV DL,[BX] 491 ADD DL,30H 492 INT 21H 493 MOV AH,02H 494 MOV DL, ' ' 495 INT 21H 496 MOV AH,02H 497 MOV DL, ' ' 498 INT 21H 499 ADD BX,1 ;往后一个元素 500 ADD CL,1 501 CMP CL,15 502 JB LINE ;当前行未到尾部,继续输出 503 MOV DL,0DH 504 MOV AH,2 505 INT 21H 506 MOV DL,0AH ;显示换行 507 MOV AH,2 508 INT 21H 509 ADD CH,1;显示当前行 自增到下一行 510 CMP CH,15 511 JAE FIN ;棋盘显示完毕,结束 512 JMP NICE 513 FIN: 514 POP DX ;结束棋盘输出 恢复现场 515 POP CX 516 POP BX 517 POP AX 518 RET ;返回原调用程序 519 SHOW ENDP 520 CSEG ENDS 521 END START ;终于写完了!!!

浙公网安备 33010602011771号