首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么一个proc运行两次而不是一个(几乎)无限循环?

为什么一个proc运行两次而不是一个(几乎)无限循环?
EN

Stack Overflow用户
提问于 2021-11-19 09:50:51
回答 1查看 79关注 0票数 1

这个代码是用于蛇游戏的。proc检测方向调用了mov_snake proc,它做了许多事情,最后调用了detect_direction。当我在DosBox (最更新的版本)中运行代码时,蛇的位置被更新了两次,代码停止运行(不是通过游戏结束,c:tasm/bin/行再次出现)。

如果你有任何想法,请评论它。

代码语言:javascript
复制
ideal
model small
stack 100h
dataseg
 message db 'game over, to play again press y$'
object_location dw 2
clock equ es:6ch
longer db 0
snake dw 2000 dup (0)
codeseg
proc cleanscreen ; cleans the screen
    push cx
    push bx
    mov cx,4000d
    mov bx,0
    clean:  mov [byte  ptr es:bx],0
    inc bx
    loop clean
    pop bx
    pop cx
    ret
endp cleanscreen

proc startgame
mov ax, 0b800h
mov es, ax 
call cleanscreen
mov dl, '*'
mov dh,200
mov di,2000d;snake head first position
mov [es:di], dx
mov [es:di-2], dx
mov [es:di-4], dx
mov [word ptr snake],1996d
mov [word ptr snake+2], 1998d
mov [word ptr snake+4], 2000d
mov cx, 3
call random
mov ah,0
int 16h
call detect_direction
ret
endp startgame

proc delay
push cx
push bx
    mov cx, 0ffffh ;delay loop
    lopa:
        mov bx, 20d
        lopb:
            dec bx
            cmp bx, 0
            jnz lopb
    loop lopa
    pop bx
    pop cx
    ret
endp delay

proc buffer
mov ah,1
int 16h
jz nothingchanged
mov ah,0
int 16h
nothingchanged:
ret
endp buffer

proc detect_direction
    cmp al, 'w'
    je up
    cmp al, 'a'
    je left
    cmp al, 'd'
    je right
    cmp al, 's'
    je  down
    cmp al, 'q'
    je gameover
up: sub di,160
jmp move_on
down: add di,160d
jmp move_on
right: add di,2
jmp move_on
left: sub di,2
jmp move_on
gameover:call endgame
move_on:
call move_func
ret
endp detect_direction

proc updatesnake
    push bx
    push si
    mov bx, offset snake 
    cmp [longer],1 ;if longer is one , increase snake length
    je increa
    mov si, [bx]
    mov [word ptr es:si], 0 ; delete tail
    sub bx,2
    increa:
        mov [es:di], dx
        add bx,2 ; prevent tail value from being deleted when being increased
    updateloop:
        mov si, [bx+2]
        mov [bx], si
        inc bx 
    loop updateloop
    mov [bx], di
    cmp [longer],1
    jne reg
    inc cx ; this prevent inseting a junk value when increasing snake's length
    reg:
    pop si
    pop bx
ret 
endp updatesnake

proc move_func
call updatesnake
call delay
call boundries
call check_if_eaten
call buffer
call detect_direction
ret
endp move_func

proc boundries
push ax
push dx
cmp di,160d
ja keep_playing
cmp di,3840d
jb keep_playing
mov ax,di
mov dl,160d
div dl
cmp ah,0
jne keep_playing
mov ax,di
inc ax
div dl
cmp ah,0
jne keep_playing
call endgame
keep_playing:
pop dx
pop ax
ret
endp boundries

proc random
push ax
push bx
push cx
push si
push dx
mov ax, [clock] ; read timer counter
mov cx, [word cs:bx] ; read one byte from memory
xor si, cx ; xor memory and counter
and si, 2000d ; leaves value between 0-2000
sal si,1 ; mult value
mov [object_location], si ; save in memory for future checking
mov dl,'@'
mov dh, 150
mov [es:si],dx
pop dx
pop si
pop cx
pop bx
pop ax
ret
endp random

