I'm making the snake game and ran into a problem.
For now, I used int 21h
, so it only moved when I pressed a key. Now I want it to move continuously so it isn't waiting for me to press a key but just checks if there's something in the buffer. To do that I'm using ah 0h and int 16h
however, I don't know how to do that it'll always move. I think I need to add a loop somewhere that checks what is in dir
and based on that it goes to where it's supposed to go but I just don't know what the loop is supposed to look like (I also think I should use delay if I'm not wrong).
I think the relevant parts are:
mov ah, 0h
int 16h
mov [byte ptr saveal], al
cmp [byte ptr saveal], 'w'
jz w
cmp [byte ptr saveal], 'a'
jz a
cmp [byte ptr saveal], 's'
jz s
cmp [byte ptr saveal], 'd'
jz d
cmp [byte ptr saveal], 'q'
jmp exit
w:
mov [byte ptr dir], 1
call up
jmp wasd
s:
mov [byte ptr dir], 2
call down
jmp wasd
a:
mov [byte ptr di], 3
call left
jmp wasd
d:
mov [byte ptr dir], 4
call right
jmp wasd
The whole code is:
MODEL small
STACK 100h
DATASEG
; --------------------------
; Your variables here
; --------------------------
saveal db ' '
dir db 0 ;not used
app dw 0 ;place of the apple
st_am dw 3
stars dw 0, 0, 0 ;places of the *
CODESEG
proc black
body:
mov [es:si], ax
add si, 2
cmp si, 25*80*2
jnz body
ret
endp black
proc up
mov di, 80*2
cmp si, di
jb not_move_up
cmp si, [app]
jnz move_up
call apple
move_up:
call delete
call replace_stars
sub si, 80*2
mov ah, 156
mov al, '*'
mov [es:si], ax
mov [stars], si
not_move_up:
ret
endp up
proc down
mov di, (24*80*2)-1
cmp si, di
jg not_move_down
cmp si, [app]
jnz move_down
call apple
move_down:
call delete
call replace_stars
add si, 80*2
mov ah, 156
mov al, '*'
mov [es:si], ax
mov [stars], si
not_move_down:
ret
endp down
proc left
mov dx, 0
mov bx, si
mov ax, si
mov si, 80*2
div si
mov si, bx
cmp dx,0
jz not_move_left
cmp si, [app]
jnz move_left
call apple
move_left:
call delete
call replace_stars
sub si, 2
mov ah, 156
mov al, '*'
mov [es:si], ax
mov [stars], si
not_move_left:
ret
endp left
proc right
mov dx, 0
mov bx, si
mov ax, si
mov si, 80*2
div si
mov si, bx
cmp dx,158
jz not_move_right
cmp si, [app]
jnz move_right
call apple
move_right:
call delete
call replace_stars
add si, 2
mov ah, 156
mov al, '*'
mov [es:si], ax
mov [stars], si
not_move_right:
ret
endp right
proc apple
mov ax, 40h
mov es, ax
mov ax, [es:6ch]
and ax, 0000001111111110b
mov di,ax
mov [app], di
mov ax, 0b800h
mov es, ax
mov al, '@'
mov ah, 154
mov [es:di], ax
ret
endp apple
proc delete
mov bx, offset stars
mov di, [st_am]
dec di
shl di, 1
mov di, [bx+di]
mov ax, 0b800h
mov es, ax
mov al, ' '
mov ah, 0
mov [es:di], ax
ret
endp delete
proc replace_stars
mov bx, [st_am] ; The amount of stars (3 or more)
dec bx
shl bx, 1 ; Offset to the last star
replace:
mov ax, [stars+bx-2]
mov [stars+bx], ax
sub bx, 2
jnz replace
ret
endp replace_stars
proc first_3_dots
mov bx, offset stars
mov si, ((12*80+40)*2)-2
mov al, '*'
mov ah, 156
mov [es:si], ax
mov [bx], si
mov si, (12*80+40)*2
mov al, '*'
mov ah, 156
mov [es:si], ax
mov [bx+2], si
mov si, ((12*80+40)*2)+2
mov al, '*'
mov ah, 156
mov [es:si], ax
mov [bx+4], si
ret
endp first_3_dots
proc delay
mov cx, 0FFFFh
delay1:
mov ax, 300
delay2:
dec ax
jnz delay2
loop delay1
ret
endp delay
proc incS
ret
endp incS
start:
mov ax, @data
mov ds, ax
; --------------------------
; Your code here
; --------------------------
mov ax, 0b800h
mov es, ax
mov si,0
mov al, ' '
mov ah, 0
call black
call first_3_dots
mov si, ((12*80+40)*2)-2
call apple
wasd:
mov ah, 0h
int 16h
mov [byte ptr saveal], al
cmp [byte ptr saveal], 'w'
jz w
cmp [byte ptr saveal], 'a'
jz a
cmp [byte ptr saveal], 's'
jz s
cmp [byte ptr saveal], 'd'
jz d
cmp [byte ptr saveal], 'q'
jmp exit
w:
mov [byte ptr dir], 1
call up
jmp wasd
s:
mov [byte ptr dir], 2
call down
jmp wasd
a:
mov [byte ptr di], 3
call left
jmp wasd
d:
mov [byte ptr dir], 4
call right
jmp wasd
exit:
mov ax, 4c00h
int 21h
END start
I think I need to add a loop somewhere that checks what is in dir and based on that it goes to where it's supposed to go but I just don't know what the loop is supposed to look like
You don't need an additional loop. The wasd main loop is fine. But instead of waiting (blocking) for the user to press a key, simply check (non-blocking) if the user has already pressed a key. If a key is indeed available then store its info in the dir variable and use it normally. And if no key is available right now, then use whatever is in the dir variable as if it had been pressed right now.
Instead of using values {1,2,3,4}, I would use the ASCII codes themselves, so {'w','a','s','d'}.
Seeing how you have initialized the snake, I also suggest you initialize the dir variable to point to the left (dir db 'a'
).
wasd:
mov ah, 01h ; BIOS.CheckKeystroke
int 16h ; -> AX ZF
mov al, [dir]
jz .key ; No key available, continue in the same direction
mov ah, 00h ; BIOS.GetKeystroke
int 16h ; -> AX
.key:
mov [saveal], al
cmp al, 'w'
je .w
cmp al, 'a'
je .a
cmp al, 's'
je .s
cmp al, 'd'
je .d
cmp al, 'q'
je exit
jmp wasd ; Invalid key
.w:
mov [dir], al
call up
jmp wasd
.s:
mov [dir], al
call down
jmp wasd
.a:
mov [dir], al ; You had a typo in `mov [byte ptr di], 3` !!!
call left ; ^
jmp wasd
.d:
mov [dir], al
call right
jmp wasd
exit:
mov ax, 4C00h
int 21h
(I also think I should use delay if I'm not wrong).
Correct. With the above changes everything will become much too fast. You need to add a suitable delay to the main loop. Waiting for one tick on the BIOS timer is already a good solution:
wasd:
xor ax, ax ; Equivalent to `mov ax, 0`
mov ds, ax ; This sets DS=0
mov ax, [word ptr 046Ch] ; Reads the BIOS timer at address DS:046Ch
.wait: ; For as long as the timer hasn't changed,
cmp ax, [word ptr 046Ch] ; we stay in this tight loop
je .wait ; Should never take longer than 1/18 sec
mov ax, @data ; This restores DS to what you have set
mov ds, ax ; at program start
mov ah, 01h ; BIOS.CheckKeystroke
int 16h ; -> AX ZF
mov al, [dir]
jz .key ; No key available, continue the same direction
...
If it still doesn't work, then try next code that is similar to your very own apple procedure:
wasd:
mov ax, 0040h
mov es, ax
mov ax, [es:6Ch]
.wait:
cmp ax, [es:6Ch]
je .wait
mov ax, 0B800h
mov es, ax
mov ah, 01h ; BIOS.CheckKeystroke
int 16h ; -> AX ZF
mov al, [dir]
jz .key ; No key available, continue the same direction
...
And in case TASM is a really stupid assembler, then also change:
mov al, [dir] --> mov al, [byte ptr dir]
mov [saveal], al --> mov [byte ptr saveal], al
mov [dir], al --> mov [byte ptr dir], al
mov [dir], al --> mov [byte ptr dir], al
mov [dir], al --> mov [byte ptr dir], al
mov [dir], al --> mov [byte ptr dir], al
[EDIT]
I assume what you wanted me to is: ```wasd: xor ax, ax mov ds, ax mov ax, [word ptr 046Ch] waitt: ;cant do .wait cmp ax, [word ptr 046Ch] ; je waitt mov ax, @data mov ds, ax mov ah, 0h int 16h mov al, [dir] jz key mov ah, 0h int 16h key: mov [byte ptr saveal], al cmp [byte ptr saveal], 'q' jz exit... ```` it's still takes a long time to respond to a pressed key.
Your recent comment contains a typo that could explain why it still takes a long time to respond. The first time that you wrote mov ah, 0h
int 16h
, it needs to be mov ah, 01h
int 16h
.
As I was curious whether it would actually work, I have translated your complete program for the FASM assembler. I believe FASM is superior to TASM, so if you're not forced to use TASM, you could consider becoming a FASM user. FASM is a modern assembler that is being actively maintainded by its author, supported by a community, and that comes with an easy to use IDE with no need for any external linker.
I'm happy to report that it all works! One change that I needed to make though, is slowing the program down even more, by turning the simple delay loop into a pair of nested loops that wait for about 3/18 sec. For your convenience, I have refrained from most other changes...
ORG 256 ; For a program with the .COM extension
; *** SETUP ***
mov ax, 0B800h ; CONST ES = 0B800h
mov es, ax
mov si, 0
mov al, ' '
mov ah, 0
call black
call first_3_dots
mov si, ((12*80+40)*2)-2
call apple
; *** MAIN LOOP ***
wasd:
push ds ; (1)
xor ax, ax ; Equivalent to `mov ax, 0`
mov ds, ax ; This sets DS=0
mov cx, 3 ; Wait about 3/18 sec
.w1:
mov ax, [046Ch] ; Reads the BIOS timer at address DS:046Ch
.w2: ; For as long as the timer hasn't changed,
cmp ax, [046Ch] ; we stay in this tight loop
je .w2 ; Should never take longer than 1/18 sec
loop .w1
pop ds ; (1) This restores DS
mov ah, 01h ; BIOS.CheckKeystroke
int 16h ; -> AX ZF
mov al, [dir]
jz .key ; No key available, continue the same direction
mov ah, 00h ; BIOS.GetKeystroke
int 16h ; -> AX
.key:
mov [saveal], al
cmp al, 'w'
je .w
cmp al, 'a'
je .a
cmp al, 's'
je .s
cmp al, 'd'
je .d
cmp al, 'q'
je exit
jmp wasd ; Invalid key
.w:
mov [dir], al
call up
jmp wasd
.s:
mov [dir], al
call down
jmp wasd
.a:
mov [dir], al
call left
jmp wasd
.d:
mov [dir], al
call right
jmp wasd
exit:
mov ax, 4C00h
int 21h
; *** PROCEDURES ***
black:
mov [es:si], ax
add si, 2
cmp si, 25*80*2
jnz black
ret
up:
mov di, 80*2
cmp si, di
jb not_move_up
cmp si, [app]
jnz move_up
call apple
move_up:
call delete
call replace_stars
sub si, 80*2
mov ah, 156
mov al, '*'
mov [es:si], ax
mov [stars], si
not_move_up:
ret
down:
mov di, (24*80*2)-1
cmp si, di
jg not_move_down
cmp si, [app]
jnz move_down
call apple
move_down:
call delete
call replace_stars
add si, 80*2
mov ah, 156
mov al, '*'
mov [es:si], ax
mov [stars], si
not_move_down:
ret
left:
mov dx, 0
mov bx, si
mov ax, si
mov si, 80*2
div si
mov si, bx
cmp dx, 0
jz not_move_left
cmp si, [app]
jnz move_left
call apple
move_left:
call delete
call replace_stars
sub si, 2
mov ah, 156
mov al, '*'
mov [es:si], ax
mov [stars], si
not_move_left:
ret
right:
mov dx, 0
mov bx, si
mov ax, si
mov si, 80*2
div si
mov si, bx
cmp dx, 158
jz not_move_right
cmp si, [app]
jnz move_right
call apple
move_right:
call delete
call replace_stars
add si, 2
mov ah, 156
mov al, '*'
mov [es:si], ax
mov [stars], si
not_move_right:
ret
apple:
mov ax, 40h
mov es, ax
mov ax, [es:6ch]
and ax, 0000001111111110b
mov di, ax
mov [app], di
mov ax, 0b800h
mov es, ax
mov al, '@'
mov ah, 154
mov [es:di], ax
ret
delete:
mov bx, stars
mov di, [st_am]
dec di
shl di, 1
mov di, [bx+di]
mov ax, 0b800h
mov es, ax
mov al, ' '
mov ah, 0
mov [es:di], ax
ret
replace_stars:
mov bx, [st_am] ; The amount of stars (3 or more)
dec bx
shl bx, 1 ; Offset to the last star
replace:
mov ax, [stars+bx-2]
mov [stars+bx], ax
sub bx, 2
jnz replace
ret
first_3_dots:
mov bx, stars
mov al, '*'
mov ah, 156
mov si, ((12*80+40)*2)-2
mov [es:si], ax
mov [bx], si
mov si, (12*80+40)*2
mov [es:si], ax
mov [bx+2], si
mov si, ((12*80+40)*2)+2
mov [es:si], ax
mov [bx+4], si
ret
; ------------------------------
saveal db 0
dir db 0
app dw 0 ; place of the apple
st_am dw 3
stars dw 0, 0, 0 ; places of the *