├── DOSBox0.74-win32-installer.exe ├── G_Snake ├── 1.png ├── 2.png ├── 3.png ├── G_Snake.asm ├── LINK.EXE ├── MASM.EXE ├── debug.exe ├── edit.com ├── map.asm ├── sMA.asm ├── sMove.asm └── 贪吃蛇加入自动回放.asm └── readme.md /DOSBox0.74-win32-installer.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meihao1203/Greedy_Snake/877fb39fb1c6d5e01d781bc2d372c6402605f279/DOSBox0.74-win32-installer.exe -------------------------------------------------------------------------------- /G_Snake/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meihao1203/Greedy_Snake/877fb39fb1c6d5e01d781bc2d372c6402605f279/G_Snake/1.png -------------------------------------------------------------------------------- /G_Snake/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meihao1203/Greedy_Snake/877fb39fb1c6d5e01d781bc2d372c6402605f279/G_Snake/2.png -------------------------------------------------------------------------------- /G_Snake/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meihao1203/Greedy_Snake/877fb39fb1c6d5e01d781bc2d372c6402605f279/G_Snake/3.png -------------------------------------------------------------------------------- /G_Snake/G_Snake.asm: -------------------------------------------------------------------------------- 1 | assume cs:code , ds:data , ss:stack 2 | data segment 3 | ;db 256 dup(0) ; 代码一点一点写,每个调用都测试一下,现在要写真正程序用到的,就注释掉这里 4 | BOUNDARY_COLOR dw 4431h ; 直接定址法,颜色=0100100b,字符31h=数字1 5 | NEXT_ROL dw 0A0h ; a0=160 6 | SNAKE_HEAD dw 0 7 | SNAKE_BODY dw 6 8 | SNAKE_STERN dw 12 9 | SNAKE dw 200 dup (0,0,0) ; 三个数是来记录前一个节点,中间点的位置,记录下一个点在内存中的相对偏移 10 | ;00 00 00 00 00 00 11 | SNAKE_COLOR dw 2201h ; 颜色00100010b,字符01h='☺' 12 | 13 | UP db 48h 14 | DOWN db 50h 15 | LEFT db 4Bh 16 | RIGHT db 4Dh 17 | 18 | SCREEN_COLOR dw 0700h 19 | 20 | NEXT_ROW dw 160 21 | 22 | DIRECTION dw 3 ; 设置自动移动 23 | DIRECTION_FUN dw offset isMoveUp - offset greedy_snake + 7e00h ; [0] 24 | dw offset isMoveDown - offset greedy_snake + 7e00h ; [2] 25 | dw offset isMoveLeft - offset greedy_snake + 7e00h ; [4] 26 | dw offset isMoveRight - offset greedy_snake + 7e00h ; [6] 27 | 28 | FOOD_LOCATION dw 160*3 + 20*2 ; 先人为的设定一个位置 29 | FOOD_COLOR dw 4439h ; 39h=字符'9' , 44h=01000100b 红色 30 | NEW_NODE dw 18 ; 后面吃到食物那要用到,18就是我们初始化小蛇后蛇尾的下一个位置 31 | 32 | GAME_OVER db 'Game Over!' 33 | GAME_DIR db 'direction: ' 34 | SCORE_STR db 'Score=' 35 | SCORE_CHAR db '0123456789ABCDEF' 36 | SCORE dw 0h 37 | SCORE_POSITION dw 160*24+60*2 38 | 39 | 40 | data ends 41 | 42 | stack segment 43 | db 128 dup(0) 44 | stack ends 45 | 46 | code segment 47 | start: 48 | mov ax , stack 49 | mov ss , ax 50 | mov sp , 128 51 | 52 | call cpy_greedy_snake 53 | call sav_old_int9 54 | call set_new_int9 55 | 56 | 57 | mov bx , 0 58 | push bx 59 | mov bx , 7e00h 60 | push bx 61 | retf ; pop ip , pop cs ; CS:IP=0:7e00 62 | 63 | mov ax , 4c00h 64 | int 21h 65 | 66 | greedy_snake: 67 | call init_reg 68 | call clear_screen 69 | call init_screen 70 | call init_food 71 | call init_snake ; 画出蛇的图形 72 | 73 | 74 | 75 | nextMove: 76 | call delay 77 | cli 78 | call isMoveDirection 79 | sti 80 | jmp nextMove 81 | 82 | 83 | 84 | testA: ; 无限循环,测试 85 | mov ax , 1000h 86 | jmp testA 87 | 88 | mov ax , 4c00h 89 | int 21h 90 | 91 | 92 | 93 | ;----------------------------------------- 94 | init_food: 95 | mov di , FOOD_LOCATION 96 | push FOOD_COLOR 97 | pop es:[di] 98 | ret 99 | 100 | 101 | ;----------------------------------------- 102 | isMoveDirection: 103 | mov bx , DIRECTION 104 | add bx , bx 105 | call word ptr ds:DIRECTION_FUN[bx] 106 | ret 107 | 108 | 109 | 110 | ;----------------------------------------- 111 | delay: 112 | push ax 113 | push dx 114 | 115 | mov dx , 3h 116 | sub ax , ax 117 | delaying: 118 | sub ax , 1 119 | sbb dx , 0 120 | cmp ax , 0 121 | jne delaying 122 | cmp dx , 0 123 | jne delaying 124 | 125 | 126 | 127 | pop dx 128 | pop ax 129 | 130 | ret 131 | 132 | 133 | ; 双向链表数据结构 134 | ;----------------------------------------- 135 | init_snake: 136 | mov bx , offset SNAKE 137 | add bx , SNAKE_HEAD 138 | mov si , 160*10+40*2 ; 屏幕上的位置(0690h) 139 | mov dx , SNAKE_COLOR ; 2201h 140 | 141 | mov word ptr ds:[bx+0] , 0 ; ds:[bx+0] = 0 142 | mov ds:[bx+2] , si 143 | mov es:[si] , dx ; 设置蛇的颜色 144 | mov word ptr ds:[bx+4] , 6 145 | 146 | sub si , 2 147 | add bx , 6 148 | 149 | mov word ptr ds:[bx+0] , 0 ; ds:[bx+0] = 0 150 | mov ds:[bx+2] , si 151 | mov es:[si] , dx ; 设置蛇的颜色 152 | mov word ptr ds:[bx+4] , 12 153 | 154 | sub si , 2 155 | add bx , 6 156 | 157 | mov word ptr ds:[bx+0] , 6 ; ds:[bx+0] = 0 158 | mov ds:[bx+2] , si 159 | mov es:[si] , dx ; 设置蛇的颜色 160 | mov word ptr ds:[bx+4] , 18 161 | 162 | 163 | ret 164 | 165 | 166 | ;----------------------------------------- 167 | init_screen: 168 | mov dx , BOUNDARY_COLOR ; 设置游戏界面边框的颜色 169 | call show_up_down_line ; 画出游戏上下边界的边框 170 | call show_left_right_line ; 画出游戏左右边界的边框 171 | call show_score ; 初始化界面下面输出字符串score= 172 | call output_score ; 输出成绩,初始为 0 173 | call show_direction ; 显示dierction 174 | 175 | ret 176 | 177 | 178 | ;----------------------------------------- 179 | show_direction: 180 | mov si , offset GAME_DIR 181 | mov di , 160*24+0*2 182 | mov cx , 11 183 | showDirection: 184 | mov al , ds:[si] 185 | mov es:[di] , al 186 | mov byte ptr es:[di+1] , 00000010b 187 | inc si 188 | add di , 2 189 | loop showDirection 190 | ret 191 | 192 | 193 | ;----------------------------------------- 194 | show_score: 195 | mov si , offset SCORE_STR 196 | mov di , 160*24+50*2 197 | 198 | mov cx , 6 199 | showScore: 200 | mov al , ds:[si] 201 | mov es:[di] , al 202 | mov byte ptr es:[di+1] , 00000010b 203 | inc si 204 | add di , 2 205 | loop showScore 206 | ret 207 | 208 | 209 | ;----------------------------------------- 210 | show_up_down_line: 211 | mov bx , 0 212 | mov cx , 80 213 | showUpDownLine: 214 | mov es:[bx] , dx ; dx=4431h,字符31h=数字1,颜色=01000100b 215 | mov es:[bx+160*23] , dx 216 | add bx , 2 217 | loop showUpDownLine 218 | ret 219 | 220 | 221 | ;----------------------------------------- 222 | output_score: 223 | mov si , offset SCORE 224 | mov ax , ds:[si] 225 | 226 | mov si , SCORE_POSITION 227 | mov dx , ax 228 | 229 | mov al , ah 230 | mov ah , 0 231 | mov cx , 4 232 | shr al , cl ; 得到最高位 233 | mov bx , ax 234 | mov al , ds:SCORE_CHAR[bx] 235 | mov byte ptr es:[si] , al 236 | mov byte ptr es:[si+1] , 00001010b 237 | add si , 2 238 | 239 | mov ax , dx 240 | mov al , ah 241 | mov ah , 0 242 | mov cx , 4 243 | shl al , cl 244 | shr al , cl ; 次高位 245 | mov bx , ax 246 | mov al , ds:SCORE_CHAR[bx] 247 | mov byte ptr es:[si] , al 248 | mov byte ptr es:[si+1] , 00001010b 249 | add si , 2 250 | 251 | mov ax , dx 252 | mov ah , 0 253 | mov cx , 4 254 | shr al , cl ; 次次高位 255 | mov bx , ax 256 | mov al , ds:SCORE_CHAR[bx] 257 | mov byte ptr es:[si] , al 258 | mov byte ptr es:[si+1] , 00001010b 259 | add si , 2 260 | 261 | mov ax , dx 262 | mov ah , 0 263 | mov cx , 4 264 | shl al , cl 265 | shr al , cl ; 最低位 266 | mov bx , ax 267 | mov al , ds:SCORE_CHAR[bx] 268 | mov byte ptr es:[si] , al 269 | mov byte ptr es:[si+1] , 00001010b 270 | add si , 2 271 | 272 | mov byte ptr es:[si] , 'H' 273 | mov byte ptr es:[si+1] , 00001010b 274 | 275 | 276 | ret 277 | 278 | 279 | ;----------------------------------------- 280 | show_left_right_line: 281 | mov bx , 0 282 | mov cx , 23 283 | showLeftRightLine: 284 | mov es:[bx] , dx 285 | mov es:[bx+79*2] , dx 286 | ;add bx , 160 287 | add bx , NEXT_ROW ; 优化写法 288 | loop showLeftRightLine 289 | ret 290 | 291 | 292 | ;----------------------------------------- 293 | init_reg: 294 | mov bx , 0b800h 295 | mov es , bx 296 | 297 | mov bx , data 298 | mov ds , bx 299 | ret 300 | 301 | 302 | ;----------------------------------------- 303 | clear_screen: 304 | mov bx , 0 305 | mov dx , SCREEN_COLOR 306 | mov cx , 2000 307 | 308 | clearScreen: 309 | mov es:[bx] , dx ; (dx)=0700,es:[0]~es:[1]=00,es:[2]~es:[3]=07,颜色属性是07,RGB=白色 310 | add bx , 2 311 | loop clearScreen 312 | ret 313 | 314 | 315 | ;------------------------------------------- 316 | new_int9: 317 | push ax 318 | 319 | call clear_buff ; 清空键盘缓冲区 320 | 321 | in al , 60h ; 60h号端口读取的是键盘扫描码 322 | pushf 323 | call dword ptr cs:[200h] 324 | 325 | cmp al , UP ; 记录方向键扫描码 326 | je isUp 327 | cmp al , LEFT 328 | je isLeft 329 | cmp al , RIGHT 330 | je isRight 331 | cmp al , DOWN 332 | je isDown 333 | 334 | cmp al , 3bh ; 字符 ‘F1’ 的扫描码 335 | jne int9Ret 336 | call change_screen_color ;写来测试看,我写的9号中断处理键盘缓冲区能不能正常工作 337 | 338 | 339 | int9Ret: 340 | pop ax 341 | iret 342 | 343 | 344 | ;------------------------------------------- 345 | isUp: 346 | mov di , 160*24 + 12*2 ; 每行最下面显示出按下的按键 347 | mov byte ptr es:[di] , 'U' 348 | mov byte ptr es:[di+1] , 00001010b 349 | cmp DIRECTION , 1 ; 自动移动加入的时候,按下按键要进行判断,如果水平方向相反,就不用移动了 350 | ; 下面写的移动函数,在移动前会判断将要移动到的位置的字符信息是不是背景(也就是没显示其他的东西),如果是 351 | ; 就直接返回了,这么写是代码风格更好。效率也高 352 | je int9Ret 353 | 354 | call isMoveUp 355 | jmp int9Ret 356 | 357 | isDown: 358 | mov di , 160*24 + 12*2 ; 每行最下面显示出按下的按键 359 | mov byte ptr es:[di] , 'D' 360 | mov byte ptr es:[di+1] , 00001010b 361 | cmp DIRECTION , 0 362 | je int9Ret 363 | 364 | call isMoveDown 365 | jmp int9Ret 366 | 367 | isLeft: 368 | mov di , 160*24 + 12*2 ; 每行最下面显示出按下的按键 369 | mov byte ptr es:[di] , 'L' 370 | mov byte ptr es:[di+1] , 00001010b 371 | cmp DIRECTION , 3 372 | je int9Ret 373 | 374 | call isMoveLeft 375 | jmp int9Ret 376 | 377 | isRight: 378 | mov di , 160*24 + 12*2 ; 每行最下面显示出按下的按键 379 | mov byte ptr es:[di] , 'R' 380 | mov byte ptr es:[di+1] , 00001010b 381 | cmp DIRECTION , 2 382 | je int9Ret 383 | 384 | call isMoveRight 385 | jmp int9Ret 386 | 387 | 388 | ;------------------------------------------- 389 | isMoveUp: 390 | mov bx , offset SNAKE 391 | add bx , SNAKE_HEAD 392 | mov si , ds:[bx+2] ; bx+2 获得节点中间的值,这个值记录的是该节点的位置 393 | sub si , NEXT_ROW ; 向上走了一步,所以中间节点的位置也要偏移160,刚好是一行 394 | 395 | cmp byte ptr es:[si] , 0 ; 向上走一步如果es:[si]=1,表示到显示区的边界了,不能移动 396 | jne noMoveUp 397 | call draw_new_snake ; 移动,重新绘制蛇 398 | 399 | mov DIRECTION , 0 ; 在按下相应按键,蛇能动了之后,就要设置自动移动了 400 | ret 401 | 402 | noMoveUp: 403 | call isFood 404 | ret 405 | 406 | 407 | ;------------------------------------------- 408 | isMoveDown: 409 | mov bx , offset SNAKE 410 | add bx , SNAKE_HEAD 411 | mov si , ds:[bx+2] 412 | add si , NEXT_ROW 413 | 414 | cmp byte ptr es:[si] , 0 415 | jne noMoveDown 416 | call draw_new_snake 417 | 418 | mov DIRECTION , 1 419 | ret 420 | noMoveDown: 421 | call isFood ;下一步不能走,加入食物后也要判断下是不是食物 422 | ret 423 | 424 | 425 | ;------------------------------------------- 426 | isMoveLeft: 427 | mov bx , offset SNAKE 428 | add bx , SNAKE_HEAD 429 | mov si , ds:[bx+2] 430 | sub si , 2 431 | 432 | cmp byte ptr es:[si] , 0 433 | jne noMoveDown 434 | call draw_new_snake 435 | mov DIRECTION , 2 436 | ret 437 | noMoveLeft: 438 | call isFood 439 | ret 440 | 441 | 442 | ;------------------------------------------- 443 | isMoveRight: 444 | mov bx , offset SNAKE 445 | add bx , SNAKE_HEAD 446 | mov si , ds:[bx+2] 447 | add si , 2 448 | 449 | cmp byte ptr es:[si] , 0 450 | jne noMoveDown 451 | call draw_new_snake 452 | mov DIRECTION , 3 453 | ret 454 | noMoveRight: 455 | call isFood 456 | ret 457 | 458 | 459 | ;------------------------------------------- 460 | isFood: 461 | cmp byte ptr es:[si] , '9' ; 我们前面设置了食物的字符是9,前景背景都是红色 462 | jne noFood 463 | 464 | call eat_food 465 | call set_new_food ; 吃掉一个要再生成一个 466 | ret 467 | noFood: 468 | call clear_screen 469 | call recover_int9Ret 470 | call end_game 471 | call return_dos 472 | 473 | ret 474 | 475 | 476 | ;------------------------------------------- 477 | return_dos: 478 | mov ax , 4c00h 479 | int 21h 480 | 481 | 482 | ;------------------------------------------- 483 | recover_int9Ret: 484 | push es 485 | mov bx , 0 486 | mov es , bx 487 | push es:[200h] 488 | pop es:[9*4] 489 | push es:[202h] 490 | pop es:[9*4+2] 491 | pop es 492 | ret 493 | ;------------------------------------------- 494 | end_game: 495 | mov si , offset GAME_OVER 496 | mov di , 160*12+35*2 497 | mov cx , 10 498 | endGame: 499 | mov al , ds:[si] 500 | mov byte ptr es:[di] , al 501 | mov byte ptr es:[di+1] , 00001100b 502 | inc si 503 | add di , 2 504 | loop endGame 505 | 506 | ret 507 | 508 | 509 | ;------------------------------------------- 510 | set_new_food: 511 | mov al , 0 512 | out 70h , al 513 | in al , 71h 514 | 515 | mov dl , al 516 | and dl , 00001111b ; dl中是个位数的数字 517 | push cx 518 | mov cl , 4 519 | mov ch , 0 520 | shr al , cl ; al中是十位数的数字 521 | pop cx ; 之前这里没有pop,导致出错,调试半天 522 | mov bl , 10 523 | mul bl ; ax=al*bl 524 | add al , dl ; 得到秒数 525 | 526 | mul al ; 如果al是奇数,得到的肯定也是一个奇数;25*80*2=4000种显示位置,最后一行是3840~4000 527 | ; 按描述算,60*60=3600种位置,所以食物不可能随机到最后一行,也不会到达游戏界面的下边界,导致蛇吃不到食物 528 | shr al , 1 ; 二进制,右移一位去掉产生奇数的1 529 | shl al , 1 ; 控制误差,再左移一位,这样误差就为1 530 | mov bx , ax ; 得到下一个食物出现的位置 531 | cmp byte ptr es:[bx] , 0 ; 如果得到的位置不是空闲的 532 | jne set_new_food ; 这里有一个问题,如果生成的食物位置不行,到这里要跳转,又要进行下次执行,但是又要发生键盘中断 533 | 534 | push FOOD_COLOR 535 | pop es:[bx] 536 | 537 | ret 538 | 539 | 540 | ;------------------------------------------- 541 | eat_food: 542 | push NEW_NODE ; 记录新节点的位置,吃到食物,新食物变新的头结点 543 | pop ds:[bx+0] 544 | ; 以前头结点的前驱指向食物,新食物的后继指向以前的头结点 545 | mov bx , offset SNAKE 546 | add bx , NEW_NODE 547 | 548 | mov word ptr ds:[bx+0] , 0 ;食物节点变成头结点,蛇变长一截 549 | mov ds:[bx+2] , si 550 | push SNAKE_COLOR 551 | pop es:[si] 552 | 553 | push SNAKE_HEAD 554 | pop ds:[bx+4] 555 | 556 | push NEW_NODE 557 | pop SNAKE_HEAD ; 设置新的头 558 | ; 00 02 04 06 08 10 12 14 16 18 20 22 559 | ;最开始小蛇相关信息在内存中存放(18 si 06 | 00 si 12 | 06 si 18 | 00 si 00) 560 | ; ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ 561 | ; pre pos next pre pos next pre pos next pre pos next 562 | ;这个图是初始小蛇走了一步,然后吃了食物后在内存中的存储状态,第四个节点就变成新的蛇头节点,前面三个节点分别变成蛇的第二个节点,第三个节点和第四个节点 563 | ;头结点变了,以前的头结点也要做相应的修改 564 | ;尾节点没变,还是在内存偏移12的位置 565 | add NEW_NODE , 6 566 | 567 | inc SCORE 568 | call output_score 569 | 570 | ret 571 | 572 | 573 | ;------------------------------------------- 574 | draw_new_snake: 575 | push SNAKE_STERN ; 蛇尾巴进栈保存,SNAKE_STERN=12 576 | pop ds:[bx+0] ; bx接上面是指向蛇头的,向上走一步,蛇头节点的第一个存储区就要修改,指向前一个节点 577 | ; 我们的做法就是我蛇尾的那个节点放到蛇头前面,这要中间的就可以不用修改,只修改第一个和最后一个 578 | 579 | mov bx , offset SNAKE 580 | add bx , SNAKE_STERN ; 找到记录最后一个节点的内存(蛇尾) 581 | 582 | push ds:[bx+0] ; 先保存最后一个节点中的pre,06 583 | 584 | mov word ptr ds:[bx+0] , 0 ; 升级成蛇头了,节点中的pre变成0 585 | mov di , ds:[bx+2] ; 中间节点保存的是位置信息,现在要改变颜色 586 | push SCREEN_COLOR 587 | pop es:[di] 588 | 589 | mov ds:[bx+2] , si ; 修改完颜色,现在最后蛇尾节点变蛇头,要重新修改位置 590 | ; si 上面是拿到了最开始蛇头的si-160 591 | 592 | push SNAKE_COLOR 593 | pop es:[si] ; 在对应位置画出来 594 | 595 | push SNAKE_HEAD ; SNAKE_HEAD=0 596 | pop ds:[bx+4] ; 最后修改节点中最后一个信息,就是下一个节点的位置(这时候应该指向原来的头结点) 597 | 598 | push SNAKE_STERN ; 曾经的头部现在已经变成尾巴了,SNAKE_STERN=12 599 | pop SNAKE_HEAD ; SNAKE_HEAD=12 600 | ; 因为这些点是记录在内存的,所以要保存他们的位置,才能够正确访问到 601 | ; 现在SNAKE_HEAD变成12,下次访问+SNAKE_HEAD就找到真正的头结点在内存中的位置, 602 | ; 初始SNAKE_HEAD=0,应该最开始我们在SNAKE中第一个节点存放头,所以只要偏移0就找到真正的头结点了 603 | 604 | 605 | pop SNAKE_STERN ; 先保存最后一个节点中的pre,现在修改了蛇尾放到头部前面,pre就是新的尾部 606 | ret 607 | 608 | 609 | ; 00 02 04 06 08 10 12 14 16 610 | ;最开始小蛇相关信息在内存中存放(00 si' 06 | 00 si 12 | 06 si 18) ; 这里每个数字都是dw,我写成对应10进制表示的数了 611 | ; ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ 612 | ; pre pos next pre pos next pre pos next 613 | ;所以SNAKE_HEAD=0,SNAKE_STERN=12 , 所以要找到对应 614 | 615 | ;第一个头节点前面什么东西也没有,所有pre=0 616 | 617 | ;head body STERN ;最开始小蛇 618 | ;▅ ▅ ▅ 619 | 620 | ;向上一步,原来的尾巴变成头,在修改对应信息就好了,比如原来最后尾巴节点颜色改成背景色 621 | ; 00 02 04 06 08 10 12 14 16 622 | ;向上一步后相关信息在内存中存放(12 si' 06 | 00 si 12 | 00 si'-1 00) 623 | ; ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ 624 | ; pre pos next pre pos next pre pos next(偏移00就可以找到第二个节点在内存中的位置,取出相关信息) 625 | ;所以SNAKE_HEAD=12,SNAKE_STERN=06 , 原来的头结点变成第二个节点,尾巴节点变成头,第二个节点变成尾巴 626 | 627 | ;STERN head body STERN ;向上一步走 628 | ;▅ ▅ ▅ ▅✘(修改颜色为背景色) 629 | 630 | 631 | ;------------------------------------------- 632 | clear_buff: 633 | mov ah , 1 634 | int 16h 635 | jz clearBuffRet ; ZF=1,键盘无输入 636 | mov ah , 0 637 | int 16h 638 | jmp clear_buff 639 | 640 | clearBuffRet: 641 | ret 642 | 643 | 644 | ;------------------------------------------- 645 | change_screen_color: 646 | push bx 647 | push cx 648 | push es 649 | 650 | mov bx , 0b800h 651 | mov es , bx 652 | mov bx , 1 653 | 654 | mov cx , 2000 655 | 656 | changeScreen: 657 | inc byte ptr es:[bx] 658 | add bx , 2 659 | loop changeScreen 660 | 661 | pop es 662 | pop cx 663 | pop bx 664 | ret 665 | 666 | greedy_snake_end: 667 | nop 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | ;------------------------------------------- 676 | set_new_int9: 677 | mov bx , 0 678 | mov es , bx 679 | 680 | cli 681 | mov word ptr es:[9*4] , offset new_int9 - offset greedy_snake + 7e00h 682 | mov word ptr es:[9*4+2] , 0 683 | sti 684 | 685 | ret 686 | 687 | 688 | ;------------------------------------------- 689 | sav_old_int9: ; 保存原来的9h号处理键盘中断 690 | mov bx , 0 691 | mov es , bx 692 | 693 | cli ; IF=0,不允许其它外中断 694 | push es:[9*4] 695 | pop es:[200h] 696 | push es:[9*4+2] 697 | pop es:[202h] 698 | sti 699 | ret 700 | 701 | 702 | ;------------------------------------------- 703 | cpy_greedy_snake: 704 | mov bx , cs 705 | mov ds , bx 706 | mov si , offset greedy_snake 707 | 708 | mov bx , 0 709 | mov es , bx 710 | mov di , 7e00h ; 711 | 712 | mov cx , offset greedy_snake_end - offset greedy_snake 713 | cld 714 | rep movsb 715 | 716 | ret 717 | 718 | code ends 719 | end start 720 | -------------------------------------------------------------------------------- /G_Snake/LINK.EXE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meihao1203/Greedy_Snake/877fb39fb1c6d5e01d781bc2d372c6402605f279/G_Snake/LINK.EXE -------------------------------------------------------------------------------- /G_Snake/MASM.EXE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meihao1203/Greedy_Snake/877fb39fb1c6d5e01d781bc2d372c6402605f279/G_Snake/MASM.EXE -------------------------------------------------------------------------------- /G_Snake/debug.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meihao1203/Greedy_Snake/877fb39fb1c6d5e01d781bc2d372c6402605f279/G_Snake/debug.exe -------------------------------------------------------------------------------- /G_Snake/edit.com: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meihao1203/Greedy_Snake/877fb39fb1c6d5e01d781bc2d372c6402605f279/G_Snake/edit.com -------------------------------------------------------------------------------- /G_Snake/map.asm: -------------------------------------------------------------------------------- 1 | assume cs:code , ds:data , ss:stack 2 | data segment 3 | ;db 256 dup(0) ; 代码一点一点写,每个调用都测试一下,现在要写真正程序用到的,就注释掉这里 4 | BOUNDARY_COLOR dw 4431h ; 直接定址法,颜色=0100100b,字符31h=数字1 5 | NEXT_ROL dw 0A0h ; a0=160 6 | SNAKE_HEAD dw 0 7 | SNAKE_BODY dw 6 ; 这个后面没用到,可以注释掉 8 | SNAKE_STERN dw 12 ; 尾巴在下面SNAKE分配的内存中的偏移 9 | SNAKE dw 200 dup (0,0,0) ; dup(0,0,0)只是为了说明小蛇存放的数据结构,00 00 00 表示一个数据结构单元 10 | ;00 00 00 ,第一个表示当前结点的前一个数据单元的这段开辟的空间中的起始位置,中间的表示要在屏幕的那个位置显示一个块,表示小蛇身体 11 | ;最后一个数据单元表示下一个数据单元在这段空间中的偏移位置,具体可以看init_snake 12 | SNAKE_COLOR dw 2201h ; 颜色00100010b,字符01h='☺' 13 | data ends 14 | stack segment 15 | db 128 dup(0) 16 | stack ends 17 | code segment 18 | start: 19 | mov ax , stack 20 | mov ss , ax 21 | mov sp , 128 22 | 23 | call cpy_greedy_snake 24 | call sav_old_int9 25 | call set_new_int9 26 | 27 | 28 | mov bx , 0 29 | push bx 30 | mov bx , 0208h 31 | push bx 32 | retf ; pop ip , pop cs ; CS:IP=0:0208h 33 | 34 | mov ax , 4c00h 35 | int 21h 36 | 37 | greedy_snake: 38 | call init_reg 39 | call clear_screen 40 | call init_screen 41 | call init_snake ; 画出蛇的图形 42 | 43 | 44 | testA: ; 无限循环,测试 45 | mov ax , 1000h 46 | jmp testA 47 | 48 | mov ax , 4c00h 49 | int 21h 50 | 51 | 52 | ; 双向链表数据结构 53 | ;----------------------------------------- 54 | init_snake: 55 | mov bx , SNAKE 56 | add bx , SNAKE_HEAD 57 | mov si , 160*10+40*2 ; 屏幕上的位置(0690h) 58 | mov dx , SNAKE_COLOR ; 2201h 59 | 60 | mov word ptr ds:[bx+0] , 0 ; ds:[bx+0] = 0,蛇头节点是第一个节点,pre(前驱)只能为0 61 | mov ds:[bx+2] , si 62 | mov es:[si] , dx ; 画蛇 63 | mov word ptr ds:[bx+4] , 6 ; 00 si o6 64 | 65 | sub si , 2 ; 初始小蛇水平方向,蛇头后一个点的位置就是si-2 66 | add bx , 6 67 | 68 | mov word ptr ds:[bx+0] , 0 ; ds:[bx+0] = 0,第二个节点前面是蛇头节点,pre=0 69 | mov ds:[bx+2] , si 70 | mov es:[si] , dx ; 设置蛇的颜色 71 | mov word ptr ds:[bx+4] , 12 72 | 73 | sub si , 2 74 | add bx , 6 75 | 76 | mov word ptr ds:[bx+0] , 6 ; ds:[bx+0] = 6 77 | mov ds:[bx+2] , si 78 | mov es:[si] , dx ; 设置蛇的颜色 79 | mov word ptr ds:[bx+4] , 18 80 | 81 | 82 | ret 83 | 84 | 85 | ;----------------------------------------- 86 | init_screen: 87 | mov dx , BOUNDARY_COLOR ; 设置游戏界面边框的颜色 88 | call show_up_down_line ; 画出游戏上下边界的边框 89 | call show_left_right_line ; 画出游戏左右边界的边框 90 | 91 | ret 92 | 93 | 94 | ;----------------------------------------- 95 | show_up_down_line: 96 | mov bx , 0 97 | mov cx , 80 98 | showUpDownLine: 99 | mov es:[bx] , dx ;dx=4831h,字符31h=数字1,颜色=0100100b 100 | mov es:[bx+160*23] , dx 101 | add bx , 2 102 | loop showUpDownLine 103 | ret 104 | 105 | 106 | ;----------------------------------------- 107 | show_left_right_line: 108 | mov bx , 0 109 | mov cx , 23 110 | showLeftRightLine: 111 | mov es:[bx] , dx 112 | mov es:[bx+79*2] , dx 113 | ;add bx , 160 114 | add bx , NEXT_ROL ; 优化写法 115 | loop showLeftRightLine 116 | ret 117 | 118 | 119 | ;----------------------------------------- 120 | init_reg: 121 | mov bx , 0b800h 122 | mov es , bx 123 | 124 | mov bx , data 125 | mov ds , bx 126 | ret 127 | 128 | 129 | ;----------------------------------------- 130 | clear_screen: 131 | mov bx , 0 132 | mov dx , 0700h 133 | mov cx , 2000 134 | 135 | clearScreen: 136 | mov es:[bx] , dx ; (dx)=0700,es:[0]~es:[1]=00,es:[2]~es:[3]=07,颜色属性是07,RGB=白色 137 | add bx , 2 138 | loop clearScreen 139 | ret 140 | 141 | 142 | 143 | 144 | 145 | ;------------------------------------------- 146 | new_int9: 147 | push ax 148 | 149 | call clear_buff ; 清空键盘缓冲区 150 | in al , 60h ; 60h号端口读取的是键盘扫描码 151 | pushf 152 | call dword ptr cs:[200h] 153 | 154 | cmp al , 3bh ; 字符 ‘F1’ 的扫描码 155 | jne int9Ret 156 | call change_screen_color ;写来测试看,我写的9号中断处理键盘缓冲区能不能正常工作 157 | 158 | 159 | int9Ret: 160 | pop ax 161 | iret 162 | 163 | 164 | ;------------------------------------------- 165 | clear_buff: 166 | mov ah , 1 167 | int 16h 168 | jz clearBuffRet ; ZF=1,键盘无输入 169 | mov ah , 0 170 | int 16h 171 | jmp clear_buff 172 | 173 | clearBuffRet: 174 | ret 175 | 176 | 177 | ;------------------------------------------- 178 | change_screen_color: 179 | push bx 180 | push cx 181 | push es 182 | 183 | mov bx , 0b800h 184 | mov es , bx 185 | mov bx , 1 186 | 187 | mov cx , 2000 188 | 189 | changeScreen: 190 | inc byte ptr es:[bx] 191 | add bx , 2 192 | loop changeScreen 193 | 194 | pop es 195 | pop cx 196 | pop bx 197 | ret 198 | 199 | 200 | 201 | greedy_snake_end: 202 | nop 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | ;------------------------------------------- 211 | set_new_int9: 212 | mov bx , 0 213 | mov es , bx 214 | 215 | cli 216 | mov word ptr es:[9*4] , offset new_int9 - offset greedy_snake + 0208h 217 | mov word ptr es:[9*4+2] , 0 218 | sti 219 | 220 | ret 221 | 222 | 223 | ;------------------------------------------- 224 | sav_old_int9: ; 保存原来的9h号处理键盘中断 225 | mov bx , 0 226 | mov es , bx 227 | 228 | cli ; IF=0,不允许其它外中断 229 | push es:[9*4] 230 | pop es:[200h] 231 | push es:[9*4+2] 232 | pop es:[202h] 233 | sti 234 | ret 235 | 236 | 237 | ;------------------------------------------- 238 | cpy_greedy_snake: 239 | mov bx , cs ; 要拷贝的代码是在当前代码段CS 240 | mov ds , bx 241 | mov si , offset greedy_snake 242 | 243 | mov bx , 0 244 | mov es , bx 245 | mov di , 0208h ; 自己指定一个位置 246 | 247 | mov cx , offset greedy_snake_end - offset greedy_snake 248 | cld 249 | rep movsb 250 | 251 | ret 252 | 253 | code ends 254 | end start 255 | -------------------------------------------------------------------------------- /G_Snake/sMA.asm: -------------------------------------------------------------------------------- 1 | assume cs:code , ds:data , ss:stack 2 | data segment 3 | ;db 256 dup(0) ; 代码一点一点写,每个调用都测试一下,现在要写真正程序用到的,就注释掉这里 4 | BOUNDARY_COLOR dw 4431h ; 直接定址法,颜色=0100100b,字符31h=数字1 5 | NEXT_ROL dw 0A0h ; a0=160 6 | SNAKE_HEAD dw 0 7 | SNAKE_BODY dw 6 8 | SNAKE_STERN dw 12 9 | SNAKE dw 200 dup (0,0,0) ; 三个数是来记录前一个节点,中间点的位置,记录下一个点在内存中的相对偏移 10 | ;00 00 00 00 00 00 11 | SNAKE_COLOR dw 2201h ; 颜色00100010b,字符01h='☺' 12 | 13 | UP db 48h 14 | DOWN db 50h 15 | LEFT db 4Bh 16 | RIGHT db 4Dh 17 | 18 | SCREEN_COLOR dw 0700h 19 | 20 | NEXT_ROW dw 160 21 | 22 | DIRECTION dw 3 ; 设置自动移动 23 | DIRECTION_FUN dw offset isMoveUp - offset greedy_snake + 7e00h ; [0] 24 | dw offset isMoveDown - offset greedy_snake + 7e00h ; [2] 25 | dw offset isMoveLeft - offset greedy_snake + 7e00h ; [4] 26 | dw offset isMoveRight - offset greedy_snake + 7e00h ; [6] 27 | 28 | 29 | data ends 30 | 31 | stack segment 32 | db 128 dup(0) 33 | stack ends 34 | 35 | code segment 36 | start: 37 | mov ax , stack 38 | mov ss , ax 39 | mov sp , 128 40 | 41 | call cpy_greedy_snake 42 | call sav_old_int9 43 | call set_new_int9 44 | 45 | 46 | mov bx , 0 47 | push bx 48 | mov bx , 7e00h 49 | push bx 50 | retf ; pop ip , pop cs ; CS:IP=0:7e00 51 | 52 | mov ax , 4c00h 53 | int 21h 54 | 55 | greedy_snake: 56 | call init_reg 57 | call clear_screen 58 | call init_screen 59 | call init_snake ; 画出蛇的图形 60 | 61 | nextMove: 62 | call delay 63 | cli 64 | call isMoveDirection 65 | sti 66 | jmp nextMove 67 | 68 | 69 | 70 | testA: ; 无限循环,测试 71 | mov ax , 1000h 72 | jmp testA 73 | 74 | mov ax , 4c00h 75 | int 21h 76 | 77 | 78 | ;----------------------------------------- 79 | isMoveDirection: 80 | mov bx , DIRECTION 81 | add bx , bx 82 | call word ptr DIRECTION_FUN[bx] ; ds:DIRECTION_FUN[bx] 83 | ret 84 | 85 | 86 | 87 | ;----------------------------------------- 88 | delay: 89 | push ax 90 | push dx 91 | 92 | mov dx , 3h 93 | sub ax , ax 94 | delaying: 95 | sub ax , 1 96 | sbb dx , 0 97 | cmp ax , 0 98 | jne delaying 99 | cmp dx , 0 100 | jne delaying 101 | 102 | 103 | 104 | pop dx 105 | pop ax 106 | 107 | ret 108 | 109 | 110 | ; 双向链表数据结构 111 | ;----------------------------------------- 112 | init_snake: 113 | mov bx , offset SNAKE 114 | add bx , SNAKE_HEAD 115 | mov si , 160*10+40*2 ; 屏幕上的位置(0690h) 116 | mov dx , SNAKE_COLOR ; 2201h 117 | 118 | mov word ptr ds:[bx+0] , 0 ; ds:[bx+0] = 0 119 | mov ds:[bx+2] , si 120 | mov es:[si] , dx ; 设置蛇的颜色 121 | mov word ptr ds:[bx+4] , 6 122 | 123 | sub si , 2 124 | add bx , 6 125 | 126 | mov word ptr ds:[bx+0] , 0 ; ds:[bx+0] = 0 127 | mov ds:[bx+2] , si 128 | mov es:[si] , dx ; 设置蛇的颜色 129 | mov word ptr ds:[bx+4] , 12 130 | 131 | sub si , 2 132 | add bx , 6 133 | 134 | mov word ptr ds:[bx+0] , 6 ; ds:[bx+0] = 0 135 | mov ds:[bx+2] , si 136 | mov es:[si] , dx ; 设置蛇的颜色 137 | mov word ptr ds:[bx+4] , 18 138 | 139 | 140 | ret 141 | 142 | 143 | ;----------------------------------------- 144 | init_screen: 145 | mov dx , BOUNDARY_COLOR ; 设置游戏界面边框的颜色 146 | call show_up_down_line ; 画出游戏上下边界的边框 147 | call show_left_right_line ; 画出游戏左右边界的边框 148 | 149 | ret 150 | 151 | 152 | ;----------------------------------------- 153 | show_up_down_line: 154 | mov bx , 0 155 | mov cx , 80 156 | showUpDownLine: 157 | mov es:[bx] , dx ; dx=4431h,字符31h=数字1,颜色=01000100b 158 | mov es:[bx+160*23] , dx 159 | add bx , 2 160 | loop showUpDownLine 161 | ret 162 | 163 | 164 | ;----------------------------------------- 165 | show_left_right_line: 166 | mov bx , 0 167 | mov cx , 23 168 | showLeftRightLine: 169 | mov es:[bx] , dx 170 | mov es:[bx+79*2] , dx 171 | ;add bx , 160 172 | add bx , NEXT_ROL ; 优化写法 173 | loop showLeftRightLine 174 | ret 175 | 176 | 177 | ;----------------------------------------- 178 | init_reg: 179 | mov bx , 0b800h 180 | mov es , bx 181 | 182 | mov bx , data 183 | mov ds , bx 184 | ret 185 | 186 | 187 | ;----------------------------------------- 188 | clear_screen: 189 | mov bx , 0 190 | mov dx , SCREEN_COLOR 191 | mov cx , 2000 192 | 193 | clearScreen: 194 | mov es:[bx] , dx ; (dx)=0700,es:[0]~es:[1]=00,es:[2]~es:[3]=07,颜色属性是07,RGB=白色 195 | add bx , 2 196 | loop clearScreen 197 | ret 198 | 199 | 200 | ;------------------------------------------- 201 | new_int9: 202 | push ax 203 | 204 | call clear_buff ; 清空键盘缓冲区 205 | 206 | in al , 60h ; 60h号端口读取的是键盘扫描码 207 | pushf 208 | call dword ptr cs:[200h] 209 | 210 | cmp al , UP ; 记录方向键扫描码 211 | je isUp 212 | cmp al , LEFT 213 | je isLeft 214 | cmp al , RIGHT 215 | je isRight 216 | cmp al , DOWN 217 | je isDown 218 | 219 | cmp al , 3bh ; 字符 ‘F1’ 的扫描码 220 | jne int9Ret 221 | call change_screen_color ;写来测试看,我写的9号中断处理键盘缓冲区能不能正常工作 222 | 223 | 224 | int9Ret: 225 | pop ax 226 | iret 227 | 228 | 229 | ;------------------------------------------- 230 | isUp: 231 | mov di , 160*24 + 0*2 ; 每行最下面显示出按下的按键 232 | mov byte ptr es:[di] , 'U' 233 | call isMoveUp 234 | jmp int9Ret 235 | 236 | isDown: 237 | mov di , 160*24 + 0*2 ; 每行最下面显示出按下的按键 238 | mov byte ptr es:[di] , 'D' 239 | call isMoveDown 240 | jmp int9Ret 241 | 242 | isLeft: 243 | mov di , 160*24 + 0*2 ; 每行最下面显示出按下的按键 244 | mov byte ptr es:[di] , 'L' 245 | call isMoveLeft 246 | jmp int9Ret 247 | 248 | isRight: 249 | mov di , 160*24 + 0*2 ; 每行最下面显示出按下的按键 250 | mov byte ptr es:[di] , 'R' 251 | call isMoveRight 252 | jmp int9Ret 253 | 254 | 255 | ;------------------------------------------- 256 | isMoveUp: 257 | mov bx , offset SNAKE 258 | add bx , SNAKE_HEAD 259 | mov si , ds:[bx+2] ; bx+2 获得节点中间的值,这个值记录的是该节点的位置 260 | sub si , NEXT_ROW ; 向上走了一步,所以中间节点的位置也要偏移160,刚好是一行 261 | 262 | cmp byte ptr es:[si] , 0 ; 向上走一步如果es:[si]=1,表示到显示区的边界了,不能移动 263 | jne noMoveUp 264 | call draw_new_snake ; 移动,重新绘制蛇 265 | 266 | mov DIRECTION , 0 ; 在按下相应按键,蛇能动了之后,就要设置自动移动了 267 | noMoveUp: 268 | ret 269 | 270 | 271 | ;------------------------------------------- 272 | isMoveDown: 273 | mov bx , offset SNAKE 274 | add bx , SNAKE_HEAD 275 | mov si , ds:[bx+2] 276 | add si , NEXT_ROW 277 | 278 | cmp byte ptr es:[si] , 0 279 | jne noMoveDown 280 | call draw_new_snake 281 | 282 | mov DIRECTION , 1 283 | noMoveDown: 284 | ret 285 | 286 | 287 | ;------------------------------------------- 288 | isMoveLeft: 289 | mov bx , offset SNAKE 290 | add bx , SNAKE_HEAD 291 | mov si , ds:[bx+2] 292 | sub si , 2 293 | 294 | cmp byte ptr es:[si] , 0 295 | jne noMoveDown 296 | call draw_new_snake 297 | mov DIRECTION , 2 298 | noMoveLeft: 299 | ret 300 | 301 | 302 | ;------------------------------------------- 303 | isMoveRight: 304 | mov bx , offset SNAKE 305 | add bx , SNAKE_HEAD 306 | mov si , ds:[bx+2] 307 | add si , 2 308 | 309 | cmp byte ptr es:[si] , 0 310 | jne noMoveDown 311 | call draw_new_snake 312 | mov DIRECTION , 3 313 | noMoveRight: 314 | ret 315 | 316 | 317 | ;------------------------------------------- 318 | draw_new_snake: 319 | push SNAKE_STERN ; 蛇尾巴进栈保存,SNAKE_STERN=12 320 | pop ds:[bx+0] ; bx接上面是指向蛇头的,向上走一步,蛇头节点的第一个存储区就要修改,指向前一个节点 321 | ; 我们的做法就是我蛇尾的那个节点放到蛇头前面,这要中间的就可以不用修改,只修改第一个和最后一个 322 | 323 | mov bx , offset snake 324 | add bx , SNAKE_STERN ; 找到记录最后一个节点的内存(蛇尾) 325 | 326 | push ds:[bx+0] ; 先保存最后一个节点中的pre,06 327 | 328 | mov word ptr ds:[bx+0] , 0 ; 升级成蛇头了,节点中的pre变成0 329 | mov di , ds:[bx+2] ; 中间节点保存的是位置信息,现在要改变颜色 330 | push SCREEN_COLOR 331 | pop es:[di] 332 | 333 | mov ds:[bx+2] , si ; 修改完颜色,现在最后蛇尾节点变蛇头,要重新修改位置 334 | ; si 上面是拿到了最开始蛇头的si-160 335 | 336 | push SNAKE_COLOR 337 | pop es:[si] ; 在对应位置画出来 338 | 339 | push SNAKE_HEAD ; SNAKE_HEAD=0 340 | pop ds:[bx+4] ; 最后修改节点中最后一个信息,就是下一个节点的位置(这时候应该指向原来的头结点) 341 | 342 | push SNAKE_STERN ; 曾经的头部现在已经变成尾巴了,SNAKE_STERN=12 343 | pop SNAKE_HEAD ; SNAKE_HEAD=12 344 | ; 因为这些点是记录在内存的,所以要保存他们的位置,才能够正确访问到 345 | ; 现在SNAKE_HEAD变成12,下次访问+SNAKE_HEAD就找到真正的头结点在内存中的位置, 346 | ; 初始SNAKE_HEAD=0,应该最开始我们在SNAKE中第一个节点存放头,所以只要偏移0就找到真正的头结点了 347 | 348 | 349 | pop SNAKE_STERN ; 先保存最后一个节点中的pre,现在修改了蛇尾放到头部前面,pre就是新的尾部 350 | ret 351 | 352 | 353 | ; 00 02 04 06 08 10 12 14 16 354 | ;最开始小蛇相关信息在内存中存放(00 si' 06 | 00 si 12 | 06 si 18) ; 这里每个数字都是dw,我写成对应10进制表示的数了 355 | ; ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ 356 | ; pre pos next pre pos next pre pos next 357 | ;所以SNAKE_HEAD=0,SNAKE_STERN=12 , 所以要找到对应 358 | 359 | ;第一个头节点前面什么东西也没有,所有pre=0 360 | 361 | ;head body STERN ;最开始小蛇 362 | ;▅ ▅ ▅ 363 | 364 | ;向上一步,原来的尾巴变成头,在修改对应信息就好了,比如原来最后尾巴节点颜色改成背景色 365 | ; 00 02 04 06 08 10 12 14 16 366 | ;向上一步后相关信息在内存中存放(12 si' 06 | 00 si 12 | 00 si'-1 00) 367 | ; ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ 368 | ; pre pos next pre pos next pre pos next(偏移00就可以找到第二个节点在内存中的位置,取出相关信息) 369 | ;所以SNAKE_HEAD=12,SNAKE_STERN=06 , 原来的头结点变成第二个节点,尾巴节点变成头,第二个节点变成尾巴 370 | 371 | ;STERN head body STERN ;向上一步走 372 | ;▅ ▅ ▅ ▅✘(修改颜色为背景色) 373 | 374 | 375 | ;------------------------------------------- 376 | clear_buff: 377 | mov ah , 1 378 | int 16h 379 | jz clearBuffRet ; ZF=1,键盘无输入 380 | mov ah , 0 381 | int 16h 382 | jmp clear_buff 383 | 384 | clearBuffRet: 385 | ret 386 | 387 | 388 | ;------------------------------------------- 389 | change_screen_color: 390 | push bx 391 | push cx 392 | push es 393 | 394 | mov bx , 0b800h 395 | mov es , bx 396 | mov bx , 1 397 | 398 | mov cx , 2000 399 | 400 | changeScreen: 401 | inc byte ptr es:[bx] 402 | add bx , 2 403 | loop changeScreen 404 | 405 | pop es 406 | pop cx 407 | pop bx 408 | ret 409 | 410 | 411 | 412 | greedy_snake_end: nop 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | ;------------------------------------------- 421 | set_new_int9: 422 | mov bx , 0 423 | mov es , bx 424 | 425 | cli 426 | mov word ptr es:[9*4] , offset new_int9 - offset greedy_snake + 7e00h 427 | mov word ptr es:[9*4+2] , 0 428 | sti 429 | 430 | ret 431 | 432 | 433 | ;------------------------------------------- 434 | sav_old_int9: ; 保存原来的9h号处理键盘中断 435 | mov bx , 0 436 | mov es , bx 437 | 438 | cli ; IF=0,不允许其它外中断 439 | push es:[9*4] 440 | pop es:[200h] 441 | push es:[9*4+2] 442 | pop es:[202h] 443 | sti 444 | ret 445 | 446 | 447 | ;------------------------------------------- 448 | cpy_greedy_snake: 449 | mov bx , cs 450 | mov ds , bx 451 | mov si , offset greedy_snake 452 | 453 | mov bx , 0 454 | mov es , bx 455 | mov di , 7e00h ; 456 | 457 | mov cx , offset greedy_snake_end - offset greedy_snake 458 | cld 459 | rep movsb 460 | 461 | ret 462 | 463 | code ends 464 | end start 465 | -------------------------------------------------------------------------------- /G_Snake/sMove.asm: -------------------------------------------------------------------------------- 1 | assume cs:code , ds:data , ss:stack 2 | data segment 3 | ;db 256 dup(0) ; 代码一点一点写,每个调用都测试一下,现在要写真正程序用到的,就注释掉这里 4 | BOUNDARY_COLOR dw 4431h ; 直接定址法,颜色=0100100b,字符31h=数字1 5 | NEXT_ROL dw 0A0h ; a0=160 6 | SNAKE_HEAD dw 0 7 | SNAKE_BODY dw 6 8 | SNAKE_STERN dw 12 9 | SNAKE dw 200 dup (0,0,0) ; 三个数是来记录前一个节点,中间点的位置,记录下一个点在内存中的相对偏移 10 | ; 小蛇结点结构体,3个字单元(word),相当于双向链表,第一个单元存放前驱,中间存放小蛇位置数据,最后一个存放后继 11 | ;00 00 00 00 00 00 12 | SNAKE_COLOR dw 2201h ; 颜色00100010b,字符01h='☺' 13 | 14 | UP db 48h 15 | DOWN db 50h 16 | LEFT db 4Bh 17 | RIGHT db 4Dh 18 | 19 | SCREEN_COLOR dw 0700h 20 | 21 | NEXT_ROW dw 160 22 | 23 | 24 | data ends 25 | stack segment 26 | db 128 dup(0) 27 | stack ends 28 | code segment 29 | start: 30 | mov ax , stack 31 | mov ss , ax 32 | mov sp , 128 33 | 34 | call cpy_greedy_snake 35 | call sav_old_int9 36 | call set_new_int9 37 | 38 | 39 | mov bx , 0 40 | push bx 41 | mov bx , 7e00h 42 | push bx 43 | retf ; pop ip , pop cs ; CS:IP=0:7e00 44 | 45 | mov ax , 4c00h 46 | int 21h 47 | 48 | greedy_snake: 49 | call init_reg 50 | call clear_screen 51 | call init_screen 52 | call init_snake ; 画出蛇的图形 53 | 54 | 55 | 56 | testA: ; 无限循环,测试 57 | mov ax , 1000h 58 | jmp testA 59 | 60 | mov ax , 4c00h 61 | int 21h 62 | 63 | 64 | 65 | 66 | ; 双向链表数据结构 67 | ;----------------------------------------- 68 | init_snake: 69 | mov bx , offset SNAKE 70 | add bx , SNAKE_HEAD 71 | mov si , 160*10+40*2 ; 屏幕上的位置(0690h) 72 | mov dx , SNAKE_COLOR ; 2201h 73 | 74 | mov word ptr ds:[bx+0] , 0 ; ds:[bx+0] = 0 75 | mov ds:[bx+2] , si 76 | mov es:[si] , dx ; 设置蛇的颜色 77 | mov word ptr ds:[bx+4] , 6 78 | 79 | sub si , 2 80 | add bx , 6 81 | 82 | mov word ptr ds:[bx+0] , 0 ; ds:[bx+0] = 0 83 | mov ds:[bx+2] , si 84 | mov es:[si] , dx ; 设置蛇的颜色 85 | mov word ptr ds:[bx+4] , 12 86 | 87 | sub si , 2 88 | add bx , 6 89 | 90 | mov word ptr ds:[bx+0] , 6 ; ds:[bx+0] = 0 91 | mov ds:[bx+2] , si 92 | mov es:[si] , dx ; 设置蛇的颜色 93 | mov word ptr ds:[bx+4] , 18 94 | 95 | 96 | ret 97 | 98 | 99 | ;----------------------------------------- 100 | init_screen: 101 | mov dx , BOUNDARY_COLOR ; 设置游戏界面边框的颜色 102 | call show_up_down_line ; 画出游戏上下边界的边框 103 | call show_left_right_line ; 画出游戏左右边界的边框 104 | 105 | ret 106 | 107 | 108 | ;----------------------------------------- 109 | show_up_down_line: 110 | mov bx , 0 111 | mov cx , 80 112 | showUpDownLine: 113 | mov es:[bx] , dx ; dx=4431h,字符31h=数字1,颜色=01000100b 114 | mov es:[bx+160*23] , dx 115 | add bx , 2 116 | loop showUpDownLine 117 | ret 118 | 119 | 120 | ;----------------------------------------- 121 | show_left_right_line: 122 | mov bx , 0 123 | mov cx , 23 124 | showLeftRightLine: 125 | mov es:[bx] , dx 126 | mov es:[bx+79*2] , dx 127 | ;add bx , 160 128 | add bx , NEXT_ROL ; 优化写法 129 | loop showLeftRightLine 130 | ret 131 | 132 | 133 | ;----------------------------------------- 134 | init_reg: 135 | mov bx , 0b800h 136 | mov es , bx 137 | 138 | mov bx , data 139 | mov ds , bx 140 | ret 141 | 142 | 143 | ;----------------------------------------- 144 | clear_screen: 145 | mov bx , 0 146 | mov dx , SCREEN_COLOR 147 | mov cx , 2000 148 | 149 | clearScreen: 150 | mov es:[bx] , dx ; (dx)=0700,es:[0]~es:[1]=00,es:[2]~es:[3]=07,颜色属性是07,RGB=白色 151 | add bx , 2 152 | loop clearScreen 153 | ret 154 | 155 | 156 | ;------------------------------------------- 157 | new_int9: 158 | push ax 159 | 160 | call clear_buff ; 清空键盘缓冲区 161 | 162 | in al , 60h ; 60h号端口读取的是键盘扫描码 163 | pushf 164 | call dword ptr cs:[200h] 165 | 166 | cmp al , UP ; 记录方向键扫描码 167 | je isUp 168 | cmp al , LEFT 169 | je isLeft 170 | cmp al , RIGHT 171 | je isRight 172 | cmp al , DOWN 173 | je isDown 174 | 175 | cmp al , 3bh ; 字符 ‘F1’ 的扫描码 176 | jne int9Ret 177 | call change_screen_color ;写来测试看,我写的9号中断处理键盘缓冲区能不能正常工作 178 | 179 | 180 | int9Ret: 181 | pop ax 182 | iret 183 | 184 | 185 | ;------------------------------------------- 186 | isUp: 187 | mov di , 160*24 + 0*2 ; 每行最下面显示出按下的按键 188 | mov byte ptr es:[di] , 'U' 189 | call isMoveUp 190 | jmp int9Ret 191 | 192 | isDown: 193 | mov di , 160*24 + 0*2 ; 每行最下面显示出按下的按键 194 | mov byte ptr es:[di] , 'D' 195 | call isMoveDown 196 | jmp int9Ret 197 | 198 | isLeft: 199 | mov di , 160*24 + 0*2 ; 每行最下面显示出按下的按键 200 | mov byte ptr es:[di] , 'L' 201 | call isMoveLeft 202 | jmp int9Ret 203 | 204 | isRight: 205 | mov di , 160*24 + 0*2 ; 每行最下面显示出按下的按键 206 | mov byte ptr es:[di] , 'R' 207 | call isMoveRight 208 | jmp int9Ret 209 | 210 | 211 | ;------------------------------------------- 212 | isMoveUp: 213 | mov bx , offset SNAKE 214 | add bx , SNAKE_HEAD 215 | mov si , ds:[bx+2] ; bx+2 获得节点中间的值,这个值记录的是该节点的位置 216 | sub si , NEXT_ROW ; 向上走了一步,所以中间节点的位置也要偏移160,刚好是一行 217 | 218 | cmp byte ptr es:[si] , 0 ; 向上走一步如果es:[si]=1,表示到显示区的边界了,不能移动 219 | jne noMoveUp 220 | call draw_new_snake ; 移动,重新绘制蛇 221 | 222 | 223 | noMoveUp: 224 | ret 225 | 226 | 227 | ;------------------------------------------- 228 | isMoveDown: 229 | mov bx , offset SNAKE 230 | add bx , SNAKE_HEAD 231 | mov si , ds:[bx+2] 232 | add si , NEXT_ROW 233 | 234 | cmp byte ptr es:[si] , 0 235 | jne noMoveDown 236 | call draw_new_snake 237 | 238 | noMoveDown: 239 | ret 240 | 241 | 242 | ;------------------------------------------- 243 | isMoveLeft: 244 | mov bx , offset SNAKE 245 | add bx , SNAKE_HEAD 246 | mov si , ds:[bx+2] 247 | sub si , 2 248 | 249 | cmp byte ptr es:[si] , 0 250 | jne noMoveDown 251 | call draw_new_snake 252 | noMoveLeft: 253 | ret 254 | 255 | 256 | ;------------------------------------------- 257 | isMoveRight: 258 | mov bx , offset SNAKE 259 | add bx , SNAKE_HEAD 260 | mov si , ds:[bx+2] 261 | add si , 2 262 | 263 | cmp byte ptr es:[si] , 0 264 | jne noMoveDown 265 | call draw_new_snake 266 | noMoveRight: 267 | ret 268 | 269 | 270 | ;------------------------------------------- 271 | draw_new_snake: 272 | push SNAKE_STERN ; 蛇尾巴进栈保存,SNAKE_STERN=12 273 | pop ds:[bx+0] ; bx接上面是指向蛇头的,向上走一步,蛇头节点的第一个存储区就要修改,指向前一个节点 274 | ; 我们的做法就是我蛇尾的那个节点放到蛇头前面,这要中间的就可以不用修改,只修改第一个和最后一个 275 | 276 | mov bx , offset SNAKE 277 | add bx , SNAKE_STERN ; 找到记录最后一个节点的内存(蛇尾) 278 | 279 | push ds:[bx+0] ; 先保存最后一个节点中的pre,06 280 | 281 | mov word ptr ds:[bx+0] , 0 ; 升级成蛇头了,节点中的pre变成0 282 | mov di , ds:[bx+2] ; 中间节点保存的是位置信息,现在要改变颜色 283 | push SCREEN_COLOR 284 | pop es:[di] 285 | 286 | mov ds:[bx+2] , si ; 修改完颜色,现在最后蛇尾节点变蛇头,要重新修改位置 287 | ; si 上面是拿到了最开始蛇头的si-160 288 | 289 | push SNAKE_COLOR 290 | pop es:[si] ; 在对应位置画出来 291 | 292 | push SNAKE_HEAD ; SNAKE_HEAD=0 293 | pop ds:[bx+4] ; 最后修改节点中最后一个信息,就是下一个节点的位置(这时候应该指向原来的头结点) 294 | 295 | push SNAKE_STERN ; 曾经的头部现在已经变成尾巴了,SNAKE_STERN=12 296 | pop SNAKE_HEAD ; SNAKE_HEAD=12 297 | ; 因为这些点是记录在内存的,所以要保存他们的位置,才能够正确访问到 298 | ; 现在SNAKE_HEAD变成12,下次访问+SNAKE_HEAD就找到真正的头结点在内存中的位置, 299 | ; 初始SNAKE_HEAD=0,应该最开始我们在SNAKE中第一个节点存放头,所以只要偏移0就找到真正的头结点了 300 | 301 | 302 | pop SNAKE_STERN ; 先保存最后一个节点中的pre,现在修改了蛇尾放到头部前面,pre就是新的尾部 303 | ret 304 | 305 | 306 | ; 00 02 04 06 08 10 12 14 16 307 | ;最开始小蛇相关信息在内存中存放(00 si' 06 | 00 si 12 | 06 si 18) ; 这里每个数字都是dw,我写成对应10进制表示的数了 308 | ; ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ 309 | ; pre pos next pre pos next pre pos next 310 | ;所以SNAKE_HEAD=0,SNAKE_STERN=12 , 所以要找到对应 311 | 312 | ;第一个头节点前面什么东西也没有,所有pre=0 313 | 314 | ;head body STERN ;最开始小蛇 315 | ;▅ ▅ ▅ 316 | 317 | ;向上一步,原来的尾巴变成头,在修改对应信息就好了,比如原来最后尾巴节点颜色改成背景色 318 | ; 00 02 04 06 08 10 12 14 16 319 | ;向上一步后相关信息在内存中存放(12 si' 06 | 00 si 12 | 00 si'-1 00) 320 | ; ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ 321 | ; pre pos next pre pos next pre pos next(偏移00就可以找到第二个节点在内存中的位置,取出相关信息) 322 | ;所以SNAKE_HEAD=12,SNAKE_STERN=06 , 原来的头结点变成第二个节点,尾巴节点变成头,第二个节点变成尾巴 323 | 324 | ;STERN head body STERN ;向上一步走 325 | ;▅ ▅ ▅ ▅✘(修改颜色为背景色) 326 | 327 | 328 | ;------------------------------------------- 329 | clear_buff: 330 | mov ah , 1 331 | int 16h 332 | jz clearBuffRet ; ZF=1,键盘无输入 333 | mov ah , 0 334 | int 16h 335 | jmp clear_buff 336 | 337 | clearBuffRet: 338 | ret 339 | 340 | 341 | ;------------------------------------------- 342 | change_screen_color: 343 | push bx 344 | push cx 345 | push es 346 | 347 | mov bx , 0b800h 348 | mov es , bx 349 | mov bx , 1 350 | 351 | mov cx , 2000 352 | 353 | changeScreen: 354 | inc byte ptr es:[bx] 355 | add bx , 2 356 | loop changeScreen 357 | 358 | pop es 359 | pop cx 360 | pop bx 361 | ret 362 | 363 | 364 | 365 | greedy_snake_end: nop 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | ;------------------------------------------- 374 | set_new_int9: 375 | mov bx , 0 376 | mov es , bx 377 | 378 | cli 379 | mov word ptr es:[9*4] , offset new_int9 - offset greedy_snake + 7e00h 380 | mov word ptr es:[9*4+2] , 0 381 | sti 382 | 383 | ret 384 | 385 | 386 | ;------------------------------------------- 387 | sav_old_int9: ; 保存原来的9h号处理键盘中断 388 | mov bx , 0 389 | mov es , bx 390 | 391 | cli ; IF=0,不允许其它外中断 392 | push es:[9*4] 393 | pop es:[200h] 394 | push es:[9*4+2] 395 | pop es:[202h] 396 | sti 397 | ret 398 | 399 | 400 | ;------------------------------------------- 401 | cpy_greedy_snake: 402 | mov bx , cs 403 | mov ds , bx 404 | mov si , offset greedy_snake 405 | 406 | mov bx , 0 407 | mov es , bx 408 | mov di , 7e00h ; 409 | 410 | mov cx , offset greedy_snake_end - offset greedy_snake 411 | cld 412 | rep movsb 413 | 414 | ret 415 | 416 | code ends 417 | end start 418 | -------------------------------------------------------------------------------- /G_Snake/贪吃蛇加入自动回放.asm: -------------------------------------------------------------------------------- 1 | assume cs:code , ds:data , ss:stack 2 | 3 | data segment 4 | SCREEN_COLOR dw 0700h 5 | BOUNDARY_COLOR dw 1131h ; 31h=49=数字1 , 11h=00010001b=蓝色 6 | 7 | NEXT_ROW dw 160 8 | 9 | SNAKE_HEAD dw 0 10 | SNAKE_STERN dw 12 11 | SNAKE dw 200 dup (0 , 0 , 0) 12 | 13 | SNAKE_COLOR dw 2201h 14 | 15 | UP db 48h 16 | DOWN db 50h 17 | LEFT db 4Bh 18 | RIGHT db 4Dh 19 | 20 | 21 | FOOD_LOCATION dw 160*3 + 20*2 22 | FOOD_COLOR dw 4439h 23 | NEW_NODE dw 18 24 | 25 | GAME_OVER db 'Game Over!' 26 | 27 | 28 | DIRECTION dw 3 29 | DIRECTION_FUNCTION dw offset isMoveUp - offset greedy_snake + 7e00h 30 | dw offset isMoveDown - offset greedy_snake + 7e00h 31 | dw offset isMoveLeft - offset greedy_snake + 7e00h 32 | dw offset isMoveRight - offset greedy_snake + 7e00h 33 | 34 | REPLAY_DIRECTION_STACK db 200 dup (0ffh) ; 记录游戏每走一步的方向 35 | REPLAY_DIRECTION_TOP dw 0 ; 指向下一个可用位置,存放下一步方向 36 | 37 | data ends 38 | 39 | stack segment 40 | db 128 dup (0) 41 | stack ends 42 | 43 | code segment 44 | start: 45 | mov ax , stack 46 | mov ss , ax 47 | mov sp , 128 48 | 49 | call cpy_greedy_snake 50 | call save_old_int9 51 | call set_new_int9 52 | 53 | mov bx , 0 54 | push bx 55 | mov bx , 7e00h 56 | push bx 57 | retf 58 | 59 | 60 | mov ax , 4c00h 61 | int 21h 62 | 63 | 64 | ;------------------------------------------------------ 65 | greedy_snake: 66 | 67 | call init_reg 68 | call clear_screen 69 | call init_screen 70 | call init_snake 71 | call init_food 72 | call init_direction 73 | 74 | 75 | ;doAgain: 76 | ; call delay 77 | ; cli 78 | ; call isMoveDirection 79 | ; sti 80 | ; jmp doAgain 81 | 82 | infinite: 83 | mov ax , 1000h 84 | jmp infinite 85 | 86 | mov ax , 4c00h 87 | int 21h 88 | 89 | 90 | ;------------------------------------------------------ 91 | init_food: 92 | mov di , FOOD_LOCATION 93 | push FOOD_COLOR 94 | pop es:[di] 95 | ret 96 | 97 | 98 | ;------------------------------------------------------ 99 | replayGame: 100 | call init_reg ; 重新回放录像就要重新初始化到刚开始运行的状态 101 | call clear_screen 102 | call init_screen 103 | 104 | mov SNAKE_HEAD , 0 ; 蛇头和蛇尾变成了运行后的状态,这两个数据就只能在这里手动来修改回最初状态 105 | mov SNAKE_STERN , 12 106 | mov REPLAY_DIRECTION_TOP , 0 ; 重新把top设置成开始状态0,开始执行 107 | 108 | call init_snake 109 | call init_direction 110 | 111 | nextDelay: 112 | call delay 113 | cli 114 | call moveDirection 115 | sti 116 | jmp nextDelay 117 | 118 | replayGameOver: 119 | call clear_screen 120 | call end_game 121 | call recover_int9Ret 122 | call return_dos 123 | 124 | 125 | ;------------------------------------------------------ 126 | moveDirection: 127 | mov bx , REPLAY_DIRECTION_TOP 128 | mov bl , ds:REPLAY_DIRECTION_STACK[bx] 129 | 130 | cmp bl , 0ffh 131 | je replayGameOver 132 | mov bh , 0 133 | add bx , bx 134 | call word ptr ds:DIRECTION_FUNCTION[bx] 135 | 136 | inc REPLAY_DIRECTION_TOP 137 | 138 | ret 139 | 140 | 141 | ;------------------------------------------------------ 142 | isMoveDirection: 143 | mov bx , DIRECTION 144 | add bx , bx 145 | call word ptr ds:DIRECTION_FUNCTION[bx] 146 | ret 147 | 148 | 149 | ;------------------------------------------------------ 150 | delay: 151 | push ax 152 | push dx 153 | 154 | mov dx , 3h 155 | sub ax , ax 156 | delaying: 157 | sub ax , 1 158 | sbb dx , 0 159 | cmp ax , 0 160 | jne delaying 161 | cmp dx , 0 162 | jne delaying 163 | pop dx 164 | pop ax 165 | ret 166 | 167 | 168 | ;------------------------------------------------------ 169 | init_direction: 170 | mov DIRECTION , 3 171 | ret 172 | 173 | ;------------------------------------------------------ 174 | init_snake: 175 | mov bx , offset SNAKE 176 | add bx , SNAKE_HEAD 177 | mov dx , SNAKE_COLOR 178 | mov si , 160*10+40*2 179 | 180 | mov word ptr ds:[bx+0] , 0 ; SNAKE_HEAD =0 , SNAKE_STERN =12 181 | mov word ptr ds:[bx+2] , si 182 | mov word ptr es:[si] , dx 183 | mov word ptr ds:[bx+4] , 6 184 | 185 | sub si , 2 186 | add bx , 6 187 | 188 | mov word ptr ds:[bx+0] , 0 189 | mov word ptr ds:[bx+2] , si 190 | mov word ptr es:[si] , dx 191 | mov word ptr ds:[bx+4] , 12 192 | 193 | sub si , 2 194 | add bx , 6 195 | 196 | mov word ptr ds:[bx+0] , 6 197 | mov word ptr ds:[bx+2] , si 198 | mov word ptr es:[si] , dx 199 | mov word ptr ds:[bx+4] , 18 200 | 201 | ret 202 | 203 | 204 | ;------------------------------------------------------ 205 | init_screen: 206 | mov dx , BOUNDARY_COLOR 207 | call show_up_down_line 208 | call show_left_right_line 209 | ret 210 | 211 | 212 | ;------------------------------------------------------ 213 | show_left_right_line: 214 | mov bx , 0 215 | mov cx , 23 216 | showLeftRightLine: 217 | mov es:[bx] , dx 218 | mov es:[bx+79*2] , dx 219 | add bx , NEXT_ROW 220 | loop showLeftRightLine 221 | ret 222 | 223 | 224 | ;------------------------------------------------------ 225 | show_up_down_line: 226 | mov bx , 0 227 | mov cx , 80 228 | showUpDownLine: 229 | mov es:[bx] , dx 230 | mov es:[bx+160*23] , dx 231 | add bx , 2 232 | loop showUpDownLine 233 | ret 234 | 235 | ;------------------------------------------------------ 236 | clear_screen: 237 | mov bx , 0 238 | mov dx , SCREEN_COLOR 239 | mov cx , 2000 240 | 241 | clearScreen: 242 | mov word ptr es:[bx] , dx 243 | add bx , 2 244 | loop clearScreen 245 | ret 246 | 247 | 248 | ;------------------------------------------------------ 249 | init_reg: 250 | mov bx , 0b800h 251 | mov es , bx 252 | mov bx , data 253 | mov ds , bx 254 | 255 | ret 256 | 257 | 258 | ;------------------------------------------------------ 259 | new_int9: 260 | push ax 261 | call clear_buff 262 | 263 | in al , 60h 264 | 265 | pushf 266 | call dword ptr cs:[200h] ; 等到代码运行的时候,这里的代码已经放到了0:200,当前的cs=0 267 | 268 | cmp al , UP 269 | je isUp 270 | cmp al , DOWN 271 | je isDown 272 | cmp al , LEFT 273 | je isLeft 274 | cmp al , RIGHT 275 | je isRight 276 | 277 | cmp al , 01h ; ESC 的扫描码 278 | je isEsc 279 | 280 | cmp al , 3bh 281 | jne int9Ret 282 | 283 | call change_screen_color 284 | 285 | 286 | int9Ret: 287 | pop ax 288 | iret 289 | 290 | 291 | ;------------------------------------------------------ 292 | isEsc: 293 | pop ax 294 | add sp , 4 295 | popf 296 | ; 这几句代码是模仿int9Ret执行的操作,因为iret的出栈顺序是 pop ip,pop cs,popf 297 | ; 这里还不能返回中断处接着执行,还要做下面其他的操作,所以只能sp+4偏移到栈中存放 298 | ; 标志寄存器的位置,恢复psw,丢掉中断处的下一句要执行的代码,接着做我们期待执行的事 299 | jmp replayGame 300 | ;jmp int9Ret 301 | 302 | 303 | ;------------------------------------------------------ 304 | isUp: 305 | mov di , 160*24+40*2 306 | mov byte ptr es:[di] , 'U' 307 | call isMoveUp 308 | mov dl , 0 ; 向上走的函数地址在DIRECTION_FUNCTION中的偏移是0 309 | call replay_direction_save 310 | jmp int9Ret 311 | 312 | 313 | isDown: 314 | mov di , 160*24+40*2 315 | mov byte ptr es:[di] , 'D' 316 | call isMoveDown 317 | mov dl , 1 318 | call replay_direction_save 319 | jmp int9Ret 320 | 321 | 322 | isLeft: 323 | mov di , 160*24+40*2 324 | mov byte ptr es:[di] , 'L' 325 | call isMoveLeft 326 | mov dl , 2 327 | call replay_direction_save 328 | 329 | jmp int9Ret 330 | 331 | 332 | isRight: 333 | mov di , 160 * 24 + 40 * 2 334 | mov byte ptr es:[di] , 'R' 335 | call isMoveRight 336 | mov dl , 2 337 | call replay_direction_save 338 | 339 | jmp int9Ret 340 | 341 | 342 | ;------------------------------------------------------ 343 | replay_direction_save: 344 | mov bx , REPLAY_DIRECTION_TOP 345 | mov ds:REPLAY_DIRECTION_STACK[bx] , dl ; 把走过的方向放到方向栈中存放 346 | inc REPLAY_DIRECTION_TOP 347 | 348 | ret 349 | 350 | 351 | ;------------------------------------------------------ 352 | isMoveUp: 353 | mov bx , offset SNAKE 354 | add bx , SNAKE_HEAD 355 | 356 | mov si , ds:[bx+2] 357 | sub si , NEXT_ROW 358 | 359 | cmp byte ptr es:[si] , 0 360 | jne noMoveUp 361 | mov DIRECTION , 0 362 | 363 | call new_snake 364 | ret 365 | noMoveUp: 366 | call isFood 367 | ret 368 | 369 | 370 | isMoveDown: 371 | mov bx , offset SNAKE 372 | add bx , SNAKE_HEAD 373 | 374 | mov si , ds:[bx+2] 375 | add si , NEXT_ROW 376 | 377 | cmp byte ptr es:[si] , 0 378 | jne noMoveDown 379 | mov DIRECTION , 1 380 | 381 | call new_snake 382 | ret 383 | noMoveDown: 384 | call isFood 385 | ret 386 | 387 | 388 | isMoveLeft: 389 | mov bx , offset SNAKE 390 | add bx , SNAKE_HEAD 391 | 392 | mov si , ds:[bx+2] 393 | sub si , 2 394 | 395 | cmp byte ptr es:[si] , 0 396 | jne noMoveLeft 397 | mov DIRECTION , 2 398 | 399 | call new_snake 400 | ret 401 | noMoveLeft: 402 | call isFood 403 | ret 404 | 405 | 406 | isMoveRight: 407 | mov bx , offset SNAKE 408 | add bx , SNAKE_HEAD 409 | 410 | mov si , ds:[bx+2] 411 | add si , 2 412 | 413 | cmp byte ptr es:[si] , 0 414 | jne noMoveRight 415 | mov DIRECTION , 3 416 | 417 | call new_snake 418 | ret 419 | noMoveRight: 420 | call isFood 421 | ret 422 | 423 | 424 | ;------------------------------------------------------ 425 | isFood: 426 | cmp byte ptr es:[si] , '9' ; 我们前面设置了食物的字符是9,前景背景都是红色 427 | jne noFood 428 | 429 | call eat_food 430 | call set_new_food ; 吃掉一个要在生成一个 431 | ret 432 | 433 | noFood: 434 | call clear_screen 435 | call end_game 436 | ;call recover_int9Ret 437 | ;call return_dos 438 | ret 439 | 440 | 441 | ;------------------------------------------------------ 442 | set_new_food: 443 | mov al , 0 444 | out 70h , al 445 | in al , 71h 446 | 447 | mov dl , al 448 | and dl , 00001111b ; dl中是个位数的数字 449 | push cx 450 | mov cl , 4 451 | mov ch , 0 452 | shr al , cl ; al中是十位数的数字 453 | pop cx 454 | mov bl , 10 455 | mul bl ; ax=al*bl 456 | add al , dl ; 得到秒数 457 | 458 | mul al ; 如果al是奇数,得到的肯定也是一个奇数,24*80=1920种显示位置,60*60=3600种位置 459 | shr al , 1 ; 二进制,右移一位去掉产生奇数的1 460 | shl al , 1 ; 控制误差,再左移一位,这样误差就为1 461 | 462 | mov bx , ax ; 得到下一个食物出现的位置 463 | cmp byte ptr es:[bx] , 0 ; 如果得到的位置不是空闲的 464 | jne set_new_food ; 这里有一个问题,如果生成的食物位置不行,到这里要跳转,又要进行下次执行,但是又要发生键盘中断 465 | 466 | push FOOD_COLOR 467 | pop es:[bx] 468 | 469 | ret 470 | 471 | 472 | ;------------------------------------------------------ 473 | eat_food: 474 | push NEW_NODE ; 记录新节点的位置 475 | pop ds:[bx+0] 476 | 477 | mov bx , offset SNAKE 478 | add bx , NEW_NODE 479 | 480 | mov word ptr ds:[bx+0] , 0 ;食物节点变成头结点,蛇变长一截 481 | mov ds:[bx+2] , si 482 | push SNAKE_COLOR 483 | pop es:[si] 484 | 485 | push SNAKE_HEAD 486 | pop ds:[bx+4] 487 | 488 | push NEW_NODE 489 | pop SNAKE_HEAD 490 | 491 | add NEW_NODE , 6 492 | ret 493 | 494 | 495 | ;------------------------------------------------------ 496 | return_dos: 497 | mov ax , 4c00h 498 | int 21h 499 | 500 | 501 | ;------------------------------------------------------ 502 | recover_int9Ret: 503 | push bx 504 | mov bx , 0 505 | mov es , bx 506 | push es:[200h] 507 | pop es:[9*4] 508 | push es:[202h] 509 | pop es:[9*4+2] 510 | pop bx 511 | ret 512 | ;------------------------------------------------------ 513 | end_game: 514 | call clear_screen 515 | mov si , offset GAME_OVER 516 | mov di , 160*12+35*2 517 | mov cx , 10 518 | s: 519 | mov al , [si] 520 | mov byte ptr es:[di] , al 521 | mov byte ptr es:[di+1] , 00000100b 522 | inc si 523 | add di , 2 524 | loop s 525 | ret 526 | 527 | 528 | ;------------------------------------------------------ 529 | new_snake: 530 | push SNAKE_STERN 531 | pop ds:[bx+0] 532 | 533 | mov bx , offset SNAKE 534 | add bx , SNAKE_STERN 535 | 536 | push ds:[bx+0] 537 | 538 | mov word ptr ds:[bx+0] , 0 539 | 540 | mov di , ds:[bx+2] 541 | push SCREEN_COLOR 542 | pop es:[di] 543 | 544 | mov ds:[bx+2] , si 545 | push SNAKE_COLOR 546 | pop es:[si] 547 | 548 | push SNAKE_HEAD 549 | pop ds:[bx+4] 550 | 551 | push SNAKE_STERN ;曾经的尾巴变成头 552 | pop SNAKE_HEAD 553 | 554 | pop SNAKE_STERN 555 | 556 | ret 557 | 558 | 559 | ;------------------------------------------------------ 560 | clear_buff: 561 | mov ah , 1 562 | int 16h 563 | jz clearBuffRet ; ZF=1,键盘无输入 564 | mov ah , 0 565 | int 16h 566 | jmp clear_buff 567 | clearBuffRet: 568 | ret 569 | 570 | 571 | ;------------------------------------------------------ 572 | change_screen_color: 573 | push bx 574 | push cx 575 | push es 576 | 577 | mov bx , 0b800h 578 | mov es , bx 579 | mov bx , 1 580 | mov cx , 2000 581 | changeScreenColor: 582 | inc byte ptr es:[bx] 583 | add bx , 2 584 | loop changeScreenColor 585 | 586 | pop es 587 | pop cx 588 | pop bx 589 | 590 | ret 591 | 592 | snake_end: 593 | nop 594 | 595 | 596 | ;------------------------------------------------------ 597 | cpy_greedy_snake: 598 | mov bx , cs 599 | mov ds , bx 600 | mov si , offset greedy_snake 601 | 602 | mov bx , 0 603 | mov es , bx 604 | mov di , 7e00h 605 | 606 | mov cx , offset snake_end - offset greedy_snake 607 | rep movsb 608 | 609 | ret 610 | 611 | 612 | ;------------------------------------------------------ 613 | save_old_int9: 614 | mov bx , 0 615 | mov es , bx 616 | 617 | push es:[9*4] 618 | pop es:[200h] 619 | push es:[9*4+2] 620 | pop es:[202h] 621 | 622 | ret 623 | 624 | 625 | ;------------------------------------------------------ 626 | set_new_int9: 627 | mov bx , 0 628 | mov es , bx 629 | cli 630 | mov word ptr es:[9*4] , offset new_int9 - offset greedy_snake + 7e00h 631 | mov word ptr es:[9*4+2] , 0 632 | sti 633 | ret 634 | 635 | 636 | 637 | code ends 638 | 639 | end start -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | > 注:GREEDY_SNAKE 是基于8086 汇编语言开发的,汇编语言风格是采用《汇编语言》第二版 王爽著; 2 | 3 | ### Greedy_Snake 要在Intel实模式下运行,所以运行项目前要安装DOSBOX 虚拟出一个8086实模式环境 ### 4 | 1. 安装DOSBOX:运行DOSBox0.74-win32-installer.exe即可安装; 5 | 2. 将Greedy_Snake clone到本地任意盘,eg:d:\Greedy_Snake 6 | - mount d:\Greedy_Snake 到一个指定虚拟盘符: 7 | - `mount k d:\Greedy_Snake` (why is k? because i like this charactor) 8 | 3. 运行G_Snake 9 | - 在DOSBOX的DOS提示符下键入: 10 | - `Z:\>K:`(回车) 11 | - `K:\>cd G_Snake`(回车) 12 | - 使用masm 5.0工具编译、链接、运行.asm源程序 13 | - MASM.EXE、LINK.EXE、debug.exe、edit.com都是开发工具,用来编译、链接、调试和编辑代码 14 | 4. G_Snake.asm 是最终代码; 15 | - `masm G_Snake.asm` (编译游戏) 16 | - `link G_Snake.obj` (链接游戏) 17 | - `G_Snake` (运行游戏) 18 | 5. G_Snake.asm分了4个步骤: 19 | - map.asm 是绘制游戏界面的 20 | - sMove.asm 是让小蛇响应对应的键盘中断自动移动 21 | - sMA.asm 是让小蛇响应方向后自动移动 22 | - G_Snake.asm 是最终程序 23 | 24 | ---------- 25 | 26 | ### G_Snake.asm 实现了随机出现食物、统计分数、显示小蛇运动方向、响应键盘中断、指定方向自动移动、游戏结束恢复9h键盘中断和正常退出。 ### 27 | 28 | ---------- 29 | #### 游戏开始界面 #### 30 | ![游戏开始界面](https://github.com/meihao1203/Greedy_Snake/blob/master/G_Snake/1.png) 31 | #### 运行吃到6个食物 #### 32 | ![游戏运行界面](https://github.com/meihao1203/Greedy_Snake/blob/master/G_Snake/2.png) 33 | #### 游戏结束界面 #### 34 | ![游戏结束界面](https://github.com/meihao1203/Greedy_Snake/blob/master/G_Snake/3.png) 35 | 36 | 37 | ---------- 38 | 注:游戏运行中有可能会卡住不出现食物,这时候是程序通过获取cmos芯片中的秒数来计算得出的食物位置不合理,正在重新获取新的秒数计算新的食物位置,很快就会恢复。 39 | 40 | [![LICENSE](https://img.shields.io/badge/license-Anti%20996-blue.svg)](https://github.com/996icu/996.ICU/blob/master/LICENSE) 41 | --------------------------------------------------------------------------------