You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在emu8086贪吃蛇游戏中检测指定内存位置实现蛇身增长?

问题描述

我用emu8086编写了一个贪吃蛇汇编游戏,代码如下:

org     100h

jmp     start

s_size  equ     7       
snake dw s_size dup(0)
tail    dw      ?

left    equ     4bh
right   equ     4dh
up      equ     48h
down    equ     50h       

cur_dir db      right
wait_time dw    0

msg1    db "                                  Snake game", 0dh,0ah,0ah
    db "                        Yilani on oklari ile kontrol et.", 0dh,0ah,0ah  
    db "                         Duvarlara degersen yanarsin!!!", 0dh,0ah, 0ah  
    db "                            Esc programi sonlandirir.", 0dh,0ah,0ah,0ah
    db "                           Baslamak icin bir tusa bas.$"

msg3  db "", 0dh,0ah, 0ah,0ah, 0ah,0ah,0ah, 0ah,0ah,0ah, 0ah,0ah,0ah, 0ah,0ah, 0ah, 0ah, 0ah,               
db "                                  !!GAME OVER!!", 0dh,0ah,0ah, 0ah, 0ah, 0ah,0ah, 0ah,0ah,      
db " $"

start:       
; print welcome message:
mov     dx, offset msg1
mov     ah, 9 
int     21h

; wait for any key:
mov ah, 00h
int 16h 

mov ax, 0xB800  ; Video memory segment
mov es, ax      ; Set ES segment to video memory

es: mov [09b4h],'?'
es: mov [0848h],'0'
es: mov [06b0h],'0' 
es: mov [01E8h],'0'

; hide text cursor:
mov     ah, 1
mov     ch, 2bh
mov     cl, 0bh
int     10h           

game_loop:
; === select first video page
mov     al, 0  
mov     ah, 05h
int     10h

mov     dx, snake[0]
; set cursor at dl,dh
mov     ah, 02h
int     10h

; print 'o' at the location:
mov     al, 'o'
mov     ah, 09h
mov     bl, 0eh 
mov     cx, 1  
int     10h

; === keep the tail:
mov     ax, snake[s_size * 2 - 2]
mov     tail, ax

call    move_snake

; === hide old tail:
mov     dx, tail
; set cursor at dl,dh
mov     ah, 02h
int     10h

; print '.' at the old location:
mov     al, ' '
mov     ah, 09h
mov     bl, 0eh 
mov     cx, 1  
int     10h

check_for_key:
; === check for player commands:
mov     ah, 01h
int     16h
jz      no_key

mov     ah, 00h
int     16h

cmp     al, 1bh    ; esc - key?
je      stop_game  

mov     cur_dir, ah

no_key:
; === wait a few moments here:
mov     ah, 00h
int     1ah
cmp     dx, wait_time
jb      check_for_key
add     dx, 4
mov     wait_time, dx

; === eternal game loop:
jmp     game_loop

stop_game:
; show cursor back:
mov     ah, 1
mov     ch, 0bh
mov     cl, 0bh
int     10h

ret

move_snake proc near
; set es to bios info segment:  
mov     ax, 40h
mov     es, ax

  ; point di to tail
  mov   di, s_size * 2 - 2
  ; move all body parts
  mov   cx, s_size-1
move_array:
  mov   ax, snake[di-2]
  mov   snake[di], ax
  sub   di, 2
  loop  move_array

cmp     cur_dir, left
  je    move_left
cmp     cur_dir, right
  je    move_right
cmp     cur_dir, up
  je    move_up
cmp     cur_dir, down
  je    move_down

jmp     stop_move       

move_left:
  mov   al, b.snake[0]
  dec   al
  mov   b.snake[0], al

  cmp   al, -1
  jne   stop_move       
  mov   al, es:[4ah]    ; col number.
  dec   al
  call game_over  
  
  jmp   stop_move

move_right:
  mov   al, b.snake[0]
  inc   al
  mov   b.snake[0], al 
  cmp   al, es:[09b4h]
  cmp   al, es:[4ah]    ; col number. 
  jb    stop_move
  call game_over   
  jmp   stop_move

