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

如何在16位x86 Intel汇编中实现贪吃蛇的转向功能

16位x86汇编文本模式贪吃蛇转向功能实现方案

我正在修读汇编课程,需要完成一个文本模式(使用字符而非像素)的贪吃蛇游戏项目。目前遇到转向功能实现难题,仅能实现无实际作用的90度转向,希望了解正确的转向实现方法。使用的是16位x86 Intel汇编,当前所有逻辑均为硬编码,未接入键盘输入。

以下是当前代码:

[org 0x0100]
jmp _start

string : db 'Score '
len: dw 6

Snake_X : dw 10
Snake_Y : dw 5

X_speed: dw 1
Y_speed: dw 1

snakeLen: dw 6
_start:
    call cls
    call playingField
    push 0x000A
    call Score
    mov cx,2
    gameLoop:
        push word[snakeLen]        ; length of snake
        call PrintsnakeX           ; horizontal movement
        inc cx
    cmp cx,20
    jl gameLoop
    push 0x000A
    call sleep
    call cls
    call playingField
    push 0x000A
    call Score
    mov cx,2
    gameLoop1:
        mov ax,[snakeLen]
        push ax
        call PrintsnakeY    ; vertical movement
        inc cx
    cmp cx,10
    jl gameLoop1
end:
mov ax,0x4c00
int 0x21

PrintsnakeX: 
    push bp
    mov bp, sp
    push es
    push ax
    push cx
    push si
    push di
        mov ax, 0xb800
        mov es, ax              ; point es to video base
        mov cx, word[bp+4]          ; load length of snake in cx
        mov ax, 80              ; load ax with columns per row
        mul word[Snake_Y]        ;  y position
        add ax, word[Snake_X]         ;  x position
        shl ax, 1               ; turn into byte offset
        add ax,cx
        mov di,ax               ; point di to required location
        mov al, '*'          ; point si to string
        mov ah, 40h          ; load attribute in ah
        push di
        cld                     ; auto increment mode
        s1:
            cmp cx,0
            jz break1
            stosw
        dec cx
        jmp s1
        break1:
        push 0x000F
        call sleep
        mov ah,0x00
        pop di
        stosw
    mov ax,word[X_speed]
    add word[Snake_X],ax
    pop di
    pop si
    pop cx
    pop ax
    pop es
    pop bp
ret 2
PrintsnakeY: 
    push bp
    mov bp, sp
    push es
    push ax
    push cx
    push si
    push di
        mov ax, 0xb800
        mov es, ax              ; point es to video base
        mov cx, [bp+4]          ; load length of string in cx
        mov ax, 80              ; load al with columns per row
        mul word[Snake_Y]        ; multiply with y position
        add ax, word[Snake_X]         ; add x position
        add ax,cx
        add ax,3
        shl ax, 1               ; turn into byte offset
        mov di,ax               ; point di to required location
        mov al, '*'          ; point si to string
        mov ah, 40h          ; load attribute in ah
        push di
        cld                     ; auto increment mode
        .s1:
            cmp cx,0
            jz break
            stosw
            add di,158
        dec cx
        jmp .s1
        break:
        push 0x000F
        call sleep
        mov ah,0x00
        pop di
        stosw
     mov ax,word[Y_speed]
    add word[Snake_Y],ax
    pop di
    pop si
    pop cx
    pop ax
    pop es
    pop bp
ret 2
cls: 
    push es
    push ax
    push cx
    push di
    mov ax, 0xb800
    mov es, ax          ; point es to video base
    xor di, di          ; point di to top left column
    mov ax, 0x0720      ; space char in normal attribute
    mov cx, 2000        ; number of screen locations
    cld                 ; auto increment mode
    rep stosw           ; clear the whole screen
    pop di
    pop cx
    pop ax
    pop es
ret


sleep:
    push bp
    mov bp,sp
    push cx
    mov cx,[bp+4]
    d1:
        push cx
        mov cx,0xFFFF
        delayLoop1:
        loop delayLoop1
        pop cx
    loop d1
    pop cx
    pop bp
ret 2 

Score:
    push bp
    mov bp,sp
    push ax
    mov ax,24   ; y position of score string
    push ax
    mov ax,2    ; x position of score string 
    push ax
    mov ax, string   ; point to score string
    push ax 
    push word[len]   ; len of score string 
    call printstr
    mov ax,24
    push ax
    mov ax,8
    push ax
    mov ax,[bp+4]
    push ax
    call digits
    pop ax
    pop bp
ret 2

playingField:
    push es
    push ax
    mov ax ,0xb800
    mov es,ax
    mov ax,164
    push ax
    mov ax,74
    push ax
    call HLine
    mov ax,164
    push ax
    mov ax,21
    push ax
    call vLine
    mov ax,312
    push ax
    mov ax,21
    push ax
    call vLine
    mov ax,3524
    push ax
    mov ax,75
    push ax
    call HLine
    pop ax
    pop es
ret

HLine:
    push bp
    mov bp,sp           
    mov di,[bp+6]
    mov ax,0x4020
    mov cx,[bp+4]
    cld
    .lp2:
        stosw
    loop .lp2
    pop bp
ret 4
vLine:
    push bp
    mov bp,sp      
    mov di,[bp+6]
    mov ax,0x4020
    mov cx,[bp+4]
    cld
    .lp1:
        stosw
        add di,158
    loop .lp1
    pop bp
ret 4