proc check_if_eaten
mov [longer],0
cmp di, [object_location]
jne skip_increase
call random
mov [longer], 1
skip_increase:
ret
endp check_if_eaten

proc endgame
push dx
call cleanscreen
mov dx, offset message
mov ah,9h
int 21h
mov ah,0
int 16
cmp al, 'y'
jne line
call startgame
line:
pop dx
ret
endp endgame
start:
    mov ax, @data
    mov ds, ax
    call startgame
exit:
    mov ax, 4c00h
    int 21h
end start
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-11-19 18:44:21

your previous question以来,您的代码有了很大的改进,但由于仍然存在许多错误,我怀疑是否有人能完全回答您的“为什么??”

我将集中讨论你的一个程序,赏金,在这里,如果蛇与顶部或底部的行或左或右列发生碰撞,您想要结束游戏。

第一个测试,cmp di,160 ja keep_playing,意味着只要蛇不接触屏幕的顶部行,就可以继续玩。其他边界都没有被核实过!

如果将条件跳转倒置到cmp di, 160 jb stop_playing中,您需要的是什么。

在第四个测试(右侧边框)中,将奇数除以160 (因为inc ax)。这将总是产生一个非零余数。所以,这不是一个有用的测试。要测试左边框和右边框,只需进行一次除法,并对其馀部分进行两次解释:0是左侧碰撞,158是右侧碰撞。

代码语言:javascript
复制
proc boundries
  push ax
  push dx
  cmp  di, 160         ; Top row
  jb   stop_playing
  cmp  di, 3840        ; Bottom row
  ja   stop_playing
  mov  ax, di
  mov  dl, 160
  div  dl
  cmp  ah, 0           ; Left column
  je   stop_playing
  cmp  ah, 158         ; Right column
  jne  keep_playing    ; All 4 tests passed!
stop_playing:
  call endgame
keep_playing:
  pop  dx
  pop  ax
  ret
endp boundries

作为一种奖励,随机过程,在AX寄存器中读取时钟,但突然不使用AX,而是开始使用SI

  • mov ax, [clock] ; read timer counter指令将读取由clock equ es:6ch定义的真正时钟,如果您将ES段寄存器临时设置为0040小时的BIOS数据页:

推送es mov ax,0040 h mov es,ax mov ax,生物钟;读取计时器计数器

  • and si, 2000 ; leaves value between 0-2000这样的AND指令不会像你期望的那样限制值!限制在该特定范围内需要到2001年除以并使用剩余部分。

这是我的新随机程序的版本:

代码语言:javascript
复制
proc random
  push ax          ; \
  push bx          ;  | (i)
  push dx          ; /

  push ds          ; (ii)
  xor  dx, dx      ; The word-sized `DIV` division requires this
  mov  ds, dx
  mov  ax, [046Ch] ; (iii) read timer counter
  pop  ds
  xor  ax, [cs:bx] ; XOR counter and a (randomly chosen?) WORD from memory
  mov  bx, 2000    ; (iiii)
  div  bx          ; DX:AX / BX -> Remainder in DX is [0,1999]
  shl  dx, 1       ; Convert into text video offset
  mov  bx, dx      ; Move to an address register
  mov  [object_location], bx
  mov  word [es:bx], 9640h ; Character @ with attribute 96h (150)

  pop  dx
  pop  bx
  pop  ax
  ret
endp random

(i)使用==的寄存器越少,需要保存的寄存器就越少!

(ii)使用DS提供较短的代码。

(iii)停止使用“令人困惑的”时钟equ,其中包含一个es:段覆盖。

(iiii)为了留在屏幕上,object_location应该限制在{0,2,4,6,…,3998]。

我最近发布了一个Q/A The quintessential Snake Game. How to keep track of the snake?。也许你可以从中得到一两个想法..。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70032868

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档