move_up:
  mov   al, b.snake[1]
  dec   al
  mov   b.snake[1], al
  cmp   al, -1
  jne   stop_move
  mov   al, es:[84h]    ; row number -1.
  call game_over      
  jmp   stop_move

move_down:
  mov   al, b.snake[1]
  inc   al
  mov   b.snake[1], al
  cmp   al, es:[84h]    ; row number -1.
  jbe   stop_move
  call game_over   
  jmp   stop_move

stop_move:
ret       

grow_snake:
ret

game_over: 
mov     dx, offset msg3
mov     ah, 9 
int     21h

; wait for any key:
mov ah, 00h
int 16h   

move_snake endp

我想让蛇头碰到视频内存中预先放置食物的09b4h、0848h、06b0h、01E8h位置时实现蛇身增长,之前尝试在stop_game里调用grow_snake、递增s_size都没成功,请问该如何检测蛇是否到达指定内存位置,并实现蛇身增长?


解决方案

一、核心问题分析

你之前的失败原因有两个:

  1. s_size是用equ定义的常量,无法在运行时修改;
  2. 没有把蛇头的坐标转换成视频内存偏移地址,无法和食物地址做有效对比。

二、步骤1:修改数据段定义

把常量s_size改成变量,同时给蛇数组预留足够空间,避免增长时溢出:

org     100h

jmp     start

s_size  dw     7        ; 从equ常量改为dw变量
snake dw 100 dup(0)     ; 预留100个位置,足够蛇身增长
tail    dw      ?

; 其余常量、变量定义不变

三、步骤2:实现蛇头与食物的碰撞检测

视频内存中每个字符占2字节(1字节字符+1字节属性),需要把蛇头的DH(行)、DL(列)坐标转换成对应偏移地址,再和食物地址对比:

; 新增检测食物的子程序,调用前DX需存入蛇头坐标(DH=行,DL=列)
check_food proc near
    mov ax, dx
    mov cl, 80
    mul cl               ; AX = DH * 80(一行80列)
    add al, dl
    adc ah, 0            ; AX = DH*80 + DL(坐标转成线性位置)
    shl ax, 1            ; AX = (DH*80+DL)*2(转成视频内存偏移)

    ; 依次对比四个食物地址
    cmp ax, 09b4h
    je eat_food
    cmp ax, 0848h
    je eat_food
    cmp ax, 06b0h
    je eat_food
    cmp ax, 01E8h
    je eat_food
    jmp no_food

eat_food:
    ; 清除已吃掉的食物(视频内存设为空格)
    mov bx, ax
    mov ax, 0B800h
    mov es, ax
    mov byte ptr es:[bx], ' '
    ; 蛇身增长:s_size加1
    inc word ptr s_size
    mov tail, 0          ; 标记不需要隐藏旧尾巴
    jmp end_check

no_food:
    ; 记录尾巴位置,后续需要隐藏
    mov ax, snake[word ptr s_size*2 - 2]
    mov tail, ax

end_check:
    ret
check_food endp

四、步骤3:修改游戏主循环流程

在移动蛇之后调用碰撞检测,根据检测结果决定是否隐藏旧尾巴:

game_loop:
; ... 原有设置光标、打印蛇头的代码不变

call    move_snake
call    check_food   ; 新增:检测是否吃到食物

; 只有没吃到食物时才隐藏旧尾巴
cmp tail, 0
je skip_hide_tail

; === hide old tail:
mov     dx, tail
mov     ah, 02h
int     10h
mov     al, ' '
mov     ah, 09h
mov     bl, 0eh 
mov     cx, 1  
int     10h

skip_hide_tail:
; ... 原有按键检测、等待代码不变

五、步骤4:调整move_snake子程序的循环逻辑

因为s_size现在是变量,循环次数需要动态获取:

move_snake proc near
mov     ax, 40h
mov     es, ax

; 循环次数 = s_size - 1
mov cx, word ptr s_size
dec cx
mov   di, word ptr s_size * 2 - 2
move_array:
mov   ax, snake[di-2]
mov   snake[di], ax
sub   di, 2
loop  move_array

; ... 原有方向移动代码不变
move_snake endp

内容的提问来源于stack exchange,提问作者MuratSklzn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 14:14:56