printstr: 
    push bp
    mov bp, sp
    push es
    push ax
    push cx
    push si
    push di
        mov ax, 0xb800
        mov es, ax              ; point es to video base
        mov al, 80              ; load al with columns per row
        mul byte [bp+10]        ; multiply with y position
        add ax, [bp+8]         ; add x position
        shl ax, 1               ; turn into byte offset
        mov di,ax               ; point di to required location
        mov si, [bp+6]          ; point si to string
        mov cx, [bp+4]          ; load length of string in cx
        mov ah, 09h             ; load attribute in ah
        cld                     ; auto increment mode
        nextchar: 
        lodsb                   ; load next char in al
        stosw                   ; print char/attribute pair
    loop nextchar               ; repeat for the whole string
    pop di
    pop si
    pop cx
    pop ax
    pop es
    pop bp
ret 8

digits:
    push bp
    mov bp,sp
    push cx
    push ax
    push bx
    push dx
    push di
    mov ax, 0xb800
    mov es, ax  
    mov al, 80              ; load al with columns per row
    mul byte [bp+8]        ; multiply with y position
    add ax, [bp+6]         ; add x position
    shl ax, 1               ; turn into byte offset
    mov di,ax     
    mov ax,[bp+4]
    mov cx,0
    mov bx,10 
        lp1:
            mov dx,0
            div bx
            add dl,0x30
            push dx
            inc cx
            cmp ax,0
        jne lp1
        cld
        lp2:
            pop ax
            mov ah,0x07
            stosw
    loop lp2
    pop di
    pop dx
    pop bx
    pop ax
    pop si
    pop bp
ret 6

核心问题分析

当前代码的gameLoop和gameLoop1是硬编码的分段移动逻辑,没有基于方向状态的动态控制;且X_speed和Y_speed同时为1,导致蛇始终斜向移动,完全缺失转向的核心逻辑。

正确转向实现步骤

1. 引入方向状态变量

新增方向存储单元,用常量定义四个方向,统一管理蛇的移动方向:

UP    equ 0
DOWN  equ 1
LEFT  equ 2
RIGHT equ 3
direction: db RIGHT  ; 初始方向设为向右

2. 修正速度变量逻辑

蛇同一时间只能有一个方向的速度,不能同时存在水平和垂直速度:

  • 初始化时,根据初始方向设置速度:比如向右时X_speed=1,Y_speed=0
  • 转向时,重置速度值:
    • 转向UP:Y_speed=-1,X_speed=0
    • 转向DOWN:Y_speed=1,X_speed=0
    • 转向LEFT:X_speed=-1,Y_speed=0
    • 转向RIGHT:X_speed=1,Y_speed=0

3. 实现键盘输入检测(替代硬编码)

使用DOS中断int 0x16实现非阻塞式键盘检测,读取按键后调整方向(禁止直接反向,比如向右时不能直接转向左):

check_key:
    mov ah, 0x01
    int 0x16          ; 检测是否有按键按下
    jz no_key_press   ; 无按键则跳过
    mov ah, 0x00
    int 0x16          ; 读取按键ASCII码
    cmp al, 'w'
    je set_up
    cmp al, 's'
    je set_down
    cmp al, 'a'
    je set_left
    cmp al, 'd'
    je set_right
no_key_press:
    ret

set_up:
    mov al, [direction]
    cmp al, DOWN
    je no_key_press   ; 禁止反向
    mov byte [direction], UP
    mov word [Y_speed], -1
    mov word [X_speed], 0
    ret

set_down:
    mov al, [direction]
    cmp al, UP
    je no_key_press
    mov byte [direction], DOWN
    mov word [Y_speed], 1
    mov word [X_speed], 0
    ret

set_left:
    mov al, [direction]
    cmp al, RIGHT
    je no_key_press
    mov byte [direction], LEFT
    mov word [X_speed], -1
    mov word [Y_speed], 0
    ret

set_right:
    mov al, [direction]
    cmp al, LEFT
    je no_key_press
    mov byte [direction], RIGHT
    mov word [X_speed], 1
    mov word [Y_speed], 0
    ret

4. 统一蛇的绘制与移动逻辑

移除分离的PrintsnakeX和PrintsnakeY,改为通用的DrawSnake函数:

  • 用数组存储蛇身每个节点的坐标,比如snake_body: dw 10,5,9,5,8,5,7,5,6,5,5,5(每个节点占两个字,存X、Y)
  • 移动时,先计算新头部坐标(当前头部+速度),然后将所有蛇节坐标向后移位,最后更新头部坐标,再重新绘制整个蛇身。

5. 重构主循环

将硬编码的分段循环替换为单一主循环,每次循环先检测键盘调整方向,再更新蛇的位置并绘制:

_start:
    call cls
    call playingField
    push 0x000A
    call Score
    ; 初始化方向和速度
    mov byte [direction], RIGHT
    mov word [X_speed], 1
    mov word [Y_speed], 0
gameLoop:
    call check_key      ; 检测键盘输入调整方向
    push word[snakeLen]
    call DrawSnake      ; 统一绘制蛇身
    push 0x000F
    call sleep
    cmp word[Snake_X], 22  ; 碰撞左边界检测
    jl end
    cmp word[Snake_X], 94  ; 碰撞右边界检测
    jg end
    cmp word[Snake_Y], 2   ; 碰撞上边界检测
    jl end
    cmp word[Snake_Y], 23  ; 碰撞下边界检测
    jg end
    jmp gameLoop
end:
mov ax,0x4c00
int 0x21

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 05:47:04